8579f6da0ed9d7439bb8f85d85d7b16dc992ff2f
[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                 if (!strcasecmp(tmp->name, "password") || !strcasecmp(tmp->name, "secret")) {
682                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
683                 } else if (!strcasecmp(tmp->name, "secret")) {
684                         /* dont let secret override vmpassword */
685                         if (ast_strlen_zero(retval->password))
686                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));        
687                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
688                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
689                 } else if (!strcasecmp(tmp->name, "pager")) {
690                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
691                 } else if (!strcasecmp(tmp->name, "email")) {
692                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
693                 } else if (!strcasecmp(tmp->name, "fullname")) {
694                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
695                 } else if (!strcasecmp(tmp->name, "context")) {
696                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
697 #ifdef IMAP_STORAGE
698                 } else if (!strcasecmp(tmp->name, "imapuser")) {
699                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
700                 } else if (!strcasecmp(tmp->name, "imappassword")) {
701                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
702 #endif
703                 } else
704                         apply_option(retval, tmp->name, tmp->value);
705                 tmp = tmp->next;
706         } 
707 }
708
709 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
710 {
711         struct ast_variable *var;
712         struct ast_vm_user *retval;
713
714         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
715                 if (!ivm)
716                         ast_set_flag(retval, VM_ALLOCED);       
717                 else
718                         memset(retval, 0, sizeof(*retval));
719                 if (mailbox) 
720                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
721                 populate_defaults(retval);
722                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
723                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
724                 else
725                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
726                 if (var) {
727                         apply_options_full(retval, var);
728                         ast_variables_destroy(var);
729                 } else { 
730                         if (!ivm) 
731                                 free(retval);
732                         retval = NULL;
733                 }       
734         } 
735         return retval;
736 }
737
738 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
739 {
740         /* This function could be made to generate one from a database, too */
741         struct ast_vm_user *vmu=NULL, *cur;
742         AST_LIST_LOCK(&users);
743
744         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
745                 context = "default";
746
747         AST_LIST_TRAVERSE(&users, cur, list) {
748                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
749                         break;
750                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
751                         break;
752         }
753         if (cur) {
754                 /* Make a copy, so that on a reload, we have no race */
755                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
756                         memcpy(vmu, cur, sizeof(*vmu));
757                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
758                         AST_LIST_NEXT(vmu, list) = NULL;
759                 }
760         } else
761                 vmu = find_user_realtime(ivm, context, mailbox);
762         AST_LIST_UNLOCK(&users);
763         return vmu;
764 }
765
766 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
767 {
768         /* This function could be made to generate one from a database, too */
769         struct ast_vm_user *cur;
770         int res = -1;
771         AST_LIST_LOCK(&users);
772         AST_LIST_TRAVERSE(&users, cur, list) {
773                 if ((!context || !strcasecmp(context, cur->context)) &&
774                         (!strcasecmp(mailbox, cur->mailbox)))
775                                 break;
776         }
777         if (cur) {
778                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
779                 res = 0;
780         }
781         AST_LIST_UNLOCK(&users);
782         return res;
783 }
784
785 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
786 {
787         struct ast_config   *cfg=NULL;
788         struct ast_variable *var=NULL;
789         struct ast_category *cat=NULL;
790         char *category=NULL, *value=NULL, *new=NULL;
791         const char *tmp=NULL;
792         int len;
793                                         
794         if (!change_password_realtime(vmu, newpassword))
795                 return;
796
797         /* check voicemail.conf */
798         if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
799                 while ((category = ast_category_browse(cfg, category))) {
800                         if (!strcasecmp(category, vmu->context)) {
801                                 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
802                                 if (!tmp) {
803                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
804                                         break;
805                                 }
806                                 value = strstr(tmp,",");
807                                 if (!value) {
808                                         ast_log(LOG_WARNING, "variable has bad format.\n");
809                                         break;
810                                 }
811                                 len = (strlen(value) + strlen(newpassword));
812                                 
813                                 if (!(new = ast_calloc(1,len))) {
814                                         ast_log(LOG_WARNING, "Memory Allocation Failed.\n");
815                                         break;
816                                 }
817                                 sprintf(new,"%s%s", newpassword, value);
818         
819                                 if (!(cat = ast_category_get(cfg, category))) {
820                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
821                                         break;
822                                 }
823                                 ast_variable_update(cat, vmu->mailbox, new, NULL);
824                         }
825                 }
826                 /* save the results */
827                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
828                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
829                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
830                 if (new)
831                         free(new);
832         }
833         category = NULL;
834         var = NULL;
835         /* check users.conf and update the password stored for the mailbox*/
836         /* if no vmpassword entry exists create one. */
837         if ((cfg = ast_config_load_with_comments("users.conf"))) {
838                 if (option_debug > 3)
839                         ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
840                 while ((category = ast_category_browse(cfg, category))) {
841                         if (option_debug > 3)
842                                 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
843                         if (!strcasecmp(category, vmu->mailbox)) {
844                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmpassword"))) {
845                                         if (option_debug > 3)
846                                                 ast_log(LOG_DEBUG, "looks like we need to make vmpassword!\n");
847                                         var = ast_variable_new("vmpassword", newpassword);
848                                 } 
849                                 if (!(new = ast_calloc(1,strlen(newpassword)))) {
850                                         if (option_debug > 3)
851                                                 ast_log(LOG_DEBUG, "Memory Allocation Failed.\n");
852                                         break;
853                                 } 
854                                 sprintf(new, "%s", newpassword);
855                                 if (!(cat = ast_category_get(cfg, category))) {
856                                         if (option_debug > 3)
857                                                 ast_log(LOG_DEBUG, "failed to get category!\n");
858                                 }
859                                 if (!var)               
860                                         ast_variable_update(cat, "vmpassword", new, NULL);
861                                 else
862                                         ast_variable_append(cat, var);
863                         }
864                 }
865                 /* save the results and clean things up */
866                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
867                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
868                 config_text_file_save("users.conf", cfg, "AppVoicemail");
869                 if (new) 
870                         free(new);
871         }
872
873 }
874
875 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
876 {
877         char buf[255];
878         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
879         if (!ast_safe_system(buf))
880                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
881 }
882
883 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
884 {
885         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
886 }
887
888 #ifdef IMAP_STORAGE
889 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
890 {
891         if (mkdir(dir, 01777) && (errno != EEXIST)) {
892                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
893                 return sprintf(dest, "%s/msg%04d", dir, num);
894         }
895         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
896         return sprintf(dest, "%s/msg%04d", dir, num);
897 }
898
899 static void vm_imap_delete(int msgnum, struct vm_state *vms)
900 {
901         unsigned long messageNum = 0;
902         char arg[10];
903
904         /* find real message number based on msgnum */
905         /* this may be an index into vms->msgArray based on the msgnum. */
906
907         messageNum = vms->msgArray[msgnum];
908         if (messageNum == 0) {
909                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
910                 return;
911         }
912         if(option_debug > 2)
913                 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
914         /* delete message */
915         sprintf (arg,"%lu",messageNum);
916         mail_setflag (vms->mailstream,arg,"\\DELETED");
917 }
918
919 #endif
920 static int make_file(char *dest, int len, char *dir, int num)
921 {
922         return snprintf(dest, len, "%s/msg%04d", dir, num);
923 }
924
925 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
926  * \param dest    String. base directory.
927  * \param len     Length of dest.
928  * \param context String. Ignored if is null or empty string.
929  * \param ext     String. Ignored if is null or empty string.
930  * \param folder  String. Ignored if is null or empty string. 
931  * \return 0 on failure, 1 on success.
932  */
933 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
934 {
935         mode_t  mode = VOICEMAIL_DIR_MODE;
936
937         if (!ast_strlen_zero(context)) {
938                 make_dir(dest, len, context, "", "");
939                 if (mkdir(dest, mode) && errno != EEXIST) {
940                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
941                         return 0;
942                 }
943         }
944         if (!ast_strlen_zero(ext)) {
945                 make_dir(dest, len, context, ext, "");
946                 if (mkdir(dest, mode) && errno != EEXIST) {
947                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
948                         return 0;
949                 }
950         }
951         if (!ast_strlen_zero(folder)) {
952                 make_dir(dest, len, context, ext, folder);
953                 if (mkdir(dest, mode) && errno != EEXIST) {
954                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
955                         return 0;
956                 }
957         }
958         return 1;
959 }
960
961 /* only return failure if ast_lock_path returns 'timeout',
962    not if the path does not exist or any other reason
963 */
964 static int vm_lock_path(const char *path)
965 {
966         switch (ast_lock_path(path)) {
967         case AST_LOCK_TIMEOUT:
968                 return -1;
969         default:
970                 return 0;
971         }
972 }
973
974
975 #ifdef ODBC_STORAGE
976 static int retrieve_file(char *dir, int msgnum)
977 {
978         int x = 0;
979         int res;
980         int fd=-1;
981         size_t fdlen = 0;
982         void *fdm=NULL;
983         SQLSMALLINT colcount=0;
984         SQLHSTMT stmt;
985         char sql[PATH_MAX];
986         char fmt[80]="";
987         char *c;
988         char coltitle[256];
989         SQLSMALLINT collen;
990         SQLSMALLINT datatype;
991         SQLSMALLINT decimaldigits;
992         SQLSMALLINT nullable;
993         SQLULEN colsize;
994         SQLLEN colsize2;
995         FILE *f=NULL;
996         char rowdata[80];
997         char fn[PATH_MAX];
998         char full_fn[PATH_MAX];
999         char msgnums[80];
1000         
1001         struct odbc_obj *obj;
1002         obj = ast_odbc_request_obj(odbc_database, 0);
1003         if (obj) {
1004                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1005                 c = strchr(fmt, '|');
1006                 if (c)
1007                         *c = '\0';
1008                 if (!strcasecmp(fmt, "wav49"))
1009                         strcpy(fmt, "WAV");
1010                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1011                 if (msgnum > -1)
1012                         make_file(fn, sizeof(fn), dir, msgnum);
1013                 else
1014                         ast_copy_string(fn, dir, sizeof(fn));
1015                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1016                 
1017                 if (!(f = fopen(full_fn, "w+"))) {
1018                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1019                         goto yuck;
1020                 }
1021                 
1022                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1023                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1024                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1025                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1026                         ast_odbc_release_obj(obj);
1027                         goto yuck;
1028                 }
1029                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1030                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1031                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1032                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1033                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1034                         ast_odbc_release_obj(obj);
1035                         goto yuck;
1036                 }
1037                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1038                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1039                 res = ast_odbc_smart_execute(obj, stmt);
1040                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1041                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1042                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1043                         ast_odbc_release_obj(obj);
1044                         goto yuck;
1045                 }
1046                 res = SQLFetch(stmt);
1047                 if (res == SQL_NO_DATA) {
1048                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1049                         ast_odbc_release_obj(obj);
1050                         goto yuck;
1051                 }
1052                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1053                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1054                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055                         ast_odbc_release_obj(obj);
1056                         goto yuck;
1057                 }
1058                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1059                 if (fd < 0) {
1060                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1061                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1062                         ast_odbc_release_obj(obj);
1063                         goto yuck;
1064                 }
1065                 res = SQLNumResultCols(stmt, &colcount);
1066                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1067                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1068                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1069                         ast_odbc_release_obj(obj);
1070                         goto yuck;
1071                 }
1072                 if (f) 
1073                         fprintf(f, "[message]\n");
1074                 for (x=0;x<colcount;x++) {
1075                         rowdata[0] = '\0';
1076                         collen = sizeof(coltitle);
1077                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1078                                                 &datatype, &colsize, &decimaldigits, &nullable);
1079                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1080                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1081                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1082                                 ast_odbc_release_obj(obj);
1083                                 goto yuck;
1084                         }
1085                         if (!strcasecmp(coltitle, "recording")) {
1086                                 off_t offset;
1087                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1088                                 fdlen = colsize2;
1089                                 if (fd > -1) {
1090                                         char tmp[1]="";
1091                                         lseek(fd, fdlen - 1, SEEK_SET);
1092                                         if (write(fd, tmp, 1) != 1) {
1093                                                 close(fd);
1094                                                 fd = -1;
1095                                                 continue;
1096                                         }
1097                                         /* Read out in small chunks */
1098                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1099                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == (void *)-1) {
1100                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1101                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1102                                                         ast_odbc_release_obj(obj);
1103                                                         goto yuck;
1104                                                 } else {
1105                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1106                                                         munmap(fdm, CHUNKSIZE);
1107                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1108                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1109                                                                 unlink(full_fn);
1110                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1111                                                                 ast_odbc_release_obj(obj);
1112                                                                 goto yuck;
1113                                                         }
1114                                                 }
1115                                         }
1116                                         truncate(full_fn, fdlen);
1117                                 }
1118                         } else {
1119                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1120                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1121                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1122                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1123                                         ast_odbc_release_obj(obj);
1124                                         goto yuck;
1125                                 }
1126                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1127                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1128                         }
1129                 }
1130                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1131                 ast_odbc_release_obj(obj);
1132         } else
1133                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1134 yuck:   
1135         if (f)
1136                 fclose(f);
1137         if (fd > -1)
1138                 close(fd);
1139         return x - 1;
1140 }
1141
1142 static int remove_file(char *dir, int msgnum)
1143 {
1144         char fn[PATH_MAX];
1145         char full_fn[PATH_MAX];
1146         char msgnums[80];
1147         
1148         if (msgnum > -1) {
1149                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1150                 make_file(fn, sizeof(fn), dir, msgnum);
1151         } else
1152                 ast_copy_string(fn, dir, sizeof(fn));
1153         ast_filedelete(fn, NULL);       
1154         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1155         unlink(full_fn);
1156         return 0;
1157 }
1158
1159 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1160 {
1161         int x = 0;
1162         int res;
1163         SQLHSTMT stmt;
1164         char sql[PATH_MAX];
1165         char rowdata[20];
1166         
1167         struct odbc_obj *obj;
1168         obj = ast_odbc_request_obj(odbc_database, 0);
1169         if (obj) {
1170                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1171                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1172                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1173                         ast_odbc_release_obj(obj);
1174                         goto yuck;
1175                 }
1176                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1177                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1178                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1179                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1180                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1181                         ast_odbc_release_obj(obj);
1182                         goto yuck;
1183                 }
1184                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1185                 res = ast_odbc_smart_execute(obj, stmt);
1186                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1187                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1188                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1189                         ast_odbc_release_obj(obj);
1190                         goto yuck;
1191                 }
1192                 res = SQLFetch(stmt);
1193                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1194                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1195                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1196                         ast_odbc_release_obj(obj);
1197                         goto yuck;
1198                 }
1199                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1200                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1201                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1202                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1203                         ast_odbc_release_obj(obj);
1204                         goto yuck;
1205                 }
1206                 if (sscanf(rowdata, "%d", &x) != 1)
1207                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1208                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1209                 ast_odbc_release_obj(obj);
1210         } else
1211                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1212 yuck:   
1213         return x - 1;
1214 }
1215
1216 static int message_exists(char *dir, int msgnum)
1217 {
1218         int x = 0;
1219         int res;
1220         SQLHSTMT stmt;
1221         char sql[PATH_MAX];
1222         char rowdata[20];
1223         char msgnums[20];
1224         
1225         struct odbc_obj *obj;
1226         obj = ast_odbc_request_obj(odbc_database, 0);
1227         if (obj) {
1228                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1229                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1230                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1231                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1232                         ast_odbc_release_obj(obj);
1233                         goto yuck;
1234                 }
1235                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1236                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1237                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1238                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1239                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1240                         ast_odbc_release_obj(obj);
1241                         goto yuck;
1242                 }
1243                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1244                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1245                 res = ast_odbc_smart_execute(obj, stmt);
1246                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1247                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1248                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1249                         ast_odbc_release_obj(obj);
1250                         goto yuck;
1251                 }
1252                 res = SQLFetch(stmt);
1253                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1254                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1255                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1256                         ast_odbc_release_obj(obj);
1257                         goto yuck;
1258                 }
1259                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1260                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1261                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1262                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1263                         ast_odbc_release_obj(obj);
1264                         goto yuck;
1265                 }
1266                 if (sscanf(rowdata, "%d", &x) != 1)
1267                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1268                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1269                 ast_odbc_release_obj(obj);
1270         } else
1271                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1272 yuck:   
1273         return x;
1274 }
1275
1276 static int count_messages(struct ast_vm_user *vmu, char *dir)
1277 {
1278         return last_message_index(vmu, dir) + 1;
1279 }
1280
1281 static void delete_file(char *sdir, int smsg)
1282 {
1283         int res;
1284         SQLHSTMT stmt;
1285         char sql[PATH_MAX];
1286         char msgnums[20];
1287         
1288         struct odbc_obj *obj;
1289         obj = ast_odbc_request_obj(odbc_database, 0);
1290         if (obj) {
1291                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1292                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1293                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1294                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1295                         ast_odbc_release_obj(obj);
1296                         goto yuck;
1297                 }
1298                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1299                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1300                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1302                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303                         ast_odbc_release_obj(obj);
1304                         goto yuck;
1305                 }
1306                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1307                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1308                 res = ast_odbc_smart_execute(obj, stmt);
1309                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1310                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1311                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1312                         ast_odbc_release_obj(obj);
1313                         goto yuck;
1314                 }
1315                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1316                 ast_odbc_release_obj(obj);
1317         } else
1318                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1319 yuck:
1320         return; 
1321 }
1322
1323 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1324 {
1325         int res;
1326         SQLHSTMT stmt;
1327         char sql[512];
1328         char msgnums[20];
1329         char msgnumd[20];
1330         struct odbc_obj *obj;
1331
1332         delete_file(ddir, dmsg);
1333         obj = ast_odbc_request_obj(odbc_database, 0);
1334         if (obj) {
1335                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1336                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1337                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1338                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1339                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1340                         ast_odbc_release_obj(obj);
1341                         goto yuck;
1342                 }
1343                 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); 
1344                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1345                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1346                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1347                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1348                         ast_odbc_release_obj(obj);
1349                         goto yuck;
1350                 }
1351                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1352                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1353                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1354                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1355                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1356                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1357                 res = ast_odbc_smart_execute(obj, stmt);
1358                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1359                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1360                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1361                         ast_odbc_release_obj(obj);
1362                         goto yuck;
1363                 }
1364                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1365                 ast_odbc_release_obj(obj);
1366         } else
1367                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1368 yuck:
1369         return; 
1370 }
1371
1372 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1373 {
1374         int x = 0;
1375         int res;
1376         int fd = -1;
1377         void *fdm=NULL;
1378         size_t fdlen = -1;
1379         SQLHSTMT stmt;
1380         SQLINTEGER len;
1381         char sql[PATH_MAX];
1382         char msgnums[20];
1383         char fn[PATH_MAX];
1384         char full_fn[PATH_MAX];
1385         char fmt[80]="";
1386         char *c;
1387         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1388         const char *category = "";
1389         struct ast_config *cfg=NULL;
1390         struct odbc_obj *obj;
1391
1392         delete_file(dir, msgnum);
1393         obj = ast_odbc_request_obj(odbc_database, 0);
1394         if (obj) {
1395                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1396                 c = strchr(fmt, '|');
1397                 if (c)
1398                         *c = '\0';
1399                 if (!strcasecmp(fmt, "wav49"))
1400                         strcpy(fmt, "WAV");
1401                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1402                 if (msgnum > -1)
1403                         make_file(fn, sizeof(fn), dir, msgnum);
1404                 else
1405                         ast_copy_string(fn, dir, sizeof(fn));
1406                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1407                 cfg = ast_config_load(full_fn);
1408                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1409                 fd = open(full_fn, O_RDWR);
1410                 if (fd < 0) {
1411                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1412                         ast_odbc_release_obj(obj);
1413                         goto yuck;
1414                 }
1415                 if (cfg) {
1416                         context = ast_variable_retrieve(cfg, "message", "context");
1417                         if (!context) context = "";
1418                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1419                         if (!macrocontext) macrocontext = "";
1420                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1421                         if (!callerid) callerid = "";
1422                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1423                         if (!origtime) origtime = "";
1424                         duration = ast_variable_retrieve(cfg, "message", "duration");
1425                         if (!duration) duration = "";
1426                         category = ast_variable_retrieve(cfg, "message", "category");
1427                         if (!category) category = "";
1428                 }
1429                 fdlen = lseek(fd, 0, SEEK_END);
1430                 lseek(fd, 0, SEEK_SET);
1431                 printf("Length is %zd\n", fdlen);
1432                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1433                 if (!fdm) {
1434                         ast_log(LOG_WARNING, "Memory map failed!\n");
1435                         ast_odbc_release_obj(obj);
1436                         goto yuck;
1437                 } 
1438                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1439                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1440                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1441                         ast_odbc_release_obj(obj);
1442                         goto yuck;
1443                 }
1444                 if (!ast_strlen_zero(category)) 
1445                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1446                 else
1447                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1448                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1449                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1450                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1451                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1452                         ast_odbc_release_obj(obj);
1453                         goto yuck;
1454                 }
1455                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1456                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1457                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1458                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1459                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1460                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1461                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1462                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1463                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1464                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1465                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1466                 if (!ast_strlen_zero(category))
1467                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1468                 res = ast_odbc_smart_execute(obj, stmt);
1469                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1470                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1471                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1472                         ast_odbc_release_obj(obj);
1473                         goto yuck;
1474                 }
1475                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1476                 ast_odbc_release_obj(obj);
1477         } else
1478                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1479 yuck:   
1480         if (cfg)
1481                 ast_config_destroy(cfg);
1482         if (fdm)
1483                 munmap(fdm, fdlen);
1484         if (fd > -1)
1485                 close(fd);
1486         return x;
1487 }
1488
1489 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1490 {
1491         int res;
1492         SQLHSTMT stmt;
1493         char sql[PATH_MAX];
1494         char msgnums[20];
1495         char msgnumd[20];
1496         struct odbc_obj *obj;
1497
1498         delete_file(ddir, dmsg);
1499         obj = ast_odbc_request_obj(odbc_database, 0);
1500         if (obj) {
1501                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1502                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1503                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1504                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1505                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1506                         ast_odbc_release_obj(obj);
1507                         goto yuck;
1508                 }
1509                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1510                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1511                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1512                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1513                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1514                         ast_odbc_release_obj(obj);
1515                         goto yuck;
1516                 }
1517                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1518                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1519                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1520                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1521                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1522                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1523                 res = ast_odbc_smart_execute(obj, stmt);
1524                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1525                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1526                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1527                         ast_odbc_release_obj(obj);
1528                         goto yuck;
1529                 }
1530                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1531                 ast_odbc_release_obj(obj);
1532         } else
1533                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1534 yuck:
1535         return; 
1536 }
1537
1538 #else
1539 #ifndef IMAP_STORAGE
1540 static int count_messages(struct ast_vm_user *vmu, char *dir)
1541 {
1542         /* Find all .txt files - even if they are not in sequence from 0000 */
1543
1544         int vmcount = 0;
1545         DIR *vmdir = NULL;
1546         struct dirent *vment = NULL;
1547
1548         if (vm_lock_path(dir))
1549                 return ERROR_LOCK_PATH;
1550
1551         if ((vmdir = opendir(dir))) {
1552                 while ((vment = readdir(vmdir))) {
1553                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1554                                 vmcount++;
1555                 }
1556                 closedir(vmdir);
1557         }
1558         ast_unlock_path(dir);
1559         
1560         return vmcount;
1561 }
1562
1563 static void rename_file(char *sfn, char *dfn)
1564 {
1565         char stxt[PATH_MAX];
1566         char dtxt[PATH_MAX];
1567         ast_filerename(sfn,dfn,NULL);
1568         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1569         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1570         rename(stxt, dtxt);
1571 }
1572
1573 static int copy(char *infile, char *outfile)
1574 {
1575         int ifd;
1576         int ofd;
1577         int res;
1578         int len;
1579         char buf[4096];
1580
1581 #ifdef HARDLINK_WHEN_POSSIBLE
1582         /* Hard link if possible; saves disk space & is faster */
1583         if (link(infile, outfile)) {
1584 #endif
1585                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1586                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1587                         return -1;
1588                 }
1589                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1590                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1591                         close(ifd);
1592                         return -1;
1593                 }
1594                 do {
1595                         len = read(ifd, buf, sizeof(buf));
1596                         if (len < 0) {
1597                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1598                                 close(ifd);
1599                                 close(ofd);
1600                                 unlink(outfile);
1601                         }
1602                         if (len) {
1603                                 res = write(ofd, buf, len);
1604                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1605                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1606                                         close(ifd);
1607                                         close(ofd);
1608                                         unlink(outfile);
1609                                 }
1610                         }
1611                 } while (len);
1612                 close(ifd);
1613                 close(ofd);
1614                 return 0;
1615 #ifdef HARDLINK_WHEN_POSSIBLE
1616         } else {
1617                 /* Hard link succeeded */
1618                 return 0;
1619         }
1620 #endif
1621 }
1622
1623 static void copy_file(char *frompath, char *topath)
1624 {
1625         char frompath2[PATH_MAX], topath2[PATH_MAX];
1626         ast_filecopy(frompath, topath, NULL);
1627         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1628         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1629         copy(frompath2, topath2);
1630 }
1631 #endif
1632 /*
1633  * A negative return value indicates an error.
1634  * \note Should always be called with a lock already set on dir.
1635  */
1636 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1637 {
1638         int x;
1639         unsigned char map[MAXMSGLIMIT] = "";
1640         DIR *msgdir;
1641         struct dirent *msgdirent;
1642         int msgdirint;
1643
1644         /* Reading the entire directory into a file map scales better than
1645          * doing a stat repeatedly on a predicted sequence.  I suspect this
1646          * is partially due to stat(2) internally doing a readdir(2) itself to
1647          * find each file. */
1648         msgdir = opendir(dir);
1649         while ((msgdirent = readdir(msgdir))) {
1650                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1651                         map[msgdirint] = 1;
1652         }
1653         closedir(msgdir);
1654
1655         for (x = 0; x < vmu->maxmsg; x++) {
1656                 if (map[x] == 0)
1657                         break;
1658         }
1659
1660         return x - 1;
1661 }
1662
1663 static int vm_delete(char *file)
1664 {
1665         char *txt;
1666         int txtsize = 0;
1667
1668         txtsize = (strlen(file) + 5)*sizeof(char);
1669         txt = alloca(txtsize);
1670         /* Sprintf here would safe because we alloca'd exactly the right length,
1671          * but trying to eliminate all sprintf's anyhow
1672          */
1673         snprintf(txt, txtsize, "%s.txt", file);
1674         unlink(txt);
1675         return ast_filedelete(file, NULL);
1676 }
1677
1678
1679 #endif
1680 static int inbuf(struct baseio *bio, FILE *fi)
1681 {
1682         int l;
1683
1684         if (bio->ateof)
1685                 return 0;
1686
1687         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1688                 if (ferror(fi))
1689                         return -1;
1690
1691                 bio->ateof = 1;
1692                 return 0;
1693         }
1694
1695         bio->iolen= l;
1696         bio->iocp= 0;
1697
1698         return 1;
1699 }
1700
1701 static int inchar(struct baseio *bio, FILE *fi)
1702 {
1703         if (bio->iocp>=bio->iolen) {
1704                 if (!inbuf(bio, fi))
1705                         return EOF;
1706         }
1707
1708         return bio->iobuf[bio->iocp++];
1709 }
1710
1711 static int ochar(struct baseio *bio, int c, FILE *so)
1712 {
1713         if (bio->linelength>=BASELINELEN) {
1714                 if (fputs(eol,so)==EOF)
1715                         return -1;
1716
1717                 bio->linelength= 0;
1718         }
1719
1720         if (putc(((unsigned char)c),so)==EOF)
1721                 return -1;
1722
1723         bio->linelength++;
1724
1725         return 1;
1726 }
1727
1728 static int base_encode(char *filename, FILE *so)
1729 {
1730         unsigned char dtable[BASEMAXINLINE];
1731         int i,hiteof= 0;
1732         FILE *fi;
1733         struct baseio bio;
1734
1735         memset(&bio, 0, sizeof(bio));
1736         bio.iocp = BASEMAXINLINE;
1737
1738         if (!(fi = fopen(filename, "rb"))) {
1739                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1740                 return -1;
1741         }
1742
1743         for (i= 0;i<9;i++) {
1744                 dtable[i]= 'A'+i;
1745                 dtable[i+9]= 'J'+i;
1746                 dtable[26+i]= 'a'+i;
1747                 dtable[26+i+9]= 'j'+i;
1748         }
1749         for (i= 0;i<8;i++) {
1750                 dtable[i+18]= 'S'+i;
1751                 dtable[26+i+18]= 's'+i;
1752         }
1753         for (i= 0;i<10;i++) {
1754                 dtable[52+i]= '0'+i;
1755         }
1756         dtable[62]= '+';
1757         dtable[63]= '/';
1758
1759         while (!hiteof){
1760                 unsigned char igroup[3],ogroup[4];
1761                 int c,n;
1762
1763                 igroup[0]= igroup[1]= igroup[2]= 0;
1764
1765                 for (n= 0;n<3;n++) {
1766                         if ((c = inchar(&bio, fi)) == EOF) {
1767                                 hiteof= 1;
1768                                 break;
1769                         }
1770
1771                         igroup[n]= (unsigned char)c;
1772                 }
1773
1774                 if (n> 0) {
1775                         ogroup[0]= dtable[igroup[0]>>2];
1776                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1777                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1778                         ogroup[3]= dtable[igroup[2]&0x3F];
1779
1780                         if (n<3) {
1781                                 ogroup[3]= '=';
1782
1783                                 if (n<2)
1784                                         ogroup[2]= '=';
1785                         }
1786
1787                         for (i= 0;i<4;i++)
1788                                 ochar(&bio, ogroup[i], so);
1789                 }
1790         }
1791
1792         if (fputs(eol,so)==EOF)
1793                 return 0;
1794
1795         fclose(fi);
1796
1797         return 1;
1798 }
1799
1800 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)
1801 {
1802         char callerid[256];
1803         /* Prepare variables for substitution in email body and subject */
1804         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1805         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1806         snprintf(passdata, passdatasize, "%d", msgnum);
1807         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1808         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1809         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1810         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1811         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1812         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1813         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1814         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1815 }
1816
1817 static char *quote(const char *from, char *to, size_t len)
1818 {
1819         char *ptr = to;
1820         *ptr++ = '"';
1821         for (; ptr < to + len - 1; from++) {
1822                 if (*from == '"')
1823                         *ptr++ = '\\';
1824                 else if (*from == '\0')
1825                         break;
1826                 *ptr++ = *from;
1827         }
1828         if (ptr < to + len - 1)
1829                 *ptr++ = '"';
1830         *ptr = '\0';
1831         return to;
1832 }
1833 /*
1834  * fill in *tm for current time according to the proper timezone, if any.
1835  * Return tm so it can be used as a function argument.
1836  */
1837 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1838 {
1839         const struct vm_zone *z = NULL;
1840         time_t t = time(NULL);
1841
1842         /* Does this user have a timezone specified? */
1843         if (!ast_strlen_zero(vmu->zonetag)) {
1844                 /* Find the zone in the list */
1845                 AST_LIST_LOCK(&zones);
1846                 AST_LIST_TRAVERSE(&zones, z, list) {
1847                         if (!strcmp(z->name, vmu->zonetag))
1848                                 break;
1849                 }
1850                 AST_LIST_UNLOCK(&zones);
1851         }
1852         ast_localtime(&t, tm, z ? z->timezone : NULL);
1853         return tm;
1854 }
1855
1856 /* same as mkstemp, but return a FILE * */
1857 static FILE *vm_mkftemp(char *template)
1858 {
1859         FILE *p = NULL;
1860         int pfd = mkstemp(template);
1861         if (pfd > -1) {
1862                 p = fdopen(pfd, "w+");
1863                 if (!p) {
1864                         close(pfd);
1865                         pfd = -1;
1866                 }
1867         }
1868         return p;
1869 }
1870
1871 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)
1872 {
1873         char date[256];
1874         char host[MAXHOSTNAMELEN] = "";
1875         char who[256];
1876         char bound[256];
1877         char fname[256];
1878         char dur[256];
1879         char tmpcmd[256];
1880         struct tm tm;
1881         char *passdata2;
1882         size_t len_passdata;
1883
1884         gethostname(host, sizeof(host)-1);
1885         if (strchr(srcemail, '@'))
1886                 ast_copy_string(who, srcemail, sizeof(who));
1887         else {
1888                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1889         }
1890         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1891         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1892         fprintf(p, "Date: %s\r\n", date);
1893
1894         /* Set date format for voicemail mail */
1895         strftime(date, sizeof(date), emaildateformat, &tm);
1896
1897         if (*fromstring) {
1898                 struct ast_channel *ast;
1899                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1900                         char *passdata;
1901                         int vmlen = strlen(fromstring)*3 + 200;
1902                         if ((passdata = alloca(vmlen))) {
1903                                 memset(passdata, 0, vmlen);
1904                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1905                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1906                                 len_passdata = strlen(passdata) * 2 + 1;
1907                                 passdata2 = alloca(len_passdata);
1908                                 fprintf(p, "From: %s <%s>\r\n", quote(passdata, passdata2, len_passdata), who);
1909                         } else
1910                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1911                         ast_channel_free(ast);
1912                 } else
1913                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1914         } else
1915                 fprintf(p, "From: Asterisk PBX <%s>\r\n", who);
1916         len_passdata = strlen(vmu->fullname) * 2 + 1;
1917         passdata2 = alloca(len_passdata);
1918         fprintf(p, "To: %s <%s>\r\n", quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1919         if (emailsubject) {
1920                 struct ast_channel *ast;
1921                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1922                         char *passdata;
1923                         int vmlen = strlen(emailsubject)*3 + 200;
1924                         if ((passdata = alloca(vmlen))) {
1925                                 memset(passdata, 0, vmlen);
1926                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1927                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1928                                 fprintf(p, "Subject: %s\r\n", passdata);
1929                         } else
1930                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1931                         ast_channel_free(ast);
1932                 } else
1933                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1934         } else  if (*emailtitle) {
1935                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1936                 fprintf(p,"\r\n") ;
1937         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1938                 fprintf(p, "Subject: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1939         else
1940                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1941         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1942         if(imap) {
1943                 /* additional information needed for IMAP searching */
1944                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum + 1);
1945                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1946                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring);
1947                 fprintf(p, "X-Asterisk-VM-Context: %s\r\n", context);
1948                 fprintf(p, "X-Asterisk-VM-Extension: %s\r\n", mailbox);
1949                 fprintf(p, "X-Asterisk-VM-Priority: %d\r\n", chan->priority);
1950                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\r\n", chan->name);
1951                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum);
1952                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname);
1953                 fprintf(p, "X-Asterisk-VM-Duration: %d\r\n", duration);
1954                 if (!ast_strlen_zero(category))
1955                         fprintf(p, "X-Asterisk-VM-Category: %s\r\n", category);
1956                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\r\n", date);
1957                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL));
1958         }
1959         if (!ast_strlen_zero(cidnum))
1960                 fprintf(p, "X-Asterisk-CallerID: %s\r\n", cidnum);
1961         if (!ast_strlen_zero(cidname))
1962                 fprintf(p, "X-Asterisk-CallerIDName: %s\r\n", cidname);
1963         fprintf(p, "MIME-Version: 1.0\r\n");
1964         if (attach_user_voicemail) {
1965                 /* Something unique. */
1966                 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1967
1968                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound);
1969
1970                 fprintf(p, "--%s\r\n", bound);
1971         }
1972         fprintf(p, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset);
1973         if (emailbody) {
1974                 struct ast_channel *ast;
1975                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1976                         char *passdata;
1977                         int vmlen = strlen(emailbody)*3 + 200;
1978                         if ((passdata = alloca(vmlen))) {
1979                                 memset(passdata, 0, vmlen);
1980                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1981                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1982                                 fprintf(p, "%s\r\n", passdata);
1983                         } else
1984                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1985                         ast_channel_free(ast);
1986                 } else
1987                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1988         } else {
1989                 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"
1990
1991                 "in mailbox %s from %s, on %s so you might\r\n"
1992                 "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, 
1993                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1994         }
1995         if (attach_user_voicemail) {
1996                 /* Eww. We want formats to tell us their own MIME type */
1997                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1998                 char tmpdir[256], newtmp[256];
1999                 int tmpfd;
2000         
2001                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2002                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2003                 tmpfd = mkstemp(newtmp);
2004                 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2005                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2006                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2007                         ast_safe_system(tmpcmd);
2008                         attach = newtmp;
2009                         ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2010                 }
2011                 fprintf(p, "--%s\r\n", bound);
2012                 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype, format, msgnum, format);
2013                 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2014                 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2015                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum, format);
2016                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2017                 base_encode(fname, p);
2018                 /* only attach if necessary */
2019                 if (imap && strcmp(format, "gsm")) {
2020                         fprintf(p, "--%s\r\n", bound);
2021                         fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum, format);
2022                         fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2023                         fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2024                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum);
2025                         snprintf(fname, sizeof(fname), "%s.gsm", attach);
2026                         base_encode(fname, p);
2027                 }
2028                 fprintf(p, "\r\n\r\n--%s--\r\n.\r\n", bound);
2029                 if (tmpfd > -1)
2030                         close(tmpfd);
2031                 unlink(newtmp);
2032         }
2033 }
2034 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)
2035 {
2036         FILE *p=NULL;
2037         char tmp[80] = "/tmp/astmail-XXXXXX";
2038         char tmp2[256];
2039
2040         if (vmu && ast_strlen_zero(vmu->email)) {
2041                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2042                 return(0);
2043         }
2044         if (!strcmp(format, "wav49"))
2045                 format = "WAV";
2046         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));
2047         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2048            command hangs */
2049         if ((p = vm_mkftemp(tmp)) == NULL) {
2050                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2051                 return -1;
2052         } else {
2053                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2054                 fclose(p);
2055                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2056                 ast_safe_system(tmp2);
2057                 if (option_debug)
2058                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2059         }
2060         return 0;
2061 }
2062
2063 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)
2064 {
2065         char date[256];
2066         char host[MAXHOSTNAMELEN] = "";
2067         char who[256];
2068         char dur[PATH_MAX];
2069         char tmp[80] = "/tmp/astmail-XXXXXX";
2070         char tmp2[PATH_MAX];
2071         struct tm tm;
2072         FILE *p;
2073
2074         if ((p = vm_mkftemp(tmp)) == NULL) {
2075                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2076                 return -1;
2077         } else {
2078                 gethostname(host, sizeof(host)-1);
2079                 if (strchr(srcemail, '@'))
2080                         ast_copy_string(who, srcemail, sizeof(who));
2081                 else {
2082                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2083                 }
2084                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2085                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2086                 fprintf(p, "Date: %s\n", date);
2087
2088                 if (*pagerfromstring) {
2089                         struct ast_channel *ast;
2090                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2091                                 char *passdata;
2092                                 int vmlen = strlen(fromstring)*3 + 200;
2093                                 if ((passdata = alloca(vmlen))) {
2094                                         memset(passdata, 0, vmlen);
2095                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2096                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2097                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2098                                 } else 
2099                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2100                                 ast_channel_free(ast);
2101                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2102                 } else
2103                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2104                 fprintf(p, "To: %s\n", pager);
2105                 if (pagersubject) {
2106                         struct ast_channel *ast;
2107                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2108                                 char *passdata;
2109                                 int vmlen = strlen(pagersubject) * 3 + 200;
2110                                 if ((passdata = alloca(vmlen))) {
2111                                         memset(passdata, 0, vmlen);
2112                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2113                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2114                                         fprintf(p, "Subject: %s\n\n", passdata);
2115                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2116                                 ast_channel_free(ast);
2117                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2118                 } else
2119                         fprintf(p, "Subject: New VM\n\n");
2120                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2121                 if (pagerbody) {
2122                         struct ast_channel *ast;
2123                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2124                                 char *passdata;
2125                                 int vmlen = strlen(pagerbody)*3 + 200;
2126                                 if ((passdata = alloca(vmlen))) {
2127                                         memset(passdata, 0, vmlen);
2128                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2129                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2130                                         fprintf(p, "%s\n", passdata);
2131                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2132                         ast_channel_free(ast);
2133                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2134                 } else {
2135                         fprintf(p, "New %s long msg in box %s\n"
2136                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2137                 }
2138                 fclose(p);
2139                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2140                 ast_safe_system(tmp2);
2141                 if (option_debug)
2142                         ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2143         }
2144         return 0;
2145 }
2146
2147 static int get_date(char *s, int len)
2148 {
2149         struct tm tm;
2150         time_t t;
2151         t = time(0);
2152         localtime_r(&t,&tm);
2153         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2154 }
2155
2156 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2157 {
2158         int res;
2159         char fn[PATH_MAX];
2160         char dest[PATH_MAX];
2161
2162         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2163
2164         if (!(res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2165                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2166                 return -1;
2167         }
2168
2169         RETRIEVE(fn, -1);
2170         if (ast_fileexists(fn, NULL, NULL) > 0) {
2171                 res = ast_stream_and_wait(chan, fn, ecodes);
2172                 if (res) {
2173                         DISPOSE(fn, -1);
2174                         return res;
2175                 }
2176         } else {
2177                 /* Dispose just in case */
2178                 DISPOSE(fn, -1);
2179                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2180                 if (res)
2181                         return res;
2182                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2183                 if (res)
2184                         return res;
2185         }
2186         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2187         return res;
2188 }
2189
2190 static void free_user(struct ast_vm_user *vmu)
2191 {
2192         if (ast_test_flag(vmu, VM_ALLOCED))
2193                 free(vmu);
2194 }
2195
2196 static void free_zone(struct vm_zone *z)
2197 {
2198         free(z);
2199 }
2200
2201 static const char *mbox(int id)
2202 {
2203         static const char *msgs[] = {
2204                 "INBOX",
2205                 "Old",
2206                 "Work",
2207                 "Family",
2208                 "Friends",
2209                 "Cust1",
2210                 "Cust2",
2211                 "Cust3",
2212                 "Cust4",
2213                 "Cust5",
2214         };
2215         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2216 }
2217
2218 #ifdef ODBC_STORAGE
2219 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2220 {
2221         int x = -1;
2222         int res;
2223         SQLHSTMT stmt;
2224         char sql[PATH_MAX];
2225         char rowdata[20];
2226         char tmp[PATH_MAX] = "";
2227         struct odbc_obj *obj;
2228         char *context;
2229
2230         if (newmsgs)
2231                 *newmsgs = 0;
2232         if (oldmsgs)
2233                 *oldmsgs = 0;
2234
2235         /* If no mailbox, return immediately */
2236         if (ast_strlen_zero(mailbox))
2237                 return 0;
2238
2239         ast_copy_string(tmp, mailbox, sizeof(tmp));
2240         
2241         context = strchr(tmp, '@');
2242         if (context) {
2243                 *context = '\0';
2244                 context++;
2245         } else
2246                 context = "default";
2247         
2248         obj = ast_odbc_request_obj(odbc_database, 0);
2249         if (obj) {
2250                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2251                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2252                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2253                         ast_odbc_release_obj(obj);
2254                         goto yuck;
2255                 }
2256                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2257                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2258                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2259                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2260                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2261                         ast_odbc_release_obj(obj);
2262                         goto yuck;
2263                 }
2264                 res = ast_odbc_smart_execute(obj, stmt);
2265                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2266                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2267                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2268                         ast_odbc_release_obj(obj);
2269                         goto yuck;
2270                 }
2271                 res = SQLFetch(stmt);
2272                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2273                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2274                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2275                         ast_odbc_release_obj(obj);
2276                         goto yuck;
2277                 }
2278                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2279                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2280                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2281                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2282                         ast_odbc_release_obj(obj);
2283                         goto yuck;
2284                 }
2285                 *newmsgs = atoi(rowdata);
2286                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2287
2288                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2289                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2290                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2291                         ast_odbc_release_obj(obj);
2292                         goto yuck;
2293                 }
2294                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2295                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2296                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2297                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2298                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2299                         ast_odbc_release_obj(obj);
2300                         goto yuck;
2301                 }
2302                 res = ast_odbc_smart_execute(obj, stmt);
2303                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2304                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2305                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2306                         ast_odbc_release_obj(obj);
2307                         goto yuck;
2308                 }
2309                 res = SQLFetch(stmt);
2310                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2311                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2312                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2313                         ast_odbc_release_obj(obj);
2314                         goto yuck;
2315                 }
2316                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2317                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2318                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2319                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2320                         ast_odbc_release_obj(obj);
2321                         goto yuck;
2322                 }
2323                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2324                 ast_odbc_release_obj(obj);
2325                 *oldmsgs = atoi(rowdata);
2326                 x = 0;
2327         } else
2328                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2329                 
2330 yuck:   
2331         return x;
2332 }
2333
2334 static int messagecount(const char *context, const char *mailbox, const char *folder)
2335 {
2336         struct odbc_obj *obj = NULL;
2337         int nummsgs = 0;
2338         int res;
2339         SQLHSTMT stmt = NULL;
2340         char sql[PATH_MAX];
2341         char rowdata[20];
2342         if (!folder)
2343                 folder = "INBOX";
2344         /* If no mailbox, return immediately */
2345         if (ast_strlen_zero(mailbox))
2346                 return 0;
2347
2348         obj = ast_odbc_request_obj(odbc_database, 0);
2349         if (obj) {
2350                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2351                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2352                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2353                         goto yuck;
2354                 }
2355                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2356                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2357                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2358                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2359                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2360                         goto yuck;
2361                 }
2362                 res = ast_odbc_smart_execute(obj, stmt);
2363                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2364                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2365                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2366                         goto yuck;
2367                 }
2368                 res = SQLFetch(stmt);
2369                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2370                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2371                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2372                         goto yuck;
2373                 }
2374                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2375                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2376                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2377                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2378                         goto yuck;
2379                 }
2380                 nummsgs = atoi(rowdata);
2381                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2382         } else
2383                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2384
2385 yuck:
2386         if (obj)
2387                 ast_odbc_release_obj(obj);
2388         return nummsgs;
2389 }
2390
2391 static int has_voicemail(const char *mailbox, const char *folder)
2392 {
2393         char *context, tmp[256];
2394         ast_copy_string(tmp, mailbox, sizeof(tmp));
2395         if ((context = strchr(tmp, '@')))
2396                 *context++ = '\0';
2397         else
2398                 context = "default";
2399
2400         if (messagecount(context, tmp, folder))
2401                 return 1;
2402         else
2403                 return 0;
2404 }
2405
2406 #elif defined(IMAP_STORAGE)
2407
2408 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)
2409 {
2410         char *myserveremail = serveremail;
2411         char fn[PATH_MAX];
2412         char mailbox[256];
2413         char *stringp;
2414         FILE *p=NULL;
2415         char tmp[80] = "/tmp/astmail-XXXXXX";
2416         long len;
2417         void *buf;
2418         STRING str;
2419         
2420         /* Attach only the first format */
2421         fmt = ast_strdupa(fmt);
2422         stringp = fmt;
2423         strsep(&stringp, "|");
2424
2425         if (!ast_strlen_zero(vmu->serveremail))
2426                 myserveremail = vmu->serveremail;
2427
2428         make_file(fn, sizeof(fn), dir, msgnum);
2429
2430         if (ast_strlen_zero(vmu->email))
2431                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2432
2433         if (!strcmp(fmt, "wav49"))
2434                 fmt = "WAV";
2435         if(option_debug > 2)
2436                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2437         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2438            command hangs */
2439         if ((p = vm_mkftemp(tmp)) == NULL) {
2440                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2441                 return -1;
2442         } else {
2443                 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);
2444                 /* read mail file to memory */          
2445                 len = ftell(p);
2446                 rewind(p);
2447                 if((buf = ast_malloc(len+1)) == NIL) {
2448                         ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2449                         return -1;
2450                 }
2451                 fread(buf, len, 1, p);
2452                 ((char *)buf)[len] = '\0';
2453                 INIT(&str, mail_string, buf, len);
2454                 imap_mailbox_name(mailbox, vms, 0, 1);
2455                 if(!mail_append(vms->mailstream, mailbox, &str))
2456                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2457                 fclose(p);
2458                 unlink(tmp);
2459                 ast_free(buf);
2460                 if(option_debug > 2)
2461                         ast_log(LOG_DEBUG, "%s stored\n", fn);
2462         }
2463         return 0;
2464
2465 }
2466
2467 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2468 {
2469         SEARCHPGM *pgm;
2470         SEARCHHEADER *hdr;
2471  
2472         struct ast_vm_user *vmu;
2473         struct vm_state *vms_p;
2474         char tmp[256] = "";
2475         char *mb, *cur;
2476         char *mailboxnc; 
2477         char *context;
2478         int ret = 0;
2479         if (newmsgs)
2480                 *newmsgs = 0;
2481         if (oldmsgs)
2482                 *oldmsgs = 0;
2483  
2484         if(option_debug > 2)
2485                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2486         /* If no mailbox, return immediately */
2487         if (ast_strlen_zero(mailbox))
2488                 return 0;
2489         if (strchr(mailbox, ',')) {
2490                 int tmpnew, tmpold;
2491                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2492                 mb = tmp;
2493                 ret = 0;
2494                 while((cur = strsep(&mb, ", "))) {
2495                         if (!ast_strlen_zero(cur)) {
2496                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2497                                         return -1;
2498                                 else {
2499                                         if (newmsgs)
2500                                                 *newmsgs += tmpnew; 
2501                                         if (oldmsgs)
2502                                                 *oldmsgs += tmpold;
2503                                 }
2504                         }
2505                 }
2506                 return 0;
2507         }
2508         ast_copy_string(tmp, mailbox, sizeof(tmp));
2509         context = strchr(tmp, '@');
2510         if (context) {
2511                 *context = '\0';
2512                 mailboxnc = tmp;
2513                 context++;
2514         } else {
2515                 context = "default";
2516                 mailboxnc = (char *)mailbox;
2517         }
2518  
2519         /* We have to get the user before we can open the stream! */
2520         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2521         vmu = find_user(NULL, context, mailboxnc);
2522         if (!vmu) {
2523                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2524                 return -1;
2525         } else {
2526                 /* No IMAP account available */
2527                 if (vmu->imapuser[0] == '\0') {
2528                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2529                         return -1;
2530                 }
2531         }
2532  
2533         /* check if someone is accessing this box right now... */
2534         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2535         if (!vms_p) {
2536                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2537         }
2538         if (vms_p) {
2539                 if(option_debug > 2)
2540                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2541                 *newmsgs = vms_p->newmessages;
2542                 *oldmsgs = vms_p->oldmessages;
2543                 return 0;
2544         }
2545  
2546         /* add one if not there... */
2547         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2548         if (!vms_p) {
2549                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2550         }
2551  
2552         if (!vms_p) {
2553                 if(option_debug > 2)
2554                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2555                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2556                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2557                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2558                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2559                 if(option_debug > 2)
2560                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2561                 vms_p->updated = 1;
2562                 vms_p->interactive = 0;
2563                 /* set mailbox to INBOX! */
2564                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2565                 init_vm_state(vms_p);
2566                 vmstate_insert(vms_p);
2567         }
2568         if (!vms_p->mailstream)
2569                 ret = init_mailstream(vms_p, 0);
2570         if (!vms_p->mailstream) {
2571                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2572                 return -1;
2573         }
2574         if (newmsgs && ret==0 && vms_p->updated==1 ) {
2575                 pgm = mail_newsearchpgm ();
2576                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2577                 pgm->header = hdr;
2578                 pgm->unseen = 1;
2579                 pgm->seen = 0;
2580                 pgm->undeleted = 1;
2581                 pgm->deleted = 0;
2582  
2583                 vms_p->vmArrayIndex = 0;
2584         
2585                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2586                 *newmsgs = vms_p->vmArrayIndex;
2587                 vms_p->newmessages = vms_p->vmArrayIndex;
2588                 mail_free_searchpgm(&pgm);
2589         }
2590         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2591                 pgm = mail_newsearchpgm ();
2592                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2593                 pgm->header = hdr;
2594                 pgm->unseen = 0;
2595                 pgm->seen = 1;
2596                 pgm->deleted = 0;
2597                 pgm->undeleted = 1;
2598  
2599                 vms_p->vmArrayIndex = 0;
2600         
2601                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2602                 *oldmsgs = vms_p->vmArrayIndex;
2603                 vms_p->oldmessages = vms_p->vmArrayIndex;
2604                 mail_free_searchpgm(&pgm);
2605         }
2606         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2607                 vms_p->updated = 0;
2608         } else if (vms_p->updated > 1) {  /* decrement delay count */
2609                 vms_p->updated--;
2610         } else {  /* no changes, so don't search */
2611                 mail_ping(vms_p->mailstream);
2612                 /* Keep the old data */
2613                 *newmsgs = vms_p->newmessages;
2614                 *oldmsgs = vms_p->oldmessages;
2615         }
2616         return 0;
2617  }
2618
2619 static int has_voicemail(const char *mailbox, const char *folder)
2620 {
2621         int newmsgs, oldmsgs;
2622         
2623         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2624                 return folder? oldmsgs: newmsgs;
2625         else
2626                 return 0;
2627 }
2628
2629 static int messagecount(const char *context, const char *mailbox, const char *folder)
2630 {
2631         int newmsgs, oldmsgs;
2632         char tmp[256] = "";
2633         
2634         if (ast_strlen_zero(mailbox))
2635                 return 0;
2636         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2637
2638         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2639                 return folder? oldmsgs: newmsgs;
2640         else
2641                 return 0;
2642 }
2643
2644 #endif
2645 #ifndef IMAP_STORAGE
2646 /* copy message only used by file storage */
2647 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)
2648 {
2649         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2650         const char *frombox = mbox(imbox);
2651         int recipmsgnum;
2652
2653         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2654
2655         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2656         
2657         if (!dir)
2658                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2659         else
2660                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2661
2662         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2663         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2664
2665         if (vm_lock_path(todir))
2666                 return ERROR_LOCK_PATH;
2667
2668         recipmsgnum = last_message_index(recip, todir) + 1;
2669         if (recipmsgnum < recip->maxmsg) {
2670                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2671                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2672         } else {
2673                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2674         }
2675         ast_unlock_path(todir);
2676         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2677         
2678         return 0;
2679 }
2680 #endif
2681 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2682 static int messagecount(const char *context, const char *mailbox, const char *folder)
2683 {
2684         return __has_voicemail(context, mailbox, folder, 0);
2685 }
2686
2687
2688 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2689 {
2690         DIR *dir;
2691         struct dirent *de;
2692         char fn[256];
2693         int ret = 0;
2694         if (!folder)
2695                 folder = "INBOX";
2696         /* If no mailbox, return immediately */
2697         if (ast_strlen_zero(mailbox))
2698                 return 0;
2699         if (!context)
2700                 context = "default";
2701         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2702         dir = opendir(fn);
2703         if (!dir)
2704                 return 0;
2705         while ((de = readdir(dir))) {
2706                 if (!strncasecmp(de->d_name, "msg", 3)) {
2707                         if (shortcircuit) {
2708                                 ret = 1;
2709                                 break;
2710                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2711                                 ret++;
2712                 }
2713         }
2714         closedir(dir);
2715         return ret;
2716 }
2717
2718
2719 static int has_voicemail(const char *mailbox, const char *folder)
2720 {
2721         char tmp[256], *tmp2 = tmp, *mbox, *context;
2722         ast_copy_string(tmp, mailbox, sizeof(tmp));
2723         while ((mbox = strsep(&tmp2, ","))) {
2724                 if ((context = strchr(mbox, '@')))
2725                         *context++ = '\0';
2726                 else
2727                         context = "default";
2728                 if (__has_voicemail(context, mbox, folder, 1))
2729                         return 1;
2730         }
2731         return 0;
2732 }
2733
2734
2735 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2736 {
2737         char tmp[256];
2738         char *context;
2739
2740         if (newmsgs)
2741                 *newmsgs = 0;
2742         if (oldmsgs)
2743                 *oldmsgs = 0;
2744         /* If no mailbox, return immediately */
2745         if (ast_strlen_zero(mailbox))
2746                 return 0;
2747         if (strchr(mailbox, ',')) {
2748                 int tmpnew, tmpold;
2749                 char *mb, *cur;
2750
2751                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2752                 mb = tmp;
2753                 while ((cur = strsep(&mb, ", "))) {
2754                         if (!ast_strlen_zero(cur)) {
2755                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2756                                         return -1;
2757                                 else {
2758                                         if (newmsgs)
2759                                                 *newmsgs += tmpnew; 
2760                                         if (oldmsgs)
2761                                                 *oldmsgs += tmpold;
2762                                 }
2763                         }
2764                 }
2765                 return 0;
2766         }
2767         ast_copy_string(tmp, mailbox, sizeof(tmp));
2768         context = strchr(tmp, '@');
2769         if (context) {
2770                 *context = '\0';
2771                 context++;
2772         } else
2773                 context = "default";
2774         if (newmsgs)
2775                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2776         if (oldmsgs)
2777                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2778         return 0;
2779 }
2780
2781 #endif
2782
2783 static void run_externnotify(char *context, char *extension)
2784 {
2785         char arguments[255];
2786         char ext_context[256] = "";
2787         int newvoicemails = 0, oldvoicemails = 0;
2788         struct ast_smdi_mwi_message *mwi_msg;
2789
2790         if (!ast_strlen_zero(context))
2791                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2792         else
2793                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2794
2795         if (!strcasecmp(externnotify, "smdi")) {
2796                 if (ast_app_has_voicemail(ext_context, NULL)) 
2797                         ast_smdi_mwi_set(smdi_iface, extension);
2798                 else
2799                         ast_smdi_mwi_unset(smdi_iface, extension);
2800
2801                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2802                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2803                         if (!strncmp(mwi_msg->cause, "INV", 3))
2804                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2805                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2806                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2807                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2808                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2809                 } else {
2810                         if (option_debug)
2811                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2812                 }
2813         } else if (!ast_strlen_zero(externnotify)) {
2814                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2815                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2816                 } else {
2817                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2818                         if (option_debug)
2819                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2820                         ast_safe_system(arguments);
2821                 }
2822         }
2823 }
2824
2825 struct leave_vm_options {
2826         unsigned int flags;
2827         signed char record_gain;
2828 };
2829
2830 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2831 {
2832 #ifdef IMAP_STORAGE
2833         int newmsgs, oldmsgs;
2834         struct vm_state *vms = NULL;
2835 #endif
2836         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2837         char callerid[256];
2838         FILE *txt;
2839         char date[256];
2840         int txtdes;
2841         int res = 0;
2842         int msgnum;
2843         int duration = 0;
2844         int ausemacro = 0;
2845         int ousemacro = 0;
2846         int ouseexten = 0;
2847         char dir[PATH_MAX], tmpdir[PATH_MAX];
2848         char dest[PATH_MAX];
2849         char fn[PATH_MAX];
2850         char prefile[PATH_MAX] = "";
2851         char tempfile[PATH_MAX] = "";
2852         char ext_context[256] = "";
2853         char fmt[80];
2854         char *context;
2855         char ecodes[16] = "#";
2856         char tmp[256] = "", *tmpptr;
2857         struct ast_vm_user *vmu;
2858         struct ast_vm_user svm;
2859         const char *category = NULL;
2860
2861         ast_copy_string(tmp, ext, sizeof(tmp));
2862         ext = tmp;
2863         context = strchr(tmp, '@');
2864         if (context) {
2865                 *context++ = '\0';
2866                 tmpptr = strchr(context, '&');
2867         } else {
2868                 tmpptr = strchr(ext, '&');
2869         }
2870
2871         if (tmpptr)
2872                 *tmpptr++ = '\0';
2873
2874         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2875
2876         if(option_debug > 2)
2877                 ast_log(LOG_DEBUG, "Before find_user\n");
2878         if (!(vmu = find_user(&svm, context, ext))) {
2879                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2880                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2881                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2882                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2883                 return res;
2884         }
2885         /* Setup pre-file if appropriate */
2886         if (strcmp(vmu->context, "default"))
2887                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2888         else
2889                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2890         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2891                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2892                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2893         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2894                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2895                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2896         }
2897         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2898         if (!(res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2899                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2900                 return -1;
2901         }
2902         RETRIEVE(tempfile, -1);
2903         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2904                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2905         DISPOSE(tempfile, -1);
2906         /* It's easier just to try to make it than to check for its existence */
2907         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2908         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2909
2910         /* Check current or macro-calling context for special extensions */
2911         if (ast_test_flag(vmu, VM_OPERATOR)) {
2912                 if (!ast_strlen_zero(vmu->exit)) {
2913                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2914                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2915                                 ouseexten = 1;
2916                         }
2917                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2918                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2919                         ouseexten = 1;
2920                 }
2921                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2922                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2923                 ousemacro = 1;
2924                 }
2925         }
2926
2927         if (!ast_strlen_zero(vmu->exit)) {
2928                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2929                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2930         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2931                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2932         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2933                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2934                 ausemacro = 1;
2935         }
2936
2937         /* Play the beginning intro if desired */
2938         if (!ast_strlen_zero(prefile)) {
2939                 RETRIEVE(prefile, -1);
2940                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2941                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2942                                 res = ast_waitstream(chan, ecodes);
2943                 } else {
2944                         if (option_debug)
2945                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2946                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2947                 }
2948                 DISPOSE(prefile, -1);
2949                 if (res < 0) {
2950                         if (option_debug)
2951                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2952                         free_user(vmu);
2953                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2954                         return -1;
2955                 }
2956         }
2957         if (res == '#') {
2958                 /* On a '#' we skip the instructions */
2959                 ast_set_flag(options, OPT_SILENT);
2960                 res = 0;
2961         }
2962         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2963                 res = ast_stream_and_wait(chan, INTRO, ecodes);
2964                 if (res == '#') {
2965                         ast_set_flag(options, OPT_SILENT);
2966                         res = 0;
2967                 }
2968         }
2969         if (res > 0)
2970                 ast_stopstream(chan);
2971         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2972          other than the operator -- an automated attendant or mailbox login for example */
2973         if (res == '*') {
2974                 chan->exten[0] = 'a';
2975                 chan->exten[1] = '\0';
2976                 if (!ast_strlen_zero(vmu->exit)) {
2977                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2978                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2979                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2980                 }
2981                 chan->priority = 0;
2982                 free_user(vmu);
2983                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2984                 return 0;
2985         }
2986
2987         /* Check for a '0' here */
2988         if (res == '0') {
2989         transfer:
2990                 if (ouseexten || ousemacro) {
2991                         chan->exten[0] = 'o';
2992                         chan->exten[1] = '\0';
2993                         if (!ast_strlen_zero(vmu->exit)) {
2994                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2995                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2996                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2997                         }
2998                         ast_play_and_wait(chan, "transfer");
2999                         chan->priority = 0;
3000                         free_user(vmu);
3001                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3002                 }
3003                 return 0;
3004         }
3005         if (res < 0) {
3006                 free_user(vmu);
3007                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3008                 return -1;
3009         }
3010         /* The meat of recording the message...  All the announcements and beeps have been played*/
3011         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3012         if (!ast_strlen_zero(fmt)) {
3013                 msgnum = 0;
3014
3015 #ifdef IMAP_STORAGE
3016                 /* Is ext a mailbox? */
3017                 /* must open stream for this user to get info! */
3018                 vms = get_vm_state_by_mailbox(ext,0);
3019                 if (vms) {
3020                         if(option_debug > 2)
3021                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3022                         newmsgs = vms->newmessages++;
3023                         oldmsgs = vms->oldmessages;
3024                 } else {
3025                         res = inboxcount(ext, &newmsgs, &oldmsgs);
3026                         if(res < 0) {
3027                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3028                                 return -1;
3029                         }
3030                         vms = get_vm_state_by_mailbox(ext,0);
3031                 }
3032                 /* here is a big difference! We add one to it later */
3033                 msgnum = newmsgs + oldmsgs;
3034                 if(option_debug > 2)
3035                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3036                 snprintf(fn, sizeof(fn), "%s/ima