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