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