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