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