Allow a Skinny device to monitor a dialplan hint (w00t!).
[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  * \par See also
26  * \arg \ref Config_vm
27  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
28  * \ingroup applications
29  * \note This module requires res_adsi to load. This needs to be optional
30  * during compilation.
31  *
32  *
33  *
34  * \note  This file is now almost impossible to work with, due to all #ifdefs.
35  *        Feels like the database code before realtime. Someone - please come up
36  *        with a plan to clean this up.
37  */
38
39 /*** MODULEINFO
40         <depend>res_adsi</depend>
41  ***/
42
43 /*** MAKEOPTS
44 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
45         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
46                 <depend>unixodbc</depend>
47                 <conflict>IMAP_STORAGE</conflict>
48                 <defaultenabled>no</defaultenabled>
49         </member>
50         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
51                 <depend>imap_tk</depend>
52                 <conflict>ODBC_STORAGE</conflict>
53                 <use>ssl</use>
54                 <defaultenabled>no</defaultenabled>
55         </member>
56 </category>
57  ***/
58
59 #include "asterisk.h"
60
61 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
62
63 #include <stdlib.h>
64 #include <errno.h>
65 #include <unistd.h>
66 #include <string.h>
67 #include <stdlib.h>
68 #include <stdio.h>
69 #include <sys/time.h>
70 #include <sys/stat.h>
71 #include <sys/types.h>
72 #include <sys/mman.h>
73 #include <time.h>
74 #include <dirent.h>
75
76 #ifdef IMAP_STORAGE
77 #include <ctype.h>
78 #include <signal.h>
79 #include <pwd.h>
80 #include "c-client.h"
81 #include "imap4r1.h"
82 #include "linkage.h"
83 #endif
84
85 #include "asterisk/lock.h"
86 #include "asterisk/file.h"
87 #include "asterisk/logger.h"
88 #include "asterisk/channel.h"
89 #include "asterisk/pbx.h"
90 #include "asterisk/options.h"
91 #include "asterisk/config.h"
92 #include "asterisk/say.h"
93 #include "asterisk/module.h"
94 #include "asterisk/adsi.h"
95 #include "asterisk/app.h"
96 #include "asterisk/manager.h"
97 #include "asterisk/dsp.h"
98 #include "asterisk/localtime.h"
99 #include "asterisk/cli.h"
100 #include "asterisk/utils.h"
101 #include "asterisk/stringfields.h"
102 #include "asterisk/smdi.h"
103
104 #ifdef ODBC_STORAGE
105 #include "asterisk/res_odbc.h"
106 #endif
107
108 #ifdef IMAP_STORAGE
109 static char imapserver[48];
110 static char imapport[8];
111 static char imapflags[128];
112 static char imapfolder[64];
113 static char authuser[32];
114 static char authpassword[42];
115
116 static int expungeonhangup = 1;
117 AST_MUTEX_DEFINE_STATIC(delimiter_lock);
118 static char delimiter = '\0';
119
120 struct vm_state;
121 struct ast_vm_user;
122
123 /* Forward declarations for IMAP */
124 static int init_mailstream(struct vm_state *vms, int box);
125 static void write_file(char *filename, char *buffer, unsigned long len);
126 static void display_body(BODY *body, char *pfx, long i);
127 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
128 static void vm_imap_delete(int msgnum, struct vm_state *vms);
129 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
130 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
131 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
132 static void vmstate_insert(struct vm_state *vms);
133 static void vmstate_delete(struct vm_state *vms);
134 static void set_update(MAILSTREAM * stream);
135 static void init_vm_state(struct vm_state *vms);
136 static void check_msgArray(struct vm_state *vms);
137 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
138 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
139 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
140 static void get_mailbox_delimiter(MAILSTREAM *stream);
141 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
142 static void imap_mailbox_name(char *spec, struct vm_state *vms, int box, int target);
143 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);
144
145
146
147 struct vmstate {
148         struct vm_state *vms;
149         AST_LIST_ENTRY(vmstate) list;
150 };
151
152 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
153
154 #endif
155
156 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
157
158 #define COMMAND_TIMEOUT 5000
159 /* Don't modify these here; set your umask at runtime instead */
160 #define VOICEMAIL_DIR_MODE      0777
161 #define VOICEMAIL_FILE_MODE     0666
162 #define CHUNKSIZE       65536
163
164 #define VOICEMAIL_CONFIG "voicemail.conf"
165 #define ASTERISK_USERNAME "asterisk"
166
167 /* Default mail command to mail voicemail. Change it with the
168     mailcmd= command in voicemail.conf */
169 #define SENDMAIL "/usr/sbin/sendmail -t"
170
171 #define INTRO "vm-intro"
172
173 #define MAXMSG 100
174 #define MAXMSGLIMIT 9999
175
176 #define BASEMAXINLINE 256
177 #define BASELINELEN 72
178 #define BASEMAXINLINE 256
179 #define eol "\r\n"
180
181 #define MAX_DATETIME_FORMAT     512
182 #define MAX_NUM_CID_CONTEXTS 10
183
184 #define VM_REVIEW        (1 << 0)
185 #define VM_OPERATOR      (1 << 1)
186 #define VM_SAYCID        (1 << 2)
187 #define VM_SVMAIL        (1 << 3)
188 #define VM_ENVELOPE      (1 << 4)
189 #define VM_SAYDURATION   (1 << 5)
190 #define VM_SKIPAFTERCMD  (1 << 6)
191 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
192 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
193 #define VM_PBXSKIP       (1 << 9)
194 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
195 #define VM_ATTACH        (1 << 11)
196 #define VM_DELETE        (1 << 12)
197 #define VM_ALLOCED       (1 << 13)
198 #define VM_SEARCH        (1 << 14)
199 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
200 #define ERROR_LOCK_PATH  -100
201
202
203 enum {
204         OPT_SILENT =           (1 << 0),
205         OPT_BUSY_GREETING =    (1 << 1),
206         OPT_UNAVAIL_GREETING = (1 << 2),
207         OPT_RECORDGAIN =       (1 << 3),
208         OPT_PREPEND_MAILBOX =  (1 << 4),
209         OPT_PRIORITY_JUMP =    (1 << 5),
210         OPT_AUTOPLAY =         (1 << 6),
211 } vm_option_flags;
212
213 enum {
214         OPT_ARG_RECORDGAIN = 0,
215         OPT_ARG_PLAYFOLDER = 1,
216         /* This *must* be the last value in this enum! */
217         OPT_ARG_ARRAY_SIZE = 2,
218 } vm_option_args;
219
220 AST_APP_OPTIONS(vm_app_options, {
221         AST_APP_OPTION('s', OPT_SILENT),
222         AST_APP_OPTION('b', OPT_BUSY_GREETING),
223         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
224         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
225         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
226         AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
227         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
228 });
229
230 static int load_config(void);
231
232 /*! \page vmlang Voicemail Language Syntaxes Supported
233
234         \par Syntaxes supported, not really language codes.
235         \arg \b en    - English
236         \arg \b de    - German
237         \arg \b es    - Spanish
238         \arg \b fr    - French
239         \arg \b it    - Italian
240         \arg \b nl    - Dutch
241         \arg \b pt    - Portuguese
242         \arg \b pt_BR - Portuguese (Brazil)
243         \arg \b gr    - Greek
244         \arg \b no    - Norwegian
245         \arg \b se    - Swedish
246
247 German requires the following additional soundfile:
248 \arg \b 1F      einE (feminine)
249
250 Spanish requires the following additional soundfile:
251 \arg \b 1M      un (masculine)
252
253 Dutch, Portuguese & Spanish require the following additional soundfiles:
254 \arg \b vm-INBOXs       singular of 'new'
255 \arg \b vm-Olds         singular of 'old/heard/read'
256
257 NB these are plural:
258 \arg \b vm-INBOX        nieuwe (nl)
259 \arg \b vm-Old          oude (nl)
260
261 Polish uses:
262 \arg \b vm-new-a        'new', feminine singular accusative
263 \arg \b vm-new-e        'new', feminine plural accusative
264 \arg \b vm-new-ych      'new', feminine plural genitive
265 \arg \b vm-old-a        'old', feminine singular accusative
266 \arg \b vm-old-e        'old', feminine plural accusative
267 \arg \b vm-old-ych      'old', feminine plural genitive
268 \arg \b digits/1-a      'one', not always same as 'digits/1'
269 \arg \b digits/2-ie     'two', not always same as 'digits/2'
270
271 Swedish uses:
272 \arg \b vm-nytt         singular of 'new'
273 \arg \b vm-nya          plural of 'new'
274 \arg \b vm-gammalt      singular of 'old'
275 \arg \b vm-gamla        plural of 'old'
276 \arg \b digits/ett      'one', not always same as 'digits/1'
277
278 Norwegian uses:
279 \arg \b vm-ny           singular of 'new'
280 \arg \b vm-nye          plural of 'new'
281 \arg \b vm-gammel       singular of 'old'
282 \arg \b vm-gamle        plural of 'old'
283
284 Dutch also uses:
285 \arg \b nl-om           'at'?
286
287 Spanish also uses:
288 \arg \b vm-youhaveno
289
290 Italian requires the following additional soundfile:
291
292 For vm_intro_it:
293 \arg \b vm-nuovo        new
294 \arg \b vm-nuovi        new plural
295 \arg \b vm-vecchio      old
296 \arg \b vm-vecchi       old plural
297
298 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
299 spelled among others when you have to change folder. For the above reasons, vm-INBOX
300 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
301
302 */
303
304 struct baseio {
305         int iocp;
306         int iolen;
307         int linelength;
308         int ateof;
309         unsigned char iobuf[BASEMAXINLINE];
310 };
311
312 /*! Structure for linked list of users */
313 struct ast_vm_user {
314         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
315         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
316         char password[80];               /*!< Secret pin code, numbers only */
317         char fullname[80];               /*!< Full name, for directory app */
318         char email[80];                  /*!< E-mail address */
319         char pager[80];                  /*!< E-mail address to pager (no attachment) */
320         char serveremail[80];            /*!< From: Mail address */
321         char mailcmd[160];               /*!< Configurable mail command */
322         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
323         char zonetag[80];                /*!< Time zone */
324         char callback[80];
325         char dialout[80];
326         char uniqueid[20];               /*!< Unique integer identifier */
327         char exit[80];
328         char attachfmt[20];              /*!< Attachment format */
329         unsigned int flags;              /*!< VM_ flags */      
330         int saydurationm;
331         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
332         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
333 #ifdef IMAP_STORAGE
334         char imapuser[80];               /*!< IMAP server login */
335         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
336 #endif
337         double volgain;                  /*!< Volume gain for voicemails sent via email */
338         AST_LIST_ENTRY(ast_vm_user) list;
339 };
340
341 /*! Voicemail time zones */
342 struct vm_zone {
343         AST_LIST_ENTRY(vm_zone) list;
344         char name[80];
345         char timezone[80];
346         char msg_format[512];
347 };
348
349 /*! Voicemail mailbox state */
350 struct vm_state {
351         char curbox[80];
352         char username[80];
353         char curdir[PATH_MAX];
354         char vmbox[PATH_MAX];
355         char fn[PATH_MAX];
356         char fn2[PATH_MAX];
357         int *deleted;
358         int *heard;
359         int curmsg;
360         int lastmsg;
361         int newmessages;
362         int oldmessages;
363         int starting;
364         int repeats;
365 #ifdef IMAP_STORAGE
366         int updated;                         /*!< decremented on each mail check until 1 -allows delay */
367         long msgArray[256];
368         MAILSTREAM *mailstream;
369         int vmArrayIndex;
370         char imapuser[80];                   /*!< IMAP server login */
371         int interactive;
372         unsigned int quota_limit;
373         unsigned int quota_usage;
374         struct vm_state *persist_vms;
375 #endif
376 };
377
378
379 #ifdef ODBC_STORAGE
380 static char odbc_database[80];
381 static char odbc_table[80];
382 #define RETRIEVE(a,b) retrieve_file(a,b)
383 #define DISPOSE(a,b) remove_file(a,b)
384 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
385 #define EXISTS(a,b,c,d) (message_exists(a,b))
386 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
387 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
388 #define DELETE(a,b,c) (delete_file(a,b))
389 #else
390 #ifdef IMAP_STORAGE
391 #define RETRIEVE(a,b)
392 #define DISPOSE(a,b)
393 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
394 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
395 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
396 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
397 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
398 #define DELETE(a,b,c) (vm_delete(c))
399 #else
400 #define RETRIEVE(a,b)
401 #define DISPOSE(a,b)
402 #define STORE(a,b,c,d,e,f,g,h,i)
403 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
404 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
405 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
406 #define DELETE(a,b,c) (vm_delete(c))
407 #endif
408 #endif
409
410 static char VM_SPOOL_DIR[PATH_MAX];
411
412 static char ext_pass_cmd[128];
413
414 #define PWDCHANGE_INTERNAL (1 << 1)
415 #define PWDCHANGE_EXTERNAL (1 << 2)
416 static int pwdchange = PWDCHANGE_INTERNAL;
417
418 #ifdef ODBC_STORAGE
419 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
420 #else
421 # ifdef IMAP_STORAGE
422 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
423 # else
424 # define tdesc "Comedian Mail (Voicemail System)"
425 # endif
426 #endif
427
428 static char userscontext[AST_MAX_EXTENSION] = "default";
429
430 static char *addesc = "Comedian Mail";
431
432 static char *synopsis_vm = "Leave a Voicemail message";
433
434 static char *descrip_vm =
435         "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
436         "application allows the calling party to leave a message for the specified\n"
437         "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
438         "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
439         "specified mailbox does not exist.\n"
440         "  The Voicemail application will exit if any of the following DTMF digits are\n"
441         "received:\n"
442         "    0 - Jump to the 'o' extension in the current dialplan context.\n"
443         "    * - Jump to the 'a' extension in the current dialplan context.\n"
444         "  This application will set the following channel variable upon completion:\n"
445         "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
446         "               application. The possible values are:\n"
447         "               SUCCESS | USEREXIT | FAILED\n\n"
448         "  Options:\n"
449         "    b    - Play the 'busy' greeting to the calling party.\n"
450         "    g(#) - Use the specified amount of gain when recording the voicemail\n"
451         "           message. The units are whole-number decibels (dB).\n"
452         "    s    - Skip the playback of instructions for leaving a message to the\n"
453         "           calling party.\n"
454         "    u    - Play the 'unavailble greeting.\n"
455         "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
456         "           error occurs.\n";
457
458 static char *synopsis_vmain = "Check Voicemail messages";
459
460 static char *descrip_vmain =
461         "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
462         "calling party to check voicemail messages. A specific mailbox, and optional\n"
463         "corresponding context, may be specified. If a mailbox is not provided, the\n"
464         "calling party will be prompted to enter one. If a context is not specified,\n"
465         "the 'default' context will be used.\n\n"
466         "  Options:\n"
467         "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
468         "           is entered by the caller.\n"
469         "    g(#) - Use the specified amount of gain when recording a voicemail\n"
470         "           message. The units are whole-number decibels (dB).\n"
471         "    s    - Skip checking the passcode for the mailbox.\n"
472         "    a(#) - Skip folder prompt and go directly to folder specified.\n"
473         "           Defaults to INBOX\n";
474
475 static char *synopsis_vm_box_exists =
476 "Check to see if Voicemail mailbox exists";
477
478 static char *descrip_vm_box_exists =
479         "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
480         "mailbox exists. If no voicemail context is specified, the 'default' context\n"
481         "will be used.\n"
482         "  This application will set the following channel variable upon completion:\n"
483         "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
484         "                        MailboxExists application. Possible values include:\n"
485         "                        SUCCESS | FAILED\n\n"
486         "  Options:\n"
487         "    j - Jump to priority n+101 if the mailbox is found.\n";
488
489 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
490
491 static char *descrip_vmauthenticate =
492         "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
493         "same way as the Authenticate application, but the passwords are taken from\n"
494         "voicemail.conf.\n"
495         "  If the mailbox is specified, only that mailbox's password will be considered\n"
496         "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
497         "be set with the authenticated mailbox.\n\n"
498         "  Options:\n"
499         "    s - Skip playing the initial prompts.\n";
500
501 /* Leave a message */
502 static char *app = "VoiceMail";
503
504 /* Check mail, control, etc */
505 static char *app2 = "VoiceMailMain";
506
507 static char *app3 = "MailboxExists";
508 static char *app4 = "VMAuthenticate";
509
510 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
511 static AST_LIST_HEAD_STATIC(zones, vm_zone);
512 static int maxsilence;
513 static int maxmsg;
514 static int silencethreshold = 128;
515 static char serveremail[80];
516 static char mailcmd[160];       /* Configurable mail cmd */
517 static char externnotify[160]; 
518 static struct ast_smdi_interface *smdi_iface = NULL;
519 static char vmfmts[80];
520 static double volgain;
521 static int vmminsecs;
522 static int vmmaxsecs;
523 static int maxgreet;
524 static int skipms;
525 static int maxlogins;
526
527 /* custom password sounds */
528 static char vm_password[80] = "vm-password";
529 static char vm_newpassword[80] = "vm-newpassword";
530 static char vm_passchanged[80] = "vm-passchanged";
531 static char vm_reenterpassword[80] = "vm-reenterpassword";
532 static char vm_mismatch[80] = "vm-mismatch";
533
534 static struct ast_flags globalflags = {0};
535
536 static int saydurationminfo;
537
538 static char dialcontext[AST_MAX_CONTEXT];
539 static char callcontext[AST_MAX_CONTEXT];
540 static char exitcontext[AST_MAX_CONTEXT];
541
542 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
543
544
545 static char *emailbody = NULL;
546 static char *emailsubject = NULL;
547 static char *pagerbody = NULL;
548 static char *pagersubject = NULL;
549 static char fromstring[100];
550 static char pagerfromstring[100];
551 static char emailtitle[100];
552 static char charset[32] = "ISO-8859-1";
553
554 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
555 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
556 static int adsiver = 1;
557 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
558
559 /* Forward declarations - generic */
560 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
561 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);
562 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
563 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
564                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
565                         signed char record_gain, struct vm_state *vms);
566 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
567 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
568 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
569 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
570 static void apply_options(struct ast_vm_user *vmu, const char *options);
571
572 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
573 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
574 #endif
575
576
577
578 static void populate_defaults(struct ast_vm_user *vmu)
579 {
580         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
581         if (saydurationminfo)
582                 vmu->saydurationm = saydurationminfo;
583         if (callcontext)
584                 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
585         if (dialcontext)
586                 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
587         if (exitcontext)
588                 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
589         if (vmmaxsecs)
590                 vmu->maxsecs = vmmaxsecs;
591         if (maxmsg)
592                 vmu->maxmsg = maxmsg;
593         vmu->volgain = volgain;
594 }
595
596 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
597 {
598         int x;
599         if (!strcasecmp(var, "attach")) {
600                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
601         } else if (!strcasecmp(var, "attachfmt")) {
602                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
603         } else if (!strcasecmp(var, "serveremail")) {
604                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
605         } else if (!strcasecmp(var, "language")) {
606                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
607         } else if (!strcasecmp(var, "tz")) {
608                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
609 #ifdef IMAP_STORAGE
610         } else if (!strcasecmp(var, "imapuser")) {
611                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
612         } else if (!strcasecmp(var, "imappassword")) {
613                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
614 #endif
615         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
616                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
617         } else if (!strcasecmp(var, "saycid")){
618                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
619         } else if (!strcasecmp(var,"sendvoicemail")){
620                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
621         } else if (!strcasecmp(var, "review")){
622                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
623         } else if (!strcasecmp(var, "tempgreetwarn")){
624                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
625         } else if (!strcasecmp(var, "operator")){
626                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
627         } else if (!strcasecmp(var, "envelope")){
628                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
629         } else if (!strcasecmp(var, "sayduration")){
630                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
631         } else if (!strcasecmp(var, "saydurationm")){
632                 if (sscanf(value, "%d", &x) == 1) {
633                         vmu->saydurationm = x;
634                 } else {
635                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
636                 }
637         } else if (!strcasecmp(var, "forcename")){
638                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
639         } else if (!strcasecmp(var, "forcegreetings")){
640                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
641         } else if (!strcasecmp(var, "callback")) {
642                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
643         } else if (!strcasecmp(var, "dialout")) {
644                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
645         } else if (!strcasecmp(var, "exitcontext")) {
646                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
647         } else if (!strcasecmp(var, "maxmessage")) {
648                 if (vmu->maxsecs <= 0) {
649                         ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
650                         vmu->maxsecs = vmmaxsecs;
651                 } else {
652                         vmu->maxsecs = atoi(value);
653                 }
654         } else if (!strcasecmp(var, "maxmsg")) {
655                 vmu->maxmsg = atoi(value);
656                 if (vmu->maxmsg <= 0) {
657                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
658                         vmu->maxmsg = MAXMSG;
659                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
660                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
661                         vmu->maxmsg = MAXMSGLIMIT;
662                 }
663         } else if (!strcasecmp(var, "volgain")) {
664                 sscanf(value, "%lf", &vmu->volgain);
665         } else if (!strcasecmp(var, "options")) {
666                 apply_options(vmu, value);
667         }
668 }
669
670 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
671 {
672         int res;
673         if (!ast_strlen_zero(vmu->uniqueid)) {
674                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
675                 if (res > 0) {
676                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
677                         res = 0;
678                 } else if (!res) {
679                         res = -1;
680                 }
681                 return res;
682         }
683         return -1;
684 }
685
686 static void apply_options(struct ast_vm_user *vmu, const char *options)
687 {       /* Destructively Parse options and apply */
688         char *stringp;
689         char *s;
690         char *var, *value;
691         stringp = ast_strdupa(options);
692         while ((s = strsep(&stringp, "|"))) {
693                 value = s;
694                 if ((var = strsep(&value, "=")) && value) {
695                         apply_option(vmu, var, value);
696                 }
697         }       
698 }
699
700 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
701 {
702         struct ast_variable *tmp;
703         tmp = var;
704         while (tmp) {
705                 if (!strcasecmp(tmp->name, "vmsecret")) {
706                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
707                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
708                         if (ast_strlen_zero(retval->password))
709                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
710                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
711                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
712                 } else if (!strcasecmp(tmp->name, "pager")) {
713                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
714                 } else if (!strcasecmp(tmp->name, "email")) {
715                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
716                 } else if (!strcasecmp(tmp->name, "fullname")) {
717                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
718                 } else if (!strcasecmp(tmp->name, "context")) {
719                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
720 #ifdef IMAP_STORAGE
721                 } else if (!strcasecmp(tmp->name, "imapuser")) {
722                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
723                 } else if (!strcasecmp(tmp->name, "imappassword")) {
724                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
725 #endif
726                 } else
727                         apply_option(retval, tmp->name, tmp->value);
728                 tmp = tmp->next;
729         } 
730 }
731
732 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
733 {
734         struct ast_variable *var;
735         struct ast_vm_user *retval;
736
737         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
738                 if (!ivm)
739                         ast_set_flag(retval, VM_ALLOCED);       
740                 else
741                         memset(retval, 0, sizeof(*retval));
742                 if (mailbox) 
743                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
744                 populate_defaults(retval);
745                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
746                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
747                 else
748                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
749                 if (var) {
750                         apply_options_full(retval, var);
751                         ast_variables_destroy(var);
752                 } else { 
753                         if (!ivm) 
754                                 free(retval);
755                         retval = NULL;
756                 }       
757         } 
758         return retval;
759 }
760
761 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
762 {
763         /* This function could be made to generate one from a database, too */
764         struct ast_vm_user *vmu=NULL, *cur;
765         AST_LIST_LOCK(&users);
766
767         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
768                 context = "default";
769
770         AST_LIST_TRAVERSE(&users, cur, list) {
771                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
772                         break;
773                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
774                         break;
775         }
776         if (cur) {
777                 /* Make a copy, so that on a reload, we have no race */
778                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
779                         memcpy(vmu, cur, sizeof(*vmu));
780                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
781                         AST_LIST_NEXT(vmu, list) = NULL;
782                 }
783         } else
784                 vmu = find_user_realtime(ivm, context, mailbox);
785         AST_LIST_UNLOCK(&users);
786         return vmu;
787 }
788
789 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
790 {
791         /* This function could be made to generate one from a database, too */
792         struct ast_vm_user *cur;
793         int res = -1;
794         AST_LIST_LOCK(&users);
795         AST_LIST_TRAVERSE(&users, cur, list) {
796                 if ((!context || !strcasecmp(context, cur->context)) &&
797                         (!strcasecmp(mailbox, cur->mailbox)))
798                                 break;
799         }
800         if (cur) {
801                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
802                 res = 0;
803         }
804         AST_LIST_UNLOCK(&users);
805         return res;
806 }
807
808 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
809 {
810         struct ast_config   *cfg=NULL;
811         struct ast_variable *var=NULL;
812         struct ast_category *cat=NULL;
813         char *category=NULL, *value=NULL, *new=NULL;
814         const char *tmp=NULL;
815                                         
816         if (!change_password_realtime(vmu, newpassword))
817                 return;
818
819         /* check voicemail.conf */
820         if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
821                 while ((category = ast_category_browse(cfg, category))) {
822                         if (!strcasecmp(category, vmu->context)) {
823                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
824                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
825                                         break;
826                                 }
827                                 value = strstr(tmp,",");
828                                 if (!value) {
829                                         ast_log(LOG_WARNING, "variable has bad format.\n");
830                                         break;
831                                 }
832                                 new = alloca((strlen(value)+strlen(newpassword)+1));
833                                 sprintf(new,"%s%s", newpassword, value);
834                                 if (!(cat = ast_category_get(cfg, category))) {
835                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
836                                         break;
837                                 }
838                                 ast_variable_update(cat, vmu->mailbox, new, NULL);
839                         }
840                 }
841                 /* save the results */
842                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
843                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
844                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
845         }
846         category = NULL;
847         var = NULL;
848         /* check users.conf and update the password stored for the mailbox*/
849         /* if no vmsecret entry exists create one. */
850         if ((cfg = ast_config_load_with_comments("users.conf"))) {
851                 if (option_debug > 3)
852                         ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
853                 while ((category = ast_category_browse(cfg, category))) {
854                         if (option_debug > 3)
855                                 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
856                         if (!strcasecmp(category, vmu->mailbox)) {
857                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
858                                         if (option_debug > 3)
859                                                 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
860                                         var = ast_variable_new("vmsecret", newpassword);
861                                 } 
862                                 new = alloca(strlen(newpassword)+1);
863                                 sprintf(new, "%s", newpassword);
864                                 if (!(cat = ast_category_get(cfg, category))) {
865                                         if (option_debug > 3)
866                                                 ast_log(LOG_DEBUG, "failed to get category!\n");
867                                         break;
868                                 }
869                                 if (!var)               
870                                         ast_variable_update(cat, "vmsecret", new, NULL);
871                                 else
872                                         ast_variable_append(cat, var);
873                         }
874                 }
875                 /* save the results and clean things up */
876                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
877                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
878                 config_text_file_save("users.conf", cfg, "AppVoicemail");
879         }
880 }
881
882 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
883 {
884         char buf[255];
885         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
886         if (!ast_safe_system(buf))
887                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
888 }
889
890 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
891 {
892         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
893 }
894
895 #ifdef IMAP_STORAGE
896 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
897 {
898         if (mkdir(dir, 01777) && (errno != EEXIST)) {
899                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
900                 return sprintf(dest, "%s/msg%04d", dir, num);
901         }
902         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
903         return sprintf(dest, "%s/msg%04d", dir, num);
904 }
905
906 static void vm_imap_delete(int msgnum, struct vm_state *vms)
907 {
908         unsigned long messageNum = 0;
909         char arg[10];
910
911         /* find real message number based on msgnum */
912         /* this may be an index into vms->msgArray based on the msgnum. */
913
914         messageNum = vms->msgArray[msgnum];
915         if (messageNum == 0) {
916                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
917                 return;
918         }
919         if(option_debug > 2)
920                 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
921         /* delete message */
922         sprintf (arg,"%lu",messageNum);
923         mail_setflag (vms->mailstream,arg,"\\DELETED");
924 }
925
926 #endif
927 static int make_file(char *dest, int len, char *dir, int num)
928 {
929         return snprintf(dest, len, "%s/msg%04d", dir, num);
930 }
931
932 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
933  * \param dest    String. base directory.
934  * \param len     Length of dest.
935  * \param context String. Ignored if is null or empty string.
936  * \param ext     String. Ignored if is null or empty string.
937  * \param folder  String. Ignored if is null or empty string. 
938  * \return 0 on failure, 1 on success.
939  */
940 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
941 {
942         mode_t  mode = VOICEMAIL_DIR_MODE;
943
944         if (!ast_strlen_zero(context)) {
945                 make_dir(dest, len, context, "", "");
946                 if (mkdir(dest, mode) && errno != EEXIST) {
947                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
948                         return 0;
949                 }
950         }
951         if (!ast_strlen_zero(ext)) {
952                 make_dir(dest, len, context, ext, "");
953                 if (mkdir(dest, mode) && errno != EEXIST) {
954                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
955                         return 0;
956                 }
957         }
958         if (!ast_strlen_zero(folder)) {
959                 make_dir(dest, len, context, ext, folder);
960                 if (mkdir(dest, mode) && errno != EEXIST) {
961                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
962                         return 0;
963                 }
964         }
965         return 1;
966 }
967
968 /*! \brief Lock file path
969     only return failure if ast_lock_path returns 'timeout',
970    not if the path does not exist or any other reason
971 */
972 static int vm_lock_path(const char *path)
973 {
974         switch (ast_lock_path(path)) {
975         case AST_LOCK_TIMEOUT:
976                 return -1;
977         default:
978                 return 0;
979         }
980 }
981
982
983 #ifdef ODBC_STORAGE
984 static int retrieve_file(char *dir, int msgnum)
985 {
986         int x = 0;
987         int res;
988         int fd=-1;
989         size_t fdlen = 0;
990         void *fdm=NULL;
991         SQLSMALLINT colcount=0;
992         SQLHSTMT stmt;
993         char sql[PATH_MAX];
994         char fmt[80]="";
995         char *c;
996         char coltitle[256];
997         SQLSMALLINT collen;
998         SQLSMALLINT datatype;
999         SQLSMALLINT decimaldigits;
1000         SQLSMALLINT nullable;
1001         SQLULEN colsize;
1002         SQLLEN colsize2;
1003         FILE *f=NULL;
1004         char rowdata[80];
1005         char fn[PATH_MAX];
1006         char full_fn[PATH_MAX];
1007         char msgnums[80];
1008         
1009         struct odbc_obj *obj;
1010         obj = ast_odbc_request_obj(odbc_database, 0);
1011         if (obj) {
1012                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1013                 c = strchr(fmt, '|');
1014                 if (c)
1015                         *c = '\0';
1016                 if (!strcasecmp(fmt, "wav49"))
1017                         strcpy(fmt, "WAV");
1018                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1019                 if (msgnum > -1)
1020                         make_file(fn, sizeof(fn), dir, msgnum);
1021                 else
1022                         ast_copy_string(fn, dir, sizeof(fn));
1023                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1024                 
1025                 if (!(f = fopen(full_fn, "w+"))) {
1026                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1027                         goto yuck;
1028                 }
1029                 
1030                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1031                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1032                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1033                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1034                         ast_odbc_release_obj(obj);
1035                         goto yuck;
1036                 }
1037                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1038                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1039                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1040                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1041                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1042                         ast_odbc_release_obj(obj);
1043                         goto yuck;
1044                 }
1045                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1046                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1047                 res = ast_odbc_smart_execute(obj, stmt);
1048                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1049                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1050                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1051                         ast_odbc_release_obj(obj);
1052                         goto yuck;
1053                 }
1054                 res = SQLFetch(stmt);
1055                 if (res == SQL_NO_DATA) {
1056                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1057                         ast_odbc_release_obj(obj);
1058                         goto yuck;
1059                 }
1060                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1061                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1062                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1063                         ast_odbc_release_obj(obj);
1064                         goto yuck;
1065                 }
1066                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1067                 if (fd < 0) {
1068                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1069                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1070                         ast_odbc_release_obj(obj);
1071                         goto yuck;
1072                 }
1073                 res = SQLNumResultCols(stmt, &colcount);
1074                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1075                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1076                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1077                         ast_odbc_release_obj(obj);
1078                         goto yuck;
1079                 }
1080                 if (f) 
1081                         fprintf(f, "[message]\n");
1082                 for (x=0;x<colcount;x++) {
1083                         rowdata[0] = '\0';
1084                         collen = sizeof(coltitle);
1085                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1086                                                 &datatype, &colsize, &decimaldigits, &nullable);
1087                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1088                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1089                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1090                                 ast_odbc_release_obj(obj);
1091                                 goto yuck;
1092                         }
1093                         if (!strcasecmp(coltitle, "recording")) {
1094                                 off_t offset;
1095                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1096                                 fdlen = colsize2;
1097                                 if (fd > -1) {
1098                                         char tmp[1]="";
1099                                         lseek(fd, fdlen - 1, SEEK_SET);
1100                                         if (write(fd, tmp, 1) != 1) {
1101                                                 close(fd);
1102                                                 fd = -1;
1103                                                 continue;
1104                                         }
1105                                         /* Read out in small chunks */
1106                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1107                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == (void *)-1) {
1108                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1109                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1110                                                         ast_odbc_release_obj(obj);
1111                                                         goto yuck;
1112                                                 } else {
1113                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1114                                                         munmap(fdm, CHUNKSIZE);
1115                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1116                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1117                                                                 unlink(full_fn);
1118                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1119                                                                 ast_odbc_release_obj(obj);
1120                                                                 goto yuck;
1121                                                         }
1122                                                 }
1123                                         }
1124                                         truncate(full_fn, fdlen);
1125                                 }
1126                         } else {
1127                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1128                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1129                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1130                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1131                                         ast_odbc_release_obj(obj);
1132                                         goto yuck;
1133                                 }
1134                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1135                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1136                         }
1137                 }
1138                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1139                 ast_odbc_release_obj(obj);
1140         } else
1141                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1142 yuck:   
1143         if (f)
1144                 fclose(f);
1145         if (fd > -1)
1146                 close(fd);
1147         return x - 1;
1148 }
1149
1150 static int remove_file(char *dir, int msgnum)
1151 {
1152         char fn[PATH_MAX];
1153         char full_fn[PATH_MAX];
1154         char msgnums[80];
1155         
1156         if (msgnum > -1) {
1157                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1158                 make_file(fn, sizeof(fn), dir, msgnum);
1159         } else
1160                 ast_copy_string(fn, dir, sizeof(fn));
1161         ast_filedelete(fn, NULL);       
1162         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1163         unlink(full_fn);
1164         return 0;
1165 }
1166
1167 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1168 {
1169         int x = 0;
1170         int res;
1171         SQLHSTMT stmt;
1172         char sql[PATH_MAX];
1173         char rowdata[20];
1174         
1175         struct odbc_obj *obj;
1176         obj = ast_odbc_request_obj(odbc_database, 0);
1177         if (obj) {
1178                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1179                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1180                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1181                         ast_odbc_release_obj(obj);
1182                         goto yuck;
1183                 }
1184                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1185                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1186                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1187                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1188                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1189                         ast_odbc_release_obj(obj);
1190                         goto yuck;
1191                 }
1192                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1193                 res = ast_odbc_smart_execute(obj, stmt);
1194                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1195                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1196                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1197                         ast_odbc_release_obj(obj);
1198                         goto yuck;
1199                 }
1200                 res = SQLFetch(stmt);
1201                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1202                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1203                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1204                         ast_odbc_release_obj(obj);
1205                         goto yuck;
1206                 }
1207                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1208                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1209                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1210                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1211                         ast_odbc_release_obj(obj);
1212                         goto yuck;
1213                 }
1214                 if (sscanf(rowdata, "%d", &x) != 1)
1215                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1216                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1217                 ast_odbc_release_obj(obj);
1218         } else
1219                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1220 yuck:   
1221         return x - 1;
1222 }
1223
1224 static int message_exists(char *dir, int msgnum)
1225 {
1226         int x = 0;
1227         int res;
1228         SQLHSTMT stmt;
1229         char sql[PATH_MAX];
1230         char rowdata[20];
1231         char msgnums[20];
1232         
1233         struct odbc_obj *obj;
1234         obj = ast_odbc_request_obj(odbc_database, 0);
1235         if (obj) {
1236                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1237                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1238                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1239                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1240                         ast_odbc_release_obj(obj);
1241                         goto yuck;
1242                 }
1243                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1244                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1245                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1246                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1247                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1248                         ast_odbc_release_obj(obj);
1249                         goto yuck;
1250                 }
1251                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1252                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1253                 res = ast_odbc_smart_execute(obj, stmt);
1254                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1255                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1256                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1257                         ast_odbc_release_obj(obj);
1258                         goto yuck;
1259                 }
1260                 res = SQLFetch(stmt);
1261                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1262                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1263                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1264                         ast_odbc_release_obj(obj);
1265                         goto yuck;
1266                 }
1267                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1268                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1269                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1270                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1271                         ast_odbc_release_obj(obj);
1272                         goto yuck;
1273                 }
1274                 if (sscanf(rowdata, "%d", &x) != 1)
1275                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1276                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1277                 ast_odbc_release_obj(obj);
1278         } else
1279                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1280 yuck:   
1281         return x;
1282 }
1283
1284 static int count_messages(struct ast_vm_user *vmu, char *dir)
1285 {
1286         return last_message_index(vmu, dir) + 1;
1287 }
1288
1289 static void delete_file(char *sdir, int smsg)
1290 {
1291         int res;
1292         SQLHSTMT stmt;
1293         char sql[PATH_MAX];
1294         char msgnums[20];
1295         
1296         struct odbc_obj *obj;
1297         obj = ast_odbc_request_obj(odbc_database, 0);
1298         if (obj) {
1299                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1300                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1301                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1302                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1303                         ast_odbc_release_obj(obj);
1304                         goto yuck;
1305                 }
1306                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1307                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1308                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1309                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1310                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1311                         ast_odbc_release_obj(obj);
1312                         goto yuck;
1313                 }
1314                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1315                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1316                 res = ast_odbc_smart_execute(obj, stmt);
1317                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1318                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1319                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1320                         ast_odbc_release_obj(obj);
1321                         goto yuck;
1322                 }
1323                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1324                 ast_odbc_release_obj(obj);
1325         } else
1326                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1327 yuck:
1328         return; 
1329 }
1330
1331 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1332 {
1333         int res;
1334         SQLHSTMT stmt;
1335         char sql[512];
1336         char msgnums[20];
1337         char msgnumd[20];
1338         struct odbc_obj *obj;
1339
1340         delete_file(ddir, dmsg);
1341         obj = ast_odbc_request_obj(odbc_database, 0);
1342         if (obj) {
1343                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1344                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1345                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1346                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1347                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1348                         ast_odbc_release_obj(obj);
1349                         goto yuck;
1350                 }
1351                 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table); 
1352                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1353                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1354                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1355                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356                         ast_odbc_release_obj(obj);
1357                         goto yuck;
1358                 }
1359                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1360                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1361                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1362                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1363                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1364                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1365                 res = ast_odbc_smart_execute(obj, stmt);
1366                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1367                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1368                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1369                         ast_odbc_release_obj(obj);
1370                         goto yuck;
1371                 }
1372                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1373                 ast_odbc_release_obj(obj);
1374         } else
1375                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1376 yuck:
1377         return; 
1378 }
1379
1380 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1381 {
1382         int x = 0;
1383         int res;
1384         int fd = -1;
1385         void *fdm=NULL;
1386         size_t fdlen = -1;
1387         SQLHSTMT stmt;
1388         SQLINTEGER len;
1389         char sql[PATH_MAX];
1390         char msgnums[20];
1391         char fn[PATH_MAX];
1392         char full_fn[PATH_MAX];
1393         char fmt[80]="";
1394         char *c;
1395         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1396         const char *category = "";
1397         struct ast_config *cfg=NULL;
1398         struct odbc_obj *obj;
1399
1400         delete_file(dir, msgnum);
1401         obj = ast_odbc_request_obj(odbc_database, 0);
1402         if (obj) {
1403                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1404                 c = strchr(fmt, '|');
1405                 if (c)
1406                         *c = '\0';
1407                 if (!strcasecmp(fmt, "wav49"))
1408                         strcpy(fmt, "WAV");
1409                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1410                 if (msgnum > -1)
1411                         make_file(fn, sizeof(fn), dir, msgnum);
1412                 else
1413                         ast_copy_string(fn, dir, sizeof(fn));
1414                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1415                 cfg = ast_config_load(full_fn);
1416                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1417                 fd = open(full_fn, O_RDWR);
1418                 if (fd < 0) {
1419                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1420                         ast_odbc_release_obj(obj);
1421                         goto yuck;
1422                 }
1423                 if (cfg) {
1424                         context = ast_variable_retrieve(cfg, "message", "context");
1425                         if (!context) context = "";
1426                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1427                         if (!macrocontext) macrocontext = "";
1428                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1429                         if (!callerid) callerid = "";
1430                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1431                         if (!origtime) origtime = "";
1432                         duration = ast_variable_retrieve(cfg, "message", "duration");
1433                         if (!duration) duration = "";
1434                         category = ast_variable_retrieve(cfg, "message", "category");
1435                         if (!category) category = "";
1436                 }
1437                 fdlen = lseek(fd, 0, SEEK_END);
1438                 lseek(fd, 0, SEEK_SET);
1439                 printf("Length is %zd\n", fdlen);
1440                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1441                 if (!fdm) {
1442                         ast_log(LOG_WARNING, "Memory map failed!\n");
1443                         ast_odbc_release_obj(obj);
1444                         goto yuck;
1445                 } 
1446                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1447                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1448                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1449                         ast_odbc_release_obj(obj);
1450                         goto yuck;
1451                 }
1452                 if (!ast_strlen_zero(category)) 
1453                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1454                 else
1455                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1456                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1457                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1458                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1459                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1460                         ast_odbc_release_obj(obj);
1461                         goto yuck;
1462                 }
1463                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1464                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1465                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1466                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1467                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1468                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1469                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1470                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1471                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1472                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1473                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1474                 if (!ast_strlen_zero(category))
1475                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1476                 res = ast_odbc_smart_execute(obj, stmt);
1477                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1478                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1479                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1480                         ast_odbc_release_obj(obj);
1481                         goto yuck;
1482                 }
1483                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1484                 ast_odbc_release_obj(obj);
1485         } else
1486                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1487 yuck:   
1488         if (cfg)
1489                 ast_config_destroy(cfg);
1490         if (fdm)
1491                 munmap(fdm, fdlen);
1492         if (fd > -1)
1493                 close(fd);
1494         return x;
1495 }
1496
1497 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1498 {
1499         int res;
1500         SQLHSTMT stmt;
1501         char sql[PATH_MAX];
1502         char msgnums[20];
1503         char msgnumd[20];
1504         struct odbc_obj *obj;
1505
1506         delete_file(ddir, dmsg);
1507         obj = ast_odbc_request_obj(odbc_database, 0);
1508         if (obj) {
1509                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1510                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1511                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1512                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1513                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1514                         ast_odbc_release_obj(obj);
1515                         goto yuck;
1516                 }
1517                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1518                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1519                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1520                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1521                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1522                         ast_odbc_release_obj(obj);
1523                         goto yuck;
1524                 }
1525                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1526                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1527                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1528                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1529                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1530                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1531                 res = ast_odbc_smart_execute(obj, stmt);
1532                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1533                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1534                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1535                         ast_odbc_release_obj(obj);
1536                         goto yuck;
1537                 }
1538                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1539                 ast_odbc_release_obj(obj);
1540         } else
1541                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1542 yuck:
1543         return; 
1544 }
1545
1546 #else
1547 #ifndef IMAP_STORAGE
1548 static int count_messages(struct ast_vm_user *vmu, char *dir)
1549 {
1550         /* Find all .txt files - even if they are not in sequence from 0000 */
1551
1552         int vmcount = 0;
1553         DIR *vmdir = NULL;
1554         struct dirent *vment = NULL;
1555
1556         if (vm_lock_path(dir))
1557                 return ERROR_LOCK_PATH;
1558
1559         if ((vmdir = opendir(dir))) {
1560                 while ((vment = readdir(vmdir))) {
1561                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1562                                 vmcount++;
1563                 }
1564                 closedir(vmdir);
1565         }
1566         ast_unlock_path(dir);
1567         
1568         return vmcount;
1569 }
1570
1571 static void rename_file(char *sfn, char *dfn)
1572 {
1573         char stxt[PATH_MAX];
1574         char dtxt[PATH_MAX];
1575         ast_filerename(sfn,dfn,NULL);
1576         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1577         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1578         rename(stxt, dtxt);
1579 }
1580
1581 static int copy(char *infile, char *outfile)
1582 {
1583         int ifd;
1584         int ofd;
1585         int res;
1586         int len;
1587         char buf[4096];
1588
1589 #ifdef HARDLINK_WHEN_POSSIBLE
1590         /* Hard link if possible; saves disk space & is faster */
1591         if (link(infile, outfile)) {
1592 #endif
1593                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1594                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1595                         return -1;
1596                 }
1597                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1598                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1599                         close(ifd);
1600                         return -1;
1601                 }
1602                 do {
1603                         len = read(ifd, buf, sizeof(buf));
1604                         if (len < 0) {
1605                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1606                                 close(ifd);
1607                                 close(ofd);
1608                                 unlink(outfile);
1609                         }
1610                         if (len) {
1611                                 res = write(ofd, buf, len);
1612                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1613                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1614                                         close(ifd);
1615                                         close(ofd);
1616                                         unlink(outfile);
1617                                 }
1618                         }
1619                 } while (len);
1620                 close(ifd);
1621                 close(ofd);
1622                 return 0;
1623 #ifdef HARDLINK_WHEN_POSSIBLE
1624         } else {
1625                 /* Hard link succeeded */
1626                 return 0;
1627         }
1628 #endif
1629 }
1630
1631 static void copy_file(char *frompath, char *topath)
1632 {
1633         char frompath2[PATH_MAX], topath2[PATH_MAX];
1634         ast_filecopy(frompath, topath, NULL);
1635         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1636         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1637         copy(frompath2, topath2);
1638 }
1639 #endif
1640
1641 /*! \brief
1642  * A negative return value indicates an error.
1643  * \note Should always be called with a lock already set on dir.
1644  */
1645 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1646 {
1647         int x;
1648         unsigned char map[MAXMSGLIMIT] = "";
1649         DIR *msgdir;
1650         struct dirent *msgdirent;
1651         int msgdirint;
1652
1653         /* Reading the entire directory into a file map scales better than
1654          * doing a stat repeatedly on a predicted sequence.  I suspect this
1655          * is partially due to stat(2) internally doing a readdir(2) itself to
1656          * find each file. */
1657         msgdir = opendir(dir);
1658         while ((msgdirent = readdir(msgdir))) {
1659                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1660                         map[msgdirint] = 1;
1661         }
1662         closedir(msgdir);
1663
1664         for (x = 0; x < vmu->maxmsg; x++) {
1665                 if (map[x] == 0)
1666                         break;
1667         }
1668
1669         return x - 1;
1670 }
1671
1672 static int vm_delete(char *file)
1673 {
1674         char *txt;
1675         int txtsize = 0;
1676
1677         txtsize = (strlen(file) + 5)*sizeof(char);
1678         txt = alloca(txtsize);
1679         /* Sprintf here would safe because we alloca'd exactly the right length,
1680          * but trying to eliminate all sprintf's anyhow
1681          */
1682         snprintf(txt, txtsize, "%s.txt", file);
1683         unlink(txt);
1684         return ast_filedelete(file, NULL);
1685 }
1686
1687
1688 #endif
1689 static int inbuf(struct baseio *bio, FILE *fi)
1690 {
1691         int l;
1692
1693         if (bio->ateof)
1694                 return 0;
1695
1696         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1697                 if (ferror(fi))
1698                         return -1;
1699
1700                 bio->ateof = 1;
1701                 return 0;
1702         }
1703
1704         bio->iolen= l;
1705         bio->iocp= 0;
1706
1707         return 1;
1708 }
1709
1710 static int inchar(struct baseio *bio, FILE *fi)
1711 {
1712         if (bio->iocp>=bio->iolen) {
1713                 if (!inbuf(bio, fi))
1714                         return EOF;
1715         }
1716
1717         return bio->iobuf[bio->iocp++];
1718 }
1719
1720 static int ochar(struct baseio *bio, int c, FILE *so)
1721 {
1722         if (bio->linelength >= BASELINELEN) {
1723                 if (fputs(eol,so) == EOF)
1724                         return -1;
1725
1726                 bio->linelength= 0;
1727         }
1728
1729         if (putc(((unsigned char)c),so) == EOF)
1730                 return -1;
1731
1732         bio->linelength++;
1733
1734         return 1;
1735 }
1736
1737 static int base_encode(char *filename, FILE *so)
1738 {
1739         unsigned char dtable[BASEMAXINLINE];
1740         int i,hiteof= 0;
1741         FILE *fi;
1742         struct baseio bio;
1743
1744         memset(&bio, 0, sizeof(bio));
1745         bio.iocp = BASEMAXINLINE;
1746
1747         if (!(fi = fopen(filename, "rb"))) {
1748                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1749                 return -1;
1750         }
1751
1752         for (i= 0; i<9; i++) {
1753                 dtable[i]= 'A'+i;
1754                 dtable[i+9]= 'J'+i;
1755                 dtable[26+i]= 'a'+i;
1756                 dtable[26+i+9]= 'j'+i;
1757         }
1758         for (i= 0; i<8; i++) {
1759                 dtable[i+18]= 'S'+i;
1760                 dtable[26+i+18]= 's'+i;
1761         }
1762         for (i= 0; i<10; i++) {
1763                 dtable[52+i]= '0'+i;
1764         }
1765         dtable[62]= '+';
1766         dtable[63]= '/';
1767
1768         while (!hiteof){
1769                 unsigned char igroup[3], ogroup[4];
1770                 int c,n;
1771
1772                 igroup[0]= igroup[1]= igroup[2]= 0;
1773
1774                 for (n= 0;n<3;n++) {
1775                         if ((c = inchar(&bio, fi)) == EOF) {
1776                                 hiteof= 1;
1777                                 break;
1778                         }
1779
1780                         igroup[n]= (unsigned char)c;
1781                 }
1782
1783                 if (n> 0) {
1784                         ogroup[0]= dtable[igroup[0]>>2];
1785                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1786                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1787                         ogroup[3]= dtable[igroup[2]&0x3F];
1788
1789                         if (n<3) {
1790                                 ogroup[3]= '=';
1791
1792                                 if (n<2)
1793                                         ogroup[2]= '=';
1794                         }
1795
1796                         for (i= 0;i<4;i++)
1797                                 ochar(&bio, ogroup[i], so);
1798                 }
1799         }
1800
1801         if (fputs(eol,so) == EOF)
1802                 return 0;
1803
1804         fclose(fi);
1805
1806         return 1;
1807 }
1808
1809 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
1810 {
1811         char callerid[256];
1812         /* Prepare variables for substitution in email body and subject */
1813         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1814         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1815         snprintf(passdata, passdatasize, "%d", msgnum);
1816         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1817         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1818         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1819         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1820         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1821         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1822         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1823         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1824 }
1825
1826 static char *quote(const char *from, char *to, size_t len)
1827 {
1828         char *ptr = to;
1829         *ptr++ = '"';
1830         for (; ptr < to + len - 1; from++) {
1831                 if (*from == '"')
1832                         *ptr++ = '\\';
1833                 else if (*from == '\0')
1834                         break;
1835                 *ptr++ = *from;
1836         }
1837         if (ptr < to + len - 1)
1838                 *ptr++ = '"';
1839         *ptr = '\0';
1840         return to;
1841 }
1842
1843 /*! \brief
1844  * fill in *tm for current time according to the proper timezone, if any.
1845  * Return tm so it can be used as a function argument.
1846  */
1847 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1848 {
1849         const struct vm_zone *z = NULL;
1850         time_t t = time(NULL);
1851
1852         /* Does this user have a timezone specified? */
1853         if (!ast_strlen_zero(vmu->zonetag)) {
1854                 /* Find the zone in the list */
1855                 AST_LIST_LOCK(&zones);
1856                 AST_LIST_TRAVERSE(&zones, z, list) {
1857                         if (!strcmp(z->name, vmu->zonetag))
1858                                 break;
1859                 }
1860                 AST_LIST_UNLOCK(&zones);
1861         }
1862         ast_localtime(&t, tm, z ? z->timezone : NULL);
1863         return tm;
1864 }
1865
1866 /*! \brief same as mkstemp, but return a FILE * */
1867 static FILE *vm_mkftemp(char *template)
1868 {
1869         FILE *p = NULL;
1870         int pfd = mkstemp(template);
1871         if (pfd > -1) {
1872                 p = fdopen(pfd, "w+");
1873                 if (!p) {
1874                         close(pfd);
1875                         pfd = -1;
1876                 }
1877         }
1878         return p;
1879 }
1880
1881 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
1882 {
1883         char date[256];
1884         char host[MAXHOSTNAMELEN] = "";
1885         char who[256];
1886         char bound[256];
1887         char fname[256];
1888         char dur[256];
1889         char tmpcmd[256];
1890         struct tm tm;
1891         char *passdata2;
1892         size_t len_passdata;
1893 #ifdef IMAP_STORAGE
1894 #define ENDL "\r\n"
1895 #else
1896 #define ENDL "\n"
1897 #endif
1898
1899         gethostname(host, sizeof(host)-1);
1900         if (strchr(srcemail, '@'))
1901                 ast_copy_string(who, srcemail, sizeof(who));
1902         else 
1903                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1904         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1905         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1906         fprintf(p, "Date: %s" ENDL, date);
1907
1908         /* Set date format for voicemail mail */
1909         strftime(date, sizeof(date), emaildateformat, &tm);
1910
1911         if (!ast_strlen_zero(fromstring)) {
1912                 struct ast_channel *ast;
1913                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1914                         char *passdata;
1915                         int vmlen = strlen(fromstring)*3 + 200;
1916                         if ((passdata = alloca(vmlen))) {
1917                                 memset(passdata, 0, vmlen);
1918                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1919                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1920                                 len_passdata = strlen(passdata) * 2 + 3;
1921                                 passdata2 = alloca(len_passdata);
1922                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1923                         } else
1924                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1925                         ast_channel_free(ast);
1926                 } else
1927                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1928         } else
1929                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1930         len_passdata = strlen(vmu->fullname) * 2 + 3;
1931         passdata2 = alloca(len_passdata);
1932         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1933         if (!ast_strlen_zero(emailsubject)) {
1934                 struct ast_channel *ast;
1935                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1936                         char *passdata;
1937                         int vmlen = strlen(emailsubject) * 3 + 200;
1938                         if ((passdata = alloca(vmlen))) {
1939                                 memset(passdata, 0, vmlen);
1940                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1941                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1942                                 fprintf(p, "Subject: %s" ENDL, passdata);
1943                         } else
1944                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1945                         ast_channel_free(ast);
1946                 } else
1947                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1948         } else  if (!ast_strlen_zero(emailtitle)) {
1949                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1950                 fprintf(p, ENDL) ;
1951         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1952                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1953         else
1954                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1955         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1956         if(imap) {
1957                 /* additional information needed for IMAP searching */
1958                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1959                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1960                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1961                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1962                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1963                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1964                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1965                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1966                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1967                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1968                 if (!ast_strlen_zero(category))
1969                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1970                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1971                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1972         }
1973         if (!ast_strlen_zero(cidnum))
1974                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1975         if (!ast_strlen_zero(cidname))
1976                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1977         fprintf(p, "MIME-Version: 1.0" ENDL);
1978         if (attach_user_voicemail) {
1979                 /* Something unique. */
1980                 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum + 1, mailbox, getpid(), (unsigned int)ast_random());
1981
1982                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL ENDL ENDL, bound);
1983
1984                 fprintf(p, "--%s" ENDL, bound);
1985         }
1986         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1987         if (emailbody) {
1988                 struct ast_channel *ast;
1989                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1990                         char *passdata;
1991                         int vmlen = strlen(emailbody)*3 + 200;
1992                         if ((passdata = alloca(vmlen))) {
1993                                 memset(passdata, 0, vmlen);
1994                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1995                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1996                                 fprintf(p, "%s" ENDL, passdata);
1997                         } else
1998                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1999                         ast_channel_free(ast);
2000                 } else
2001                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2002         } else {
2003                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2004
2005                 "in mailbox %s from %s, on %s so you might" ENDL
2006                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2007                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2008         }
2009         if (attach_user_voicemail) {
2010                 /* Eww. We want formats to tell us their own MIME type */
2011                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2012                 char tmpdir[256], newtmp[256];
2013                 int tmpfd;
2014         
2015                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2016                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2017                 tmpfd = mkstemp(newtmp);
2018                 if (option_debug > 2)
2019                         ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2020                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2021                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2022                         ast_safe_system(tmpcmd);
2023                         attach = newtmp;
2024                         if (option_debug > 2)
2025                                 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2026                 }
2027                 fprintf(p, "--%s" ENDL, bound);
2028                 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2029                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2030                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2031                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2032                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2033                 base_encode(fname, p);
2034                 /* only attach if necessary */
2035                 if (imap && !strcmp(format, "gsm")) {
2036                         fprintf(p, "--%s" ENDL, bound);
2037                         fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"" ENDL, msgnum + 1, format);
2038                         fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2039                         fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2040                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"" ENDL ENDL, msgnum + 1);
2041                         snprintf(fname, sizeof(fname), "%s.gsm", attach);
2042                         base_encode(fname, p);
2043                 }
2044                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2045                 if (tmpfd > -1)
2046                         close(tmpfd);
2047                 unlink(newtmp);
2048         }
2049 #undef ENDL
2050 }
2051
2052 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
2053 {
2054         FILE *p=NULL;
2055         char tmp[80] = "/tmp/astmail-XXXXXX";
2056         char tmp2[256];
2057
2058         if (vmu && ast_strlen_zero(vmu->email)) {
2059                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2060                 return(0);
2061         }
2062         if (!strcmp(format, "wav49"))
2063                 format = "WAV";
2064         if (option_debug > 2)
2065                 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
2066         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2067            command hangs */
2068         if ((p = vm_mkftemp(tmp)) == NULL) {
2069                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2070                 return -1;
2071         } else {
2072                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2073                 fclose(p);
2074                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2075                 ast_safe_system(tmp2);
2076                 if (option_debug)
2077                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2078         }
2079         return 0;
2080 }
2081
2082 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
2083 {
2084         char date[256];
2085         char host[MAXHOSTNAMELEN] = "";
2086         char who[256];
2087         char dur[PATH_MAX];
2088         char tmp[80] = "/tmp/astmail-XXXXXX";
2089         char tmp2[PATH_MAX];
2090         struct tm tm;
2091         FILE *p;
2092
2093         if ((p = vm_mkftemp(tmp)) == NULL) {
2094                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2095                 return -1;
2096         }
2097         gethostname(host, sizeof(host)-1);
2098         if (strchr(srcemail, '@'))
2099                 ast_copy_string(who, srcemail, sizeof(who));
2100         else 
2101                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2102         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2103         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2104         fprintf(p, "Date: %s\n", date);
2105
2106         if (*pagerfromstring) {
2107                 struct ast_channel *ast;
2108                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2109                         char *passdata;
2110                         int vmlen = strlen(fromstring)*3 + 200;
2111                         if ((passdata = alloca(vmlen))) {
2112                                 memset(passdata, 0, vmlen);
2113                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2114                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2115                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2116                         } else 
2117                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2118                         ast_channel_free(ast);
2119                 } else 
2120                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2121         } else
2122                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2123         fprintf(p, "To: %s\n", pager);
2124         if (pagersubject) {
2125                 struct ast_channel *ast;
2126                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2127                         char *passdata;
2128                         int vmlen = strlen(pagersubject) * 3 + 200;
2129                         if ((passdata = alloca(vmlen))) {
2130                                 memset(passdata, 0, vmlen);
2131                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2132                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2133                                 fprintf(p, "Subject: %s\n\n", passdata);
2134                         } else
2135                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2136                         ast_channel_free(ast);
2137                 } else
2138                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2139         } else
2140                 fprintf(p, "Subject: New VM\n\n");
2141
2142         strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2143         if (pagerbody) {
2144                 struct ast_channel *ast;
2145                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2146                         char *passdata;
2147                         int vmlen = strlen(pagerbody)*3 + 200;
2148                         if ((passdata = alloca(vmlen))) {
2149                                 memset(passdata, 0, vmlen);
2150                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2151                                 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2152                                 fprintf(p, "%s\n", passdata);
2153                         } else
2154                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2155                         ast_channel_free(ast);
2156                 } else
2157                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2158         } else {
2159                 fprintf(p, "New %s long msg in box %s\n"
2160                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2161         }
2162         fclose(p);
2163         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2164         ast_safe_system(tmp2);
2165         if (option_debug)
2166                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2167         return 0;
2168 }
2169
2170 static int get_date(char *s, int len)
2171 {
2172         struct tm tm;
2173         time_t t;
2174         t = time(0);
2175         localtime_r(&t,&tm);
2176         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2177 }
2178
2179 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2180 {
2181         int res;
2182         char fn[PATH_MAX];
2183         char dest[PATH_MAX];
2184
2185         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2186
2187         if (!(res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2188                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2189                 return -1;
2190         }
2191
2192         RETRIEVE(fn, -1);
2193         if (ast_fileexists(fn, NULL, NULL) > 0) {
2194                 res = ast_stream_and_wait(chan, fn, ecodes);
2195                 if (res) {
2196                         DISPOSE(fn, -1);
2197                         return res;
2198                 }
2199         } else {
2200                 /* Dispose just in case */
2201                 DISPOSE(fn, -1);
2202                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2203                 if (res)
2204                         return res;
2205                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2206                 if (res)
2207                         return res;
2208         }
2209         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2210         return res;
2211 }
2212
2213 static void free_user(struct ast_vm_user *vmu)
2214 {
2215         if (ast_test_flag(vmu, VM_ALLOCED))
2216                 free(vmu);
2217 }
2218
2219 static void free_zone(struct vm_zone *z)
2220 {
2221         free(z);
2222 }
2223
2224 static const char *mbox(int id)
2225 {
2226         static const char *msgs[] = {
2227                 "INBOX",
2228                 "Old",
2229                 "Work",
2230                 "Family",
2231                 "Friends",
2232                 "Cust1",
2233                 "Cust2",
2234                 "Cust3",
2235                 "Cust4",
2236                 "Cust5",
2237         };
2238         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2239 }
2240
2241 #ifdef ODBC_STORAGE
2242 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2243 {
2244         int x = -1;
2245         int res;
2246         SQLHSTMT stmt;
2247         char sql[PATH_MAX];
2248         char rowdata[20];
2249         char tmp[PATH_MAX] = "";
2250         struct odbc_obj *obj;
2251         char *context;
2252
2253         if (newmsgs)
2254                 *newmsgs = 0;
2255         if (oldmsgs)
2256                 *oldmsgs = 0;
2257
2258         /* If no mailbox, return immediately */
2259         if (ast_strlen_zero(mailbox))
2260                 return 0;
2261
2262         ast_copy_string(tmp, mailbox, sizeof(tmp));
2263         
2264         context = strchr(tmp, '@');
2265         if (context) {
2266                 *context = '\0';
2267                 context++;
2268         } else
2269                 context = "default";
2270         
2271         obj = ast_odbc_request_obj(odbc_database, 0);
2272         if (obj) {
2273                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2274                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2275                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2276                         ast_odbc_release_obj(obj);
2277                         goto yuck;
2278                 }
2279                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2280                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2281                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2282                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2283                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2284                         ast_odbc_release_obj(obj);
2285                         goto yuck;
2286                 }
2287                 res = ast_odbc_smart_execute(obj, stmt);
2288                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2289                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2290                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2291                         ast_odbc_release_obj(obj);
2292                         goto yuck;
2293                 }
2294                 res = SQLFetch(stmt);
2295                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2296                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2297                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2298                         ast_odbc_release_obj(obj);
2299                         goto yuck;
2300                 }
2301                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2302                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2303                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2304                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2305                         ast_odbc_release_obj(obj);
2306                         goto yuck;
2307                 }
2308                 *newmsgs = atoi(rowdata);
2309                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2310
2311                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2312                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2313                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2314                         ast_odbc_release_obj(obj);
2315                         goto yuck;
2316                 }
2317                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2318                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2319                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2320                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2321                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2322                         ast_odbc_release_obj(obj);
2323                         goto yuck;
2324                 }
2325                 res = ast_odbc_smart_execute(obj, stmt);
2326                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2327                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2328                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2329                         ast_odbc_release_obj(obj);
2330                         goto yuck;
2331                 }
2332                 res = SQLFetch(stmt);
2333                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2334                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2335                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2336                         ast_odbc_release_obj(obj);
2337                         goto yuck;
2338                 }
2339                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2340                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2341                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2342                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2343                         ast_odbc_release_obj(obj);
2344                         goto yuck;
2345                 }
2346                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2347                 ast_odbc_release_obj(obj);
2348                 *oldmsgs = atoi(rowdata);
2349                 x = 0;
2350         } else
2351                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2352                 
2353 yuck:   
2354         return x;
2355 }
2356
2357 static int messagecount(const char *context, const char *mailbox, const char *folder)
2358 {
2359         struct odbc_obj *obj = NULL;
2360         int nummsgs = 0;
2361         int res;
2362         SQLHSTMT stmt = NULL;
2363         char sql[PATH_MAX];
2364         char rowdata[20];
2365         if (!folder)
2366                 folder = "INBOX";
2367         /* If no mailbox, return immediately */
2368         if (ast_strlen_zero(mailbox))
2369                 return 0;
2370
2371         obj = ast_odbc_request_obj(odbc_database, 0);
2372         if (obj) {
2373                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2374                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2375                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2376                         goto yuck;
2377                 }
2378                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2379                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2380                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2381                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2382                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2383                         goto yuck;
2384                 }
2385                 res = ast_odbc_smart_execute(obj, stmt);
2386                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2387                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2388                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2389                         goto yuck;
2390                 }
2391                 res = SQLFetch(stmt);
2392                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2393                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2394                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2395                         goto yuck;
2396                 }
2397                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2398                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2399                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2400                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2401                         goto yuck;
2402                 }
2403                 nummsgs = atoi(rowdata);
2404                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2405         } else
2406                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2407
2408 yuck:
2409         if (obj)
2410                 ast_odbc_release_obj(obj);
2411         return nummsgs;
2412 }
2413
2414 static int has_voicemail(const char *mailbox, const char *folder)
2415 {
2416         char *context, tmp[256];
2417         ast_copy_string(tmp, mailbox, sizeof(tmp));
2418         if ((context = strchr(tmp, '@')))
2419                 *context++ = '\0';
2420         else
2421                 context = "default";
2422
2423         if (messagecount(context, tmp, folder))
2424                 return 1;
2425         else
2426                 return 0;
2427 }
2428
2429 #elif defined(IMAP_STORAGE)
2430
2431 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)
2432 {
2433         char *myserveremail = serveremail;
2434         char fn[PATH_MAX];
2435         char mailbox[256];
2436         char *stringp;
2437         FILE *p=NULL;
2438         char tmp[80] = "/tmp/astmail-XXXXXX";
2439         long len;
2440         void *buf;
2441         STRING str;
2442         
2443         /* Attach only the first format */
2444         fmt = ast_strdupa(fmt);
2445         stringp = fmt;
2446         strsep(&stringp, "|");
2447
2448         if (!ast_strlen_zero(vmu->serveremail))
2449                 myserveremail = vmu->serveremail;
2450
2451         make_file(fn, sizeof(fn), dir, msgnum);
2452
2453         if (ast_strlen_zero(vmu->email))
2454                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2455
2456         if (!strcmp(fmt, "wav49"))
2457                 fmt = "WAV";
2458         if(option_debug > 2)
2459                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2460
2461         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2462            command hangs */
2463         if (!(p = vm_mkftemp(tmp))) {
2464                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2465                 return -1;
2466         }
2467         
2468         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, fmt, duration, 1, chan, NULL, 1);
2469         /* read mail file to memory */          
2470         len = ftell(p);
2471         rewind(p);
2472         if (!(buf = ast_malloc(len+1))) {
2473                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2474                 return -1;
2475         }
2476         fread(buf, len, 1, p);
2477         ((char *)buf)[len] = '\0';
2478         INIT(&str, mail_string, buf, len);
2479         init_mailstream(vms, 0);
2480         imap_mailbox_name(mailbox, vms, 0, 1);
2481         if(!mail_append(vms->mailstream, mailbox, &str))
2482                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2483         fclose(p);
2484         unlink(tmp);
2485         ast_free(buf);
2486         if(option_debug > 2)
2487                 ast_log(LOG_DEBUG, "%s stored\n", fn);
2488         return 0;
2489
2490 }
2491
2492 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2493 {
2494         SEARCHPGM *pgm;
2495         SEARCHHEADER *hdr;
2496  
2497         struct ast_vm_user *vmu;
2498         struct vm_state *vms_p;
2499         char tmp[256] = "";
2500         char *mb, *cur;
2501         char *mailboxnc; 
2502         char *context;
2503         int ret = 0;
2504
2505         if (newmsgs)
2506                 *newmsgs = 0;
2507
2508         if (oldmsgs)
2509                 *oldmsgs = 0;
2510  
2511         if(option_debug > 2)
2512                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2513
2514         /* If no mailbox, return immediately */
2515         if (ast_strlen_zero(mailbox))
2516                 return 0;
2517
2518         if (strchr(mailbox, ',')) {
2519                 int tmpnew, tmpold;
2520                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2521                 mb = tmp;
2522                 ret = 0;
2523                 while((cur = strsep(&mb, ", "))) {
2524                         if (!ast_strlen_zero(cur)) {
2525                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2526                                         return -1;
2527                                 else {
2528                                         if (newmsgs)
2529                                                 *newmsgs += tmpnew; 
2530                                         if (oldmsgs)
2531                                                 *oldmsgs += tmpold;
2532                                 }
2533                         }
2534                 }
2535                 return 0;
2536         }
2537
2538         ast_copy_string(tmp, mailbox, sizeof(tmp));
2539
2540         if ((context = strchr(tmp, '@'))) {
2541                 *context = '\0';
2542                 mailboxnc = tmp;
2543                 context++;
2544         } else {
2545                 context = "default";
2546                 mailboxnc = (char *)mailbox;
2547         }
2548  
2549         /* We have to get the user before we can open the stream! */
2550         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2551         if (!(vmu = find_user(NULL, context, mailboxnc))) {
2552                 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2553                 return -1;
2554         }
2555         
2556         /* No IMAP account available */
2557         if (vmu->imapuser[0] == '\0') {
2558                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2559                 return -1;
2560         }
2561  
2562         /* check if someone is accessing this box right now... */
2563         if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2564                 if(option_debug > 2)
2565                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2566                 *newmsgs = vms_p->newmessages;
2567                 *oldmsgs = vms_p->oldmessages;
2568                 return 0;
2569         }
2570  
2571         /* add one if not there... */
2572         if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2573                 if(option_debug > 2)
2574                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2575                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2576                         return -1;
2577                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2578                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2579                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2580                 if(option_debug > 2)
2581                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2582                 vms_p->updated = 1;
2583                 /* set mailbox to INBOX! */
2584                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2585                 init_vm_state(vms_p);
2586                 vmstate_insert(vms_p);
2587         }
2588
2589         /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2590         ret = init_mailstream(vms_p, 0);
2591         if (!vms_p->mailstream) {
2592                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2593                 return -1;
2594         }
2595
2596         if (!ret && vms_p->updated == 1) {
2597                 if (newmsgs) {
2598                         pgm = mail_newsearchpgm();
2599                         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2600                         pgm->header = hdr;
2601                         pgm->unseen = 1;
2602                         pgm->seen = 0;
2603                         pgm->undeleted = 1;
2604                         pgm->deleted = 0;
2605                         vms_p->vmArrayIndex = 0;
2606                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2607                         *newmsgs = vms_p->vmArrayIndex;
2608                         vms_p->newmessages = vms_p->vmArrayIndex;
2609                         mail_free_searchpgm(&pgm);
2610                 }
2611                 if (oldmsgs) {
2612                         pgm = mail_newsearchpgm ();
2613                         hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2614                         pgm->header = hdr;
2615                         pgm->unseen = 0;
2616                         pgm->seen = 1;
2617                         pgm->undeleted = 1;
2618                         pgm->deleted = 0;
2619                         vms_p->vmArrayIndex = 0;
2620                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2621                         *oldmsgs = vms_p->vmArrayIndex;
2622                         vms_p->oldmessages = vms_p->vmArrayIndex;
2623                         mail_free_searchpgm(&pgm);
2624                 }
2625         }
2626
2627         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2628                 vms_p->updated = 0;
2629         } else if (vms_p->updated > 1) {  /* decrement delay count */
2630                 vms_p->updated--;
2631         } else {  /* no changes, so don't search */
2632                 mail_ping(vms_p->mailstream);
2633                 /* Keep the old data */
2634                 *newmsgs = vms_p->newmessages;
2635                 *oldmsgs = vms_p->oldmessages;
2636         }
2637
2638         return 0;
2639  }
2640
2641 static int has_voicemail(const char *mailbox, const char *folder)
2642 {
2643         int newmsgs, oldmsgs;
2644         
2645         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2646                 return folder? oldmsgs: newmsgs;
2647         else
2648                 return 0;
2649 }
2650
2651 static int messagecount(const char *context, const char *mailbox, const char *folder)
2652 {
2653         int newmsgs, oldmsgs;
2654         char tmp[256] = "";
2655         
2656         if (ast_strlen_zero(mailbox))
2657                 return 0;
2658         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2659
2660         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2661                 return folder? oldmsgs: newmsgs;
2662         else
2663                 return 0;
2664 }
2665
2666 #endif
2667 #ifndef IMAP_STORAGE
2668 /* copy message only used by file storage */
2669 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)
2670 {
2671         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2672         const char *frombox = mbox(imbox);
2673         int recipmsgnum;
2674
2675         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2676
2677         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2678         
2679         if (!dir)
2680                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2681         else
2682                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2683
2684         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2685         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2686
2687         if (vm_lock_path(todir))
2688                 return ERROR_LOCK_PATH;
2689
2690         recipmsgnum = last_message_index(recip, todir) + 1;
2691         if (recipmsgnum < recip->maxmsg) {
2692                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2693                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2694         } else {
2695                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2696         }
2697         ast_unlock_path(todir);
2698         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2699         
2700         return 0;
2701 }
2702 #endif
2703 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2704 static int messagecount(const char *context, const char *mailbox, const char *folder)
2705 {
2706         return __has_voicemail(context, mailbox, folder, 0);
2707 }
2708
2709
2710 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2711 {
2712         DIR *dir;
2713         struct dirent *de;
2714         char fn[256];
2715         int ret = 0;
2716
2717         /* If no mailbox, return immediately */
2718         if (ast_strlen_zero(mailbox))
2719                 return 0;
2720
2721         if (ast_strlen_zero(folder))
2722                 folder = "INBOX";
2723         if (ast_strlen_zero(context))
2724                 context = "default";
2725
2726         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2727
2728         if (!(dir = opendir(fn)))
2729                 return 0;
2730
2731         while ((de = readdir(dir))) {
2732                 if (!strncasecmp(de->d_name, "msg", 3)) {
2733                         if (shortcircuit) {
2734                                 ret = 1;
2735                                 break;
2736                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2737                                 ret++;
2738                 }
2739         }
2740
2741         closedir(dir);
2742
2743         return ret;
2744 }
2745
2746
2747 static int has_voicemail(const char *mailbox, const char *folder)
2748 {
2749         char tmp[256], *tmp2 = tmp, *mbox, *context;
2750         ast_copy_string(tmp, mailbox, sizeof(tmp));
2751         while ((mbox = strsep(&tmp2, ","))) {
2752                 if ((context = strchr(mbox, '@')))
2753                         *context++ = '\0';
2754                 else
2755                         context = "default";
2756                 if (__has_voicemail(context, mbox, folder, 1))
2757                         return 1;
2758         }
2759         return 0;
2760 }
2761
2762
2763 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2764 {
2765         char tmp[256];
2766         char *context;
2767
2768         /* If no mailbox, return immediately */
2769         if (ast_strlen_zero(mailbox))
2770                 return 0;
2771
2772         if (newmsgs)
2773                 *newmsgs = 0;
2774         if (oldmsgs)
2775                 *oldmsgs = 0;
2776
2777         if (strchr(mailbox, ',')) {
2778                 int tmpnew, tmpold;
2779                 char *mb, *cur;
2780
2781                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2782                 mb = tmp;
2783                 while ((cur = strsep(&mb, ", "))) {
2784                         if (!ast_strlen_zero(cur)) {
2785                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2786                                         return -1;
2787                                 else {
2788                                         if (newmsgs)
2789                                                 *newmsgs += tmpnew; 
2790                                         if (oldmsgs)
2791                                                 *oldmsgs += tmpold;
2792                                 }
2793                         }
2794                 }
2795                 return 0;
2796         }
2797
2798         ast_copy_string(tmp, mailbox, sizeof(tmp));
2799         
2800         if ((context = strchr(tmp, '@')))
2801                 *context++ = '\0';
2802         else
2803                 context = "default";
2804
2805         if (newmsgs)
2806                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2807         if (oldmsgs)
2808                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2809
2810         return 0;
2811 }
2812
2813 #endif
2814
2815 static void run_externnotify(char *context, char *extension)
2816 {
2817         char arguments[255];
2818         char ext_context[256] = "";
2819         int newvoicemails = 0, oldvoicemails = 0;
2820         struct ast_smdi_mwi_message *mwi_msg;
2821
2822         if (!ast_strlen_zero(context))
2823                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2824         else
2825                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2826
2827         if (smdi_iface) {
2828                 if (ast_app_has_voicemail(ext_context, NULL)) 
2829                         ast_smdi_mwi_set(smdi_iface, extension);
2830                 else
2831                         ast_smdi_mwi_unset(smdi_iface, extension);
2832
2833                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2834                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2835                         if (!strncmp(mwi_msg->cause, "INV", 3))
2836                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2837                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2838                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2839                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2840                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2841                 } else {
2842                         if (option_debug)
2843                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2844                 }
2845         }
2846
2847         if (!ast_strlen_zero(externnotify)) {
2848                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2849                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2850                 } else {
2851                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2852                         if (option_debug)
2853                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2854                         ast_safe_system(arguments);
2855                 }
2856         }
2857 }
2858
2859 struct leave_vm_options {
2860         unsigned int flags;
2861         signed char record_gain;
2862 };
2863
2864 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2865 {
2866 #ifdef IMAP_STORAGE
2867         int newmsgs, oldmsgs;
2868         struct vm_state *vms = NULL;
2869 #endif
2870         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2871         char callerid[256];
2872         FILE *txt;
2873         char date[256];
2874         int txtdes;
2875         int res = 0;
2876         int msgnum;
2877         int duration = 0;
2878         int ausemacro = 0;
2879         int ousemacro = 0;
2880         int ouseexten = 0;
2881         char dir[PATH_MAX], tmpdir[PATH_MAX];
2882         char dest[PATH_MAX];
2883         char fn[PATH_MAX];
2884         char prefile[PATH_MAX] = "";
2885         char tempfile[PATH_MAX] = "";
2886         char ext_context[256] = "";
2887         char fmt[80];
2888         char *context;
2889         char ecodes[16] = "#";
2890         char tmp[256] = "", *tmpptr;
2891         struct ast_vm_user *vmu;
2892         struct ast_vm_user svm;
2893         const char *category = NULL;
2894
2895         ast_copy_string(tmp, ext, sizeof(tmp));
2896         ext = tmp;
2897         if ((context = strchr(tmp, '@'))) {
2898                 *context++ = '\0';
2899                 tmpptr = strchr(context, '&');
2900         } else {
2901                 tmpptr = strchr(ext, '&');
2902         }
2903
2904         if (tmpptr)
2905                 *tmpptr++ = '\0';
2906
2907         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2908
2909         if(option_debug > 2)
2910                 ast_log(LOG_DEBUG, "Before find_user\n");
2911         if (!(vmu = find_user(&svm, context, ext))) {
2912                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2913                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2914                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2915                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2916                 return res;
2917         }
2918         /* Setup pre-file if appropriate */
2919         if (strcmp(vmu->context, "default"))
2920                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2921         else
2922                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2923         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2924                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2925                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2926         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2927                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2928                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2929         }
2930         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2931         if (!(res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2932                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2933                 return -1;
2934         }
2935         RETRIEVE(tempfile, -1);
2936         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2937                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2938         DISPOSE(tempfile, -1);
2939         /* It's easier just to try to make it than to check for its existence */
2940         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2941         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2942
2943         /* Check current or macro-calling context for special extensions */
2944         if (ast_test_flag(vmu, VM_OPERATOR)) {
2945                 if (!ast_strlen_zero(vmu->exit)) {
2946                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2947                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2948                                 ouseexten = 1;
2949                         }
2950                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2951                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2952                         ouseexten = 1;
2953                 }
2954                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2955                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2956                 ousemacro = 1;
2957                 }
2958         }
2959
2960         if (!ast_strlen_zero(vmu->exit)) {
2961                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2962                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2963         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2964                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2965         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2966                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2967                 ausemacro = 1;
2968         }
2969
2970         /* Play the beginning intro if desired */
2971         if (!ast_strlen_zero(prefile)) {
2972                 RETRIEVE(prefile, -1);
2973                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2974                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2975                                 res = ast_waitstream(chan, ecodes);
2976                 } else {
2977                         if (option_debug)
2978                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2979                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2980                 }
2981                 DISPOSE(prefile, -1);
2982                 if (res < 0) {
2983                         if (option_debug)
2984                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2985                         free_user(vmu);
2986                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2987                         return -1;
2988                 }
2989         }
2990         if (res == '#') {
2991                 /* On a '#' we skip the instructions */
2992                 ast_set_flag(options, OPT_SILENT);
2993                 res = 0;
2994         }
2995         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2996                 res = ast_stream_and_wait(chan, INTRO, ecodes);
2997                 if (res == '#') {
2998                         ast_set_flag(options, OPT_SILENT);
2999                         res = 0;
3000                 }
3001         }
3002         if (res > 0)
3003                 ast_stopstream(chan);
3004         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3005          other than the operator -- an automated attendant or mailbox login for example */
3006         if (res == '*') {
3007                 chan->exten[0] = 'a';
3008                 chan->exten[1] = '\0';
3009                 if (!ast_strlen_zero(vmu->exit)) {
3010                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3011                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3012                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3013                 }
3014                 chan->priority = 0;
3015                 free_user(vmu);
3016                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3017                 return 0;
3018         }
3019
3020         /* Check for a '0' here */
3021         if (res == '0') {
3022         transfer:
3023                 if (ouseexten || ousemacro) {
3024                         chan->exten[0] = 'o';
3025                         chan->exten[1] = '\0';
3026                         if (!ast_strlen_zero(vmu->exit)) {
3027                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3028                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3029                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3030                         }
3031                         ast_play_and_wait(chan, "transfer");
3032                         chan->priority = 0;
3033                         free_user(vmu);
3034                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3035                 }
3036                 return 0;
3037         }
3038         if (res < 0) {
3039                 free_user(vmu);
3040                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3041                 return -1;
3042         }
3043         /* The meat of recording the message...  All the announcements and beeps have been played*/
3044         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3045         if (!ast_strlen_zero(fmt)) {
3046                 msgnum = 0;
3047
3048 #ifdef IMAP_STORAGE
3049                 /* Is ext a mailbox? */