Merged revisions 280161 via svnmerge from
[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",
2207                 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
2208                 S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
2209                 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
2210         /* read mail file to memory */
2211         len = ftell(p);
2212         rewind(p);
2213         if (!(buf = ast_malloc(len + 1))) {
2214                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2215                 fclose(p);
2216                 if (tempcopy)
2217                         *(vmu->email) = '\0';
2218                 return -1;
2219         }
2220         if (fread(buf, len, 1, p) < len) {
2221                 if (ferror(p)) {
2222                         ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
2223                         return -1;
2224                 }
2225         }
2226         ((char *) buf)[len] = '\0';
2227         INIT(&str, mail_string, buf, len);
2228         ret = init_mailstream(vms, NEW_FOLDER);
2229         if (ret == 0) {
2230                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
2231                 ast_mutex_lock(&vms->lock);
2232                 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2233                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2234                 ast_mutex_unlock(&vms->lock);
2235                 fclose(p);
2236                 unlink(tmp);
2237                 ast_free(buf);
2238         } else {
2239                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2240                 fclose(p);
2241                 unlink(tmp);
2242                 ast_free(buf);
2243                 return -1;
2244         }
2245         ast_debug(3, "%s stored\n", fn);
2246         
2247         if (tempcopy)
2248                 *(vmu->email) = '\0';
2249         
2250         return 0;
2251
2252 }
2253
2254 /*!
2255  * \brief Gets the number of messages that exist in the inbox folder.
2256  * \param mailbox_context
2257  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2258  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2259  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2260  * 
2261  * This method is used when IMAP backend is used.
2262  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2263  *
2264  * \return zero on success, -1 on error.
2265  */
2266
2267 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2268 {
2269         char tmp[PATH_MAX] = "";
2270         char *mailboxnc;
2271         char *context;
2272         char *mb;
2273         char *cur;
2274         if (newmsgs)
2275                 *newmsgs = 0;
2276         if (oldmsgs)
2277                 *oldmsgs = 0;
2278         if (urgentmsgs)
2279                 *urgentmsgs = 0;
2280
2281         ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2282         /* If no mailbox, return immediately */
2283         if (ast_strlen_zero(mailbox_context))
2284                 return 0;
2285         
2286         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2287         context = strchr(tmp, '@');
2288         if (strchr(mailbox_context, ',')) {
2289                 int tmpnew, tmpold, tmpurgent;
2290                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2291                 mb = tmp;
2292                 while ((cur = strsep(&mb, ", "))) {
2293                         if (!ast_strlen_zero(cur)) {
2294                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2295                                         return -1;
2296                                 else {
2297                                         if (newmsgs)
2298                                                 *newmsgs += tmpnew; 
2299                                         if (oldmsgs)
2300                                                 *oldmsgs += tmpold;
2301                                         if (urgentmsgs)
2302                                                 *urgentmsgs += tmpurgent;
2303                                 }
2304                         }
2305                 }
2306                 return 0;
2307         }
2308         if (context) {
2309                 *context = '\0';
2310                 mailboxnc = tmp;
2311                 context++;
2312         } else {
2313                 context = "default";
2314                 mailboxnc = (char *) mailbox_context;
2315         }
2316
2317         if (newmsgs) {
2318                 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2319                 if (!vmu) {
2320                         ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2321                         return -1;
2322                 }
2323                 if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2324                         return -1;
2325                 }
2326         }
2327         if (oldmsgs) {
2328                 if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2329                         return -1;
2330                 }
2331         }
2332         if (urgentmsgs) {
2333                 if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2334                         return -1;
2335                 }
2336         }
2337         return 0;
2338 }
2339
2340 /** 
2341  * \brief Determines if the given folder has messages.
2342  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2343  * \param folder the folder to look in
2344  *
2345  * This function is used when the mailbox is stored in an IMAP back end.
2346  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2347  * \return 1 if the folder has one or more messages. zero otherwise.
2348  */
2349
2350 static int has_voicemail(const char *mailbox, const char *folder)
2351 {
2352         char tmp[256], *tmp2, *box, *context;
2353         ast_copy_string(tmp, mailbox, sizeof(tmp));
2354         tmp2 = tmp;
2355         if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2356                 while ((box = strsep(&tmp2, ",&"))) {
2357                         if (!ast_strlen_zero(box)) {
2358                                 if (has_voicemail(box, folder)) {
2359                                         return 1;
2360                                 }
2361                         }
2362                 }
2363         }
2364         if ((context = strchr(tmp, '@'))) {
2365                 *context++ = '\0';
2366         } else {
2367                 context = "default";
2368         }
2369         return __messagecount(context, tmp, folder) ? 1 : 0;
2370 }
2371
2372 /*!
2373  * \brief Copies a message from one mailbox to another.
2374  * \param chan
2375  * \param vmu
2376  * \param imbox
2377  * \param msgnum
2378  * \param duration
2379  * \param recip
2380  * \param fmt
2381  * \param dir
2382  *
2383  * This works with IMAP storage based mailboxes.
2384  *
2385  * \return zero on success, -1 on error.
2386  */
2387 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)
2388 {
2389         struct vm_state *sendvms = NULL, *destvms = NULL;
2390         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2391         if (msgnum >= recip->maxmsg) {
2392                 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2393                 return -1;
2394         }
2395         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2396                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2397                 return -1;
2398         }
2399         if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2400                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2401                 return -1;
2402         }
2403         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2404         ast_mutex_lock(&sendvms->lock);
2405         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2406                 ast_mutex_unlock(&sendvms->lock);
2407                 return 0;
2408         }
2409         ast_mutex_unlock(&sendvms->lock);
2410         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2411         return -1;
2412 }
2413
2414 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2415 {
2416         char tmp[256], *t = tmp;
2417         size_t left = sizeof(tmp);
2418         
2419         if (box == OLD_FOLDER) {
2420                 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2421         } else {
2422                 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2423         }
2424
2425         if (box == NEW_FOLDER) {
2426                 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2427         } else {
2428                 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2429         }
2430
2431         /* Build up server information */
2432         ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
2433
2434         /* Add authentication user if present */
2435         if (!ast_strlen_zero(authuser))
2436                 ast_build_string(&t, &left, "/authuser=%s", authuser);
2437
2438         /* Add flags if present */
2439         if (!ast_strlen_zero(imapflags))
2440                 ast_build_string(&t, &left, "/%s", imapflags);
2441
2442         /* End with username */
2443 #if 1
2444         ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2445 #else
2446         ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2447 #endif
2448         if (box == NEW_FOLDER || box == OLD_FOLDER)
2449                 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2450         else if (box == GREETINGS_FOLDER)
2451                 snprintf(spec, len, "%s%s", tmp, greetingfolder);
2452         else {  /* Other folders such as Friends, Family, etc... */
2453                 if (!ast_strlen_zero(imapparentfolder)) {
2454                         /* imapparentfolder would typically be set to INBOX */
2455                         snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2456                 } else {
2457                         snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2458                 }
2459         }
2460 }
2461
2462 static int init_mailstream(struct vm_state *vms, int box)
2463 {
2464         MAILSTREAM *stream = NIL;
2465         long debug;
2466         char tmp[256];
2467         
2468         if (!vms) {
2469                 ast_log(LOG_ERROR, "vm_state is NULL!\n");
2470                 return -1;
2471         }
2472         if (option_debug > 2)
2473                 ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
2474         if (vms->mailstream == NIL || !vms->mailstream) {
2475                 if (option_debug)
2476                         ast_log(LOG_DEBUG, "mailstream not set.\n");
2477         } else {
2478                 stream = vms->mailstream;
2479         }
2480         /* debug = T;  user wants protocol telemetry? */
2481         debug = NIL;  /* NO protocol telemetry? */
2482
2483         if (delimiter == '\0') {                /* did not probe the server yet */
2484                 char *cp;
2485 #ifdef USE_SYSTEM_IMAP
2486 #include <imap/linkage.c>
2487 #elif defined(USE_SYSTEM_CCLIENT)
2488 #include <c-client/linkage.c>
2489 #else
2490 #include "linkage.c"
2491 #endif
2492                 /* Connect to INBOX first to get folders delimiter */
2493                 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2494                 ast_mutex_lock(&vms->lock);
2495                 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2496                 ast_mutex_unlock(&vms->lock);
2497                 if (stream == NIL) {
2498                         ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2499                         return -1;
2500                 }
2501                 get_mailbox_delimiter(stream);
2502                 /* update delimiter in imapfolder */
2503                 for (cp = vms->imapfolder; *cp; cp++)
2504                         if (*cp == '/')
2505                                 *cp = delimiter;
2506         }
2507         /* Now connect to the target folder */
2508         imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2509         if (option_debug > 2)
2510                 ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
2511         ast_mutex_lock(&vms->lock);
2512         vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2513         ast_mutex_unlock(&vms->lock);
2514         if (vms->mailstream == NIL) {
2515                 return -1;
2516         } else {
2517                 return 0;
2518         }
2519 }
2520
2521 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2522 {
2523         SEARCHPGM *pgm;
2524         SEARCHHEADER *hdr;
2525         int ret, urgent = 0;
2526
2527         /* If Urgent, then look at INBOX */
2528         if (box == 11) {
2529                 box = NEW_FOLDER;
2530                 urgent = 1;
2531         }
2532
2533         ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2534         ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
2535         vms->imapversion = vmu->imapversion;
2536         ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
2537
2538         if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2539                 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2540                 return -1;
2541         }
2542         
2543         create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2544         
2545         /* Check Quota */
2546         if  (box == 0)  {
2547                 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
2548                 check_quota(vms, (char *) mbox(vmu, box));
2549         }
2550
2551         ast_mutex_lock(&vms->lock);
2552         pgm = mail_newsearchpgm();
2553
2554         /* Check IMAP folder for Asterisk messages only... */
2555         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2556         hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2557         pgm->header = hdr;
2558         pgm->deleted = 0;
2559         pgm->undeleted = 1;
2560
2561         /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2562         if (box == NEW_FOLDER && urgent == 1) {
2563                 pgm->unseen = 1;
2564                 pgm->seen = 0;
2565                 pgm->flagged = 1;
2566                 pgm->unflagged = 0;
2567         } else if (box == NEW_FOLDER && urgent == 0) {
2568                 pgm->unseen = 1;
2569                 pgm->seen = 0;
2570                 pgm->flagged = 0;
2571                 pgm->unflagged = 1;
2572         } else if (box == OLD_FOLDER) {
2573                 pgm->seen = 1;
2574                 pgm->unseen = 0;
2575         }
2576
2577         ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
2578
2579         vms->vmArrayIndex = 0;
2580         mail_search_full (vms->mailstream, NULL, pgm, NIL);
2581         vms->lastmsg = vms->vmArrayIndex - 1;
2582         mail_free_searchpgm(&pgm);
2583
2584         ast_mutex_unlock(&vms->lock);
2585         return 0;
2586 }
2587
2588 static void write_file(char *filename, char *buffer, unsigned long len)
2589 {
2590         FILE *output;
2591
2592         output = fopen (filename, "w");
2593         if (fwrite(buffer, len, 1, output) != 1) {
2594                 if (ferror(output)) {
2595                         ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2596                 }
2597         }
2598         fclose (output);
2599 }
2600
2601 static void update_messages_by_imapuser(const char *user, unsigned long number)
2602 {
2603         struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2604
2605         if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2606                 return;
2607         }
2608
2609         ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2610         vms->msgArray[vms->vmArrayIndex++] = number;
2611 }
2612
2613 void mm_searched(MAILSTREAM *stream, unsigned long number)
2614 {
2615         char *mailbox = stream->mailbox, buf[1024] = "", *user;
2616
2617         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2618                 return;
2619
2620         update_messages_by_imapuser(user, number);
2621 }
2622
2623 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2624 {
2625         struct ast_variable *var;
2626         struct ast_vm_user *vmu;
2627
2628         vmu = ast_calloc(1, sizeof *vmu);
2629         if (!vmu)
2630                 return NULL;
2631         ast_set_flag(vmu, VM_ALLOCED);
2632         populate_defaults(vmu);
2633
2634         var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2635         if (var) {
2636                 apply_options_full(vmu, var);
2637                 ast_variables_destroy(var);
2638                 return vmu;
2639         } else {
2640                 ast_free(vmu);
2641                 return NULL;
2642         }
2643 }
2644
2645 /* Interfaces to C-client */
2646
2647 void mm_exists(MAILSTREAM * stream, unsigned long number)
2648 {
2649         /* mail_ping will callback here if new mail! */
2650         ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2651         if (number == 0) return;
2652         set_update(stream);
2653 }
2654
2655
2656 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2657 {
2658         /* mail_ping will callback here if expunged mail! */
2659         ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2660         if (number == 0) return;
2661         set_update(stream);
2662 }
2663
2664
2665 void mm_flags(MAILSTREAM * stream, unsigned long number)
2666 {
2667         /* mail_ping will callback here if read mail! */
2668         ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2669         if (number == 0) return;
2670         set_update(stream);
2671 }
2672
2673
2674 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2675 {
2676         ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2677         mm_log (string, errflg);
2678 }
2679
2680
2681 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2682 {
2683         if (delimiter == '\0') {
2684                 delimiter = delim;
2685         }
2686
2687         ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2688         if (attributes & LATT_NOINFERIORS)
2689                 ast_debug(5, "no inferiors\n");
2690         if (attributes & LATT_NOSELECT)
2691                 ast_debug(5, "no select\n");
2692         if (attributes & LATT_MARKED)
2693                 ast_debug(5, "marked\n");
2694         if (attributes & LATT_UNMARKED)
2695                 ast_debug(5, "unmarked\n");
2696 }
2697
2698
2699 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2700 {
2701         ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2702         if (attributes & LATT_NOINFERIORS)
2703                 ast_debug(5, "no inferiors\n");
2704         if (attributes & LATT_NOSELECT)
2705                 ast_debug(5, "no select\n");
2706         if (attributes & LATT_MARKED)
2707                 ast_debug(5, "marked\n");
2708         if (attributes & LATT_UNMARKED)
2709                 ast_debug(5, "unmarked\n");
2710 }
2711
2712
2713 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2714 {
2715         ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2716         if (status->flags & SA_MESSAGES)
2717                 ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2718         if (status->flags & SA_RECENT)
2719                 ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2720         if (status->flags & SA_UNSEEN)
2721                 ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2722         if (status->flags & SA_UIDVALIDITY)
2723                 ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2724         if (status->flags & SA_UIDNEXT)
2725                 ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2726         ast_log(AST_LOG_NOTICE, "\n");
2727 }
2728
2729
2730 void mm_log(char *string, long errflg)
2731 {
2732         switch ((short) errflg) {
2733                 case NIL:
2734                         ast_debug(1, "IMAP Info: %s\n", string);
2735                         break;
2736                 case PARSE:
2737                 case WARN:
2738                         ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2739                         break;
2740                 case ERROR:
2741                         ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2742                         break;
2743         }
2744 }
2745
2746
2747 void mm_dlog(char *string)
2748 {
2749         ast_log(AST_LOG_NOTICE, "%s\n", string);
2750 }
2751
2752
2753 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2754 {
2755         struct ast_vm_user *vmu;
2756
2757         ast_debug(4, "Entering callback mm_login\n");
2758
2759         ast_copy_string(user, mb->user, MAILTMPLEN);
2760
2761         /* We should only do this when necessary */
2762         if (!ast_strlen_zero(authpassword)) {
2763                 ast_copy_string(pwd, authpassword, MAILTMPLEN);
2764         } else {
2765                 AST_LIST_TRAVERSE(&users, vmu, list) {
2766                         if (!strcasecmp(mb->user, vmu->imapuser)) {
2767                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2768                                 break;
2769                         }
2770                 }
2771                 if (!vmu) {
2772                         if ((vmu = find_user_realtime_imapuser(mb->user))) {
2773                                 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2774                                 free_user(vmu);
2775                         }
2776                 }
2777         }
2778 }
2779
2780
2781 void mm_critical(MAILSTREAM * stream)
2782 {
2783 }
2784
2785
2786 void mm_nocritical(MAILSTREAM * stream)
2787 {
2788 }
2789
2790
2791 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2792 {
2793         kill (getpid (), SIGSTOP);
2794         return NIL;
2795 }
2796
2797
2798 void mm_fatal(char *string)
2799 {
2800         ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2801 }
2802
2803 /* C-client callback to handle quota */
2804 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2805 {
2806         struct vm_state *vms;
2807         char *mailbox = stream->mailbox, *user;
2808         char buf[1024] = "";
2809         unsigned long usage = 0, limit = 0;
2810         
2811         while (pquota) {
2812                 usage = pquota->usage;
2813                 limit = pquota->limit;
2814                 pquota = pquota->next;
2815         }
2816         
2817         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)))) {
2818                 ast_log(AST_LOG_ERROR, "No state found.\n");
2819                 return;
2820         }
2821
2822         ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2823
2824         vms->quota_usage = usage;
2825         vms->quota_limit = limit;
2826 }
2827
2828 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2829 {
2830         char *start, *eol_pnt;
2831         int taglen;
2832
2833         if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2834                 return NULL;
2835
2836         taglen = strlen(tag) + 1;
2837         if (taglen < 1)
2838                 return NULL;
2839
2840         if (!(start = strstr(header, tag)))
2841                 return NULL;
2842
2843         /* Since we can be called multiple times we should clear our buffer */
2844         memset(buf, 0, len);
2845
2846         ast_copy_string(buf, start+taglen, len);
2847         if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2848                 *eol_pnt = '\0';
2849         return buf;
2850 }
2851
2852 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2853 {
2854         char *start, *quote, *eol_pnt;
2855
2856         if (ast_strlen_zero(mailbox))
2857                 return NULL;
2858
2859         if (!(start = strstr(mailbox, "/user=")))
2860                 return NULL;
2861
2862         ast_copy_string(buf, start+6, len);
2863
2864         if (!(quote = strchr(buf, '\"'))) {
2865                 if (!(eol_pnt = strchr(buf, '/')))
2866                         eol_pnt = strchr(buf,'}');
2867                 *eol_pnt = '\0';
2868                 return buf;
2869         } else {
2870                 eol_pnt = strchr(buf+1,'\"');
2871                 *eol_pnt = '\0';
2872                 return buf+1;
2873         }
2874 }
2875
2876 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
2877 {
2878         struct vm_state *vms_p;
2879
2880         pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2881         if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
2882                 return vms_p;
2883         }
2884         if (option_debug > 4)
2885                 ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
2886         if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2887                 return NULL;
2888         ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
2889         ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
2890         ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2891         ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
2892         vms_p->mailstream = NIL; /* save for access from interactive entry point */
2893         vms_p->imapversion = vmu->imapversion;
2894         if (option_debug > 4)
2895                 ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
2896         vms_p->updated = 1;
2897         /* set mailbox to INBOX! */
2898         ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
2899         init_vm_state(vms_p);
2900         vmstate_insert(vms_p);
2901         return vms_p;
2902 }
2903
2904 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
2905 {
2906         struct vmstate *vlist = NULL;
2907
2908         if (interactive) {
2909                 struct vm_state *vms;
2910                 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2911                 vms = pthread_getspecific(ts_vmstate.key);
2912                 return vms;
2913         }
2914
2915         AST_LIST_LOCK(&vmstates);
2916         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2917                 if (!vlist->vms) {
2918                         ast_debug(3, "error: vms is NULL for %s\n", user);
2919                         continue;
2920                 }
2921                 if (vlist->vms->imapversion != imapversion) {
2922                         continue;
2923                 }
2924                 if (!vlist->vms->imapuser) {
2925                         ast_debug(3, "error: imapuser is NULL for %s\n", user);
2926                         continue;
2927                 }
2928
2929                 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
2930                         AST_LIST_UNLOCK(&vmstates);
2931                         return vlist->vms;
2932                 }
2933         }
2934         AST_LIST_UNLOCK(&vmstates);
2935
2936         ast_debug(3, "%s not found in vmstates\n", user);
2937
2938         return NULL;
2939 }
2940
2941 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
2942 {
2943
2944         struct vmstate *vlist = NULL;
2945         const char *local_context = S_OR(context, "default");
2946
2947         if (interactive) {
2948                 struct vm_state *vms;
2949                 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2950                 vms = pthread_getspecific(ts_vmstate.key);
2951                 return vms;
2952         }
2953
2954         AST_LIST_LOCK(&vmstates);
2955         AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2956                 if (!vlist->vms) {
2957                         ast_debug(3, "error: vms is NULL for %s\n", mailbox);
2958                         continue;
2959                 }
2960                 if (vlist->vms->imapversion != imapversion) {
2961                         continue;
2962                 }
2963                 if (!vlist->vms->username || !vlist->vms->context) {
2964                         ast_debug(3, "error: username is NULL for %s\n", mailbox);
2965                         continue;
2966                 }
2967
2968                 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);
2969                 
2970                 if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
2971                         ast_debug(3, "Found it!\n");
2972                         AST_LIST_UNLOCK(&vmstates);
2973                         return vlist->vms;
2974                 }
2975         }
2976         AST_LIST_UNLOCK(&vmstates);
2977
2978         ast_debug(3, "%s not found in vmstates\n", mailbox);
2979
2980         return NULL;
2981 }
2982
2983 static void vmstate_insert(struct vm_state *vms) 
2984 {
2985         struct vmstate *v;
2986         struct vm_state *altvms;
2987
2988         /* If interactive, it probably already exists, and we should
2989            use the one we already have since it is more up to date.
2990            We can compare the username to find the duplicate */
2991         if (vms->interactive == 1) {
2992                 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
2993                 if (altvms) {   
2994                         ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
2995                         vms->newmessages = altvms->newmessages;
2996                         vms->oldmessages = altvms->oldmessages;
2997                         vms->vmArrayIndex = altvms->vmArrayIndex;
2998                         vms->lastmsg = altvms->lastmsg;
2999                         vms->curmsg = altvms->curmsg;
3000                         /* get a pointer to the persistent store */
3001                         vms->persist_vms = altvms;
3002                         /* Reuse the mailstream? */
3003 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3004                         vms->mailstream = altvms->mailstream;
3005 #else
3006                         vms->mailstream = NIL;
3007 #endif
3008                 }
3009                 return;
3010         }
3011
3012         if (!(v = ast_calloc(1, sizeof(*v))))
3013                 return;
3014         
3015         v->vms = vms;
3016
3017         ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3018
3019         AST_LIST_LOCK(&vmstates);
3020         AST_LIST_INSERT_TAIL(&vmstates, v, list);
3021         AST_LIST_UNLOCK(&vmstates);
3022 }
3023
3024 static void vmstate_delete(struct vm_state *vms) 
3025 {
3026         struct vmstate *vc = NULL;
3027         struct vm_state *altvms = NULL;
3028
3029         /* If interactive, we should copy pertinent info
3030            back to the persistent state (to make update immediate) */
3031         if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3032                 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3033                 altvms->newmessages = vms->newmessages;
3034                 altvms->oldmessages = vms->oldmessages;
3035                 altvms->updated = 1;
3036                 vms->mailstream = mail_close(vms->mailstream);
3037
3038                 /* Interactive states are not stored within the persistent list */
3039                 return;
3040         }
3041         
3042         ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3043         
3044         AST_LIST_LOCK(&vmstates);
3045         AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3046                 if (vc->vms == vms) {
3047                         AST_LIST_REMOVE_CURRENT(list);
3048                         break;
3049                 }
3050         }
3051         AST_LIST_TRAVERSE_SAFE_END
3052         AST_LIST_UNLOCK(&vmstates);
3053         
3054         if (vc) {
3055                 ast_mutex_destroy(&vc->vms->lock);
3056                 ast_free(vc);
3057         }
3058         else
3059                 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3060 }
3061
3062 static void set_update(MAILSTREAM * stream) 
3063 {
3064         struct vm_state *vms;
3065         char *mailbox = stream->mailbox, *user;
3066         char buf[1024] = "";
3067
3068         if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3069                 if (user && option_debug > 2)
3070                         ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3071                 return;
3072         }
3073
3074         ast_debug(3, "User %s mailbox set for update.\n", user);
3075
3076         vms->updated = 1; /* Set updated flag since mailbox changed */
3077 }
3078
3079 static void init_vm_state(struct vm_state *vms) 
3080 {
3081         int x;
3082         vms->vmArrayIndex = 0;
3083         for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
3084                 vms->msgArray[x] = 0;
3085         }
3086         ast_mutex_init(&vms->lock);
3087 }
3088
3089 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
3090 {
3091         char *body_content;
3092         char *body_decoded;
3093         char *fn = is_intro ? vms->introfn : vms->fn;
3094         unsigned long len;
3095         unsigned long newlen;
3096         char filename[256];
3097         
3098         if (!body || body == NIL)
3099                 return -1;
3100
3101         ast_mutex_lock(&vms->lock);
3102         body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3103         ast_mutex_unlock(&vms->lock);
3104         if (body_content != NIL) {
3105                 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3106                 /* ast_debug(1,body_content); */
3107                 body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3108                 /* If the body of the file is empty, return an error */
3109                 if (!newlen) {
3110                         return -1;
3111                 }
3112                 write_file(filename, (char *) body_decoded, newlen);
3113         } else {
3114                 ast_debug(5, "Body of message is NULL.\n");
3115                 return -1;
3116         }
3117         return 0;
3118 }
3119
3120 /*! 
3121  * \brief Get delimiter via mm_list callback 
3122  * \param stream
3123  *
3124  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3125  */
3126 /* MUTEX should already be held */
3127 static void get_mailbox_delimiter(MAILSTREAM *stream) {
3128         char tmp[50];
3129         snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
3130         mail_list(stream, tmp, "*");
3131 }