Merged revisions 130959 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                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2604                         ast_safe_system(tmpcmd);
2605                         attach = newtmp;
2606                         ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2607                 }
2608         }
2609         fprintf(p, "--%s" ENDL, bound);
2610         if (msgnum > -1)
2611                 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
2612         else
2613                 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
2614         fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2615         fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2616         if (msgnum > -1)
2617                 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
2618         else
2619                 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
2620         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2621         base_encode(fname, p);
2622         if (last)
2623                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2624         if (tmpfd > -1) {
2625                 unlink(fname);
2626                 close(tmpfd);
2627                 unlink(newtmp);
2628         }
2629         return 0;
2630 }
2631 #undef ENDL
2632
2633 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)
2634 {
2635         FILE *p=NULL;
2636         char tmp[80] = "/tmp/astmail-XXXXXX";
2637         char tmp2[256];
2638
2639         if (vmu && ast_strlen_zero(vmu->email)) {
2640                 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2641                 return(0);
2642         }
2643         if (!strcmp(format, "wav49"))
2644                 format = "WAV";
2645         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));
2646         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2647            command hangs */
2648         if ((p = vm_mkftemp(tmp)) == NULL) {
2649                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2650                 return -1;
2651         } else {
2652                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
2653                 fclose(p);
2654                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2655                 ast_safe_system(tmp2);
2656                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2657         }
2658         return 0;
2659 }
2660
2661 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)
2662 {
2663         char date[256];
2664         char host[MAXHOSTNAMELEN] = "";
2665         char who[256];
2666         char dur[PATH_MAX];
2667         char tmp[80] = "/tmp/astmail-XXXXXX";
2668         char tmp2[PATH_MAX];
2669         struct ast_tm tm;
2670         FILE *p;
2671
2672         if ((p = vm_mkftemp(tmp)) == NULL) {
2673                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2674                 return -1;
2675         }
2676         gethostname(host, sizeof(host)-1);
2677         if (strchr(srcemail, '@'))
2678                 ast_copy_string(who, srcemail, sizeof(who));
2679         else 
2680                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2681         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2682         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2683         fprintf(p, "Date: %s\n", date);
2684
2685         if (*pagerfromstring) {
2686                 struct ast_channel *ast;
2687                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2688                         char *passdata;
2689                         int vmlen = strlen(fromstring)*3 + 200;
2690                         passdata = alloca(vmlen);
2691                         memset(passdata, 0, vmlen);
2692                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2693                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2694                         fprintf(p, "From: %s <%s>\n", passdata, who);
2695                         ast_channel_free(ast);
2696                 } else 
2697                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2698         } else
2699                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2700         fprintf(p, "To: %s\n", pager);
2701         if (pagersubject) {
2702                 struct ast_channel *ast;
2703                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2704                         char *passdata;
2705                         int vmlen = strlen(pagersubject) * 3 + 200;
2706                         passdata = alloca(vmlen);
2707                         memset(passdata, 0, vmlen);
2708                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2709                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2710                         fprintf(p, "Subject: %s\n\n", passdata);
2711                         ast_channel_free(ast);
2712                 } else
2713                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2714         } else {
2715                 if (ast_strlen_zero(flag)) {
2716                         fprintf(p, "Subject: New VM\n\n");
2717                 } else {
2718                         fprintf(p, "Subject: New %s VM\n\n", flag);
2719                 }
2720         }
2721
2722         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2723         if (pagerbody) {
2724                 struct ast_channel *ast;
2725                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2726                         char *passdata;
2727                         int vmlen = strlen(pagerbody) * 3 + 200;
2728                         passdata = alloca(vmlen);
2729                         memset(passdata, 0, vmlen);
2730                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2731                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2732                         fprintf(p, "%s\n", passdata);
2733                         ast_channel_free(ast);
2734                 } else
2735                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2736         } else {
2737                 fprintf(p, "New %s long %s msg in box %s\n"
2738                                 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2739         }
2740         fclose(p);
2741         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2742         ast_safe_system(tmp2);
2743         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2744         return 0;
2745 }
2746
2747 /*!
2748  * \brief Gets the current date and time, as formatted string.
2749  * \param s The buffer to hold the output formatted date.
2750  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
2751  * 
2752  * The date format string used is "%a %b %e %r UTC %Y".
2753  * 
2754  * \return zero on success, -1 on error.
2755  */
2756 static int get_date(char *s, int len)
2757 {
2758         struct ast_tm tm;
2759         struct timeval t = ast_tvnow();
2760         
2761         ast_localtime(&t, &tm, "UTC");
2762
2763         return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
2764 }
2765
2766 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2767 {
2768         int res;
2769         char fn[PATH_MAX];
2770         char dest[PATH_MAX];
2771
2772         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2773
2774         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2775                 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
2776                 return -1;
2777         }
2778
2779         RETRIEVE(fn, -1, ext, context);
2780         if (ast_fileexists(fn, NULL, NULL) > 0) {
2781                 res = ast_stream_and_wait(chan, fn, ecodes);
2782                 if (res) {
2783                         DISPOSE(fn, -1);
2784                         return res;
2785                 }
2786         } else {
2787                 /* Dispose just in case */
2788                 DISPOSE(fn, -1);
2789                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2790                 if (res)
2791                         return res;
2792                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2793                 if (res)
2794                         return res;
2795         }
2796         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2797         return res;
2798 }
2799
2800 static void free_user(struct ast_vm_user *vmu)
2801 {
2802         if (!ast_test_flag(vmu, VM_ALLOCED))
2803                 return;
2804
2805         ast_free(vmu);
2806 }
2807
2808 static void free_zone(struct vm_zone *z)
2809 {
2810         ast_free(z);
2811 }
2812
2813 /*!
2814  * \brief Gets the name of the mailbox folder from the numeric index.
2815  * \param id The numerical index for the folder name.
2816  * 
2817  * When an invalid number is entered, or one that exceeds the pre-configured list of folder names, the name "tmp" is returned.
2818  *
2819  * \return the String name that coresponds to this folder index.
2820  */
2821 static const char *mbox(int id)
2822 {
2823         static const char *msgs[] = {
2824 #ifdef IMAP_STORAGE
2825                 imapfolder,
2826 #else
2827                 "INBOX",
2828 #endif
2829                 "Old",
2830                 "Work",
2831                 "Family",
2832                 "Friends",
2833                 "Cust1",
2834                 "Cust2",
2835                 "Cust3",
2836                 "Cust4",
2837                 "Cust5",
2838                 "Deleted",
2839                 "Urgent"
2840         };
2841         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2842 }
2843 #ifdef IMAP_STORAGE
2844 /*!
2845  * \brief Converts a string folder name into the numerical identifier.
2846  * \param folder the string folder name to be converted to an id.
2847  *
2848  * This is the opposite of the mbox() function.
2849  *
2850  * \return the id that coresponds to the folder name
2851  */
2852 static int folder_int(const char *folder)
2853 {
2854         /* assume a NULL folder means INBOX */
2855         if (!folder)
2856                 return 0;
2857 #ifdef IMAP_STORAGE
2858         if (!strcasecmp(folder, imapfolder))
2859 #else
2860         if (!strcasecmp(folder, "INBOX"))
2861 #endif
2862                 return 0;
2863         else if (!strcasecmp(folder, "Old"))
2864                 return 1;
2865         else if (!strcasecmp(folder, "Work"))
2866                 return 2;
2867         else if (!strcasecmp(folder, "Family"))
2868                 return 3;
2869         else if (!strcasecmp(folder, "Friends"))
2870                 return 4;
2871         else if (!strcasecmp(folder, "Cust1"))
2872                 return 5;
2873         else if (!strcasecmp(folder, "Cust2"))
2874                 return 6;
2875         else if (!strcasecmp(folder, "Cust3"))
2876                 return 7;
2877         else if (!strcasecmp(folder, "Cust4"))
2878                 return 8;
2879         else if (!strcasecmp(folder, "Cust5"))
2880                 return 9;
2881         else if (!strcasecmp(folder, "Deleted"))
2882                 return 10;
2883         else if (!strcasecmp(folder, "Urgent"))
2884                 return 11;
2885         else /*assume they meant INBOX if folder is not found otherwise*/
2886                 return 0;
2887 }
2888 #endif
2889
2890 #ifdef ODBC_STORAGE
2891 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2892 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2893 {
2894         int x = -1;
2895         int res;
2896         SQLHSTMT stmt;
2897         char sql[PATH_MAX];
2898         char rowdata[20];
2899         char tmp[PATH_MAX] = "";
2900         struct odbc_obj *obj;
2901         char *context;
2902         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2903
2904         if (newmsgs)
2905                 *newmsgs = 0;
2906         if (oldmsgs)
2907                 *oldmsgs = 0;
2908         if (urgentmsgs)
2909                 *urgentmsgs = 0;
2910
2911         /* If no mailbox, return immediately */
2912         if (ast_strlen_zero(mailbox))
2913                 return 0;
2914
2915         ast_copy_string(tmp, mailbox, sizeof(tmp));
2916         
2917         context = strchr(tmp, '@');
2918         if (context) {
2919                 *context = '\0';
2920                 context++;
2921         } else
2922                 context = "default";
2923         
2924         obj = ast_odbc_request_obj(odbc_database, 0);
2925         if (obj) {
2926                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2927                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2928                 if (!stmt) {
2929                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2930                         ast_odbc_release_obj(obj);
2931                         goto yuck;
2932                 }
2933                 res = SQLFetch(stmt);
2934                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2935                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2936                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2937                         ast_odbc_release_obj(obj);
2938                         goto yuck;
2939                 }
2940                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2941                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2942                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2943                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2944                         ast_odbc_release_obj(obj);
2945                         goto yuck;
2946                 }
2947                 *newmsgs = atoi(rowdata);
2948                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2949
2950                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2951                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2952                 if (!stmt) {
2953                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2954                         ast_odbc_release_obj(obj);
2955                         goto yuck;
2956                 }
2957                 res = SQLFetch(stmt);
2958                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2959                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2960                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2961                         ast_odbc_release_obj(obj);
2962                         goto yuck;
2963                 }
2964                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2965                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2966                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2967                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2968                         ast_odbc_release_obj(obj);
2969                         goto yuck;
2970                 }
2971                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2972                 *oldmsgs = atoi(rowdata);
2973
2974                 if (!urgentmsgs) {
2975                         x = 0;
2976                         ast_odbc_release_obj(obj);
2977                         goto yuck;
2978                 }
2979
2980                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
2981                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2982                 if (!stmt) {
2983                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2984                         ast_odbc_release_obj(obj);
2985                         goto yuck;
2986                 }
2987                 res = SQLFetch(stmt);
2988                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2989                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2990                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2991                         ast_odbc_release_obj(obj);
2992                         goto yuck;
2993                 }
2994                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2995                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2996                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2997                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2998                         ast_odbc_release_obj(obj);
2999                         goto yuck;
3000                 }
3001                 *urgentmsgs = atoi(rowdata);
3002                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3003                 ast_odbc_release_obj(obj);
3004                 x = 0;
3005         } else
3006                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3007                 
3008 yuck:   
3009         return x;
3010 }
3011
3012 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
3013 {
3014         return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
3015 }
3016
3017 /*!
3018  * \brief Gets the number of messages that exist in a mailbox folder.
3019  * \param context
3020  * \param mailbox
3021  * \param folder
3022  * 
3023  * This method is used when ODBC backend is used.
3024  * \return The number of messages in this mailbox folder (zero or more).
3025  */
3026 static int messagecount(const char *context, const char *mailbox, const char *folder)
3027 {
3028         struct odbc_obj *obj = NULL;
3029         int nummsgs = 0;
3030         int res;
3031         SQLHSTMT stmt = NULL;
3032         char sql[PATH_MAX];
3033         char rowdata[20];
3034         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
3035         if (!folder)
3036                 folder = "INBOX";
3037         /* If no mailbox, return immediately */
3038         if (ast_strlen_zero(mailbox))
3039                 return 0;
3040
3041         obj = ast_odbc_request_obj(odbc_database, 0);
3042         if (obj) {
3043                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
3044                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3045                 if (!stmt) {
3046                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3047                         goto yuck;
3048                 }
3049                 res = SQLFetch(stmt);
3050                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3051                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3052                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3053                         goto yuck;
3054                 }
3055                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3056                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3057                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3058                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3059                         goto yuck;
3060                 }
3061                 nummsgs = atoi(rowdata);
3062                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3063         } else
3064                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3065
3066 yuck:
3067         if (obj)
3068                 ast_odbc_release_obj(obj);
3069         return nummsgs;
3070 }
3071
3072 /** 
3073  * \brief Determines if the given folder has messages.
3074  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3075  * 
3076  * This function is used when the mailbox is stored in an ODBC back end.
3077  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3078  * \return 1 if the folder has one or more messages. zero otherwise.
3079  */
3080 static int has_voicemail(const char *mailbox, const char *folder)
3081 {
3082         char tmp[256], *tmp2 = tmp, *mbox, *context;
3083         ast_copy_string(tmp, mailbox, sizeof(tmp));
3084         while ((context = mbox = strsep(&tmp2, ","))) {
3085                 strsep(&context, "@");
3086                 if (ast_strlen_zero(context))
3087                         context = "default";
3088                 if (messagecount(context, mbox, folder))
3089                         return 1;
3090         }
3091         return 0;
3092 }
3093
3094 #elif defined(IMAP_STORAGE)
3095
3096 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)
3097 {
3098         char *myserveremail = serveremail;
3099         char fn[PATH_MAX];
3100         char intro[PATH_MAX];
3101         char mailbox[256];
3102         char *stringp;
3103         FILE *p=NULL;
3104         char tmp[80] = "/tmp/astmail-XXXXXX";
3105         long len;
3106         void *buf;
3107         int tempcopy = 0;
3108         STRING str;
3109         int ret; /* for better error checking */
3110         char *imapflags = NIL;
3111
3112         /* Set urgent flag for IMAP message */
3113         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
3114                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
3115                 imapflags="\\FLAGGED";
3116         }
3117         
3118         /* Attach only the first format */
3119         fmt = ast_strdupa(fmt);
3120         stringp = fmt;
3121         strsep(&stringp, "|");
3122
3123         if (!ast_strlen_zero(vmu->serveremail))
3124                 myserveremail = vmu->serveremail;
3125
3126         if (msgnum > -1)
3127                 make_file(fn, sizeof(fn), dir, msgnum);
3128         else
3129                 ast_copy_string (fn, dir, sizeof(fn));
3130         
3131         if (ast_strlen_zero(vmu->email)) {
3132                 /* We need the vmu->email to be set when we call make_email_file, but
3133                  * if we keep it set, a duplicate e-mail will be created. So at the end
3134                  * of this function, we will revert back to an empty string if tempcopy
3135                  * is 1.
3136                  */
3137                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
3138                 tempcopy = 1;
3139         }
3140
3141         if (!ast_strlen_zero(introfile)) {
3142                 snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
3143         } else {
3144                 intro[0] = '\0';
3145         }
3146
3147         if (!strcmp(fmt, "wav49"))
3148                 fmt = "WAV";
3149         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
3150
3151         /* Make a temporary file instead of piping directly to sendmail, in case the mail
3152            command hangs. */
3153         if (!(p = vm_mkftemp(tmp))) {
3154                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
3155                 if (tempcopy)
3156                         *(vmu->email) = '\0';
3157                 return -1;
3158         }
3159
3160         if (msgnum < 0 && imapgreetings) {
3161                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
3162                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
3163                         return -1;
3164                 }
3165                 imap_delete_old_greeting(fn, vms);
3166         }
3167         
3168         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);
3169         /* read mail file to memory */          
3170         len = ftell(p);
3171         rewind(p);
3172         if (!(buf = ast_malloc(len + 1))) {
3173                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
3174                 fclose(p);
3175                 if (tempcopy)
3176                         *(vmu->email) = '\0';
3177                 return -1;
3178         }
3179         fread(buf, len, 1, p);
3180         ((char *)buf)[len] = '\0';
3181         INIT(&str, mail_string, buf, len);
3182         ret = init_mailstream(vms, NEW_FOLDER);
3183         if (ret == 0) {
3184                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
3185                 if(!mail_append_full(vms->mailstream, mailbox, imapflags, NIL, &str))
3186                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3187                 fclose(p);
3188                 unlink(tmp);
3189                 ast_free(buf);
3190         } else {
3191                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
3192                 fclose(p);
3193                 unlink(tmp);
3194                 ast_free(buf);
3195                 return -1;
3196         }
3197         ast_debug(3, "%s stored\n", fn);
3198         
3199         if (tempcopy)
3200                 *(vmu->email) = '\0';
3201         
3202         return 0;
3203
3204 }
3205
3206 /*!
3207  * \brief Gets the number of messages that exist in a mailbox folder.
3208  * \param context
3209  * \param mailbox
3210  * \param folder
3211  * 
3212  * This method is used when IMAP backend is used.
3213  * \return The number of messages in this mailbox folder (zero or more).
3214  */
3215 static int messagecount(const char *context, const char *mailbox, const char *folder)
3216 {
3217         SEARCHPGM *pgm;
3218         SEARCHHEADER *hdr;
3219
3220         struct ast_vm_user *vmu, vmus;
3221         struct vm_state *vms_p;
3222         int ret = 0;
3223         int fold = folder_int(folder);
3224         int urgent = 0;
3225         
3226         if (ast_strlen_zero(mailbox))
3227                 return 0;
3228
3229         /* We have to get the user before we can open the stream! */
3230         vmu = find_user(&vmus, context, mailbox);
3231         if (!vmu) {
3232                 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
3233                 return -1;
3234         } else {
3235                 /* No IMAP account available */
3236                 if (vmu->imapuser[0] == '\0') {
3237                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
3238                         return -1;
3239                 }
3240         }
3241         
3242         /* No IMAP account available */
3243         if (vmu->imapuser[0] == '\0') {
3244                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
3245                 free_user(vmu);
3246                 return -1;
3247         }
3248
3249         /* check if someone is accessing this box right now... */
3250         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
3251         if (!vms_p) {
3252                 vms_p = get_vm_state_by_mailbox(mailbox,1);
3253         }
3254         if (vms_p) {
3255                 ast_debug(3, "Returning before search - user is logged in\n");
3256                 if (fold == 0) { /* INBOX */
3257                         return vms_p->newmessages;
3258                 }
3259                 if (fold == 1) { /* Old messages */
3260                         return vms_p->oldmessages;
3261                 }
3262                 if (fold == 11) {/*Urgent messages*/
3263                         return vms_p->urgentmessages;
3264                 }
3265         }
3266
3267         /* add one if not there... */
3268         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
3269         if (!vms_p) {
3270                 vms_p = get_vm_state_by_mailbox(mailbox,0);
3271         }
3272
3273         /* If URGENT, then look at INBOX */
3274         if (fold == 11) {
3275                 fold = NEW_FOLDER;
3276                 urgent = 1;
3277         }
3278
3279         if (!vms_p) {
3280                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
3281                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
3282                         return -1;
3283                 }
3284                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
3285                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3286                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
3287                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
3288                 vms_p->updated = 1;
3289                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
3290                 init_vm_state(vms_p);
3291                 vmstate_insert(vms_p);
3292         }
3293         ret = init_mailstream(vms_p, fold);
3294         if (!vms_p->mailstream) {
3295                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
3296                 return -1;
3297         }
3298         if (ret == 0) {
3299                 pgm = mail_newsearchpgm ();
3300                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
3301                 pgm->header = hdr;
3302                 if (fold != 1) {
3303                         pgm->unseen = 1;
3304                         pgm->seen = 0;
3305                 }
3306                 /* In the special case where fold is 1 (old messages) we have to do things a bit
3307                  * differently. Old messages are stored in the INBOX but are marked as "seen"
3308                  */
3309                 else {
3310                         pgm->unseen = 0;
3311                         pgm->seen = 1;
3312                 }
3313                 /* look for urgent messages */
3314                 if (urgent == 1) {
3315                         pgm->flagged = 1;
3316                         pgm->unflagged = 0;
3317                 }
3318                 pgm->undeleted = 1;
3319                 pgm->deleted = 0;
3320
3321                 vms_p->vmArrayIndex = 0;
3322                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
3323                 if (fold == 0 && urgent == 0)
3324                         vms_p->newmessages = vms_p->vmArrayIndex;
3325                 if (fold == 1)
3326                         vms_p->oldmessages = vms_p->vmArrayIndex;
3327                 if (fold == 0 && urgent == 1)
3328                         vms_p->urgentmessages = vms_p->vmArrayIndex;
3329                 /*Freeing the searchpgm also frees the searchhdr*/
3330                 mail_free_searchpgm(&pgm);
3331                 vms_p->updated = 0;
3332                 return vms_p->vmArrayIndex;
3333         } else {  
3334                 mail_ping(vms_p->mailstream);
3335         }
3336         return 0;
3337 }
3338
3339 /*!
3340  * \brief Gets the number of messages that exist in the inbox folder.
3341  * \param mailbox_context
3342  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3343  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3344  * 
3345  * This method is used when IMAP backend is used.
3346  * Simultaneously determines the count of new and old messages. The total messages would then be the sum of these two.
3347  *
3348  * \return zero on success, -1 on error.
3349  */
3350 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3351 {
3352         char tmp[PATH_MAX] = "";
3353         char *mailboxnc;
3354         char *context;
3355         char *mb;
3356         char *cur;
3357         if (newmsgs)
3358                 *newmsgs = 0;
3359         if (oldmsgs)
3360                 *oldmsgs = 0;
3361         if (urgentmsgs)
3362                 *urgentmsgs = 0;
3363
3364         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
3365         /* If no mailbox, return immediately */
3366         if (ast_strlen_zero(mailbox_context))
3367                 return 0;
3368         
3369         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3370         context = strchr(tmp, '@');
3371         if (strchr(mailbox_context, ',')) {
3372                 int tmpnew, tmpold, tmpurgent;
3373                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3374                 mb = tmp;
3375                 while ((cur = strsep(&mb, ", "))) {
3376                         if (!ast_strlen_zero(cur)) {
3377                                 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3378                                         return -1;
3379                                 else {
3380                                         if (newmsgs)
3381                                                 *newmsgs += tmpnew; 
3382                                         if (oldmsgs)
3383                                                 *oldmsgs += tmpold;
3384                                         if (urgentmsgs)
3385                                                 *urgentmsgs += tmpurgent;
3386                                 }
3387                         }
3388                 }
3389                 return 0;
3390         }
3391         if (context) {
3392                 *context = '\0';
3393                 mailboxnc = tmp;
3394                 context++;
3395         } else {
3396                 context = "default";
3397                 mailboxnc = (char *)mailbox_context;
3398         }
3399         if (newmsgs) {
3400                 if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
3401                         return -1;
3402         }
3403         if (oldmsgs) {
3404                 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
3405                         return -1;
3406         }
3407         if (urgentmsgs) {
3408                 if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
3409                         return -1;
3410         }
3411         return 0;
3412 }
3413
3414 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
3415 {
3416         return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
3417 }
3418
3419 /** 
3420  * \brief Determines if the given folder has messages.
3421  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3422  * \param folder the folder to look in
3423  *
3424  * This function is used when the mailbox is stored in an IMAP back end.
3425  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3426  * \return 1 if the folder has one or more messages. zero otherwise.
3427  */
3428 static int has_voicemail(const char *mailbox, const char *folder)
3429 {
3430         char tmp[256], *tmp2, *mbox, *context;
3431         ast_copy_string(tmp, mailbox, sizeof(tmp));
3432         tmp2 = tmp;
3433         if (strchr(tmp2, ',')) {
3434                 while ((mbox = strsep(&tmp2, ","))) {
3435                         if (!ast_strlen_zero(mbox)) {
3436                                 if (has_voicemail(mbox, folder))
3437                                         return 1;
3438                         }
3439                 }
3440         }
3441         if ((context= strchr(tmp, '@')))
3442                 *context++ = '\0';
3443         else
3444                 context = "default";
3445         return messagecount(context, tmp, folder) ? 1 : 0;
3446 }
3447
3448 /*!
3449  * \brief Copies a message from one mailbox to another.
3450  * \param chan
3451  * \param vmu
3452  * \param imbox
3453  * \param msgnum
3454  * \param duration
3455  * \param recip
3456  * \param fmt
3457  * \param dir
3458  *
3459  * This works with IMAP storage based mailboxes.
3460  *
3461  * \return zero on success, -1 on error.
3462  */
3463 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)
3464 {
3465         struct vm_state *sendvms = NULL, *destvms = NULL;
3466         char messagestring[10]; /* I guess this could be a problem if someone has more than 999,999,999 messages... */
3467         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3468                 ast_log(AST_LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3469                 return -1;
3470         }
3471         if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
3472                 ast_log(AST_LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3473                 return -1;
3474         }
3475         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3476         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
3477                 return 0;
3478         ast_log(AST_LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3479         return -1;
3480 }
3481
3482 #endif
3483 #ifndef IMAP_STORAGE
3484 /*! 
3485  * \brief Copies a message from one mailbox to another.
3486  * \param chan
3487  * \param vmu
3488  * \param imbox
3489  * \param msgnum
3490  * \param duration
3491  * \param recip
3492  * \param fmt
3493  * \param dir
3494  *
3495  * This is only used by file storage based mailboxes.
3496  *
3497  * \return zero on success, -1 on error.
3498  */
3499 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)
3500 {
3501         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
3502         const char *frombox = mbox(imbox);
3503         int recipmsgnum;
3504
3505         ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
3506
3507         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
3508                 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
3509         } else {
3510                 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3511         }
3512         
3513         if (!dir)
3514                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
3515         else
3516                 ast_copy_string(fromdir, dir, sizeof(fromdir));
3517
3518         make_file(frompath, sizeof(frompath), fromdir, msgnum);
3519         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3520
3521         if (vm_lock_path(todir))
3522                 return ERROR_LOCK_PATH;
3523
3524         recipmsgnum = last_message_index(recip, todir) + 1;
3525         if (recipmsgnum < recip->maxmsg) {
3526                 make_file(topath, sizeof(topath), todir, recipmsgnum);
3527                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
3528         } else {
3529                 ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
3530         }
3531         ast_unlock_path(todir);
3532         notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
3533         
3534         return 0;
3535 }
3536 #endif
3537 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
3538
3539 static int messagecount(const char *context, const char *mailbox, const char *folder)
3540 {
3541         return __has_voicemail(context, mailbox, folder, 0);
3542 }
3543
3544 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
3545 {
3546         DIR *dir;
3547         struct dirent *de;
3548         char fn[256];
3549         int ret = 0;
3550
3551         /* If no mailbox, return immediately */
3552         if (ast_strlen_zero(mailbox))
3553                 return 0;
3554
3555         if (ast_strlen_zero(folder))
3556                 folder = "INBOX";
3557         if (ast_strlen_zero(context))
3558                 context = "default";
3559
3560         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
3561
3562         if (!(dir = opendir(fn)))
3563                 return 0;
3564
3565         while ((de = readdir(dir))) {
3566                 if (!strncasecmp(de->d_name, "msg", 3)) {
3567                         if (shortcircuit) {
3568                                 ret = 1;
3569                                 break;
3570                         } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
3571                                 if (shortcircuit) return 1;
3572                                 ret++;
3573                         }
3574                 }
3575         }
3576
3577         closedir(dir);
3578
3579         /* If we are checking INBOX, we should check Urgent as well */
3580         if (strcmp(folder, "INBOX") == 0) {
3581                 return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
3582         } else {