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