Merged revisions 58931 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);
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);
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=NULL;
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)) == (void *)-1) {
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         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1167         unlink(full_fn);
1168         return 0;
1169 }
1170
1171 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1172 {
1173         int x = 0;
1174         int res;
1175         SQLHSTMT stmt;
1176         char sql[PATH_MAX];
1177         char rowdata[20];
1178         
1179         struct odbc_obj *obj;
1180         obj = ast_odbc_request_obj(odbc_database, 0);
1181         if (obj) {
1182                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1183                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1184                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1185                         ast_odbc_release_obj(obj);
1186                         goto yuck;
1187                 }
1188                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1189                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1190                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1192                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193                         ast_odbc_release_obj(obj);
1194                         goto yuck;
1195                 }
1196                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1197                 res = ast_odbc_smart_execute(obj, stmt);
1198                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1199                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1200                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1201                         ast_odbc_release_obj(obj);
1202                         goto yuck;
1203                 }
1204                 res = SQLFetch(stmt);
1205                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1206                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1207                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1208                         ast_odbc_release_obj(obj);
1209                         goto yuck;
1210                 }
1211                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1212                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1213                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1214                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1215                         ast_odbc_release_obj(obj);
1216                         goto yuck;
1217                 }
1218                 if (sscanf(rowdata, "%d", &x) != 1)
1219                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1220                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1221                 ast_odbc_release_obj(obj);
1222         } else
1223                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1224 yuck:   
1225         return x - 1;
1226 }
1227
1228 static int message_exists(char *dir, int msgnum)
1229 {
1230         int x = 0;
1231         int res;
1232         SQLHSTMT stmt;
1233         char sql[PATH_MAX];
1234         char rowdata[20];
1235         char msgnums[20];
1236         
1237         struct odbc_obj *obj;
1238         obj = ast_odbc_request_obj(odbc_database, 0);
1239         if (obj) {
1240                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1241                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1242                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1243                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1244                         ast_odbc_release_obj(obj);
1245                         goto yuck;
1246                 }
1247                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1248                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1249                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1250                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1251                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1252                         ast_odbc_release_obj(obj);
1253                         goto yuck;
1254                 }
1255                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1256                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1257                 res = ast_odbc_smart_execute(obj, stmt);
1258                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1259                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1260                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1261                         ast_odbc_release_obj(obj);
1262                         goto yuck;
1263                 }
1264                 res = SQLFetch(stmt);
1265                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1266                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1267                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1268                         ast_odbc_release_obj(obj);
1269                         goto yuck;
1270                 }
1271                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1272                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1273                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1274                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1275                         ast_odbc_release_obj(obj);
1276                         goto yuck;
1277                 }
1278                 if (sscanf(rowdata, "%d", &x) != 1)
1279                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1280                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1281                 ast_odbc_release_obj(obj);
1282         } else
1283                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1284 yuck:   
1285         return x;
1286 }
1287
1288 static int count_messages(struct ast_vm_user *vmu, char *dir)
1289 {
1290         return last_message_index(vmu, dir) + 1;
1291 }
1292
1293 static void delete_file(char *sdir, int smsg)
1294 {
1295         int res;
1296         SQLHSTMT stmt;
1297         char sql[PATH_MAX];
1298         char msgnums[20];
1299         
1300         struct odbc_obj *obj;
1301         obj = ast_odbc_request_obj(odbc_database, 0);
1302         if (obj) {
1303                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1304                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1305                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1306                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1307                         ast_odbc_release_obj(obj);
1308                         goto yuck;
1309                 }
1310                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1311                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1312                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1313                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1314                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1315                         ast_odbc_release_obj(obj);
1316                         goto yuck;
1317                 }
1318                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1319                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1320                 res = ast_odbc_smart_execute(obj, stmt);
1321                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1322                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1323                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1324                         ast_odbc_release_obj(obj);
1325                         goto yuck;
1326                 }
1327                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1328                 ast_odbc_release_obj(obj);
1329         } else
1330                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1331 yuck:
1332         return; 
1333 }
1334
1335 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1336 {
1337         int res;
1338         SQLHSTMT stmt;
1339         char sql[512];
1340         char msgnums[20];
1341         char msgnumd[20];
1342         struct odbc_obj *obj;
1343
1344         delete_file(ddir, dmsg);
1345         obj = ast_odbc_request_obj(odbc_database, 0);
1346         if (obj) {
1347                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1348                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1349                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1350                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1351                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1352                         ast_odbc_release_obj(obj);
1353                         goto yuck;
1354                 }
1355                 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); 
1356                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1357                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1358                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1359                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1360                         ast_odbc_release_obj(obj);
1361                         goto yuck;
1362                 }
1363                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1364                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1365                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1366                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1367                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1368                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1369                 res = ast_odbc_smart_execute(obj, stmt);
1370                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1371                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1372                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1373                         ast_odbc_release_obj(obj);
1374                         goto yuck;
1375                 }
1376                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1377                 ast_odbc_release_obj(obj);
1378         } else
1379                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1380 yuck:
1381         return; 
1382 }
1383
1384 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1385 {
1386         int x = 0;
1387         int res;
1388         int fd = -1;
1389         void *fdm=NULL;
1390         size_t fdlen = -1;
1391         SQLHSTMT stmt;
1392         SQLINTEGER len;
1393         char sql[PATH_MAX];
1394         char msgnums[20];
1395         char fn[PATH_MAX];
1396         char full_fn[PATH_MAX];
1397         char fmt[80]="";
1398         char *c;
1399         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1400         const char *category = "";
1401         struct ast_config *cfg=NULL;
1402         struct odbc_obj *obj;
1403
1404         delete_file(dir, msgnum);
1405         obj = ast_odbc_request_obj(odbc_database, 0);
1406         if (obj) {
1407                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1408                 c = strchr(fmt, '|');
1409                 if (c)
1410                         *c = '\0';
1411                 if (!strcasecmp(fmt, "wav49"))
1412                         strcpy(fmt, "WAV");
1413                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1414                 if (msgnum > -1)
1415                         make_file(fn, sizeof(fn), dir, msgnum);
1416                 else
1417                         ast_copy_string(fn, dir, sizeof(fn));
1418                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1419                 cfg = ast_config_load(full_fn);
1420                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1421                 fd = open(full_fn, O_RDWR);
1422                 if (fd < 0) {
1423                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1424                         ast_odbc_release_obj(obj);
1425                         goto yuck;
1426                 }
1427                 if (cfg) {
1428                         context = ast_variable_retrieve(cfg, "message", "context");
1429                         if (!context) context = "";
1430                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1431                         if (!macrocontext) macrocontext = "";
1432                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1433                         if (!callerid) callerid = "";
1434                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1435                         if (!origtime) origtime = "";
1436                         duration = ast_variable_retrieve(cfg, "message", "duration");
1437                         if (!duration) duration = "";
1438                         category = ast_variable_retrieve(cfg, "message", "category");
1439                         if (!category) category = "";
1440                 }
1441                 fdlen = lseek(fd, 0, SEEK_END);
1442                 lseek(fd, 0, SEEK_SET);
1443                 printf("Length is %zd\n", fdlen);
1444                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1445                 if (!fdm) {
1446                         ast_log(LOG_WARNING, "Memory map failed!\n");
1447                         ast_odbc_release_obj(obj);
1448                         goto yuck;
1449                 } 
1450                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1451                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1452                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1453                         ast_odbc_release_obj(obj);
1454                         goto yuck;
1455                 }
1456                 if (!ast_strlen_zero(category)) 
1457                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1458                 else
1459                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1460                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1461                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1462                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1463                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1464                         ast_odbc_release_obj(obj);
1465                         goto yuck;
1466                 }
1467                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1468                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1469                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1470                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1471                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1472                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1473                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1474                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1475                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1476                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1477                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1478                 if (!ast_strlen_zero(category))
1479                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1480                 res = ast_odbc_smart_execute(obj, stmt);
1481                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1482                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1483                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1484                         ast_odbc_release_obj(obj);
1485                         goto yuck;
1486                 }
1487                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1488                 ast_odbc_release_obj(obj);
1489         } else
1490                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1491 yuck:   
1492         if (cfg)
1493                 ast_config_destroy(cfg);
1494         if (fdm)
1495                 munmap(fdm, fdlen);
1496         if (fd > -1)
1497                 close(fd);
1498         return x;
1499 }
1500
1501 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1502 {
1503         int res;
1504         SQLHSTMT stmt;
1505         char sql[PATH_MAX];
1506         char msgnums[20];
1507         char msgnumd[20];
1508         struct odbc_obj *obj;
1509
1510         delete_file(ddir, dmsg);
1511         obj = ast_odbc_request_obj(odbc_database, 0);
1512         if (obj) {
1513                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1514                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1515                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1516                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1517                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1518                         ast_odbc_release_obj(obj);
1519                         goto yuck;
1520                 }
1521                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1522                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1523                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1524                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1525                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1526                         ast_odbc_release_obj(obj);
1527                         goto yuck;
1528                 }
1529                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1530                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1531                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1532                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1533                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1534                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1535                 res = ast_odbc_smart_execute(obj, stmt);
1536                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1537                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1538                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1539                         ast_odbc_release_obj(obj);
1540                         goto yuck;
1541                 }
1542                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1543                 ast_odbc_release_obj(obj);
1544         } else
1545                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1546 yuck:
1547         return; 
1548 }
1549
1550 #else
1551 #ifndef IMAP_STORAGE
1552 static int count_messages(struct ast_vm_user *vmu, char *dir)
1553 {
1554         /* Find all .txt files - even if they are not in sequence from 0000 */
1555
1556         int vmcount = 0;
1557         DIR *vmdir = NULL;
1558         struct dirent *vment = NULL;
1559
1560         if (vm_lock_path(dir))
1561                 return ERROR_LOCK_PATH;
1562
1563         if ((vmdir = opendir(dir))) {
1564                 while ((vment = readdir(vmdir))) {
1565                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1566                                 vmcount++;
1567                 }
1568                 closedir(vmdir);
1569         }
1570         ast_unlock_path(dir);
1571         
1572         return vmcount;
1573 }
1574
1575 static void rename_file(char *sfn, char *dfn)
1576 {
1577         char stxt[PATH_MAX];
1578         char dtxt[PATH_MAX];
1579         ast_filerename(sfn,dfn,NULL);
1580         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1581         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1582         rename(stxt, dtxt);
1583 }
1584
1585 static int copy(char *infile, char *outfile)
1586 {
1587         int ifd;
1588         int ofd;
1589         int res;
1590         int len;
1591         char buf[4096];
1592
1593 #ifdef HARDLINK_WHEN_POSSIBLE
1594         /* Hard link if possible; saves disk space & is faster */
1595         if (link(infile, outfile)) {
1596 #endif
1597                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1598                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1599                         return -1;
1600                 }
1601                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1602                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1603                         close(ifd);
1604                         return -1;
1605                 }
1606                 do {
1607                         len = read(ifd, buf, sizeof(buf));
1608                         if (len < 0) {
1609                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1610                                 close(ifd);
1611                                 close(ofd);
1612                                 unlink(outfile);
1613                         }
1614                         if (len) {
1615                                 res = write(ofd, buf, len);
1616                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1617                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1618                                         close(ifd);
1619                                         close(ofd);
1620                                         unlink(outfile);
1621                                 }
1622                         }
1623                 } while (len);
1624                 close(ifd);
1625                 close(ofd);
1626                 return 0;
1627 #ifdef HARDLINK_WHEN_POSSIBLE
1628         } else {
1629                 /* Hard link succeeded */
1630                 return 0;
1631         }
1632 #endif
1633 }
1634
1635 static void copy_file(char *frompath, char *topath)
1636 {
1637         char frompath2[PATH_MAX], topath2[PATH_MAX];
1638         ast_filecopy(frompath, topath, NULL);
1639         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1640         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1641         copy(frompath2, topath2);
1642 }
1643 #endif
1644
1645 /*! \brief
1646  * A negative return value indicates an error.
1647  * \note Should always be called with a lock already set on dir.
1648  */
1649 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1650 {
1651         int x;
1652         unsigned char map[MAXMSGLIMIT] = "";
1653         DIR *msgdir;
1654         struct dirent *msgdirent;
1655         int msgdirint;
1656
1657         /* Reading the entire directory into a file map scales better than
1658          * doing a stat repeatedly on a predicted sequence.  I suspect this
1659          * is partially due to stat(2) internally doing a readdir(2) itself to
1660          * find each file. */
1661         msgdir = opendir(dir);
1662         while ((msgdirent = readdir(msgdir))) {
1663                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1664                         map[msgdirint] = 1;
1665         }
1666         closedir(msgdir);
1667
1668         for (x = 0; x < vmu->maxmsg; x++) {
1669                 if (map[x] == 0)
1670                         break;
1671         }
1672
1673         return x - 1;
1674 }
1675
1676 static int vm_delete(char *file)
1677 {
1678         char *txt;
1679         int txtsize = 0;
1680
1681         txtsize = (strlen(file) + 5)*sizeof(char);
1682         txt = alloca(txtsize);
1683         /* Sprintf here would safe because we alloca'd exactly the right length,
1684          * but trying to eliminate all sprintf's anyhow
1685          */
1686         snprintf(txt, txtsize, "%s.txt", file);
1687         unlink(txt);
1688         return ast_filedelete(file, NULL);
1689 }
1690
1691
1692 #endif
1693 static int inbuf(struct baseio *bio, FILE *fi)
1694 {
1695         int l;
1696
1697         if (bio->ateof)
1698                 return 0;
1699
1700         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1701                 if (ferror(fi))
1702                         return -1;
1703
1704                 bio->ateof = 1;
1705                 return 0;
1706         }
1707
1708         bio->iolen= l;
1709         bio->iocp= 0;
1710
1711         return 1;
1712 }
1713
1714 static int inchar(struct baseio *bio, FILE *fi)
1715 {
1716         if (bio->iocp>=bio->iolen) {
1717                 if (!inbuf(bio, fi))
1718                         return EOF;
1719         }
1720
1721         return bio->iobuf[bio->iocp++];
1722 }
1723
1724 static int ochar(struct baseio *bio, int c, FILE *so)
1725 {
1726         if (bio->linelength >= BASELINELEN) {
1727                 if (fputs(eol,so) == EOF)
1728                         return -1;
1729
1730                 bio->linelength= 0;
1731         }
1732
1733         if (putc(((unsigned char)c),so) == EOF)
1734                 return -1;
1735
1736         bio->linelength++;
1737
1738         return 1;
1739 }
1740
1741 static int base_encode(char *filename, FILE *so)
1742 {
1743         unsigned char dtable[BASEMAXINLINE];
1744         int i,hiteof= 0;
1745         FILE *fi;
1746         struct baseio bio;
1747
1748         memset(&bio, 0, sizeof(bio));
1749         bio.iocp = BASEMAXINLINE;
1750
1751         if (!(fi = fopen(filename, "rb"))) {
1752                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1753                 return -1;
1754         }
1755
1756         for (i= 0; i<9; i++) {
1757                 dtable[i]= 'A'+i;
1758                 dtable[i+9]= 'J'+i;
1759                 dtable[26+i]= 'a'+i;
1760                 dtable[26+i+9]= 'j'+i;
1761         }
1762         for (i= 0; i<8; i++) {
1763                 dtable[i+18]= 'S'+i;
1764                 dtable[26+i+18]= 's'+i;
1765         }
1766         for (i= 0; i<10; i++) {
1767                 dtable[52+i]= '0'+i;
1768         }
1769         dtable[62]= '+';
1770         dtable[63]= '/';
1771
1772         while (!hiteof){
1773                 unsigned char igroup[3], ogroup[4];
1774                 int c,n;
1775
1776                 igroup[0]= igroup[1]= igroup[2]= 0;
1777
1778                 for (n= 0;n<3;n++) {
1779                         if ((c = inchar(&bio, fi)) == EOF) {
1780                                 hiteof= 1;
1781                                 break;
1782                         }
1783
1784                         igroup[n]= (unsigned char)c;
1785                 }
1786
1787                 if (n> 0) {
1788                         ogroup[0]= dtable[igroup[0]>>2];
1789                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1790                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1791                         ogroup[3]= dtable[igroup[2]&0x3F];
1792
1793                         if (n<3) {
1794                                 ogroup[3]= '=';
1795
1796                                 if (n<2)
1797                                         ogroup[2]= '=';
1798                         }
1799
1800                         for (i= 0;i<4;i++)
1801                                 ochar(&bio, ogroup[i], so);
1802                 }
1803         }
1804
1805         if (fputs(eol,so) == EOF)
1806                 return 0;
1807
1808         fclose(fi);
1809
1810         return 1;
1811 }
1812
1813 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)
1814 {
1815         char callerid[256];
1816         /* Prepare variables for substitution in email body and subject */
1817         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1818         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1819         snprintf(passdata, passdatasize, "%d", msgnum);
1820         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1821         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1822         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1823         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1824         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1825         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1826         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1827         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1828 }
1829
1830 static char *quote(const char *from, char *to, size_t len)
1831 {
1832         char *ptr = to;
1833         *ptr++ = '"';
1834         for (; ptr < to + len - 1; from++) {
1835                 if (*from == '"')
1836                         *ptr++ = '\\';
1837                 else if (*from == '\0')
1838                         break;
1839                 *ptr++ = *from;
1840         }
1841         if (ptr < to + len - 1)
1842                 *ptr++ = '"';
1843         *ptr = '\0';
1844         return to;
1845 }
1846
1847 /*! \brief
1848  * fill in *tm for current time according to the proper timezone, if any.
1849  * Return tm so it can be used as a function argument.
1850  */
1851 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1852 {
1853         const struct vm_zone *z = NULL;
1854         time_t t = time(NULL);
1855
1856         /* Does this user have a timezone specified? */
1857         if (!ast_strlen_zero(vmu->zonetag)) {
1858                 /* Find the zone in the list */
1859                 AST_LIST_LOCK(&zones);
1860                 AST_LIST_TRAVERSE(&zones, z, list) {
1861                         if (!strcmp(z->name, vmu->zonetag))
1862                                 break;
1863                 }
1864                 AST_LIST_UNLOCK(&zones);
1865         }
1866         ast_localtime(&t, tm, z ? z->timezone : NULL);
1867         return tm;
1868 }
1869
1870 /*! \brief same as mkstemp, but return a FILE * */
1871 static FILE *vm_mkftemp(char *template)
1872 {
1873         FILE *p = NULL;
1874         int pfd = mkstemp(template);
1875         if (pfd > -1) {
1876                 p = fdopen(pfd, "w+");
1877                 if (!p) {
1878                         close(pfd);
1879                         pfd = -1;
1880                 }
1881         }
1882         return p;
1883 }
1884
1885 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)
1886 {
1887         char date[256];
1888         char host[MAXHOSTNAMELEN] = "";
1889         char who[256];
1890         char bound[256];
1891         char fname[256];
1892         char dur[256];
1893         char tmpcmd[256];
1894         struct tm tm;
1895         char *passdata2;
1896         size_t len_passdata;
1897 #ifdef IMAP_STORAGE
1898 #define ENDL "\r\n"
1899 #else
1900 #define ENDL "\n"
1901 #endif
1902
1903         gethostname(host, sizeof(host)-1);
1904         if (strchr(srcemail, '@'))
1905                 ast_copy_string(who, srcemail, sizeof(who));
1906         else 
1907                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1908         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1909         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1910         fprintf(p, "Date: %s" ENDL, date);
1911
1912         /* Set date format for voicemail mail */
1913         strftime(date, sizeof(date), emaildateformat, &tm);
1914
1915         if (!ast_strlen_zero(fromstring)) {
1916                 struct ast_channel *ast;
1917                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1918                         char *passdata;
1919                         int vmlen = strlen(fromstring)*3 + 200;
1920                         if ((passdata = alloca(vmlen))) {
1921                                 memset(passdata, 0, vmlen);
1922                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1923                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1924                                 len_passdata = strlen(passdata) * 2 + 3;
1925                                 passdata2 = alloca(len_passdata);
1926                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1927                         } else
1928                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1929                         ast_channel_free(ast);
1930                 } else
1931                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1932         } else
1933                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1934         len_passdata = strlen(vmu->fullname) * 2 + 3;
1935         passdata2 = alloca(len_passdata);
1936         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1937         if (!ast_strlen_zero(emailsubject)) {
1938                 struct ast_channel *ast;
1939                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1940                         char *passdata;
1941                         int vmlen = strlen(emailsubject) * 3 + 200;
1942                         if ((passdata = alloca(vmlen))) {
1943                                 memset(passdata, 0, vmlen);
1944                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1945                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1946                                 fprintf(p, "Subject: %s" ENDL, passdata);
1947                         } else
1948                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1949                         ast_channel_free(ast);
1950                 } else
1951                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1952         } else  if (!ast_strlen_zero(emailtitle)) {
1953                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1954                 fprintf(p, ENDL) ;
1955         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1956                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1957         else
1958                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
1959         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, getpid(), host);
1960         if(imap) {
1961                 /* additional information needed for IMAP searching */
1962                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
1963                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1964                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
1965                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
1966                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
1967                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
1968                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
1969                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
1970                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
1971                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
1972                 if (!ast_strlen_zero(category))
1973                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
1974                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
1975                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
1976         }
1977         if (!ast_strlen_zero(cidnum))
1978                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
1979         if (!ast_strlen_zero(cidname))
1980                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
1981         fprintf(p, "MIME-Version: 1.0" ENDL);
1982         if (attach_user_voicemail) {
1983                 /* Something unique. */
1984                 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum + 1, mailbox, getpid(), (unsigned int)ast_random());
1985
1986                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL ENDL ENDL, bound);
1987
1988                 fprintf(p, "--%s" ENDL, bound);
1989         }
1990         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
1991         if (emailbody) {
1992                 struct ast_channel *ast;
1993                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1994                         char *passdata;
1995                         int vmlen = strlen(emailbody)*3 + 200;
1996                         if ((passdata = alloca(vmlen))) {
1997                                 memset(passdata, 0, vmlen);
1998                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1999                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2000                                 fprintf(p, "%s" ENDL, passdata);
2001                         } else
2002                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2003                         ast_channel_free(ast);
2004                 } else
2005                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2006         } else {
2007                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2008
2009                 "in mailbox %s from %s, on %s so you might" ENDL
2010                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2011                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2012         }
2013         if (attach_user_voicemail) {
2014                 /* Eww. We want formats to tell us their own MIME type */
2015                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2016                 char tmpdir[256], newtmp[256];
2017                 int tmpfd;
2018         
2019                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2020                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2021                 tmpfd = mkstemp(newtmp);
2022                 if (option_debug > 2)
2023                         ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2024                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2025                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2026                         ast_safe_system(tmpcmd);
2027                         attach = newtmp;
2028                         if (option_debug > 2)
2029                                 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2030                 }
2031                 fprintf(p, "--%s" ENDL, bound);
2032                 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2033                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2034                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2035                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2036                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2037                 base_encode(fname, p);
2038                 /* only attach if necessary */
2039                 if (imap && !strcmp(format, "gsm")) {
2040                         fprintf(p, "--%s" ENDL, bound);
2041                         fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"" ENDL, msgnum + 1, format);
2042                         fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2043                         fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2044                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"" ENDL ENDL, msgnum + 1);
2045                         snprintf(fname, sizeof(fname), "%s.gsm", attach);
2046                         base_encode(fname, p);
2047                 }
2048                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2049                 if (tmpfd > -1)
2050                         close(tmpfd);
2051                 unlink(newtmp);
2052         }
2053 #undef ENDL
2054 }
2055
2056 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)
2057 {
2058         FILE *p=NULL;
2059         char tmp[80] = "/tmp/astmail-XXXXXX";
2060         char tmp2[256];
2061
2062         if (vmu && ast_strlen_zero(vmu->email)) {
2063                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2064                 return(0);
2065         }
2066         if (!strcmp(format, "wav49"))
2067                 format = "WAV";
2068         if (option_debug > 2)
2069                 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));
2070         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2071            command hangs */
2072         if ((p = vm_mkftemp(tmp)) == NULL) {
2073                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2074                 return -1;
2075         } else {
2076                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2077                 fclose(p);
2078                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2079                 ast_safe_system(tmp2);
2080                 if (option_debug)
2081                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2082         }
2083         return 0;
2084 }
2085
2086 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)
2087 {
2088         char date[256];
2089         char host[MAXHOSTNAMELEN] = "";
2090         char who[256];
2091         char dur[PATH_MAX];
2092         char tmp[80] = "/tmp/astmail-XXXXXX";
2093         char tmp2[PATH_MAX];
2094         struct tm tm;
2095         FILE *p;
2096
2097         if ((p = vm_mkftemp(tmp)) == NULL) {
2098                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2099                 return -1;
2100         }
2101         gethostname(host, sizeof(host)-1);
2102         if (strchr(srcemail, '@'))
2103                 ast_copy_string(who, srcemail, sizeof(who));
2104         else 
2105                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2106         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2107         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2108         fprintf(p, "Date: %s\n", date);
2109
2110         if (*pagerfromstring) {
2111                 struct ast_channel *ast;
2112                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2113                         char *passdata;
2114                         int vmlen = strlen(fromstring)*3 + 200;
2115                         if ((passdata = alloca(vmlen))) {
2116                                 memset(passdata, 0, vmlen);
2117                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2118                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2119                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2120                         } else 
2121                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2122                         ast_channel_free(ast);
2123                 } else 
2124                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2125         } else
2126                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2127         fprintf(p, "To: %s\n", pager);
2128         if (pagersubject) {
2129                 struct ast_channel *ast;
2130                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2131                         char *passdata;
2132                         int vmlen = strlen(pagersubject) * 3 + 200;
2133                         if ((passdata = alloca(vmlen))) {
2134                                 memset(passdata, 0, vmlen);
2135                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2136                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2137                                 fprintf(p, "Subject: %s\n\n", passdata);
2138                         } else
2139                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2140                         ast_channel_free(ast);
2141                 } else
2142                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2143         } else
2144                 fprintf(p, "Subject: New VM\n\n");
2145
2146         strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2147         if (pagerbody) {
2148                 struct ast_channel *ast;
2149                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2150                         char *passdata;
2151                         int vmlen = strlen(pagerbody)*3 + 200;
2152                         if ((passdata = alloca(vmlen))) {
2153                                 memset(passdata, 0, vmlen);
2154                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2155                                 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2156                                 fprintf(p, "%s\n", passdata);
2157                         } else
2158                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2159                         ast_channel_free(ast);
2160                 } else
2161                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2162         } else {
2163                 fprintf(p, "New %s long msg in box %s\n"
2164                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2165         }
2166         fclose(p);
2167         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2168         ast_safe_system(tmp2);
2169         if (option_debug)
2170                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2171         return 0;
2172 }
2173
2174 static int get_date(char *s, int len)
2175 {
2176         struct tm tm;
2177         time_t t;
2178         t = time(0);
2179         localtime_r(&t,&tm);
2180         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2181 }
2182
2183 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2184 {
2185         int res;
2186         char fn[PATH_MAX];
2187         char dest[PATH_MAX];
2188
2189         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2190
2191         if ((res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2192                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2193                 return -1;
2194         }
2195
2196         RETRIEVE(fn, -1);
2197         if (ast_fileexists(fn, NULL, NULL) > 0) {
2198                 res = ast_stream_and_wait(chan, fn, ecodes);
2199                 if (res) {
2200                         DISPOSE(fn, -1);
2201                         return res;
2202                 }
2203         } else {
2204                 /* Dispose just in case */
2205                 DISPOSE(fn, -1);
2206                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2207                 if (res)
2208                         return res;
2209                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2210                 if (res)
2211                         return res;
2212         }
2213         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2214         return res;
2215 }
2216
2217 static void free_user(struct ast_vm_user *vmu)
2218 {
2219         if (ast_test_flag(vmu, VM_ALLOCED))
2220                 free(vmu);
2221 }
2222
2223 static void free_zone(struct vm_zone *z)
2224 {
2225         free(z);
2226 }
2227
2228 static const char *mbox(int id)
2229 {
2230         static const char *msgs[] = {
2231                 "INBOX",
2232                 "Old",
2233                 "Work",
2234                 "Family",
2235                 "Friends",
2236                 "Cust1",
2237                 "Cust2",
2238                 "Cust3",
2239                 "Cust4",
2240                 "Cust5",
2241         };
2242         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2243 }
2244
2245 #ifdef ODBC_STORAGE
2246 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2247 {
2248         int x = -1;
2249         int res;
2250         SQLHSTMT stmt;
2251         char sql[PATH_MAX];
2252         char rowdata[20];
2253         char tmp[PATH_MAX] = "";
2254         struct odbc_obj *obj;
2255         char *context;
2256
2257         if (newmsgs)
2258                 *newmsgs = 0;
2259         if (oldmsgs)
2260                 *oldmsgs = 0;
2261
2262         /* If no mailbox, return immediately */
2263         if (ast_strlen_zero(mailbox))
2264                 return 0;
2265
2266         ast_copy_string(tmp, mailbox, sizeof(tmp));
2267         
2268         context = strchr(tmp, '@');
2269         if (context) {
2270                 *context = '\0';
2271                 context++;
2272         } else
2273                 context = "default";
2274         
2275         obj = ast_odbc_request_obj(odbc_database, 0);
2276         if (obj) {
2277                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2278                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2279                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2280                         ast_odbc_release_obj(obj);
2281                         goto yuck;
2282                 }
2283                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2284                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2285                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2286                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2287                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2288                         ast_odbc_release_obj(obj);
2289                         goto yuck;
2290                 }
2291                 res = ast_odbc_smart_execute(obj, stmt);
2292                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2293                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2294                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2295                         ast_odbc_release_obj(obj);
2296                         goto yuck;
2297                 }
2298                 res = SQLFetch(stmt);
2299                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2300                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2301                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2302                         ast_odbc_release_obj(obj);
2303                         goto yuck;
2304                 }
2305                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2306                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2307                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2308                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2309                         ast_odbc_release_obj(obj);
2310                         goto yuck;
2311                 }
2312                 *newmsgs = atoi(rowdata);
2313                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2314
2315                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2316                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2317                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2318                         ast_odbc_release_obj(obj);
2319                         goto yuck;
2320                 }
2321                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2322                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2323                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2324                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2325                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2326                         ast_odbc_release_obj(obj);
2327                         goto yuck;
2328                 }
2329                 res = ast_odbc_smart_execute(obj, stmt);
2330                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2331                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2332                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2333                         ast_odbc_release_obj(obj);
2334                         goto yuck;
2335                 }
2336                 res = SQLFetch(stmt);
2337                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2338                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2339                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2340                         ast_odbc_release_obj(obj);
2341                         goto yuck;
2342                 }
2343                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2344                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2345                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2346                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2347                         ast_odbc_release_obj(obj);
2348                         goto yuck;
2349                 }
2350                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2351                 ast_odbc_release_obj(obj);
2352                 *oldmsgs = atoi(rowdata);
2353                 x = 0;
2354         } else
2355                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2356                 
2357 yuck:   
2358         return x;
2359 }
2360
2361 static int messagecount(const char *context, const char *mailbox, const char *folder)
2362 {
2363         struct odbc_obj *obj = NULL;
2364         int nummsgs = 0;
2365         int res;
2366         SQLHSTMT stmt = NULL;
2367         char sql[PATH_MAX];
2368         char rowdata[20];
2369         if (!folder)
2370                 folder = "INBOX";
2371         /* If no mailbox, return immediately */
2372         if (ast_strlen_zero(mailbox))
2373                 return 0;
2374
2375         obj = ast_odbc_request_obj(odbc_database, 0);
2376         if (obj) {
2377                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2378                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2379                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2380                         goto yuck;
2381                 }
2382                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2383                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2384                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2385                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2386                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2387                         goto yuck;
2388                 }
2389                 res = ast_odbc_smart_execute(obj, stmt);
2390                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2391                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2392                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2393                         goto yuck;
2394                 }
2395                 res = SQLFetch(stmt);
2396                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2397                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2398                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2399                         goto yuck;
2400                 }
2401                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2402                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2403                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2404                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2405                         goto yuck;
2406                 }
2407                 nummsgs = atoi(rowdata);
2408                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2409         } else
2410                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2411
2412 yuck:
2413         if (obj)
2414                 ast_odbc_release_obj(obj);
2415         return nummsgs;
2416 }
2417
2418 static int has_voicemail(const char *mailbox, const char *folder)
2419 {
2420         char *context, tmp[256];
2421         ast_copy_string(tmp, mailbox, sizeof(tmp));
2422         if ((context = strchr(tmp, '@')))
2423                 *context++ = '\0';
2424         else
2425                 context = "default";
2426
2427         if (messagecount(context, tmp, folder))
2428                 return 1;
2429         else
2430                 return 0;
2431 }
2432
2433 #elif defined(IMAP_STORAGE)
2434
2435 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)
2436 {
2437         char *myserveremail = serveremail;
2438         char fn[PATH_MAX];
2439         char mailbox[256];
2440         char *stringp;
2441         FILE *p=NULL;
2442         char tmp[80] = "/tmp/astmail-XXXXXX";
2443         long len;
2444         void *buf;
2445         STRING str;
2446         
2447         /* Attach only the first format */
2448         fmt = ast_strdupa(fmt);
2449         stringp = fmt;
2450         strsep(&stringp, "|");
2451
2452         if (!ast_strlen_zero(vmu->serveremail))
2453                 myserveremail = vmu->serveremail;
2454
2455         make_file(fn, sizeof(fn), dir, msgnum);
2456
2457         if (ast_strlen_zero(vmu->email))
2458                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2459
2460         if (!strcmp(fmt, "wav49"))
2461                 fmt = "WAV";
2462         if(option_debug > 2)
2463                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2464
2465         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2466            command hangs */
2467         if (!(p = vm_mkftemp(tmp))) {
2468                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2469                 return -1;
2470         }
2471         
2472         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);
2473         /* read mail file to memory */          
2474         len = ftell(p);
2475         rewind(p);
2476         if (!(buf = ast_malloc(len+1))) {
2477                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2478                 return -1;
2479         }
2480         fread(buf, len, 1, p);
2481         ((char *)buf)[len] = '\0';
2482         INIT(&str, mail_string, buf, len);
2483         init_mailstream(vms, 0);
2484         imap_mailbox_name(mailbox, vms, 0, 1);
2485         if(!mail_append(vms->mailstream, mailbox, &str))
2486                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2487         fclose(p);
2488         unlink(tmp);
2489         ast_free(buf);
2490         if(option_debug > 2)
2491                 ast_log(LOG_DEBUG, "%s stored\n", fn);
2492         return 0;
2493
2494 }
2495
2496 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2497 {
2498         SEARCHPGM *pgm;
2499         SEARCHHEADER *hdr;
2500  
2501         struct ast_vm_user *vmu;
2502         struct vm_state *vms_p;
2503         char tmp[256] = "";
2504         char *mb, *cur;
2505         char *mailboxnc; 
2506         char *context;
2507         int ret = 0;
2508
2509         if (newmsgs)
2510                 *newmsgs = 0;
2511
2512         if (oldmsgs)
2513                 *oldmsgs = 0;
2514  
2515         if(option_debug > 2)
2516                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2517
2518         /* If no mailbox, return immediately */
2519         if (ast_strlen_zero(mailbox))
2520                 return 0;
2521
2522         if (strchr(mailbox, ',')) {
2523                 int tmpnew, tmpold;
2524                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2525                 mb = tmp;
2526                 ret = 0;
2527                 while((cur = strsep(&mb, ", "))) {
2528                         if (!ast_strlen_zero(cur)) {
2529                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2530                                         return -1;
2531                                 else {
2532                                         if (newmsgs)
2533                                                 *newmsgs += tmpnew; 
2534                                         if (oldmsgs)
2535                                                 *oldmsgs += tmpold;
2536                                 }
2537                         }
2538                 }
2539                 return 0;
2540         }
2541
2542         ast_copy_string(tmp, mailbox, sizeof(tmp));
2543
2544         if ((context = strchr(tmp, '@'))) {
2545                 *context = '\0';
2546                 mailboxnc = tmp;
2547                 context++;
2548         } else {
2549                 context = "default";
2550                 mailboxnc = (char *)mailbox;
2551         }
2552  
2553         /* We have to get the user before we can open the stream! */
2554         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2555         if (!(vmu = find_user(NULL, context, mailboxnc))) {
2556                 ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2557                 return -1;
2558         }
2559         
2560         /* No IMAP account available */
2561         if (vmu->imapuser[0] == '\0') {
2562                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2563                 return -1;
2564         }
2565  
2566         /* check if someone is accessing this box right now... */
2567         if ((vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1)) || (vms_p = get_vm_state_by_mailbox(mailboxnc, 1))) {
2568                 if(option_debug > 2)
2569                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2570                 *newmsgs = vms_p->newmessages;
2571                 *oldmsgs = vms_p->oldmessages;
2572                 return 0;
2573         }
2574  
2575         /* add one if not there... */
2576         if (!(vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0)) && !(vms_p = get_vm_state_by_mailbox(mailboxnc, 0))) {
2577                 if(option_debug > 2)
2578                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2579                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2580                         return -1;
2581                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2582                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2583                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2584                 if(option_debug > 2)
2585                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2586                 vms_p->updated = 1;
2587                 /* set mailbox to INBOX! */
2588                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2589                 init_vm_state(vms_p);
2590                 vmstate_insert(vms_p);
2591         }
2592
2593         /* If no mailstream exists yet and even after attempting to initialize it fails, bail out */
2594         ret = init_mailstream(vms_p, 0);
2595         if (!vms_p->mailstream) {
2596                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2597                 return -1;
2598         }
2599
2600         if (!ret && vms_p->updated == 1) {
2601                 if (newmsgs) {
2602                         pgm = mail_newsearchpgm();
2603                         hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (char *)mailboxnc);
2604                         pgm->header = hdr;
2605                         pgm->unseen = 1;
2606                         pgm->seen = 0;
2607                         pgm->undeleted = 1;
2608                         pgm->deleted = 0;
2609                         vms_p->vmArrayIndex = 0;
2610                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2611                         *newmsgs = vms_p->vmArrayIndex;
2612                         vms_p->newmessages = vms_p->vmArrayIndex;
2613                         mail_free_searchpgm(&pgm);
2614                 }
2615                 if (oldmsgs) {
2616                         pgm = mail_newsearchpgm ();
2617                         hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2618                         pgm->header = hdr;
2619                         pgm->unseen = 0;
2620                         pgm->seen = 1;
2621                         pgm->undeleted = 1;
2622                         pgm->deleted = 0;
2623                         vms_p->vmArrayIndex = 0;
2624                         mail_search_full(vms_p->mailstream, NULL, pgm, NIL);
2625                         *oldmsgs = vms_p->vmArrayIndex;
2626                         vms_p->oldmessages = vms_p->vmArrayIndex;
2627                         mail_free_searchpgm(&pgm);
2628                 }
2629         }
2630
2631         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2632                 vms_p->updated = 0;
2633         } else if (vms_p->updated > 1) {  /* decrement delay count */
2634                 vms_p->updated--;
2635         } else {  /* no changes, so don't search */
2636                 mail_ping(vms_p->mailstream);
2637                 /* Keep the old data */
2638                 *newmsgs = vms_p->newmessages;
2639                 *oldmsgs = vms_p->oldmessages;
2640         }
2641
2642         return 0;
2643  }
2644
2645 static int has_voicemail(const char *mailbox, const char *folder)
2646 {
2647         int newmsgs, oldmsgs;
2648         
2649         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2650                 return folder? oldmsgs: newmsgs;
2651         else
2652                 return 0;
2653 }
2654
2655 static int messagecount(const char *context, const char *mailbox, const char *folder)
2656 {
2657         int newmsgs, oldmsgs;
2658         char tmp[256] = "";
2659         
2660         if (ast_strlen_zero(mailbox))
2661                 return 0;
2662         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2663
2664         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2665                 return folder? oldmsgs: newmsgs;
2666         else
2667                 return 0;
2668 }
2669
2670 #endif
2671 #ifndef IMAP_STORAGE
2672 /* copy message only used by file storage */
2673 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)
2674 {
2675         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2676         const char *frombox = mbox(imbox);
2677         int recipmsgnum;
2678
2679         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2680
2681         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2682         
2683         if (!dir)
2684                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2685         else
2686                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2687
2688         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2689         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2690
2691         if (vm_lock_path(todir))
2692                 return ERROR_LOCK_PATH;
2693
2694         recipmsgnum = last_message_index(recip, todir) + 1;
2695         if (recipmsgnum < recip->maxmsg) {
2696                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2697                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2698         } else {
2699                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2700         }
2701         ast_unlock_path(todir);
2702         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2703         
2704         return 0;
2705 }
2706 #endif
2707 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2708 static int messagecount(const char *context, const char *mailbox, const char *folder)
2709 {
2710         return __has_voicemail(context, mailbox, folder, 0);
2711 }
2712
2713
2714 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2715 {
2716         DIR *dir;
2717         struct dirent *de;
2718         char fn[256];
2719         int ret = 0;
2720
2721         /* If no mailbox, return immediately */
2722         if (ast_strlen_zero(mailbox))
2723                 return 0;
2724
2725         if (ast_strlen_zero(folder))
2726                 folder = "INBOX";
2727         if (ast_strlen_zero(context))
2728                 context = "default";
2729
2730         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2731
2732         if (!(dir = opendir(fn)))
2733                 return 0;
2734
2735         while ((de = readdir(dir))) {
2736                 if (!strncasecmp(de->d_name, "msg", 3)) {
2737                         if (shortcircuit) {
2738                                 ret = 1;
2739                                 break;
2740                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2741                                 ret++;
2742                 }
2743         }
2744
2745         closedir(dir);
2746
2747         return ret;
2748 }
2749
2750
2751 static int has_voicemail(const char *mailbox, const char *folder)
2752 {
2753         char tmp[256], *tmp2 = tmp, *mbox, *context;
2754         ast_copy_string(tmp, mailbox, sizeof(tmp));
2755         while ((mbox = strsep(&tmp2, ","))) {
2756                 if ((context = strchr(mbox, '@')))
2757                         *context++ = '\0';
2758                 else
2759                         context = "default";
2760                 if (__has_voicemail(context, mbox, folder, 1))
2761                         return 1;
2762         }
2763         return 0;
2764 }
2765
2766
2767 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2768 {
2769         char tmp[256];
2770         char *context;
2771
2772         /* If no mailbox, return immediately */
2773         if (ast_strlen_zero(mailbox))
2774                 return 0;
2775
2776         if (newmsgs)
2777                 *newmsgs = 0;
2778         if (oldmsgs)
2779                 *oldmsgs = 0;
2780
2781         if (strchr(mailbox, ',')) {
2782                 int tmpnew, tmpold;
2783                 char *mb, *cur;
2784
2785                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2786                 mb = tmp;
2787                 while ((cur = strsep(&mb, ", "))) {
2788                         if (!ast_strlen_zero(cur)) {
2789                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2790                                         return -1;
2791                                 else {
2792                                         if (newmsgs)
2793                                                 *newmsgs += tmpnew; 
2794                                         if (oldmsgs)
2795                                                 *oldmsgs += tmpold;
2796                                 }
2797                         }
2798                 }
2799                 return 0;
2800         }
2801
2802         ast_copy_string(tmp, mailbox, sizeof(tmp));
2803         
2804         if ((context = strchr(tmp, '@')))
2805                 *context++ = '\0';
2806         else
2807                 context = "default";
2808
2809         if (newmsgs)
2810                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2811         if (oldmsgs)
2812                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2813
2814         return 0;
2815 }
2816
2817 #endif
2818
2819 static void run_externnotify(char *context, char *extension)
2820 {
2821         char arguments[255];
2822         char ext_context[256] = "";
2823         int newvoicemails = 0, oldvoicemails = 0;
2824         struct ast_smdi_mwi_message *mwi_msg;
2825
2826         if (!ast_strlen_zero(context))
2827                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2828         else
2829                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2830
2831         if (smdi_iface) {
2832                 if (ast_app_has_voicemail(ext_context, NULL)) 
2833                         ast_smdi_mwi_set(smdi_iface, extension);
2834                 else
2835                         ast_smdi_mwi_unset(smdi_iface, extension);
2836
2837                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2838                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2839                         if (!strncmp(mwi_msg->cause, "INV", 3))
2840                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2841                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2842                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2843                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2844                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2845                 } else {
2846                         if (option_debug)
2847                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2848                 }
2849         }
2850
2851         if (!ast_strlen_zero(externnotify)) {
2852                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2853                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2854                 } else {
2855                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2856                         if (option_debug)
2857                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2858                         ast_safe_system(arguments);
2859                 }
2860         }
2861 }
2862
2863 struct leave_vm_options {
2864         unsigned int flags;
2865         signed char record_gain;
2866 };
2867
2868 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2869 {
2870 #ifdef IMAP_STORAGE
2871         int newmsgs, oldmsgs;
2872         struct vm_state *vms = NULL;
2873 #endif
2874         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2875         char callerid[256];
2876         FILE *txt;
2877         char date[256];
2878         int txtdes;
2879         int res = 0;
2880         int msgnum;
2881         int duration = 0;
2882         int ausemacro = 0;
2883         int ousemacro = 0;
2884         int ouseexten = 0;
2885         char dir[PATH_MAX], tmpdir[PATH_MAX];
2886         char dest[PATH_MAX];
2887         char fn[PATH_MAX];
2888         char prefile[PATH_MAX] = "";
2889         char tempfile[PATH_MAX] = "";
2890         char ext_context[256] = "";
2891         char fmt[80];
2892         char *context;
2893         char ecodes[16] = "#";
2894         char tmp[256] = "", *tmpptr;
2895         struct ast_vm_user *vmu;
2896         struct ast_vm_user svm;
2897         const char *category = NULL;
2898
2899         ast_copy_string(tmp, ext, sizeof(tmp));
2900         ext = tmp;
2901         if ((context = strchr(tmp, '@'))) {
2902                 *context++ = '\0';
2903                 tmpptr = strchr(context, '&');
2904         } else {
2905                 tmpptr = strchr(ext, '&');
2906         }
2907
2908         if (tmpptr)
2909                 *tmpptr++ = '\0';
2910
2911         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2912
2913         if(option_debug > 2)
2914                 ast_log(LOG_DEBUG, "Before find_user\n");
2915         if (!(vmu = find_user(&svm, context, ext))) {
2916                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2917                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2918                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2919                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2920                 return res;
2921         }
2922         /* Setup pre-file if appropriate */
2923         if (strcmp(vmu->context, "default"))
2924                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2925         else
2926                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2927         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2928                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2929                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2930         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2931                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2932                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2933         }
2934         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2935         if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2936                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2937                 return -1;
2938         }
2939         RETRIEVE(tempfile, -1);
2940         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2941                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2942         DISPOSE(tempfile, -1);
2943         /* It's easier just to try to make it than to check for its existence */
2944         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2945         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2946
2947         /* Check current or macro-calling context for special extensions */
2948         if (ast_test_flag(vmu, VM_OPERATOR)) {
2949                 if (!ast_strlen_zero(vmu->exit)) {
2950                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2951                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2952                                 ouseexten = 1;
2953                         }
2954                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2955                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2956                         ouseexten = 1;
2957                 }
2958                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2959                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2960                 ousemacro = 1;
2961                 }
2962         }
2963
2964         if (!ast_strlen_zero(vmu->exit)) {
2965                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2966                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2967         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2968                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2969         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2970                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2971                 ausemacro = 1;
2972         }
2973
2974         /* Play the beginning intro if desired */
2975         if (!ast_strlen_zero(prefile)) {
2976                 RETRIEVE(prefile, -1);
2977                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2978                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2979                                 res = ast_waitstream(chan, ecodes);
2980                 } else {
2981                         if (option_debug)
2982                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2983                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2984                 }
2985                 DISPOSE(prefile, -1);
2986                 if (res < 0) {
2987                         if (option_debug)
2988                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2989                         free_user(vmu);
2990                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2991                         return -1;
2992                 }
2993         }
2994         if (res == '#') {
2995                 /* On a '#' we skip the instructions */
2996                 ast_set_flag(options, OPT_SILENT);
2997                 res = 0;
2998         }
2999         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3000                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3001                 if (res == '#') {
3002                         ast_set_flag(options, OPT_SILENT);
3003                         res = 0;
3004                 }
3005         }
3006         if (res > 0)
3007                 ast_stopstream(chan);
3008         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3009          other than the operator -- an automated attendant or mailbox login for example */
3010         if (res == '*') {
3011                 chan->exten[0] = 'a';
3012                 chan->exten[1] = '\0';
3013                 if (!ast_strlen_zero(vmu->exit)) {
3014                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3015                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3016                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3017                 }
3018                 chan->priority = 0;
3019                 free_user(vmu);
3020                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3021                 return 0;
3022         }
3023
3024         /* Check for a '0' here */
3025         if (res == '0') {
3026         transfer:
3027                 if (ouseexten || ousemacro) {
3028                         chan->exten[0] = 'o';
3029                         chan->exten[1] = '\0';
3030                         if (!ast_strlen_zero(vmu->exit)) {
3031                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3032                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3033                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3034                         }
3035                         ast_play_and_wait(chan, "transfer");
3036                         chan->priority = 0;
3037                         free_user(vmu);
3038                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3039                 }
3040                 return 0;
3041         }
3042         if (res < 0) {
3043                 free_user(vmu);
3044                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3045                 return -1;
3046         }
3047         /* The meat of recording the message...  All the announcements and beeps have been played*/
3048         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3049         if (!ast_strlen_zero(fmt)) {
3050                 msgnum = 0;
3051
3052 #ifdef IMAP_STORAGE
3053                 /* Is ext a mailbox? */
3054                 /* must open stream for this user to get info! */
3055                 vms = get_vm_state_by_mailbox(ext,0);
3056                 if (vms) {
3057                         if(option_debug > 2)
3058                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3059                         newmsgs = vms->newmessages++;
3060                         oldmsgs = vms->oldmessages;
3061                 } else {
3062                         res = inboxcount(ext, &newmsgs, &oldmsgs);
3063                         if(res < 0) {
3064                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3065                                 return -1;
3066                         }
3067                         vms = get_vm_state_by_mailbox(ext,0);
3068                 }
3069                 /* here is a big difference! We add one to it later */
3070                 msgnum = newmsgs + oldmsgs;
3071                 if(option_debug > 2)
3072                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3073                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3074                 /* set variable for compatibility */
3075                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3076
3077                 /* Check if mailbox is full */
3078                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3079                         if (option_debug)
3080                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3081                         ast_play_and_wait(chan, "vm-mailboxfull");
3082                         return -1;
3083                 }
3084                 /* here is a big difference! We add one to it later */
3085                 msgnum = newmsgs + oldmsgs;
3086                 if(option_debug > 2)
3087                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3088
3089 #else
3090                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3091                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3092                         if (!res)
3093                                 res = ast_waitstream(chan, "");
3094                         ast_log(LOG_WARNING, "No more messages possible\n");
3095                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3096                         goto leave_vm_out;
3097                 }
3098
3099 #endif
3100                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3101                 txtdes = mkstemp(tmptxtfile);
3102                 if (txtdes < 0) {
3103                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3104                         if (!res)
3105                                 res = ast_waitstream(chan, "");
3106                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3107                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3108                         goto leave_vm_out;
3109                 }
3110
3111                 /* Now play the beep once we have the message number for our next message. */
3112                 if (res >= 0) {
3113                         /* Unless we're *really* silent, try to send the beep */
3114                         res = ast_stream_and_wait(chan, "beep", "");
3115                 }
3116                                 
3117                 /* Store information */
3118                 txt = fdopen(txtdes, "w+");
3119                 if (txt) {
3120                         get_date(date, sizeof(date));
3121                         fprintf(txt, 
3122                                 ";\n"
3123                                 "; Message Information file\n"
3124                                 ";\n"
3125                                 "[message]\n"
3126                                 "origmailbox=%s\n"
3127                                 "context=%s\n"
3128                                 "macrocontext=%s\n"
3129                                 "exten=%s\n"
3130                                 "priority=%d\n"
3131                                 "callerchan=%s\n"
3132                                 "callerid=%s\n"
3133                                 "origdate=%s\n"
3134                                 "origtime=%ld\n"
3135                                 "category=%s\n",
3136                                 ext,
3137                                 chan->context,
3138                                 chan->macrocontext, 
3139                                 chan->exten,
3140                                 chan->priority,
3141                                 chan->name,
3142                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3143                                 date, (long)time(NULL),
3144                                 category ? category : ""); 
3145                 } else
3146                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3147 #ifdef IMAP_STORAGE
3148                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3149 #else
3150                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3151 #endif
3152
3153                 if (txt) {
3154                         if (duration < vmminsecs) {
3155                                 if (option_verbose > 2) 
3156                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3157                                 ast_filedelete(tmptxtfile, NULL);
3158                                 unlink(tmptxtfile);
3159                         } else {
3160                                 fprintf(txt, "duration=%d\n", duration);
3161                                 fclose(txt);
3162                                 if (vm_lock_path(dir)) {
3163                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3164                                         /* Delete files */
3165                                         ast_filedelete(tmptxtfile, NULL);
3166                                         unlink(tmptxtfile);
3167                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3168                                         if (option_debug) 
3169                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3170                                         unlink(tmptxtfile);
3171                                         ast_unlock_path(dir);
3172                                 } else {
3173                                         msgnum = last_message_index(vmu, dir) + 1;
3174                                         make_file(fn, sizeof(fn), dir, msgnum);
3175
3176                                         /* assign a variable with the name of the voicemail file */ 
3177 #ifndef IMAP_STORAGE
3178                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3179 #else
3180                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3181 #endif
3182
3183                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3184                                         ast_filerename(tmptxtfile, fn, NULL);
3185                                         rename(tmptxtfile, txtfile);
3186
3187                                         ast_unlock_path(dir);
3188 #ifndef IMAP_STORAGE
3189                                         /* Are there to be more recipients of this message? */
3190                                         while (tmpptr) {
3191                                                 struct ast_vm_user recipu, *recip;
3192                                                 char *exten, *context;
3193                                         
3194                                                 exten = strsep(&tmpptr, "&");
3195                                                 context = strchr(exten, '@');
3196                                                 if (context) {
3197                                                         *context = '\0';
3198                                                         context++;
3199                                                 }
3200                                                 if ((recip = find_user(&recipu, context, exten))) {
3201                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3202                                                         free_user(recip);
3203                                                 }
3204                                         }
3205 #endif
3206                                         if (ast_fileexists(fn, NULL, NULL)) {
3207                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3208                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3209                                                 DISPOSE(dir, msgnum);
3210                                         }
3211                                 }
3212                         }
3213                 }
3214                 if (res == '0') {
3215                         goto transfer;
3216                 } else if (res > 0)
3217                         res = 0;
3218
3219                 if (duration < vmminsecs)
3220                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3221                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3222                 else
3223                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3224         } else
3225                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3226 leave_vm_out:
3227         free_user(vmu);
3228         
3229         return res;
3230 }
3231
3232 #ifndef IMAP_STORAGE
3233 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3234 {
3235         /* we know max messages, so stop process when number is hit */
3236
3237         int x,dest;
3238         char sfn[PATH_MAX];
3239         char dfn[PATH_MAX];
3240
3241         if (vm_lock_path(dir))
3242                 return ERROR_LOCK_PATH;
3243
3244         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3245                 make_file(sfn, sizeof(sfn), dir, x);
3246                 if (EXISTS(dir, x, sfn, NULL)) {
3247                         
3248                         if (x != dest) {
3249                                 make_file(dfn, sizeof(dfn), dir, dest);
3250                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3251                         }
3252                         
3253                         dest++;
3254                 }
3255         }
3256         ast_unlock_path(dir);
3257
3258         return 0;
3259 }
3260 #endif
3261
3262 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3263 {
3264         int d;
3265         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3266         return d;
3267 }
3268
3269 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3270 {
3271 #ifdef IMAP_STORAGE
3272         /* we must use mbox(x) folder names, and copy the message there */
3273         /* simple. huh? */
3274         char dbox[256];
3275         long res;
3276         char sequence[10];
3277
3278         /* if save to Old folder, just leave in INBOX */
3279         if (box == 1) return 10;
3280         /* get the real IMAP message number for this message */
3281         sprintf(sequence,"%ld",vms->msgArray[msg]);
3282         imap_mailbox_name(dbox, vms, box, 1);
3283         if (option_debug > 2)
3284                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3285         res = mail_copy(vms->mailstream, sequence, dbox);
3286         if (res == 1) return 0;
3287         return 1;
3288 #else
3289         char *dir = vms->curdir;
3290         char *username = vms->username;
3291         char *context = vmu->context;
3292         char sfn[PATH_MAX];
3293         char dfn[PATH_MAX];
3294         char ddir[PATH_MAX];
3295         const char *dbox = mbox(box);
3296         int x;
3297         make_file(sfn, sizeof(sfn), dir, msg);
3298         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3299
3300         if (vm_lock_path(ddir))
3301                 return ERROR_LOCK_PATH;
3302
3303         x = last_message_index(vmu, ddir) + 1;
3304         make_file(dfn, sizeof(dfn), ddir, x);
3305
3306         if (x >= vmu->maxmsg) {
3307                 ast_unlock_path(ddir);
3308                 return -1;
3309         }
3310         if (strcmp(sfn, dfn)) {
3311                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3312         }
3313         ast_unlock_path(ddir);
3314 #endif
3315         return 0;
3316 }
3317
3318 static int adsi_logo(unsigned char *buf)
3319 {
3320         int bytes = 0;
3321         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3322         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3323         return bytes;
3324 }
3325
3326 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3327 {
3328         unsigned char buf[256];
3329         int bytes=0;
3330         int x;
3331         char num[5];
3332
3333         *useadsi = 0;
3334         bytes += ast_adsi_data_mode(buf + bytes);
3335         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3336
3337         bytes = 0;
3338         bytes += adsi_logo(buf);
3339         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3340 #ifdef DISPLAY
3341         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3342 #endif
3343         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3344         bytes += ast_adsi_data_mode(buf + bytes);
3345         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3346
3347         if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3348                 bytes = 0;
3349                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3350                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3351                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3352                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3353                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3354                 return 0;
3355         }
3356
3357 #ifdef DISPLAY
3358         /* Add a dot */
3359         bytes = 0;
3360         bytes += ast_adsi_logo(buf);
3361         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3362         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3363         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3364         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3365 #endif
3366         bytes = 0;
3367         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3368         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3369         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3370         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3371         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3372         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3373         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3374
3375 #ifdef DISPLAY
3376         /* Add another dot */
3377         bytes = 0;
3378         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
3379         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3380
3381         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3382         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3383 #endif
3384
3385         bytes = 0;
3386         /* These buttons we load but don't use yet */
3387         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3388         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3389         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3390         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3391         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3392         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3393         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3394
3395 #ifdef DISPLAY
3396         /* Add another dot */
3397         bytes = 0;
3398         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
3399         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3400         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3401 #endif
3402
3403         bytes = 0;
3404         for (x=0;x<5;x++) {
3405                 snprintf(num, sizeof(num), "%d", x);
3406                 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3407         }
3408         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3409         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3410
3411 #ifdef DISPLAY
3412         /* Add another dot */
3413         bytes = 0;
3414         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
3415         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3416         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3417 #endif
3418
3419         if (ast_adsi_end_download(chan)) {
3420                 bytes = 0;
3421                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3422                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3423                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3424                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3425                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3426                 return 0;
3427         }
3428         bytes = 0;
3429         bytes += ast_adsi_download_disconnect(buf + bytes);
3430         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3431         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3432
3433         if (option_debug)
3434                 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3435
3436 #ifdef DISPLAY
3437         /* Add last dot */
3438         bytes = 0;
3439         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
3440         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3441 #endif
3442         if (option_debug)
3443                 ast_log(LOG_DEBUG, "Restarting session...\n");
3444
3445         bytes = 0;
3446         /* Load the session now */
3447         if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3448                 *useadsi = 1;
3449                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3450         } else
3451                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3452
3453         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3454         return 0;