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