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