Merged revisions 68211 via svnmerge from
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref Unixodbc - http://www.unixodbc.org
26  * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
28  * 
29  * \par See also
30  * \arg \ref Config_vm
31  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32  * \ingroup applications
33  * \note This module requires res_adsi to load. This needs to be optional
34  * during compilation.
35  *
36  *
37  *
38  * \note  This file is now almost impossible to work with, due to all #ifdefs.
39  *        Feels like the database code before realtime. Someone - please come up
40  *        with a plan to clean this up.
41  */
42
43 /*** MODULEINFO
44         <depend>res_smdi</depend>
45  ***/
46
47 /*** MAKEOPTS
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
49         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50                 <depend>unixodbc</depend>
51                 <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                                 ast_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                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2119                 if (tmpfd > -1)
2120                         close(tmpfd);
2121                 unlink(newtmp);
2122         }
2123 #undef ENDL
2124 }
2125
2126 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)
2127 {
2128         FILE *p=NULL;
2129         char tmp[80] = "/tmp/astmail-XXXXXX";
2130         char tmp2[256];
2131
2132         if (vmu && ast_strlen_zero(vmu->email)) {
2133                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2134                 return(0);
2135         }
2136         if (!strcmp(format, "wav49"))
2137                 format = "WAV";
2138         if (option_debug > 2)
2139                 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));
2140         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2141            command hangs */
2142         if ((p = vm_mkftemp(tmp)) == NULL) {
2143                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2144                 return -1;
2145         } else {
2146                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2147                 fclose(p);
2148                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2149                 ast_safe_system(tmp2);
2150                 if (option_debug)
2151                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2152         }
2153         return 0;
2154 }
2155
2156 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)
2157 {
2158         char date[256];
2159         char host[MAXHOSTNAMELEN] = "";
2160         char who[256];
2161         char dur[PATH_MAX];
2162         char tmp[80] = "/tmp/astmail-XXXXXX";
2163         char tmp2[PATH_MAX];
2164         struct tm tm;
2165         FILE *p;
2166
2167         if ((p = vm_mkftemp(tmp)) == NULL) {
2168                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2169                 return -1;
2170         }
2171         gethostname(host, sizeof(host)-1);
2172         if (strchr(srcemail, '@'))
2173                 ast_copy_string(who, srcemail, sizeof(who));
2174         else 
2175                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2176         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2177         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2178         fprintf(p, "Date: %s\n", date);
2179
2180         if (*pagerfromstring) {
2181                 struct ast_channel *ast;
2182                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2183                         char *passdata;
2184                         int vmlen = strlen(fromstring)*3 + 200;
2185                         if ((passdata = alloca(vmlen))) {
2186                                 memset(passdata, 0, vmlen);
2187                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2188                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2189                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2190                         } else 
2191                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2192                         ast_channel_free(ast);
2193                 } else 
2194                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2195         } else
2196                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2197         fprintf(p, "To: %s\n", pager);
2198         if (pagersubject) {
2199                 struct ast_channel *ast;
2200                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2201                         char *passdata;
2202                         int vmlen = strlen(pagersubject) * 3 + 200;
2203                         if ((passdata = alloca(vmlen))) {
2204                                 memset(passdata, 0, vmlen);
2205                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2206                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2207                                 fprintf(p, "Subject: %s\n\n", passdata);
2208                         } else
2209                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2210                         ast_channel_free(ast);
2211                 } else
2212                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2213         } else
2214                 fprintf(p, "Subject: New VM\n\n");
2215
2216         strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2217         if (pagerbody) {
2218                 struct ast_channel *ast;
2219                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2220                         char *passdata;
2221                         int vmlen = strlen(pagerbody) * 3 + 200;
2222                         passdata = alloca(vmlen);
2223                         memset(passdata, 0, vmlen);
2224                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2225                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2226                         fprintf(p, "%s\n", passdata);
2227                         ast_channel_free(ast);
2228                 } else
2229                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2230         } else {
2231                 fprintf(p, "New %s long msg in box %s\n"
2232                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2233         }
2234         fclose(p);
2235         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2236         ast_safe_system(tmp2);
2237         if (option_debug)
2238                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2239         return 0;
2240 }
2241
2242 static int get_date(char *s, int len)
2243 {
2244         struct tm tm;
2245         time_t t;
2246         t = time(0);
2247         localtime_r(&t,&tm);
2248         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2249 }
2250
2251 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2252 {
2253         int res;
2254         char fn[PATH_MAX];
2255         char dest[PATH_MAX];
2256
2257         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2258
2259         if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2260                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2261                 return -1;
2262         }
2263
2264         RETRIEVE(fn, -1);
2265         if (ast_fileexists(fn, NULL, NULL) > 0) {
2266                 res = ast_stream_and_wait(chan, fn, ecodes);
2267                 if (res) {
2268                         DISPOSE(fn, -1);
2269                         return res;
2270                 }
2271         } else {
2272                 /* Dispose just in case */
2273                 DISPOSE(fn, -1);
2274                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2275                 if (res)
2276                         return res;
2277                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2278                 if (res)
2279                         return res;
2280         }
2281         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2282         return res;
2283 }
2284
2285 static void free_user(struct ast_vm_user *vmu)
2286 {
2287         if (!ast_test_flag(vmu, VM_ALLOCED))
2288                 return;
2289
2290         ast_free(vmu);
2291 }
2292
2293 static void free_zone(struct vm_zone *z)
2294 {
2295         ast_free(z);
2296 }
2297
2298 static const char *mbox(int id)
2299 {
2300         static const char *msgs[] = {
2301                 "INBOX",
2302                 "Old",
2303                 "Work",
2304                 "Family",
2305                 "Friends",
2306                 "Cust1",
2307                 "Cust2",
2308                 "Cust3",
2309                 "Cust4",
2310                 "Cust5",
2311         };
2312         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2313 }
2314
2315 #ifdef ODBC_STORAGE
2316 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2317 {
2318         int x = -1;
2319         int res;
2320         SQLHSTMT stmt;
2321         char sql[PATH_MAX];
2322         char rowdata[20];
2323         char tmp[PATH_MAX] = "";
2324         struct odbc_obj *obj;
2325         char *context;
2326
2327         if (newmsgs)
2328                 *newmsgs = 0;
2329         if (oldmsgs)
2330                 *oldmsgs = 0;
2331
2332         /* If no mailbox, return immediately */
2333         if (ast_strlen_zero(mailbox))
2334                 return 0;
2335
2336         ast_copy_string(tmp, mailbox, sizeof(tmp));
2337         
2338         context = strchr(tmp, '@');
2339         if (context) {
2340                 *context = '\0';
2341                 context++;
2342         } else
2343                 context = "default";
2344         
2345         obj = ast_odbc_request_obj(odbc_database, 0);
2346         if (obj) {
2347                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2348                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2349                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2350                         ast_odbc_release_obj(obj);
2351                         goto yuck;
2352                 }
2353                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2354                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2355                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2356                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2357                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2358                         ast_odbc_release_obj(obj);
2359                         goto yuck;
2360                 }
2361                 res = ast_odbc_smart_execute(obj, stmt);
2362                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2363                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2364                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2365                         ast_odbc_release_obj(obj);
2366                         goto yuck;
2367                 }
2368                 res = SQLFetch(stmt);
2369                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2370                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2371                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2372                         ast_odbc_release_obj(obj);
2373                         goto yuck;
2374                 }
2375                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2376                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2377                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2378                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2379                         ast_odbc_release_obj(obj);
2380                         goto yuck;
2381                 }
2382                 *newmsgs = atoi(rowdata);
2383                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2384
2385                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2386                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2387                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2388                         ast_odbc_release_obj(obj);
2389                         goto yuck;
2390                 }
2391                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2392                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2393                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2394                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2395                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2396                         ast_odbc_release_obj(obj);
2397                         goto yuck;
2398                 }
2399                 res = ast_odbc_smart_execute(obj, stmt);
2400                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2401                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2402                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2403                         ast_odbc_release_obj(obj);
2404                         goto yuck;
2405                 }
2406                 res = SQLFetch(stmt);
2407                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2408                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2409                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2410                         ast_odbc_release_obj(obj);
2411                         goto yuck;
2412                 }
2413                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2414                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2415                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2416                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2417                         ast_odbc_release_obj(obj);
2418                         goto yuck;
2419                 }
2420                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2421                 ast_odbc_release_obj(obj);
2422                 *oldmsgs = atoi(rowdata);
2423                 x = 0;
2424         } else
2425                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2426                 
2427 yuck:   
2428         return x;
2429 }
2430
2431 static int messagecount(const char *context, const char *mailbox, const char *folder)
2432 {
2433         struct odbc_obj *obj = NULL;
2434         int nummsgs = 0;
2435         int res;
2436         SQLHSTMT stmt = NULL;
2437         char sql[PATH_MAX];
2438         char rowdata[20];
2439         if (!folder)
2440                 folder = "INBOX";
2441         /* If no mailbox, return immediately */
2442         if (ast_strlen_zero(mailbox))
2443                 return 0;
2444
2445         obj = ast_odbc_request_obj(odbc_database, 0);
2446         if (obj) {
2447                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2448                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2449                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2450                         goto yuck;
2451                 }
2452                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2453                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2454                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2455                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2456                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2457                         goto yuck;
2458                 }
2459                 res = ast_odbc_smart_execute(obj, stmt);
2460                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2461                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2462                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2463                         goto yuck;
2464                 }
2465                 res = SQLFetch(stmt);
2466                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2467                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2468                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2469                         goto yuck;
2470                 }
2471                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2472                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2473                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2474                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2475                         goto yuck;
2476                 }
2477                 nummsgs = atoi(rowdata);
2478                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2479         } else
2480                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2481
2482 yuck:
2483         if (obj)
2484                 ast_odbc_release_obj(obj);
2485         return nummsgs;
2486 }
2487
2488 static int has_voicemail(const char *mailbox, const char *folder)
2489 {
2490         char *context, tmp[256];
2491         ast_copy_string(tmp, mailbox, sizeof(tmp));
2492         if ((context = strchr(tmp, '@')))
2493                 *context++ = '\0';
2494         else
2495                 context = "default";
2496
2497         if (messagecount(context, tmp, folder))
2498                 return 1;
2499         else
2500                 return 0;
2501 }
2502
2503 #elif defined(IMAP_STORAGE)
2504
2505 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
2506 {
2507         char *myserveremail = serveremail;
2508         char fn[PATH_MAX];
2509         char mailbox[256];
2510         char *stringp;
2511         FILE *p=NULL;
2512         char tmp[80] = "/tmp/astmail-XXXXXX";
2513         long len;
2514         void *buf;
2515         STRING str;
2516         
2517         /* Attach only the first format */
2518         fmt = ast_strdupa(fmt);
2519         stringp = fmt;
2520         strsep(&stringp, "|");
2521
2522         if (!ast_strlen_zero(vmu->serveremail))
2523                 myserveremail = vmu->serveremail;
2524
2525         make_file(fn, sizeof(fn), dir, msgnum);
2526
2527         if (ast_strlen_zero(vmu->email))
2528                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2529
2530         if (!strcmp(fmt, "wav49"))
2531                 fmt = "WAV";
2532         if(option_debug > 2)
2533                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2534
2535         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2536            command hangs */
2537         if (!(p = vm_mkftemp(tmp))) {
2538                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2539                 return -1;
2540         }
2541         
2542         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);
2543         /* read mail file to memory */          
2544         len = ftell(p);
2545         rewind(p);
2546         if (!(buf = ast_malloc(len+1))) {
2547                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2548                 return -1;
2549         }
2550         fread(buf, len, 1, p);
2551         ((char *)buf)[len] = '\0';
2552         INIT(&str, mail_string, buf, len);
2553         init_mailstream(vms, 0);
2554         imap_mailbox_name(mailbox, vms, 0, 1);
2555         if(!mail_append(vms->mailstream, mailbox, &str))
2556                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2557         fclose(p);
2558         unlink(tmp);
2559         ast_free(buf);
2560         if(option_debug > 2)
2561                 ast_log(LOG_DEBUG, "%s stored\n", fn);
2562         return 0;
2563
2564 }
2565
2566 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2567 {
2568         SEARCHPGM *pgm;
2569         SEARCHHEADER *hdr;
2570  
2571         struct ast_vm_user *vmu;
2572         struct vm_state *vms_p;
2573         char tmp[256] = "";
2574         char *mb, *cur;
2575         char *mailboxnc; 
2576         char *context;
2577         int ret = 0;
2578
2579         if (newmsgs)
2580                 *newmsgs = 0;
2581
2582         if (oldmsgs)
2583                 *oldmsgs = 0;
2584  
2585         if(option_debug > 2)
2586                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2587
2588         /* If no mailbox, return immediately */
2589         if (ast_strlen_zero(mailbox))
2590                 return 0;
2591
2592         if (strchr(mailbox, ',')) {
2593                 int tmpnew, tmpold;
2594                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2595                 mb = tmp;
2596                 ret = 0;
2597                 while((cur = strsep(&mb, ", "))) {
2598                         if (!ast_strlen_zero(cur)) {
2599                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2600                                         return -1;
2601                                 else {
2602                                         if (newmsgs)
2603                                                 *newmsgs += tmpnew; 
2604                                         if (oldmsgs)
2605                                                 *oldmsgs += tmpold;
2606                                 }
2607                         }
2608                 }
2609                 return 0;
2610         }
2611
2612         ast_copy_string(tmp, mailbox, sizeof(tmp));
2613
2614         if ((context = strchr(tmp, '@'))) {
2615                 *context = '\0';
2616                 mailboxnc = tmp;
2617                 context++;
2618         } else {
2619                 context = "default";
2620                 mailboxnc = (char *)mailbox;
2621         }
2622  
2623         /* We have to get the user before we can open the stream! */
2624         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2625         if (!(vmu = find_user(NULL, context, mailboxnc))) {
2626                 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2627                 return -1;
2628         }
2629         
2630         /* No IMAP account available */
2631         if (vmu->imapuser[0] == '\0') {
2632                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2633                 free_user(vmu);
2634                 return -1;
2635         }
2636  
2637         /* check if someone is accessing this box right now... */
2638         if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2639                 if(option_debug > 2)
2640                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2641                 *newmsgs = vms_p->newmessages;
2642                 *oldmsgs = vms_p->oldmessages;
2643                 free_user(vmu);
2644                 return 0;
2645         }
2646  
2647         /* add one if not there... */
2648         if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2649                 if(option_debug > 2)
2650                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2651                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2652                         free_user(vmu);
2653                         return -1;
2654                 }
2655                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2656                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2657                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2658                 if(option_debug > 2)
2659                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2660                 vms_p->updated = 1;
2661                 /* set mailbox to INBOX! */
2662                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2663                 init_vm_state(vms_p);
2664                 vmstate_insert(vms_p);
2665         }
2666
2667         /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2668         ret = init_mailstream(vms_p, 0);
2669         if (!vms_p->mailstream) {
2670                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2671                 free_user(vmu);
2672                 return -1;
2673         }
2674
2675         if (!ret && vms_p->updated == 1) {
2676                 if (newmsgs) {
2677                         pgm = mail_newsearchpgm();
2678                         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2679                         pgm->header = hdr;
2680                         pgm->unseen = 1;
2681                         pgm->seen = 0;
2682                         pgm->undeleted = 1;
2683                         pgm->deleted = 0;
2684                         vms_p->vmArrayIndex = 0;
2685                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2686                         *newmsgs = vms_p->vmArrayIndex;
2687                         vms_p->newmessages = vms_p->vmArrayIndex;
2688                         mail_free_searchpgm(&pgm);
2689                 }
2690                 if (oldmsgs) {
2691                         pgm = mail_newsearchpgm ();
2692                         hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2693                         pgm->header = hdr;
2694                         pgm->unseen = 0;
2695                         pgm->seen = 1;
2696                         pgm->undeleted = 1;
2697                         pgm->deleted = 0;
2698                         vms_p->vmArrayIndex = 0;
2699                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2700                         *oldmsgs = vms_p->vmArrayIndex;
2701                         vms_p->oldmessages = vms_p->vmArrayIndex;
2702                         mail_free_searchpgm(&pgm);
2703                 }
2704         }
2705
2706         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2707                 vms_p->updated = 0;
2708         } else if (vms_p->updated > 1) {  /* decrement delay count */
2709                 vms_p->updated--;
2710         } else {  /* no changes, so don't search */
2711                 mail_ping(vms_p->mailstream);
2712                 /* Keep the old data */
2713                 *newmsgs = vms_p->newmessages;
2714                 *oldmsgs = vms_p->oldmessages;
2715         }
2716
2717         free_user(vmu);
2718         return 0;
2719  }
2720
2721 static int has_voicemail(const char *mailbox, const char *folder)
2722 {
2723         int newmsgs, oldmsgs;
2724         
2725         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2726                 return folder? oldmsgs: newmsgs;
2727         else
2728                 return 0;
2729 }
2730
2731 static int messagecount(const char *context, const char *mailbox, const char *folder)
2732 {
2733         int newmsgs, oldmsgs;
2734         char tmp[256] = "";
2735         
2736         if (ast_strlen_zero(mailbox))
2737                 return 0;
2738         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2739
2740         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2741                 return folder? oldmsgs: newmsgs;
2742         else
2743                 return 0;
2744 }
2745
2746 #endif
2747 #ifndef IMAP_STORAGE
2748 /* copy message only used by file storage */
2749 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)
2750 {
2751         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2752         const char *frombox = mbox(imbox);
2753         int recipmsgnum;
2754
2755         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2756
2757         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2758         
2759         if (!dir)
2760                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2761         else
2762                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2763
2764         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2765         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2766
2767         if (vm_lock_path(todir))
2768                 return ERROR_LOCK_PATH;
2769
2770         recipmsgnum = last_message_index(recip, todir) + 1;
2771         if (recipmsgnum < recip->maxmsg) {
2772                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2773                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2774         } else {
2775                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2776         }
2777         ast_unlock_path(todir);
2778         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2779         
2780         return 0;
2781 }
2782 #endif
2783 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2784 static int messagecount(const char *context, const char *mailbox, const char *folder)
2785 {
2786         return __has_voicemail(context, mailbox, folder, 0);
2787 }
2788
2789
2790 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2791 {
2792         DIR *dir;
2793         struct dirent *de;
2794         char fn[256];
2795         int ret = 0;
2796
2797         /* If no mailbox, return immediately */
2798         if (ast_strlen_zero(mailbox))
2799                 return 0;
2800
2801         if (ast_strlen_zero(folder))
2802                 folder = "INBOX";
2803         if (ast_strlen_zero(context))
2804                 context = "default";
2805
2806         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2807
2808         if (!(dir = opendir(fn)))
2809                 return 0;
2810
2811         while ((de = readdir(dir))) {
2812                 if (!strncasecmp(de->d_name, "msg", 3)) {
2813                         if (shortcircuit) {
2814                                 ret = 1;
2815                                 break;
2816                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2817                                 ret++;
2818                 }
2819         }
2820
2821         closedir(dir);
2822
2823         return ret;
2824 }
2825
2826
2827 static int has_voicemail(const char *mailbox, const char *folder)
2828 {
2829         char tmp[256], *tmp2 = tmp, *mbox, *context;
2830         ast_copy_string(tmp, mailbox, sizeof(tmp));
2831         while ((mbox = strsep(&tmp2, ","))) {
2832                 if ((context = strchr(mbox, '@')))
2833                         *context++ = '\0';
2834                 else
2835                         context = "default";
2836                 if (__has_voicemail(context, mbox, folder, 1))
2837                         return 1;
2838         }
2839         return 0;
2840 }
2841
2842
2843 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2844 {
2845         char tmp[256];
2846         char *context;
2847
2848         /* If no mailbox, return immediately */
2849         if (ast_strlen_zero(mailbox))
2850                 return 0;
2851
2852         if (newmsgs)
2853                 *newmsgs = 0;
2854         if (oldmsgs)
2855                 *oldmsgs = 0;
2856
2857         if (strchr(mailbox, ',')) {
2858                 int tmpnew, tmpold;
2859                 char *mb, *cur;
2860
2861                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2862                 mb = tmp;
2863                 while ((cur = strsep(&mb, ", "))) {
2864                         if (!ast_strlen_zero(cur)) {
2865                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2866                                         return -1;
2867                                 else {
2868                                         if (newmsgs)
2869                                                 *newmsgs += tmpnew; 
2870                                         if (oldmsgs)
2871                                                 *oldmsgs += tmpold;
2872                                 }
2873                         }
2874                 }
2875                 return 0;
2876         }
2877
2878         ast_copy_string(tmp, mailbox, sizeof(tmp));
2879         
2880         if ((context = strchr(tmp, '@')))
2881                 *context++ = '\0';
2882         else
2883                 context = "default";
2884
2885         if (newmsgs)
2886                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2887         if (oldmsgs)
2888                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2889
2890         return 0;
2891 }
2892
2893 #endif
2894
2895 static void run_externnotify(char *context, char *extension)
2896 {
2897         char arguments[255];
2898         char ext_context[256] = "";
2899         int newvoicemails = 0, oldvoicemails = 0;
2900         struct ast_smdi_mwi_message *mwi_msg;
2901
2902         if (!ast_strlen_zero(context))
2903                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2904         else
2905                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2906
2907         if (smdi_iface) {
2908                 if (ast_app_has_voicemail(ext_context, NULL)) 
2909                         ast_smdi_mwi_set(smdi_iface, extension);
2910                 else
2911                         ast_smdi_mwi_unset(smdi_iface, extension);
2912
2913                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2914                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2915                         if (!strncmp(mwi_msg->cause, "INV", 3))
2916                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2917                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2918                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2919                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2920                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2921                 } else {
2922                         if (option_debug)
2923                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2924                 }
2925         }
2926
2927         if (!ast_strlen_zero(externnotify)) {
2928                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2929                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2930                 } else {
2931                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2932                         if (option_debug)
2933                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2934                         ast_safe_system(arguments);
2935                 }
2936         }
2937 }
2938
2939 struct leave_vm_options {
2940         unsigned int flags;
2941         signed char record_gain;
2942 };
2943
2944 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2945 {
2946 #ifdef IMAP_STORAGE
2947         int newmsgs, oldmsgs;
2948         struct vm_state *vms = NULL;
2949 #endif
2950         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2951         char callerid[256];
2952         FILE *txt;
2953         char date[256];
2954         int txtdes;
2955         int res = 0;
2956         int msgnum;
2957         int duration = 0;
2958         int ausemacro = 0;
2959         int ousemacro = 0;
2960         int ouseexten = 0;
2961         int rtmsgid = 0;
2962         char tmpid[16];
2963         char tmpdur[16];
2964         char priority[16];
2965         char origtime[16];
2966         char dir[PATH_MAX], tmpdir[PATH_MAX];
2967         char dest[PATH_MAX];
2968         char fn[PATH_MAX];
2969         char prefile[PATH_MAX] = "";
2970         char tempfile[PATH_MAX] = "";
2971         char ext_context[256] = "";
2972         char fmt[80];
2973         char *context;
2974         char ecodes[16] = "#";
2975         char tmp[1024] = "", *tmpptr;
2976         struct ast_vm_user *vmu;
2977         struct ast_vm_user svm;
2978         const char *category = NULL;
2979
2980         ast_copy_string(tmp, ext, sizeof(tmp));
2981         ext = tmp;
2982         if ((context = strchr(tmp, '@'))) {
2983                 *context++ = '\0';
2984                 tmpptr = strchr(context, '&');
2985         } else {
2986                 tmpptr = strchr(ext, '&');
2987         }
2988
2989         if (tmpptr)
2990                 *tmpptr++ = '\0';
2991
2992         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2993
2994         if (option_debug > 2)
2995                 ast_log(LOG_DEBUG, "Before find_user\n");
2996         if (!(vmu = find_user(&svm, context, ext))) {
2997                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2998                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2999                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3000                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3001                 return res;
3002         }
3003         /* Setup pre-file if appropriate */
3004         if (strcmp(vmu->context, "default"))
3005                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3006         else
3007                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
3008         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3009                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3010                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3011         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3012                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3013                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3014         }
3015         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3016         if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3017                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3018                 return -1;
3019         }
3020         RETRIEVE(tempfile, -1);
3021         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3022                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3023         DISPOSE(tempfile, -1);
3024         /* It's easier just to try to make it than to check for its existence */
3025         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3026         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
3027
3028         /* Check current or macro-calling context for special extensions */
3029         if (ast_test_flag(vmu, VM_OPERATOR)) {
3030                 if (!ast_strlen_zero(vmu->exit)) {
3031                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3032                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3033                                 ouseexten = 1;
3034                         }
3035                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3036                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3037                         ouseexten = 1;
3038                 }
3039                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3040                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3041                 ousemacro = 1;
3042                 }
3043         }
3044
3045         if (!ast_strlen_zero(vmu->exit)) {
3046                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3047                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3048         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3049                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3050         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3051                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3052                 ausemacro = 1;
3053         }
3054
3055         /* Play the beginning intro if desired */
3056         if (!ast_strlen_zero(prefile)) {
3057                 RETRIEVE(prefile, -1);
3058                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3059                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3060                                 res = ast_waitstream(chan, ecodes);
3061                 } else {
3062                         if (option_debug)
3063                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
3064                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3065                 }
3066                 DISPOSE(prefile, -1);
3067                 if (res < 0) {
3068                         if (option_debug)
3069                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
3070                         free_user(vmu);
3071                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3072                         return -1;
3073                 }
3074         }
3075         if (res == '#') {
3076                 /* On a '#' we skip the instructions */
3077                 ast_set_flag(options, OPT_SILENT);
3078                 res = 0;
3079         }
3080         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3081                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3082                 if (res == '#') {
3083                         ast_set_flag(options, OPT_SILENT);
3084                         res = 0;
3085                 }
3086         }
3087         if (res > 0)
3088                 ast_stopstream(chan);
3089         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3090          other than the operator -- an automated attendant or mailbox login for example */
3091         if (res == '*') {
3092                 chan->exten[0] = 'a';
3093                 chan->exten[1] = '\0';
3094                 if (!ast_strlen_zero(vmu->exit)) {
3095                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3096                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3097                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3098                 }
3099                 chan->priority = 0;
3100                 free_user(vmu);
3101                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3102                 return 0;
3103         }
3104
3105         /* Check for a '0' here */
3106         if (res == '0') {
3107         transfer:
3108                 if (ouseexten || ousemacro) {
3109                         chan->exten[0] = 'o';
3110                         chan->exten[1] = '\0';
3111                         if (!ast_strlen_zero(vmu->exit)) {
3112                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3113                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3114                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3115                         }
3116                         ast_play_and_wait(chan, "transfer");
3117                         chan->priority = 0;
3118                         free_user(vmu);
3119                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3120                 }
3121                 return 0;
3122         }
3123         if (res < 0) {
3124                 free_user(vmu);
3125                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3126                 return -1;
3127         }
3128         /* The meat of recording the message...  All the announcements and beeps have been played*/
3129         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3130         if (!ast_strlen_zero(fmt)) {
3131                 msgnum = 0;
3132
3133 #ifdef IMAP_STORAGE
3134                 /* Is ext a mailbox? */
3135                 /* must open stream for this user to get info! */
3136                 vms = get_vm_state_by_mailbox(ext,0);
3137                 if (vms) {
3138                         if(option_debug > 2)
3139                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3140                         newmsgs = vms->newmessages++;
3141                         oldmsgs = vms->oldmessages;
3142                 } else {
3143                         res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3144                         if(res < 0) {
3145                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3146                                 return -1;
3147                         }
3148                         vms = get_vm_state_by_mailbox(ext,0);
3149                 }
3150                 /* here is a big difference! We add one to it later */
3151                 msgnum = newmsgs + oldmsgs;
3152                 if(option_debug > 2)
3153                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3154                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3155                 /* set variable for compatibility */
3156                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3157
3158                 /* Check if mailbox is full */
3159                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3160                         if (option_debug)
3161                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3162                         ast_play_and_wait(chan, "vm-mailboxfull");
3163                         return -1;
3164                 }
3165                 /* here is a big difference! We add one to it later */
3166                 msgnum = newmsgs + oldmsgs;
3167                 if(option_debug > 2)
3168                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3169
3170 #else
3171                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3172                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3173                         if (!res)
3174                                 res = ast_waitstream(chan, "");
3175                         ast_log(LOG_WARNING, "No more messages possible\n");
3176                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3177                         goto leave_vm_out;
3178                 }
3179
3180 #endif
3181                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3182                 txtdes = mkstemp(tmptxtfile);
3183                 if (txtdes < 0) {
3184                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3185                         if (!res)
3186                                 res = ast_waitstream(chan, "");
3187                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3188                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3189                         goto leave_vm_out;
3190                 }
3191
3192                 /* Now play the beep once we have the message number for our next message. */
3193                 if (res >= 0) {
3194                         /* Unless we're *really* silent, try to send the beep */
3195                         res = ast_stream_and_wait(chan, "beep", "");
3196                 }
3197                                 
3198                 /* Store information in real-time storage */
3199                 if (ast_check_realtime("voicemail_data")) {
3200                         snprintf(priority, sizeof(priority), "%d", chan->priority);
3201                         snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3202                         get_date(date, sizeof(date));
3203                         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);
3204                 }
3205
3206                 /* Store information */
3207                 txt = fdopen(txtdes, "w+");
3208                 if (txt) {
3209                         get_date(date, sizeof(date));
3210                         fprintf(txt, 
3211                                 ";\n"
3212                                 "; Message Information file\n"
3213                                 ";\n"
3214                                 "[message]\n"
3215                                 "origmailbox=%s\n"
3216                                 "context=%s\n"
3217                                 "macrocontext=%s\n"
3218                                 "exten=%s\n"
3219                                 "priority=%d\n"
3220                                 "callerchan=%s\n"
3221                                 "callerid=%s\n"
3222                                 "origdate=%s\n"
3223                                 "origtime=%ld\n"
3224                                 "category=%s\n",
3225                                 ext,
3226                                 chan->context,
3227                                 chan->macrocontext, 
3228                                 chan->exten,
3229                                 chan->priority,
3230                                 chan->name,
3231                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3232                                 date, (long)time(NULL),
3233                                 category ? category : ""); 
3234                 } else
3235                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3236 #ifdef IMAP_STORAGE
3237                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3238 #else
3239                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3240 #endif
3241
3242                 if (txt) {
3243                         if (duration < vmminsecs) {
3244                                 if (option_verbose > 2) 
3245                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3246                                 ast_filedelete(tmptxtfile, NULL);
3247                                 unlink(tmptxtfile);
3248                                 if (ast_check_realtime("voicemail_data")) {
3249                                         snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3250                                         ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3251                                 }
3252                         } else {
3253                                 fprintf(txt, "duration=%d\n", duration);
3254                                 fclose(txt);
3255                                 if (vm_lock_path(dir)) {
3256                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3257                                         /* Delete files */
3258                                         ast_filedelete(tmptxtfile, NULL);
3259                                         unlink(tmptxtfile);
3260                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3261                                         if (option_debug) 
3262                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3263                                         unlink(tmptxtfile);
3264                                         ast_unlock_path(dir);
3265                                         if (ast_check_realtime("voicemail_data")) {
3266                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3267                                                 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3268                                         }
3269                                 } else {
3270                                         msgnum = last_message_index(vmu, dir) + 1;
3271                                         make_file(fn, sizeof(fn), dir, msgnum);
3272
3273                                         /* assign a variable with the name of the voicemail file */ 
3274 #ifndef IMAP_STORAGE
3275                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3276 #else
3277                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3278 #endif
3279
3280                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3281                                         ast_filerename(tmptxtfile, fn, NULL);
3282                                         rename(tmptxtfile, txtfile);
3283
3284                                         ast_unlock_path(dir);
3285                                         if (ast_check_realtime("voicemail_data")) {
3286                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3287                                                 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3288                                                 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3289                                         }
3290 #ifndef IMAP_STORAGE
3291                                         /* Are there to be more recipients of this message? */
3292                                         while (tmpptr) {
3293                                                 struct ast_vm_user recipu, *recip;
3294                                                 char *exten, *context;
3295                                         
3296                                                 exten = strsep(&tmpptr, "&");
3297                                                 context = strchr(exten, '@');
3298                                                 if (context) {
3299                                                         *context = '\0';
3300                                                         context++;
3301                                                 }
3302                                                 if ((recip = find_user(&recipu, context, exten))) {
3303                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3304                                                         free_user(recip);
3305                                                 }
3306                                         }
3307 #endif
3308                                         if (ast_fileexists(fn, NULL, NULL)) {
3309                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3310                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3311                                                 DISPOSE(dir, msgnum);
3312                                         }
3313                                 }
3314                         }
3315                 }
3316                 if (res == '0') {
3317                         goto transfer;
3318                 } else if (res > 0)
3319                         res = 0;
3320
3321                 if (duration < vmminsecs)
3322                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3323                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3324                 else
3325                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3326         } else
3327                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3328 leave_vm_out:
3329         free_user(vmu);
3330         
3331         return res;
3332 }
3333
3334 #ifndef IMAP_STORAGE
3335 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3336 {
3337         /* we know max messages, so stop process when number is hit */
3338
3339         int x,dest;
3340         char sfn[PATH_MAX];
3341         char dfn[PATH_MAX];
3342
3343         if (vm_lock_path(dir))
3344                 return ERROR_LOCK_PATH;
3345
3346         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3347                 make_file(sfn, sizeof(sfn), dir, x);
3348                 if (EXISTS(dir, x, sfn, NULL)) {
3349                         
3350                         if (x != dest) {
3351                                 make_file(dfn, sizeof(dfn), dir, dest);
3352                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3353                         }
3354                         
3355                         dest++;
3356                 }
3357         }
3358         ast_unlock_path(dir);
3359
3360         return 0;
3361 }
3362 #endif
3363
3364 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3365 {
3366         int d;
3367         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3368         return d;
3369 }
3370
3371 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3372 {
3373 #ifdef IMAP_STORAGE
3374         /* we must use mbox(x) folder names, and copy the message there */
3375         /* simple. huh? */
3376         char dbox[256];
3377         long res;
3378         char sequence[10];
3379
3380         /* if save to Old folder, just leave in INBOX */
3381         if (box == 1) return 10;
3382         /* get the real IMAP message number for this message */
3383         sprintf(sequence,"%ld",vms->msgArray[msg]);
3384         imap_mailbox_name(dbox, vms, box, 1);
3385         if (option_debug > 2)
3386                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3387         res = mail_copy(vms->mailstream, sequence, dbox);
3388         if (res == 1) return 0;
3389         return 1;
3390 #else
3391         char *dir = vms->curdir;
3392         char *username = vms->username;
3393         char *context = vmu->context;
3394         char sfn[PATH_MAX];
3395         char dfn[PATH_MAX];
3396         char ddir[PATH_MAX];
3397         const char *dbox = mbox(box);
3398         int x;
3399         make_file(sfn, sizeof(sfn), dir, msg);
3400         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3401
3402         if (vm_lock_path(ddir))
3403                 return ERROR_LOCK_PATH;
3404
3405         x = last_message_index(vmu, ddir) + 1;
3406         make_file(dfn, sizeof(dfn), ddir, x);
3407
3408         if (x >= vmu->maxmsg) {
3409                 ast_unlock_path(ddir);
3410                 return -1;
3411         }
3412         if (strcmp(sfn, dfn)) {
3413                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3414         }
3415         ast_unlock_path(ddir);
3416 #endif
3417         return 0;
3418 }
3419
3420 static int adsi_logo(unsigned char *buf)
3421 {
3422         int bytes = 0;
3423         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3424         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3425         return bytes;
3426 }
3427
3428 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3429 {
3430         unsigned char buf[256];
3431         int bytes=0;
3432         int x;
3433         char num[5];
3434
3435         *useadsi = 0;
3436         bytes += ast_adsi_data_mode(buf + bytes);
3437         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3438
3439         bytes = 0;
3440         bytes += adsi_logo(buf);
3441         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3442 #ifdef DISPLAY
3443         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");