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