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