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