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