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