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