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