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