Merged revisions 49459 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                 ast_log(LOG_DEBUG, "Name: %s Value: %s\n", tmp->name, tmp->value);
682                 if (!strcasecmp(tmp->name, "password")) {
683                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
684                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
685                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
686                 } else if (!strcasecmp(tmp->name, "pager")) {
687                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
688                 } else if (!strcasecmp(tmp->name, "email")) {
689                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
690                 } else if (!strcasecmp(tmp->name, "fullname")) {
691                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
692                 } else if (!strcasecmp(tmp->name, "context")) {
693                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
694                 } else
695                         apply_option(retval, tmp->name, tmp->value);
696                 tmp = tmp->next;
697         } 
698 }
699
700 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
701 {
702         struct ast_variable *var;
703         struct ast_vm_user *retval;
704
705         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
706                 if (!ivm)
707                         ast_set_flag(retval, VM_ALLOCED);       
708                 else
709                         memset(retval, 0, sizeof(*retval));
710                 if (mailbox) 
711                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
712                 populate_defaults(retval);
713                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
714                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
715                 else
716                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
717                 if (var) {
718                         apply_options_full(retval, var);
719                         ast_variables_destroy(var);
720                 } else { 
721                         if (!ivm) 
722                                 free(retval);
723                         retval = NULL;
724                 }       
725         } 
726         return retval;
727 }
728
729 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
730 {
731         /* This function could be made to generate one from a database, too */
732         struct ast_vm_user *vmu=NULL, *cur;
733         AST_LIST_LOCK(&users);
734
735         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
736                 context = "default";
737
738         AST_LIST_TRAVERSE(&users, cur, list) {
739                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
740                         break;
741                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
742                         break;
743         }
744         if (cur) {
745                 /* Make a copy, so that on a reload, we have no race */
746                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
747                         memcpy(vmu, cur, sizeof(*vmu));
748                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
749                         AST_LIST_NEXT(vmu, list) = NULL;
750                 }
751         } else
752                 vmu = find_user_realtime(ivm, context, mailbox);
753         AST_LIST_UNLOCK(&users);
754         return vmu;
755 }
756
757 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
758 {
759         /* This function could be made to generate one from a database, too */
760         struct ast_vm_user *cur;
761         int res = -1;
762         AST_LIST_LOCK(&users);
763         AST_LIST_TRAVERSE(&users, cur, list) {
764                 if ((!context || !strcasecmp(context, cur->context)) &&
765                         (!strcasecmp(mailbox, cur->mailbox)))
766                                 break;
767         }
768         if (cur) {
769                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
770                 res = 0;
771         }
772         AST_LIST_UNLOCK(&users);
773         return res;
774 }
775
776 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
777 {
778         /*  There's probably a better way of doing this. */
779         /*  That's why I've put the password change in a separate function. */
780         /*  This could also be done with a database function */
781         
782         FILE *configin;
783         FILE *configout;
784         int linenum=0;
785         char inbuf[256];
786         char orig[256];
787         char currcontext[256] = "";
788         char tmpin[PATH_MAX];
789         char tmpout[PATH_MAX];
790         struct stat statbuf;
791
792         if (!change_password_realtime(vmu, newpassword))
793                 return;
794
795         snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
796         snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
797         configin = fopen(tmpin,"r");
798         if (configin)
799                 configout = fopen(tmpout,"w+");
800         else
801                 configout = NULL;
802         if (!configin || !configout) {
803                 if (configin)
804                         fclose(configin);
805                 else
806                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
807                 if (configout)
808                         fclose(configout);
809                 else
810                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
811                         return;
812         }
813
814         while (!feof(configin)) {
815                 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
816
817                 /* Read in the line */
818                 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
819                         continue;
820                 linenum++;
821
822                 /* Make a backup of it */
823                 ast_copy_string(orig, inbuf, sizeof(orig));
824
825                 /*
826                   Read the file line by line, split each line into a comment and command section
827                   only parse the command portion of the line
828                 */
829                 if (inbuf[strlen(inbuf) - 1] == '\n')
830                         inbuf[strlen(inbuf) - 1] = '\0';
831
832                 if ((comment = strchr(inbuf, ';')))
833                         *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
834
835                 if (ast_strlen_zero(inbuf)) {
836                         fprintf(configout, "%s", orig);
837                         continue;
838                 }
839
840                 /* Check for a context, first '[' to first ']' */
841                 if ((tmpctx = strchr(inbuf, '['))) {
842                         tmpctxend = strchr(tmpctx, ']');
843                         if (tmpctxend) {
844                                 /* Valid context */
845                                 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
846                                 fprintf(configout, "%s", orig);
847                                 continue;
848                         }
849                 }
850
851                 /* This isn't a context line, check for MBX => PSWD... */
852                 user = inbuf;
853                 if ((pass = strchr(user, '='))) {
854                         /* We have a line in the form of aaaaa=aaaaaa */
855                         *pass++ = '\0';
856
857                         user = ast_strip(user);
858
859                         if (*pass == '>')
860                                 *pass++ = '\0';
861
862                         pass = ast_skip_blanks(pass);
863
864                         /* 
865                            Since no whitespace allowed in fields, or more correctly white space
866                            inside the fields is there for a purpose, we can just terminate pass
867                            at the comma or EOL whichever comes first.
868                         */
869                         if ((rest = strchr(pass, ',')))
870                                 *rest++ = '\0';
871                 } else {
872                         user = NULL;
873                 }                       
874
875                 /* Compare user, pass AND context */
876                 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
877                         !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
878                         !strcasecmp(currcontext, vmu->context)) {
879                         /* This is the line */
880                         if (rest) {
881                                 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
882                         } else {
883                                 fprintf(configout, "%s => %s", user, newpassword);
884                         }
885                         /* If there was a comment on the line print it out */
886                         if (comment) {
887                                 fprintf(configout, ";%s\n", comment);
888                         } else {
889                                 fprintf(configout, "\n");
890                         }
891                 } else {
892                         /* Put it back like it was */
893                         fprintf(configout, "%s", orig);
894                 }
895         }
896         fclose(configin);
897         fclose(configout);
898
899         stat(tmpin, &statbuf);
900         chmod(tmpout, statbuf.st_mode);
901         chown(tmpout, statbuf.st_uid, statbuf.st_gid);
902         unlink(tmpin);
903         rename(tmpout, tmpin);
904         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
905         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
906 }
907
908 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
909 {
910         char buf[255];
911         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
912         if (!ast_safe_system(buf))
913                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
914 }
915
916 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
917 {
918         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
919 }
920
921 #ifdef IMAP_STORAGE
922 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
923 {
924         if (mkdir(dir, 01777) && (errno != EEXIST)) {
925                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
926                 return sprintf(dest, "%s/msg%04d", dir, num);
927         }
928         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
929         return sprintf(dest, "%s/msg%04d", dir, num);
930 }
931
932 static void vm_imap_delete(int msgnum, struct vm_state *vms)
933 {
934         unsigned long messageNum = 0;
935         char arg[10];
936
937         /* find real message number based on msgnum */
938         /* this may be an index into vms->msgArray based on the msgnum. */
939
940         messageNum = vms->msgArray[msgnum];
941         if (messageNum == 0) {
942                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
943                 return;
944         }
945         if(option_debug > 2)
946                 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
947         /* delete message */
948         sprintf (arg,"%lu",messageNum);
949         mail_setflag (vms->mailstream,arg,"\\DELETED");
950 }
951
952 #endif
953 static int make_file(char *dest, int len, char *dir, int num)
954 {
955         return snprintf(dest, len, "%s/msg%04d", dir, num);
956 }
957
958 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
959  * \param dest    String. base directory.
960  * \param len     Length of dest.
961  * \param context String. Ignored if is null or empty string.
962  * \param ext     String. Ignored if is null or empty string.
963  * \param folder  String. Ignored if is null or empty string. 
964  * \return 0 on failure, 1 on success.
965  */
966 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
967 {
968         mode_t  mode = VOICEMAIL_DIR_MODE;
969
970         if (!ast_strlen_zero(context)) {
971                 make_dir(dest, len, context, "", "");
972                 if (mkdir(dest, mode) && errno != EEXIST) {
973                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
974                         return 0;
975                 }
976         }
977         if (!ast_strlen_zero(ext)) {
978                 make_dir(dest, len, context, ext, "");
979                 if (mkdir(dest, mode) && errno != EEXIST) {
980                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
981                         return 0;
982                 }
983         }
984         if (!ast_strlen_zero(folder)) {
985                 make_dir(dest, len, context, ext, folder);
986                 if (mkdir(dest, mode) && errno != EEXIST) {
987                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
988                         return 0;
989                 }
990         }
991         return 1;
992 }
993
994 /* only return failure if ast_lock_path returns 'timeout',
995    not if the path does not exist or any other reason
996 */
997 static int vm_lock_path(const char *path)
998 {
999         switch (ast_lock_path(path)) {
1000         case AST_LOCK_TIMEOUT:
1001                 return -1;
1002         default:
1003                 return 0;
1004         }
1005 }
1006
1007
1008 #ifdef ODBC_STORAGE
1009 static int retrieve_file(char *dir, int msgnum)
1010 {
1011         int x = 0;
1012         int res;
1013         int fd=-1;
1014         size_t fdlen = 0;
1015         void *fdm=NULL;
1016         SQLSMALLINT colcount=0;
1017         SQLHSTMT stmt;
1018         char sql[PATH_MAX];
1019         char fmt[80]="";
1020         char *c;
1021         char coltitle[256];
1022         SQLSMALLINT collen;
1023         SQLSMALLINT datatype;
1024         SQLSMALLINT decimaldigits;
1025         SQLSMALLINT nullable;
1026         SQLULEN colsize;
1027         SQLLEN colsize2;
1028         FILE *f=NULL;
1029         char rowdata[80];
1030         char fn[PATH_MAX];
1031         char full_fn[PATH_MAX];
1032         char msgnums[80];
1033         
1034         struct odbc_obj *obj;
1035         obj = ast_odbc_request_obj(odbc_database, 0);
1036         if (obj) {
1037                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1038                 c = strchr(fmt, '|');
1039                 if (c)
1040                         *c = '\0';
1041                 if (!strcasecmp(fmt, "wav49"))
1042                         strcpy(fmt, "WAV");
1043                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1044                 if (msgnum > -1)
1045                         make_file(fn, sizeof(fn), dir, msgnum);
1046                 else
1047                         ast_copy_string(fn, dir, sizeof(fn));
1048                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1049                 
1050                 if (!(f = fopen(full_fn, "w+"))) {
1051                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1052                         goto yuck;
1053                 }
1054                 
1055                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1056                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1057                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1058                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1059                         ast_odbc_release_obj(obj);
1060                         goto yuck;
1061                 }
1062                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1063                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1064                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1065                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1066                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1067                         ast_odbc_release_obj(obj);
1068                         goto yuck;
1069                 }
1070                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1071                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1072                 res = ast_odbc_smart_execute(obj, stmt);
1073                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1074                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1075                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1076                         ast_odbc_release_obj(obj);
1077                         goto yuck;
1078                 }
1079                 res = SQLFetch(stmt);
1080                 if (res == SQL_NO_DATA) {
1081                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1082                         ast_odbc_release_obj(obj);
1083                         goto yuck;
1084                 }
1085                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1086                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1087                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1088                         ast_odbc_release_obj(obj);
1089                         goto yuck;
1090                 }
1091                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1092                 if (fd < 0) {
1093                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1094                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1095                         ast_odbc_release_obj(obj);
1096                         goto yuck;
1097                 }
1098                 res = SQLNumResultCols(stmt, &colcount);
1099                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1100                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1101                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1102                         ast_odbc_release_obj(obj);
1103                         goto yuck;
1104                 }
1105                 if (f) 
1106                         fprintf(f, "[message]\n");
1107                 for (x=0;x<colcount;x++) {
1108                         rowdata[0] = '\0';
1109                         collen = sizeof(coltitle);
1110                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1111                                                 &datatype, &colsize, &decimaldigits, &nullable);
1112                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1113                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1114                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1115                                 ast_odbc_release_obj(obj);
1116                                 goto yuck;
1117                         }
1118                         if (!strcasecmp(coltitle, "recording")) {
1119                                 off_t offset;
1120                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize2);
1121                                 fdlen = colsize2;
1122                                 if (fd > -1) {
1123                                         char tmp[1]="";
1124                                         lseek(fd, fdlen - 1, SEEK_SET);
1125                                         if (write(fd, tmp, 1) != 1) {
1126                                                 close(fd);
1127                                                 fd = -1;
1128                                                 continue;
1129                                         }
1130                                         /* Read out in small chunks */
1131                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1132                                                 /* +1 because SQLGetData likes null-terminating binary data */
1133                                                 if ((fdm = mmap(NULL, CHUNKSIZE + 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == (void *)-1) {
1134                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1135                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1136                                                         ast_odbc_release_obj(obj);
1137                                                         goto yuck;
1138                                                 } else {
1139                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE + 1, NULL);
1140                                                         munmap(fdm, 0);
1141                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1142                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1143                                                                 unlink(full_fn);
1144                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1145                                                                 ast_odbc_release_obj(obj);
1146                                                                 goto yuck;
1147                                                         }
1148                                                 }
1149                                         }
1150                                         truncate(full_fn, fdlen);
1151                                 }
1152                         } else {
1153                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1154                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1155                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1156                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1157                                         ast_odbc_release_obj(obj);
1158                                         goto yuck;
1159                                 }
1160                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1161                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1162                         }
1163                 }
1164                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1165                 ast_odbc_release_obj(obj);
1166         } else
1167                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1168 yuck:   
1169         if (f)
1170                 fclose(f);
1171         if (fd > -1)
1172                 close(fd);
1173         return x - 1;
1174 }
1175
1176 static int remove_file(char *dir, int msgnum)
1177 {
1178         char fn[PATH_MAX];
1179         char full_fn[PATH_MAX];
1180         char msgnums[80];
1181         
1182         if (msgnum > -1) {
1183                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1184                 make_file(fn, sizeof(fn), dir, msgnum);
1185         } else
1186                 ast_copy_string(fn, dir, sizeof(fn));
1187         ast_filedelete(fn, NULL);       
1188         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1189         unlink(full_fn);
1190         return 0;
1191 }
1192
1193 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1194 {
1195         int x = 0;
1196         int res;
1197         SQLHSTMT stmt;
1198         char sql[PATH_MAX];
1199         char rowdata[20];
1200         
1201         struct odbc_obj *obj;
1202         obj = ast_odbc_request_obj(odbc_database, 0);
1203         if (obj) {
1204                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1205                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1206                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1207                         ast_odbc_release_obj(obj);
1208                         goto yuck;
1209                 }
1210                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1211                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1212                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1213                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1214                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1215                         ast_odbc_release_obj(obj);
1216                         goto yuck;
1217                 }
1218                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1219                 res = ast_odbc_smart_execute(obj, stmt);
1220                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1221                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1222                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1223                         ast_odbc_release_obj(obj);
1224                         goto yuck;
1225                 }
1226                 res = SQLFetch(stmt);
1227                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1228                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1229                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1230                         ast_odbc_release_obj(obj);
1231                         goto yuck;
1232                 }
1233                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1234                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1235                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1236                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1237                         ast_odbc_release_obj(obj);
1238                         goto yuck;
1239                 }
1240                 if (sscanf(rowdata, "%d", &x) != 1)
1241                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1242                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1243                 ast_odbc_release_obj(obj);
1244         } else
1245                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1246 yuck:   
1247         return x - 1;
1248 }
1249
1250 static int message_exists(char *dir, int msgnum)
1251 {
1252         int x = 0;
1253         int res;
1254         SQLHSTMT stmt;
1255         char sql[PATH_MAX];
1256         char rowdata[20];
1257         char msgnums[20];
1258         
1259         struct odbc_obj *obj;
1260         obj = ast_odbc_request_obj(odbc_database, 0);
1261         if (obj) {
1262                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1263                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1264                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1265                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1266                         ast_odbc_release_obj(obj);
1267                         goto yuck;
1268                 }
1269                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1270                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1271                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1272                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1273                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1274                         ast_odbc_release_obj(obj);
1275                         goto yuck;
1276                 }
1277                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1278                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1279                 res = ast_odbc_smart_execute(obj, stmt);
1280                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1281                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1282                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1283                         ast_odbc_release_obj(obj);
1284                         goto yuck;
1285                 }
1286                 res = SQLFetch(stmt);
1287                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1288                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1289                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1290                         ast_odbc_release_obj(obj);
1291                         goto yuck;
1292                 }
1293                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1294                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1295                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1296                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1297                         ast_odbc_release_obj(obj);
1298                         goto yuck;
1299                 }
1300                 if (sscanf(rowdata, "%d", &x) != 1)
1301                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1302                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303                 ast_odbc_release_obj(obj);
1304         } else
1305                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1306 yuck:   
1307         return x;
1308 }
1309
1310 static int count_messages(struct ast_vm_user *vmu, char *dir)
1311 {
1312         return last_message_index(vmu, dir) + 1;
1313 }
1314
1315 static void delete_file(char *sdir, int smsg)
1316 {
1317         int res;
1318         SQLHSTMT stmt;
1319         char sql[PATH_MAX];
1320         char msgnums[20];
1321         
1322         struct odbc_obj *obj;
1323         obj = ast_odbc_request_obj(odbc_database, 0);
1324         if (obj) {
1325                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1326                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1327                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1328                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1329                         ast_odbc_release_obj(obj);
1330                         goto yuck;
1331                 }
1332                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1333                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1334                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1335                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1336                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1337                         ast_odbc_release_obj(obj);
1338                         goto yuck;
1339                 }
1340                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1341                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1342                 res = ast_odbc_smart_execute(obj, stmt);
1343                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1344                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1345                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1346                         ast_odbc_release_obj(obj);
1347                         goto yuck;
1348                 }
1349                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1350                 ast_odbc_release_obj(obj);
1351         } else
1352                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1353 yuck:
1354         return; 
1355 }
1356
1357 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1358 {
1359         int res;
1360         SQLHSTMT stmt;
1361         char sql[512];
1362         char msgnums[20];
1363         char msgnumd[20];
1364         struct odbc_obj *obj;
1365
1366         delete_file(ddir, dmsg);
1367         obj = ast_odbc_request_obj(odbc_database, 0);
1368         if (obj) {
1369                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1370                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1371                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1372                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1373                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1374                         ast_odbc_release_obj(obj);
1375                         goto yuck;
1376                 }
1377                 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); 
1378                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1379                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1380                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1381                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1382                         ast_odbc_release_obj(obj);
1383                         goto yuck;
1384                 }
1385                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1386                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1387                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1388                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1389                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1390                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1391                 res = ast_odbc_smart_execute(obj, stmt);
1392                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1393                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1394                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1395                         ast_odbc_release_obj(obj);
1396                         goto yuck;
1397                 }
1398                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1399                 ast_odbc_release_obj(obj);
1400         } else
1401                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1402 yuck:
1403         return; 
1404 }
1405
1406 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1407 {
1408         int x = 0;
1409         int res;
1410         int fd = -1;
1411         void *fdm=NULL;
1412         size_t fdlen = -1;
1413         SQLHSTMT stmt;
1414         SQLINTEGER len;
1415         char sql[PATH_MAX];
1416         char msgnums[20];
1417         char fn[PATH_MAX];
1418         char full_fn[PATH_MAX];
1419         char fmt[80]="";
1420         char *c;
1421         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1422         const char *category = "";
1423         struct ast_config *cfg=NULL;
1424         struct odbc_obj *obj;
1425
1426         delete_file(dir, msgnum);
1427         obj = ast_odbc_request_obj(odbc_database, 0);
1428         if (obj) {
1429                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1430                 c = strchr(fmt, '|');
1431                 if (c)
1432                         *c = '\0';
1433                 if (!strcasecmp(fmt, "wav49"))
1434                         strcpy(fmt, "WAV");
1435                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1436                 if (msgnum > -1)
1437                         make_file(fn, sizeof(fn), dir, msgnum);
1438                 else
1439                         ast_copy_string(fn, dir, sizeof(fn));
1440                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1441                 cfg = ast_config_load(full_fn);
1442                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1443                 fd = open(full_fn, O_RDWR);
1444                 if (fd < 0) {
1445                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1446                         ast_odbc_release_obj(obj);
1447                         goto yuck;
1448                 }
1449                 if (cfg) {
1450                         context = ast_variable_retrieve(cfg, "message", "context");
1451                         if (!context) context = "";
1452                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1453                         if (!macrocontext) macrocontext = "";
1454                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1455                         if (!callerid) callerid = "";
1456                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1457                         if (!origtime) origtime = "";
1458                         duration = ast_variable_retrieve(cfg, "message", "duration");
1459                         if (!duration) duration = "";
1460                         category = ast_variable_retrieve(cfg, "message", "category");
1461                         if (!category) category = "";
1462                 }
1463                 fdlen = lseek(fd, 0, SEEK_END);
1464                 lseek(fd, 0, SEEK_SET);
1465                 printf("Length is %zd\n", fdlen);
1466                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1467                 if (!fdm) {
1468                         ast_log(LOG_WARNING, "Memory map failed!\n");
1469                         ast_odbc_release_obj(obj);
1470                         goto yuck;
1471                 } 
1472                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1473                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1474                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1475                         ast_odbc_release_obj(obj);
1476                         goto yuck;
1477                 }
1478                 if (!ast_strlen_zero(category)) 
1479                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1480                 else
1481                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1482                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1483                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1484                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1485                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1486                         ast_odbc_release_obj(obj);
1487                         goto yuck;
1488                 }
1489                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1490                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1491                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1492                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1493                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1494                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1495                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1496                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1497                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1498                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1499                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1500                 if (!ast_strlen_zero(category))
1501                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1502                 res = ast_odbc_smart_execute(obj, stmt);
1503                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1504                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1505                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1506                         ast_odbc_release_obj(obj);
1507                         goto yuck;
1508                 }
1509                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1510                 ast_odbc_release_obj(obj);
1511         } else
1512                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1513 yuck:   
1514         if (cfg)
1515                 ast_config_destroy(cfg);
1516         if (fdm)
1517                 munmap(fdm, fdlen);
1518         if (fd > -1)
1519                 close(fd);
1520         return x;
1521 }
1522
1523 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1524 {
1525         int res;
1526         SQLHSTMT stmt;
1527         char sql[PATH_MAX];
1528         char msgnums[20];
1529         char msgnumd[20];
1530         struct odbc_obj *obj;
1531
1532         delete_file(ddir, dmsg);
1533         obj = ast_odbc_request_obj(odbc_database, 0);
1534         if (obj) {
1535                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1536                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1537                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1538                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1539                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1540                         ast_odbc_release_obj(obj);
1541                         goto yuck;
1542                 }
1543                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1544                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1545                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1546                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1547                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1548                         ast_odbc_release_obj(obj);
1549                         goto yuck;
1550                 }
1551                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1552                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1553                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1554                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1555                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1556                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1557                 res = ast_odbc_smart_execute(obj, stmt);
1558                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1559                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1560                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1561                         ast_odbc_release_obj(obj);
1562                         goto yuck;
1563                 }
1564                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1565                 ast_odbc_release_obj(obj);
1566         } else
1567                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1568 yuck:
1569         return; 
1570 }
1571
1572 #else
1573 #ifndef IMAP_STORAGE
1574 static int count_messages(struct ast_vm_user *vmu, char *dir)
1575 {
1576         /* Find all .txt files - even if they are not in sequence from 0000 */
1577
1578         int vmcount = 0;
1579         DIR *vmdir = NULL;
1580         struct dirent *vment = NULL;
1581
1582         if (vm_lock_path(dir))
1583                 return ERROR_LOCK_PATH;
1584
1585         if ((vmdir = opendir(dir))) {
1586                 while ((vment = readdir(vmdir))) {
1587                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1588                                 vmcount++;
1589                 }
1590                 closedir(vmdir);
1591         }
1592         ast_unlock_path(dir);
1593         
1594         return vmcount;
1595 }
1596
1597 static void rename_file(char *sfn, char *dfn)
1598 {
1599         char stxt[PATH_MAX];
1600         char dtxt[PATH_MAX];
1601         ast_filerename(sfn,dfn,NULL);
1602         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1603         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1604         rename(stxt, dtxt);
1605 }
1606
1607 static int copy(char *infile, char *outfile)
1608 {
1609         int ifd;
1610         int ofd;
1611         int res;
1612         int len;
1613         char buf[4096];
1614
1615 #ifdef HARDLINK_WHEN_POSSIBLE
1616         /* Hard link if possible; saves disk space & is faster */
1617         if (link(infile, outfile)) {
1618 #endif
1619                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1620                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1621                         return -1;
1622                 }
1623                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1624                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1625                         close(ifd);
1626                         return -1;
1627                 }
1628                 do {
1629                         len = read(ifd, buf, sizeof(buf));
1630                         if (len < 0) {
1631                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1632                                 close(ifd);
1633                                 close(ofd);
1634                                 unlink(outfile);
1635                         }
1636                         if (len) {
1637                                 res = write(ofd, buf, len);
1638                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1639                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1640                                         close(ifd);
1641                                         close(ofd);
1642                                         unlink(outfile);
1643                                 }
1644                         }
1645                 } while (len);
1646                 close(ifd);
1647                 close(ofd);
1648                 return 0;
1649 #ifdef HARDLINK_WHEN_POSSIBLE
1650         } else {
1651                 /* Hard link succeeded */
1652                 return 0;
1653         }
1654 #endif
1655 }
1656
1657 static void copy_file(char *frompath, char *topath)
1658 {
1659         char frompath2[PATH_MAX], topath2[PATH_MAX];
1660         ast_filecopy(frompath, topath, NULL);
1661         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1662         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1663         copy(frompath2, topath2);
1664 }
1665 #endif
1666 /*
1667  * A negative return value indicates an error.
1668  * \note Should always be called with a lock already set on dir.
1669  */
1670 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1671 {
1672         int x;
1673         unsigned char map[MAXMSGLIMIT] = "";
1674         DIR *msgdir;
1675         struct dirent *msgdirent;
1676         int msgdirint;
1677
1678         /* Reading the entire directory into a file map scales better than
1679          * doing a stat repeatedly on a predicted sequence.  I suspect this
1680          * is partially due to stat(2) internally doing a readdir(2) itself to
1681          * find each file. */
1682         msgdir = opendir(dir);
1683         while ((msgdirent = readdir(msgdir))) {
1684                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1685                         map[msgdirint] = 1;
1686         }
1687         closedir(msgdir);
1688
1689         for (x = 0; x < vmu->maxmsg; x++) {
1690                 if (map[x] == 0)
1691                         break;
1692         }
1693
1694         return x - 1;
1695 }
1696
1697 static int vm_delete(char *file)
1698 {
1699         char *txt;
1700         int txtsize = 0;
1701
1702         txtsize = (strlen(file) + 5)*sizeof(char);
1703         txt = alloca(txtsize);
1704         /* Sprintf here would safe because we alloca'd exactly the right length,
1705          * but trying to eliminate all sprintf's anyhow
1706          */
1707         snprintf(txt, txtsize, "%s.txt", file);
1708         unlink(txt);
1709         return ast_filedelete(file, NULL);
1710 }
1711
1712
1713 #endif
1714 static int inbuf(struct baseio *bio, FILE *fi)
1715 {
1716         int l;
1717
1718         if (bio->ateof)
1719                 return 0;
1720
1721         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1722                 if (ferror(fi))
1723                         return -1;
1724
1725                 bio->ateof = 1;
1726                 return 0;
1727         }
1728
1729         bio->iolen= l;
1730         bio->iocp= 0;
1731
1732         return 1;
1733 }
1734
1735 static int inchar(struct baseio *bio, FILE *fi)
1736 {
1737         if (bio->iocp>=bio->iolen) {
1738                 if (!inbuf(bio, fi))
1739                         return EOF;
1740         }
1741
1742         return bio->iobuf[bio->iocp++];
1743 }
1744
1745 static int ochar(struct baseio *bio, int c, FILE *so)
1746 {
1747         if (bio->linelength>=BASELINELEN) {
1748                 if (fputs(eol,so)==EOF)
1749                         return -1;
1750
1751                 bio->linelength= 0;
1752         }
1753
1754         if (putc(((unsigned char)c),so)==EOF)
1755                 return -1;
1756
1757         bio->linelength++;
1758
1759         return 1;
1760 }
1761
1762 static int base_encode(char *filename, FILE *so)
1763 {
1764         unsigned char dtable[BASEMAXINLINE];
1765         int i,hiteof= 0;
1766         FILE *fi;
1767         struct baseio bio;
1768
1769         memset(&bio, 0, sizeof(bio));
1770         bio.iocp = BASEMAXINLINE;
1771
1772         if (!(fi = fopen(filename, "rb"))) {
1773                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1774                 return -1;
1775         }
1776
1777         for (i= 0;i<9;i++) {
1778                 dtable[i]= 'A'+i;
1779                 dtable[i+9]= 'J'+i;
1780                 dtable[26+i]= 'a'+i;
1781                 dtable[26+i+9]= 'j'+i;
1782         }
1783         for (i= 0;i<8;i++) {
1784                 dtable[i+18]= 'S'+i;
1785                 dtable[26+i+18]= 's'+i;
1786         }
1787         for (i= 0;i<10;i++) {
1788                 dtable[52+i]= '0'+i;
1789         }
1790         dtable[62]= '+';
1791         dtable[63]= '/';
1792
1793         while (!hiteof){
1794                 unsigned char igroup[3],ogroup[4];
1795                 int c,n;
1796
1797                 igroup[0]= igroup[1]= igroup[2]= 0;
1798
1799                 for (n= 0;n<3;n++) {
1800                         if ((c = inchar(&bio, fi)) == EOF) {
1801                                 hiteof= 1;
1802                                 break;
1803                         }
1804
1805                         igroup[n]= (unsigned char)c;
1806                 }
1807
1808                 if (n> 0) {
1809                         ogroup[0]= dtable[igroup[0]>>2];
1810                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1811                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1812                         ogroup[3]= dtable[igroup[2]&0x3F];
1813
1814                         if (n<3) {
1815                                 ogroup[3]= '=';
1816
1817                                 if (n<2)
1818                                         ogroup[2]= '=';
1819                         }
1820
1821                         for (i= 0;i<4;i++)
1822                                 ochar(&bio, ogroup[i], so);
1823                 }
1824         }
1825
1826         if (fputs(eol,so)==EOF)
1827                 return 0;
1828
1829         fclose(fi);
1830
1831         return 1;
1832 }
1833
1834 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)
1835 {
1836         char callerid[256];
1837         /* Prepare variables for substitution in email body and subject */
1838         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1839         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1840         snprintf(passdata, passdatasize, "%d", msgnum);
1841         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1842         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1843         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1844         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1845         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1846         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1847         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1848         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1849 }
1850
1851 /*
1852  * fill in *tm for current time according to the proper timezone, if any.
1853  * Return tm so it can be used as a function argument.
1854  */
1855 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1856 {
1857         const struct vm_zone *z = NULL;
1858         time_t t = time(NULL);
1859
1860         /* Does this user have a timezone specified? */
1861         if (!ast_strlen_zero(vmu->zonetag)) {
1862                 /* Find the zone in the list */
1863                 AST_LIST_LOCK(&zones);
1864                 AST_LIST_TRAVERSE(&zones, z, list) {
1865                         if (!strcmp(z->name, vmu->zonetag))
1866                                 break;
1867                 }
1868                 AST_LIST_UNLOCK(&zones);
1869         }
1870         ast_localtime(&t, tm, z ? z->timezone : NULL);
1871         return tm;
1872 }
1873
1874 /* same as mkstemp, but return a FILE * */
1875 static FILE *vm_mkftemp(char *template)
1876 {
1877         FILE *p = NULL;
1878         int pfd = mkstemp(template);
1879         if (pfd > -1) {
1880                 p = fdopen(pfd, "w+");
1881                 if (!p) {
1882                         close(pfd);
1883                         pfd = -1;
1884                 }
1885         }
1886         return p;
1887 }
1888
1889 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)
1890 {
1891         char date[256];
1892         char host[MAXHOSTNAMELEN] = "";
1893         char who[256];
1894         char bound[256];
1895         char fname[256];
1896         char dur[256];
1897         char tmpcmd[256];
1898         struct tm tm;
1899
1900         gethostname(host, sizeof(host)-1);
1901         if (strchr(srcemail, '@'))
1902                 ast_copy_string(who, srcemail, sizeof(who));
1903         else {
1904                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1905         }
1906         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1907         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1908         fprintf(p, "Date: %s\r\n", date);
1909
1910         /* Set date format for voicemail mail */
1911         strftime(date, sizeof(date), emaildateformat, &tm);
1912
1913         if (*fromstring) {
1914                 struct ast_channel *ast;
1915                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1916                         char *passdata;
1917                         int vmlen = strlen(fromstring)*3 + 200;
1918                         if ((passdata = alloca(vmlen))) {
1919                                 memset(passdata, 0, vmlen);
1920                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1921                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1922                                 fprintf(p, "From: %s <%s>\r\n",passdata,who);
1923                         } else
1924                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1925                         ast_channel_free(ast);
1926                 } else
1927                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1928         } else
1929                 fprintf(p, "From: Asterisk PBX <%s>\r\n", who);
1930         fprintf(p, "To: %s <%s>\r\n", vmu->fullname, vmu->email);
1931         if (emailsubject) {
1932                 struct ast_channel *ast;
1933                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1934                         char *passdata;
1935                         int vmlen = strlen(emailsubject)*3 + 200;
1936                         if ((passdata = alloca(vmlen))) {
1937                                 memset(passdata, 0, vmlen);
1938                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1939                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1940                                 fprintf(p, "Subject: %s\r\n", passdata);
1941                         } else
1942                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1943                         ast_channel_free(ast);
1944                 } else
1945                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1946         } else  if (*emailtitle) {
1947                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1948                 fprintf(p,"\r\n") ;
1949         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1950                 fprintf(p, "Subject: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1951         else
1952                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1953         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1954         if(imap) {
1955                 /* additional information needed for IMAP searching */
1956                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum + 1);
1957                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1958                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring);
1959                 fprintf(p, "X-Asterisk-VM-Context: %s\r\n", context);
1960                 fprintf(p, "X-Asterisk-VM-Extension: %s\r\n", mailbox);
1961                 fprintf(p, "X-Asterisk-VM-Priority: %d\r\n", chan->priority);
1962                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\r\n", chan->name);
1963                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum);
1964                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname);
1965                 fprintf(p, "X-Asterisk-VM-Duration: %d\r\n", duration);
1966                 if (!ast_strlen_zero(category))
1967                         fprintf(p, "X-Asterisk-VM-Category: %s\r\n", category);
1968                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\r\n", date);
1969                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL));
1970         }
1971         if (!ast_strlen_zero(cidnum))
1972                 fprintf(p, "X-Asterisk-CallerID: %s\r\n", cidnum);
1973         if (!ast_strlen_zero(cidname))
1974                 fprintf(p, "X-Asterisk-CallerIDName: %s\r\n", cidname);
1975         fprintf(p, "MIME-Version: 1.0\r\n");
1976         if (attach_user_voicemail) {
1977                 /* Something unique. */
1978                 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1979
1980                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound);
1981
1982                 fprintf(p, "--%s\r\n", bound);
1983         }
1984         fprintf(p, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset);
1985         if (emailbody) {
1986                 struct ast_channel *ast;
1987                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1988                         char *passdata;
1989                         int vmlen = strlen(emailbody)*3 + 200;
1990                         if ((passdata = alloca(vmlen))) {
1991                                 memset(passdata, 0, vmlen);
1992                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1993                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1994                                 fprintf(p, "%s\r\n", passdata);
1995                         } else
1996                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1997                         ast_channel_free(ast);
1998                 } else
1999                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2000         } else {
2001                 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"
2002
2003                 "in mailbox %s from %s, on %s so you might\r\n"
2004                 "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, 
2005                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2006         }
2007         if (attach_user_voicemail) {
2008                 /* Eww. We want formats to tell us their own MIME type */
2009                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2010                 char tmpdir[256], newtmp[256];
2011                 int tmpfd;
2012         
2013                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2014                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2015                 tmpfd = mkstemp(newtmp);
2016                 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
2017                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2018                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2019                         ast_safe_system(tmpcmd);
2020                         attach = newtmp;
2021                         ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2022                 }
2023                 fprintf(p, "--%s\r\n", bound);
2024                 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype, format, msgnum, format);
2025                 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2026                 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2027                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum, format);
2028                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2029                 base_encode(fname, p);
2030                 /* only attach if necessary */
2031                 if (imap && strcmp(format, "gsm")) {
2032                         fprintf(p, "--%s\r\n", bound);
2033                         fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum, format);
2034                         fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2035                         fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2036                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum);
2037                         snprintf(fname, sizeof(fname), "%s.gsm", attach);
2038                         base_encode(fname, p);
2039                 }
2040                 fprintf(p, "\r\n\r\n--%s--\r\n.\r\n", bound);
2041                 if (tmpfd > -1)
2042                         close(tmpfd);
2043                 unlink(newtmp);
2044         }
2045 }
2046 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)
2047 {
2048         FILE *p=NULL;
2049         char tmp[80] = "/tmp/astmail-XXXXXX";
2050         char tmp2[256];
2051
2052         if (vmu && ast_strlen_zero(vmu->email)) {
2053                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2054                 return(0);
2055         }
2056         if (!strcmp(format, "wav49"))
2057                 format = "WAV";
2058         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));
2059         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2060            command hangs */
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                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2066                 fclose(p);
2067                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2068                 ast_safe_system(tmp2);
2069                 if (option_debug)
2070                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2071         }
2072         return 0;
2073 }
2074
2075 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)
2076 {
2077         char date[256];
2078         char host[MAXHOSTNAMELEN] = "";
2079         char who[256];
2080         char dur[PATH_MAX];
2081         char tmp[80] = "/tmp/astmail-XXXXXX";
2082         char tmp2[PATH_MAX];
2083         struct tm tm;
2084         FILE *p;
2085
2086         if ((p = vm_mkftemp(tmp)) == NULL) {
2087                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2088                 return -1;
2089         } else {
2090                 gethostname(host, sizeof(host)-1);
2091                 if (strchr(srcemail, '@'))
2092                         ast_copy_string(who, srcemail, sizeof(who));
2093                 else {
2094                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2095                 }
2096                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2097                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2098                 fprintf(p, "Date: %s\n", date);
2099
2100                 if (*pagerfromstring) {
2101                         struct ast_channel *ast;
2102                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2103                                 char *passdata;
2104                                 int vmlen = strlen(fromstring)*3 + 200;
2105                                 if ((passdata = alloca(vmlen))) {
2106                                         memset(passdata, 0, vmlen);
2107                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2108                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2109                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2110                                 } else 
2111                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2112                                 ast_channel_free(ast);
2113                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2114                 } else
2115                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2116                 fprintf(p, "To: %s\n", pager);
2117                 if (pagersubject) {
2118                         struct ast_channel *ast;
2119                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2120                                 char *passdata;
2121                                 int vmlen = strlen(pagersubject) * 3 + 200;
2122                                 if ((passdata = alloca(vmlen))) {
2123                                         memset(passdata, 0, vmlen);
2124                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2125                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2126                                         fprintf(p, "Subject: %s\n\n", passdata);
2127                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2128                                 ast_channel_free(ast);
2129                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2130                 } else
2131                         fprintf(p, "Subject: New VM\n\n");
2132                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2133                 if (pagerbody) {
2134                         struct ast_channel *ast;
2135                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2136                                 char *passdata;
2137                                 int vmlen = strlen(pagerbody)*3 + 200;
2138                                 if ((passdata = alloca(vmlen))) {
2139                                         memset(passdata, 0, vmlen);
2140                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2141                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2142                                         fprintf(p, "%s\n", passdata);
2143                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2144                         ast_channel_free(ast);
2145                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2146                 } else {
2147                         fprintf(p, "New %s long msg in box %s\n"
2148                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2149                 }
2150                 fclose(p);
2151                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2152                 ast_safe_system(tmp2);
2153                 if (option_debug)
2154                         ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2155         }
2156         return 0;
2157 }
2158
2159 static int get_date(char *s, int len)
2160 {
2161         struct tm tm;
2162         time_t t;
2163         t = time(0);
2164         localtime_r(&t,&tm);
2165         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2166 }
2167
2168 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2169 {
2170         int res;
2171         char fn[PATH_MAX];
2172         char dest[PATH_MAX];
2173
2174         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2175
2176         if (!(res = create_dirpath(dest, sizeof(dest), context, ext, "greet"))) {
2177                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2178                 return -1;
2179         }
2180
2181         RETRIEVE(fn, -1);
2182         if (ast_fileexists(fn, NULL, NULL) > 0) {
2183                 res = ast_stream_and_wait(chan, fn, ecodes);
2184                 if (res) {
2185                         DISPOSE(fn, -1);
2186                         return res;
2187                 }
2188         } else {
2189                 /* Dispose just in case */
2190                 DISPOSE(fn, -1);
2191                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2192                 if (res)
2193                         return res;
2194                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2195                 if (res)
2196                         return res;
2197         }
2198         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2199         return res;
2200 }
2201
2202 static void free_user(struct ast_vm_user *vmu)
2203 {
2204         if (ast_test_flag(vmu, VM_ALLOCED))
2205                 free(vmu);
2206 }
2207
2208 static void free_zone(struct vm_zone *z)
2209 {
2210         free(z);
2211 }
2212
2213 static const char *mbox(int id)
2214 {
2215         static const char *msgs[] = {
2216                 "INBOX",
2217                 "Old",
2218                 "Work",
2219                 "Family",
2220                 "Friends",
2221                 "Cust1",
2222                 "Cust2",
2223                 "Cust3",
2224                 "Cust4",
2225                 "Cust5",
2226         };
2227         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2228 }
2229
2230 #ifdef ODBC_STORAGE
2231 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2232 {
2233         int x = -1;
2234         int res;
2235         SQLHSTMT stmt;
2236         char sql[PATH_MAX];
2237         char rowdata[20];
2238         char tmp[PATH_MAX] = "";
2239         struct odbc_obj *obj;
2240         char *context;
2241
2242         if (newmsgs)
2243                 *newmsgs = 0;
2244         if (oldmsgs)
2245                 *oldmsgs = 0;
2246
2247         /* If no mailbox, return immediately */
2248         if (ast_strlen_zero(mailbox))
2249                 return 0;
2250
2251         ast_copy_string(tmp, mailbox, sizeof(tmp));
2252         
2253         context = strchr(tmp, '@');
2254         if (context) {
2255                 *context = '\0';
2256                 context++;
2257         } else
2258                 context = "default";
2259         
2260         obj = ast_odbc_request_obj(odbc_database, 0);
2261         if (obj) {
2262                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2263                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2265                         ast_odbc_release_obj(obj);
2266                         goto yuck;
2267                 }
2268                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2269                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2270                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2271                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2272                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2273                         ast_odbc_release_obj(obj);
2274                         goto yuck;
2275                 }
2276                 res = ast_odbc_smart_execute(obj, stmt);
2277                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2278                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2279                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2280                         ast_odbc_release_obj(obj);
2281                         goto yuck;
2282                 }
2283                 res = SQLFetch(stmt);
2284                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2285                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2286                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2287                         ast_odbc_release_obj(obj);
2288                         goto yuck;
2289                 }
2290                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2291                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2292                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2293                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2294                         ast_odbc_release_obj(obj);
2295                         goto yuck;
2296                 }
2297                 *newmsgs = atoi(rowdata);
2298                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2299
2300                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2301                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2302                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2303                         ast_odbc_release_obj(obj);
2304                         goto yuck;
2305                 }
2306                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2307                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2308                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2309                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2310                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2311                         ast_odbc_release_obj(obj);
2312                         goto yuck;
2313                 }
2314                 res = ast_odbc_smart_execute(obj, stmt);
2315                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2316                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2317                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2318                         ast_odbc_release_obj(obj);
2319                         goto yuck;
2320                 }
2321                 res = SQLFetch(stmt);
2322                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2323                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2324                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2325                         ast_odbc_release_obj(obj);
2326                         goto yuck;
2327                 }
2328                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2329                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2330                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2331                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2332                         ast_odbc_release_obj(obj);
2333                         goto yuck;
2334                 }
2335                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2336                 ast_odbc_release_obj(obj);
2337                 *oldmsgs = atoi(rowdata);
2338                 x = 0;
2339         } else
2340                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2341                 
2342 yuck:   
2343         return x;
2344 }
2345
2346 static int messagecount(const char *context, const char *mailbox, const char *folder)
2347 {
2348         struct odbc_obj *obj = NULL;
2349         int nummsgs = 0;
2350         int res;
2351         SQLHSTMT stmt = NULL;
2352         char sql[PATH_MAX];
2353         char rowdata[20];
2354         if (!folder)
2355                 folder = "INBOX";
2356         /* If no mailbox, return immediately */
2357         if (ast_strlen_zero(mailbox))
2358                 return 0;
2359
2360         obj = ast_odbc_request_obj(odbc_database, 0);
2361         if (obj) {
2362                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2363                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2364                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2365                         goto yuck;
2366                 }
2367                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2368                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
2369                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2370                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2371                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2372                         goto yuck;
2373                 }
2374                 res = ast_odbc_smart_execute(obj, stmt);
2375                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2376                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2377                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2378                         goto yuck;
2379                 }
2380                 res = SQLFetch(stmt);
2381                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2382                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2383                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2384                         goto yuck;
2385                 }
2386                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2387                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2388                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2389                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2390                         goto yuck;
2391                 }
2392                 nummsgs = atoi(rowdata);
2393                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2394         } else
2395                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2396
2397 yuck:
2398         if (obj)
2399                 ast_odbc_release_obj(obj);
2400         return nummsgs;
2401 }
2402
2403 static int has_voicemail(const char *mailbox, const char *folder)
2404 {
2405         char *context, tmp[256];
2406         ast_copy_string(tmp, mailbox, sizeof(tmp));
2407         if ((context = strchr(tmp, '@')))
2408                 *context++ = '\0';
2409         else
2410                 context = "default";
2411
2412         if (messagecount(context, tmp, folder))
2413                 return 1;
2414         else
2415                 return 0;
2416 }
2417
2418 #elif defined(IMAP_STORAGE)
2419
2420 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)
2421 {
2422         char *myserveremail = serveremail;
2423         char fn[PATH_MAX];
2424         char mailbox[256];
2425         char *stringp;
2426         FILE *p=NULL;
2427         char tmp[80] = "/tmp/astmail-XXXXXX";
2428         long len;
2429         void *buf;
2430         STRING str;
2431         
2432         /* Attach only the first format */
2433         fmt = ast_strdupa(fmt);
2434         stringp = fmt;
2435         strsep(&stringp, "|");
2436
2437         if (!ast_strlen_zero(vmu->serveremail))
2438                 myserveremail = vmu->serveremail;
2439
2440         make_file(fn, sizeof(fn), dir, msgnum);
2441
2442         if (ast_strlen_zero(vmu->email))
2443                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2444
2445         if (!strcmp(fmt, "wav49"))
2446                 fmt = "WAV";
2447         if(option_debug > 2)
2448                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2449         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2450            command hangs */
2451         if ((p = vm_mkftemp(tmp)) == NULL) {
2452                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2453                 return -1;
2454         } else {
2455                 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);
2456                 /* read mail file to memory */          
2457                 len = ftell(p);
2458                 rewind(p);
2459                 if((buf = ast_malloc(len+1)) == NIL) {
2460                         ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2461                         return -1;
2462                 }
2463                 fread(buf, len, 1, p);
2464                 ((char *)buf)[len] = '\0';
2465                 INIT(&str, mail_string, buf, len);
2466                 imap_mailbox_name(mailbox, vms, 0, 1);
2467                 if(!mail_append(vms->mailstream, mailbox, &str))
2468                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2469                 fclose(p);
2470                 unlink(tmp);
2471                 ast_free(buf);
2472                 if(option_debug > 2)
2473                         ast_log(LOG_DEBUG, "%s stored\n", fn);
2474         }
2475         return 0;
2476
2477 }
2478
2479 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2480 {
2481         SEARCHPGM *pgm;
2482         SEARCHHEADER *hdr;
2483  
2484         struct ast_vm_user *vmu;
2485         struct vm_state *vms_p;
2486         char tmp[256] = "";
2487         char *mb, *cur;
2488         char *mailboxnc; 
2489         char *context;
2490         int ret = 0;
2491         if (newmsgs)
2492                 *newmsgs = 0;
2493         if (oldmsgs)
2494                 *oldmsgs = 0;
2495  
2496         if(option_debug > 2)
2497                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2498         /* If no mailbox, return immediately */
2499         if (ast_strlen_zero(mailbox))
2500                 return 0;
2501         if (strchr(mailbox, ',')) {
2502                 int tmpnew, tmpold;
2503                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2504                 mb = tmp;
2505                 ret = 0;
2506                 while((cur = strsep(&mb, ", "))) {
2507                         if (!ast_strlen_zero(cur)) {
2508                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2509                                         return -1;
2510                                 else {
2511                                         if (newmsgs)
2512                                                 *newmsgs += tmpnew; 
2513                                         if (oldmsgs)
2514                                                 *oldmsgs += tmpold;
2515                                 }
2516                         }
2517                 }
2518                 return 0;
2519         }
2520         ast_copy_string(tmp, mailbox, sizeof(tmp));
2521         context = strchr(tmp, '@');
2522         if (context) {
2523                 *context = '\0';
2524                 mailboxnc = tmp;
2525                 context++;
2526         } else {
2527                 context = "default";
2528                 mailboxnc = (char *)mailbox;
2529         }
2530  
2531         /* We have to get the user before we can open the stream! */
2532         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2533         vmu = find_user(NULL, context, mailboxnc);
2534         if (!vmu) {
2535                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2536                 return -1;
2537         } else {
2538                 /* No IMAP account available */
2539                 if (vmu->imapuser[0] == '\0') {
2540                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2541                         return -1;
2542                 }
2543         }
2544  
2545         /* check if someone is accessing this box right now... */
2546         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2547         if (!vms_p) {
2548                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2549         }
2550         if (vms_p) {
2551                 if(option_debug > 2)
2552                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2553                 *newmsgs = vms_p->newmessages;
2554                 *oldmsgs = vms_p->oldmessages;
2555                 return 0;
2556         }
2557  
2558         /* add one if not there... */
2559         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2560         if (!vms_p) {
2561                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2562         }
2563  
2564         if (!vms_p) {
2565                 if(option_debug > 2)
2566                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2567                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2568                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2569                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2570                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2571                 if(option_debug > 2)
2572                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2573                 vms_p->updated = 1;
2574                 vms_p->interactive = 0;
2575                 /* set mailbox to INBOX! */
2576                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2577                 init_vm_state(vms_p);
2578                 vmstate_insert(vms_p);
2579         }
2580         if (!vms_p->mailstream)
2581                 ret = init_mailstream(vms_p, 0);
2582         if (!vms_p->mailstream) {
2583                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2584                 return -1;
2585         }
2586         if (newmsgs && ret==0 && vms_p->updated==1 ) {
2587                 pgm = mail_newsearchpgm ();
2588                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2589                 pgm->header = hdr;
2590                 pgm->unseen = 1;
2591                 pgm->seen = 0;
2592                 pgm->undeleted = 1;
2593                 pgm->deleted = 0;
2594  
2595                 vms_p->vmArrayIndex = 0;
2596         
2597                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2598                 *newmsgs = vms_p->vmArrayIndex;
2599                 vms_p->newmessages = vms_p->vmArrayIndex;
2600                 mail_free_searchpgm(&pgm);
2601         }
2602         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2603                 pgm = mail_newsearchpgm ();
2604                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2605                 pgm->header = hdr;
2606                 pgm->unseen = 0;
2607                 pgm->seen = 1;
2608                 pgm->deleted = 0;
2609                 pgm->undeleted = 1;
2610  
2611                 vms_p->vmArrayIndex = 0;
2612         
2613                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2614                 *oldmsgs = vms_p->vmArrayIndex;
2615                 vms_p->oldmessages = vms_p->vmArrayIndex;
2616                 mail_free_searchpgm(&pgm);
2617         }
2618         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2619                 vms_p->updated = 0;
2620         } else if (vms_p->updated > 1) {  /* decrement delay count */
2621                 vms_p->updated--;
2622         } else {  /* no changes, so don't search */
2623                 mail_ping(vms_p->mailstream);
2624                 /* Keep the old data */
2625                 *newmsgs = vms_p->newmessages;
2626                 *oldmsgs = vms_p->oldmessages;
2627         }
2628         return 0;
2629  }
2630
2631 static int has_voicemail(const char *mailbox, const char *folder)
2632 {
2633         int newmsgs, oldmsgs;
2634         
2635         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2636                 return folder? oldmsgs: newmsgs;
2637         else
2638                 return 0;
2639 }
2640
2641 static int messagecount(const char *context, const char *mailbox, const char *folder)
2642 {
2643         int newmsgs, oldmsgs;
2644         char tmp[256] = "";
2645         
2646         if (ast_strlen_zero(mailbox))
2647                 return 0;
2648         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2649
2650         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2651                 return folder? oldmsgs: newmsgs;
2652         else
2653                 return 0;
2654 }
2655
2656 #endif
2657 #ifndef IMAP_STORAGE
2658 /* copy message only used by file storage */
2659 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)
2660 {
2661         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2662         const char *frombox = mbox(imbox);
2663         int recipmsgnum;
2664
2665         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2666
2667         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2668
2669         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2670         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2671         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2672
2673         if (vm_lock_path(todir))
2674                 return ERROR_LOCK_PATH;
2675
2676         recipmsgnum = last_message_index(recip, todir) + 1;
2677         if (recipmsgnum < recip->maxmsg) {
2678                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2679                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2680         } else {
2681                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2682         }
2683         ast_unlock_path(todir);
2684         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2685         
2686         return 0;
2687 }
2688 #endif
2689 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2690 static int messagecount(const char *context, const char *mailbox, const char *folder)
2691 {
2692         return __has_voicemail(context, mailbox, folder, 0);
2693 }
2694
2695
2696 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2697 {
2698         DIR *dir;
2699         struct dirent *de;
2700         char fn[256];
2701         int ret = 0;
2702         if (!folder)
2703                 folder = "INBOX";
2704         /* If no mailbox, return immediately */
2705         if (ast_strlen_zero(mailbox))
2706                 return 0;
2707         if (!context)
2708                 context = "default";
2709         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2710         dir = opendir(fn);
2711         if (!dir)
2712                 return 0;
2713         while ((de = readdir(dir))) {
2714                 if (!strncasecmp(de->d_name, "msg", 3)) {
2715                         if (shortcircuit) {
2716                                 ret = 1;
2717                                 break;
2718                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2719                                 ret++;
2720                 }
2721         }
2722         closedir(dir);
2723         return ret;
2724 }
2725
2726
2727 static int has_voicemail(const char *mailbox, const char *folder)
2728 {
2729         char tmp[256], *tmp2 = tmp, *mbox, *context;
2730         ast_copy_string(tmp, mailbox, sizeof(tmp));
2731         while ((mbox = strsep(&tmp2, ","))) {
2732                 if ((context = strchr(mbox, '@')))
2733                         *context++ = '\0';
2734                 else
2735                         context = "default";
2736                 if (__has_voicemail(context, mbox, folder, 1))
2737                         return 1;
2738         }
2739         return 0;
2740 }
2741
2742
2743 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2744 {
2745         char tmp[256];
2746         char *context;
2747
2748         if (newmsgs)
2749                 *newmsgs = 0;
2750         if (oldmsgs)
2751                 *oldmsgs = 0;
2752         /* If no mailbox, return immediately */
2753         if (ast_strlen_zero(mailbox))
2754                 return 0;
2755         if (strchr(mailbox, ',')) {
2756                 int tmpnew, tmpold;
2757                 char *mb, *cur;
2758
2759                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2760                 mb = tmp;
2761                 while ((cur = strsep(&mb, ", "))) {
2762                         if (!ast_strlen_zero(cur)) {
2763                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2764                                         return -1;
2765                                 else {
2766                                         if (newmsgs)
2767                                                 *newmsgs += tmpnew; 
2768                                         if (oldmsgs)
2769                                                 *oldmsgs += tmpold;
2770                                 }
2771                         }
2772                 }
2773                 return 0;
2774         }
2775         ast_copy_string(tmp, mailbox, sizeof(tmp));
2776         context = strchr(tmp, '@');
2777         if (context) {
2778                 *context = '\0';
2779                 context++;
2780         } else
2781                 context = "default";
2782         if (newmsgs)
2783                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2784         if (oldmsgs)
2785                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2786         return 0;
2787 }
2788
2789 #endif
2790
2791 static void run_externnotify(char *context, char *extension)
2792 {
2793         char arguments[255];
2794         char ext_context[256] = "";
2795         int newvoicemails = 0, oldvoicemails = 0;
2796         struct ast_smdi_mwi_message *mwi_msg;
2797
2798         if (!ast_strlen_zero(context))
2799                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2800         else
2801                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2802
2803         if (!strcasecmp(externnotify, "smdi")) {
2804                 if (ast_app_has_voicemail(ext_context, NULL)) 
2805                         ast_smdi_mwi_set(smdi_iface, extension);
2806                 else
2807                         ast_smdi_mwi_unset(smdi_iface, extension);
2808
2809                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2810                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2811                         if (!strncmp(mwi_msg->cause, "INV", 3))
2812                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2813                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2814                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2815                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2816                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2817                 } else {
2818                         if (option_debug)
2819                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2820                 }
2821         } else if (!ast_strlen_zero(externnotify)) {
2822                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2823                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2824                 } else {
2825                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2826                         if (option_debug)
2827                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2828                         ast_safe_system(arguments);
2829                 }
2830         }
2831 }
2832
2833 struct leave_vm_options {
2834         unsigned int flags;
2835         signed char record_gain;
2836 };
2837
2838 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2839 {
2840 #ifdef IMAP_STORAGE
2841         int newmsgs, oldmsgs;
2842         struct vm_state *vms = NULL;
2843 #endif
2844         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2845         char callerid[256];
2846         FILE *txt;
2847         char date[256];
2848         int txtdes;
2849         int res = 0;
2850         int msgnum;
2851         int duration = 0;
2852         int ausemacro = 0;
2853         int ousemacro = 0;
2854         int ouseexten = 0;
2855         char dir[PATH_MAX], tmpdir[PATH_MAX];
2856         char dest[PATH_MAX];
2857         char fn[PATH_MAX];
2858         char prefile[PATH_MAX] = "";
2859         char tempfile[PATH_MAX] = "";
2860         char ext_context[256] = "";
2861         char fmt[80];
2862         char *context;
2863         char ecodes[16] = "#";
2864         char tmp[256] = "", *tmpptr;
2865         struct ast_vm_user *vmu;
2866         struct ast_vm_user svm;
2867         const char *category = NULL;
2868
2869         ast_copy_string(tmp, ext, sizeof(tmp));
2870         ext = tmp;
2871         context = strchr(tmp, '@');
2872         if (context) {
2873                 *context++ = '\0';
2874                 tmpptr = strchr(context, '&');
2875         } else {
2876                 tmpptr = strchr(ext, '&');
2877         }
2878
2879         if (tmpptr)
2880                 *tmpptr++ = '\0';
2881
2882         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2883
2884         if(option_debug > 2)
2885                 ast_log(LOG_DEBUG, "Before find_user\n");
2886         if (!(vmu = find_user(&svm, context, ext))) {
2887                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2888                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2889                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2890                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2891                 return res;
2892         }
2893         /* Setup pre-file if appropriate */
2894         if (strcmp(vmu->context, "default"))
2895                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2896         else
2897                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2898         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
2899                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
2900                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2901         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
2902                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
2903                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2904         }
2905         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2906         if (!(res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
2907                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
2908                 return -1;
2909         }
2910         RETRIEVE(tempfile, -1);
2911         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2912                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2913         DISPOSE(tempfile, -1);
2914         /* It's easier just to try to make it than to check for its existence */
2915         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2916         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2917
2918         /* Check current or macro-calling context for special extensions */
2919         if (ast_test_flag(vmu, VM_OPERATOR)) {
2920                 if (!ast_strlen_zero(vmu->exit)) {
2921                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2922                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2923                                 ouseexten = 1;
2924                         }
2925                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2926                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2927                         ouseexten = 1;
2928                 }
2929                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2930                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2931                 ousemacro = 1;
2932                 }
2933         }
2934
2935         if (!ast_strlen_zero(vmu->exit)) {
2936                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2937                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2938         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2939                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2940         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2941                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2942                 ausemacro = 1;
2943         }
2944
2945         /* Play the beginning intro if desired */
2946         if (!ast_strlen_zero(prefile)) {
2947                 RETRIEVE(prefile, -1);
2948                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2949                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2950                                 res = ast_waitstream(chan, ecodes);
2951                 } else {
2952                         if (option_debug)
2953                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2954                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2955                 }
2956                 DISPOSE(prefile, -1);
2957                 if (res < 0) {
2958                         if (option_debug)
2959                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2960                         free_user(vmu);
2961                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2962                         return -1;
2963                 }
2964         }
2965         if (res == '#') {
2966                 /* On a '#' we skip the instructions */
2967                 ast_set_flag(options, OPT_SILENT);
2968                 res = 0;
2969         }
2970         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2971                 res = ast_stream_and_wait(chan, INTRO, ecodes);
2972                 if (res == '#') {
2973                         ast_set_flag(options, OPT_SILENT);
2974                         res = 0;
2975                 }
2976         }
2977         if (res > 0)
2978                 ast_stopstream(chan);
2979         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2980          other than the operator -- an automated attendant or mailbox login for example */
2981         if (res == '*') {
2982                 chan->exten[0] = 'a';
2983                 chan->exten[1] = '\0';
2984                 if (!ast_strlen_zero(vmu->exit)) {
2985                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2986                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2987                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2988                 }
2989                 chan->priority = 0;
2990                 free_user(vmu);
2991                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2992                 return 0;
2993         }
2994
2995         /* Check for a '0' here */
2996         if (res == '0') {
2997         transfer:
2998                 if (ouseexten || ousemacro) {
2999                         chan->exten[0] = 'o';
3000                         chan->exten[1] = '\0';
3001                         if (!ast_strlen_zero(vmu->exit)) {
3002                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3003                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3004                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3005                         }
3006                         ast_play_and_wait(chan, "transfer");
3007                         chan->priority = 0;
3008                         free_user(vmu);
3009                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3010                 }
3011                 return 0;
3012         }
3013         if (res < 0) {
3014                 free_user(vmu);
3015                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3016                 return -1;
3017         }
3018         /* The meat of recording the message...  All the announcements and beeps have been played*/
3019         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3020         if (!ast_strlen_zero(fmt)) {
3021                 msgnum = 0;
3022
3023 #ifdef IMAP_STORAGE
3024                 /* Is ext a mailbox? */
3025                 /* must open stream for this user to get info! */
3026                 vms = get_vm_state_by_mailbox(ext,0);
3027                 if (vms) {
3028                         if(option_debug > 2)
3029                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
3030                         newmsgs = vms->newmessages++;
3031                         oldmsgs = vms->oldmessages;
3032                 } else {
3033                         res = inboxcount(ext, &newmsgs, &oldmsgs);
3034                         if(res < 0) {
3035                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3036                                 return -1;
3037                         }
3038                         vms = get_vm_state_by_mailbox(ext,0);
3039                 }
3040                 /* here is a big difference! We add one to it later */
3041                 msgnum = newmsgs + oldmsgs;
3042                 if(option_debug > 2)
3043                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3044                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3045                 /* set variable for compatibility */
3046                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3047
3048                 /* Check if mailbox is full */
3049                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3050                         if (option_debug)
3051                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3052                         ast_play_and_wait(chan, "vm-mailboxfull");
3053                         return -1;
3054                 }
3055                 /* here is a big difference! We add one to it later */
3056                 msgnum = newmsgs + oldmsgs;
3057                 if(option_debug > 2)
3058                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3059
3060 #else
3061                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3062                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3063                         if (!res)
3064                                 res = ast_waitstream(chan, "");
3065                         ast_log(LOG_WARNING, "No more messages possible\n");
3066                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3067                         goto leave_vm_out;
3068                 }
3069
3070 #endif
3071                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3072                 txtdes = mkstemp(tmptxtfile);
3073                 if (txtdes < 0) {
3074                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3075                         if (!res)
3076                                 res = ast_waitstream(chan, "");
3077                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3078                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3079                         goto leave_vm_out;
3080                 }
3081
3082                 /* Now play the beep once we have the message number for our next message. */
3083                 if (res >= 0) {
3084                         /* Unless we're *really* silent, try to send the beep */
3085                         res = ast_stream_and_wait(chan, "beep", "");
3086                 }
3087                                 
3088                 /* Store information */
3089                 txt = fdopen(txtdes, "w+");
3090                 if (txt) {
3091                         get_date(date, sizeof(date));
3092                         fprintf(txt, 
3093                                 ";\n"
3094                                 "; Message Information file\n"
3095                                 ";\n"
3096                                 "[message]\n"
3097                                 "origmailbox=%s\n"
3098                                 "context=%s\n"
3099                                 "macrocontext=%s\n"
3100                                 "exten=%s\n"
3101                                 "priority=%d\n"
3102                                 "callerchan=%s\n"
3103                                 "callerid=%s\n"
3104                                 "origdate=%s\n"
3105                                 "origtime=%ld\n"
3106                                 "category=%s\n",
3107                                 ext,
3108                                 chan->context,
3109                                 chan->macrocontext, 
3110                                 chan->exten,
3111                                 chan->priority,
3112                                 chan->name,
3113                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3114                                 date, (long)time(NULL),
3115                                 category ? category : ""); 
3116                 } else
3117                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3118 #ifdef IMAP_STORAGE
3119                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3120 #else
3121                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3122 #endif
3123
3124                 if (txt) {
3125                         if (duration < vmminsecs) {
3126                                 if (option_verbose > 2) 
3127                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3128                                 ast_filedelete(tmptxtfile, NULL);
3129                                 unlink(tmptxtfile);
3130                         } else {
3131                                 fprintf(txt, "duration=%d\n", duration);
3132                                 fclose(txt);
3133                                 if (vm_lock_path(dir)) {
3134                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3135                                         /* Delete files */
3136                                         ast_filedelete(tmptxtfile, NULL);
3137                                         unlink(tmptxtfile);
3138                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3139                                         if (option_debug) 
3140                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3141                                         unlink(tmptxtfile);
3142                                         ast_unlock_path(dir);
3143                                 } else {
3144                                         msgnum = last_message_index(vmu, dir) + 1;
3145                                         make_file(fn, sizeof(fn), dir, msgnum);
3146
3147                                         /* assign a variable with the name of the voicemail file */ 
3148 #ifndef IMAP_STORAGE
3149                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3150 #else
3151                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3152 #endif
3153
3154                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3155                                         ast_filerename(tmptxtfile, fn, NULL);
3156                                         rename(tmptxtfile, txtfile);
3157
3158                                         ast_unlock_path(dir);
3159 #ifndef IMAP_STORAGE
3160                                         /* Are there to be more recipients of this message? */
3161                                         while (tmpptr) {
3162                                                 struct ast_vm_user recipu, *recip;
3163                                                 char *exten, *context;
3164                                         
3165                                                 exten = strsep(&tmpptr, "&");
3166                                                 context = strchr(exten, '@');
3167                                                 if (context) {
3168                                                         *context = '\0';
3169                                                         context++;
3170                                                 }
3171                                                 if ((recip = find_user(&recipu, context, exten))) {
3172                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3173                                                         free_user(recip);
3174                                                 }
3175                                         }
3176 #endif
3177                                         if (ast_fileexists(fn, NULL, NULL)) {
3178                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3179                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3180                                                 DISPOSE(dir, msgnum);
3181                                         }
3182                                 }
3183                         }
3184                 }
3185                 if (res == '0') {
3186                         goto transfer;
3187                 } else if (res > 0)
3188                         res = 0;
3189
3190                 if (duration < vmminsecs)
3191                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3192                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3193                 else
3194                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3195         } else
3196                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3197 leave_vm_out:
3198         free_user(vmu);
3199         
3200         return res;
3201 }
3202
3203 #ifndef IMAP_STORAGE
3204 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3205 {
3206         /* we know max messages, so stop process when number is hit */
3207
3208         int x,dest;
3209         char sfn[PATH_MAX];
3210         char dfn[PATH_MAX];
3211
3212         if (vm_lock_path(dir))
3213                 return ERROR_LOCK_PATH;
3214
3215         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3216                 make_file(sfn, sizeof(sfn), dir, x);
3217                 if (EXISTS(dir, x, sfn, NULL)) {
3218                         
3219                         if (x != dest) {
3220                                 make_file(dfn, sizeof(dfn), dir, dest);
3221                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3222                         }
3223                         
3224                         dest++;
3225                 }
3226         }
3227         ast_unlock_path(dir);
3228
3229         return 0;
3230 }
3231 #endif
3232
3233 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3234 {
3235         int d;
3236         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3237         return d;
3238 }
3239
3240 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3241 {
3242 #ifdef IMAP_STORAGE
3243         /* we must use mbox(x) folder names, and copy the message there */
3244         /* simple. huh? */
3245         char dbox[256];
3246         long res;
3247         char sequence[10];
3248
3249         /* if save to Old folder, just leave in INBOX */
3250         if (box == 1) return 10;
3251         /* get the real IMAP message number for this message */
3252         sprintf(sequence,"%ld",vms->msgArray[msg]);
3253         imap_mailbox_name(dbox, vms, box, 1);
3254         if (option_debug > 2)
3255                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3256         res = mail_copy(vms->mailstream, sequence, dbox);
3257         if (res == 1) return 0;
3258         return 1;
3259 #else
3260         char *dir = vms->curdir;
3261         char *username = vms->username;
3262         char *context = vmu->context;
3263         char sfn[PATH_MAX];
3264         char dfn[PATH_MAX];
3265         char ddir[PATH_MAX];
3266         const char *dbox = mbox(box);
3267         int x;
3268         make_file(sfn, sizeof(sfn), dir, msg);
3269         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3270
3271         if (vm_lock_path(ddir))
3272                 return ERROR_LOCK_PATH;
3273
3274         x = last_message_index(vmu, ddir) + 1;
3275         make_file(dfn, sizeof(dfn), ddir, x);
3276
3277         if (x >= vmu->maxmsg) {
3278                 ast_unlock_path(ddir);
3279                 return -1;
3280         }
3281         if (strcmp(sfn, dfn)) {
3282                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3283         }
3284         ast_unlock_path(ddir);
3285 #endif
3286         return 0;
3287 }
3288
3289 static int adsi_logo(unsigned char *buf)
3290 {
3291         int bytes = 0;
3292         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3293         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3294         return bytes;
3295 }
3296
3297 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3298 {
3299         unsigned char buf[256];
3300         int bytes=0;
3301         int x;
3302         char num[5];
3303
3304         *useadsi = 0;
3305         bytes += ast_adsi_data_mode(buf + bytes);
3306         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3307
3308         bytes = 0;
3309         bytes += adsi_logo(buf);
3310         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3311 #ifdef DISPLAY
3312         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3313 #endif
3314         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3315         bytes += ast_adsi_data_mode(buf + bytes);
3316         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3317
3318         if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3319                 bytes = 0;
3320                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3321                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3322                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3323                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3324                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3325                 return 0;
3326         }
3327
3328 #ifdef DISPLAY
3329         /* Add a dot */
3330         bytes = 0;
3331         bytes += ast_adsi_logo(buf);
3332         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3333         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3334         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3335         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3336 #endif
3337         bytes = 0;
3338         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3339         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3340         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3341         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3342         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3343         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3344         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3345
3346 #ifdef DISPLAY
3347         /* Add another dot */
3348         bytes = 0;
3349         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
3350         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3351
3352         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3353         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3354 #endif
3355
3356         bytes = 0;
3357         /* These buttons we load but don't use yet */
3358         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3359         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3360         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3361         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3362         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3363         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3364         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3365
3366 #ifdef DISPLAY
3367         /* Add another dot */
3368         bytes = 0;
3369         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
3370         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3371         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3372 #endif
3373
3374         bytes = 0;
3375         for (x=0;x<5;x++) {
3376                 snprintf(num, sizeof(num), "%d", x);
3377                 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3378         }
3379         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3380         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3381
3382 #ifdef DISPLAY
3383         /* Add another dot */
3384         bytes = 0;
3385         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
3386         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3387         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3388 #endif
3389
3390         if (ast_adsi_end_download(chan)) {
3391                 bytes = 0;
3392                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3393                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3394                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3395                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3396                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3397                 return 0;
3398         }
3399         bytes = 0;
3400         bytes += ast_adsi_download_disconnect(buf + bytes);
3401         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3402         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3403
3404         if (option_debug)
3405                 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3406
3407 #ifdef DISPLAY
3408         /* Add last dot */
3409         bytes = 0;
3410         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
3411         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3412 #endif
3413         if (option_debug)
3414                 ast_log(LOG_DEBUG, "Restarting session...\n");
3415
3416         bytes = 0;
3417         /* Load the session now */
3418         if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3419                 *useadsi = 1;
3420                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3421         } else
3422                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3423
3424         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3425         return 0;
3426 }
3427
3428 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3429 {
3430         int x;
3431         if (!ast_adsi_available(chan))
3432                 return;
3433         x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3434         if (x < 0)
3435                 return;
3436         if (!x) {
3437                 if (adsi_load_vmail(chan, useadsi)) {
3438                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3439                         return;
3440                 }
3441         } else
3442                 *useadsi = 1;
3443 }
3444
3445 static void adsi_login(struct ast_channel *chan)
3446 {
3447         unsigned char buf[256];
3448         int bytes=0;
3449         unsigned char keys[8];
3450         int x;
3451         if (!ast_adsi_available(chan))
3452                 return;
3453
3454         for (x=0;x<8;x++)
3455                 keys[x] = 0;
3456         /* Set one key for next */
3457         keys[3] = ADSI_KEY_APPS + 3;