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