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