app_voicemail: Cleanup stale lock files on module load
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*!
20  * \file
21  * \author Mark Spencer <markster@digium.com>
22  * \brief Comedian Mail - Voicemail System
23  *
24  * unixODBC (http://www.unixodbc.org/)
25  * A source distribution of University of Washington's IMAP c-client
26  *         (http://www.washington.edu/imap/)
27  *
28  * \par See also
29  * \arg \ref Config_vm
30  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
31  * \ingroup applications
32  * \todo This module requires res_adsi to load. This needs to be optional
33  * during compilation.
34  *
35  * \todo This file is now almost impossible to work with, due to all \#ifdefs.
36  *       Feels like the database code before realtime. Someone - please come up
37  *       with a plan to clean this up.
38  */
39
40 /*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
41  * \addtogroup configuration_file Configuration Files
42  */
43
44 /*!
45  * \page voicemail.conf voicemail.conf
46  * \verbinclude voicemail.conf.sample
47  */
48
49 /*** MODULEINFO
50         <defaultenabled>yes</defaultenabled>
51         <use type="module">res_adsi</use>
52         <use type="module">res_smdi</use>
53         <support_level>core</support_level>
54  ***/
55
56 /*** MAKEOPTS
57 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
58         <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
59                 <conflict>ODBC_STORAGE</conflict>
60                 <conflict>IMAP_STORAGE</conflict>
61                 <defaultenabled>yes</defaultenabled>
62                 <support_level>core</support_level>
63         </member>
64         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
65                 <depend>generic_odbc</depend>
66                 <conflict>IMAP_STORAGE</conflict>
67                 <conflict>FILE_STORAGE</conflict>
68                 <defaultenabled>no</defaultenabled>
69                 <support_level>core</support_level>
70         </member>
71         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
72                 <depend>imap_tk</depend>
73                 <conflict>ODBC_STORAGE</conflict>
74                 <conflict>FILE_STORAGE</conflict>
75                 <use type="external">openssl</use>
76                 <defaultenabled>no</defaultenabled>
77                 <support_level>core</support_level>
78         </member>
79 </category>
80 ***/
81
82 #include "asterisk.h"
83
84 #ifdef IMAP_STORAGE
85 #include <ctype.h>
86 #include <signal.h>
87 #include <pwd.h>
88 #ifdef USE_SYSTEM_IMAP
89 #include <imap/c-client.h>
90 #include <imap/imap4r1.h>
91 #include <imap/linkage.h>
92 #elif defined (USE_SYSTEM_CCLIENT)
93 #include <c-client/c-client.h>
94 #include <c-client/imap4r1.h>
95 #include <c-client/linkage.h>
96 #else
97 #include "c-client.h"
98 #include "imap4r1.h"
99 #include "linkage.h"
100 #endif
101 #endif
102
103 #include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
104 #include <sys/time.h>
105 #include <sys/stat.h>
106 #include <sys/mman.h>
107 #include <time.h>
108 #include <dirent.h>
109 #if defined(__FreeBSD__) || defined(__OpenBSD__)
110 #include <sys/wait.h>
111 #endif
112
113 #include "asterisk/logger.h"
114 #include "asterisk/lock.h"
115 #include "asterisk/file.h"
116 #include "asterisk/channel.h"
117 #include "asterisk/pbx.h"
118 #include "asterisk/config.h"
119 #include "asterisk/say.h"
120 #include "asterisk/module.h"
121 #include "asterisk/adsi.h"
122 #include "asterisk/app.h"
123 #include "asterisk/manager.h"
124 #include "asterisk/dsp.h"
125 #include "asterisk/localtime.h"
126 #include "asterisk/cli.h"
127 #include "asterisk/utils.h"
128 #include "asterisk/stringfields.h"
129 #include "asterisk/strings.h"
130 #include "asterisk/smdi.h"
131 #include "asterisk/astobj2.h"
132 #include "asterisk/taskprocessor.h"
133 #include "asterisk/test.h"
134 #include "asterisk/format_cache.h"
135
136 #ifdef ODBC_STORAGE
137 #include "asterisk/res_odbc.h"
138 #endif
139
140 #ifdef IMAP_STORAGE
141 #include "asterisk/threadstorage.h"
142 #endif
143
144 /*** DOCUMENTATION
145         <application name="VoiceMail" language="en_US">
146                 <synopsis>
147                         Leave a Voicemail message.
148                 </synopsis>
149                 <syntax>
150                         <parameter name="mailboxs" argsep="&amp;" required="true">
151                                 <argument name="mailbox1" argsep="@" required="true">
152                                         <argument name="mailbox" required="true" />
153                                         <argument name="context" />
154                                 </argument>
155                                 <argument name="mailbox2" argsep="@" multiple="true">
156                                         <argument name="mailbox" required="true" />
157                                         <argument name="context" />
158                                 </argument>
159                         </parameter>
160                         <parameter name="options">
161                                 <optionlist>
162                                         <option name="b">
163                                                 <para>Play the <literal>busy</literal> greeting to the calling party.</para>
164                                         </option>
165                                         <option name="d">
166                                                 <argument name="c" />
167                                                 <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
168                                                 if played during the greeting. Context defaults to the current context.</para>
169                                         </option>
170                                         <option name="g">
171                                                 <argument name="#" required="true" />
172                                                 <para>Use the specified amount of gain when recording the voicemail
173                                                 message. The units are whole-number decibels (dB). Only works on supported
174                                                 technologies, which is DAHDI only.</para>
175                                         </option>
176                                         <option name="s">
177                                                 <para>Skip the playback of instructions for leaving a message to the
178                                                 calling party.</para>
179                                         </option>
180                                         <option name="u">
181                                                 <para>Play the <literal>unavailable</literal> greeting.</para>
182                                         </option>
183                                         <option name="U">
184                                                 <para>Mark message as <literal>URGENT</literal>.</para>
185                                         </option>
186                                         <option name="P">
187                                                 <para>Mark message as <literal>PRIORITY</literal>.</para>
188                                         </option>
189                                 </optionlist>
190                         </parameter>
191                 </syntax>
192                 <description>
193                         <para>This application allows the calling party to leave a message for the specified
194                         list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
195                         the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
196                         exist.</para>
197                         <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
198                         <enumlist>
199                                 <enum name="0">
200                                         <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
201                                 </enum>
202                                 <enum name="*">
203                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
204                                 </enum>
205                         </enumlist>
206                         <para>This application will set the following channel variable upon completion:</para>
207                         <variablelist>
208                                 <variable name="VMSTATUS">
209                                         <para>This indicates the status of the execution of the VoiceMail application.</para>
210                                         <value name="SUCCESS" />
211                                         <value name="USEREXIT" />
212                                         <value name="FAILED" />
213                                 </variable>
214                         </variablelist>
215                 </description>
216                 <see-also>
217                         <ref type="application">VoiceMailMain</ref>
218                 </see-also>
219         </application>
220         <application name="VoiceMailMain" language="en_US">
221                 <synopsis>
222                         Check Voicemail messages.
223                 </synopsis>
224                 <syntax>
225                         <parameter name="mailbox" required="true" argsep="@">
226                                 <argument name="mailbox" />
227                                 <argument name="context" />
228                         </parameter>
229                         <parameter name="options">
230                                 <optionlist>
231                                         <option name="p">
232                                                 <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
233                                                 the mailbox that is entered by the caller.</para>
234                                         </option>
235                                         <option name="g">
236                                                 <argument name="#" required="true" />
237                                                 <para>Use the specified amount of gain when recording a voicemail message.
238                                                 The units are whole-number decibels (dB).</para>
239                                         </option>
240                                         <option name="s">
241                                                 <para>Skip checking the passcode for the mailbox.</para>
242                                         </option>
243                                         <option name="a">
244                                                 <argument name="folder" required="true" />
245                                                 <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
246                                                 Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
247                                                 <enumlist>
248                                                         <enum name="0"><para>INBOX</para></enum>
249                                                         <enum name="1"><para>Old</para></enum>
250                                                         <enum name="2"><para>Work</para></enum>
251                                                         <enum name="3"><para>Family</para></enum>
252                                                         <enum name="4"><para>Friends</para></enum>
253                                                         <enum name="5"><para>Cust1</para></enum>
254                                                         <enum name="6"><para>Cust2</para></enum>
255                                                         <enum name="7"><para>Cust3</para></enum>
256                                                         <enum name="8"><para>Cust4</para></enum>
257                                                         <enum name="9"><para>Cust5</para></enum>
258                                                 </enumlist>
259                                         </option>
260                                 </optionlist>
261                         </parameter>
262                 </syntax>
263                 <description>
264                         <para>This application allows the calling party to check voicemail messages. A specific
265                         <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
266                         may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
267                         be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
268                         <literal>default</literal> context will be used.</para>
269                         <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
270                         or Password, and the extension exists:</para>
271                         <enumlist>
272                                 <enum name="*">
273                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
274                                 </enum>
275                         </enumlist>
276                 </description>
277                 <see-also>
278                         <ref type="application">VoiceMail</ref>
279                 </see-also>
280         </application>
281         <application name="MailboxExists" language="en_US">
282                 <synopsis>
283                         Check to see if Voicemail mailbox exists.
284                 </synopsis>
285                 <syntax>
286                         <parameter name="mailbox" required="true" argsep="@">
287                                 <argument name="mailbox" required="true" />
288                                 <argument name="context" />
289                         </parameter>
290                         <parameter name="options">
291                                 <para>None options.</para>
292                         </parameter>
293                 </syntax>
294                 <description>
295                         <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
296                         <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
297                         <replaceable>context</replaceable> is specified, the <literal>default</literal> context
298                         will be used.</para>
299                         <para>This application will set the following channel variable upon completion:</para>
300                         <variablelist>
301                                 <variable name="VMBOXEXISTSSTATUS">
302                                         <para>This will contain the status of the execution of the MailboxExists application.
303                                         Possible values include:</para>
304                                         <value name="SUCCESS" />
305                                         <value name="FAILED" />
306                                 </variable>
307                         </variablelist>
308                 </description>
309                 <see-also>
310                         <ref type="function">VM_INFO</ref>
311                 </see-also>
312         </application>
313         <application name="VMAuthenticate" language="en_US">
314                 <synopsis>
315                         Authenticate with Voicemail passwords.
316                 </synopsis>
317                 <syntax>
318                         <parameter name="mailbox" required="true" argsep="@">
319                                 <argument name="mailbox" />
320                                 <argument name="context" />
321                         </parameter>
322                         <parameter name="options">
323                                 <optionlist>
324                                         <option name="s">
325                                                 <para>Skip playing the initial prompts.</para>
326                                         </option>
327                                 </optionlist>
328                         </parameter>
329                 </syntax>
330                 <description>
331                         <para>This application behaves the same way as the Authenticate application, but the passwords
332                         are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
333                         specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
334                         is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
335                         mailbox.</para>
336                         <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
337                         or Password, and the extension exists:</para>
338                         <enumlist>
339                                 <enum name="*">
340                                         <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
341                                 </enum>
342                         </enumlist>
343                 </description>
344         </application>
345         <application name="VoiceMailPlayMsg" language="en_US">
346                 <synopsis>
347                         Play a single voice mail msg from a mailbox by msg id.
348                 </synopsis>
349                 <syntax>
350                         <parameter name="mailbox" required="true" argsep="@">
351                                 <argument name="mailbox" />
352                                 <argument name="context" />
353                         </parameter>
354                         <parameter name="msg_id" required="true">
355                                 <para>The msg id of the msg to play back. </para>
356                         </parameter>
357                 </syntax>
358                 <description>
359                         <para>This application sets the following channel variable upon completion:</para>
360                         <variablelist>
361                                 <variable name="VOICEMAIL_PLAYBACKSTATUS">
362                                         <para>The status of the playback attempt as a text string.</para>
363                                         <value name="SUCCESS"/>
364                                         <value name="FAILED"/>
365                                 </variable>
366                         </variablelist>
367                 </description>
368         </application>
369         <application name="VMSayName" language="en_US">
370                 <synopsis>
371                         Play the name of a voicemail user
372                 </synopsis>
373                 <syntax>
374                         <parameter name="mailbox" required="true" argsep="@">
375                                 <argument name="mailbox" />
376                                 <argument name="context" />
377                         </parameter>
378                 </syntax>
379                 <description>
380                         <para>This application will say the recorded name of the voicemail user specified as the
381                         argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
382                 </description>
383         </application>
384         <function name="MAILBOX_EXISTS" language="en_US">
385                 <synopsis>
386                         Tell if a mailbox is configured.
387                 </synopsis>
388                 <syntax argsep="@">
389                         <parameter name="mailbox" required="true" />
390                         <parameter name="context" />
391                 </syntax>
392                 <description>
393                         <note><para>DEPRECATED. Use VM_INFO(mailbox[@context],exists) instead.</para></note>
394                         <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
395                         If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
396                         context.</para>
397                 </description>
398                 <see-also>
399                         <ref type="function">VM_INFO</ref>
400                 </see-also>
401         </function>
402         <function name="VM_INFO" language="en_US">
403                 <synopsis>
404                         Returns the selected attribute from a mailbox.
405                 </synopsis>
406                 <syntax argsep=",">
407                         <parameter name="mailbox" argsep="@" required="true">
408                                 <argument name="mailbox" required="true" />
409                                 <argument name="context" />
410                         </parameter>
411                         <parameter name="attribute" required="true">
412                                 <optionlist>
413                                         <option name="count">
414                                                 <para>Count of messages in specified <replaceable>folder</replaceable>.
415                                                 If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
416                                         </option>
417                                         <option name="email">
418                                                 <para>E-mail address associated with the mailbox.</para>
419                                         </option>
420                                         <option name="exists">
421                                                 <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
422                                         </option>
423                                         <option name="fullname">
424                                                 <para>Full name associated with the mailbox.</para>
425                                         </option>
426                                         <option name="language">
427                                                 <para>Mailbox language if overridden, otherwise the language of the channel.</para>
428                                         </option>
429                                         <option name="locale">
430                                                 <para>Mailbox locale if overridden, otherwise global locale.</para>
431                                         </option>
432                                         <option name="pager">
433                                                 <para>Pager e-mail address associated with the mailbox.</para>
434                                         </option>
435                                         <option name="password">
436                                                 <para>Mailbox access password.</para>
437                                         </option>
438                                         <option name="tz">
439                                                 <para>Mailbox timezone if overridden, otherwise global timezone</para>
440                                         </option>
441                                 </optionlist>
442                         </parameter>
443                         <parameter name="folder" required="false">
444                                 <para>If not specified, <literal>INBOX</literal> is assumed.</para>
445                         </parameter>
446                 </syntax>
447                 <description>
448                         <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
449                         If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
450                         context. Where the <replaceable>folder</replaceable> can be specified, common folders
451                         include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
452                         <literal>Family</literal> and <literal>Friends</literal>.</para>
453                 </description>
454         </function>
455         <manager name="VoicemailUsersList" language="en_US">
456                 <synopsis>
457                         List All Voicemail User Information.
458                 </synopsis>
459                 <syntax>
460                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
461                 </syntax>
462                 <description>
463                 </description>
464         </manager>
465         <manager name="VoicemailUserStatus" language="en_US">
466                 <synopsis>
467                         Show the status of given voicemail user's info.
468                 </synopsis>
469                 <syntax>
470                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
471                         <parameter name="Context" required="true">
472                                 <para>The context you want to check.</para>
473                         </parameter>
474                         <parameter name="Mailbox" required="true">
475                                 <para>The mailbox you want to check.</para>
476                         </parameter>
477                 </syntax>
478                 <description>
479                         <para>Retrieves the status of the given voicemail user.</para>
480                 </description>
481         </manager>
482         <manager name="VoicemailRefresh" language="en_US">
483                 <synopsis>
484                         Tell Asterisk to poll mailboxes for a change
485                 </synopsis>
486                 <syntax>
487                         <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
488                         <parameter name="Context" />
489                         <parameter name="Mailbox" />
490                 </syntax>
491                 <description>
492                         <para>Normally, MWI indicators are only sent when Asterisk itself
493                         changes a mailbox.  With external programs that modify the content
494                         of a mailbox from outside the application, an option exists called
495                         <literal>pollmailboxes</literal> that will cause voicemail to
496                         continually scan all mailboxes on a system for changes.  This can
497                         cause a large amount of load on a system.  This command allows
498                         external applications to signal when a particular mailbox has
499                         changed, thus permitting external applications to modify mailboxes
500                         and MWI to work without introducing considerable CPU load.</para>
501                         <para>If <replaceable>Context</replaceable> is not specified, all
502                         mailboxes on the system will be polled for changes.  If
503                         <replaceable>Context</replaceable> is specified, but
504                         <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
505                         within <replaceable>Context</replaceable> will be polled.
506                         Otherwise, only a single mailbox will be polled for changes.</para>
507                 </description>
508         </manager>
509  ***/
510
511 #ifdef IMAP_STORAGE
512 static char imapserver[48];
513 static char imapport[8];
514 static char imapflags[128];
515 static char imapfolder[64];
516 static char imapparentfolder[64] = "\0";
517 static char greetingfolder[64];
518 static char authuser[32];
519 static char authpassword[42];
520 static int imapversion = 1;
521
522 static int expungeonhangup = 1;
523 static int imapgreetings = 0;
524 static int imap_poll_logout = 0;
525 static char delimiter = '\0';
526
527 /* mail_open cannot be protected on a stream basis */
528 ast_mutex_t mail_open_lock;
529
530 struct vm_state;
531 struct ast_vm_user;
532
533 AST_THREADSTORAGE(ts_vmstate);
534
535 /* Forward declarations for IMAP */
536 static int init_mailstream(struct vm_state *vms, int box);
537 static void write_file(char *filename, char *buffer, unsigned long len);
538 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
539 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
540 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
541 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
542 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
543 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
544 static void vmstate_insert(struct vm_state *vms);
545 static void vmstate_delete(struct vm_state *vms);
546 static void set_update(MAILSTREAM * stream);
547 static void init_vm_state(struct vm_state *vms);
548 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
549 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
550 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
551 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
552 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
553 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
554 static void update_messages_by_imapuser(const char *user, unsigned long number);
555 static int vm_delete(char *file);
556
557 static int imap_remove_file (char *dir, int msgnum);
558 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
559 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
560 static void check_quota(struct vm_state *vms, char *mailbox);
561 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
562 static void imap_logout(const char *mailbox_id);
563
564 struct vmstate {
565         struct vm_state *vms;
566         AST_LIST_ENTRY(vmstate) list;
567 };
568
569 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
570
571 #endif
572
573 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
574
575 #define COMMAND_TIMEOUT 5000
576 /* Don't modify these here; set your umask at runtime instead */
577 #define VOICEMAIL_DIR_MODE      0777
578 #define VOICEMAIL_FILE_MODE     0666
579 #define CHUNKSIZE       65536
580
581 #define VOICEMAIL_CONFIG "voicemail.conf"
582 #define ASTERISK_USERNAME "asterisk"
583
584 /* Define fast-forward, pause, restart, and reverse keys
585  * while listening to a voicemail message - these are
586  * strings, not characters */
587 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
588 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
589 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
590 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
591 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
592 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
593
594 /* Default mail command to mail voicemail. Change it with the
595  * mailcmd= command in voicemail.conf */
596 #define SENDMAIL "/usr/sbin/sendmail -t"
597
598 #define INTRO "vm-intro"
599
600 #define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
601
602 #define MAXMSG 100
603 #define MAXMSGLIMIT 9999
604
605 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
606
607 #define BASELINELEN 72
608 #define BASEMAXINLINE 256
609 #ifdef IMAP_STORAGE
610 #define ENDL "\r\n"
611 #else
612 #define ENDL "\n"
613 #endif
614
615 #define MAX_DATETIME_FORMAT     512
616 #define MAX_NUM_CID_CONTEXTS 10
617
618 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
619 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
620 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
621 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
622 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
623 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
624 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
625 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
626 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
627 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
628 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
629 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
630 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
631 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
632 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
633 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
634 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
635 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
636 #define VM_FWDURGAUTO    (1 << 18)  /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
637 #define ERROR_LOCK_PATH  -100
638 #define ERROR_MAX_MSGS   -101
639 #define OPERATOR_EXIT     300
640
641 enum vm_box {
642         NEW_FOLDER =            0,
643         OLD_FOLDER =            1,
644         WORK_FOLDER =           2,
645         FAMILY_FOLDER =         3,
646         FRIENDS_FOLDER =        4,
647         GREETINGS_FOLDER =      -1
648 };
649
650 enum vm_option_flags {
651         OPT_SILENT =           (1 << 0),
652         OPT_BUSY_GREETING =    (1 << 1),
653         OPT_UNAVAIL_GREETING = (1 << 2),
654         OPT_RECORDGAIN =       (1 << 3),
655         OPT_PREPEND_MAILBOX =  (1 << 4),
656         OPT_AUTOPLAY =         (1 << 6),
657         OPT_DTMFEXIT =         (1 << 7),
658         OPT_MESSAGE_Urgent =   (1 << 8),
659         OPT_MESSAGE_PRIORITY = (1 << 9)
660 };
661
662 enum vm_option_args {
663         OPT_ARG_RECORDGAIN = 0,
664         OPT_ARG_PLAYFOLDER = 1,
665         OPT_ARG_DTMFEXIT   = 2,
666         /* This *must* be the last value in this enum! */
667         OPT_ARG_ARRAY_SIZE = 3,
668 };
669
670 enum vm_passwordlocation {
671         OPT_PWLOC_VOICEMAILCONF = 0,
672         OPT_PWLOC_SPOOLDIR      = 1,
673         OPT_PWLOC_USERSCONF     = 2,
674 };
675
676 AST_APP_OPTIONS(vm_app_options, {
677         AST_APP_OPTION('s', OPT_SILENT),
678         AST_APP_OPTION('b', OPT_BUSY_GREETING),
679         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
680         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
681         AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
682         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
683         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
684         AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
685         AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
686 });
687
688 static const char * const mailbox_folders[] = {
689 #ifdef IMAP_STORAGE
690         imapfolder,
691 #else
692         "INBOX",
693 #endif
694         "Old",
695         "Work",
696         "Family",
697         "Friends",
698         "Cust1",
699         "Cust2",
700         "Cust3",
701         "Cust4",
702         "Cust5",
703         "Deleted",
704         "Urgent",
705 };
706
707 static int load_config(int reload);
708 #ifdef TEST_FRAMEWORK
709 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
710 #endif
711 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
712
713 /*! \page vmlang Voicemail Language Syntaxes Supported
714
715         \par Syntaxes supported, not really language codes.
716         \arg \b en    - English
717         \arg \b de    - German
718         \arg \b es    - Spanish
719         \arg \b fr    - French
720         \arg \b it    - Italian
721         \arg \b nl    - Dutch
722         \arg \b pt    - Portuguese
723         \arg \b pt_BR - Portuguese (Brazil)
724         \arg \b gr    - Greek
725         \arg \b no    - Norwegian
726         \arg \b se    - Swedish
727         \arg \b tw    - Chinese (Taiwan)
728         \arg \b ua - Ukrainian
729
730 German requires the following additional soundfile:
731 \arg \b 1F      einE (feminine)
732
733 Spanish requires the following additional soundfile:
734 \arg \b 1M      un (masculine)
735
736 Dutch, Portuguese & Spanish require the following additional soundfiles:
737 \arg \b vm-INBOXs       singular of 'new'
738 \arg \b vm-Olds         singular of 'old/heard/read'
739
740 NB these are plural:
741 \arg \b vm-INBOX        nieuwe (nl)
742 \arg \b vm-Old          oude (nl)
743
744 Polish uses:
745 \arg \b vm-new-a        'new', feminine singular accusative
746 \arg \b vm-new-e        'new', feminine plural accusative
747 \arg \b vm-new-ych      'new', feminine plural genitive
748 \arg \b vm-old-a        'old', feminine singular accusative
749 \arg \b vm-old-e        'old', feminine plural accusative
750 \arg \b vm-old-ych      'old', feminine plural genitive
751 \arg \b digits/1-a      'one', not always same as 'digits/1'
752 \arg \b digits/2-ie     'two', not always same as 'digits/2'
753
754 Swedish uses:
755 \arg \b vm-nytt         singular of 'new'
756 \arg \b vm-nya          plural of 'new'
757 \arg \b vm-gammalt      singular of 'old'
758 \arg \b vm-gamla        plural of 'old'
759 \arg \b digits/ett      'one', not always same as 'digits/1'
760
761 Norwegian uses:
762 \arg \b vm-ny           singular of 'new'
763 \arg \b vm-nye          plural of 'new'
764 \arg \b vm-gammel       singular of 'old'
765 \arg \b vm-gamle        plural of 'old'
766
767 Dutch also uses:
768 \arg \b nl-om           'at'?
769
770 Spanish also uses:
771 \arg \b vm-youhaveno
772
773 Italian requires the following additional soundfile:
774
775 For vm_intro_it:
776 \arg \b vm-nuovo        new
777 \arg \b vm-nuovi        new plural
778 \arg \b vm-vecchio      old
779 \arg \b vm-vecchi       old plural
780
781 Japanese requires the following additional soundfile:
782 \arg \b jp-arimasu          there is
783 \arg \b jp-arimasen         there is not
784 \arg \b jp-oshitekudasai    please press
785 \arg \b jp-ni               article ni
786 \arg \b jp-ga               article ga
787 \arg \b jp-wa               article wa
788 \arg \b jp-wo               article wo
789
790 Chinese (Taiwan) requires the following additional soundfile:
791 \arg \b vm-tong         A class-word for call (tong1)
792 \arg \b vm-ri           A class-word for day (ri4)
793 \arg \b vm-you          You (ni3)
794 \arg \b vm-haveno   Have no (mei2 you3)
795 \arg \b vm-have     Have (you3)
796 \arg \b vm-listen   To listen (yao4 ting1)
797
798
799 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
800 spelled among others when you have to change folder. For the above reasons, vm-INBOX
801 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
802
803 */
804
805 struct baseio {
806         int iocp;
807         int iolen;
808         int linelength;
809         int ateof;
810         unsigned char iobuf[BASEMAXINLINE];
811 };
812
813 #define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
814 #define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
815 /* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
816 #define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
817
818 /*! Structure for linked list of users
819  * Use ast_vm_user_destroy() to free one of these structures. */
820 struct ast_vm_user {
821         char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
822         char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
823         char password[80];               /*!< Secret pin code, numbers only */
824         char fullname[80];               /*!< Full name, for directory app */
825         char *email;                     /*!< E-mail address */
826         char *emailsubject;              /*!< E-mail subject */
827         char *emailbody;                 /*!< E-mail body */
828         char pager[80];                  /*!< E-mail address to pager (no attachment) */
829         char serveremail[80];            /*!< From: Mail address */
830         char fromstring[100];            /*!< From: Username */
831         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
832         char zonetag[80];                /*!< Time zone */
833         char locale[20];                 /*!< The locale (for presentation of date/time) */
834         char callback[80];
835         char dialout[80];
836         char uniqueid[80];               /*!< Unique integer identifier */
837         char exit[80];
838         char attachfmt[20];              /*!< Attachment format */
839         unsigned int flags;              /*!< VM_ flags */
840         int saydurationm;
841         int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
842         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
843         int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
844         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
845         int passwordlocation;            /*!< Storage location of the password */
846 #ifdef IMAP_STORAGE
847         char imapserver[48];             /*!< IMAP server address */
848         char imapport[8];                /*!< IMAP server port */
849         char imapflags[128];             /*!< IMAP optional flags */
850         char imapuser[80];               /*!< IMAP server login */
851         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
852         char imapfolder[64];             /*!< IMAP voicemail folder */
853         char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
854         int imapversion;                 /*!< If configuration changes, use the new values */
855 #endif
856         double volgain;                  /*!< Volume gain for voicemails sent via email */
857         AST_LIST_ENTRY(ast_vm_user) list;
858 };
859
860 /*! Voicemail time zones */
861 struct vm_zone {
862         AST_LIST_ENTRY(vm_zone) list;
863         char name[80];
864         char timezone[80];
865         char msg_format[512];
866 };
867
868 #define VMSTATE_MAX_MSG_ARRAY 256
869
870 /*! Voicemail mailbox state */
871 struct vm_state {
872         char curbox[80];
873         char username[80];
874         char context[80];
875         char curdir[PATH_MAX];
876         char vmbox[PATH_MAX];
877         char fn[PATH_MAX];
878         char intro[PATH_MAX];
879         int *deleted;
880         int *heard;
881         int dh_arraysize; /* used for deleted / heard allocation */
882         int curmsg;
883         int lastmsg;
884         int newmessages;
885         int oldmessages;
886         int urgentmessages;
887         int starting;
888         int repeats;
889 #ifdef IMAP_STORAGE
890         ast_mutex_t lock;
891         int updated;                         /*!< decremented on each mail check until 1 -allows delay */
892         long *msgArray;
893         unsigned msg_array_max;
894         MAILSTREAM *mailstream;
895         int vmArrayIndex;
896         char imapuser[80];                   /*!< IMAP server login */
897         char imapfolder[64];                 /*!< IMAP voicemail folder */
898         char imapserver[48];                 /*!< IMAP server address */
899         char imapport[8];                    /*!< IMAP server port */
900         char imapflags[128];                 /*!< IMAP optional flags */
901         int imapversion;
902         int interactive;
903         char introfn[PATH_MAX];              /*!< Name of prepended file */
904         unsigned int quota_limit;
905         unsigned int quota_usage;
906         struct vm_state *persist_vms;
907 #endif
908 };
909
910 #ifdef ODBC_STORAGE
911 static char odbc_database[80];
912 static char odbc_table[80];
913 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
914 #define DISPOSE(a,b) remove_file(a,b)
915 #define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
916 #define EXISTS(a,b,c,d) (message_exists(a,b))
917 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
918 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
919 #define DELETE(a,b,c,d) (delete_file(a,b))
920 #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
921 #else
922 #ifdef IMAP_STORAGE
923 #define DISPOSE(a,b) (imap_remove_file(a,b))
924 #define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
925 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
926 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
927 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
928 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
929 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
930 #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
931 #else
932 #define RETRIEVE(a,b,c,d)
933 #define DISPOSE(a,b)
934 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
935 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
936 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
937 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
938 #define DELETE(a,b,c,d) (vm_delete(c))
939 #define UPDATE_MSG_ID(a, b, c, d, e, f)
940 #endif
941 #endif
942
943 static char VM_SPOOL_DIR[PATH_MAX];
944
945 static char ext_pass_cmd[128];
946 static char ext_pass_check_cmd[128];
947
948 static int my_umask;
949
950 #define PWDCHANGE_INTERNAL (1 << 1)
951 #define PWDCHANGE_EXTERNAL (1 << 2)
952 static int pwdchange = PWDCHANGE_INTERNAL;
953
954 #ifdef ODBC_STORAGE
955 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
956 #else
957 # ifdef IMAP_STORAGE
958 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
959 # else
960 # define tdesc "Comedian Mail (Voicemail System)"
961 # endif
962 #endif
963
964 static char userscontext[AST_MAX_EXTENSION] = "default";
965
966 static char *addesc = "Comedian Mail";
967
968 /* Leave a message */
969 static char *app = "VoiceMail";
970
971 /* Check mail, control, etc */
972 static char *app2 = "VoiceMailMain";
973
974 static char *app3 = "MailboxExists";
975 static char *app4 = "VMAuthenticate";
976
977 static char *playmsg_app = "VoiceMailPlayMsg";
978
979 static char *sayname_app = "VMSayName";
980
981 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
982 static AST_LIST_HEAD_STATIC(zones, vm_zone);
983 static char zonetag[80];
984 static char locale[20];
985 static int maxsilence;
986 static int maxmsg;
987 static int maxdeletedmsg;
988 static int silencethreshold = 128;
989 static char serveremail[80];
990 static char mailcmd[160];       /* Configurable mail cmd */
991 static char externnotify[160];
992 static struct ast_smdi_interface *smdi_iface = NULL;
993 static char vmfmts[80];
994 static double volgain;
995 static int vmminsecs;
996 static int vmmaxsecs;
997 static int maxgreet;
998 static int skipms;
999 static int maxlogins;
1000 static int minpassword;
1001 static int passwordlocation;
1002 static char aliasescontext[MAX_VM_CONTEXT_LEN];
1003
1004 /*! Poll mailboxes for changes since there is something external to
1005  *  app_voicemail that may change them. */
1006 static unsigned int poll_mailboxes;
1007
1008 /*! Polling frequency */
1009 static unsigned int poll_freq;
1010 /*! By default, poll every 30 seconds */
1011 #define DEFAULT_POLL_FREQ 30
1012
1013 AST_MUTEX_DEFINE_STATIC(poll_lock);
1014 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
1015 static pthread_t poll_thread = AST_PTHREADT_NULL;
1016 static unsigned char poll_thread_run;
1017
1018 /*! Subscription to MWI event subscription changes */
1019 static struct stasis_subscription *mwi_sub_sub;
1020
1021 /*!
1022  * \brief An MWI subscription
1023  *
1024  * This is so we can keep track of which mailboxes are subscribed to.
1025  * This way, we know which mailboxes to poll when the pollmailboxes
1026  * option is being used.
1027  */
1028 struct mwi_sub {
1029         AST_RWLIST_ENTRY(mwi_sub) entry;
1030         int old_urgent;
1031         int old_new;
1032         int old_old;
1033         char *uniqueid;
1034         char mailbox[0];
1035 };
1036
1037 struct mwi_sub_task {
1038         const char *mailbox;
1039         const char *context;
1040         const char *uniqueid;
1041 };
1042
1043 static void mwi_sub_task_dtor(struct mwi_sub_task *mwist)
1044 {
1045         ast_free((void *) mwist->mailbox);
1046         ast_free((void *) mwist->context);
1047         ast_free((void *) mwist->uniqueid);
1048         ast_free(mwist);
1049 }
1050
1051 static struct ast_taskprocessor *mwi_subscription_tps;
1052
1053 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
1054
1055 struct alias_mailbox_mapping {
1056         char *alias;
1057         char *mailbox;
1058         char buf[0];
1059 };
1060
1061 struct mailbox_alias_mapping {
1062         char *alias;
1063         char *mailbox;
1064         char buf[0];
1065 };
1066
1067 #define MAPPING_BUCKETS 511
1068 static struct ao2_container *alias_mailbox_mappings;
1069 AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias);
1070 AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias);
1071
1072 static struct ao2_container *mailbox_alias_mappings;
1073 AO2_STRING_FIELD_HASH_FN(mailbox_alias_mapping, mailbox);
1074 AO2_STRING_FIELD_CMP_FN(mailbox_alias_mapping, mailbox);
1075
1076 /* custom audio control prompts for voicemail playback */
1077 static char listen_control_forward_key[12];
1078 static char listen_control_reverse_key[12];
1079 static char listen_control_pause_key[12];
1080 static char listen_control_restart_key[12];
1081 static char listen_control_stop_key[12];
1082
1083 /* custom password sounds */
1084 static char vm_login[80] = "vm-login";
1085 static char vm_newuser[80] = "vm-newuser";
1086 static char vm_password[80] = "vm-password";
1087 static char vm_newpassword[80] = "vm-newpassword";
1088 static char vm_passchanged[80] = "vm-passchanged";
1089 static char vm_reenterpassword[80] = "vm-reenterpassword";
1090 static char vm_mismatch[80] = "vm-mismatch";
1091 static char vm_invalid_password[80] = "vm-invalid-password";
1092 static char vm_pls_try_again[80] = "vm-pls-try-again";
1093
1094 /*
1095  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1096  * 1. create a sound along the lines of "Please try again.  When done, press the pound key" which could be spliced
1097  * from existing sound clips.  This would require some programming changes in the area of vm_forward options and also
1098  * app.c's __ast_play_and_record function
1099  * 2. create a sound prompt saying "Please try again.  When done recording, press any key to stop and send the prepended
1100  * message."  At the time of this comment, I think this would require new voice work to be commissioned.
1101  * 3. Something way different like providing instructions before a time out or a post-recording menu.  This would require
1102  * more effort than either of the other two.
1103  */
1104 static char vm_prepend_timeout[80] = "vm-then-pound";
1105
1106 static struct ast_flags globalflags = {0};
1107
1108 static int saydurationminfo;
1109
1110 static char dialcontext[AST_MAX_CONTEXT] = "";
1111 static char callcontext[AST_MAX_CONTEXT] = "";
1112 static char exitcontext[AST_MAX_CONTEXT] = "";
1113
1114 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
1115
1116
1117 static char *emailbody = NULL;
1118 static char *emailsubject = NULL;
1119 static char *pagerbody = NULL;
1120 static char *pagersubject = NULL;
1121 static char fromstring[100];
1122 static char pagerfromstring[100];
1123 static char charset[32] = "ISO-8859-1";
1124
1125 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1126 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1127 static int adsiver = 1;
1128 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1129 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1130
1131 /* Forward declarations - generic */
1132 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1133 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1134 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
1135 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1136 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1137                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1138                         signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1139 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1140 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1141 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
1142 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id);
1143 static void apply_options(struct ast_vm_user *vmu, const char *options);
1144 static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
1145 static int is_valid_dtmf(const char *key);
1146 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1147 static int write_password_to_file(const char *secretfn, const char *password);
1148 static const char *substitute_escapes(const char *value);
1149 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
1150 static void notify_new_state(struct ast_vm_user *vmu);
1151 static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1152
1153
1154 /*!
1155  * Place a message in the indicated folder
1156  *
1157  * \param vmu Voicemail user
1158  * \param vms Current voicemail state for the user
1159  * \param msg The message number to save
1160  * \param box The folder into which the message should be saved
1161  * \param[out] newmsg The new message number of the saved message
1162  * \param move Tells whether to copy or to move the message
1163  *
1164  * \note the "move" parameter is only honored for IMAP voicemail presently
1165  * \retval 0 Success
1166  * \retval other Failure
1167  */
1168 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1169
1170 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD);
1171 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1172
1173 static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old);
1174 static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
1175 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1176 static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
1177
1178 #ifdef TEST_FRAMEWORK
1179 static int vm_test_destroy_user(const char *context, const char *mailbox);
1180 static int vm_test_create_user(const char *context, const char *mailbox);
1181 #endif
1182
1183 /*!
1184  * \internal
1185  * \brief Parse the given mailbox_id into mailbox and context.
1186  * \since 12.0.0
1187  *
1188  * \param mailbox_id The mailbox@context string to separate.
1189  * \param mailbox Where the mailbox part will start.
1190  * \param context Where the context part will start.  ("default" if not present)
1191  *
1192  * \retval 0 on success.
1193  * \retval -1 on error.
1194  */
1195 static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1196 {
1197         if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1198                 return -1;
1199         }
1200         *context = mailbox_id;
1201         *mailbox = strsep(context, "@");
1202         if (ast_strlen_zero(*mailbox)) {
1203                 return -1;
1204         }
1205         if (ast_strlen_zero(*context)) {
1206                 *context = "default";
1207         }
1208         return 0;
1209 }
1210
1211 struct ao2_container *inprocess_container;
1212
1213 struct inprocess {
1214         int count;
1215         char *context;
1216         char mailbox[0];
1217 };
1218
1219 static int inprocess_hash_fn(const void *obj, const int flags)
1220 {
1221         const struct inprocess *i = obj;
1222         return atoi(i->mailbox);
1223 }
1224
1225 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1226 {
1227         struct inprocess *i = obj, *j = arg;
1228         if (strcmp(i->mailbox, j->mailbox)) {
1229                 return 0;
1230         }
1231         return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1232 }
1233
1234 static int inprocess_count(const char *context, const char *mailbox, int delta)
1235 {
1236         struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
1237         arg->context = arg->mailbox + strlen(mailbox) + 1;
1238         strcpy(arg->mailbox, mailbox); /* SAFE */
1239         strcpy(arg->context, context); /* SAFE */
1240         ao2_lock(inprocess_container);
1241         if ((i = ao2_find(inprocess_container, arg, 0))) {
1242                 int ret = ast_atomic_fetchadd_int(&i->count, delta);
1243                 ao2_unlock(inprocess_container);
1244                 ao2_ref(i, -1);
1245                 return ret;
1246         }
1247         if (delta < 0) {
1248                 ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1249         }
1250         if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
1251                 ao2_unlock(inprocess_container);
1252                 return 0;
1253         }
1254         i->context = i->mailbox + strlen(mailbox) + 1;
1255         strcpy(i->mailbox, mailbox); /* SAFE */
1256         strcpy(i->context, context); /* SAFE */
1257         i->count = delta;
1258         ao2_link(inprocess_container, i);
1259         ao2_unlock(inprocess_container);
1260         ao2_ref(i, -1);
1261         return 0;
1262 }
1263
1264 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1265 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1266 #endif
1267
1268 /*!
1269  * \brief Strips control and non 7-bit clean characters from input string.
1270  *
1271  * \note To map control and none 7-bit characters to a 7-bit clean characters
1272  *  please use ast_str_encode_mine().
1273  */
1274 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1275 {
1276         char *bufptr = buf;
1277         for (; *input; input++) {
1278                 if (*input < 32) {
1279                         continue;
1280                 }
1281                 *bufptr++ = *input;
1282                 if (bufptr == buf + buflen - 1) {
1283                         break;
1284                 }
1285         }
1286         *bufptr = '\0';
1287         return buf;
1288 }
1289
1290
1291 /*!
1292  * \brief Sets default voicemail system options to a voicemail user.
1293  *
1294  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1295  * - all the globalflags
1296  * - the saydurationminfo
1297  * - the callcontext
1298  * - the dialcontext
1299  * - the exitcontext
1300  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1301  * - volume gain.
1302  * - emailsubject, emailbody set to NULL
1303  */
1304 static void populate_defaults(struct ast_vm_user *vmu)
1305 {
1306         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1307         vmu->passwordlocation = passwordlocation;
1308         if (saydurationminfo) {
1309                 vmu->saydurationm = saydurationminfo;
1310         }
1311         ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1312         ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1313         ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1314         ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1315         ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1316         if (vmminsecs) {
1317                 vmu->minsecs = vmminsecs;
1318         }
1319         if (vmmaxsecs) {
1320                 vmu->maxsecs = vmmaxsecs;
1321         }
1322         if (maxmsg) {
1323                 vmu->maxmsg = maxmsg;
1324         }
1325         if (maxdeletedmsg) {
1326                 vmu->maxdeletedmsg = maxdeletedmsg;
1327         }
1328         vmu->volgain = volgain;
1329         ast_free(vmu->email);
1330         vmu->email = NULL;
1331         ast_free(vmu->emailsubject);
1332         vmu->emailsubject = NULL;
1333         ast_free(vmu->emailbody);
1334         vmu->emailbody = NULL;
1335 #ifdef IMAP_STORAGE
1336         ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1337         ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1338         ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1339         ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1340 #endif
1341 }
1342
1343 /*!
1344  * \brief Sets a specific property value.
1345  * \param vmu The voicemail user object to work with.
1346  * \param var The name of the property to be set.
1347  * \param value The value to be set to the property.
1348  *
1349  * The property name must be one of the understood properties. See the source for details.
1350  */
1351 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1352 {
1353         int x;
1354         if (!strcasecmp(var, "attach")) {
1355                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1356         } else if (!strcasecmp(var, "attachfmt")) {
1357                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1358         } else if (!strcasecmp(var, "serveremail")) {
1359                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1360         } else if (!strcasecmp(var, "fromstring")) {
1361                 ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1362         } else if (!strcasecmp(var, "emailbody")) {
1363                 ast_free(vmu->emailbody);
1364                 vmu->emailbody = ast_strdup(substitute_escapes(value));
1365         } else if (!strcasecmp(var, "emailsubject")) {
1366                 ast_free(vmu->emailsubject);
1367                 vmu->emailsubject = ast_strdup(substitute_escapes(value));
1368         } else if (!strcasecmp(var, "language")) {
1369                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
1370         } else if (!strcasecmp(var, "tz")) {
1371                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1372         } else if (!strcasecmp(var, "locale")) {
1373                 ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1374 #ifdef IMAP_STORAGE
1375         } else if (!strcasecmp(var, "imapuser")) {
1376                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1377                 vmu->imapversion = imapversion;
1378         } else if (!strcasecmp(var, "imapserver")) {
1379                 ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1380                 vmu->imapversion = imapversion;
1381         } else if (!strcasecmp(var, "imapport")) {
1382                 ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1383                 vmu->imapversion = imapversion;
1384         } else if (!strcasecmp(var, "imapflags")) {
1385                 ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1386                 vmu->imapversion = imapversion;
1387         } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1388                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1389                 vmu->imapversion = imapversion;
1390         } else if (!strcasecmp(var, "imapfolder")) {
1391                 ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1392                 vmu->imapversion = imapversion;
1393         } else if (!strcasecmp(var, "imapvmshareid")) {
1394                 ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1395                 vmu->imapversion = imapversion;
1396 #endif
1397         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1398                 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1399         } else if (!strcasecmp(var, "saycid")){
1400                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1401         } else if (!strcasecmp(var, "sendvoicemail")){
1402                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1403         } else if (!strcasecmp(var, "review")){
1404                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1405         } else if (!strcasecmp(var, "tempgreetwarn")){
1406                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1407         } else if (!strcasecmp(var, "messagewrap")){
1408                 ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1409         } else if (!strcasecmp(var, "operator")) {
1410                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1411         } else if (!strcasecmp(var, "envelope")){
1412                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1413         } else if (!strcasecmp(var, "moveheard")){
1414                 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1415         } else if (!strcasecmp(var, "sayduration")){
1416                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1417         } else if (!strcasecmp(var, "saydurationm")){
1418                 if (sscanf(value, "%30d", &x) == 1) {
1419                         vmu->saydurationm = x;
1420                 } else {
1421                         ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1422                 }
1423         } else if (!strcasecmp(var, "forcename")){
1424                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1425         } else if (!strcasecmp(var, "forcegreetings")){
1426                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1427         } else if (!strcasecmp(var, "callback")) {
1428                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1429         } else if (!strcasecmp(var, "dialout")) {
1430                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1431         } else if (!strcasecmp(var, "exitcontext")) {
1432                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1433         } else if (!strcasecmp(var, "minsecs")) {
1434                 if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1435                         vmu->minsecs = x;
1436                 } else {
1437                         ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1438                         vmu->minsecs = vmminsecs;
1439                 }
1440         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1441                 vmu->maxsecs = atoi(value);
1442                 if (vmu->maxsecs <= 0) {
1443                         ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1444                         vmu->maxsecs = vmmaxsecs;
1445                 } else {
1446                         vmu->maxsecs = atoi(value);
1447                 }
1448                 if (!strcasecmp(var, "maxmessage"))
1449                         ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
1450         } else if (!strcasecmp(var, "maxmsg")) {
1451                 vmu->maxmsg = atoi(value);
1452                 /* Accept maxmsg=0 (Greetings only voicemail) */
1453                 if (vmu->maxmsg < 0) {
1454                         ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1455                         vmu->maxmsg = MAXMSG;
1456                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
1457                         ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1458                         vmu->maxmsg = MAXMSGLIMIT;
1459                 }
1460         } else if (!strcasecmp(var, "nextaftercmd")) {
1461                 ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1462         } else if (!strcasecmp(var, "backupdeleted")) {
1463                 if (sscanf(value, "%30d", &x) == 1)
1464                         vmu->maxdeletedmsg = x;
1465                 else if (ast_true(value))
1466                         vmu->maxdeletedmsg = MAXMSG;
1467                 else
1468                         vmu->maxdeletedmsg = 0;
1469
1470                 if (vmu->maxdeletedmsg < 0) {
1471                         ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1472                         vmu->maxdeletedmsg = MAXMSG;
1473                 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1474                         ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1475                         vmu->maxdeletedmsg = MAXMSGLIMIT;
1476                 }
1477         } else if (!strcasecmp(var, "volgain")) {
1478                 sscanf(value, "%30lf", &vmu->volgain);
1479         } else if (!strcasecmp(var, "passwordlocation")) {
1480                 if (!strcasecmp(value, "spooldir")) {
1481                         vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
1482                 } else {
1483                         vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
1484                 }
1485         } else if (!strcasecmp(var, "options")) {
1486                 apply_options(vmu, value);
1487         }
1488 }
1489
1490 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1491 {
1492         int fds[2], pid = 0;
1493
1494         memset(buf, 0, len);
1495
1496         if (pipe(fds)) {
1497                 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1498         } else {
1499                 /* good to go*/
1500                 pid = ast_safe_fork(0);
1501
1502                 if (pid < 0) {
1503                         /* ok maybe not */
1504                         close(fds[0]);
1505                         close(fds[1]);
1506                         snprintf(buf, len, "FAILURE: Fork failed");
1507                 } else if (pid) {
1508                         /* parent */
1509                         close(fds[1]);
1510                         if (read(fds[0], buf, len) < 0) {
1511                                 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1512                         }
1513                         close(fds[0]);
1514                 } else {
1515                         /*  child */
1516                         AST_DECLARE_APP_ARGS(arg,
1517                                 AST_APP_ARG(v)[20];
1518                         );
1519                         char *mycmd = ast_strdupa(command);
1520
1521                         close(fds[0]);
1522                         dup2(fds[1], STDOUT_FILENO);
1523                         close(fds[1]);
1524                         ast_close_fds_above_n(STDOUT_FILENO);
1525
1526                         AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1527
1528                         execv(arg.v[0], arg.v);
1529                         printf("FAILURE: %s", strerror(errno));
1530                         _exit(0);
1531                 }
1532         }
1533         return buf;
1534 }
1535
1536 /*!
1537  * \brief Check that password meets minimum required length
1538  * \param vmu The voicemail user to change the password for.
1539  * \param password The password string to check
1540  *
1541  * \return zero on ok, 1 on not ok.
1542  */
1543 static int check_password(struct ast_vm_user *vmu, char *password)
1544 {
1545         /* check minimum length */
1546         if (strlen(password) < minpassword)
1547                 return 1;
1548         /* check that password does not contain '*' character */
1549         if (!ast_strlen_zero(password) && password[0] == '*')
1550                 return 1;
1551         if (!ast_strlen_zero(ext_pass_check_cmd)) {
1552                 char cmd[255], buf[255];
1553
1554                 ast_debug(1, "Verify password policies for %s\n", password);
1555
1556                 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1557                 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1558                         ast_debug(5, "Result: %s\n", buf);
1559                         if (!strncasecmp(buf, "VALID", 5)) {
1560                                 ast_debug(3, "Passed password check: '%s'\n", buf);
1561                                 return 0;
1562                         } else if (!strncasecmp(buf, "FAILURE", 7)) {
1563                                 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1564                                 return 0;
1565                         } else {
1566                                 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1567                                 return 1;
1568                         }
1569                 }
1570         }
1571         return 0;
1572 }
1573
1574 /*!
1575  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1576  * \param vmu The voicemail user to change the password for.
1577  * \param password The new value to be set to the password for this user.
1578  *
1579  * This only works if there is a realtime engine configured.
1580  * This is called from the (top level) vm_change_password.
1581  *
1582  * \return zero on success, -1 on error.
1583  */
1584 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1585 {
1586         int res = -1;
1587         if (!strcmp(vmu->password, password)) {
1588                 /* No change (but an update would return 0 rows updated, so we opt out here) */
1589                 return 0;
1590         }
1591
1592         if (strlen(password) > 10) {
1593                 ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1594         }
1595         if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1596                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1597                 ast_copy_string(vmu->password, password, sizeof(vmu->password));
1598                 res = 0;
1599         }
1600         return res;
1601 }
1602
1603 /*!
1604  * \brief Destructively Parse options and apply.
1605  */
1606 static void apply_options(struct ast_vm_user *vmu, const char *options)
1607 {
1608         char *stringp;
1609         char *s;
1610         char *var, *value;
1611         stringp = ast_strdupa(options);
1612         while ((s = strsep(&stringp, "|"))) {
1613                 value = s;
1614                 if ((var = strsep(&value, "=")) && value) {
1615                         apply_option(vmu, var, value);
1616                 }
1617         }
1618 }
1619
1620 /*!
1621  * \brief Loads the options specific to a voicemail user.
1622  *
1623  * This is called when a vm_user structure is being set up, such as from load_options.
1624  */
1625 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1626 {
1627         for (; var; var = var->next) {
1628                 if (!strcasecmp(var->name, "vmsecret")) {
1629                         ast_copy_string(retval->password, var->value, sizeof(retval->password));
1630                 } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1631                         if (ast_strlen_zero(retval->password)) {
1632                                 if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1633                                         ast_log(LOG_WARNING, "Invalid password detected for mailbox %s.  The password"
1634                                                 "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1635                                 } else {
1636                                         ast_copy_string(retval->password, var->value, sizeof(retval->password));
1637                                 }
1638                         }
1639                 } else if (!strcasecmp(var->name, "uniqueid")) {
1640                         ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1641                 } else if (!strcasecmp(var->name, "pager")) {
1642                         ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1643                 } else if (!strcasecmp(var->name, "email")) {
1644                         ast_free(retval->email);
1645                         retval->email = ast_strdup(var->value);
1646                 } else if (!strcasecmp(var->name, "fullname")) {
1647                         ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1648                 } else if (!strcasecmp(var->name, "context")) {
1649                         ast_copy_string(retval->context, var->value, sizeof(retval->context));
1650                 } else if (!strcasecmp(var->name, "emailsubject")) {
1651                         ast_free(retval->emailsubject);
1652                         retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1653                 } else if (!strcasecmp(var->name, "emailbody")) {
1654                         ast_free(retval->emailbody);
1655                         retval->emailbody = ast_strdup(substitute_escapes(var->value));
1656 #ifdef IMAP_STORAGE
1657                 } else if (!strcasecmp(var->name, "imapuser")) {
1658                         ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1659                         retval->imapversion = imapversion;
1660                 } else if (!strcasecmp(var->name, "imapserver")) {
1661                         ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1662                         retval->imapversion = imapversion;
1663                 } else if (!strcasecmp(var->name, "imapport")) {
1664                         ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1665                         retval->imapversion = imapversion;
1666                 } else if (!strcasecmp(var->name, "imapflags")) {
1667                         ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1668                         retval->imapversion = imapversion;
1669                 } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1670                         ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1671                         retval->imapversion = imapversion;
1672                 } else if (!strcasecmp(var->name, "imapfolder")) {
1673                         ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1674                         retval->imapversion = imapversion;
1675                 } else if (!strcasecmp(var->name, "imapvmshareid")) {
1676                         ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1677                         retval->imapversion = imapversion;
1678 #endif
1679                 } else
1680                         apply_option(retval, var->name, var->value);
1681         }
1682 }
1683
1684 /*!
1685  * \brief Determines if a DTMF key entered is valid.
1686  * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
1687  *
1688  * Tests the character entered against the set of valid DTMF characters.
1689  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1690  */
1691 static int is_valid_dtmf(const char *key)
1692 {
1693         int i;
1694         char *local_key = ast_strdupa(key);
1695
1696         for (i = 0; i < strlen(key); ++i) {
1697                 if (!strchr(VALID_DTMF, *local_key)) {
1698                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1699                         return 0;
1700                 }
1701                 local_key++;
1702         }
1703         return 1;
1704 }
1705
1706 /*!
1707  * \brief Finds a voicemail user from the realtime engine.
1708  * \param ivm
1709  * \param context
1710  * \param mailbox
1711  *
1712  * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
1713  *
1714  * \return The ast_vm_user structure for the user that was found.
1715  */
1716 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1717 {
1718         struct ast_variable *var;
1719         struct ast_vm_user *retval;
1720
1721         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1722                 if (ivm) {
1723                         memset(retval, 0, sizeof(*retval));
1724                 }
1725                 populate_defaults(retval);
1726                 if (!ivm) {
1727                         ast_set_flag(retval, VM_ALLOCED);
1728                 }
1729                 if (mailbox) {
1730                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1731                 }
1732                 if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1733                         var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1734                 } else {
1735                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1736                 }
1737                 if (var) {
1738                         apply_options_full(retval, var);
1739                         ast_variables_destroy(var);
1740                 } else {
1741                         if (!ivm)
1742                                 ast_free(retval);
1743                         retval = NULL;
1744                 }
1745         }
1746         return retval;
1747 }
1748
1749 /*!
1750  * \brief Finds a voicemail user from the users file or the realtime engine.
1751  * \param ivm
1752  * \param context
1753  * \param mailbox
1754  *
1755  * \return The ast_vm_user structure for the user that was found.
1756  */
1757 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1758 {
1759         /* This function could be made to generate one from a database, too */
1760         struct ast_vm_user *vmu = NULL, *cur;
1761         AST_LIST_LOCK(&users);
1762
1763         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1764                 context = "default";
1765
1766         AST_LIST_TRAVERSE(&users, cur, list) {
1767 #ifdef IMAP_STORAGE
1768                 if (cur->imapversion != imapversion) {
1769                         continue;
1770                 }
1771 #endif
1772                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1773                         break;
1774                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1775                         break;
1776         }
1777         if (cur) {
1778                 /* Make a copy, so that on a reload, we have no race */
1779                 if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
1780                         ast_free(vmu->email);
1781                         ast_free(vmu->emailbody);
1782                         ast_free(vmu->emailsubject);
1783                         *vmu = *cur;
1784                         vmu->email = ast_strdup(cur->email);
1785                         vmu->emailbody = ast_strdup(cur->emailbody);
1786                         vmu->emailsubject = ast_strdup(cur->emailsubject);
1787                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1788                         AST_LIST_NEXT(vmu, list) = NULL;
1789                 }
1790         }
1791         AST_LIST_UNLOCK(&users);
1792         if (!vmu) {
1793                 vmu = find_user_realtime(ivm, context, mailbox);
1794         }
1795         if (!vmu && !ast_strlen_zero(aliasescontext)) {
1796                 struct alias_mailbox_mapping *mapping;
1797                 char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
1798
1799                 snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
1800                         mailbox,
1801                         ast_strlen_zero(context) ? "" : "@",
1802                         S_OR(context, ""));
1803
1804                 mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
1805                 if (mapping) {
1806                         char *search_mailbox = NULL;
1807                         char *search_context = NULL;
1808
1809                         separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
1810                         ao2_ref(mapping, -1);
1811                         vmu = find_user(ivm, search_mailbox, search_context);
1812                 }
1813         }
1814
1815         return vmu;
1816 }
1817
1818 /*!
1819  * \brief Resets a user password to a specified password.
1820  * \param context
1821  * \param mailbox
1822  * \param newpass
1823  *
1824  * This does the actual change password work, called by the vm_change_password() function.
1825  *
1826  * \return zero on success, -1 on error.
1827  */
1828 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1829 {
1830         /* This function could be made to generate one from a database, too */
1831         struct ast_vm_user *cur;
1832         int res = -1;
1833         AST_LIST_LOCK(&users);
1834         AST_LIST_TRAVERSE(&users, cur, list) {
1835                 if ((!context || !strcasecmp(context, cur->context)) &&
1836                         (!strcasecmp(mailbox, cur->mailbox)))
1837                                 break;
1838         }
1839         if (cur) {
1840                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1841                 res = 0;
1842         }
1843         AST_LIST_UNLOCK(&users);
1844         return res;
1845 }
1846
1847 /*!
1848  * \brief Check if configuration file is valid
1849  */
1850 static inline int valid_config(const struct ast_config *cfg)
1851 {
1852         return cfg && cfg != CONFIG_STATUS_FILEINVALID;
1853 }
1854
1855 /*!
1856  * \brief The handler for the change password option.
1857  * \param vmu The voicemail user to work with.
1858  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1859  * This is called when a new user logs in for the first time and the option to force them to change their password is set.
1860  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1861  */
1862 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1863 {
1864         struct ast_config   *cfg = NULL;
1865         struct ast_variable *var = NULL;
1866         struct ast_category *cat = NULL;
1867         char *category = NULL, *value = NULL, *new = NULL;
1868         const char *tmp = NULL;
1869         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1870         char secretfn[PATH_MAX] = "";
1871         int found = 0;
1872
1873         if (!change_password_realtime(vmu, newpassword))
1874                 return;
1875
1876         /* check if we should store the secret in the spool directory next to the messages */
1877         switch (vmu->passwordlocation) {
1878         case OPT_PWLOC_SPOOLDIR:
1879                 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1880                 if (write_password_to_file(secretfn, newpassword) == 0) {
1881                         ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1882                         ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1883                         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1884                         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1885                         break;
1886                 } else {
1887                         ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1888                 }
1889                 /* Fall-through */
1890         case OPT_PWLOC_VOICEMAILCONF:
1891                 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
1892                         while ((category = ast_category_browse(cfg, category))) {
1893                                 if (!strcasecmp(category, vmu->context)) {
1894                                         if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1895                                                 ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1896                                                 break;
1897                                         }
1898                                         value = strstr(tmp, ",");
1899                                         if (!value) {
1900                                                 new = ast_alloca(strlen(newpassword)+1);
1901                                                 sprintf(new, "%s", newpassword);
1902                                         } else {
1903                                                 new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
1904                                                 sprintf(new, "%s%s", newpassword, value);
1905                                         }
1906                                         if (!(cat = ast_category_get(cfg, category, NULL))) {
1907                                                 ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1908                                                 break;
1909                                         }
1910                                         ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1911                                         found = 1;
1912                                 }
1913                         }
1914                         /* save the results */
1915                         if (found) {
1916                                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1917                                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1918                                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1919                                 ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
1920                                 ast_config_destroy(cfg);
1921                                 break;
1922                         }
1923
1924                         ast_config_destroy(cfg);
1925                 }
1926                 /* Fall-through */
1927         case OPT_PWLOC_USERSCONF:
1928                 /* check users.conf and update the password stored for the mailbox */
1929                 /* if no vmsecret entry exists create one. */
1930                 if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
1931                         ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1932                         for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1933                                 ast_debug(4, "users.conf: %s\n", category);
1934                                 if (!strcasecmp(category, vmu->mailbox)) {
1935                                         if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1936                                                 ast_debug(3, "looks like we need to make vmsecret!\n");
1937                                                 var = ast_variable_new("vmsecret", newpassword, "");
1938                                         } else {
1939                                                 var = NULL;
1940                                         }
1941                                         new = ast_alloca(strlen(newpassword) + 1);
1942                                         sprintf(new, "%s", newpassword);
1943                                         if (!(cat = ast_category_get(cfg, category, NULL))) {
1944                                                 ast_debug(4, "failed to get category!\n");
1945                                                 ast_free(var);
1946                                                 break;
1947                                         }
1948                                         if (!var) {
1949                                                 ast_variable_update(cat, "vmsecret", new, NULL, 0);
1950                                         } else {
1951                                                 ast_variable_append(cat, var);
1952                                         }
1953                                         found = 1;
1954                                         break;
1955                                 }
1956                         }
1957                         /* save the results and clean things up */
1958                         if (found) {
1959                                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1960                                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1961                                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1962                                 ast_config_text_file_save("users.conf", cfg, "app_voicemail");
1963                         }
1964
1965                         ast_config_destroy(cfg);
1966                 }
1967         }
1968 }
1969
1970 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1971 {
1972         char buf[255];
1973         snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1974         ast_debug(1, "External password: %s\n",buf);
1975         if (!ast_safe_system(buf)) {
1976                 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1977                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1978                 /* Reset the password in memory, too */
1979                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1980         }
1981 }
1982
1983 /*!
1984  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1985  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1986  * \param len The length of the path string that was written out.
1987  * \param context
1988  * \param ext
1989  * \param folder
1990  *
1991  * The path is constructed as
1992  *      VM_SPOOL_DIRcontext/ext/folder
1993  *
1994  * \return zero on success, -1 on error.
1995  */
1996 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1997 {
1998         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1999 }
2000
2001 /*!
2002  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2003  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2004  * \param len The length of the path string that was written out.
2005  * \param dir
2006  * \param num
2007  *
2008  * The path is constructed as
2009  *      VM_SPOOL_DIRcontext/ext/folder
2010  *
2011  * \return zero on success, -1 on error.
2012  */
2013 static int make_file(char *dest, const int len, const char *dir, const int num)
2014 {
2015         return snprintf(dest, len, "%s/msg%04d", dir, num);
2016 }
2017
2018 /* same as mkstemp, but return a FILE * */
2019 static FILE *vm_mkftemp(char *template)
2020 {
2021         FILE *p = NULL;
2022         int pfd = mkstemp(template);
2023         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
2024         if (pfd > -1) {
2025                 p = fdopen(pfd, "w+");
2026                 if (!p) {
2027                         close(pfd);
2028                         pfd = -1;
2029                 }
2030         }
2031         return p;
2032 }
2033
2034 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
2035  * \param dest    String. base directory.
2036  * \param len     Length of dest.
2037  * \param context String. Ignored if is null or empty string.
2038  * \param ext     String. Ignored if is null or empty string.
2039  * \param folder  String. Ignored if is null or empty string.
2040  * \return -1 on failure, 0 on success.
2041  */
2042 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
2043 {
2044         mode_t  mode = VOICEMAIL_DIR_MODE;
2045         int res;
2046
2047         make_dir(dest, len, context, ext, folder);
2048         if ((res = ast_mkdir(dest, mode))) {
2049                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
2050                 return -1;
2051         }
2052         return 0;
2053 }
2054
2055 static const char *mbox(struct ast_vm_user *vmu, int id)
2056 {
2057 #ifdef IMAP_STORAGE
2058         if (vmu && id == 0) {
2059                 return vmu->imapfolder;
2060         }
2061 #endif
2062         return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
2063 }
2064
2065 static const char *vm_index_to_foldername(int id)
2066 {
2067         return mbox(NULL, id);
2068 }
2069
2070
2071 static int get_folder_by_name(const char *name)
2072 {
2073         size_t i;
2074
2075         for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
2076                 if (strcasecmp(name, mailbox_folders[i]) == 0) {
2077                         return i;
2078                 }
2079         }
2080
2081         return -1;
2082 }
2083
2084 static void free_user(struct ast_vm_user *vmu)
2085 {
2086         if (!vmu) {
2087                 return;
2088         }
2089
2090         ast_free(vmu->email);
2091         vmu->email = NULL;
2092         ast_free(vmu->emailbody);
2093         vmu->emailbody = NULL;
2094         ast_free(vmu->emailsubject);
2095         vmu->emailsubject = NULL;
2096
2097         if (ast_test_flag(vmu, VM_ALLOCED)) {
2098                 ast_free(vmu);
2099         }
2100 }
2101
2102 static void free_user_final(struct ast_vm_user *vmu)
2103 {
2104         if (!vmu) {
2105                 return;
2106         }
2107
2108         if (!ast_strlen_zero(vmu->mailbox)) {
2109                 ast_delete_mwi_state_full(vmu->mailbox, vmu->context, NULL);
2110         }
2111
2112         free_user(vmu);
2113 }
2114
2115 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2116
2117         int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2118
2119         /* remove old allocation */
2120         if (vms->deleted) {
2121                 ast_free(vms->deleted);
2122                 vms->deleted = NULL;
2123         }
2124         if (vms->heard) {
2125                 ast_free(vms->heard);
2126                 vms->heard = NULL;
2127         }
2128         vms->dh_arraysize = 0;
2129
2130         if (arraysize > 0) {
2131                 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2132                         return -1;
2133                 }
2134                 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2135                         ast_free(vms->deleted);
2136                         vms->deleted = NULL;
2137                         return -1;
2138                 }
2139                 vms->dh_arraysize = arraysize;
2140         }
2141
2142         return 0;
2143 }
2144
2145 /* All IMAP-specific functions should go in this block. This
2146  * keeps them from being spread out all over the code */
2147 #ifdef IMAP_STORAGE
2148 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2149 {
2150         char arg[10];
2151         struct vm_state *vms;
2152         unsigned long messageNum;
2153
2154         /* If greetings aren't stored in IMAP, just delete the file */
2155         if (msgnum < 0 && !imapgreetings) {
2156                 ast_filedelete(file, NULL);
2157                 return;
2158         }
2159
2160         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2161                 ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
2162                 return;
2163         }
2164
2165         if (msgnum < 0) {
2166                 imap_delete_old_greeting(file, vms);
2167                 return;
2168         }
2169
2170         /* find real message number based on msgnum */
2171         /* this may be an index into vms->msgArray based on the msgnum. */
2172         messageNum = vms->msgArray[msgnum];
2173         if (messageNum == 0) {
2174                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2175                 return;
2176         }
2177         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2178         /* delete message */
2179         snprintf (arg, sizeof(arg), "%lu", messageNum);
2180         ast_mutex_lock(&vms->lock);
2181         mail_setflag (vms->mailstream, arg, "\\DELETED");
2182         mail_expunge(vms->mailstream);
2183         ast_mutex_unlock(&vms->lock);
2184 }
2185
2186 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
2187 {
2188         struct ast_channel *chan;
2189         char *cid;
2190         char *cid_name;
2191         char *cid_num;
2192         struct vm_state *vms;
2193         const char *duration_str;
2194         int duration = 0;
2195
2196         /*
2197          * First, get things initially set up. If any of this fails, then
2198          * back out before doing anything substantial
2199          */
2200         vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2201         if (!vms) {
2202                 return;
2203         }
2204
2205         if (open_mailbox(vms, vmu, folder)) {
2206                 return;
2207         }
2208
2209         chan = ast_dummy_channel_alloc();
2210         if (!chan) {
2211                 close_mailbox(vms, vmu);
2212                 return;
2213         }
2214
2215         /*
2216          * We need to make sure the new message we save has the same
2217          * callerid, flag, and duration as the original message
2218          */
2219         cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2220
2221         if (!ast_strlen_zero(cid)) {
2222                 ast_callerid_parse(cid, &cid_name, &cid_num);
2223                 ast_party_caller_init(ast_channel_caller(chan));
2224                 if (!ast_strlen_zero(cid_name)) {
2225                         ast_channel_caller(chan)->id.name.valid = 1;
2226                         ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2227                 }
2228                 if (!ast_strlen_zero(cid_num)) {
2229                         ast_channel_caller(chan)->id.number.valid = 1;
2230                         ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2231                 }
2232         }
2233
2234         duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2235
2236         if (!ast_strlen_zero(duration_str)) {
2237                 sscanf(duration_str, "%30d", &duration);
2238         }
2239
2240         /*
2241          * IMAP messages cannot be altered once delivered. So we have to delete the
2242          * current message and then re-add it with the updated message ID.
2243          *
2244          * Furthermore, there currently is no atomic way to create a new message and to
2245          * store it in an arbitrary folder. So we have to save it to the INBOX and then
2246          * move to the appropriate folder.
2247          */
2248         if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2249                         duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2250                 if (folder != NEW_FOLDER) {
2251                         save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2252                 }
2253                 vm_imap_delete(dir, msgnum, vmu);
2254         }
2255         close_mailbox(vms, vmu);
2256         ast_channel_unref(chan);
2257 }
2258
2259 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2260 {
2261         struct vm_state *vms_p;
2262         char *file, *filename;
2263         char dest[PATH_MAX];
2264         char *attachment;
2265         int i;
2266         BODY *body;
2267         int ret = 0;
2268         int curr_mbox;
2269
2270         /* This function is only used for retrieval of IMAP greetings
2271          * regular messages are not retrieved this way, nor are greetings
2272          * if they are stored locally*/
2273         if (msgnum > -1 || !imapgreetings) {
2274                 return 0;
2275         } else {
2276                 file = strrchr(ast_strdupa(dir), '/');
2277                 if (file)
2278                         *file++ = '\0';
2279                 else {
2280                         ast_debug(1, "Failed to procure file name from directory passed.\n");
2281                         return -1;
2282                 }
2283         }
2284
2285         /* check if someone is accessing this box right now... */
2286         if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2287                 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2288                 /* Unlike when retrieving a message, it is reasonable not to be able to find a
2289                 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2290                 * that's all we need to do.
2291                 */
2292                 if (!(vms_p = create_vm_state_from_user(vmu))) {
2293                         ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2294                         return -1;
2295                 }
2296         }
2297
2298         /* Greetings will never have a prepended message */
2299         *vms_p->introfn = '\0';
2300
2301         ast_mutex_lock(&vms_p->lock);
2302
2303         /* get the current mailbox so that we can point the mailstream back to it later */
2304         curr_mbox = get_folder_by_name(vms_p->curbox);
2305
2306         if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2307                 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2308                 ast_mutex_unlock(&vms_p->lock);
2309                 return -1;
2310         }
2311
2312         /*XXX Yuck, this could probably be done a lot better */
2313         for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2314                 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2315                 /* We have the body, now we extract the file name of the first attachment. */
2316                 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2317                         attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
2318                 } else {
2319                         ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2320                         ret = -1;
2321                         break;
2322                 }
2323                 filename = strsep(&attachment, ".");
2324                 if (!strcmp(filename, file)) {
2325                         ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2326                         vms_p->msgArray[vms_p->curmsg] = i + 1;
2327                         create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2328                         save_body(body, vms_p, "2", attachment, 0);
2329                         ret = 0;
2330                         break;
2331                 }
2332         }
2333
2334         if (curr_mbox != -1) {
2335                 /* restore previous mbox stream */
2336                 if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2337                         ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2338                         ret = -1;
2339                 }
2340         }
2341         ast_mutex_unlock(&vms_p->lock);
2342         return ret;
2343 }
2344
2345 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2346 {
2347         BODY *body;
2348         char *header_content;
2349         char *attachedfilefmt;
2350         char buf[80];
2351         struct vm_state *vms;
2352         char text_file[PATH_MAX];
2353         FILE *text_file_ptr;
2354         int res = 0;
2355         struct ast_vm_user *vmu;
2356         int curr_mbox;
2357
2358         if (!(vmu = find_user(NULL, context, mailbox))) {
2359                 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2360                 return -1;
2361         }
2362
2363         if (msgnum < 0) {
2364                 if (imapgreetings) {
2365                         res = imap_retrieve_greeting(dir, msgnum, vmu);
2366                         goto exit;
2367                 } else {
2368                         res = 0;
2369                         goto exit;
2370                 }
2371         }
2372
2373         /* Before anything can happen, we need a vm_state so that we can
2374          * actually access the imap server through the vms->mailstream
2375          */
2376         if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2377                 /* This should not happen. If it does, then I guess we'd
2378                  * need to create the vm_state, extract which mailbox to
2379                  * open, and then set up the msgArray so that the correct
2380                  * IMAP message could be accessed. If I have seen correctly
2381                  * though, the vms should be obtainable from the vmstates list
2382                  * and should have its msgArray properly set up.
2383                  */
2384                 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2385                 res = -1;
2386                 goto exit;
2387         }
2388
2389         /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2390         curr_mbox = get_folder_by_name(vms->curbox);
2391         if (curr_mbox < 0) {
2392                 ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2393                 curr_mbox = 0;
2394         }
2395         init_mailstream(vms, curr_mbox);
2396         if (!vms->mailstream) {
2397                 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2398                 res = -1;
2399                 goto exit;
2400         }
2401
2402         make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2403         snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2404
2405         /* Don't try to retrieve a message from IMAP if it already is on the file system */
2406         if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2407                 res = 0;
2408                 goto exit;
2409         }
2410
2411         ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2412         if (vms->msgArray[msgnum] == 0) {
2413                 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2414                 res = -1;
2415                 goto exit;
2416         }
2417
2418         /* This will only work for new messages... */
2419         ast_mutex_lock(&vms->lock);
2420         header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2421         ast_mutex_unlock(&vms->lock);
2422         /* empty string means no valid header */
2423         if (ast_strlen_zero(header_content)) {
2424                 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2425                 res = -1;
2426                 goto exit;
2427         }
2428
2429         ast_mutex_lock(&vms->lock);
2430         mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2431         ast_mutex_unlock(&vms->lock);
2432
2433         /* We have the body, now we extract the file name of the first attachment. */
2434         if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2435                 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2436         } else {
2437                 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2438                 res = -1;
2439                 goto exit;
2440         }
2441
2442         /* Find the format of the attached file */
2443
2444         strsep(&attachedfilefmt, ".");
2445         if (!attachedfilefmt) {
2446                 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2447                 res = -1;
2448                 goto exit;
2449         }
2450
2451         save_body(body, vms, "2", attachedfilefmt, 0);
2452         if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2453                 *vms->introfn = '\0';
2454         }
2455
2456         /* Get info from headers!! */
2457         snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2458
2459         if (!(text_file_ptr = fopen(text_file, "w"))) {
2460                 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2461                 goto exit;
2462         }
2463
2464         fprintf(text_file_ptr, "%s\n", "[message]");
2465
2466         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2467                 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2468         }
2469         if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2470                 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2471         }
2472         if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2473                 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2474         }
2475         if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2476                 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2477         }
2478         if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2479                 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2480         }
2481         if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2482                 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2483         }
2484         if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2485                 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2486         }
2487         if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2488                 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2489         }
2490         fclose(text_file_ptr);
2491
2492 exit:
2493         free_user(vmu);
2494         return res;
2495 }
2496
2497 static int folder_int(const char *folder)
2498 {
2499         /*assume a NULL folder means INBOX*/
2500         if (!folder) {
2501                 return 0;
2502         }
2503         if (!strcasecmp(folder, imapfolder)) {
2504                 return 0;
2505         } else if (!strcasecmp(folder, "Old")) {
2506                 return 1;
2507         } else if (!strcasecmp(folder, "Work")) {
2508                 return 2;
2509         } else if (!strcasecmp(folder, "Family")) {
2510                 return 3;
2511         } else if (!strcasecmp(folder, "Friends")) {
2512                 return 4;
2513         } else if (!strcasecmp(folder, "Cust1")) {
2514                 return 5;
2515         } else if (!strcasecmp(folder, "Cust2")) {
2516                 return 6;
2517         } else if (!strcasecmp(folder, "Cust3")) {
2518                 return 7;
2519         } else if (!strcasecmp(folder, "Cust4")) {
2520                 return 8;
2521         } else if (!strcasecmp(folder, "Cust5")) {
2522                 return 9;
2523         } else if (!strcasecmp(folder, "Urgent")) {
2524                 return 11;
2525         } else { /*assume they meant INBOX if folder is not found otherwise*/
2526                 return 0;
2527         }
2528 }
2529
2530 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2531 {
2532         SEARCHPGM *pgm;
2533         SEARCHHEADER *hdr;
2534
2535         struct ast_vm_user *vmu, vmus;
2536         struct vm_state *vms_p;
2537         int ret = 0;
2538         int fold = folder_int(folder);
2539         int urgent = 0;
2540
2541         /* If URGENT, then look at INBOX */
2542         if (fold == 11) {
2543                 fold = NEW_FOLDER;
2544                 urgent = 1;
2545         }
2546
2547         if (ast_strlen_zero(mailbox))
2548                 return 0;
2549
2550         /* We have to get the user before we can open the stream! */
2551         memset(&vmus, 0, sizeof(vmus));
2552         vmu = find_user(&vmus, context, mailbox);
2553         if (!vmu) {
2554                 ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2555                 free_user(vmu);
2556                 return -1;
2557         } else {
2558                 /* No IMAP account available */
2559                 if (vmu->imapuser[0] == '\0') {
2560                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2561                         free_user(vmu);
2562                         return -1;
2563                 }
2564         }
2565
2566         /* No IMAP account available */
2567         if (vmu->imapuser[0] == '\0') {
2568                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2569                 free_user(vmu);
2570                 return -1;
2571         }
2572
2573         /* check if someone is accessing this box right now... */
2574         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2575         if (!vms_p) {
2576                 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2577         }
2578         if (vms_p) {
2579                 ast_debug(3, "Returning before search - user is logged in\n");
2580                 if (fold == 0) { /* INBOX */
2581                         free_user(vmu);
2582                         return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2583                 }
2584                 if (fold == 1) { /* Old messages */
2585                         free_user(vmu);
2586                         return vms_p->oldmessages;
2587                 }
2588         }
2589
2590         /* add one if not there... */
2591         vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2592         if (!vms_p) {
2593                 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2594         }
2595
2596         if (!vms_p) {
2597                 vms_p = create_vm_state_from_user(vmu);
2598         }
2599         ret = init_mailstream(vms_p, fold);
2600         if (!vms_p->mailstream) {
2601                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2602                 free_user(vmu);
2603                 return -1;
2604         }
2605         if (ret == 0) {
2606                 ast_mutex_lock(&vms_p->lock);
2607                 pgm = mail_newsearchpgm ();
2608                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2609                 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2610                 pgm->header = hdr;
2611                 if (fold != OLD_FOLDER) {
2612                         pgm->unseen = 1;
2613                         pgm->seen = 0;
2614                 }
2615                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2616                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2617                  */
2618                 else {
2619                         pgm->unseen = 0;
2620                         pgm->seen = 1;
2621                 }
2622                 /* look for urgent messages */
2623                 if (fold == NEW_FOLDER) {
2624                         if (urgent) {
2625                                 pgm->flagged = 1;
2626                                 pgm->unflagged = 0;
2627                         } else {
2628                                 pgm->flagged = 0;
2629                                 pgm->unflagged = 1;
2630                         }
2631                 }
2632                 pgm->undeleted = 1;
2633                 pgm->deleted = 0;
2634
2635                 vms_p->vmArrayIndex = 0;
2636                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2637                 if (fold == 0 && urgent == 0)
2638                         vms_p->newmessages = vms_p->vmArrayIndex;
2639                 if (fold == 1)
2640                         vms_p->oldmessages = vms_p->vmArrayIndex;
2641                 if (fold == 0 && urgent == 1)
2642                         vms_p->urgentmessages = vms_p->vmArrayIndex;
2643                 /*Freeing the searchpgm also frees the searchhdr*/
2644                 mail_free_searchpgm(&pgm);
2645                 ast_mutex_unlock(&vms_p->lock);
2646                 free_user(vmu);
2647                 vms_p->updated = 0;
2648                 return vms_p->vmArrayIndex;
2649         } else {
2650                 ast_mutex_lock(&vms_p->lock);
2651                 mail_ping(vms_p->mailstream);
2652                 ast_mutex_unlock(&vms_p->lock);
2653         }
2654         free_user(vmu);
2655         return 0;
2656 }
2657
2658 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2659 {
2660         /* Check if mailbox is full */
2661         check_quota(vms, vmu->imapfolder);
2662         if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2663                 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2664                 if (chan) {
2665                         ast_play_and_wait(chan, "vm-mailboxfull");
2666                 }
2667                 return -1;
2668         }
2669
2670         /* Check if we have exceeded maxmsg */
2671         ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
2672         if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2673                 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2674                 if (chan) {
2675                         ast_play_and_wait(chan, "vm-mailboxfull");
2676                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2677                 }
2678                 return -1;
2679         }
2680
2681         return 0;
2682 }
2683
2684 /*!
2685  * \brief Gets the number of messages that exist in a mailbox folder.
2686  * \param mailbox_id
2687  * \param folder
2688  *
2689  * This method is used when IMAP backend is used.
2690  * \return The number of messages in this mailbox folder (zero or more).
2691  */
2692 static int messagecount(const char *mailbox_id, const char *folder)
2693 {
2694         char *context;
2695         char *mailbox;
2696
2697         if (ast_strlen_zero(mailbox_id)
2698                 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2699                 return 0;
2700         }
2701
2702         if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2703                 return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2704         } else {
2705                 return __messagecount(context, mailbox, folder);
2706         }
2707 }
2708
2709 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id)
2710 {
2711         char *myserveremail = serveremail;
2712         char fn[PATH_MAX];
2713         char introfn[PATH_MAX];
2714         char mailbox[256];
2715         char *stringp;
2716         FILE *p = NULL;
2717         char tmp[80] = "/tmp/astmail-XXXXXX";
2718         long len;
2719         void *buf;
2720         int tempcopy = 0;
2721         STRING str;
2722         int ret; /* for better error checking */
2723         char *imap_flags = NIL;
2724         int msgcount;
2725         int box = NEW_FOLDER;
2726
2727         snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2728         msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2729
2730         /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2731         if (msgnum < 0) {
2732                 if(!imapgreetings) {
2733                         return 0;
2734                 } else {
2735                         box = GREETINGS_FOLDER;
2736                 }
2737         }
2738
2739         if (imap_check_limits(chan, vms, vmu, msgcount)) {
2740                 return -1;
2741         }
2742
2743         /* Set urgent flag for IMAP message */
2744         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2745                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2746                 imap_flags = "\\FLAGGED";
2747         }
2748
2749         /* Attach only the first format */
2750         fmt = ast_strdupa(fmt);
2751         stringp = fmt;
2752         strsep(&stringp, "|");
2753
2754         if (!ast_strlen_zero(vmu->serveremail))
2755                 myserveremail = vmu->serveremail;
2756
2757         if (msgnum > -1)
2758                 make_file(fn, sizeof(fn), dir, msgnum);
2759         else
2760                 ast_copy_string (fn, dir, sizeof(fn));
2761
2762         snprintf(introfn, sizeof(introfn), "%sintro", fn);
2763         if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2764                 *introfn = '\0';
2765         }
2766
2767         if (ast_strlen_zero(vmu->email)) {
2768                 /* We need the vmu->email to be set when we call make_email_file, but
2769                  * if we keep it set, a duplicate e-mail will be created. So at the end
2770                  * of this function, we will revert back to an empty string if tempcopy
2771                  * is 1.
2772                  */
2773                 vmu->email = ast_strdup(vmu->imapuser);
2774                 tempcopy = 1;
2775         }
2776
2777         if (!strcmp(fmt, "wav49"))
2778                 fmt = "WAV";
2779         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2780
2781         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2782            command hangs. */
2783         if (!(p = vm_mkftemp(tmp))) {
2784                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2785                 if (tempcopy) {
2786                         ast_free(vmu->email);
2787                         vmu->email = NULL;
2788                 }
2789                 return -1;
2790         }
2791
2792         if (msgnum < 0 && imapgreetings) {
2793                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2794                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2795                         return -1;
2796                 }
2797                 imap_delete_old_greeting(fn, vms);
2798         }
2799
2800         make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2801                 chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2802                 chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2803                 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2804         /* read mail file to memory */
2805         len = ftell(p);
2806         rewind(p);
2807         if (!(buf = ast_malloc(len + 1))) {
2808                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2809                 fclose(p);
2810                 if (tempcopy)
2811                         *(vmu->email) = '\0';
2812                 return -1;
2813         }
2814         if (fread(buf, 1, len, p) != len) {
2815                 if (ferror(p)) {
2816                         ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
2817                         return -1;
2818                 }
2819         }
2820         ((char *) buf)[len] = '\0';
2821         INIT(&str, mail_string, buf, len);
2822         ret = init_mailstream(vms, box);
2823         if (ret == 0) {
2824                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2825                 ast_mutex_lock(&vms->lock);
2826                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2827                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2828                 ast_mutex_unlock(&vms->lock);
2829                 fclose(p);
2830                 unlink(tmp);
2831                 ast_free(buf);
2832         } else {
2833                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2834                 fclose(p);
2835                 unlink(tmp);
2836                 ast_free(buf);
2837                 return -1;
2838         }
2839         ast_debug(3, "%s stored\n", fn);
2840
2841         if (tempcopy)
2842                 *(vmu->email) = '\0';
2843         inprocess_count(vmu->mailbox, vmu->context, -1);
2844         return 0;
2845
2846 }
2847
2848 /*!
2849  * \brief Gets the number of messages that exist in the inbox folder.
2850  * \param mailbox_context
2851  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2852  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2853  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2854  *
2855  * This method is used when IMAP backend is used.
2856  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2857  *
2858  * \return zero on success, -1 on error.
2859  */
2860
2861 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2862 {
2863         char tmp[PATH_MAX] = "";
2864         char *mailboxnc;
2865         char *context;
2866         char *mb;
2867         char *cur;
2868         if (newmsgs)
2869                 *newmsgs = 0;
2870         if (oldmsgs)
2871                 *oldmsgs = 0;
2872         if (urgentmsgs)
2873                 *urgentmsgs = 0;
2874
2875         ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2876         /* If no mailbox, return immediately */
2877         if (ast_strlen_zero(mailbox_context))
2878                 return 0;
2879
2880         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2881         context = strchr(tmp, '@');
2882         if (strchr(mailbox_context, ',')) {
2883                 int tmpnew, tmpold, tmpurgent;
2884                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2885                 mb = tmp;
2886                 while ((cur = strsep(&mb, ", "))) {
2887                         if (!ast_strlen_zero(cur)) {
2888                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2889                                         return -1;
2890                                 else {
2891                                         if (newmsgs)
2892                                                 *newmsgs += tmpnew;
2893                                         if (oldmsgs)
2894                                                 *oldmsgs += tmpold;
2895                                         if (urgentmsgs)
2896                                                 *urgentmsgs += tmpurgent;
2897                                 }
2898                         }
2899                 }
2900                 return 0;
2901         }
2902         if (context) {
2903                 *context = '\0';
2904                 mailboxnc = tmp;
2905                 context++;
2906         } else {
2907                 context = "default";
2908                 mailboxnc = (char *) mailbox_context;
2909         }
2910
2911         if (newmsgs) {
2912                 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2913                 if (!vmu) {
2914                         ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2915                         return -1;
2916                 }
2917                 if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2918                         free_user(vmu);
2919                         return -1;
2920                 }
2921                 free_user(vmu);
2922         }
2923         if (oldmsgs) {
2924                 if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2925                         return -1;
2926                 }
2927         }
2928         if (urgentmsgs) {
2929                 if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2930                         return -1;
2931                 }
2932         }
2933         return 0;
2934 }
2935
2936 /**
2937  * \brief Determines if the given folder has messages.
2938  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2939  * \param folder the folder to look in
2940  *
2941  * This function is used when the mailbox is stored in an IMAP back end.
2942  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2943  * \return 1 if the folder has one or more messages. zero otherwise.
2944  */
2945
2946 static int has_voicemail(const char *mailbox, const char *folder)
2947 {
2948         char tmp[256], *tmp2, *box, *context;
2949         ast_copy_string(tmp, mailbox, sizeof(tmp));
2950         tmp2 = tmp;
2951         if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2952                 while ((box = strsep(&tmp2, ",&"))) {
2953                         if (!ast_strlen_zero(box)) {
2954                                 if (has_voicemail(box, folder)) {
2955                                         return 1;
2956                                 }
2957                         }
2958                 }
2959         }
2960         if ((context = strchr(tmp, '@'))) {
2961                 *context++ = '\0';
2962         } else {
2963                 context = "default";
2964         }
2965         return __messagecount(context, tmp, folder) ? 1 : 0;
2966 }
2967
2968 /*!
2969  * \brief Copies a message from one mailbox to another.
2970  * \param chan
2971  * \param vmu
2972  * \param imbox
2973  * \param msgnum
2974  * \param duration
2975  * \param recip
2976  * \param fmt
2977  * \param dir
2978  *
2979  * This works with IMAP storage based mailboxes.
2980  *
2981  * \return zero on success, -1 on error.
2982  */
2983 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag, const char *dest_folder)
2984 {
2985         struct vm_state *sendvms = NULL;
2986         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2987         if (msgnum >= recip->maxmsg) {
2988                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2989                 return -1;
2990         }
2991         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2992                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2993                 return -1;
2994         }
2995         if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
2996                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2997                 return -1;
2998         }
2999         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3000         ast_mutex_lock(&sendvms->lock);
3001         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
3002                 ast_mutex_unlock(&sendvms->lock);
3003                 return 0;
3004         }
3005         ast_mutex_unlock(&sendvms->lock);
3006         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3007         return -1;
3008 }
3009
3010 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
3011 {
3012         char tmp[256], *t = tmp;
3013         size_t left = sizeof(tmp);
3014
3015         if (box == OLD_FOLDER) {
3016                 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
3017         } else {
3018                 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
3019         }
3020
3021         if (box == NEW_FOLDER) {
3022                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
3023         } else {
3024                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
3025         }
3026
3027         /* Build up server information */
3028         ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
3029
3030         /* Add authentication user if present */
3031         if (!ast_strlen_zero(authuser))
3032                 ast_build_string(&t, &left, "/authuser=%s", authuser);
3033
3034         /* Add flags if present */
3035         if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
3036                 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
3037         }
3038
3039         /* End with username */
3040 #if 1
3041         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
3042 #else
3043         ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
3044 #endif
3045         if (box == NEW_FOLDER || box == OLD_FOLDER)
3046                 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
3047         else if (box == GREETINGS_FOLDER)
3048                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
3049         else {  /* Other folders such as Friends, Family, etc... */
3050                 if (!ast_strlen_zero(imapparentfolder)) {
3051                         /* imapparentfolder would typically be set to INBOX */
3052                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
3053                 } else {
3054                         snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
3055                 }
3056         }
3057 }