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