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