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