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