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