Merged revisions 63360 via svnmerge from
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref Unixodbc - http://www.unixodbc.org
26  * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
28  * 
29  * \par See also
30  * \arg \ref Config_vm
31  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32  * \ingroup applications
33  * \note This module requires res_adsi to load. This needs to be optional
34  * during compilation.
35  *
36  *
37  *
38  * \note  This file is now almost impossible to work with, due to all #ifdefs.
39  *        Feels like the database code before realtime. Someone - please come up
40  *        with a plan to clean this up.
41  */
42
43 /*** MODULEINFO
44         <depend>res_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                 free_user(vmu);
2646                 return -1;
2647         }
2648  
2649         /* check if someone is accessing this box right now... */
2650         if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2651                 if(option_debug > 2)
2652                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2653                 *newmsgs = vms_p->newmessages;
2654                 *oldmsgs = vms_p->oldmessages;
2655                 free_user(vmu);
2656                 return 0;
2657         }
2658  
2659         /* add one if not there... */
2660         if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2661                 if(option_debug > 2)
2662                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2663                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2664                         free_user(vmu);
2665                         return -1;
2666                 }
2667                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2668                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2669                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2670                 if(option_debug > 2)
2671                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2672                 vms_p->updated = 1;
2673                 /* set mailbox to INBOX! */
2674                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2675                 init_vm_state(vms_p);
2676                 vmstate_insert(vms_p);
2677         }
2678
2679         /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2680         ret = init_mailstream(vms_p, 0);
2681         if (!vms_p->mailstream) {
2682                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2683                 free_user(vmu);
2684                 return -1;
2685         }
2686
2687         if (!ret && vms_p->updated == 1) {
2688                 if (newmsgs) {
2689                         pgm = mail_newsearchpgm();
2690                         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2691                         pgm->header = hdr;
2692                         pgm->unseen = 1;
2693                         pgm->seen = 0;
2694                         pgm->undeleted = 1;
2695                         pgm->deleted = 0;
2696                         vms_p->vmArrayIndex = 0;
2697                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2698                         *newmsgs = vms_p->vmArrayIndex;
2699                         vms_p->newmessages = vms_p->vmArrayIndex;
2700                         mail_free_searchpgm(&pgm);
2701                 }
2702                 if (oldmsgs) {
2703                         pgm = mail_newsearchpgm ();
2704                         hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2705                         pgm->header = hdr;
2706                         pgm->unseen = 0;
2707                         pgm->seen = 1;
2708                         pgm->undeleted = 1;
2709                         pgm->deleted = 0;
2710                         vms_p->vmArrayIndex = 0;
2711                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2712                         *oldmsgs = vms_p->vmArrayIndex;
2713                         vms_p->oldmessages = vms_p->vmArrayIndex;
2714                         mail_free_searchpgm(&pgm);
2715                 }
2716         }
2717
2718         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2719                 vms_p->updated = 0;
2720         } else if (vms_p->updated > 1) {  /* decrement delay count */
2721                 vms_p->updated--;
2722         } else {  /* no changes, so don't search */
2723                 mail_ping(vms_p->mailstream);
2724                 /* Keep the old data */
2725                 *newmsgs = vms_p->newmessages;
2726                 *oldmsgs = vms_p->oldmessages;
2727         }
2728
2729         free_user(vmu);
2730         return 0;
2731  }
2732
2733 static int has_voicemail(const char *mailbox, const char *folder)
2734 {
2735         int newmsgs, oldmsgs;
2736         
2737         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2738                 return folder? oldmsgs: newmsgs;
2739         else
2740                 return 0;
2741 }
2742
2743 static int messagecount(const char *context, const char *mailbox, const char *folder)
2744 {
2745         int newmsgs, oldmsgs;
2746         char tmp[256] = "";
2747         
2748         if (ast_strlen_zero(mailbox))
2749                 return 0;
2750         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2751
2752         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2753                 return folder? oldmsgs: newmsgs;
2754         else
2755                 return 0;
2756 }
2757
2758 #endif
2759 #ifndef IMAP_STORAGE
2760 /* copy message only used by file storage */
2761 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)
2762 {
2763         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2764         const char *frombox = mbox(imbox);
2765         int recipmsgnum;
2766
2767         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2768
2769         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2770         
2771         if (!dir)
2772                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2773         else
2774                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2775
2776         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2777         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2778
2779         if (vm_lock_path(todir))
2780                 return ERROR_LOCK_PATH;
2781
2782         recipmsgnum = last_message_index(recip, todir) + 1;
2783         if (recipmsgnum < recip->maxmsg) {
2784                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2785                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2786         } else {
2787                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2788         }
2789         ast_unlock_path(todir);
2790         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2791         
2792         return 0;
2793 }
2794 #endif
2795 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2796 static int messagecount(const char *context, const char *mailbox, const char *folder)
2797 {
2798         return __has_voicemail(context, mailbox, folder, 0);
2799 }
2800
2801
2802 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2803 {
2804         DIR *dir;
2805         struct dirent *de;
2806         char fn[256];
2807         int ret = 0;
2808
2809         /* If no mailbox, return immediately */
2810         if (ast_strlen_zero(mailbox))
2811                 return 0;
2812
2813         if (ast_strlen_zero(folder))
2814                 folder = "INBOX";
2815         if (ast_strlen_zero(context))
2816                 context = "default";
2817
2818         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2819
2820         if (!(dir = opendir(fn)))
2821                 return 0;
2822
2823         while ((de = readdir(dir))) {
2824                 if (!strncasecmp(de->d_name, "msg", 3)) {
2825                         if (shortcircuit) {
2826                                 ret = 1;
2827                                 break;
2828                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2829                                 ret++;
2830                 }
2831         }
2832
2833         closedir(dir);
2834
2835         return ret;
2836 }
2837
2838
2839 static int has_voicemail(const char *mailbox, const char *folder)
2840 {
2841         char tmp[256], *tmp2 = tmp, *mbox, *context;
2842         ast_copy_string(tmp, mailbox, sizeof(tmp));
2843         while ((mbox = strsep(&tmp2, ","))) {
2844                 if ((context = strchr(mbox, '@')))
2845                         *context++ = '\0';
2846                 else
2847                         context = "default";
2848                 if (__has_voicemail(context, mbox, folder, 1))
2849                         return 1;
2850         }
2851         return 0;
2852 }
2853
2854
2855 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2856 {
2857         char tmp[256];
2858         char *context;
2859
2860         /* If no mailbox, return immediately */
2861         if (ast_strlen_zero(mailbox))
2862                 return 0;
2863
2864         if (newmsgs)
2865                 *newmsgs = 0;
2866         if (oldmsgs)
2867                 *oldmsgs = 0;
2868
2869         if (strchr(mailbox, ',')) {
2870                 int tmpnew, tmpold;
2871                 char *mb, *cur;
2872
2873                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2874                 mb = tmp;
2875                 while ((cur = strsep(&mb, ", "))) {
2876                         if (!ast_strlen_zero(cur)) {
2877                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2878                                         return -1;
2879                                 else {
2880                                         if (newmsgs)
2881                                                 *newmsgs += tmpnew; 
2882                                         if (oldmsgs)
2883                                                 *oldmsgs += tmpold;
2884                                 }
2885                         }
2886                 }
2887                 return 0;
2888         }
2889
2890         ast_copy_string(tmp, mailbox, sizeof(tmp));
2891         
2892         if ((context = strchr(tmp, '@')))
2893                 *context++ = '\0';
2894         else
2895                 context = "default";
2896
2897         if (newmsgs)
2898                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2899         if (oldmsgs)
2900                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2901
2902         return 0;
2903 }
2904
2905 #endif
2906
2907 static void run_externnotify(char *context, char *extension)
2908 {
2909         char arguments[255];
2910         char ext_context[256] = "";
2911         int newvoicemails = 0, oldvoicemails = 0;
2912         struct ast_smdi_mwi_message *mwi_msg;
2913
2914         if (!ast_strlen_zero(context))
2915                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2916         else
2917                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2918
2919         if (smdi_iface) {
2920                 if (ast_app_has_voicemail(ext_context, NULL)) 
2921                         ast_smdi_mwi_set(smdi_iface, extension);
2922                 else
2923                         ast_smdi_mwi_unset(smdi_iface, extension);
2924
2925                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2926                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2927                         if (!strncmp(mwi_msg->cause, "INV", 3))
2928                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2929                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2930                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2931                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2932                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2933                 } else {
2934                         if (option_debug)
2935                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2936                 }
2937         }
2938
2939         if (!ast_strlen_zero(externnotify)) {
2940                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2941                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2942                 } else {
2943                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2944                         if (option_debug)
2945                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2946                         ast_safe_system(arguments);
2947                 }
2948         }
2949 }
2950
2951 struct leave_vm_options {
2952         unsigned int flags;
2953         signed char record_gain;
2954 };
2955
2956 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2957 {
2958 #ifdef IMAP_STORAGE
2959         int newmsgs, oldmsgs;
2960         struct vm_state *vms = NULL;
2961 #endif
2962         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2963         char callerid[256];
2964         FILE *txt;
2965         char date[256];
2966         int txtdes;
2967         int res = 0;
2968         int msgnum;
2969         int duration = 0;
2970         int ausemacro = 0;
2971         int ousemacro = 0;
2972         int ouseexten = 0;
2973         int rtmsgid = 0;
2974         char tmpid[16];
2975         char tmpdur[16];
2976         char priority[16];
2977         char origtime[16];
2978         char dir[PATH_MAX], tmpdir[PATH_MAX];
2979         char dest[PATH_MAX];
2980         char fn[PATH_MAX];
2981         char prefile[PATH_MAX] = "";
2982         char tempfile[PATH_MAX] = "";
2983         char ext_context[256] = "";
2984         char fmt[80];
2985         char *context;
2986         char ecodes[16] = "#";
2987         char tmp[1024] = "", *tmpptr;
2988         struct ast_vm_user *vmu;
2989         struct ast_vm_user svm;
2990         const char *category = NULL;
2991
2992         ast_copy_string(tmp, ext, sizeof(tmp));
2993         ext = tmp;
2994         if ((context = strchr(tmp, '@'))) {
2995                 *context++ = '\0';
2996                 tmpptr = strchr(context, '&');
2997         } else {
2998                 tmpptr = strchr(ext, '&');
2999         }
3000
3001         if (tmpptr)
3002                 *tmpptr++ = '\0';
3003
3004         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3005
3006         if(option_debug > 2)
3007                 ast_log(LOG_DEBUG, "Before find_user\n");
3008         if (!(vmu = find_user(&svm, context, ext))) {
3009                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3010                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
3011                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3012                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3013                 return res;
3014         }
3015         /* Setup pre-file if appropriate */
3016         if (strcmp(vmu->context, "default"))
3017                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3018         else
3019                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
3020         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3021                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3022                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3023         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3024                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3025                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3026         }
3027         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3028         if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3029                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3030                 return -1;
3031         }
3032         RETRIEVE(tempfile, -1);
3033         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3034                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3035         DISPOSE(tempfile, -1);
3036         /* It's easier just to try to make it than to check for its existence */
3037         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3038         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
3039
3040         /* Check current or macro-calling context for special extensions */
3041         if (ast_test_flag(vmu, VM_OPERATOR)) {
3042                 if (!ast_strlen_zero(vmu->exit)) {
3043                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3044                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3045                                 ouseexten = 1;
3046                         }
3047                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3048                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3049                         ouseexten = 1;
3050                 }
3051                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3052                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3053                 ousemacro = 1;
3054                 }
3055         }
3056
3057         if (!ast_strlen_zero(vmu->exit)) {
3058                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3059                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3060         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3061                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3062         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3063                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3064                 ausemacro = 1;
3065         }
3066
3067         /* Play the beginning intro if desired */
3068         if (!ast_strlen_zero(prefile)) {
3069                 RETRIEVE(prefile, -1);
3070                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3071                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3072                                 res = ast_waitstream(chan, ecodes);
3073                 } else {
3074                         if (option_debug)
3075                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
3076                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3077                 }
3078                 DISPOSE(prefile, -1);
3079                 if (res < 0) {
3080                         if (option_debug)
3081                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
3082                         free_user(vmu);
3083                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3084                         return -1;
3085                 }
3086         }
3087         if (res == '#') {
3088                 /* On a '#' we skip the instructions */
3089                 ast_set_flag(options, OPT_SILENT);
3090                 res = 0;
3091         }
3092         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3093                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3094                 if (res == '#') {
3095                         ast_set_flag(options, OPT_SILENT);
3096                         res = 0;
3097                 }
3098         }
3099         if (res > 0)
3100                 ast_stopstream(chan);
3101         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3102          other than the operator -- an automated attendant or mailbox login for example */
3103         if (res == '*') {
3104                 chan->exten[0] = 'a';
3105                 chan->exten[1] = '\0';
3106                 if (!ast_strlen_zero(vmu->exit)) {
3107                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3108                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3109                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3110                 }
3111                 chan->priority = 0;
3112                 free_user(vmu);
3113                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3114                 return 0;
3115         }
3116
3117         /* Check for a '0' here */
3118         if (res == '0') {
3119         transfer:
3120                 if (ouseexten || ousemacro) {
3121                         chan->exten[0] = 'o';
3122                         chan->exten[1] = '\0';
3123                         if (!ast_strlen_zero(vmu->exit)) {
3124                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3125                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3126                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3127                         }
3128                         ast_play_and_wait(chan, "transfer");
3129                         chan->priority = 0;
3130                         free_user(vmu);
3131                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3132                 }
3133                 return 0;
3134         }
3135         if (res < 0) {
3136                 free_user(vmu);
3137                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3138                 return -1;
3139         }
3140         /* The meat of recording the message...  All the announcements and beeps have been played*/
3141         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3142         if (!ast_strlen_zero(fmt)) {
3143                 msgnum = 0;
3144
3145 #ifdef IMAP_STORAGE
3146                 /* Is ext a mailbox? */
3147                 /* must open stream for this user to get info! */
3148                 vms = get_vm_state_by_mailbox(ext,0);
3149                 if (vms) {
3150                         if(option_debug > 2)
3151                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3152                         newmsgs = vms->newmessages++;
3153                         oldmsgs = vms->oldmessages;
3154                 } else {
3155                         res = inboxcount(ext, &newmsgs, &oldmsgs);
3156                         if(res < 0) {
3157                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3158                                 return -1;
3159                         }
3160                         vms = get_vm_state_by_mailbox(ext,0);
3161                 }
3162                 /* here is a big difference! We add one to it later */
3163                 msgnum = newmsgs + oldmsgs;
3164                 if(option_debug > 2)
3165                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3166                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3167                 /* set variable for compatibility */
3168                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3169
3170                 /* Check if mailbox is full */
3171                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3172                         if (option_debug)
3173                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3174                         ast_play_and_wait(chan, "vm-mailboxfull");
3175                         return -1;
3176                 }
3177                 /* here is a big difference! We add one to it later */
3178                 msgnum = newmsgs + oldmsgs;
3179                 if(option_debug > 2)
3180                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3181
3182 #else
3183                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3184                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3185                         if (!res)
3186                                 res = ast_waitstream(chan, "");
3187                         ast_log(LOG_WARNING, "No more messages possible\n");
3188                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3189                         goto leave_vm_out;
3190                 }
3191
3192 #endif
3193                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3194                 txtdes = mkstemp(tmptxtfile);
3195                 if (txtdes < 0) {
3196                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3197                         if (!res)
3198                                 res = ast_waitstream(chan, "");
3199                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3200                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3201                         goto leave_vm_out;
3202                 }
3203
3204                 /* Now play the beep once we have the message number for our next message. */
3205                 if (res >= 0) {
3206                         /* Unless we're *really* silent, try to send the beep */
3207                         res = ast_stream_and_wait(chan, "beep", "");
3208                 }
3209                                 
3210                 /* Store information in real-time storage */
3211                 if (ast_check_realtime("voicemail_data")) {
3212                         snprintf(priority, sizeof(priority), "%d", chan->priority);
3213                         snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3214                         get_date(date, sizeof(date));
3215                         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);
3216                 }
3217
3218                 /* Store information */
3219                 txt = fdopen(txtdes, "w+");
3220                 if (txt) {
3221                         get_date(date, sizeof(date));
3222                         fprintf(txt, 
3223                                 ";\n"
3224                                 "; Message Information file\n"
3225                                 ";\n"
3226                                 "[message]\n"
3227                                 "origmailbox=%s\n"
3228                                 "context=%s\n"
3229                                 "macrocontext=%s\n"
3230                                 "exten=%s\n"
3231                                 "priority=%d\n"
3232                                 "callerchan=%s\n"
3233                                 "callerid=%s\n"
3234                                 "origdate=%s\n"
3235                                 "origtime=%ld\n"
3236                                 "category=%s\n",
3237                                 ext,
3238                                 chan->context,
3239                                 chan->macrocontext, 
3240                                 chan->exten,
3241                                 chan->priority,
3242                                 chan->name,
3243                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3244                                 date, (long)time(NULL),
3245                                 category ? category : ""); 
3246                 } else
3247                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3248 #ifdef IMAP_STORAGE
3249                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3250 #else
3251                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3252 #endif
3253
3254                 if (txt) {
3255                         if (duration < vmminsecs) {
3256                                 if (option_verbose > 2) 
3257                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3258                                 ast_filedelete(tmptxtfile, NULL);
3259                                 unlink(tmptxtfile);
3260                                 if (ast_check_realtime("voicemail_data")) {
3261                                         snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3262                                         ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3263                                 }
3264                         } else {
3265                                 fprintf(txt, "duration=%d\n", duration);
3266                                 fclose(txt);
3267                                 if (vm_lock_path(dir)) {
3268                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3269                                         /* Delete files */
3270                                         ast_filedelete(tmptxtfile, NULL);
3271                                         unlink(tmptxtfile);
3272                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3273                                         if (option_debug) 
3274                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3275                                         unlink(tmptxtfile);
3276                                         ast_unlock_path(dir);
3277                                         if (ast_check_realtime("voicemail_data")) {
3278                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3279                                                 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3280                                         }
3281                                 } else {
3282                                         msgnum = last_message_index(vmu, dir) + 1;
3283                                         make_file(fn, sizeof(fn), dir, msgnum);
3284
3285                                         /* assign a variable with the name of the voicemail file */ 
3286 #ifndef IMAP_STORAGE
3287                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3288 #else
3289                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3290 #endif
3291
3292                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3293                                         ast_filerename(tmptxtfile, fn, NULL);
3294                                         rename(tmptxtfile, txtfile);
3295
3296                                         ast_unlock_path(dir);
3297                                         if (ast_check_realtime("voicemail_data")) {
3298                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3299                                                 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3300                                                 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3301                                         }
3302 #ifndef IMAP_STORAGE
3303                                         /* Are there to be more recipients of this message? */
3304                                         while (tmpptr) {
3305                                                 struct ast_vm_user recipu, *recip;
3306                                                 char *exten, *context;
3307                                         
3308                                                 exten = strsep(&tmpptr, "&");
3309                                                 context = strchr(exten, '@');
3310                                                 if (context) {
3311                                                         *context = '\0';
3312                                                         context++;
3313                                                 }
3314                                                 if ((recip = find_user(&recipu, context, exten))) {
3315                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3316                                                         free_user(recip);
3317                                                 }
3318                                         }
3319 #endif
3320                                         if (ast_fileexists(fn, NULL, NULL)) {
3321                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3322                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3323                                                 DISPOSE(dir, msgnum);
3324                                         }
3325                                 }
3326                         }
3327                 }
3328                 if (res == '0') {
3329                         goto transfer;
3330                 } else if (res > 0)
3331                         res = 0;
3332
3333                 if (duration < vmminsecs)
3334                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3335                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3336                 else
3337                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3338         } else
3339                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3340 leave_vm_out:
3341         free_user(vmu);
3342         
3343         return res;
3344 }
3345
3346 #ifndef IMAP_STORAGE
3347 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3348 {
3349         /* we know max messages, so stop process when number is hit */
3350
3351         int x,dest;
3352         char sfn[PATH_MAX];
3353         char dfn[PATH_MAX];
3354
3355         if (vm_lock_path(dir))
3356                 return ERROR_LOCK_PATH;
3357
3358         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3359                 make_file(sfn, sizeof(sfn), dir, x);
3360                 if (EXISTS(dir, x, sfn, NULL)) {
3361                         
3362                         if (x != dest) {
3363                                 make_file(dfn, sizeof(dfn), dir, dest);
3364                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3365                         }
3366                         
3367                         dest++;
3368                 }
3369         }
3370         ast_unlock_path(dir);
3371
3372         return 0;
3373 }
3374 #endif
3375
3376 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3377 {
3378         int d;
3379         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3380         return d;
3381 }
3382
3383 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3384 {
3385 #ifdef IMAP_STORAGE
3386         /* we must use mbox(x) folder names, and copy the message there */
3387         /* simple. huh? */
3388         char dbox[256];
3389         long res;
3390         char sequence[10];
3391
3392         /* if save to Old folder, just leave in INBOX */
3393         if (box == 1) return 10;
3394         /* get the real IMAP message number for this message */
3395         sprintf(sequence,"%ld",vms->msgArray[msg]);
3396         imap_mailbox_name(dbox, vms, box, 1);
3397         if (option_debug > 2)
3398                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3399         res = mail_copy(vms->mailstream, sequence, dbox);
3400         if (res == 1) return 0;
3401         return 1;
3402 #else
3403         char *dir = vms->curdir;
3404         char *username = vms->username;
3405         char *context = vmu->context;
3406         char sfn[PATH_MAX];
3407         char dfn[PATH_MAX];
3408         char ddir[PATH_MAX];
3409         const char *dbox = mbox(box);
3410         int x;
3411         make_file(sfn, sizeof(sfn), dir, msg);
3412         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3413
3414         if (vm_lock_path(ddir))
3415                 return ERROR_LOCK_PATH;
3416
3417         x = last_message_index(vmu, ddir) + 1;
3418         make_file(dfn, sizeof(dfn), ddir, x);
3419
3420         if (x >= vmu->maxmsg) {
3421                 ast_unlock_path(ddir);
3422                 return -1;
3423         }
3424         if (strcmp(sfn, dfn)) {
3425                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3426         }
3427         ast_unlock_path(ddir);
3428 #endif
3429         return 0;
3430 }
3431
3432 static int adsi_logo(unsigned char *buf)
3433 {
3434         int bytes = 0;
3435         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3436         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-20