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