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