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