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