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