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