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