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