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