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