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