Found a grievous logical error in get_vm_state_by_imapuser.
[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 #define PWDCHANGE_INTERNAL (1 << 1)
459 #define PWDCHANGE_EXTERNAL (1 << 2)
460 static int pwdchange = PWDCHANGE_INTERNAL;
461
462 #ifdef ODBC_STORAGE
463 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
464 #else
465 # ifdef IMAP_STORAGE
466 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
467 # else
468 # define tdesc "Comedian Mail (Voicemail System)"
469 # endif
470 #endif
471
472 static char userscontext[AST_MAX_EXTENSION] = "default";
473
474 static char *addesc = "Comedian Mail";
475
476 static char *synopsis_vm = "Leave a Voicemail message";
477
478 static char *descrip_vm =
479         "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
480         "application allows the calling party to leave a message for the specified\n"
481         "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
482         "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
483         "specified mailbox does not exist.\n"
484         "  The Voicemail application will exit if any of the following DTMF digits are\n"
485         "received:\n"
486         "    0 - Jump to the 'o' extension in the current dialplan context.\n"
487         "    * - Jump to the 'a' extension in the current dialplan context.\n"
488         "  This application will set the following channel variable upon completion:\n"
489         "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
490         "               application. The possible values are:\n"
491         "               SUCCESS | USEREXIT | FAILED\n\n"
492         "  Options:\n"
493         "    b    - Play the 'busy' greeting to the calling party.\n"
494         "    g(#) - Use the specified amount of gain when recording the voicemail\n"
495         "           message. The units are whole-number decibels (dB).\n"
496         "    s    - Skip the playback of instructions for leaving a message to the\n"
497         "           calling party.\n"
498         "    u    - Play the 'unavailable greeting.\n";
499
500 static char *synopsis_vmain = "Check Voicemail messages";
501
502 static char *descrip_vmain =
503         "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
504         "calling party to check voicemail messages. A specific mailbox, and optional\n"
505         "corresponding context, may be specified. If a mailbox is not provided, the\n"
506         "calling party will be prompted to enter one. If a context is not specified,\n"
507         "the 'default' context will be used.\n\n"
508         "  Options:\n"
509         "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
510         "           is entered by the caller.\n"
511         "    g(#) - Use the specified amount of gain when recording a voicemail\n"
512         "           message. The units are whole-number decibels (dB).\n"
513         "    s    - Skip checking the passcode for the mailbox.\n"
514         "    a(#) - Skip folder prompt and go directly to folder specified.\n"
515         "           Defaults to INBOX\n";
516
517 static char *synopsis_vm_box_exists =
518 "Check to see if Voicemail mailbox exists";
519
520 static char *descrip_vm_box_exists =
521         "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
522         "mailbox exists. If no voicemail context is specified, the 'default' context\n"
523         "will be used.\n"
524         "  This application will set the following channel variable upon completion:\n"
525         "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
526         "                        MailboxExists application. Possible values include:\n"
527         "                        SUCCESS | FAILED\n\n"
528         "  Options: (none)\n";
529
530 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
531
532 static char *descrip_vmauthenticate =
533         "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
534         "same way as the Authenticate application, but the passwords are taken from\n"
535         "voicemail.conf.\n"
536         "  If the mailbox is specified, only that mailbox's password will be considered\n"
537         "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
538         "be set with the authenticated mailbox.\n\n"
539         "  Options:\n"
540         "    s - Skip playing the initial prompts.\n";
541
542 /* Leave a message */
543 static char *app = "VoiceMail";
544
545 /* Check mail, control, etc */
546 static char *app2 = "VoiceMailMain";
547
548 static char *app3 = "MailboxExists";
549 static char *app4 = "VMAuthenticate";
550
551 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
552 static AST_LIST_HEAD_STATIC(zones, vm_zone);
553 static int maxsilence;
554 static int maxmsg;
555 static int silencethreshold = 128;
556 static char serveremail[80];
557 static char mailcmd[160];       /* Configurable mail cmd */
558 static char externnotify[160]; 
559 static struct ast_smdi_interface *smdi_iface = NULL;
560 static char vmfmts[80];
561 static double volgain;
562 static int vmminsecs;
563 static int vmmaxsecs;
564 static int maxgreet;
565 static int skipms;
566 static int maxlogins;
567
568 /*! Poll mailboxes for changes since there is something external to
569  *  app_voicemail that may change them. */
570 static unsigned int poll_mailboxes;
571
572 /*! Polling frequency */
573 static unsigned int poll_freq;
574 /*! By default, poll every 30 seconds */
575 #define DEFAULT_POLL_FREQ 30
576
577 AST_MUTEX_DEFINE_STATIC(poll_lock);
578 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
579 static pthread_t poll_thread = AST_PTHREADT_NULL;
580 static unsigned char poll_thread_run;
581
582 /*! Subscription to ... MWI event subscriptions */
583 static struct ast_event_sub *mwi_sub_sub;
584 /*! Subscription to ... MWI event un-subscriptions */
585 static struct ast_event_sub *mwi_unsub_sub;
586
587 /*!
588  * \brief An MWI subscription
589  *
590  * This is so we can keep track of which mailboxes are subscribed to.
591  * This way, we know which mailboxes to poll when the pollmailboxes
592  * option is being used.
593  */
594 struct mwi_sub {
595         AST_RWLIST_ENTRY(mwi_sub) entry;
596         int old_new;
597         int old_old;
598         uint32_t uniqueid;
599         char mailbox[1];
600 };
601
602 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
603
604 /* custom audio control prompts for voicemail playback */
605 static char listen_control_forward_key[12];
606 static char listen_control_reverse_key[12];
607 static char listen_control_pause_key[12];
608 static char listen_control_restart_key[12];
609 static char listen_control_stop_key[12];
610
611 /* custom password sounds */
612 static char vm_password[80] = "vm-password";
613 static char vm_newpassword[80] = "vm-newpassword";
614 static char vm_passchanged[80] = "vm-passchanged";
615 static char vm_reenterpassword[80] = "vm-reenterpassword";
616 static char vm_mismatch[80] = "vm-mismatch";
617
618 static struct ast_flags globalflags = {0};
619
620 static int saydurationminfo;
621
622 static char dialcontext[AST_MAX_CONTEXT];
623 static char callcontext[AST_MAX_CONTEXT];
624 static char exitcontext[AST_MAX_CONTEXT];
625
626 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
627
628
629 static char *emailbody = NULL;
630 static char *emailsubject = NULL;
631 static char *pagerbody = NULL;
632 static char *pagersubject = NULL;
633 static char fromstring[100];
634 static char pagerfromstring[100];
635 static char emailtitle[100];
636 static char charset[32] = "ISO-8859-1";
637
638 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
639 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
640 static int adsiver = 1;
641 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
642
643 /* Forward declarations - generic */
644 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
645 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
646 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
647 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
648                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
649                         signed char record_gain, struct vm_state *vms);
650 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
651 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
652 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
653 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
654 static void apply_options(struct ast_vm_user *vmu, const char *options);
655 static int is_valid_dtmf(const char *key);
656
657 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
658 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
659 #endif
660
661
662
663 static void populate_defaults(struct ast_vm_user *vmu)
664 {
665         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
666         if (saydurationminfo)
667                 vmu->saydurationm = saydurationminfo;
668         if (callcontext)
669                 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
670         if (dialcontext)
671                 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
672         if (exitcontext)
673                 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
674         if (vmmaxsecs)
675                 vmu->maxsecs = vmmaxsecs;
676         if (maxmsg)
677                 vmu->maxmsg = maxmsg;
678         vmu->volgain = volgain;
679 }
680
681 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
682 {
683         int x;
684         if (!strcasecmp(var, "attach")) {
685                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
686         } else if (!strcasecmp(var, "attachfmt")) {
687                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
688         } else if (!strcasecmp(var, "serveremail")) {
689                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
690         } else if (!strcasecmp(var, "language")) {
691                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
692         } else if (!strcasecmp(var, "tz")) {
693                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
694 #ifdef IMAP_STORAGE
695         } else if (!strcasecmp(var, "imapuser")) {
696                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
697         } else if (!strcasecmp(var, "imappassword")) {
698                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
699 #endif
700         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
701                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
702         } else if (!strcasecmp(var, "saycid")){
703                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
704         } else if (!strcasecmp(var,"sendvoicemail")){
705                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
706         } else if (!strcasecmp(var, "review")){
707                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
708         } else if (!strcasecmp(var, "tempgreetwarn")){
709                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
710         } else if (!strcasecmp(var, "operator")){
711                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
712         } else if (!strcasecmp(var, "envelope")){
713                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
714         } else if (!strcasecmp(var, "sayduration")){
715                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
716         } else if (!strcasecmp(var, "saydurationm")){
717                 if (sscanf(value, "%d", &x) == 1) {
718                         vmu->saydurationm = x;
719                 } else {
720                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
721                 }
722         } else if (!strcasecmp(var, "forcename")){
723                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
724         } else if (!strcasecmp(var, "forcegreetings")){
725                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
726         } else if (!strcasecmp(var, "callback")) {
727                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
728         } else if (!strcasecmp(var, "dialout")) {
729                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
730         } else if (!strcasecmp(var, "exitcontext")) {
731                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
732         } else if (!strcasecmp(var, "maxmessage")) {
733                 if (vmu->maxsecs <= 0) {
734                         ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
735                         vmu->maxsecs = vmmaxsecs;
736                 } else {
737                         vmu->maxsecs = atoi(value);
738                 }
739         } else if (!strcasecmp(var, "maxmsg")) {
740                 vmu->maxmsg = atoi(value);
741                 if (vmu->maxmsg <= 0) {
742                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
743                         vmu->maxmsg = MAXMSG;
744                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
745                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
746                         vmu->maxmsg = MAXMSGLIMIT;
747                 }
748         } else if (!strcasecmp(var, "volgain")) {
749                 sscanf(value, "%lf", &vmu->volgain);
750         } else if (!strcasecmp(var, "options")) {
751                 apply_options(vmu, value);
752         }
753 }
754
755 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
756 {
757         int res;
758         if (!ast_strlen_zero(vmu->uniqueid)) {
759                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
760                 if (res > 0) {
761                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
762                         res = 0;
763                 } else if (!res) {
764                         res = -1;
765                 }
766                 return res;
767         }
768         return -1;
769 }
770
771 static void apply_options(struct ast_vm_user *vmu, const char *options)
772 {       /* Destructively Parse options and apply */
773         char *stringp;
774         char *s;
775         char *var, *value;
776         stringp = ast_strdupa(options);
777         while ((s = strsep(&stringp, "|"))) {
778                 value = s;
779                 if ((var = strsep(&value, "=")) && value) {
780                         apply_option(vmu, var, value);
781                 }
782         }       
783 }
784
785 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
786 {
787         struct ast_variable *tmp;
788         tmp = var;
789         while (tmp) {
790                 if (!strcasecmp(tmp->name, "vmsecret")) {
791                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
792                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
793                         if (ast_strlen_zero(retval->password))
794                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
795                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
796                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
797                 } else if (!strcasecmp(tmp->name, "pager")) {
798                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
799                 } else if (!strcasecmp(tmp->name, "email")) {
800                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
801                 } else if (!strcasecmp(tmp->name, "fullname")) {
802                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
803                 } else if (!strcasecmp(tmp->name, "context")) {
804                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
805 #ifdef IMAP_STORAGE
806                 } else if (!strcasecmp(tmp->name, "imapuser")) {
807                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
808                 } else if (!strcasecmp(tmp->name, "imappassword")) {
809                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
810 #endif
811                 } else
812                         apply_option(retval, tmp->name, tmp->value);
813                 tmp = tmp->next;
814         } 
815 }
816
817 static int is_valid_dtmf(const char *key)
818 {
819         int i;
820         char *local_key = ast_strdupa(key);
821
822         for(i = 0; i < strlen(key); ++i) {
823                 if(!strchr(VALID_DTMF, *local_key)) {
824                         ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
825                         return 0;
826                 }
827                 local_key++;
828         }
829         return 1;
830 }
831
832 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
833 {
834         struct ast_variable *var;
835         struct ast_vm_user *retval;
836
837         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
838                 if (!ivm)
839                         ast_set_flag(retval, VM_ALLOCED);       
840                 else
841                         memset(retval, 0, sizeof(*retval));
842                 if (mailbox) 
843                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
844                 populate_defaults(retval);
845                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
846                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
847                 else
848                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
849                 if (var) {
850                         apply_options_full(retval, var);
851                         ast_variables_destroy(var);
852                 } else { 
853                         if (!ivm) 
854                                 ast_free(retval);
855                         retval = NULL;
856                 }       
857         } 
858         return retval;
859 }
860
861 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
862 {
863         /* This function could be made to generate one from a database, too */
864         struct ast_vm_user *vmu=NULL, *cur;
865         AST_LIST_LOCK(&users);
866
867         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
868                 context = "default";
869
870         AST_LIST_TRAVERSE(&users, cur, list) {
871                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
872                         break;
873                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
874                         break;
875         }
876         if (cur) {
877                 /* Make a copy, so that on a reload, we have no race */
878                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
879                         memcpy(vmu, cur, sizeof(*vmu));
880                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
881                         AST_LIST_NEXT(vmu, list) = NULL;
882                 }
883         } else
884                 vmu = find_user_realtime(ivm, context, mailbox);
885         AST_LIST_UNLOCK(&users);
886         return vmu;
887 }
888
889 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
890 {
891         /* This function could be made to generate one from a database, too */
892         struct ast_vm_user *cur;
893         int res = -1;
894         AST_LIST_LOCK(&users);
895         AST_LIST_TRAVERSE(&users, cur, list) {
896                 if ((!context || !strcasecmp(context, cur->context)) &&
897                         (!strcasecmp(mailbox, cur->mailbox)))
898                                 break;
899         }
900         if (cur) {
901                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
902                 res = 0;
903         }
904         AST_LIST_UNLOCK(&users);
905         return res;
906 }
907
908 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
909 {
910         struct ast_config   *cfg=NULL;
911         struct ast_variable *var=NULL;
912         struct ast_category *cat=NULL;
913         char *category=NULL, *value=NULL, *new=NULL;
914         const char *tmp=NULL;
915                                         
916         if (!change_password_realtime(vmu, newpassword))
917                 return;
918
919         /* check voicemail.conf */
920         if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
921                 while ((category = ast_category_browse(cfg, category))) {
922                         if (!strcasecmp(category, vmu->context)) {
923                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
924                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
925                                         break;
926                                 }
927                                 value = strstr(tmp,",");
928                                 if (!value) {
929                                         ast_log(LOG_WARNING, "variable has bad format.\n");
930                                         break;
931                                 }
932                                 new = alloca((strlen(value)+strlen(newpassword)+1));
933                                 sprintf(new,"%s%s", newpassword, value);
934                                 if (!(cat = ast_category_get(cfg, category))) {
935                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
936                                         break;
937                                 }
938                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
939                         }
940                 }
941                 /* save the results */
942                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
943                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
944                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
945         }
946         category = NULL;
947         var = NULL;
948         /* check users.conf and update the password stored for the mailbox*/
949         /* if no vmsecret entry exists create one. */
950         if ((cfg = ast_config_load_with_comments("users.conf"))) {
951                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
952                 while ((category = ast_category_browse(cfg, category))) {
953                         ast_debug(4, "users.conf: %s\n", category);
954                         if (!strcasecmp(category, vmu->mailbox)) {
955                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
956                                         ast_debug(3, "looks like we need to make vmsecret!\n");
957                                         var = ast_variable_new("vmsecret", newpassword);
958                                 } 
959                                 new = alloca(strlen(newpassword)+1);
960                                 sprintf(new, "%s", newpassword);
961                                 if (!(cat = ast_category_get(cfg, category))) {
962                                         ast_debug(4, "failed to get category!\n");
963                                         break;
964                                 }
965                                 if (!var)               
966                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
967                                 else
968                                         ast_variable_append(cat, var);
969                         }
970                 }
971                 /* save the results and clean things up */
972                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
973                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
974                 config_text_file_save("users.conf", cfg, "AppVoicemail");
975         }
976 }
977
978 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
979 {
980         char buf[255];
981         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
982         if (!ast_safe_system(buf))
983                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
984 }
985
986 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
987 {
988         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
989 }
990
991 #ifdef IMAP_STORAGE
992 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
993 {
994         int res;
995         if ((res = ast_mkdir(dir, 01777))) {
996                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
997                 return sprintf(dest, "%s/msg%04d", dir, num);
998         }
999         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
1000         return sprintf(dest, "%s/msg%04d", dir, num);
1001 }
1002
1003 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1004 {
1005         unsigned long messageNum = 0;
1006         char arg[10];
1007
1008         /* find real message number based on msgnum */
1009         /* this may be an index into vms->msgArray based on the msgnum. */
1010
1011         messageNum = vms->msgArray[msgnum];
1012         if (messageNum == 0) {
1013                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1014                 return;
1015         }
1016         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1017         /* delete message */
1018         sprintf (arg,"%lu",messageNum);
1019         mail_setflag (vms->mailstream,arg,"\\DELETED");
1020 }
1021
1022 #endif
1023 static int make_file(char *dest, int len, char *dir, int num)
1024 {
1025         return snprintf(dest, len, "%s/msg%04d", dir, num);
1026 }
1027
1028 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1029  * \param dest    String. base directory.
1030  * \param len     Length of dest.
1031  * \param context String. Ignored if is null or empty string.
1032  * \param ext     String. Ignored if is null or empty string.
1033  * \param folder  String. Ignored if is null or empty string. 
1034  * \return -1 on failure, 0 on success.
1035  */
1036 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1037 {
1038         mode_t  mode = VOICEMAIL_DIR_MODE;
1039         int res;
1040
1041         make_dir(dest, len, context, ext, folder);
1042         if ((res = ast_mkdir(dest, mode))) {
1043                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1044                 return -1;
1045         }
1046         return 0;
1047 }
1048
1049 /*! \brief Lock file path
1050     only return failure if ast_lock_path returns 'timeout',
1051    not if the path does not exist or any other reason
1052 */
1053 static int vm_lock_path(const char *path)
1054 {
1055         switch (ast_lock_path(path)) {
1056         case AST_LOCK_TIMEOUT:
1057                 return -1;
1058         default:
1059                 return 0;
1060         }
1061 }
1062
1063
1064 #ifdef ODBC_STORAGE
1065 static int retrieve_file(char *dir, int msgnum)
1066 {
1067         int x = 0;
1068         int res;
1069         int fd=-1;
1070         size_t fdlen = 0;
1071         void *fdm = MAP_FAILED;
1072         SQLSMALLINT colcount=0;
1073         SQLHSTMT stmt;
1074         char sql[PATH_MAX];
1075         char fmt[80]="";
1076         char *c;
1077         char coltitle[256];
1078         SQLSMALLINT collen;
1079         SQLSMALLINT datatype;
1080         SQLSMALLINT decimaldigits;
1081         SQLSMALLINT nullable;
1082         SQLULEN colsize;
1083         SQLLEN colsize2;
1084         FILE *f=NULL;
1085         char rowdata[80];
1086         char fn[PATH_MAX];
1087         char full_fn[PATH_MAX];
1088         char msgnums[80];
1089         
1090         struct odbc_obj *obj;
1091         obj = ast_odbc_request_obj(odbc_database, 0);
1092         if (obj) {
1093                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1094                 c = strchr(fmt, '|');
1095                 if (c)
1096                         *c = '\0';
1097                 if (!strcasecmp(fmt, "wav49"))
1098                         strcpy(fmt, "WAV");
1099                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1100                 if (msgnum > -1)
1101                         make_file(fn, sizeof(fn), dir, msgnum);
1102                 else
1103                         ast_copy_string(fn, dir, sizeof(fn));
1104                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1105                 
1106                 if (!(f = fopen(full_fn, "w+"))) {
1107                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1108                         goto yuck;
1109                 }
1110                 
1111                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1112                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1113                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1114                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1115                         ast_odbc_release_obj(obj);
1116                         goto yuck;
1117                 }
1118                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1119                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1120                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1121                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1122                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1123                         ast_odbc_release_obj(obj);
1124                         goto yuck;
1125                 }
1126                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1127                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1128                 res = ast_odbc_smart_execute(obj, stmt);
1129                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1130                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1131                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1132                         ast_odbc_release_obj(obj);
1133                         goto yuck;
1134                 }
1135                 res = SQLFetch(stmt);
1136                 if (res == SQL_NO_DATA) {
1137                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1138                         ast_odbc_release_obj(obj);
1139                         goto yuck;
1140                 }
1141                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1142                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1143                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1144                         ast_odbc_release_obj(obj);
1145                         goto yuck;
1146                 }
1147                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1148                 if (fd < 0) {
1149                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1150                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1151                         ast_odbc_release_obj(obj);
1152                         goto yuck;
1153                 }
1154                 res = SQLNumResultCols(stmt, &colcount);
1155                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1156                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1157                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1158                         ast_odbc_release_obj(obj);
1159                         goto yuck;
1160                 }
1161                 if (f) 
1162                         fprintf(f, "[message]\n");
1163                 for (x=0;x<colcount;x++) {
1164                         rowdata[0] = '\0';
1165                         collen = sizeof(coltitle);
1166                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1167                                                 &datatype, &colsize, &decimaldigits, &nullable);
1168                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1169                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1170                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1171                                 ast_odbc_release_obj(obj);
1172                                 goto yuck;
1173                         }
1174                         if (!strcasecmp(coltitle, "recording")) {
1175                                 off_t offset;
1176                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1177                                 fdlen = colsize2;
1178                                 if (fd > -1) {
1179                                         char tmp[1]="";
1180                                         lseek(fd, fdlen - 1, SEEK_SET);
1181                                         if (write(fd, tmp, 1) != 1) {
1182                                                 close(fd);
1183                                                 fd = -1;
1184                                                 continue;
1185                                         }
1186                                         /* Read out in small chunks */
1187                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1188                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1189                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1190                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1191                                                         ast_odbc_release_obj(obj);
1192                                                         goto yuck;
1193                                                 } else {
1194                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1195                                                         munmap(fdm, CHUNKSIZE);
1196                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1197                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1198                                                                 unlink(full_fn);
1199                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1200                                                                 ast_odbc_release_obj(obj);
1201                                                                 goto yuck;
1202                                                         }
1203                                                 }
1204                                         }
1205                                         truncate(full_fn, fdlen);
1206                                 }
1207                         } else {
1208                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1209                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1210                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1211                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1212                                         ast_odbc_release_obj(obj);
1213                                         goto yuck;
1214                                 }
1215                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1216                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1217                         }
1218                 }
1219                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1220                 ast_odbc_release_obj(obj);
1221         } else
1222                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1223 yuck:   
1224         if (f)
1225                 fclose(f);
1226         if (fd > -1)
1227                 close(fd);
1228         return x - 1;
1229 }
1230
1231 static int remove_file(char *dir, int msgnum)
1232 {
1233         char fn[PATH_MAX];
1234         char full_fn[PATH_MAX];
1235         char msgnums[80];
1236         
1237         if (msgnum > -1) {
1238                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1239                 make_file(fn, sizeof(fn), dir, msgnum);
1240         } else
1241                 ast_copy_string(fn, dir, sizeof(fn));
1242         ast_filedelete(fn, NULL);       
1243         if (ast_check_realtime("voicemail_data")) {
1244                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1245         }
1246         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1247         unlink(full_fn);
1248         return 0;
1249 }
1250
1251 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1252 {
1253         int x = 0;
1254         int res;
1255         SQLHSTMT stmt;
1256         char sql[PATH_MAX];
1257         char rowdata[20];
1258         
1259         struct odbc_obj *obj;
1260         obj = ast_odbc_request_obj(odbc_database, 0);
1261         if (obj) {
1262                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1263                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1264                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1265                         ast_odbc_release_obj(obj);
1266                         goto yuck;
1267                 }
1268                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1269                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1270                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1271                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1272                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1273                         ast_odbc_release_obj(obj);
1274                         goto yuck;
1275                 }
1276                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1277                 res = ast_odbc_smart_execute(obj, stmt);
1278                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1279                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1280                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1281                         ast_odbc_release_obj(obj);
1282                         goto yuck;
1283                 }
1284                 res = SQLFetch(stmt);
1285                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1286                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1287                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1288                         ast_odbc_release_obj(obj);
1289                         goto yuck;
1290                 }
1291                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1292                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1293                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1294                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1295                         ast_odbc_release_obj(obj);
1296                         goto yuck;
1297                 }
1298                 if (sscanf(rowdata, "%d", &x) != 1)
1299                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1300                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1301                 ast_odbc_release_obj(obj);
1302         } else
1303                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1304 yuck:   
1305         return x - 1;
1306 }
1307
1308 static int message_exists(char *dir, int msgnum)
1309 {
1310         int x = 0;
1311         int res;
1312         SQLHSTMT stmt;
1313         char sql[PATH_MAX];
1314         char rowdata[20];
1315         char msgnums[20];
1316         
1317         struct odbc_obj *obj;
1318         obj = ast_odbc_request_obj(odbc_database, 0);
1319         if (obj) {
1320                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1321                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1322                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1323                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1324                         ast_odbc_release_obj(obj);
1325                         goto yuck;
1326                 }
1327                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1328                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1329                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1330                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1331                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1332                         ast_odbc_release_obj(obj);
1333                         goto yuck;
1334                 }
1335                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1336                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1337                 res = ast_odbc_smart_execute(obj, stmt);
1338                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1339                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1340                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1341                         ast_odbc_release_obj(obj);
1342                         goto yuck;
1343                 }
1344                 res = SQLFetch(stmt);
1345                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1346                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1347                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1348                         ast_odbc_release_obj(obj);
1349                         goto yuck;
1350                 }
1351                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1352                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1353                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1354                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1355                         ast_odbc_release_obj(obj);
1356                         goto yuck;
1357                 }
1358                 if (sscanf(rowdata, "%d", &x) != 1)
1359                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1360                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1361                 ast_odbc_release_obj(obj);
1362         } else
1363                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1364 yuck:   
1365         return x;
1366 }
1367
1368 static int count_messages(struct ast_vm_user *vmu, char *dir)
1369 {
1370         return last_message_index(vmu, dir) + 1;
1371 }
1372
1373 static void delete_file(char *sdir, int smsg)
1374 {
1375         int res;
1376         SQLHSTMT stmt;
1377         char sql[PATH_MAX];
1378         char msgnums[20];
1379         
1380         struct odbc_obj *obj;
1381         obj = ast_odbc_request_obj(odbc_database, 0);
1382         if (obj) {
1383                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1384                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1385                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1386                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1387                         ast_odbc_release_obj(obj);
1388                         goto yuck;
1389                 }
1390                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1391                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1392                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1393                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1394                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1395                         ast_odbc_release_obj(obj);
1396                         goto yuck;
1397                 }
1398                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1399                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1400                 res = ast_odbc_smart_execute(obj, stmt);
1401                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1402                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1403                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1404                         ast_odbc_release_obj(obj);
1405                         goto yuck;
1406                 }
1407                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1408                 ast_odbc_release_obj(obj);
1409         } else
1410                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1411 yuck:
1412         return; 
1413 }
1414
1415 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1416 {
1417         int res;
1418         SQLHSTMT stmt;
1419         char sql[512];
1420         char msgnums[20];
1421         char msgnumd[20];
1422         struct odbc_obj *obj;
1423
1424         delete_file(ddir, dmsg);
1425         obj = ast_odbc_request_obj(odbc_database, 0);
1426         if (obj) {
1427                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1428                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1429                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1430                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1431                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1432                         ast_odbc_release_obj(obj);
1433                         goto yuck;
1434                 }
1435                 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); 
1436                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1437                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1438                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1439                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1440                         ast_odbc_release_obj(obj);
1441                         goto yuck;
1442                 }
1443                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1444                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1445                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1446                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1447                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1448                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1449                 res = ast_odbc_smart_execute(obj, stmt);
1450                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1451                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1452                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1453                         ast_odbc_release_obj(obj);
1454                         goto yuck;
1455                 }
1456                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1457                 ast_odbc_release_obj(obj);
1458         } else
1459                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1460 yuck:
1461         return; 
1462 }
1463
1464 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1465 {
1466         int x = 0;
1467         int res;
1468         int fd = -1;
1469         void *fdm = MAP_FAILED;
1470         size_t fdlen = -1;
1471         SQLHSTMT stmt;
1472         SQLLEN len;
1473         char sql[PATH_MAX];
1474         char msgnums[20];
1475         char fn[PATH_MAX];
1476         char full_fn[PATH_MAX];
1477         char fmt[80]="";
1478         char *c;
1479         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1480         const char *category = "";
1481         struct ast_config *cfg=NULL;
1482         struct odbc_obj *obj;
1483
1484         delete_file(dir, msgnum);
1485         obj = ast_odbc_request_obj(odbc_database, 0);
1486         if (obj) {
1487                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1488                 c = strchr(fmt, '|');
1489                 if (c)
1490                         *c = '\0';
1491                 if (!strcasecmp(fmt, "wav49"))
1492                         strcpy(fmt, "WAV");
1493                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1494                 if (msgnum > -1)
1495                         make_file(fn, sizeof(fn), dir, msgnum);
1496                 else
1497                         ast_copy_string(fn, dir, sizeof(fn));
1498                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1499                 cfg = ast_config_load(full_fn);
1500                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1501                 fd = open(full_fn, O_RDWR);
1502                 if (fd < 0) {
1503                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1504                         ast_odbc_release_obj(obj);
1505                         goto yuck;
1506                 }
1507                 if (cfg) {
1508                         context = ast_variable_retrieve(cfg, "message", "context");
1509                         if (!context) context = "";
1510                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1511                         if (!macrocontext) macrocontext = "";
1512                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1513                         if (!callerid) callerid = "";
1514                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1515                         if (!origtime) origtime = "";
1516                         duration = ast_variable_retrieve(cfg, "message", "duration");
1517                         if (!duration) duration = "";
1518                         category = ast_variable_retrieve(cfg, "message", "category");
1519                         if (!category) category = "";
1520                 }
1521                 fdlen = lseek(fd, 0, SEEK_END);
1522                 lseek(fd, 0, SEEK_SET);
1523                 printf("Length is %zd\n", fdlen);
1524                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1525                 if (fdm == MAP_FAILED) {
1526                         ast_log(LOG_WARNING, "Memory map failed!\n");
1527                         ast_odbc_release_obj(obj);
1528                         goto yuck;
1529                 } 
1530                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1531                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1532                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1533                         ast_odbc_release_obj(obj);
1534                         goto yuck;
1535                 }
1536                 if (!ast_strlen_zero(category)) 
1537                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1538                 else
1539                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1540                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1541                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1542                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1543                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1544                         ast_odbc_release_obj(obj);
1545                         goto yuck;
1546                 }
1547                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1548                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1549                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1550                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1551                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1552                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1553                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1554                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1555                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1556                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1557                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1558                 if (!ast_strlen_zero(category))
1559                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1560                 res = ast_odbc_smart_execute(obj, stmt);
1561                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1562                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1563                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1564                         ast_odbc_release_obj(obj);
1565                         goto yuck;
1566                 }
1567                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1568                 ast_odbc_release_obj(obj);
1569         } else
1570                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1571 yuck:   
1572         if (cfg)
1573                 ast_config_destroy(cfg);
1574         if (fdm != MAP_FAILED)
1575                 munmap(fdm, fdlen);
1576         if (fd > -1)
1577                 close(fd);
1578         return x;
1579 }
1580
1581 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1582 {
1583         int res;
1584         SQLHSTMT stmt;
1585         char sql[PATH_MAX];
1586         char msgnums[20];
1587         char msgnumd[20];
1588         struct odbc_obj *obj;
1589
1590         delete_file(ddir, dmsg);
1591         obj = ast_odbc_request_obj(odbc_database, 0);
1592         if (obj) {
1593                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1594                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1595                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1596                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1597                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1598                         ast_odbc_release_obj(obj);
1599                         goto yuck;
1600                 }
1601                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1602                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1603                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1604                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1605                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1606                         ast_odbc_release_obj(obj);
1607                         goto yuck;
1608                 }
1609                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1610                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1611                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1612                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1613                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1614                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1615                 res = ast_odbc_smart_execute(obj, stmt);
1616                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1617                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1618                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1619                         ast_odbc_release_obj(obj);
1620                         goto yuck;
1621                 }
1622                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1623                 ast_odbc_release_obj(obj);
1624         } else
1625                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1626 yuck:
1627         return; 
1628 }
1629
1630 #else
1631 #ifndef IMAP_STORAGE
1632 static int count_messages(struct ast_vm_user *vmu, char *dir)
1633 {
1634         /* Find all .txt files - even if they are not in sequence from 0000 */
1635
1636         int vmcount = 0;
1637         DIR *vmdir = NULL;
1638         struct dirent *vment = NULL;
1639
1640         if (vm_lock_path(dir))
1641                 return ERROR_LOCK_PATH;
1642
1643         if ((vmdir = opendir(dir))) {
1644                 while ((vment = readdir(vmdir))) {
1645                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1646                                 vmcount++;
1647                 }
1648                 closedir(vmdir);
1649         }
1650         ast_unlock_path(dir);
1651         
1652         return vmcount;
1653 }
1654
1655 static void rename_file(char *sfn, char *dfn)
1656 {
1657         char stxt[PATH_MAX];
1658         char dtxt[PATH_MAX];
1659         ast_filerename(sfn,dfn,NULL);
1660         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1661         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1662         if (ast_check_realtime("voicemail_data")) {
1663                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1664         }
1665         rename(stxt, dtxt);
1666 }
1667
1668 static int copy(char *infile, char *outfile)
1669 {
1670         int ifd;
1671         int ofd;
1672         int res;
1673         int len;
1674         char buf[4096];
1675
1676 #ifdef HARDLINK_WHEN_POSSIBLE
1677         /* Hard link if possible; saves disk space & is faster */
1678         if (link(infile, outfile)) {
1679 #endif
1680                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1681                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1682                         return -1;
1683                 }
1684                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1685                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1686                         close(ifd);
1687                         return -1;
1688                 }
1689                 do {
1690                         len = read(ifd, buf, sizeof(buf));
1691                         if (len < 0) {
1692                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1693                                 close(ifd);
1694                                 close(ofd);
1695                                 unlink(outfile);
1696                         }
1697                         if (len) {
1698                                 res = write(ofd, buf, len);
1699                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1700                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1701                                         close(ifd);
1702                                         close(ofd);
1703                                         unlink(outfile);
1704                                 }
1705                         }
1706                 } while (len);
1707                 close(ifd);
1708                 close(ofd);
1709                 return 0;
1710 #ifdef HARDLINK_WHEN_POSSIBLE
1711         } else {
1712                 /* Hard link succeeded */
1713                 return 0;
1714         }
1715 #endif
1716 }
1717
1718 static void copy_file(char *frompath, char *topath)
1719 {
1720         char frompath2[PATH_MAX], topath2[PATH_MAX];
1721         struct ast_variable *tmp,*var = NULL;
1722         char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1723         ast_filecopy(frompath, topath, NULL);
1724         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1725         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1726         if (ast_check_realtime("voicemail_data")) {
1727                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1728                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1729                 for (tmp = var; tmp; tmp = tmp->next) {
1730                         if (!strcasecmp(tmp->name, "origmailbox")) {
1731                                 origmailbox = tmp->value;
1732                         } else if (!strcasecmp(tmp->name, "context")) {
1733                                 context = tmp->value;
1734                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
1735                                 macrocontext = tmp->value;
1736                         } else if (!strcasecmp(tmp->name, "exten")) {
1737                                 exten = tmp->value;
1738                         } else if (!strcasecmp(tmp->name, "priority")) {
1739                                 priority = tmp->value;
1740                         } else if (!strcasecmp(tmp->name, "callerchan")) {
1741                                 callerchan = tmp->value;
1742                         } else if (!strcasecmp(tmp->name, "callerid")) {
1743                                 callerid = tmp->value;
1744                         } else if (!strcasecmp(tmp->name, "origdate")) {
1745                                 origdate = tmp->value;
1746                         } else if (!strcasecmp(tmp->name, "origtime")) {
1747                                 origtime = tmp->value;
1748                         } else if (!strcasecmp(tmp->name, "category")) {
1749                                 category = tmp->value;
1750                         } else if (!strcasecmp(tmp->name, "duration")) {
1751                                 duration = tmp->value;
1752                         }
1753                 }
1754                 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);
1755         }
1756         copy(frompath2, topath2);
1757         ast_variables_destroy(var);
1758 }
1759 #endif
1760
1761 /*! \brief
1762  * A negative return value indicates an error.
1763  * \note Should always be called with a lock already set on dir.
1764  */
1765 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1766 {
1767         int x;
1768         unsigned char map[MAXMSGLIMIT] = "";
1769         DIR *msgdir;
1770         struct dirent *msgdirent;
1771         int msgdirint;
1772
1773         /* Reading the entire directory into a file map scales better than
1774          * doing a stat repeatedly on a predicted sequence.  I suspect this
1775          * is partially due to stat(2) internally doing a readdir(2) itself to
1776          * find each file. */
1777         msgdir = opendir(dir);
1778         while ((msgdirent = readdir(msgdir))) {
1779                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1780                         map[msgdirint] = 1;
1781         }
1782         closedir(msgdir);
1783
1784         for (x = 0; x < vmu->maxmsg; x++) {
1785                 if (map[x] == 0)
1786                         break;
1787         }
1788
1789         return x - 1;
1790 }
1791
1792 #endif
1793
1794 static int vm_delete(char *file)
1795 {
1796         char *txt;
1797         int txtsize = 0;
1798
1799         txtsize = (strlen(file) + 5)*sizeof(char);
1800         txt = alloca(txtsize);
1801         /* Sprintf here would safe because we alloca'd exactly the right length,
1802          * but trying to eliminate all sprintf's anyhow
1803          */
1804         if (ast_check_realtime("voicemail_data")) {
1805                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1806         }
1807         snprintf(txt, txtsize, "%s.txt", file);
1808         unlink(txt);
1809         return ast_filedelete(file, NULL);
1810 }
1811
1812 static int inbuf(struct baseio *bio, FILE *fi)
1813 {
1814         int l;
1815
1816         if (bio->ateof)
1817                 return 0;
1818
1819         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1820                 if (ferror(fi))
1821                         return -1;
1822
1823                 bio->ateof = 1;
1824                 return 0;
1825         }
1826
1827         bio->iolen= l;
1828         bio->iocp= 0;
1829
1830         return 1;
1831 }
1832
1833 static int inchar(struct baseio *bio, FILE *fi)
1834 {
1835         if (bio->iocp>=bio->iolen) {
1836                 if (!inbuf(bio, fi))
1837                         return EOF;
1838         }
1839
1840         return bio->iobuf[bio->iocp++];
1841 }
1842
1843 static int ochar(struct baseio *bio, int c, FILE *so)
1844 {
1845         if (bio->linelength >= BASELINELEN) {
1846                 if (fputs(eol,so) == EOF)
1847                         return -1;
1848
1849                 bio->linelength= 0;
1850         }
1851
1852         if (putc(((unsigned char)c),so) == EOF)
1853                 return -1;
1854
1855         bio->linelength++;
1856
1857         return 1;
1858 }
1859
1860 static int base_encode(char *filename, FILE *so)
1861 {
1862         unsigned char dtable[BASEMAXINLINE];
1863         int i,hiteof= 0;
1864         FILE *fi;
1865         struct baseio bio;
1866
1867         memset(&bio, 0, sizeof(bio));
1868         bio.iocp = BASEMAXINLINE;
1869
1870         if (!(fi = fopen(filename, "rb"))) {
1871                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1872                 return -1;
1873         }
1874
1875         for (i= 0; i<9; i++) {
1876                 dtable[i]= 'A'+i;
1877                 dtable[i+9]= 'J'+i;
1878                 dtable[26+i]= 'a'+i;
1879                 dtable[26+i+9]= 'j'+i;
1880         }
1881         for (i= 0; i<8; i++) {
1882                 dtable[i+18]= 'S'+i;
1883                 dtable[26+i+18]= 's'+i;
1884         }
1885         for (i= 0; i<10; i++) {
1886                 dtable[52+i]= '0'+i;
1887         }
1888         dtable[62]= '+';
1889         dtable[63]= '/';
1890
1891         while (!hiteof){
1892                 unsigned char igroup[3], ogroup[4];
1893                 int c,n;
1894
1895                 igroup[0]= igroup[1]= igroup[2]= 0;
1896
1897                 for (n= 0;n<3;n++) {
1898                         if ((c = inchar(&bio, fi)) == EOF) {
1899                                 hiteof= 1;
1900                                 break;
1901                         }
1902
1903                         igroup[n]= (unsigned char)c;
1904                 }
1905
1906                 if (n> 0) {
1907                         ogroup[0]= dtable[igroup[0]>>2];
1908                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1909                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1910                         ogroup[3]= dtable[igroup[2]&0x3F];
1911
1912                         if (n<3) {
1913                                 ogroup[3]= '=';
1914
1915                                 if (n<2)
1916                                         ogroup[2]= '=';
1917                         }
1918
1919                         for (i= 0;i<4;i++)
1920                                 ochar(&bio, ogroup[i], so);
1921                 }
1922         }
1923
1924         if (fputs(eol,so) == EOF)
1925                 return 0;
1926
1927         fclose(fi);
1928
1929         return 1;
1930 }
1931
1932 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)
1933 {
1934         char callerid[256];
1935         /* Prepare variables for substitution in email body and subject */
1936         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1937         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1938         snprintf(passdata, passdatasize, "%d", msgnum);
1939         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1940         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1941         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1942         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1943         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1944         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1945         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1946         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1947 }
1948
1949 static char *quote(const char *from, char *to, size_t len)
1950 {
1951         char *ptr = to;
1952         *ptr++ = '"';
1953         for (; ptr < to + len - 1; from++) {
1954                 if (*from == '"')
1955                         *ptr++ = '\\';
1956                 else if (*from == '\0')
1957                         break;
1958                 *ptr++ = *from;
1959         }
1960         if (ptr < to + len - 1)
1961                 *ptr++ = '"';
1962         *ptr = '\0';
1963         return to;
1964 }
1965
1966 /*! \brief
1967  * fill in *tm for current time according to the proper timezone, if any.
1968  * Return tm so it can be used as a function argument.
1969  */
1970 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1971 {
1972         const struct vm_zone *z = NULL;
1973         time_t t = time(NULL);
1974
1975         /* Does this user have a timezone specified? */
1976         if (!ast_strlen_zero(vmu->zonetag)) {
1977                 /* Find the zone in the list */
1978                 AST_LIST_LOCK(&zones);
1979                 AST_LIST_TRAVERSE(&zones, z, list) {
1980                         if (!strcmp(z->name, vmu->zonetag))
1981                                 break;
1982                 }
1983                 AST_LIST_UNLOCK(&zones);
1984         }
1985         ast_localtime(&t, tm, z ? z->timezone : NULL);
1986         return tm;
1987 }
1988
1989 /*! \brief same as mkstemp, but return a FILE * */
1990 static FILE *vm_mkftemp(char *template)
1991 {
1992         FILE *p = NULL;
1993         int pfd = mkstemp(template);
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 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         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         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                         ast_debug(3, "newtmp: %s\n", newtmp);
2154                         if (tmpfd > -1) {
2155                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2156                                 ast_safe_system(tmpcmd);
2157                                 attach = newtmp;
2158                                 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2159                         }
2160                 }
2161                 fprintf(p, "--%s" ENDL, bound);
2162                 if (msgnum > -1)
2163                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2164                 else
2165                         fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2166                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2167                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2168                 if (msgnum > -1)
2169                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2170                 else
2171                         fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2172                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2173                 base_encode(fname, p);
2174                 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2175                 if (tmpfd > -1) {
2176                         unlink(fname);
2177                         close(tmpfd);
2178                         unlink(newtmp);
2179                 }
2180         }
2181 #undef ENDL
2182 }
2183
2184 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)
2185 {
2186         FILE *p=NULL;
2187         char tmp[80] = "/tmp/astmail-XXXXXX";
2188         char tmp2[256];
2189
2190         if (vmu && ast_strlen_zero(vmu->email)) {
2191                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2192                 return(0);
2193         }
2194         if (!strcmp(format, "wav49"))
2195                 format = "WAV";
2196         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));
2197         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2198            command hangs */
2199         if ((p = vm_mkftemp(tmp)) == NULL) {
2200                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2201                 return -1;
2202         } else {
2203                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2204                 fclose(p);
2205                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2206                 ast_safe_system(tmp2);
2207                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2208         }
2209         return 0;
2210 }
2211
2212 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)
2213 {
2214         char date[256];
2215         char host[MAXHOSTNAMELEN] = "";
2216         char who[256];
2217         char dur[PATH_MAX];
2218         char tmp[80] = "/tmp/astmail-XXXXXX";
2219         char tmp2[PATH_MAX];
2220         struct tm tm;
2221         FILE *p;
2222
2223         if ((p = vm_mkftemp(tmp)) == NULL) {
2224                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2225                 return -1;
2226         }
2227         gethostname(host, sizeof(host)-1);
2228         if (strchr(srcemail, '@'))
2229                 ast_copy_string(who, srcemail, sizeof(who));
2230         else 
2231                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2232         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2233         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2234         fprintf(p, "Date: %s\n", date);
2235
2236         if (*pagerfromstring) {
2237                 struct ast_channel *ast;
2238                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2239                         char *passdata;
2240                         int vmlen = strlen(fromstring)*3 + 200;
2241                         if ((passdata = alloca(vmlen))) {
2242                                 memset(passdata, 0, vmlen);
2243                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2244                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2245                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2246                         } else 
2247                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2248                         ast_channel_free(ast);
2249                 } else 
2250                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2251         } else
2252                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2253         fprintf(p, "To: %s\n", pager);
2254         if (pagersubject) {
2255                 struct ast_channel *ast;
2256                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2257                         char *passdata;
2258                         int vmlen = strlen(pagersubject) * 3 + 200;
2259                         if ((passdata = alloca(vmlen))) {
2260                                 memset(passdata, 0, vmlen);
2261                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2262                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2263                                 fprintf(p, "Subject: %s\n\n", passdata);
2264                         } else
2265                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2266                         ast_channel_free(ast);
2267                 } else
2268                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2269         } else
2270                 fprintf(p, "Subject: New VM\n\n");
2271
2272         strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2273         if (pagerbody) {
2274                 struct ast_channel *ast;
2275                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2276                         char *passdata;
2277                         int vmlen = strlen(pagerbody) * 3 + 200;
2278                         passdata = alloca(vmlen);
2279                         memset(passdata, 0, vmlen);
2280                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2281                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2282                         fprintf(p, "%s\n", passdata);
2283                         ast_channel_free(ast);
2284                 } else
2285                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2286         } else {
2287                 fprintf(p, "New %s long msg in box %s\n"
2288                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2289         }
2290         fclose(p);
2291         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2292         ast_safe_system(tmp2);
2293         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2294         return 0;
2295 }
2296
2297 static int get_date(char *s, int len)
2298 {
2299         struct tm tm;
2300         time_t t;
2301         t = time(0);
2302         ast_localtime(&t, &tm, NULL);
2303         return 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, "greet"))) {
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 dest[PATH_MAX];
3022         char fn[PATH_MAX];
3023         char prefile[PATH_MAX] = "";
3024         char tempfile[PATH_MAX] = "";
3025         char ext_context[256] = "";
3026         char fmt[80];
3027         char *context;
3028         char ecodes[16] = "#";
3029         char tmp[1024] = "", *tmpptr;
3030         struct ast_vm_user *vmu;
3031         struct ast_vm_user svm;
3032         const char *category = NULL;
3033
3034         ast_copy_string(tmp, ext, sizeof(tmp));
3035         ext = tmp;
3036         if ((context = strchr(tmp, '@'))) {
3037                 *context++ = '\0';
3038                 tmpptr = strchr(context, '&');
3039         } else {
3040                 tmpptr = strchr(ext, '&');
3041         }
3042
3043         if (tmpptr)
3044                 *tmpptr++ = '\0';
3045
3046         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3047
3048         ast_debug(3, "Before find_user\n");
3049         if (!(vmu = find_user(&svm, context, ext))) {
3050                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3051                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3052                 return res;
3053         }
3054         /* Setup pre-file if appropriate */
3055         if (strcmp(vmu->context, "default"))
3056                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3057         else
3058                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
3059         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3060                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3061                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3062         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3063                 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3064                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3065         }
3066         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3067         if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3068                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3069                 return -1;
3070         }
3071         RETRIEVE(tempfile, -1, ext, context);
3072         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3073                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3074         DISPOSE(tempfile, -1);
3075         /* It's easier just to try to make it than to check for its existence */
3076         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3077         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
3078
3079         /* Check current or macro-calling context for special extensions */
3080         if (ast_test_flag(vmu, VM_OPERATOR)) {
3081                 if (!ast_strlen_zero(vmu->exit)) {
3082                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3083                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3084                                 ouseexten = 1;
3085                         }
3086                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3087                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3088                         ouseexten = 1;
3089                 }
3090                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3091                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3092                 ousemacro = 1;
3093                 }
3094         }
3095
3096         if (!ast_strlen_zero(vmu->exit)) {
3097                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3098                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3099         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3100                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3101         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3102                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3103                 ausemacro = 1;
3104         }
3105
3106         /* Play the beginning intro if desired */
3107         if (!ast_strlen_zero(prefile)) {
3108                 RETRIEVE(prefile, -1, ext, context);
3109                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3110                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3111                                 res = ast_waitstream(chan, ecodes);
3112                 } else {
3113                         ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3114                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3115                 }
3116                 DISPOSE(prefile, -1);
3117                 if (res < 0) {
3118                         ast_debug(1, "Hang up during prefile playback\n");
3119                         free_user(vmu);
3120                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3121                         return -1;
3122                 }
3123         }
3124         if (res == '#') {
3125                 /* On a '#' we skip the instructions */
3126                 ast_set_flag(options, OPT_SILENT);
3127                 res = 0;
3128         }
3129         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3130                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3131                 if (res == '#') {
3132                         ast_set_flag(options, OPT_SILENT);
3133                         res = 0;
3134                 }
3135         }
3136         if (res > 0)
3137                 ast_stopstream(chan);
3138         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3139          other than the operator -- an automated attendant or mailbox login for example */
3140         if (res == '*') {
3141                 chan->exten[0] = 'a';
3142                 chan->exten[1] = '\0';
3143                 if (!ast_strlen_zero(vmu->exit)) {
3144                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3145                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3146                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3147                 }
3148                 chan->priority = 0;
3149                 free_user(vmu);
3150                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3151                 return 0;
3152         }
3153
3154         /* Check for a '0' here */
3155         if (res == '0') {
3156         transfer:
3157                 if (ouseexten || ousemacro) {
3158                         chan->exten[0] = 'o';
3159                         chan->exten[1] = '\0';
3160                         if (!ast_strlen_zero(vmu->exit)) {
3161                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3162                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3163                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3164                         }
3165                         ast_play_and_wait(chan, "transfer");
3166                         chan->priority = 0;
3167                         free_user(vmu);
3168                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3169                 }
3170                 return 0;
3171         }
3172         if (res < 0) {
3173                 free_user(vmu);
3174                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3175                 return -1;
3176         }
3177         /* The meat of recording the message...  All the announcements and beeps have been played*/
3178         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3179         if (!ast_strlen_zero(fmt)) {
3180                 msgnum = 0;
3181
3182 #ifdef IMAP_STORAGE
3183                 /* Is ext a mailbox? */
3184                 /* must open stream for this user to get info! */
3185                 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3186                 if(res < 0) {
3187                         ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3188                         return -1;
3189                 }
3190                 if((vms = get_vm_state_by_mailbox(ext,0)))  
3191                         vms->newmessages++; /*still need to increment new message count*/
3192                 
3193                 /* here is a big difference! We add one to it later */
3194                 msgnum = newmsgs + oldmsgs;
3195                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3196                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3197                 /* set variable for compatibility */
3198                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3199
3200                 /* Check if mailbox is full */
3201                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3202                         ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3203                         ast_play_and_wait(chan, "vm-mailboxfull");
3204                         return -1;
3205                 }
3206                 /* here is a big difference! We add one to it later */
3207                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3208
3209 #else
3210                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3211                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3212                         if (!res)
3213                                 res = ast_waitstream(chan, "");
3214                         ast_log(LOG_WARNING, "No more messages possible\n");
3215                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3216                         goto leave_vm_out;
3217                 }
3218
3219 #endif
3220                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3221                 txtdes = mkstemp(tmptxtfile);
3222                 if (txtdes < 0) {
3223                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3224                         if (!res)
3225                                 res = ast_waitstream(chan, "");
3226                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3227                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3228                         goto leave_vm_out;
3229                 }
3230
3231                 /* Now play the beep once we have the message number for our next message. */
3232                 if (res >= 0) {
3233                         /* Unless we're *really* silent, try to send the beep */
3234                         res = ast_stream_and_wait(chan, "beep", "");
3235                 }
3236                                 
3237                 /* Store information in real-time storage */
3238                 if (ast_check_realtime("voicemail_data")) {
3239                         snprintf(priority, sizeof(priority), "%d", chan->priority);
3240                         snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3241                         get_date(date, sizeof(date));
3242                         rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", category ? category : "", NULL);
3243                 }
3244
3245                 /* Store information */
3246                 txt = fdopen(txtdes, "w+");
3247                 if (txt) {
3248                         get_date(date, sizeof(date));
3249                         fprintf(txt, 
3250                                 ";\n"
3251                                 "; Message Information file\n"
3252                                 ";\n"
3253                                 "[message]\n"
3254                                 "origmailbox=%s\n"
3255                                 "context=%s\n"
3256                                 "macrocontext=%s\n"
3257                                 "exten=%s\n"
3258                                 "priority=%d\n"
3259                                 "callerchan=%s\n"
3260                                 "callerid=%s\n"
3261                                 "origdate=%s\n"
3262                                 "origtime=%ld\n"
3263                                 "category=%s\n",
3264                                 ext,
3265                                 chan->context,
3266                                 chan->macrocontext, 
3267                                 chan->exten,
3268                                 chan->priority,
3269                                 chan->name,
3270                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3271                                 date, (long)time(NULL),
3272                                 category ? category : ""); 
3273                 } else
3274                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3275 #ifdef IMAP_STORAGE
3276                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3277 #else
3278                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3279 #endif
3280
3281                 if (txt) {
3282                         if (duration < vmminsecs) {
3283                                 if (option_verbose > 2) 
3284                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3285                                 ast_filedelete(tmptxtfile, NULL);
3286                                 unlink(tmptxtfile);
3287                                 if (ast_check_realtime("voicemail_data")) {
3288                                         snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3289                                         ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3290                                 }
3291                         } else {
3292                                 fprintf(txt, "duration=%d\n", duration);
3293                                 fclose(txt);
3294                                 if (vm_lock_path(dir)) {
3295                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3296                                         /* Delete files */
3297                                         ast_filedelete(tmptxtfile, NULL);
3298                                         unlink(tmptxtfile);
3299                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3300                                         ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
3301                                         unlink(tmptxtfile);
3302                                         ast_unlock_path(dir);
3303                                         if (ast_check_realtime("voicemail_data")) {
3304                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3305                                                 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3306                                         }
3307                                 } else {
3308 #ifndef IMAP_STORAGE
3309                                         msgnum = last_message_index(vmu, dir) + 1;
3310 #endif
3311                                         make_file(fn, sizeof(fn), dir, msgnum);
3312
3313                                         /* assign a variable with the name of the voicemail file */ 
3314 #ifndef IMAP_STORAGE
3315                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3316 #else
3317                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3318 #endif
3319
3320                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3321                                         ast_filerename(tmptxtfile, fn, NULL);
3322                                         rename(tmptxtfile, txtfile);
3323
3324                                         ast_unlock_path(dir);
3325                                         if (ast_check_realtime("voicemail_data")) {
3326                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3327                                                 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3328                                                 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3329                                         }
3330 #ifndef IMAP_STORAGE
3331                                         /* Are there to be more recipients of this message? */
3332                                         while (tmpptr) {
3333                                                 struct ast_vm_user recipu, *recip;
3334                                                 char *exten, *context;
3335                                         
3336                                                 exten = strsep(&tmpptr, "&");
3337                                                 context = strchr(exten, '@');
3338                                                 if (context) {
3339                                                         *context = '\0';
3340                                                         context++;
3341                                                 }
3342                                                 if ((recip = find_user(&recipu, context, exten))) {
3343                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3344                                                         free_user(recip);
3345                                                 }
3346                                         }
3347 #endif
3348                                         if (ast_fileexists(fn, NULL, NULL)) {
3349                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3350                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3351                                                 DISPOSE(dir, msgnum);
3352                                         }
3353                                 }
3354                         }
3355                 }
3356                 if (res == '0') {
3357                         goto transfer;
3358                 } else if (res > 0)
3359                         res = 0;
3360
3361                 if (duration < vmminsecs)
3362                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3363                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3364                 else
3365                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3366         } else
3367                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3368 leave_vm_out:
3369         free_user(vmu);
3370         
3371         return res;
3372 }
3373
3374 #ifndef IMAP_STORAGE
3375 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3376 {
3377         /* we know max messages, so stop process when number is hit */
3378
3379         int x,dest;
3380         char sfn[PATH_MAX];
3381         char dfn[PATH_MAX];
3382
3383         if (vm_lock_path(dir))
3384                 return ERROR_LOCK_PATH;
3385
3386         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3387                 make_file(sfn, sizeof(sfn), dir, x);
3388                 if (EXISTS(dir, x, sfn, NULL)) {
3389                         
3390                         if (x != dest) {
3391                                 make_file(dfn, sizeof(dfn), dir, dest);
3392                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3393                         }
3394                         
3395                         dest++;
3396                 }
3397         }
3398         ast_unlock_path(dir);
3399
3400         return 0;
3401 }
3402 #endif
3403
3404 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3405 {
3406         int d;
3407         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3408         return d;
3409 }
3410
3411 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3412 {
3413 #ifdef IMAP_STORAGE
3414         /* we must use mbox(x) folder names, and copy the message there */
3415         /* simple. huh? */
3416         char dbox[256];
3417         long res;
3418         char sequence[10];
3419
3420         /* if save to Old folder, just leave in INBOX */
3421         if (box == 1) return 10;
3422         /* get the real IMAP message number for this message */
3423         sprintf(sequence,"%ld",vms->msgArray[msg]);
3424         imap_mailbox_name(dbox, vms, box, 1);
3425         ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3426         res = mail_copy(vms->mailstream, sequence, dbox);
3427         if (res == 1) return 0;
3428         return 1;
3429 #else
3430         char *dir = vms->curdir;
3431         char *username = vms->username;
3432         char *context = vmu->context;
3433         char sfn[PATH_MAX];
3434         char dfn[PATH_MAX];
3435         char ddir[PATH_MAX];
3436         const char *dbox = mbox(box);
3437         int x;
3438         make_file(sfn, sizeof(sfn), dir, msg);
3439         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3440
3441         if (vm_lock_path(ddir))
3442                 return ERROR_LOCK_PATH;
3443
3444         x = last_message_index(vmu, ddir) + 1;
3445         make_file(dfn, sizeof(dfn), ddir, x);
3446
3447         if (x >= vmu->maxmsg) {
3448                 ast_unlock_path(ddir);
3449                 return -1;
3450         }
3451         if (strcmp(sfn, dfn)) {
3452                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3453         }
3454         ast_unlock_path(ddir);
3455 #endif
3456         return 0;
3457 }
3458
3459 static int a