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