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