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