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