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