9f0f21180f43933892e8cb629650d3988c347050
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref Unixodbc - http://www.unixodbc.org
26  * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
28  * 
29  * \par See also
30  * \arg \ref Config_vm
31  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32  * \ingroup applications
33  * \note This module requires res_adsi to load. This needs to be optional
34  * during compilation.
35  *
36  *
37  *
38  * \note  This file is now almost impossible to work with, due to all \#ifdefs.
39  *        Feels like the database code before realtime. Someone - please come up
40  *        with a plan to clean this up.
41  */
42
43 /*** MODULEINFO
44         <depend>res_smdi</depend>
45  ***/
46
47 /*** MAKEOPTS
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
49         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50                 <depend>unixodbc</depend>
51                 <depend>ltdl</depend>
52                 <conflict>IMAP_STORAGE</conflict>
53                 <defaultenabled>no</defaultenabled>
54         </member>
55         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
56                 <depend>imap_tk</depend>
57                 <conflict>ODBC_STORAGE</conflict>
58                 <use>ssl</use>
59                 <defaultenabled>no</defaultenabled>
60         </member>
61 </category>
62  ***/
63
64 /* It is important to include the IMAP_STORAGE related headers
65  * before asterisk.h since asterisk.h includes logger.h. logger.h
66  * and c-client.h have conflicting definitions for AST_LOG_WARNING and
67  * AST_LOG_DEBUG, so it's important that we use Asterisk's definitions
68  * here instead of the c-client's 
69  */
70 #ifdef IMAP_STORAGE
71 #include <ctype.h>
72 #include <signal.h>
73 #include <pwd.h>
74 #ifdef USE_SYSTEM_IMAP
75 #include <imap/c-client.h>
76 #include <imap/imap4r1.h>
77 #include <imap/linkage.h>
78 #elif defined (USE_SYSTEM_CCLIENT)
79 #include <c-client/c-client.h>
80 #include <c-client/imap4r1.h>
81 #include <c-client/linkage.h>
82 #else
83 #include "c-client.h"
84 #include "imap4r1.h"
85 #include "linkage.h"
86 #endif
87 #endif
88
89 #include "asterisk.h"
90
91 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
92
93 #include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
94 #include <sys/time.h>
95 #include <sys/stat.h>
96 #include <sys/mman.h>
97 #include <time.h>
98 #include <dirent.h>
99
100 #include "asterisk/lock.h"
101 #include "asterisk/file.h"
102 #include "asterisk/channel.h"
103 #include "asterisk/pbx.h"
104 #include "asterisk/config.h"
105 #include "asterisk/say.h"
106 #include "asterisk/module.h"
107 #include "asterisk/adsi.h"
108 #include "asterisk/app.h"
109 #include "asterisk/manager.h"
110 #include "asterisk/dsp.h"
111 #include "asterisk/localtime.h"
112 #include "asterisk/cli.h"
113 #include "asterisk/utils.h"
114 #include "asterisk/stringfields.h"
115 #include "asterisk/smdi.h"
116 #include "asterisk/event.h"
117 #include "asterisk/taskprocessor.h"
118
119 #ifdef ODBC_STORAGE
120 #include "asterisk/res_odbc.h"
121 #endif
122
123 #ifdef IMAP_STORAGE
124 static char imapserver[48];
125 static char imapport[8];
126 static char imapflags[128];
127 static char imapfolder[64];
128 static char greetingfolder[64];
129 static char authuser[32];
130 static char authpassword[42];
131
132 static int expungeonhangup = 1;
133 static int imapgreetings = 0;
134 static char delimiter = '\0';
135
136 struct vm_state;
137 struct ast_vm_user;
138
139 /* Forward declarations for IMAP */
140 static int init_mailstream(struct vm_state *vms, int box);
141 static void write_file(char *filename, char *buffer, unsigned long len);
142 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
143 static void vm_imap_delete(int msgnum, struct vm_state *vms);
144 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
145 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
146 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
147 static void vmstate_insert(struct vm_state *vms);
148 static void vmstate_delete(struct vm_state *vms);
149 static void set_update(MAILSTREAM * stream);
150 static void init_vm_state(struct vm_state *vms);
151 static void check_msgArray(struct vm_state *vms);
152 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
153 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, char *altfile);
154 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix);
155 static void get_mailbox_delimiter(MAILSTREAM *stream);
156 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
157 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
158 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, char *introfile, const char *flag);
159 static void update_messages_by_imapuser(const char *user, unsigned long number);
160
161 static int imap_remove_file (char *dir, int msgnum);
162 static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, const char *context);
163 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
164 static void check_quota(struct vm_state *vms, char *mailbox);
165 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
166 struct vmstate {
167         struct vm_state *vms;
168         AST_LIST_ENTRY(vmstate) list;
169 };
170
171 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
172
173 #endif
174
175 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
176
177 #define COMMAND_TIMEOUT 5000
178 /* Don't modify these here; set your umask at runtime instead */
179 #define VOICEMAIL_DIR_MODE      0777
180 #define VOICEMAIL_FILE_MODE     0666
181 #define CHUNKSIZE       65536
182
183 #define VOICEMAIL_CONFIG "voicemail.conf"
184 #define ASTERISK_USERNAME "asterisk"
185
186 /* Define fast-forward, pause, restart, and reverse keys
187    while listening to a voicemail message - these are
188    strings, not characters */
189 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
190 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
191 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
192 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
193 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
194 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
195
196 /* Default mail command to mail voicemail. Change it with the
197     mailcmd= command in voicemail.conf */
198 #define SENDMAIL "/usr/sbin/sendmail -t"
199
200 #define INTRO "vm-intro"
201
202 #define MAXMSG 100
203 #define MAXMSGLIMIT 9999
204
205 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
206
207 #define BASELINELEN 72
208 #define BASEMAXINLINE 256
209 #define eol "\r\n"
210
211 #define MAX_DATETIME_FORMAT     512
212 #define MAX_NUM_CID_CONTEXTS 10
213
214 #define VM_REVIEW        (1 << 0)   /*!< After recording, permit the caller to review the recording before saving */
215 #define VM_OPERATOR      (1 << 1)   /*!< Allow 0 to be pressed to go to 'o' extension */
216 #define VM_SAYCID        (1 << 2)   /*!< Repeat the CallerID info during envelope playback */
217 #define VM_SVMAIL        (1 << 3)   /*!< Allow the user to compose a new VM from within VoicemailMain */
218 #define VM_ENVELOPE      (1 << 4)   /*!< Play the envelope information (who-from, time received, etc.) */
219 #define VM_SAYDURATION   (1 << 5)   /*!< Play the length of the message during envelope playback */
220 #define VM_SKIPAFTERCMD  (1 << 6)   /*!< After deletion, assume caller wants to go to the next message */
221 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
222 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
223 #define VM_PBXSKIP       (1 << 9)   /*!< Skip the [PBX] preamble in the Subject line of emails */
224 #define VM_DIRECFORWARD  (1 << 10)  /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
225 #define VM_ATTACH        (1 << 11)  /*!< Attach message to voicemail notifications? */
226 #define VM_DELETE        (1 << 12)  /*!< Delete message after sending notification */
227 #define VM_ALLOCED       (1 << 13)  /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
228 #define VM_SEARCH        (1 << 14)  /*!< Search all contexts for a matching mailbox */
229 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
230 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
231 #define VM_MESSAGEWRAP   (1 << 17)  /*!< Wrap around from the last message to the first, and vice-versa */
232 #define ERROR_LOCK_PATH  -100
233
234
235 enum {
236         NEW_FOLDER,
237         OLD_FOLDER,
238         WORK_FOLDER,
239         FAMILY_FOLDER,
240         FRIENDS_FOLDER,
241         GREETINGS_FOLDER
242 } vm_box;
243
244 enum {
245         OPT_SILENT =           (1 << 0),
246         OPT_BUSY_GREETING =    (1 << 1),
247         OPT_UNAVAIL_GREETING = (1 << 2),
248         OPT_RECORDGAIN =       (1 << 3),
249         OPT_PREPEND_MAILBOX =  (1 << 4),
250         OPT_AUTOPLAY =         (1 << 6),
251         OPT_DTMFEXIT =         (1 << 7),
252         OPT_MESSAGE_Urgent =   (1 << 8),
253         OPT_MESSAGE_PRIORITY = (1 << 9)
254 } vm_option_flags;
255
256 enum {
257         OPT_ARG_RECORDGAIN = 0,
258         OPT_ARG_PLAYFOLDER = 1,
259         OPT_ARG_DTMFEXIT   = 2,
260         /* This *must* be the last value in this enum! */
261         OPT_ARG_ARRAY_SIZE = 3,
262 } vm_option_args;
263
264 AST_APP_OPTIONS(vm_app_options, {
265         AST_APP_OPTION('s', OPT_SILENT),
266         AST_APP_OPTION('b', OPT_BUSY_GREETING),
267         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
268         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
269         AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
270         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
271         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
272         AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
273         AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
274 });
275
276 static int load_config(int reload);
277
278 /*! \page vmlang Voicemail Language Syntaxes Supported
279
280         \par Syntaxes supported, not really language codes.
281         \arg \b en    - English
282         \arg \b de    - German
283         \arg \b es    - Spanish
284         \arg \b fr    - French
285         \arg \b it    - Italian
286         \arg \b nl    - Dutch
287         \arg \b pt    - Portuguese
288         \arg \b pt_BR - Portuguese (Brazil)
289         \arg \b gr    - Greek
290         \arg \b no    - Norwegian
291         \arg \b se    - Swedish
292         \arg \b tw    - Chinese (Taiwan)
293         \arg \b ua - Ukrainian
294
295 German requires the following additional soundfile:
296 \arg \b 1F      einE (feminine)
297
298 Spanish requires the following additional soundfile:
299 \arg \b 1M      un (masculine)
300
301 Dutch, Portuguese & Spanish require the following additional soundfiles:
302 \arg \b vm-INBOXs       singular of 'new'
303 \arg \b vm-Olds         singular of 'old/heard/read'
304
305 NB these are plural:
306 \arg \b vm-INBOX        nieuwe (nl)
307 \arg \b vm-Old          oude (nl)
308
309 Polish uses:
310 \arg \b vm-new-a        'new', feminine singular accusative
311 \arg \b vm-new-e        'new', feminine plural accusative
312 \arg \b vm-new-ych      'new', feminine plural genitive
313 \arg \b vm-old-a        'old', feminine singular accusative
314 \arg \b vm-old-e        'old', feminine plural accusative
315 \arg \b vm-old-ych      'old', feminine plural genitive
316 \arg \b digits/1-a      'one', not always same as 'digits/1'
317 \arg \b digits/2-ie     'two', not always same as 'digits/2'
318
319 Swedish uses:
320 \arg \b vm-nytt         singular of 'new'
321 \arg \b vm-nya          plural of 'new'
322 \arg \b vm-gammalt      singular of 'old'
323 \arg \b vm-gamla        plural of 'old'
324 \arg \b digits/ett      'one', not always same as 'digits/1'
325
326 Norwegian uses:
327 \arg \b vm-ny           singular of 'new'
328 \arg \b vm-nye          plural of 'new'
329 \arg \b vm-gammel       singular of 'old'
330 \arg \b vm-gamle        plural of 'old'
331
332 Dutch also uses:
333 \arg \b nl-om           'at'?
334
335 Spanish also uses:
336 \arg \b vm-youhaveno
337
338 Ukrainian requires the following additional soundfile:
339 \arg \b vm-nove         'nove'
340 \arg \b vm-stare        'stare'
341 \arg \b digits/ua/1e    'odne'
342
343 Italian requires the following additional soundfile:
344
345 For vm_intro_it:
346 \arg \b vm-nuovo        new
347 \arg \b vm-nuovi        new plural
348 \arg \b vm-vecchio      old
349 \arg \b vm-vecchi       old plural
350
351 Chinese (Taiwan) requires the following additional soundfile:
352 \arg \b vm-tong         A class-word for call (tong1)
353 \arg \b vm-ri           A class-word for day (ri4)
354 \arg \b vm-you          You (ni3)
355 \arg \b vm-haveno   Have no (mei2 you3)
356 \arg \b vm-have     Have (you3)
357 \arg \b vm-listen   To listen (yao4 ting1)
358
359
360 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
361 spelled among others when you have to change folder. For the above reasons, vm-INBOX
362 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
363
364 */
365
366 struct baseio {
367         int iocp;
368         int iolen;
369         int linelength;
370         int ateof;
371         unsigned char iobuf[BASEMAXINLINE];
372 };
373
374 /*! Structure for linked list of users 
375  * Use ast_vm_user_destroy() to free one of these structures. */
376 struct ast_vm_user {
377         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
378         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
379         char password[80];               /*!< Secret pin code, numbers only */
380         char fullname[80];               /*!< Full name, for directory app */
381         char email[80];                  /*!< E-mail address */
382         char pager[80];                  /*!< E-mail address to pager (no attachment) */
383         char serveremail[80];            /*!< From: Mail address */
384         char mailcmd[160];               /*!< Configurable mail command */
385         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
386         char zonetag[80];                /*!< Time zone */
387         char callback[80];
388         char dialout[80];
389         char uniqueid[80];               /*!< Unique integer identifier */
390         char exit[80];
391         char attachfmt[20];              /*!< Attachment format */
392         unsigned int flags;              /*!< VM_ flags */      
393         int saydurationm;
394         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
395         int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
396         int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
397 #ifdef IMAP_STORAGE
398         char imapuser[80];               /*!< IMAP server login */
399         char imappassword[80];           /*!< IMAP server password if authpassword not defined */
400 #endif
401         double volgain;                  /*!< Volume gain for voicemails sent via email */
402         AST_LIST_ENTRY(ast_vm_user) list;
403 };
404
405 /*! Voicemail time zones */
406 struct vm_zone {
407         AST_LIST_ENTRY(vm_zone) list;
408         char name[80];
409         char timezone[80];
410         char msg_format[512];
411 };
412
413 #define VMSTATE_MAX_MSG_ARRAY 256
414
415 /*! Voicemail mailbox state */
416 struct vm_state {
417         char curbox[80];
418         char username[80];
419         char curdir[PATH_MAX];
420         char vmbox[PATH_MAX];
421         char fn[PATH_MAX];
422         char fn2[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
706 /*!
707  * \brief Sets default voicemail system options to a voicemail user.
708  *
709  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
710  * - all the globalflags
711  * - the saydurationminfo
712  * - the callcontext
713  * - the dialcontext
714  * - the exitcontext
715  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
716  * - volume gain.
717  */
718 static void populate_defaults(struct ast_vm_user *vmu)
719 {
720         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
721         if (saydurationminfo)
722                 vmu->saydurationm = saydurationminfo;
723         ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
724         ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
725         ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
726         if (vmmaxsecs)
727                 vmu->maxsecs = vmmaxsecs;
728         if (maxmsg)
729                 vmu->maxmsg = maxmsg;
730         if (maxdeletedmsg)
731                 vmu->maxdeletedmsg = maxdeletedmsg;
732         vmu->volgain = volgain;
733 }
734
735 /*!
736  * \brief Sets a a specific property value.
737  * \param vmu The voicemail user object to work with.
738  * \param var The name of the property to be set.
739  * \param value The value to be set to the property.
740  * 
741  * The property name must be one of the understood properties. See the source for details.
742  */
743 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
744 {
745         int x;
746         if (!strcasecmp(var, "attach")) {
747                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
748         } else if (!strcasecmp(var, "attachfmt")) {
749                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
750         } else if (!strcasecmp(var, "serveremail")) {
751                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
752         } else if (!strcasecmp(var, "language")) {
753                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
754         } else if (!strcasecmp(var, "tz")) {
755                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
756 #ifdef IMAP_STORAGE
757         } else if (!strcasecmp(var, "imapuser")) {
758                 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
759         } else if (!strcasecmp(var, "imappassword")) {
760                 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
761 #endif
762         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
763                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
764         } else if (!strcasecmp(var, "saycid")){
765                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
766         } else if (!strcasecmp(var,"sendvoicemail")){
767                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
768         } else if (!strcasecmp(var, "review")){
769                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
770         } else if (!strcasecmp(var, "tempgreetwarn")){
771                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
772         } else if (!strcasecmp(var, "messagewrap")){
773                 ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);    
774         } else if (!strcasecmp(var, "operator")) {
775                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
776         } else if (!strcasecmp(var, "envelope")){
777                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
778         } else if (!strcasecmp(var, "moveheard")){
779                 ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
780         } else if (!strcasecmp(var, "sayduration")){
781                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
782         } else if (!strcasecmp(var, "saydurationm")){
783                 if (sscanf(value, "%d", &x) == 1) {
784                         vmu->saydurationm = x;
785                 } else {
786                         ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
787                 }
788         } else if (!strcasecmp(var, "forcename")){
789                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
790         } else if (!strcasecmp(var, "forcegreetings")){
791                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
792         } else if (!strcasecmp(var, "callback")) {
793                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
794         } else if (!strcasecmp(var, "dialout")) {
795                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
796         } else if (!strcasecmp(var, "exitcontext")) {
797                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
798         } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
799                 if (vmu->maxsecs <= 0) {
800                         ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
801                         vmu->maxsecs = vmmaxsecs;
802                 } else {
803                         vmu->maxsecs = atoi(value);
804                 }
805                 if (!strcasecmp(var, "maxmessage"))
806                         ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
807         } else if (!strcasecmp(var, "maxmsg")) {
808                 vmu->maxmsg = atoi(value);
809                 if (vmu->maxmsg <= 0) {
810                         ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
811                         vmu->maxmsg = MAXMSG;
812                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
813                         ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
814                         vmu->maxmsg = MAXMSGLIMIT;
815                 }
816         } else if (!strcasecmp(var, "backupdeleted")) {
817                 if (sscanf(value, "%d", &x) == 1)
818                         vmu->maxdeletedmsg = x;
819                 else if (ast_true(value))
820                         vmu->maxdeletedmsg = MAXMSG;
821                 else
822                         vmu->maxdeletedmsg = 0;
823
824                 if (vmu->maxdeletedmsg < 0) {
825                         ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
826                         vmu->maxdeletedmsg = MAXMSG;
827                 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
828                         ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
829                         vmu->maxdeletedmsg = MAXMSGLIMIT;
830                 }
831         } else if (!strcasecmp(var, "volgain")) {
832                 sscanf(value, "%lf", &vmu->volgain);
833         } else if (!strcasecmp(var, "options")) {
834                 apply_options(vmu, value);
835         }
836 }
837
838 static char *vm_check_password_shell(char *command, char *buf, size_t len) 
839 {
840         int fds[2], pid = 0;
841
842         memset(buf, 0, len);
843
844         if (pipe(fds)) {
845                 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
846         } else {
847                 /* good to go*/
848                 pid = ast_safe_fork(0);
849
850                 if (pid < 0) {
851                         /* ok maybe not */
852                         close(fds[0]);
853                         close(fds[1]);
854                         snprintf(buf, len, "FAILURE: Fork failed");
855                 } else if (pid) {
856                         /* parent */
857                         close(fds[1]);
858                         read(fds[0], buf, len);
859                         close(fds[0]);
860                 } else {
861                         /*  child */
862                         AST_DECLARE_APP_ARGS(arg,
863                                 AST_APP_ARG(v)[20];
864                         );
865                         char *mycmd = ast_strdupa(command);
866
867                         close(fds[0]);
868                         dup2(fds[1], STDOUT_FILENO);
869                         close(fds[1]);
870                         ast_close_fds_above_n(STDOUT_FILENO);
871
872                         AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
873
874                         execv(arg.v[0], arg.v); 
875                         printf("FAILURE: %s", strerror(errno));
876                         _exit(0);
877                 }
878         }
879         return buf;
880 }
881
882 /*!
883  * \brief Check that password meets minimum required length
884  * \param vmu The voicemail user to change the password for.
885  * \param password The password string to check
886  *
887  * \return zero on ok, 1 on not ok.
888  */
889 static int check_password(struct ast_vm_user *vmu, char *password)
890 {
891         /* check minimum length */
892         if (strlen(password) < minpassword)
893                 return 1;
894         if (!ast_strlen_zero(ext_pass_check_cmd)) {
895                 char cmd[255], buf[255];
896
897                 ast_log(LOG_DEBUG, "Verify password policies for %s\n", password);
898
899                 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
900                 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
901                         ast_debug(5, "Result: %s\n", buf);
902                         if (!strncasecmp(buf, "VALID", 5)) {
903                                 ast_debug(3, "Passed password check: '%s'\n", buf);
904                                 return 0;
905                         } else if (!strncasecmp(buf, "FAILURE", 7)) {
906                                 ast_log(LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
907                                 return 0;
908                         } else {
909                                 ast_log(LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
910                                 return 1;
911                         }
912                 }
913         }
914         return 0;
915 }
916
917 /*! 
918  * \brief Performs a change of the voicemail passowrd in the realtime engine.
919  * \param vmu The voicemail user to change the password for.
920  * \param password The new value to be set to the password for this user.
921  * 
922  * This only works if the voicemail user has a unique id, and if there is a realtime engine configured.
923  * This is called from the (top level) vm_change_password.
924  *
925  * \return zero on success, -1 on error.
926  */
927 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
928 {
929         int res;
930         if (!ast_strlen_zero(vmu->uniqueid)) {
931                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
932                 if (res > 0) {
933                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
934                         res = 0;
935                 } else if (!res) {
936                         res = -1;
937                 }
938                 return res;
939         }
940         return -1;
941 }
942
943 /*!
944  * \brief Destructively Parse options and apply.
945  */
946 static void apply_options(struct ast_vm_user *vmu, const char *options)
947 {       
948         char *stringp;
949         char *s;
950         char *var, *value;
951         stringp = ast_strdupa(options);
952         while ((s = strsep(&stringp, "|"))) {
953                 value = s;
954                 if ((var = strsep(&value, "=")) && value) {
955                         apply_option(vmu, var, value);
956                 }
957         }       
958 }
959
960 /*!
961  * \brief Loads the options specific to a voicemail user.
962  * 
963  * This is called when a vm_user structure is being set up, such as from load_options.
964  */
965 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
966 {
967         struct ast_variable *tmp;
968         tmp = var;
969         while (tmp) {
970                 if (!strcasecmp(tmp->name, "vmsecret")) {
971                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
972                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
973                         if (ast_strlen_zero(retval->password))
974                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
975                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
976                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
977                 } else if (!strcasecmp(tmp->name, "pager")) {
978                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
979                 } else if (!strcasecmp(tmp->name, "email")) {
980                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
981                 } else if (!strcasecmp(tmp->name, "fullname")) {
982                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
983                 } else if (!strcasecmp(tmp->name, "context")) {
984                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
985 #ifdef IMAP_STORAGE
986                 } else if (!strcasecmp(tmp->name, "imapuser")) {
987                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
988                 } else if (!strcasecmp(tmp->name, "imappassword")) {
989                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
990 #endif
991                 } else
992                         apply_option(retval, tmp->name, tmp->value);
993                 tmp = tmp->next;
994         } 
995 }
996
997 /*!
998  * \brief Determines if a DTMF key entered is valid.
999  * \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.
1000  *
1001  * Tests the character entered against the set of valid DTMF characters. 
1002  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1003  */
1004 static int is_valid_dtmf(const char *key)
1005 {
1006         int i;
1007         char *local_key = ast_strdupa(key);
1008
1009         for (i = 0; i < strlen(key); ++i) {
1010                 if (!strchr(VALID_DTMF, *local_key)) {
1011                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1012                         return 0;
1013                 }
1014                 local_key++;
1015         }
1016         return 1;
1017 }
1018
1019 /*!
1020  * \brief Finds a voicemail user from the realtime engine.
1021  * \param ivm
1022  * \param context
1023  * \param mailbox
1024  *
1025  * 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.
1026  *
1027  * \return The ast_vm_user structure for the user that was found.
1028  */
1029 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1030 {
1031         struct ast_variable *var;
1032         struct ast_vm_user *retval;
1033
1034         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1035                 if (!ivm)
1036                         ast_set_flag(retval, VM_ALLOCED);       
1037                 else
1038                         memset(retval, 0, sizeof(*retval));
1039                 if (mailbox) 
1040                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1041                 populate_defaults(retval);
1042                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
1043                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
1044                 else
1045                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
1046                 if (var) {
1047                         apply_options_full(retval, var);
1048                         ast_variables_destroy(var);
1049                 } else { 
1050                         if (!ivm) 
1051                                 ast_free(retval);
1052                         retval = NULL;
1053                 }       
1054         } 
1055         return retval;
1056 }
1057
1058 /*!
1059  * \brief Finds a voicemail user from the users file or the realtime engine.
1060  * \param ivm
1061  * \param context
1062  * \param mailbox
1063  * 
1064  * \return The ast_vm_user structure for the user that was found.
1065  */
1066 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1067 {
1068         /* This function could be made to generate one from a database, too */
1069         struct ast_vm_user *vmu=NULL, *cur;
1070         AST_LIST_LOCK(&users);
1071
1072         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1073                 context = "default";
1074
1075         AST_LIST_TRAVERSE(&users, cur, list) {
1076                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1077                         break;
1078                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1079                         break;
1080         }
1081         if (cur) {
1082                 /* Make a copy, so that on a reload, we have no race */
1083                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1084                         memcpy(vmu, cur, sizeof(*vmu));
1085                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1086                         AST_LIST_NEXT(vmu, list) = NULL;
1087                 }
1088         } else
1089                 vmu = find_user_realtime(ivm, context, mailbox);
1090         AST_LIST_UNLOCK(&users);
1091         return vmu;
1092 }
1093
1094 /*!
1095  * \brief Resets a user password to a specified password.
1096  * \param context
1097  * \param mailbox
1098  * \param newpass
1099  *
1100  * This does the actual change password work, called by the vm_change_password() function.
1101  *
1102  * \return zero on success, -1 on error.
1103  */
1104 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1105 {
1106         /* This function could be made to generate one from a database, too */
1107         struct ast_vm_user *cur;
1108         int res = -1;
1109         AST_LIST_LOCK(&users);
1110         AST_LIST_TRAVERSE(&users, cur, list) {
1111                 if ((!context || !strcasecmp(context, cur->context)) &&
1112                         (!strcasecmp(mailbox, cur->mailbox)))
1113                                 break;
1114         }
1115         if (cur) {
1116                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1117                 res = 0;
1118         }
1119         AST_LIST_UNLOCK(&users);
1120         return res;
1121 }
1122
1123 /*! 
1124  * \brief The handler for the change password option.
1125  * \param vmu The voicemail user to work with.
1126  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1127  * 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.
1128  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1129  */
1130 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1131 {
1132         struct ast_config   *cfg=NULL;
1133         struct ast_variable *var=NULL;
1134         struct ast_category *cat=NULL;
1135         char *category=NULL, *value=NULL, *new=NULL;
1136         const char *tmp=NULL;
1137         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1138         if (!change_password_realtime(vmu, newpassword))
1139                 return;
1140
1141         /* check voicemail.conf */
1142         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
1143                 while ((category = ast_category_browse(cfg, category))) {
1144                         if (!strcasecmp(category, vmu->context)) {
1145                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1146                                         ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1147                                         break;
1148                                 }
1149                                 value = strstr(tmp,",");
1150                                 if (!value) {
1151                                         ast_log(AST_LOG_WARNING, "variable has bad format.\n");
1152                                         break;
1153                                 }
1154                                 new = alloca((strlen(value)+strlen(newpassword)+1));
1155                                 sprintf(new,"%s%s", newpassword, value);
1156                                 if (!(cat = ast_category_get(cfg, category))) {
1157                                         ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1158                                         break;
1159                                 }
1160                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1161                         }
1162                 }
1163                 /* save the results */
1164                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1165                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1166                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1167         }
1168         category = NULL;
1169         var = NULL;
1170         /* check users.conf and update the password stored for the mailbox*/
1171         /* if no vmsecret entry exists create one. */
1172         if ((cfg = ast_config_load("users.conf", config_flags))) {
1173                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1174                 while ((category = ast_category_browse(cfg, category))) {
1175                         ast_debug(4, "users.conf: %s\n", category);
1176                         if (!strcasecmp(category, vmu->mailbox)) {
1177                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
1178                                         ast_debug(3, "looks like we need to make vmsecret!\n");
1179                                         var = ast_variable_new("vmsecret", newpassword, "");
1180                                 } 
1181                                 new = alloca(strlen(newpassword)+1);
1182                                 sprintf(new, "%s", newpassword);
1183                                 if (!(cat = ast_category_get(cfg, category))) {
1184                                         ast_debug(4, "failed to get category!\n");
1185                                         break;
1186                                 }
1187                                 if (!var)               
1188                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
1189                                 else
1190                                         ast_variable_append(cat, var);
1191                         }
1192                 }
1193                 /* save the results and clean things up */
1194                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
1195                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1196                 config_text_file_save("users.conf", cfg, "AppVoicemail");
1197         }
1198 }
1199
1200 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1201 {
1202         char buf[255];
1203         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
1204         if (!ast_safe_system(buf)) {
1205                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1206                 /* Reset the password in memory, too */
1207                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1208         }
1209 }
1210
1211 /*! 
1212  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1213  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1214  * \param len The length of the path string that was written out.
1215  * 
1216  * The path is constructed as 
1217  *      VM_SPOOL_DIRcontext/ext/folder
1218  *
1219  * \return zero on success, -1 on error.
1220  */
1221 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1222 {
1223         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1224 }
1225
1226 #ifdef IMAP_STORAGE
1227 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix)
1228 {
1229         int res;
1230         if ((res = ast_mkdir(dir, 01777))) {
1231                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1232                 return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
1233         }
1234         return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
1235 }
1236
1237 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1238 {
1239         unsigned long messageNum = 0;
1240         char arg[10];
1241
1242         /* find real message number based on msgnum */
1243         /* this may be an index into vms->msgArray based on the msgnum. */
1244
1245         messageNum = vms->msgArray[msgnum];
1246         if (messageNum == 0) {
1247                 ast_log(AST_LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1248                 return;
1249         }
1250         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1251         /* delete message */
1252         snprintf (arg, sizeof(arg), "%lu",messageNum);
1253         mail_setflag (vms->mailstream,arg,"\\DELETED");
1254 }
1255
1256 #endif
1257 static int make_file(char *dest, int len, char *dir, int num)
1258 {
1259         return snprintf(dest, len, "%s/msg%04d", dir, num);
1260 }
1261
1262 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1263  * \param dest    String. base directory.
1264  * \param len     Length of dest.
1265  * \param context String. Ignored if is null or empty string.
1266  * \param ext     String. Ignored if is null or empty string.
1267  * \param folder  String. Ignored if is null or empty string. 
1268  * \return -1 on failure, 0 on success.
1269  */
1270 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1271 {
1272         mode_t  mode = VOICEMAIL_DIR_MODE;
1273         int res;
1274
1275         make_dir(dest, len, context, ext, folder);
1276         if ((res = ast_mkdir(dest, mode))) {
1277                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1278                 return -1;
1279         }
1280         return 0;
1281 }
1282
1283 /*! \brief Lock file path
1284     only return failure if ast_lock_path returns 'timeout',
1285    not if the path does not exist or any other reason
1286 */
1287 static int vm_lock_path(const char *path)
1288 {
1289         switch (ast_lock_path(path)) {
1290         case AST_LOCK_TIMEOUT:
1291                 return -1;
1292         default:
1293                 return 0;
1294         }
1295 }
1296
1297
1298 #ifdef ODBC_STORAGE
1299 struct generic_prepare_struct {
1300         char *sql;
1301         int argc;
1302         char **argv;
1303 };
1304
1305 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1306 {
1307         struct generic_prepare_struct *gps = data;
1308         int res, i;
1309         SQLHSTMT stmt;
1310
1311         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1312         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1313                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
1314                 return NULL;
1315         }
1316         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1317         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1318                 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1319                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1320                 return NULL;
1321         }
1322         for (i = 0; i < gps->argc; i++)
1323                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1324
1325         return stmt;
1326 }
1327
1328 /*!
1329  * \brief Retrieves a file from an ODBC data store.
1330  * \param dir the path to the file to be retreived.
1331  * \param msgnum the message number, such as within a mailbox folder.
1332  * 
1333  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
1334  * 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.
1335  *
1336  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
1337  * The output is the message information file with the name msgnum and the extension .txt
1338  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
1339  * 
1340  * \return 0 on success, -1 on error.
1341  */
1342 static int retrieve_file(char *dir, int msgnum)
1343 {
1344         int x = 0;
1345         int res;
1346         int fd=-1;
1347         size_t fdlen = 0;
1348         void *fdm = MAP_FAILED;
1349         SQLSMALLINT colcount=0;
1350         SQLHSTMT stmt;
1351         char sql[PATH_MAX];
1352         char fmt[80]="";
1353         char *c;
1354         char coltitle[256];
1355         SQLSMALLINT collen;
1356         SQLSMALLINT datatype;
1357         SQLSMALLINT decimaldigits;
1358         SQLSMALLINT nullable;
1359         SQLULEN colsize;
1360         SQLLEN colsize2;
1361         FILE *f=NULL;
1362         char rowdata[80];
1363         char fn[PATH_MAX];
1364         char full_fn[PATH_MAX];
1365         char msgnums[80];
1366         char *argv[] = { dir, msgnums };
1367         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1368
1369         struct odbc_obj *obj;
1370         obj = ast_odbc_request_obj(odbc_database, 0);
1371         if (obj) {
1372                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1373                 c = strchr(fmt, '|');
1374                 if (c)
1375                         *c = '\0';
1376                 if (!strcasecmp(fmt, "wav49"))
1377                         strcpy(fmt, "WAV");
1378                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1379                 if (msgnum > -1)
1380                         make_file(fn, sizeof(fn), dir, msgnum);
1381                 else
1382                         ast_copy_string(fn, dir, sizeof(fn));
1383
1384                 /* Create the information file */
1385                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1386                 
1387                 if (!(f = fopen(full_fn, "w+"))) {
1388                         ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1389                         goto yuck;
1390                 }
1391                 
1392                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1393                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1394                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1395                 if (!stmt) {
1396                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1397                         ast_odbc_release_obj(obj);
1398                         goto yuck;
1399                 }
1400                 res = SQLFetch(stmt);
1401                 if (res == SQL_NO_DATA) {
1402                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1403                         ast_odbc_release_obj(obj);
1404                         goto yuck;
1405                 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1406                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1407                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1408                         ast_odbc_release_obj(obj);
1409                         goto yuck;
1410                 }
1411                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1412                 if (fd < 0) {
1413                         ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1414                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1415                         ast_odbc_release_obj(obj);
1416                         goto yuck;
1417                 }
1418                 res = SQLNumResultCols(stmt, &colcount);
1419                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1420                         ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1421                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1422                         ast_odbc_release_obj(obj);
1423                         goto yuck;
1424                 }
1425                 if (f) 
1426                         fprintf(f, "[message]\n");
1427                 for (x=0;x<colcount;x++) {
1428                         rowdata[0] = '\0';
1429                         collen = sizeof(coltitle);
1430                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1431                                                 &datatype, &colsize, &decimaldigits, &nullable);
1432                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1433                                 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1434                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1435                                 ast_odbc_release_obj(obj);
1436                                 goto yuck;
1437                         }
1438                         if (!strcasecmp(coltitle, "recording")) {
1439                                 off_t offset;
1440                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1441                                 fdlen = colsize2;
1442                                 if (fd > -1) {
1443                                         char tmp[1]="";
1444                                         lseek(fd, fdlen - 1, SEEK_SET);
1445                                         if (write(fd, tmp, 1) != 1) {
1446                                                 close(fd);
1447                                                 fd = -1;
1448                                                 continue;
1449                                         }
1450                                         /* Read out in small chunks */
1451                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1452                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1453                                                         ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1454                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1455                                                         ast_odbc_release_obj(obj);
1456                                                         goto yuck;
1457                                                 } else {
1458                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1459                                                         munmap(fdm, CHUNKSIZE);
1460                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1461                                                                 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1462                                                                 unlink(full_fn);
1463                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1464                                                                 ast_odbc_release_obj(obj);
1465                                                                 goto yuck;
1466                                                         }
1467                                                 }
1468                                         }
1469                                         truncate(full_fn, fdlen);
1470                                 }
1471                         } else {
1472                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1473                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1474                                         ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
1475                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1476                                         ast_odbc_release_obj(obj);
1477                                         goto yuck;
1478                                 }
1479                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1480                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1481                         }
1482                 }
1483                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1484                 ast_odbc_release_obj(obj);
1485         } else
1486                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1487 yuck:   
1488         if (f)
1489                 fclose(f);
1490         if (fd > -1)
1491                 close(fd);
1492         return x - 1;
1493 }
1494
1495 /*!
1496  * \brief Removes a voicemail message file.
1497  * \param dir the path to the message file.
1498  * \param msgnum the unique number for the message within the mailbox.
1499  *
1500  * Removes the message content file and the information file.
1501  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
1502  * Typical use is to clean up after a RETRIEVE operation. 
1503  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
1504  * \return zero on success, -1 on error.
1505  */
1506 static int remove_file(char *dir, int msgnum)
1507 {
1508         char fn[PATH_MAX];
1509         char full_fn[PATH_MAX];
1510         char msgnums[80];
1511         
1512         if (msgnum > -1) {
1513                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1514                 make_file(fn, sizeof(fn), dir, msgnum);
1515         } else
1516                 ast_copy_string(fn, dir, sizeof(fn));
1517         ast_filedelete(fn, NULL);       
1518         if (ast_check_realtime("voicemail_data")) {
1519                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1520         }
1521         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1522         unlink(full_fn);
1523         return 0;
1524 }
1525
1526 /*!
1527  * \brief Determines the highest message number in use for a given user and mailbox folder.
1528  * \param vmu 
1529  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1530  *
1531  * This method is used when mailboxes are stored in an ODBC back end.
1532  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
1533  *
1534  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
1535  */
1536 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1537 {
1538         int x = 0;
1539         int res;
1540         SQLHSTMT stmt;
1541         char sql[PATH_MAX];
1542         char rowdata[20];
1543         char *argv[] = { dir };
1544         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1545
1546         struct odbc_obj *obj;
1547         obj = ast_odbc_request_obj(odbc_database, 0);
1548         if (obj) {
1549                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1550                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1551                 if (!stmt) {
1552                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1553                         ast_odbc_release_obj(obj);
1554                         goto yuck;
1555                 }
1556                 res = SQLFetch(stmt);
1557                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1558                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1559                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1560                         ast_odbc_release_obj(obj);
1561                         goto yuck;
1562                 }
1563                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1564                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1565                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1566                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1567                         ast_odbc_release_obj(obj);
1568                         goto yuck;
1569                 }
1570                 if (sscanf(rowdata, "%d", &x) != 1)
1571                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
1572                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1573                 ast_odbc_release_obj(obj);
1574         } else
1575                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1576 yuck:   
1577         return x - 1;
1578 }
1579
1580 /*!
1581  * \brief Determines if the specified message exists.
1582  * \param dir the folder the mailbox folder to look for messages. 
1583  * \param msgnum the message index to query for.
1584  *
1585  * This method is used when mailboxes are stored in an ODBC back end.
1586  *
1587  * \return greater than zero if the message exists, zero when the message does not exist or on error.
1588  */
1589 static int message_exists(char *dir, int msgnum)
1590 {
1591         int x = 0;
1592         int res;
1593         SQLHSTMT stmt;
1594         char sql[PATH_MAX];
1595         char rowdata[20];
1596         char msgnums[20];
1597         char *argv[] = { dir, msgnums };
1598         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1599
1600         struct odbc_obj *obj;
1601         obj = ast_odbc_request_obj(odbc_database, 0);
1602         if (obj) {
1603                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1604                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1605                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1606                 if (!stmt) {
1607                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1608                         ast_odbc_release_obj(obj);
1609                         goto yuck;
1610                 }
1611                 res = SQLFetch(stmt);
1612                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1613                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1614                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1615                         ast_odbc_release_obj(obj);
1616                         goto yuck;
1617                 }
1618                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1619                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1620                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1621                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1622                         ast_odbc_release_obj(obj);
1623                         goto yuck;
1624                 }
1625                 if (sscanf(rowdata, "%d", &x) != 1)
1626                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
1627                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1628                 ast_odbc_release_obj(obj);
1629         } else
1630                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1631 yuck:   
1632         return x;
1633 }
1634
1635 /*!
1636  * \brief returns the one-based count for messages.
1637  * \param vmu
1638  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1639  *
1640  * This method is used when mailboxes are stored in an ODBC back end.
1641  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
1642  * one-based messages.
1643  * This method just calls last_message_index and returns +1 of its value.
1644  *
1645  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
1646  */
1647 static int count_messages(struct ast_vm_user *vmu, char *dir)
1648 {
1649         return last_message_index(vmu, dir) + 1;
1650 }
1651
1652 /*!
1653  * \brief Deletes a message from the mailbox folder.
1654  * \param sdir The mailbox folder to work in.
1655  * \param smsg The message index to be deleted.
1656  *
1657  * This method is used when mailboxes are stored in an ODBC back end.
1658  * The specified message is directly deleted from the database 'voicemessages' table.
1659  * 
1660  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
1661  */
1662 static void delete_file(char *sdir, int smsg)
1663 {
1664         SQLHSTMT stmt;
1665         char sql[PATH_MAX];
1666         char msgnums[20];
1667         char *argv[] = { sdir, msgnums };
1668         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1669
1670         struct odbc_obj *obj;
1671         obj = ast_odbc_request_obj(odbc_database, 0);
1672         if (obj) {
1673                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1674                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1675                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1676                 if (!stmt)
1677                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1678                 else
1679                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1680                 ast_odbc_release_obj(obj);
1681         } else
1682                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1683         return; 
1684 }
1685
1686 /*!
1687  * \brief Copies a voicemail from one mailbox to another.
1688  * \param sdir the folder for which to look for the message to be copied.
1689  * \param smsg the index of the message to be copied.
1690  * \param ddir the destination folder to copy the message into.
1691  * \param dmsg the index to be used for the copied message.
1692  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
1693  * \param dmailboxcontext The context for the destination user.
1694  *
1695  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
1696  */
1697 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1698 {
1699         SQLHSTMT stmt;
1700         char sql[512];
1701         char msgnums[20];
1702         char msgnumd[20];
1703         struct odbc_obj *obj;
1704         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1705         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1706
1707         delete_file(ddir, dmsg);
1708         obj = ast_odbc_request_obj(odbc_database, 0);
1709         if (obj) {
1710                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1711                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1712                 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);
1713                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1714                 if (!stmt)
1715                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1716                 else
1717                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1718                 ast_odbc_release_obj(obj);
1719         } else
1720                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1721         return; 
1722 }
1723
1724 struct insert_data {
1725         char *sql;
1726         char *dir;
1727         char *msgnums;
1728         void *data;
1729         SQLLEN datalen;
1730         const char *context;
1731         const char *macrocontext;
1732         const char *callerid;
1733         const char *origtime;
1734         const char *duration;
1735         char *mailboxuser;
1736         char *mailboxcontext;
1737         const char *category;
1738         const char *flag;
1739 };
1740
1741 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
1742 {
1743         struct insert_data *data = vdata;
1744         int res;
1745         SQLHSTMT stmt;
1746         SQLLEN len = data->datalen;
1747
1748         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1749         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1750                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
1751                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1752                 return NULL;
1753         }
1754
1755         SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
1756         SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
1757         SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &len);
1758         SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
1759         SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
1760         SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
1761         SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
1762         SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
1763         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
1764         SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
1765         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
1766         if (!ast_strlen_zero(data->category)) {
1767                 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
1768         }
1769         res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
1770         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1771                 ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
1772                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1773                 return NULL;
1774         }
1775
1776         return stmt;
1777 }
1778
1779 /*!
1780  * \brief Stores a voicemail into the database.
1781  * \param dir the folder the mailbox folder to store the message.
1782  * \param mailboxuser the user owning the mailbox folder.
1783  * \param mailboxcontext
1784  * \param msgnum the message index for the message to be stored.
1785  *
1786  * This method is used when mailboxes are stored in an ODBC back end.
1787  * The message sound file and information file is looked up on the file system. 
1788  * A SQL query is invoked to store the message into the (MySQL) database.
1789  *
1790  * \return the zero on success -1 on error.
1791  */
1792 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1793 {
1794         int res = 0;
1795         int fd = -1;
1796         void *fdm = MAP_FAILED;
1797         size_t fdlen = -1;
1798         SQLHSTMT stmt;
1799         char sql[PATH_MAX];
1800         char msgnums[20];
1801         char fn[PATH_MAX];
1802         char full_fn[PATH_MAX];
1803         char fmt[80]="";
1804         char *c;
1805         struct ast_config *cfg=NULL;
1806         struct odbc_obj *obj;
1807         struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
1808         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1809
1810         delete_file(dir, msgnum);
1811         if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
1812                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1813                 return -1;
1814         }
1815
1816         do {
1817                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1818                 c = strchr(fmt, '|');
1819                 if (c)
1820                         *c = '\0';
1821                 if (!strcasecmp(fmt, "wav49"))
1822                         strcpy(fmt, "WAV");
1823                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1824                 if (msgnum > -1)
1825                         make_file(fn, sizeof(fn), dir, msgnum);
1826                 else
1827                         ast_copy_string(fn, dir, sizeof(fn));
1828                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1829                 cfg = ast_config_load(full_fn, config_flags);
1830                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1831                 fd = open(full_fn, O_RDWR);
1832                 if (fd < 0) {
1833                         ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1834                         res = -1;
1835                         break;
1836                 }
1837                 if (cfg) {
1838                         if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
1839                                 idata.context = "";
1840                         }
1841                         if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
1842                                 idata.macrocontext = "";
1843                         }
1844                         if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
1845                                 idata.callerid = "";
1846                         }
1847                         if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
1848                                 idata.origtime = "";
1849                         }
1850                         if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
1851                                 idata.duration = "";
1852                         }
1853                         if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
1854                                 idata.category = "";
1855                         }
1856                         if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
1857                                 idata.flag = "";
1858                         }
1859                 }
1860                 fdlen = lseek(fd, 0, SEEK_END);
1861                 lseek(fd, 0, SEEK_SET);
1862                 printf("Length is %zd\n", fdlen);
1863                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1864                 if (fdm == MAP_FAILED) {
1865                         ast_log(AST_LOG_WARNING, "Memory map failed!\n");
1866                         res = -1;
1867                         break;
1868                 } 
1869                 idata.data = fdm;
1870                 idata.datalen = fdlen;
1871
1872                 if (!ast_strlen_zero(idata.category)) 
1873                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1874                 else
1875                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1876
1877                 if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
1878                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1879                 } else {
1880                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1881                         res = -1;
1882                 }
1883         } while (0);
1884         if (obj) {
1885                 ast_odbc_release_obj(obj);
1886         }
1887         if (cfg)
1888                 ast_config_destroy(cfg);
1889         if (fdm != MAP_FAILED)
1890                 munmap(fdm, fdlen);
1891         if (fd > -1)
1892                 close(fd);
1893         return res;
1894 }
1895
1896 /*!
1897  * \brief Renames a message in a mailbox folder.
1898  * \param sdir The folder of the message to be renamed.
1899  * \param smsg The index of the message to be renamed.
1900  * \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.
1901  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
1902  * \param ddir The destination folder for the message to be renamed into
1903  * \param dmsg The destination message for the message to be renamed.
1904  *
1905  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
1906  * 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.
1907  * 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.
1908  */
1909 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1910 {
1911         SQLHSTMT stmt;
1912         char sql[PATH_MAX];
1913         char msgnums[20];
1914         char msgnumd[20];
1915         struct odbc_obj *obj;
1916         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1917         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1918
1919         delete_file(ddir, dmsg);
1920         obj = ast_odbc_request_obj(odbc_database, 0);
1921         if (obj) {
1922                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1923                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1924                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1925                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1926                 if (!stmt)
1927                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1928                 else
1929                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1930                 ast_odbc_release_obj(obj);
1931         } else
1932                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1933         return; 
1934 }
1935
1936 #else
1937 #ifndef IMAP_STORAGE
1938 /*!
1939  * \brief Find all .txt files - even if they are not in sequence from 0000.
1940  * \param vmu
1941  * \param dir
1942  *
1943  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1944  *
1945  * \return the count of messages, zero or more.
1946  */
1947 static int count_messages(struct ast_vm_user *vmu, char *dir)
1948 {
1949
1950         int vmcount = 0;
1951         DIR *vmdir = NULL;
1952         struct dirent *vment = NULL;
1953
1954         if (vm_lock_path(dir))
1955                 return ERROR_LOCK_PATH;
1956
1957         if ((vmdir = opendir(dir))) {
1958                 while ((vment = readdir(vmdir))) {
1959                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
1960                                 vmcount++;
1961                         }
1962                 }
1963                 closedir(vmdir);
1964         }
1965         ast_unlock_path(dir);
1966         
1967         return vmcount;
1968 }
1969
1970 /*!
1971  * \brief Renames a message in a mailbox folder.
1972  * \param sfn The path to the mailbox information and data file to be renamed.
1973  * \param dfn The path for where the message data and information files will be renamed to.
1974  *
1975  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1976  */
1977 static void rename_file(char *sfn, char *dfn)
1978 {
1979         char stxt[PATH_MAX];
1980         char dtxt[PATH_MAX];
1981         ast_filerename(sfn,dfn,NULL);
1982         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1983         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1984         if (ast_check_realtime("voicemail_data")) {
1985                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1986         }
1987         rename(stxt, dtxt);
1988 }
1989
1990 /*! 
1991  * \brief Determines the highest message number in use for a given user and mailbox folder.
1992  * \param vmu 
1993  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1994  *
1995  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1996  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
1997  *
1998  * \note Should always be called with a lock already set on dir.
1999  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
2000  */
2001 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2002 {
2003         int x;
2004         unsigned char map[MAXMSGLIMIT] = "";
2005         DIR *msgdir;
2006         struct dirent *msgdirent;
2007         int msgdirint;
2008
2009         /* Reading the entire directory into a file map scales better than
2010          * doing a stat repeatedly on a predicted sequence.  I suspect this
2011          * is partially due to stat(2) internally doing a readdir(2) itself to
2012          * find each file. */
2013         msgdir = opendir(dir);
2014         while ((msgdirent = readdir(msgdir))) {
2015                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
2016                         map[msgdirint] = 1;
2017         }
2018         closedir(msgdir);
2019
2020         for (x = 0; x < vmu->maxmsg; x++) {
2021                 if (map[x] == 0)
2022                         break;
2023         }
2024
2025         return x - 1;
2026 }
2027
2028
2029 /*!
2030  * \brief Utility function to copy a file.
2031  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
2032  * \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.
2033  *
2034  * 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.
2035  * The copy operation copies up to 4096 bytes at once.
2036  *
2037  * \return zero on success, -1 on error.
2038  */
2039 static int copy(char *infile, char *outfile)
2040 {
2041         int ifd;
2042         int ofd;
2043         int res;
2044         int len;
2045         char buf[4096];
2046
2047 #ifdef HARDLINK_WHEN_POSSIBLE
2048         /* Hard link if possible; saves disk space & is faster */
2049         if (link(infile, outfile)) {
2050 #endif
2051                 if ((ifd = open(infile, O_RDONLY)) < 0) {
2052                         ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
2053                         return -1;
2054                 }
2055                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
2056                         ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
2057                         close(ifd);
2058                         return -1;
2059                 }
2060                 do {
2061                         len = read(ifd, buf, sizeof(buf));
2062                         if (len < 0) {
2063                                 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
2064                                 close(ifd);
2065                                 close(ofd);
2066                                 unlink(outfile);
2067                         }
2068                         if (len) {
2069                                 res = write(ofd, buf, len);
2070                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
2071                                         ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
2072                                         close(ifd);
2073                                         close(ofd);
2074                                         unlink(outfile);
2075                                 }
2076                         }
2077                 } while (len);
2078                 close(ifd);
2079                 close(ofd);
2080                 return 0;
2081 #ifdef HARDLINK_WHEN_POSSIBLE
2082         } else {
2083                 /* Hard link succeeded */
2084                 return 0;
2085         }
2086 #endif
2087 }
2088
2089 /*!
2090  * \brief Copies a voicemail information (envelope) file.
2091  * \param frompath
2092  * \param topath 
2093  *
2094  * Every voicemail has the data (.wav) file, and the information file.
2095  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
2096  * This is used by the COPY macro when not using IMAP storage.
2097  */
2098 static void copy_plain_file(char *frompath, char *topath)
2099 {
2100         char frompath2[PATH_MAX], topath2[PATH_MAX];
2101         struct ast_variable *tmp,*var = NULL;
2102         const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
2103         ast_filecopy(frompath, topath, NULL);
2104         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
2105         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
2106         if (ast_check_realtime("voicemail_data")) {
2107                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
2108                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
2109                 for (tmp = var; tmp; tmp = tmp->next) {
2110                         if (!strcasecmp(tmp->name, "origmailbox")) {
2111                                 origmailbox = tmp->value;
2112                         } else if (!strcasecmp(tmp->name, "context")) {
2113                                 context = tmp->value;
2114                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
2115                                 macrocontext = tmp->value;
2116                         } else if (!strcasecmp(tmp->name, "exten")) {
2117                                 exten = tmp->value;
2118                         } else if (!strcasecmp(tmp->name, "priority")) {
2119                                 priority = tmp->value;
2120                         } else if (!strcasecmp(tmp->name, "callerchan")) {
2121                                 callerchan = tmp->value;
2122                         } else if (!strcasecmp(tmp->name, "callerid")) {
2123                                 callerid = tmp->value;
2124                         } else if (!strcasecmp(tmp->name, "origdate")) {
2125                                 origdate = tmp->value;
2126                         } else if (!strcasecmp(tmp->name, "origtime")) {
2127                                 origtime = tmp->value;
2128                         } else if (!strcasecmp(tmp->name, "category")) {
2129                                 category = tmp->value;
2130                         } else if (!strcasecmp(tmp->name, "duration")) {
2131                                 duration = tmp->value;
2132                         }
2133                 }
2134                 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
2135         }
2136         copy(frompath2, topath2);
2137         ast_variables_destroy(var);
2138 }
2139
2140 #endif /* #ifndef IMAP_STORAGE */
2141 #endif /* #else of #ifdef ODBC_STORAGE */
2142 #ifndef ODBC_STORAGE
2143 /*! 
2144  * \brief Removes the voicemail sound and information file.
2145  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
2146  *
2147  * This is used by the DELETE macro when voicemails are stored on the file system.
2148  *
2149  * \return zero on success, -1 on error.
2150  */
2151 static int vm_delete(char *file)
2152 {
2153         char *txt;
2154         int txtsize = 0;
2155
2156         txtsize = (strlen(file) + 5)*sizeof(char);
2157         txt = alloca(txtsize);
2158         /* Sprintf here would safe because we alloca'd exactly the right length,
2159          * but trying to eliminate all sprintf's anyhow
2160          */
2161         if (ast_check_realtime("voicemail_data")) {
2162                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
2163         }
2164         snprintf(txt, txtsize, "%s.txt", file);
2165         unlink(txt);
2166         return ast_filedelete(file, NULL);
2167 }
2168 #endif
2169
2170 /*!
2171  * \brief utility used by inchar(), for base_encode()
2172  */
2173 static int inbuf(struct baseio *bio, FILE *fi)
2174 {
2175         int l;
2176
2177         if (bio->ateof)
2178                 return 0;
2179
2180         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
2181                 if (ferror(fi))
2182                         return -1;
2183
2184                 bio->ateof = 1;
2185                 return 0;
2186         }
2187
2188         bio->iolen= l;
2189         bio->iocp= 0;
2190
2191         return 1;
2192 }
2193
2194 /*!
2195  * \brief utility used by base_encode()
2196  */
2197 static int inchar(struct baseio *bio, FILE *fi)
2198 {
2199         if (bio->iocp>=bio->iolen) {
2200                 if (!inbuf(bio, fi))
2201                         return EOF;
2202         }
2203
2204         return bio->iobuf[bio->iocp++];
2205 }
2206
2207 /*!
2208  * \brief utility used by base_encode()
2209  */
2210 static int ochar(struct baseio *bio, int c, FILE *so)
2211 {
2212         if (bio->linelength >= BASELINELEN) {
2213                 if (fputs(eol,so) == EOF)
2214                         return -1;
2215
2216                 bio->linelength= 0;
2217         }
2218
2219         if (putc(((unsigned char)c),so) == EOF)
2220                 return -1;
2221
2222         bio->linelength++;
2223
2224         return 1;
2225 }
2226
2227 /*!
2228  * \brief Performs a base 64 encode algorithm on the contents of a File
2229  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
2230  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
2231  *
2232  * 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 ?
2233  *
2234  * \return zero on success, -1 on error.
2235  */
2236 static int base_encode(char *filename, FILE *so)
2237 {
2238         static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
2239                 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
2240                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
2241                 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
2242         int i,hiteof= 0;
2243         FILE *fi;
2244         struct baseio bio;
2245
2246         memset(&bio, 0, sizeof(bio));
2247         bio.iocp = BASEMAXINLINE;
2248
2249         if (!(fi = fopen(filename, "rb"))) {
2250                 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
2251                 return -1;
2252         }
2253
2254         while (!hiteof){
2255                 unsigned char igroup[3], ogroup[4];
2256                 int c,n;
2257
2258                 igroup[0]= igroup[1]= igroup[2]= 0;
2259
2260                 for (n= 0;n<3;n++) {
2261                         if ((c = inchar(&bio, fi)) == EOF) {
2262                                 hiteof= 1;
2263                                 break;
2264                         }
2265
2266                         igroup[n]= (unsigned char)c;
2267                 }
2268
2269                 if (n> 0) {
2270                         ogroup[0]= dtable[igroup[0]>>2];
2271                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
2272                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
2273                         ogroup[3]= dtable[igroup[2]&0x3F];
2274
2275                         if (n<3) {
2276                                 ogroup[3]= '=';
2277
2278                                 if (n<2)
2279                                         ogroup[2]= '=';
2280                         }
2281
2282                         for (i= 0;i<4;i++)
2283                                 ochar(&bio, ogroup[i], so);
2284                 }
2285         }
2286
2287         fclose(fi);
2288         
2289         if (fputs(eol,so)==EOF)
2290                 return 0;
2291
2292         return 1;
2293 }
2294
2295 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)
2296 {
2297         char callerid[256];
2298         /* Prepare variables for substitution in email body and subject */
2299         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
2300         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
2301         snprintf(passdata, passdatasize, "%d", msgnum);
2302         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
2303         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
2304         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
2305         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
2306         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
2307         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
2308         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
2309         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
2310         pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
2311 }
2312
2313 /*!
2314  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
2315  * \param from The string to work with.
2316  * \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.
2317  * 
2318  * \return The destination string with quotes wrapped on it (the to field).
2319  */
2320 static char *quote(const char *from, char *to, size_t len)
2321 {
2322         char *ptr = to;
2323         *ptr++ = '"';
2324         for (; ptr < to + len - 1; from++) {
2325                 if (*from == '"')
2326                         *ptr++ = '\\';
2327                 else if (*from == '\0')
2328                         break;
2329                 *ptr++ = *from;
2330         }
2331         if (ptr < to + len - 1)
2332                 *ptr++ = '"';
2333         *ptr = '\0';
2334         return to;
2335 }
2336
2337 /*! \brief
2338  * fill in *tm for current time according to the proper timezone, if any.
2339  * Return tm so it can be used as a function argument.
2340  */
2341 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
2342 {
2343         const struct vm_zone *z = NULL;
2344         struct timeval t = ast_tvnow();
2345
2346         /* Does this user have a timezone specified? */
2347         if (!ast_strlen_zero(vmu->zonetag)) {
2348                 /* Find the zone in the list */
2349                 AST_LIST_LOCK(&zones);
2350                 AST_LIST_TRAVERSE(&zones, z, list) {
2351                         if (!strcmp(z->name, vmu->zonetag))
2352                                 break;
2353                 }
2354                 AST_LIST_UNLOCK(&zones);
2355         }
2356         ast_localtime(&t, tm, z ? z->timezone : NULL);
2357         return tm;
2358 }
2359
2360 /*! \brief same as mkstemp, but return a FILE * */
2361 static FILE *vm_mkftemp(char *template)
2362 {
2363         FILE *p = NULL;
2364         int pfd = mkstemp(template);
2365         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
2366         if (pfd > -1) {
2367                 p = fdopen(pfd, "w+");
2368                 if (!p) {
2369                         close(pfd);
2370                         pfd = -1;
2371                 }
2372         }
2373         return p;
2374 }
2375
2376 /*!
2377  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
2378  * \param p The output file to generate the email contents into.
2379  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
2380  * \param vmu The voicemail user who is sending the voicemail.
2381  * \param msgnum The message index in the mailbox folder.
2382  * \param context 
2383  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
2384  * \param cidnum The caller ID number.
2385  * \param cidname The caller ID name.
2386  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
2387  * \param format The message sound file format. i.e. .wav
2388  * \param duration The time of the message content, in seconds.
2389  * \param attach_user_voicemail if 1, the sound file is attached to the email.
2390  * \param chan
2391  * \param category
2392  * \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.
2393  *
2394  * 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.
2395  */
2396 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)
2397 {
2398         char date[256];
2399         char host[MAXHOSTNAMELEN] = "";
2400         char who[256];
2401         char bound[256];
2402         char dur[256];
2403         struct ast_tm tm;
2404         char *passdata2;
2405         size_t len_passdata;
2406         char *greeting_attachment; 
2407         char filename[256];
2408
2409 #ifdef IMAP_STORAGE
2410 #define ENDL "\r\n"
2411 #else
2412 #define ENDL "\n"
2413 #endif
2414
2415         gethostname(host, sizeof(host)-1);
2416
2417         if (strchr(srcemail, '@'))
2418                 ast_copy_string(who, srcemail, sizeof(who));
2419         else 
2420                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2421         
2422         greeting_attachment = strrchr(ast_strdupa(attach), '/');
2423         if (greeting_attachment)
2424                 *greeting_attachment++ = '\0';
2425
2426         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2427         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2428         fprintf(p, "Date: %s" ENDL, date);
2429
2430         /* Set date format for voicemail mail */
2431         ast_strftime(date, sizeof(date), emaildateformat, &tm);
2432
2433         if (!ast_strlen_zero(fromstring)) {
2434                 struct ast_channel *ast;
2435                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2436                         char *passdata;
2437                         int vmlen = strlen(fromstring)*3 + 200;
2438                         passdata = alloca(vmlen);
2439                         memset(passdata, 0, vmlen);
2440                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2441                         pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
2442                         len_passdata = strlen(passdata) * 2 + 3;
2443                         passdata2 = alloca(len_passdata);
2444                         fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
2445                         ast_channel_free(ast);
2446                 } else
2447                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2448         } else
2449                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
2450         len_passdata = strlen(vmu->fullname) * 2 + 3;
2451         passdata2 = alloca(len_passdata);
2452         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
2453         if (!ast_strlen_zero(emailsubject)) {
2454                 struct ast_channel *ast;
2455                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2456                         char *passdata;
2457                         int vmlen = strlen(emailsubject) * 3 + 200;
2458                         passdata = alloca(vmlen);
2459                         memset(passdata, 0, vmlen);
2460                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2461                         pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2462                         fprintf(p, "Subject: %s" ENDL, passdata);
2463                         ast_channel_free(ast);
2464                 } else
2465                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2466         } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
2467                 if (ast_strlen_zero(flag)) {
2468                         fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2469                 } else {
2470                         fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
2471                 }
2472         } else {
2473                 if (ast_strlen_zero(flag)) {
2474                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2475                 } else {
2476                         fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
2477                 }
2478         }
2479
2480         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2481         if (imap) {
2482                 /* additional information needed for IMAP searching */
2483                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2484                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2485                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2486                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2487                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2488                 /* flag added for Urgent */
2489                 fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
2490                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2491                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2492                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2493                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2494                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2495                 if (!ast_strlen_zero(category)) 
2496                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2497                 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
2498                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2499                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2500         }
2501         if (!ast_strlen_zero(cidnum))
2502                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2503         if (!ast_strlen_zero(cidname))
2504                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2505         fprintf(p, "MIME-Version: 1.0" ENDL);
2506         if (attach_user_voicemail) {
2507                 /* Something unique. */
2508                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2509
2510                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2511                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2512                 fprintf(p, "--%s" ENDL, bound);
2513         }
2514         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2515         if (emailbody) {
2516                 struct ast_channel *ast;
2517                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2518                         char *passdata;
2519                         int vmlen = strlen(emailbody)*3 + 200;
2520                         passdata = alloca(vmlen);
2521                         memset(passdata, 0, vmlen);
2522                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2523                         pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2524                         fprintf(p, "%s" ENDL, passdata);
2525                         ast_channel_free(ast);
2526                 } else
2527                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2528         } else if (msgnum > -1){
2529                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
2530                 "in mailbox %s from %s, on %s so you might" ENDL
2531                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2532                 dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2533         } else {
2534                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2535                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2536         }
2537
2538         if (attach_user_voicemail) {
2539                 if (!ast_strlen_zero(attach2)) {
2540                         snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
2541                         ast_debug(5, "creating attachment filename %s\n", filename);
2542                         add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 0, msgnum);
2543                         snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
2544                         ast_debug(5, "creating second attachment filename %s\n", filename);
2545                         add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
2546                 } else {
2547                         snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
2548                         ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
2549                         add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
2550                 }
2551         }
2552 }
2553
2554 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)
2555 {
2556         char tmpdir[256], newtmp[256];
2557         char fname[256];
2558         char tmpcmd[256];
2559         int tmpfd = -1;
2560
2561         /* Eww. We want formats to tell us their own MIME type */
2562         char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2563
2564         if (vmu->volgain < -.001 || vmu->volgain > .001) {
2565                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2566                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2567                 tmpfd = mkstemp(newtmp);
2568                 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2569                 ast_debug(3, "newtmp: %s\n", newtmp);
2570                 if (tmpfd > -1) {
2571                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2572                         ast_safe_system(tmpcmd);
2573                         attach = newtmp;
2574                         ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2575                 }
2576         }
2577         fprintf(p, "--%s" ENDL, bound);
2578         if (msgnum > -1)
2579                 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
2580         else
2581                 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
2582         fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2583         fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2584         if (msgnum > -1)
2585                 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
2586         else
2587                 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
2588         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2589         base_encode(fname, p);
2590         if (last)
2591                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2592         if (tmpfd > -1) {
2593                 unlink(fname);
2594                 close(tmpfd);
2595                 unlink(newtmp);
2596         }
2597         return 0;
2598 }
2599 #undef ENDL
2600
2601 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)
2602 {
2603         FILE *p=NULL;
2604         char tmp[80] = "/tmp/astmail-XXXXXX";
2605         char tmp2[256];
2606
2607         if (vmu && ast_strlen_zero(vmu->email)) {
2608                 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2609                 return(0);
2610         }
2611         if (!strcmp(format, "wav49"))
2612                 format = "WAV";
2613         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));
2614         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2615            command hangs */
2616         if ((p = vm_mkftemp(tmp)) == NULL) {
2617                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2618                 return -1;
2619         } else {
2620                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
2621                 fclose(p);
2622                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2623                 ast_safe_system(tmp2);
2624                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2625         }
2626         return 0;
2627 }
2628
2629 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)
2630 {
2631         char date[256];
2632         char host[MAXHOSTNAMELEN] = "";
2633         char who[256];
2634         char dur[PATH_MAX];
2635         char tmp[80] = "/tmp/astmail-XXXXXX";
2636         char tmp2[PATH_MAX];
2637         struct ast_tm tm;
2638         FILE *p;
2639
2640         if ((p = vm_mkftemp(tmp)) == NULL) {
2641                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2642                 return -1;
2643         }
2644         gethostname(host, sizeof(host)-1);
2645         if (strchr(srcemail, '@'))
2646                 ast_copy_string(who, srcemail, sizeof(who));
2647         else 
2648                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2649         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2650         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2651         fprintf(p, "Date: %s\n", date);
2652
2653         if (*pagerfromstring) {
2654                 struct ast_channel *ast;
2655                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2656                         char *passdata;
2657                         int vmlen = strlen(fromstring)*3 + 200;
2658                         passdata = alloca(vmlen);
2659                         memset(passdata, 0, vmlen);
2660                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2661                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2662                         fprintf(p, "From: %s <%s>\n", passdata, who);
2663                         ast_channel_free(ast);
2664                 } else 
2665                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2666         } else
2667                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2668         fprintf(p, "To: %s\n", pager);
2669         if (pagersubject) {
2670                 struct ast_channel *ast;
2671                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2672                         char *passdata;
2673                         int vmlen = strlen(pagersubject) * 3 + 200;
2674                         passdata = alloca(vmlen);
2675                         memset(passdata, 0, vmlen);
2676                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2677                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2678                         fprintf(p, "Subject: %s\n\n", passdata);
2679                         ast_channel_free(ast);
2680                 } else
2681                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2682         } else {
2683                 if (ast_strlen_zero(flag)) {
2684                         fprintf(p, "Subject: New VM\n\n");
2685                 } else {
2686                         fprintf(p, "Subject: New %s VM\n\n", flag);
2687                 }
2688         }
2689
2690         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2691         if (pagerbody) {
2692                 struct ast_channel *ast;
2693                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2694                         char *passdata;
2695                         int vmlen = strlen(pagerbody) * 3 + 200;
2696                         passdata = alloca(vmlen);
2697                         memset(passdata, 0, vmlen);
2698                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2699                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2700                         fprintf(p, "%s\n", passdata);
2701                         ast_channel_free(ast);
2702                 } else
2703                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2704         } else {
2705                 fprintf(p, "New %s long %s msg in box %s\n"
2706                                 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2707         }
2708         fclose(p);
2709         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2710         ast_safe_system(tmp2);
2711         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2712         return 0;
2713 }
2714
2715 /*!
2716  * \brief Gets the current date and time, as formatted string.
2717  * \param s The buffer to hold the output formatted date.
2718  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
2719  * 
2720  * The date format string used is "%a %b %e %r UTC %Y".
2721  * 
2722  * \return zero on success, -1 on error.
2723  */
2724 static int get_date(char *s, int len)
2725 {
2726         struct ast_tm tm;
2727         struct timeval t = ast_tvnow();
2728         
2729         ast_localtime(&t, &tm, "UTC");
2730
2731         return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
2732 }
2733
2734 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2735 {
2736         int res;
2737         char fn[PATH_MAX];
2738         char dest[PATH_MAX];
2739
2740         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2741
2742         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2743                 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
2744                 return -1;
2745         }
2746
2747         RETRIEVE(fn, -1, ext, context);
2748         if (ast_fileexists(fn, NULL, NULL) > 0) {
2749                 res = ast_stream_and_wait(chan, fn, ecodes);
2750                 if (res) {
2751                         DISPOSE(fn, -1);
2752                         return res;
2753                 }
2754         } else {
2755                 /* Dispose just in case */
2756                 DISPOSE(fn, -1);
2757                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2758                 if (res)
2759                         return res;
2760                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2761                 if (res)
2762                         return res;
2763         }
2764         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2765         return res;
2766 }
2767
2768 static void free_user(struct ast_vm_user *vmu)
2769 {
2770         if (!ast_test_flag(vmu, VM_ALLOCED))
2771                 return;
2772
2773         ast_free(vmu);
2774 }
2775
2776 static void free_zone(struct vm_zone *z)
2777 {
2778         ast_free(z);
2779 }
2780
2781 /*!
2782  * \brief Gets the name of the mailbox folder from the numeric index.
2783  * \param id The numerical index for the folder name.
2784  * 
2785  * When an invalid number is entered, or one that exceeds the pre-configured list of folder names, the name "tmp" is returned.
2786  *
2787  * \return the String name that coresponds to this folder index.
2788  */
2789 static const char *mbox(int id)
2790 {
2791         static const char *msgs[] = {
2792 #ifdef IMAP_STORAGE
2793                 imapfolder,
2794 #else
2795                 "INBOX",
2796 #endif
2797                 "Old",
2798                 "Work",
2799                 "Family",
2800                 "Friends",
2801                 "Cust1",
2802                 "Cust2",
2803                 "Cust3",
2804                 "Cust4",
2805                 "Cust5",
2806                 "Deleted",
2807                 "Urgent"
2808         };
2809         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2810 }
2811 #ifdef IMAP_STORAGE
2812 /*!
2813  * \brief Converts a string folder name into the numerical identifier.
2814  * \param folder the string folder name to be converted to an id.
2815  *
2816  * This is the opposite of the mbox() function.
2817  *
2818  * \return the id that coresponds to the folder name
2819  */
2820 static int folder_int(const char *folder)
2821 {
2822         /* assume a NULL folder means INBOX */
2823         if (!folder)
2824                 return 0;
2825 #ifdef IMAP_STORAGE
2826         if (!strcasecmp(folder, imapfolder))
2827 #else
2828         if (!strcasecmp(folder, "INBOX"))
2829 #endif
2830                 return 0;
2831         else if (!strcasecmp(folder, "Old"))
2832                 return 1;
2833         else if (!strcasecmp(folder, "Work"))
2834                 return 2;
2835         else if (!strcasecmp(folder, "Family"))
2836                 return 3;
2837         else if (!strcasecmp(folder, "Friends"))
2838                 return 4;
2839         else if (!strcasecmp(folder, "Cust1"))
2840                 return 5;
2841         else if (!strcasecmp(folder, "Cust2"))
2842                 return 6;
2843         else if (!strcasecmp(folder, "Cust3"))
2844                 return 7;
2845         else if (!strcasecmp(folder, "Cust4"))
2846                 return 8;
2847         else if (!strcasecmp(folder, "Cust5"))
2848                 return 9;
2849         else if (!strcasecmp(folder, "Deleted"))
2850                 return 10;
2851         else if (!strcasecmp(folder, "Urgent"))
2852                 return 11;
2853         else /*assume they meant INBOX if folder is not found otherwise*/
2854                 return 0;
2855 }
2856 #endif
2857
2858 #ifdef ODBC_STORAGE
2859 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2860 static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2861 {
2862         int x = -1;
2863         int res;
2864         SQLHSTMT stmt;
2865         char sql[PATH_MAX];
2866         char rowdata[20];
2867         char tmp[PATH_MAX] = "";
2868         struct odbc_obj *obj;
2869         char *context;
2870         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2871
2872         if (newmsgs)
2873                 *newmsgs = 0;
2874         if (oldmsgs)
2875                 *oldmsgs = 0;
2876         if (urgentmsgs)
2877                 *urgentmsgs = 0;
2878
2879         /* If no mailbox, return immediately */
2880         if (ast_strlen_zero(mailbox))
2881                 return 0;
2882
2883         ast_copy_string(tmp, mailbox, sizeof(tmp));
2884         
2885         context = strchr(tmp, '@');
2886         if (context) {
2887                 *context = '\0';
2888                 context++;
2889         } else
2890                 context = "default";
2891         
2892         obj = ast_odbc_request_obj(odbc_database, 0);
2893         if (obj) {
2894                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2895                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2896                 if (!stmt) {
2897                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2898                         ast_odbc_release_obj(obj);
2899                         goto yuck;
2900                 }
2901                 res = SQLFetch(stmt);
2902                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2903                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2904                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2905                         ast_odbc_release_obj(obj);
2906                         goto yuck;
2907                 }
2908                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2909                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2910                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2911                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2912                         ast_odbc_release_obj(obj);
2913                         goto yuck;
2914                 }
2915                 *newmsgs = atoi(rowdata);
2916                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2917
2918                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2919                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2920                 if (!stmt) {
2921                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2922                         ast_odbc_release_obj(obj);
2923                         goto yuck;
2924                 }
2925                 res = SQLFetch(stmt);
2926                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2927                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2928                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2929                         ast_odbc_release_obj(obj);
2930                         goto yuck;
2931                 }
2932                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2933                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2934                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2935                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2936                         ast_odbc_release_obj(obj);
2937                         goto yuck;
2938                 }
2939                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2940                 *oldmsgs = atoi(rowdata);
2941                 
2942                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
2943                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2944                 if (!stmt) {
2945                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2946                         ast_odbc_release_obj(obj);
2947                         goto yuck;
2948                 }
2949                 res = SQLFetch(stmt);
2950                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2951                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2952                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2953                         ast_odbc_release_obj(obj);
2954                         goto yuck;
2955                 }
2956                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2957                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2958                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2959                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2960                         ast_odbc_release_obj(obj);
2961                         goto yuck;
2962                 }
2963                 *urgentmsgs = atoi(rowdata);
2964                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2965                 ast_odbc_release_obj(obj);
2966                 x = 0;
2967         } else
2968                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2969                 
2970 yuck:   
2971         return x;
2972 }
2973
2974 /*!
2975  * \brief Gets the number of messages that exist in a mailbox folder.
2976  * \param context
2977  * \param mailbox
2978  * \param folder
2979  * 
2980  * This method is used when ODBC backend is used.
2981  * \return The number of messages in this mailbox folder (zero or more).
2982  */
2983 static int messagecount(const char *context, const char *mailbox, const char *folder)
2984 {
2985         struct odbc_obj *obj = NULL;
2986         int nummsgs = 0;
2987         int res;
2988         SQLHSTMT stmt = NULL;
2989         char sql[PATH_MAX];
2990         char rowdata[20];
2991         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2992         if (!folder)
2993                 folder = "INBOX";
2994         /* If no mailbox, return immediately */
2995         if (ast_strlen_zero(mailbox))
2996                 return 0;
2997
2998         obj = ast_odbc_request_obj(odbc_database, 0);
2999         if (obj) {
3000                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
3001                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3002                 if (!stmt) {
3003                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3004                         goto yuck;
3005                 }
3006                 res = SQLFetch(stmt);
3007                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3008                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3009                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3010                         goto yuck;
3011                 }
3012                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3013                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3014                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3015                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3016                         goto yuck;
3017                 }
3018                 nummsgs = atoi(rowdata);
3019                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3020         } else
3021                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3022
3023 yuck:
3024         if (obj)
3025                 ast_odbc_release_obj(obj);
3026         return nummsgs;
3027 }
3028
3029 /** 
3030  * \brief Determines if the given folder has messages.
3031  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3032  * 
3033  * This function is used when the mailbox is stored in an ODBC back end.
3034  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3035  * \return 1 if the folder has one or more messages. zero otherwise.
3036  */
3037 static int has_voicemail(const char *mailbox, const char *folder)
3038 {
3039         char tmp[256], *tmp2 = tmp, *mbox, *context;
3040         ast_copy_string(tmp, mailbox, sizeof(tmp));
3041         while ((context = mbox = strsep(&tmp2, ","))) {
3042                 strsep(&context, "@");
3043                 if (ast_strlen_zero(context))
3044                         context = "default";
3045                 if (messagecount(context, mbox, folder))
3046                         return 1;
3047         }
3048         return 0;
3049 }
3050
3051 #elif defined(IMAP_STORAGE)
3052
3053 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)
3054 {
3055         char *myserveremail = serveremail;
3056         char fn[PATH_MAX];
3057         char intro[PATH_MAX];
3058         char mailbox[256];
3059         char *stringp;
3060         FILE *p=NULL;
3061         char tmp[80] = "/tmp/astmail-XXXXXX";
3062         long len;
3063         void *buf;
3064         int tempcopy = 0;
3065         STRING str;
3066         int ret; /* for better error checking */
3067         char *imapflags = NIL;
3068
3069         /* Set urgent flag for IMAP message */
3070         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
3071                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
3072                 imapflags="\\FLAGGED";
3073         }
3074         
3075         /* Attach only the first format */
3076         fmt = ast_strdupa(fmt);
3077         stringp = fmt;
3078         strsep(&stringp, "|");
3079
3080         if (!ast_strlen_zero(vmu->serveremail))
3081                 myserveremail = vmu->serveremail;
3082
3083         if (msgnum > -1)
3084                 make_file(fn, sizeof(fn), dir, msgnum);
3085         else
3086                 ast_copy_string (fn, dir, sizeof(fn));
3087         
3088         if (ast_strlen_zero(vmu->email)) {
3089                 /* We need the vmu->email to be set when we call make_email_file, but
3090                  * if we keep it set, a duplicate e-mail will be created. So at the end
3091                  * of this function, we will revert back to an empty string if tempcopy
3092                  * is 1.
3093                  */
3094                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
3095                 tempcopy = 1;
3096         }
3097
3098         if (!ast_strlen_zero(introfile)) {
3099                 snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
3100         } else {
3101                 intro[0] = '\0';
3102         }
3103
3104         if (!strcmp(fmt, "wav49"))
3105                 fmt = "WAV";
3106         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
3107
3108         /* Make a temporary file instead of piping directly to sendmail, in case the mail
3109            command hangs. */
3110         if (!(p = vm_mkftemp(tmp))) {
3111                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
3112                 if (tempcopy)
3113                         *(vmu->email) = '\0';
3114                 return -1;
3115         }
3116
3117         if (msgnum < 0 && imapgreetings) {
3118                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {