Expand RQ_INTEGER type out to multiple types, one for each precision
[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                 if (strlen(password) > 10) {
932                         ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), NULL);
933                 }
934                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
935                 if (res > 0) {
936                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
937                         res = 0;
938                 } else if (!res) {
939                         res = -1;
940                 }
941                 return res;
942         }
943         return -1;
944 }
945
946 /*!
947  * \brief Destructively Parse options and apply.
948  */
949 static void apply_options(struct ast_vm_user *vmu, const char *options)
950 {       
951         char *stringp;
952         char *s;
953         char *var, *value;
954         stringp = ast_strdupa(options);
955         while ((s = strsep(&stringp, "|"))) {
956                 value = s;
957                 if ((var = strsep(&value, "=")) && value) {
958                         apply_option(vmu, var, value);
959                 }
960         }       
961 }
962
963 /*!
964  * \brief Loads the options specific to a voicemail user.
965  * 
966  * This is called when a vm_user structure is being set up, such as from load_options.
967  */
968 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
969 {
970         struct ast_variable *tmp;
971         tmp = var;
972         while (tmp) {
973                 if (!strcasecmp(tmp->name, "vmsecret")) {
974                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
975                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
976                         if (ast_strlen_zero(retval->password))
977                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
978                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
979                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
980                 } else if (!strcasecmp(tmp->name, "pager")) {
981                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
982                 } else if (!strcasecmp(tmp->name, "email")) {
983                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
984                 } else if (!strcasecmp(tmp->name, "fullname")) {
985                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
986                 } else if (!strcasecmp(tmp->name, "context")) {
987                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
988 #ifdef IMAP_STORAGE
989                 } else if (!strcasecmp(tmp->name, "imapuser")) {
990                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
991                 } else if (!strcasecmp(tmp->name, "imappassword")) {
992                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
993 #endif
994                 } else
995                         apply_option(retval, tmp->name, tmp->value);
996                 tmp = tmp->next;
997         } 
998 }
999
1000 /*!
1001  * \brief Determines if a DTMF key entered is valid.
1002  * \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.
1003  *
1004  * Tests the character entered against the set of valid DTMF characters. 
1005  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1006  */
1007 static int is_valid_dtmf(const char *key)
1008 {
1009         int i;
1010         char *local_key = ast_strdupa(key);
1011
1012         for (i = 0; i < strlen(key); ++i) {
1013                 if (!strchr(VALID_DTMF, *local_key)) {
1014                         ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1015                         return 0;
1016                 }
1017                 local_key++;
1018         }
1019         return 1;
1020 }
1021
1022 /*!
1023  * \brief Finds a voicemail user from the realtime engine.
1024  * \param ivm
1025  * \param context
1026  * \param mailbox
1027  *
1028  * 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.
1029  *
1030  * \return The ast_vm_user structure for the user that was found.
1031  */
1032 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1033 {
1034         struct ast_variable *var;
1035         struct ast_vm_user *retval;
1036
1037         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1038                 if (!ivm)
1039                         ast_set_flag(retval, VM_ALLOCED);       
1040                 else
1041                         memset(retval, 0, sizeof(*retval));
1042                 if (mailbox) 
1043                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1044                 populate_defaults(retval);
1045                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
1046                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
1047                 else
1048                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
1049                 if (var) {
1050                         apply_options_full(retval, var);
1051                         ast_variables_destroy(var);
1052                 } else { 
1053                         if (!ivm) 
1054                                 ast_free(retval);
1055                         retval = NULL;
1056                 }       
1057         } 
1058         return retval;
1059 }
1060
1061 /*!
1062  * \brief Finds a voicemail user from the users file or the realtime engine.
1063  * \param ivm
1064  * \param context
1065  * \param mailbox
1066  * 
1067  * \return The ast_vm_user structure for the user that was found.
1068  */
1069 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1070 {
1071         /* This function could be made to generate one from a database, too */
1072         struct ast_vm_user *vmu=NULL, *cur;
1073         AST_LIST_LOCK(&users);
1074
1075         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1076                 context = "default";
1077
1078         AST_LIST_TRAVERSE(&users, cur, list) {
1079                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1080                         break;
1081                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1082                         break;
1083         }
1084         if (cur) {
1085                 /* Make a copy, so that on a reload, we have no race */
1086                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1087                         memcpy(vmu, cur, sizeof(*vmu));
1088                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1089                         AST_LIST_NEXT(vmu, list) = NULL;
1090                 }
1091         } else
1092                 vmu = find_user_realtime(ivm, context, mailbox);
1093         AST_LIST_UNLOCK(&users);
1094         return vmu;
1095 }
1096
1097 /*!
1098  * \brief Resets a user password to a specified password.
1099  * \param context
1100  * \param mailbox
1101  * \param newpass
1102  *
1103  * This does the actual change password work, called by the vm_change_password() function.
1104  *
1105  * \return zero on success, -1 on error.
1106  */
1107 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1108 {
1109         /* This function could be made to generate one from a database, too */
1110         struct ast_vm_user *cur;
1111         int res = -1;
1112         AST_LIST_LOCK(&users);
1113         AST_LIST_TRAVERSE(&users, cur, list) {
1114                 if ((!context || !strcasecmp(context, cur->context)) &&
1115                         (!strcasecmp(mailbox, cur->mailbox)))
1116                                 break;
1117         }
1118         if (cur) {
1119                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
1120                 res = 0;
1121         }
1122         AST_LIST_UNLOCK(&users);
1123         return res;
1124 }
1125
1126 /*! 
1127  * \brief The handler for the change password option.
1128  * \param vmu The voicemail user to work with.
1129  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1130  * 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.
1131  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1132  */
1133 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1134 {
1135         struct ast_config   *cfg=NULL;
1136         struct ast_variable *var=NULL;
1137         struct ast_category *cat=NULL;
1138         char *category=NULL, *value=NULL, *new=NULL;
1139         const char *tmp=NULL;
1140         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1141         if (!change_password_realtime(vmu, newpassword))
1142                 return;
1143
1144         /* check voicemail.conf */
1145         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
1146                 while ((category = ast_category_browse(cfg, category))) {
1147                         if (!strcasecmp(category, vmu->context)) {
1148                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1149                                         ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1150                                         break;
1151                                 }
1152                                 value = strstr(tmp,",");
1153                                 if (!value) {
1154                                         ast_log(AST_LOG_WARNING, "variable has bad format.\n");
1155                                         break;
1156                                 }
1157                                 new = alloca((strlen(value)+strlen(newpassword)+1));
1158                                 sprintf(new,"%s%s", newpassword, value);
1159                                 if (!(cat = ast_category_get(cfg, category))) {
1160                                         ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1161                                         break;
1162                                 }
1163                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1164                         }
1165                 }
1166                 /* save the results */
1167                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1168                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1169                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1170         }
1171         category = NULL;
1172         var = NULL;
1173         /* check users.conf and update the password stored for the mailbox*/
1174         /* if no vmsecret entry exists create one. */
1175         if ((cfg = ast_config_load("users.conf", config_flags))) {
1176                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1177                 while ((category = ast_category_browse(cfg, category))) {
1178                         ast_debug(4, "users.conf: %s\n", category);
1179                         if (!strcasecmp(category, vmu->mailbox)) {
1180                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
1181                                         ast_debug(3, "looks like we need to make vmsecret!\n");
1182                                         var = ast_variable_new("vmsecret", newpassword, "");
1183                                 } 
1184                                 new = alloca(strlen(newpassword)+1);
1185                                 sprintf(new, "%s", newpassword);
1186                                 if (!(cat = ast_category_get(cfg, category))) {
1187                                         ast_debug(4, "failed to get category!\n");
1188                                         break;
1189                                 }
1190                                 if (!var)               
1191                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
1192                                 else
1193                                         ast_variable_append(cat, var);
1194                         }
1195                 }
1196                 /* save the results and clean things up */
1197                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
1198                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1199                 config_text_file_save("users.conf", cfg, "AppVoicemail");
1200         }
1201 }
1202
1203 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1204 {
1205         char buf[255];
1206         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
1207         if (!ast_safe_system(buf)) {
1208                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1209                 /* Reset the password in memory, too */
1210                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1211         }
1212 }
1213
1214 /*! 
1215  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1216  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1217  * \param len The length of the path string that was written out.
1218  * 
1219  * The path is constructed as 
1220  *      VM_SPOOL_DIRcontext/ext/folder
1221  *
1222  * \return zero on success, -1 on error.
1223  */
1224 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1225 {
1226         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1227 }
1228
1229 #ifdef IMAP_STORAGE
1230 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix)
1231 {
1232         int res;
1233         if ((res = ast_mkdir(dir, 01777))) {
1234                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1235                 return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
1236         }
1237         return snprintf(dest, len, "%s/%smsg%04d", dir, prefix, num);
1238 }
1239
1240 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1241 {
1242         unsigned long messageNum = 0;
1243         char arg[10];
1244
1245         /* find real message number based on msgnum */
1246         /* this may be an index into vms->msgArray based on the msgnum. */
1247
1248         messageNum = vms->msgArray[msgnum];
1249         if (messageNum == 0) {
1250                 ast_log(AST_LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1251                 return;
1252         }
1253         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1254         /* delete message */
1255         snprintf (arg, sizeof(arg), "%lu",messageNum);
1256         mail_setflag (vms->mailstream,arg,"\\DELETED");
1257 }
1258
1259 #endif
1260 static int make_file(char *dest, int len, char *dir, int num)
1261 {
1262         return snprintf(dest, len, "%s/msg%04d", dir, num);
1263 }
1264
1265 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1266  * \param dest    String. base directory.
1267  * \param len     Length of dest.
1268  * \param context String. Ignored if is null or empty string.
1269  * \param ext     String. Ignored if is null or empty string.
1270  * \param folder  String. Ignored if is null or empty string. 
1271  * \return -1 on failure, 0 on success.
1272  */
1273 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1274 {
1275         mode_t  mode = VOICEMAIL_DIR_MODE;
1276         int res;
1277
1278         make_dir(dest, len, context, ext, folder);
1279         if ((res = ast_mkdir(dest, mode))) {
1280                 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1281                 return -1;
1282         }
1283         return 0;
1284 }
1285
1286 /*! \brief Lock file path
1287     only return failure if ast_lock_path returns 'timeout',
1288    not if the path does not exist or any other reason
1289 */
1290 static int vm_lock_path(const char *path)
1291 {
1292         switch (ast_lock_path(path)) {
1293         case AST_LOCK_TIMEOUT:
1294                 return -1;
1295         default:
1296                 return 0;
1297         }
1298 }
1299
1300
1301 #ifdef ODBC_STORAGE
1302 struct generic_prepare_struct {
1303         char *sql;
1304         int argc;
1305         char **argv;
1306 };
1307
1308 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1309 {
1310         struct generic_prepare_struct *gps = data;
1311         int res, i;
1312         SQLHSTMT stmt;
1313
1314         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1315         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1316                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
1317                 return NULL;
1318         }
1319         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1320         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1321                 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1322                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1323                 return NULL;
1324         }
1325         for (i = 0; i < gps->argc; i++)
1326                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1327
1328         return stmt;
1329 }
1330
1331 /*!
1332  * \brief Retrieves a file from an ODBC data store.
1333  * \param dir the path to the file to be retreived.
1334  * \param msgnum the message number, such as within a mailbox folder.
1335  * 
1336  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
1337  * 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.
1338  *
1339  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
1340  * The output is the message information file with the name msgnum and the extension .txt
1341  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
1342  * 
1343  * \return 0 on success, -1 on error.
1344  */
1345 static int retrieve_file(char *dir, int msgnum)
1346 {
1347         int x = 0;
1348         int res;
1349         int fd=-1;
1350         size_t fdlen = 0;
1351         void *fdm = MAP_FAILED;
1352         SQLSMALLINT colcount=0;
1353         SQLHSTMT stmt;
1354         char sql[PATH_MAX];
1355         char fmt[80]="";
1356         char *c;
1357         char coltitle[256];
1358         SQLSMALLINT collen;
1359         SQLSMALLINT datatype;
1360         SQLSMALLINT decimaldigits;
1361         SQLSMALLINT nullable;
1362         SQLULEN colsize;
1363         SQLLEN colsize2;
1364         FILE *f=NULL;
1365         char rowdata[80];
1366         char fn[PATH_MAX];
1367         char full_fn[PATH_MAX];
1368         char msgnums[80];
1369         char *argv[] = { dir, msgnums };
1370         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1371
1372         struct odbc_obj *obj;
1373         obj = ast_odbc_request_obj(odbc_database, 0);
1374         if (obj) {
1375                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1376                 c = strchr(fmt, '|');
1377                 if (c)
1378                         *c = '\0';
1379                 if (!strcasecmp(fmt, "wav49"))
1380                         strcpy(fmt, "WAV");
1381                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1382                 if (msgnum > -1)
1383                         make_file(fn, sizeof(fn), dir, msgnum);
1384                 else
1385                         ast_copy_string(fn, dir, sizeof(fn));
1386
1387                 /* Create the information file */
1388                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1389                 
1390                 if (!(f = fopen(full_fn, "w+"))) {
1391                         ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1392                         goto yuck;
1393                 }
1394                 
1395                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1396                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1397                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1398                 if (!stmt) {
1399                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1400                         ast_odbc_release_obj(obj);
1401                         goto yuck;
1402                 }
1403                 res = SQLFetch(stmt);
1404                 if (res == SQL_NO_DATA) {
1405                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1406                         ast_odbc_release_obj(obj);
1407                         goto yuck;
1408                 } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1409                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1410                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1411                         ast_odbc_release_obj(obj);
1412                         goto yuck;
1413                 }
1414                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1415                 if (fd < 0) {
1416                         ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1417                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1418                         ast_odbc_release_obj(obj);
1419                         goto yuck;
1420                 }
1421                 res = SQLNumResultCols(stmt, &colcount);
1422                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1423                         ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1424                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1425                         ast_odbc_release_obj(obj);
1426                         goto yuck;
1427                 }
1428                 if (f) 
1429                         fprintf(f, "[message]\n");
1430                 for (x=0;x<colcount;x++) {
1431                         rowdata[0] = '\0';
1432                         collen = sizeof(coltitle);
1433                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1434                                                 &datatype, &colsize, &decimaldigits, &nullable);
1435                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1436                                 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1437                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1438                                 ast_odbc_release_obj(obj);
1439                                 goto yuck;
1440                         }
1441                         if (!strcasecmp(coltitle, "recording")) {
1442                                 off_t offset;
1443                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1444                                 fdlen = colsize2;
1445                                 if (fd > -1) {
1446                                         char tmp[1]="";
1447                                         lseek(fd, fdlen - 1, SEEK_SET);
1448                                         if (write(fd, tmp, 1) != 1) {
1449                                                 close(fd);
1450                                                 fd = -1;
1451                                                 continue;
1452                                         }
1453                                         /* Read out in small chunks */
1454                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1455                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1456                                                         ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1457                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1458                                                         ast_odbc_release_obj(obj);
1459                                                         goto yuck;
1460                                                 } else {
1461                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1462                                                         munmap(fdm, CHUNKSIZE);
1463                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1464                                                                 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1465                                                                 unlink(full_fn);
1466                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1467                                                                 ast_odbc_release_obj(obj);
1468                                                                 goto yuck;
1469                                                         }
1470                                                 }
1471                                         }
1472                                         truncate(full_fn, fdlen);
1473                                 }
1474                         } else {
1475                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1476                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1477                                         ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
1478                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1479                                         ast_odbc_release_obj(obj);
1480                                         goto yuck;
1481                                 }
1482                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1483                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1484                         }
1485                 }
1486                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1487                 ast_odbc_release_obj(obj);
1488         } else
1489                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1490 yuck:   
1491         if (f)
1492                 fclose(f);
1493         if (fd > -1)
1494                 close(fd);
1495         return x - 1;
1496 }
1497
1498 /*!
1499  * \brief Removes a voicemail message file.
1500  * \param dir the path to the message file.
1501  * \param msgnum the unique number for the message within the mailbox.
1502  *
1503  * Removes the message content file and the information file.
1504  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
1505  * Typical use is to clean up after a RETRIEVE operation. 
1506  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
1507  * \return zero on success, -1 on error.
1508  */
1509 static int remove_file(char *dir, int msgnum)
1510 {
1511         char fn[PATH_MAX];
1512         char full_fn[PATH_MAX];
1513         char msgnums[80];
1514         
1515         if (msgnum > -1) {
1516                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1517                 make_file(fn, sizeof(fn), dir, msgnum);
1518         } else
1519                 ast_copy_string(fn, dir, sizeof(fn));
1520         ast_filedelete(fn, NULL);       
1521         if (ast_check_realtime("voicemail_data")) {
1522                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1523         }
1524         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1525         unlink(full_fn);
1526         return 0;
1527 }
1528
1529 /*!
1530  * \brief Determines the highest message number in use for a given user and mailbox folder.
1531  * \param vmu 
1532  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1533  *
1534  * This method is used when mailboxes are stored in an ODBC back end.
1535  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
1536  *
1537  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
1538  */
1539 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1540 {
1541         int x = 0;
1542         int res;
1543         SQLHSTMT stmt;
1544         char sql[PATH_MAX];
1545         char rowdata[20];
1546         char *argv[] = { dir };
1547         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1548
1549         struct odbc_obj *obj;
1550         obj = ast_odbc_request_obj(odbc_database, 0);
1551         if (obj) {
1552                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1553                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1554                 if (!stmt) {
1555                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1556                         ast_odbc_release_obj(obj);
1557                         goto yuck;
1558                 }
1559                 res = SQLFetch(stmt);
1560                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1561                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1562                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1563                         ast_odbc_release_obj(obj);
1564                         goto yuck;
1565                 }
1566                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1567                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1568                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1569                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1570                         ast_odbc_release_obj(obj);
1571                         goto yuck;
1572                 }
1573                 if (sscanf(rowdata, "%d", &x) != 1)
1574                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
1575                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1576                 ast_odbc_release_obj(obj);
1577         } else
1578                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1579 yuck:   
1580         return x - 1;
1581 }
1582
1583 /*!
1584  * \brief Determines if the specified message exists.
1585  * \param dir the folder the mailbox folder to look for messages. 
1586  * \param msgnum the message index to query for.
1587  *
1588  * This method is used when mailboxes are stored in an ODBC back end.
1589  *
1590  * \return greater than zero if the message exists, zero when the message does not exist or on error.
1591  */
1592 static int message_exists(char *dir, int msgnum)
1593 {
1594         int x = 0;
1595         int res;
1596         SQLHSTMT stmt;
1597         char sql[PATH_MAX];
1598         char rowdata[20];
1599         char msgnums[20];
1600         char *argv[] = { dir, msgnums };
1601         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1602
1603         struct odbc_obj *obj;
1604         obj = ast_odbc_request_obj(odbc_database, 0);
1605         if (obj) {
1606                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1607                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1608                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1609                 if (!stmt) {
1610                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1611                         ast_odbc_release_obj(obj);
1612                         goto yuck;
1613                 }
1614                 res = SQLFetch(stmt);
1615                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1616                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1617                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1618                         ast_odbc_release_obj(obj);
1619                         goto yuck;
1620                 }
1621                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1622                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1623                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1624                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1625                         ast_odbc_release_obj(obj);
1626                         goto yuck;
1627                 }
1628                 if (sscanf(rowdata, "%d", &x) != 1)
1629                         ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
1630                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1631                 ast_odbc_release_obj(obj);
1632         } else
1633                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1634 yuck:   
1635         return x;
1636 }
1637
1638 /*!
1639  * \brief returns the one-based count for messages.
1640  * \param vmu
1641  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1642  *
1643  * This method is used when mailboxes are stored in an ODBC back end.
1644  * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
1645  * one-based messages.
1646  * This method just calls last_message_index and returns +1 of its value.
1647  *
1648  * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
1649  */
1650 static int count_messages(struct ast_vm_user *vmu, char *dir)
1651 {
1652         return last_message_index(vmu, dir) + 1;
1653 }
1654
1655 /*!
1656  * \brief Deletes a message from the mailbox folder.
1657  * \param sdir The mailbox folder to work in.
1658  * \param smsg The message index to be deleted.
1659  *
1660  * This method is used when mailboxes are stored in an ODBC back end.
1661  * The specified message is directly deleted from the database 'voicemessages' table.
1662  * 
1663  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
1664  */
1665 static void delete_file(char *sdir, int smsg)
1666 {
1667         SQLHSTMT stmt;
1668         char sql[PATH_MAX];
1669         char msgnums[20];
1670         char *argv[] = { sdir, msgnums };
1671         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1672
1673         struct odbc_obj *obj;
1674         obj = ast_odbc_request_obj(odbc_database, 0);
1675         if (obj) {
1676                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1677                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1678                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1679                 if (!stmt)
1680                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1681                 else
1682                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1683                 ast_odbc_release_obj(obj);
1684         } else
1685                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1686         return; 
1687 }
1688
1689 /*!
1690  * \brief Copies a voicemail from one mailbox to another.
1691  * \param sdir the folder for which to look for the message to be copied.
1692  * \param smsg the index of the message to be copied.
1693  * \param ddir the destination folder to copy the message into.
1694  * \param dmsg the index to be used for the copied message.
1695  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
1696  * \param dmailboxcontext The context for the destination user.
1697  *
1698  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
1699  */
1700 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1701 {
1702         SQLHSTMT stmt;
1703         char sql[512];
1704         char msgnums[20];
1705         char msgnumd[20];
1706         struct odbc_obj *obj;
1707         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1708         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1709
1710         delete_file(ddir, dmsg);
1711         obj = ast_odbc_request_obj(odbc_database, 0);
1712         if (obj) {
1713                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1714                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1715                 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);
1716                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1717                 if (!stmt)
1718                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1719                 else
1720                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1721                 ast_odbc_release_obj(obj);
1722         } else
1723                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1724         return; 
1725 }
1726
1727 struct insert_data {
1728         char *sql;
1729         char *dir;
1730         char *msgnums;
1731         void *data;
1732         SQLLEN datalen;
1733         const char *context;
1734         const char *macrocontext;
1735         const char *callerid;
1736         const char *origtime;
1737         const char *duration;
1738         char *mailboxuser;
1739         char *mailboxcontext;
1740         const char *category;
1741         const char *flag;
1742 };
1743
1744 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
1745 {
1746         struct insert_data *data = vdata;
1747         int res;
1748         SQLHSTMT stmt;
1749         SQLLEN len = data->datalen;
1750
1751         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1752         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1753                 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
1754                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1755                 return NULL;
1756         }
1757
1758         SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
1759         SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
1760         SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &len);
1761         SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
1762         SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
1763         SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
1764         SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
1765         SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
1766         SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
1767         SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
1768         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
1769         if (!ast_strlen_zero(data->category)) {
1770                 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
1771         }
1772         res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
1773         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1774                 ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
1775                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1776                 return NULL;
1777         }
1778
1779         return stmt;
1780 }
1781
1782 /*!
1783  * \brief Stores a voicemail into the database.
1784  * \param dir the folder the mailbox folder to store the message.
1785  * \param mailboxuser the user owning the mailbox folder.
1786  * \param mailboxcontext
1787  * \param msgnum the message index for the message to be stored.
1788  *
1789  * This method is used when mailboxes are stored in an ODBC back end.
1790  * The message sound file and information file is looked up on the file system. 
1791  * A SQL query is invoked to store the message into the (MySQL) database.
1792  *
1793  * \return the zero on success -1 on error.
1794  */
1795 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1796 {
1797         int res = 0;
1798         int fd = -1;
1799         void *fdm = MAP_FAILED;
1800         size_t fdlen = -1;
1801         SQLHSTMT stmt;
1802         char sql[PATH_MAX];
1803         char msgnums[20];
1804         char fn[PATH_MAX];
1805         char full_fn[PATH_MAX];
1806         char fmt[80]="";
1807         char *c;
1808         struct ast_config *cfg=NULL;
1809         struct odbc_obj *obj;
1810         struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
1811         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1812
1813         delete_file(dir, msgnum);
1814         if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
1815                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1816                 return -1;
1817         }
1818
1819         do {
1820                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1821                 c = strchr(fmt, '|');
1822                 if (c)
1823                         *c = '\0';
1824                 if (!strcasecmp(fmt, "wav49"))
1825                         strcpy(fmt, "WAV");
1826                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1827                 if (msgnum > -1)
1828                         make_file(fn, sizeof(fn), dir, msgnum);
1829                 else
1830                         ast_copy_string(fn, dir, sizeof(fn));
1831                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1832                 cfg = ast_config_load(full_fn, config_flags);
1833                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1834                 fd = open(full_fn, O_RDWR);
1835                 if (fd < 0) {
1836                         ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1837                         res = -1;
1838                         break;
1839                 }
1840                 if (cfg) {
1841                         if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
1842                                 idata.context = "";
1843                         }
1844                         if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
1845                                 idata.macrocontext = "";
1846                         }
1847                         if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
1848                                 idata.callerid = "";
1849                         }
1850                         if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
1851                                 idata.origtime = "";
1852                         }
1853                         if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
1854                                 idata.duration = "";
1855                         }
1856                         if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
1857                                 idata.category = "";
1858                         }
1859                         if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
1860                                 idata.flag = "";
1861                         }
1862                 }
1863                 fdlen = lseek(fd, 0, SEEK_END);
1864                 lseek(fd, 0, SEEK_SET);
1865                 printf("Length is %zd\n", fdlen);
1866                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1867                 if (fdm == MAP_FAILED) {
1868                         ast_log(AST_LOG_WARNING, "Memory map failed!\n");
1869                         res = -1;
1870                         break;
1871                 } 
1872                 idata.data = fdm;
1873                 idata.datalen = fdlen;
1874
1875                 if (!ast_strlen_zero(idata.category)) 
1876                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1877                 else
1878                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1879
1880                 if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
1881                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1882                 } else {
1883                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1884                         res = -1;
1885                 }
1886         } while (0);
1887         if (obj) {
1888                 ast_odbc_release_obj(obj);
1889         }
1890         if (cfg)
1891                 ast_config_destroy(cfg);
1892         if (fdm != MAP_FAILED)
1893                 munmap(fdm, fdlen);
1894         if (fd > -1)
1895                 close(fd);
1896         return res;
1897 }
1898
1899 /*!
1900  * \brief Renames a message in a mailbox folder.
1901  * \param sdir The folder of the message to be renamed.
1902  * \param smsg The index of the message to be renamed.
1903  * \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.
1904  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
1905  * \param ddir The destination folder for the message to be renamed into
1906  * \param dmsg The destination message for the message to be renamed.
1907  *
1908  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
1909  * 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.
1910  * 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.
1911  */
1912 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1913 {
1914         SQLHSTMT stmt;
1915         char sql[PATH_MAX];
1916         char msgnums[20];
1917         char msgnumd[20];
1918         struct odbc_obj *obj;
1919         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1920         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1921
1922         delete_file(ddir, dmsg);
1923         obj = ast_odbc_request_obj(odbc_database, 0);
1924         if (obj) {
1925                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1926                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1927                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1928                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1929                 if (!stmt)
1930                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1931                 else
1932                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1933                 ast_odbc_release_obj(obj);
1934         } else
1935                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1936         return; 
1937 }
1938
1939 #else
1940 #ifndef IMAP_STORAGE
1941 /*!
1942  * \brief Find all .txt files - even if they are not in sequence from 0000.
1943  * \param vmu
1944  * \param dir
1945  *
1946  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1947  *
1948  * \return the count of messages, zero or more.
1949  */
1950 static int count_messages(struct ast_vm_user *vmu, char *dir)
1951 {
1952
1953         int vmcount = 0;
1954         DIR *vmdir = NULL;
1955         struct dirent *vment = NULL;
1956
1957         if (vm_lock_path(dir))
1958                 return ERROR_LOCK_PATH;
1959
1960         if ((vmdir = opendir(dir))) {
1961                 while ((vment = readdir(vmdir))) {
1962                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
1963                                 vmcount++;
1964                         }
1965                 }
1966                 closedir(vmdir);
1967         }
1968         ast_unlock_path(dir);
1969         
1970         return vmcount;
1971 }
1972
1973 /*!
1974  * \brief Renames a message in a mailbox folder.
1975  * \param sfn The path to the mailbox information and data file to be renamed.
1976  * \param dfn The path for where the message data and information files will be renamed to.
1977  *
1978  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1979  */
1980 static void rename_file(char *sfn, char *dfn)
1981 {
1982         char stxt[PATH_MAX];
1983         char dtxt[PATH_MAX];
1984         ast_filerename(sfn,dfn,NULL);
1985         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1986         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1987         if (ast_check_realtime("voicemail_data")) {
1988                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1989         }
1990         rename(stxt, dtxt);
1991 }
1992
1993 /*! 
1994  * \brief Determines the highest message number in use for a given user and mailbox folder.
1995  * \param vmu 
1996  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
1997  *
1998  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
1999  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
2000  *
2001  * \note Should always be called with a lock already set on dir.
2002  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
2003  */
2004 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2005 {
2006         int x;
2007         unsigned char map[MAXMSGLIMIT] = "";
2008         DIR *msgdir;
2009         struct dirent *msgdirent;
2010         int msgdirint;
2011
2012         /* Reading the entire directory into a file map scales better than
2013          * doing a stat repeatedly on a predicted sequence.  I suspect this
2014          * is partially due to stat(2) internally doing a readdir(2) itself to
2015          * find each file. */
2016         msgdir = opendir(dir);
2017         while ((msgdirent = readdir(msgdir))) {
2018                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
2019                         map[msgdirint] = 1;
2020         }
2021         closedir(msgdir);
2022
2023         for (x = 0; x < vmu->maxmsg; x++) {
2024                 if (map[x] == 0)
2025                         break;
2026         }
2027
2028         return x - 1;
2029 }
2030
2031
2032 /*!
2033  * \brief Utility function to copy a file.
2034  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
2035  * \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.
2036  *
2037  * 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.
2038  * The copy operation copies up to 4096 bytes at once.
2039  *
2040  * \return zero on success, -1 on error.
2041  */
2042 static int copy(char *infile, char *outfile)
2043 {
2044         int ifd;
2045         int ofd;
2046         int res;
2047         int len;
2048         char buf[4096];
2049
2050 #ifdef HARDLINK_WHEN_POSSIBLE
2051         /* Hard link if possible; saves disk space & is faster */
2052         if (link(infile, outfile)) {
2053 #endif
2054                 if ((ifd = open(infile, O_RDONLY)) < 0) {
2055                         ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
2056                         return -1;
2057                 }
2058                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
2059                         ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
2060                         close(ifd);
2061                         return -1;
2062                 }
2063                 do {
2064                         len = read(ifd, buf, sizeof(buf));
2065                         if (len < 0) {
2066                                 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
2067                                 close(ifd);
2068                                 close(ofd);
2069                                 unlink(outfile);
2070                         }
2071                         if (len) {
2072                                 res = write(ofd, buf, len);
2073                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
2074                                         ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
2075                                         close(ifd);
2076                                         close(ofd);
2077                                         unlink(outfile);
2078                                 }
2079                         }
2080                 } while (len);
2081                 close(ifd);
2082                 close(ofd);
2083                 return 0;
2084 #ifdef HARDLINK_WHEN_POSSIBLE
2085         } else {
2086                 /* Hard link succeeded */
2087                 return 0;
2088         }
2089 #endif
2090 }
2091
2092 /*!
2093  * \brief Copies a voicemail information (envelope) file.
2094  * \param frompath
2095  * \param topath 
2096  *
2097  * Every voicemail has the data (.wav) file, and the information file.
2098  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
2099  * This is used by the COPY macro when not using IMAP storage.
2100  */
2101 static void copy_plain_file(char *frompath, char *topath)
2102 {
2103         char frompath2[PATH_MAX], topath2[PATH_MAX];
2104         struct ast_variable *tmp,*var = NULL;
2105         const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
2106         ast_filecopy(frompath, topath, NULL);
2107         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
2108         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
2109         if (ast_check_realtime("voicemail_data")) {
2110                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
2111                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
2112                 for (tmp = var; tmp; tmp = tmp->next) {
2113                         if (!strcasecmp(tmp->name, "origmailbox")) {
2114                                 origmailbox = tmp->value;
2115                         } else if (!strcasecmp(tmp->name, "context")) {
2116                                 context = tmp->value;
2117                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
2118                                 macrocontext = tmp->value;
2119                         } else if (!strcasecmp(tmp->name, "exten")) {
2120                                 exten = tmp->value;
2121                         } else if (!strcasecmp(tmp->name, "priority")) {
2122                                 priority = tmp->value;
2123                         } else if (!strcasecmp(tmp->name, "callerchan")) {
2124                                 callerchan = tmp->value;
2125                         } else if (!strcasecmp(tmp->name, "callerid")) {
2126                                 callerid = tmp->value;
2127                         } else if (!strcasecmp(tmp->name, "origdate")) {
2128                                 origdate = tmp->value;
2129                         } else if (!strcasecmp(tmp->name, "origtime")) {
2130                                 origtime = tmp->value;
2131                         } else if (!strcasecmp(tmp->name, "category")) {
2132                                 category = tmp->value;
2133                         } else if (!strcasecmp(tmp->name, "duration")) {
2134                                 duration = tmp->value;
2135                         }
2136                 }
2137                 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);
2138         }
2139         copy(frompath2, topath2);
2140         ast_variables_destroy(var);
2141 }
2142
2143 #endif /* #ifndef IMAP_STORAGE */
2144 #endif /* #else of #ifdef ODBC_STORAGE */
2145 #ifndef ODBC_STORAGE
2146 /*! 
2147  * \brief Removes the voicemail sound and information file.
2148  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
2149  *
2150  * This is used by the DELETE macro when voicemails are stored on the file system.
2151  *
2152  * \return zero on success, -1 on error.
2153  */
2154 static int vm_delete(char *file)
2155 {
2156         char *txt;
2157         int txtsize = 0;
2158
2159         txtsize = (strlen(file) + 5)*sizeof(char);
2160         txt = alloca(txtsize);
2161         /* Sprintf here would safe because we alloca'd exactly the right length,
2162          * but trying to eliminate all sprintf's anyhow
2163          */
2164         if (ast_check_realtime("voicemail_data")) {
2165                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
2166         }
2167         snprintf(txt, txtsize, "%s.txt", file);
2168         unlink(txt);
2169         return ast_filedelete(file, NULL);
2170 }
2171 #endif
2172
2173 /*!
2174  * \brief utility used by inchar(), for base_encode()
2175  */
2176 static int inbuf(struct baseio *bio, FILE *fi)
2177 {
2178         int l;
2179
2180         if (bio->ateof)
2181                 return 0;
2182
2183         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
2184                 if (ferror(fi))
2185                         return -1;
2186
2187                 bio->ateof = 1;
2188                 return 0;
2189         }
2190
2191         bio->iolen= l;
2192         bio->iocp= 0;
2193
2194         return 1;
2195 }
2196
2197 /*!
2198  * \brief utility used by base_encode()
2199  */
2200 static int inchar(struct baseio *bio, FILE *fi)
2201 {
2202         if (bio->iocp>=bio->iolen) {
2203                 if (!inbuf(bio, fi))
2204                         return EOF;
2205         }
2206
2207         return bio->iobuf[bio->iocp++];
2208 }
2209
2210 /*!
2211  * \brief utility used by base_encode()
2212  */
2213 static int ochar(struct baseio *bio, int c, FILE *so)
2214 {
2215         if (bio->linelength >= BASELINELEN) {
2216                 if (fputs(eol,so) == EOF)
2217                         return -1;
2218
2219                 bio->linelength= 0;
2220         }
2221
2222         if (putc(((unsigned char)c),so) == EOF)
2223                 return -1;
2224
2225         bio->linelength++;
2226
2227         return 1;
2228 }
2229
2230 /*!
2231  * \brief Performs a base 64 encode algorithm on the contents of a File
2232  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
2233  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
2234  *
2235  * 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 ?
2236  *
2237  * \return zero on success, -1 on error.
2238  */
2239 static int base_encode(char *filename, FILE *so)
2240 {
2241         static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
2242                 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
2243                 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
2244                 '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
2245         int i,hiteof= 0;
2246         FILE *fi;
2247         struct baseio bio;
2248
2249         memset(&bio, 0, sizeof(bio));
2250         bio.iocp = BASEMAXINLINE;
2251
2252         if (!(fi = fopen(filename, "rb"))) {
2253                 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
2254                 return -1;
2255         }
2256
2257         while (!hiteof){
2258                 unsigned char igroup[3], ogroup[4];
2259                 int c,n;
2260
2261                 igroup[0]= igroup[1]= igroup[2]= 0;
2262
2263                 for (n= 0;n<3;n++) {
2264                         if ((c = inchar(&bio, fi)) == EOF) {
2265                                 hiteof= 1;
2266                                 break;
2267                         }
2268
2269                         igroup[n]= (unsigned char)c;
2270                 }
2271
2272                 if (n> 0) {
2273                         ogroup[0]= dtable[igroup[0]>>2];
2274                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
2275                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
2276                         ogroup[3]= dtable[igroup[2]&0x3F];
2277
2278                         if (n<3) {
2279                                 ogroup[3]= '=';
2280
2281                                 if (n<2)
2282                                         ogroup[2]= '=';
2283                         }
2284
2285                         for (i= 0;i<4;i++)
2286                                 ochar(&bio, ogroup[i], so);
2287                 }
2288         }
2289
2290         fclose(fi);
2291         
2292         if (fputs(eol,so)==EOF)
2293                 return 0;
2294
2295         return 1;
2296 }
2297
2298 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)
2299 {
2300         char callerid[256];
2301         /* Prepare variables for substitution in email body and subject */
2302         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
2303         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
2304         snprintf(passdata, passdatasize, "%d", msgnum);
2305         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
2306         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
2307         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
2308         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
2309         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
2310         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
2311         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
2312         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
2313         pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
2314 }
2315
2316 /*!
2317  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
2318  * \param from The string to work with.
2319  * \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.
2320  * 
2321  * \return The destination string with quotes wrapped on it (the to field).
2322  */
2323 static char *quote(const char *from, char *to, size_t len)
2324 {
2325         char *ptr = to;
2326         *ptr++ = '"';
2327         for (; ptr < to + len - 1; from++) {
2328                 if (*from == '"')
2329                         *ptr++ = '\\';
2330                 else if (*from == '\0')
2331                         break;
2332                 *ptr++ = *from;
2333         }
2334         if (ptr < to + len - 1)
2335                 *ptr++ = '"';
2336         *ptr = '\0';
2337         return to;
2338 }
2339
2340 /*! \brief
2341  * fill in *tm for current time according to the proper timezone, if any.
2342  * Return tm so it can be used as a function argument.
2343  */
2344 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
2345 {
2346         const struct vm_zone *z = NULL;
2347         struct timeval t = ast_tvnow();
2348
2349         /* Does this user have a timezone specified? */
2350         if (!ast_strlen_zero(vmu->zonetag)) {
2351                 /* Find the zone in the list */
2352                 AST_LIST_LOCK(&zones);
2353                 AST_LIST_TRAVERSE(&zones, z, list) {
2354                         if (!strcmp(z->name, vmu->zonetag))
2355                                 break;
2356                 }
2357                 AST_LIST_UNLOCK(&zones);
2358         }
2359         ast_localtime(&t, tm, z ? z->timezone : NULL);
2360         return tm;
2361 }
2362
2363 /*! \brief same as mkstemp, but return a FILE * */
2364 static FILE *vm_mkftemp(char *template)
2365 {
2366         FILE *p = NULL;
2367         int pfd = mkstemp(template);
2368         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
2369         if (pfd > -1) {
2370                 p = fdopen(pfd, "w+");
2371                 if (!p) {
2372                         close(pfd);
2373                         pfd = -1;
2374                 }
2375         }
2376         return p;
2377 }
2378
2379 /*!
2380  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
2381  * \param p The output file to generate the email contents into.
2382  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
2383  * \param vmu The voicemail user who is sending the voicemail.
2384  * \param msgnum The message index in the mailbox folder.
2385  * \param context 
2386  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
2387  * \param cidnum The caller ID number.
2388  * \param cidname The caller ID name.
2389  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
2390  * \param format The message sound file format. i.e. .wav
2391  * \param duration The time of the message content, in seconds.
2392  * \param attach_user_voicemail if 1, the sound file is attached to the email.
2393  * \param chan
2394  * \param category
2395  * \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.
2396  *
2397  * 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.
2398  */
2399 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)
2400 {
2401         char date[256];
2402         char host[MAXHOSTNAMELEN] = "";
2403         char who[256];
2404         char bound[256];
2405         char dur[256];
2406         struct ast_tm tm;
2407         char *passdata2;
2408         size_t len_passdata;
2409         char *greeting_attachment; 
2410         char filename[256];
2411
2412 #ifdef IMAP_STORAGE
2413 #define ENDL "\r\n"
2414 #else
2415 #define ENDL "\n"
2416 #endif
2417
2418         gethostname(host, sizeof(host)-1);
2419
2420         if (strchr(srcemail, '@'))
2421                 ast_copy_string(who, srcemail, sizeof(who));
2422         else 
2423                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2424         
2425         greeting_attachment = strrchr(ast_strdupa(attach), '/');
2426         if (greeting_attachment)
2427                 *greeting_attachment++ = '\0';
2428
2429         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2430         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2431         fprintf(p, "Date: %s" ENDL, date);
2432
2433         /* Set date format for voicemail mail */
2434         ast_strftime(date, sizeof(date), emaildateformat, &tm);
2435
2436         if (!ast_strlen_zero(fromstring)) {
2437                 struct ast_channel *ast;
2438                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2439                         char *passdata;
2440                         int vmlen = strlen(fromstring)*3 + 200;
2441                         passdata = alloca(vmlen);
2442                         memset(passdata, 0, vmlen);
2443                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2444                         pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
2445                         len_passdata = strlen(passdata) * 2 + 3;
2446                         passdata2 = alloca(len_passdata);
2447                         fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
2448                         ast_channel_free(ast);
2449                 } else
2450                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2451         } else
2452                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
2453         len_passdata = strlen(vmu->fullname) * 2 + 3;
2454         passdata2 = alloca(len_passdata);
2455         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
2456         if (!ast_strlen_zero(emailsubject)) {
2457                 struct ast_channel *ast;
2458                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2459                         char *passdata;
2460                         int vmlen = strlen(emailsubject) * 3 + 200;
2461                         passdata = alloca(vmlen);
2462                         memset(passdata, 0, vmlen);
2463                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2464                         pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2465                         fprintf(p, "Subject: %s" ENDL, passdata);
2466                         ast_channel_free(ast);
2467                 } else
2468                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2469         } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
2470                 if (ast_strlen_zero(flag)) {
2471                         fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2472                 } else {
2473                         fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
2474                 }
2475         } else {
2476                 if (ast_strlen_zero(flag)) {
2477                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2478                 } else {
2479                         fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
2480                 }
2481         }
2482
2483         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2484         if (imap) {
2485                 /* additional information needed for IMAP searching */
2486                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2487                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2488                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2489                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2490                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2491                 /* flag added for Urgent */
2492                 fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
2493                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2494                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2495                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2496                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2497                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2498                 if (!ast_strlen_zero(category)) 
2499                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2500                 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
2501                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2502                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2503         }
2504         if (!ast_strlen_zero(cidnum))
2505                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2506         if (!ast_strlen_zero(cidname))
2507                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2508         fprintf(p, "MIME-Version: 1.0" ENDL);
2509         if (attach_user_voicemail) {
2510                 /* Something unique. */
2511                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2512
2513                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2514                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2515                 fprintf(p, "--%s" ENDL, bound);
2516         }
2517         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2518         if (emailbody) {
2519                 struct ast_channel *ast;
2520                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2521                         char *passdata;
2522                         int vmlen = strlen(emailbody)*3 + 200;
2523                         passdata = alloca(vmlen);
2524                         memset(passdata, 0, vmlen);
2525                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2526                         pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2527                         fprintf(p, "%s" ENDL, passdata);
2528                         ast_channel_free(ast);
2529                 } else
2530                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2531         } else if (msgnum > -1){
2532                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
2533                 "in mailbox %s from %s, on %s so you might" ENDL
2534                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2535                 dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2536         } else {
2537                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2538                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2539         }
2540
2541         if (attach_user_voicemail) {
2542                 if (!ast_strlen_zero(attach2)) {
2543                         snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
2544                         ast_debug(5, "creating attachment filename %s\n", filename);
2545                         add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 0, msgnum);
2546                         snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
2547                         ast_debug(5, "creating second attachment filename %s\n", filename);
2548                         add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
2549                 } else {
2550                         snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
2551                         ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
2552                         add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
2553                 }
2554         }
2555 }
2556
2557 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)
2558 {
2559         char tmpdir[256], newtmp[256];
2560         char fname[256];
2561         char tmpcmd[256];
2562         int tmpfd = -1;
2563
2564         /* Eww. We want formats to tell us their own MIME type */
2565         char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2566
2567         if (vmu->volgain < -.001 || vmu->volgain > .001) {
2568                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2569                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2570                 tmpfd = mkstemp(newtmp);
2571                 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2572                 ast_debug(3, "newtmp: %s\n", newtmp);
2573                 if (tmpfd > -1) {
2574                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2575                         ast_safe_system(tmpcmd);
2576                         attach = newtmp;
2577                         ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2578                 }
2579         }
2580         fprintf(p, "--%s" ENDL, bound);
2581         if (msgnum > -1)
2582                 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
2583         else
2584                 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
2585         fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2586         fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2587         if (msgnum > -1)
2588                 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
2589         else
2590                 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
2591         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2592         base_encode(fname, p);
2593         if (last)
2594                 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
2595         if (tmpfd > -1) {
2596                 unlink(fname);
2597                 close(tmpfd);
2598                 unlink(newtmp);
2599         }
2600         return 0;
2601 }
2602 #undef ENDL
2603
2604 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)
2605 {
2606         FILE *p=NULL;
2607         char tmp[80] = "/tmp/astmail-XXXXXX";
2608         char tmp2[256];
2609
2610         if (vmu && ast_strlen_zero(vmu->email)) {
2611                 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2612                 return(0);
2613         }
2614         if (!strcmp(format, "wav49"))
2615                 format = "WAV";
2616         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));
2617         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2618            command hangs */
2619         if ((p = vm_mkftemp(tmp)) == NULL) {
2620                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2621                 return -1;
2622         } else {
2623                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
2624                 fclose(p);
2625                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2626                 ast_safe_system(tmp2);
2627                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2628         }
2629         return 0;
2630 }
2631
2632 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)
2633 {
2634         char date[256];
2635         char host[MAXHOSTNAMELEN] = "";
2636         char who[256];
2637         char dur[PATH_MAX];
2638         char tmp[80] = "/tmp/astmail-XXXXXX";
2639         char tmp2[PATH_MAX];
2640         struct ast_tm tm;
2641         FILE *p;
2642
2643         if ((p = vm_mkftemp(tmp)) == NULL) {
2644                 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2645                 return -1;
2646         }
2647         gethostname(host, sizeof(host)-1);
2648         if (strchr(srcemail, '@'))
2649                 ast_copy_string(who, srcemail, sizeof(who));
2650         else 
2651                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2652         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2653         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2654         fprintf(p, "Date: %s\n", date);
2655
2656         if (*pagerfromstring) {
2657                 struct ast_channel *ast;
2658                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2659                         char *passdata;
2660                         int vmlen = strlen(fromstring)*3 + 200;
2661                         passdata = alloca(vmlen);
2662                         memset(passdata, 0, vmlen);
2663                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2664                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2665                         fprintf(p, "From: %s <%s>\n", passdata, who);
2666                         ast_channel_free(ast);
2667                 } else 
2668                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2669         } else
2670                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2671         fprintf(p, "To: %s\n", pager);
2672         if (pagersubject) {
2673                 struct ast_channel *ast;
2674                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2675                         char *passdata;
2676                         int vmlen = strlen(pagersubject) * 3 + 200;
2677                         passdata = alloca(vmlen);
2678                         memset(passdata, 0, vmlen);
2679                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2680                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2681                         fprintf(p, "Subject: %s\n\n", passdata);
2682                         ast_channel_free(ast);
2683                 } else
2684                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2685         } else {
2686                 if (ast_strlen_zero(flag)) {
2687                         fprintf(p, "Subject: New VM\n\n");
2688                 } else {
2689                         fprintf(p, "Subject: New %s VM\n\n", flag);
2690                 }
2691         }
2692
2693         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2694         if (pagerbody) {
2695                 struct ast_channel *ast;
2696                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2697                         char *passdata;
2698                         int vmlen = strlen(pagerbody) * 3 + 200;
2699                         passdata = alloca(vmlen);
2700                         memset(passdata, 0, vmlen);
2701                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
2702                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2703                         fprintf(p, "%s\n", passdata);
2704                         ast_channel_free(ast);
2705                 } else
2706                         ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2707         } else {
2708                 fprintf(p, "New %s long %s msg in box %s\n"
2709                                 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2710         }
2711         fclose(p);
2712         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2713         ast_safe_system(tmp2);
2714         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2715         return 0;
2716 }
2717
2718 /*!
2719  * \brief Gets the current date and time, as formatted string.
2720  * \param s The buffer to hold the output formatted date.
2721  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
2722  * 
2723  * The date format string used is "%a %b %e %r UTC %Y".
2724  * 
2725  * \return zero on success, -1 on error.
2726  */
2727 static int get_date(char *s, int len)
2728 {
2729         struct ast_tm tm;
2730         struct timeval t = ast_tvnow();
2731         
2732         ast_localtime(&t, &tm, "UTC");
2733
2734         return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
2735 }
2736
2737 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2738 {
2739         int res;
2740         char fn[PATH_MAX];
2741         char dest[PATH_MAX];
2742
2743         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2744
2745         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2746                 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
2747                 return -1;
2748         }
2749
2750         RETRIEVE(fn, -1, ext, context);
2751         if (ast_fileexists(fn, NULL, NULL) > 0) {
2752                 res = ast_stream_and_wait(chan, fn, ecodes);
2753                 if (res) {
2754                         DISPOSE(fn, -1);
2755                         return res;
2756                 }
2757         } else {
2758                 /* Dispose just in case */
2759                 DISPOSE(fn, -1);
2760                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2761                 if (res)
2762                         return res;
2763                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2764                 if (res)
2765                         return res;
2766         }
2767         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2768         return res;
2769 }
2770
2771 static void free_user(struct ast_vm_user *vmu)
2772 {
2773         if (!ast_test_flag(vmu, VM_ALLOCED))
2774                 return;
2775
2776         ast_free(vmu);
2777 }
2778
2779 static void free_zone(struct vm_zone *z)
2780 {
2781         ast_free(z);
2782 }
2783
2784 /*!
2785  * \brief Gets the name of the mailbox folder from the numeric index.
2786  * \param id The numerical index for the folder name.
2787  * 
2788  * When an invalid number is entered, or one that exceeds the pre-configured list of folder names, the name "tmp" is returned.
2789  *
2790  * \return the String name that coresponds to this folder index.
2791  */
2792 static const char *mbox(int id)
2793 {
2794         static const char *msgs[] = {
2795 #ifdef IMAP_STORAGE
2796                 imapfolder,
2797 #else
2798                 "INBOX",
2799 #endif
2800                 "Old",
2801                 "Work",
2802                 "Family",
2803                 "Friends",
2804                 "Cust1",
2805                 "Cust2",
2806                 "Cust3",
2807                 "Cust4",
2808                 "Cust5",
2809                 "Deleted",
2810                 "Urgent"
2811         };
2812         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2813 }
2814 #ifdef IMAP_STORAGE
2815 /*!
2816  * \brief Converts a string folder name into the numerical identifier.
2817  * \param folder the string folder name to be converted to an id.
2818  *
2819  * This is the opposite of the mbox() function.
2820  *
2821  * \return the id that coresponds to the folder name
2822  */
2823 static int folder_int(const char *folder)
2824 {
2825         /* assume a NULL folder means INBOX */
2826         if (!folder)
2827                 return 0;
2828 #ifdef IMAP_STORAGE
2829         if (!strcasecmp(folder, imapfolder))
2830 #else
2831         if (!strcasecmp(folder, "INBOX"))
2832 #endif
2833                 return 0;
2834         else if (!strcasecmp(folder, "Old"))
2835                 return 1;
2836         else if (!strcasecmp(folder, "Work"))
2837                 return 2;
2838         else if (!strcasecmp(folder, "Family"))
2839                 return 3;
2840         else if (!strcasecmp(folder, "Friends"))
2841                 return 4;
2842         else if (!strcasecmp(folder, "Cust1"))
2843                 return 5;
2844         else if (!strcasecmp(folder, "Cust2"))
2845                 return 6;
2846         else if (!strcasecmp(folder, "Cust3"))
2847                 return 7;
2848         else if (!strcasecmp(folder, "Cust4"))
2849                 return 8;
2850         else if (!strcasecmp(folder, "Cust5"))
2851                 return 9;
2852         else if (!strcasecmp(folder, "Deleted"))
2853                 return 10;
2854         else if (!strcasecmp(folder, "Urgent"))
2855                 return 11;
2856         else /*assume they meant INBOX if folder is not found otherwise*/
2857                 return 0;
2858 }
2859 #endif
2860
2861 #ifdef ODBC_STORAGE
2862 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2863 static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2864 {
2865         int x = -1;
2866         int res;
2867         SQLHSTMT stmt;
2868         char sql[PATH_MAX];
2869         char rowdata[20];
2870         char tmp[PATH_MAX] = "";
2871         struct odbc_obj *obj;
2872         char *context;
2873         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2874
2875         if (newmsgs)
2876                 *newmsgs = 0;
2877         if (oldmsgs)
2878                 *oldmsgs = 0;
2879         if (urgentmsgs)
2880                 *urgentmsgs = 0;
2881
2882         /* If no mailbox, return immediately */
2883         if (ast_strlen_zero(mailbox))
2884                 return 0;
2885
2886         ast_copy_string(tmp, mailbox, sizeof(tmp));
2887         
2888         context = strchr(tmp, '@');
2889         if (context) {
2890                 *context = '\0';
2891                 context++;
2892         } else
2893                 context = "default";
2894         
2895         obj = ast_odbc_request_obj(odbc_database, 0);
2896         if (obj) {
2897                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2898                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2899                 if (!stmt) {
2900                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2901                         ast_odbc_release_obj(obj);
2902                         goto yuck;
2903                 }
2904                 res = SQLFetch(stmt);
2905                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2906                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2907                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2908                         ast_odbc_release_obj(obj);
2909                         goto yuck;
2910                 }
2911                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2912                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2913                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2914                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2915                         ast_odbc_release_obj(obj);
2916                         goto yuck;
2917                 }
2918                 *newmsgs = atoi(rowdata);
2919                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2920
2921                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2922                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2923                 if (!stmt) {
2924                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2925                         ast_odbc_release_obj(obj);
2926                         goto yuck;
2927                 }
2928                 res = SQLFetch(stmt);
2929                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2930                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2931                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2932                         ast_odbc_release_obj(obj);
2933                         goto yuck;
2934                 }
2935                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2936                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2937                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2938                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2939                         ast_odbc_release_obj(obj);
2940                         goto yuck;
2941                 }
2942                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2943                 *oldmsgs = atoi(rowdata);
2944                 
2945                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
2946                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2947                 if (!stmt) {
2948                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2949                         ast_odbc_release_obj(obj);
2950                         goto yuck;
2951                 }
2952                 res = SQLFetch(stmt);
2953                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2954                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2955                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2956                         ast_odbc_release_obj(obj);
2957                         goto yuck;
2958                 }
2959                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2960                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2961                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2962                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2963                         ast_odbc_release_obj(obj);
2964                         goto yuck;
2965                 }
2966                 *urgentmsgs = atoi(rowdata);
2967                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2968                 ast_odbc_release_obj(obj);
2969                 x = 0;
2970         } else
2971                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2972                 
2973 yuck:   
2974         return x;
2975 }
2976
2977 /*!
2978  * \brief Gets the number of messages that exist in a mailbox folder.
2979  * \param context
2980  * \param mailbox
2981  * \param folder
2982  * 
2983  * This method is used when ODBC backend is used.
2984  * \return The number of messages in this mailbox folder (zero or more).
2985  */
2986 static int messagecount(const char *context, const char *mailbox, const char *folder)
2987 {
2988         struct odbc_obj *obj = NULL;
2989         int nummsgs = 0;
2990         int res;
2991         SQLHSTMT stmt = NULL;
2992         char sql[PATH_MAX];
2993         char rowdata[20];
2994         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2995         if (!folder)
2996                 folder = "INBOX";
2997         /* If no mailbox, return immediately */
2998         if (ast_strlen_zero(mailbox))
2999                 return 0;
3000
3001         obj = ast_odbc_request_obj(odbc_database, 0);
3002         if (obj) {
3003                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
3004                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3005                 if (!stmt) {
3006                         ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3007                         goto yuck;
3008                 }
3009                 res = SQLFetch(stmt);
3010                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3011                         ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3012                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3013                         goto yuck;
3014                 }
3015                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3016                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3017                         ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3018                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3019                         goto yuck;
3020                 }
3021                 nummsgs = atoi(rowdata);
3022                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3023         } else
3024                 ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3025
3026 yuck:
3027         if (obj)
3028                 ast_odbc_release_obj(obj);
3029         return nummsgs;
3030 }
3031
3032 /** 
3033  * \brief Determines if the given folder has messages.
3034  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3035  * 
3036  * This function is used when the mailbox is stored in an ODBC back end.
3037  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3038  * \return 1 if the folder has one or more messages. zero otherwise.
3039  */
3040 static int has_voicemail(const char *mailbox, const char *folder)
3041 {
3042         char tmp[256], *tmp2 = tmp, *mbox, *context;
3043         ast_copy_string(tmp, mailbox, sizeof(tmp));
3044         while ((context = mbox = strsep(&tmp2, ","))) {
3045                 strsep(&context, "@");
3046                 if (ast_strlen_zero(context))
3047                         context = "default";
3048                 if (messagecount(context, mbox, folder))
3049                         return 1;
3050         }
3051         return 0;
3052 }
3053
3054 #elif defined(IMAP_STORAGE)
3055
3056 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)
3057 {
3058         char *myserveremail = serveremail;
3059         char fn[PATH_MAX];
3060         char intro[PATH_MAX];
3061         char mailbox[256];
3062         char *stringp;
3063         FILE *p=NULL;
3064         char tmp[80] = "/tmp/astmail-XXXXXX";
3065         long len;
3066         void *buf;
3067         int tempcopy = 0;
3068         STRING str;
3069         int ret; /* for better error checking */
3070         char *imapflags = NIL;
3071
3072         /* Set urgent flag for IMAP message */
3073         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
3074                 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
3075                 imapflags="\\FLAGGED";
3076         }
3077         
3078         /* Attach only the first format */
3079         fmt = ast_strdupa(fmt);
3080         stringp = fmt;
3081         strsep(&stringp, "|");
3082
3083         if (!ast_strlen_zero(vmu->serveremail))
3084                 myserveremail = vmu->serveremail;
3085
3086         if (msgnum > -1)
3087                 make_file(fn, sizeof(fn), dir, msgnum);
3088         else
3089                 ast_copy_string (fn, dir, sizeof(fn));
3090         
3091         if (ast_strlen_zero(vmu->email)) {
3092                 /* We need the vmu->email to be set when we call make_email_file, but
3093                  * if we keep it set, a duplicate e-mail will be created. So at the end
3094                  * of this function, we will revert back to an empty string if tempcopy
3095                  * is 1.
3096                  */
3097                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
3098                 tempcopy = 1;
3099         }
3100
3101         if (!ast_strlen_zero(introfile)) {
3102                 snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
3103         } else {
3104                 intro[0] = '\0';
3105         }
3106
3107         if (!strcmp(fmt, "wav49"))
3108                 fmt = "WAV";
3109         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
3110
3111         /* Make a temporary file instead of piping directly to sendmail, in case the mail
3112            command hangs. */
3113         if (!(p = vm_mkftemp(tmp))) {
3114                 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
3115                 if (tempcopy)
3116                         *(vmu->email) = '\0';
3117                 return -1;
3118         }
3119
3120         if (msgnum < 0 && imapgreetings) {
3121                 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
3122                         ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
3123                         return -1;
3124                 }
3125                 imap_delete_old_greeting(fn, vms);
3126         }
3127         
3128         make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, intro, fmt, duration, 1, chan, NULL, 1, flag);
3129         /* read mail file to memory */          
3130         len = ftell(p);
3131         rewind(p);
3132         if (!(buf = ast_malloc(len + 1))) {
3133                 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
3134                 fclose(p);
3135                 if (tempcopy)
3136                         *(vmu->email) = '\0';
3137                 return -1;
3138         }
3139         fread(buf, len, 1, p);
3140         ((char *)buf)[len] = '\0';
3141         INIT(&str, mail_string, buf, len);
3142         ret = init_mailstream(vms, NEW_FOLDER);
3143         if (ret == 0) {
3144                 imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
3145                 if(!mail_append_full(vms->mailstream, mailbox, imapflags, NIL, &str))
3146                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3147                 fclose(p);
3148                 unlink(tmp);
3149                 ast_free(buf);
3150         } else {
3151                 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
3152                 fclose(p);
3153                 unlink(tmp);
3154                 ast_free(buf);
3155                 return -1;
3156         }
3157         ast_debug(3, "%s stored\n", fn);
3158         
3159         if (tempcopy)
3160                 *(vmu->email) = '\0';
3161         
3162         return 0;
3163
3164 }
3165
3166 /*!
3167  * \brief Gets the number of messages that exist in a mailbox folder.
3168  * \param context
3169  * \param mailbox
3170  * \param folder
3171  * 
3172  * This method is used when IMAP backend is used.
3173  * \return The number of messages in this mailbox folder (zero or more).
3174  */
3175 static int messagecount(const char *context, const char *mailbox, const char *folder)
3176 {
3177         SEARCHPGM *pgm;
3178         SEARCHHEADER *hdr;
3179
3180         struct ast_vm_user *vmu, vmus;
3181         struct vm_state *vms_p;
3182         int ret = 0;
3183         int fold = folder_int(folder);
3184         int urgent = 0;
3185         
3186         if (ast_strlen_zero(mailbox))
3187                 return 0;
3188
3189         /* We have to get the user before we can open the stream! */
3190         vmu = find_user(&vmus, context, mailbox);
3191         if (!vmu) {
3192                 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
3193                 return -1;
3194         } else {
3195                 /* No IMAP account available */
3196                 if (vmu->imapuser[0] == '\0') {
3197                         ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
3198                         return -1;
3199                 }
3200         }
3201         
3202         /* No IMAP account available */
3203         if (vmu->imapuser[0] == '\0') {
3204                 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
3205                 free_user(vmu);
3206                 return -1;
3207         }
3208
3209         /* check if someone is accessing this box right now... */
3210         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
3211         if (!vms_p) {
3212                 vms_p = get_vm_state_by_mailbox(mailbox,1);
3213         }
3214         if (vms_p) {
3215                 ast_debug(3, "Returning before search - user is logged in\n");
3216                 if (fold == 0) { /* INBOX */
3217                         return vms_p->newmessages;
3218                 }
3219                 if (fold == 1) { /* Old messages */
3220                         return vms_p->oldmessages;
3221                 }
3222                 if (fold == 11) {/*Urgent messages*/
3223                         return vms_p->urgentmessages;
3224                 }
3225         }
3226
3227         /* add one if not there... */
3228         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
3229         if (!vms_p) {
3230                 vms_p = get_vm_state_by_mailbox(mailbox,0);
3231         }
3232
3233         /* If URGENT, then look at INBOX */
3234         if (fold == 11) {
3235                 fold = NEW_FOLDER;
3236                 urgent = 1;
3237         }
3238
3239         if (!vms_p) {
3240                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
3241                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
3242                         return -1;
3243                 }
3244                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
3245                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3246                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
3247                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
3248                 vms_p->updated = 1;
3249                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
3250                 init_vm_state(vms_p);
3251                 vmstate_insert(vms_p);
3252         }
3253         ret = init_mailstream(vms_p, fold);
3254         if (!vms_p->mailstream) {
3255                 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
3256                 return -1;
3257         }
3258         if (ret == 0) {
3259                 pgm = mail_newsearchpgm ();
3260                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
3261                 pgm->header = hdr;
3262                 if (fold != 1) {
3263                         pgm->unseen = 1;
3264                         pgm->seen = 0;
3265                 }
3266                 /* In the special case where fold is 1 (old messages) we have to do things a bit
3267                  * differently. Old messages are stored in the INBOX but are marked as "seen"
3268                  */
3269                 else {
3270                         pgm->unseen = 0;
3271                         pgm->seen = 1;
3272                 }
3273                 /* look for urgent messages */
3274                 if (urgent == 1) {
3275                         pgm->flagged = 1;
3276                         pgm->unflagged = 0;
3277                 }
3278                 pgm->undeleted = 1;
3279                 pgm->deleted = 0;
3280
3281                 vms_p->vmArrayIndex = 0;
3282                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
3283                 if (fold == 0 && urgent == 0)
3284                         vms_p->newmessages = vms_p->vmArrayIndex;
3285                 if (fold == 1)
3286                         vms_p->oldmessages = vms_p->vmArrayIndex;
3287                 if (fold == 0 && urgent == 1)
3288                         vms_p->urgentmessages = vms_p->vmArrayIndex;
3289                 /*Freeing the searchpgm also frees the searchhdr*/
3290                 mail_free_searchpgm(&pgm);
3291                 vms_p->updated = 0;
3292                 return vms_p->vmArrayIndex;
3293         } else {  
3294                 mail_ping(vms_p->mailstream);
3295         }
3296         return 0;
3297 }
3298
3299 /*!
3300  * \brief Gets the number of messages that exist in the inbox folder.
3301  * \param mailbox_context
3302  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3303  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3304  * 
3305  * This method is used when IMAP backend is used.
3306  * Simultaneously determines the count of new and old messages. The total messages would then be the sum of these two.
3307  *
3308  * \return zero on success, -1 on error.
3309  */
3310 static int inboxcount(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3311 {
3312         char tmp[PATH_MAX] = "";
3313         char *mailboxnc;
3314         char *context;
3315         char *mb;
3316         char *cur;
3317         if (newmsgs)
3318                 *newmsgs = 0;
3319         if (oldmsgs)
3320                 *oldmsgs = 0;
3321         if (urgentmsgs)
3322                 *urgentmsgs = 0;
3323
3324         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
3325         /* If no mailbox, return immediately */
3326         if (ast_strlen_zero(mailbox_context))
3327                 return 0;
3328         
3329         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3330         context = strchr(tmp, '@');
3331         if (strchr(mailbox_context, ',')) {
3332                 int tmpnew, tmpold, tmpurgent;
3333                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3334                 mb = tmp;
3335                 while ((cur = strsep(&mb, ", "))) {
3336                         if (!ast_strlen_zero(cur)) {
3337                                 if (inboxcount(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3338                                         return -1;
3339                                 else {
3340                                         if (newmsgs)
3341                                                 *newmsgs += tmpnew; 
3342                                         if (oldmsgs)
3343                                                 *oldmsgs += tmpold;
3344                                         if (urgentmsgs)
3345                                                 *urgentmsgs += tmpurgent;
3346                                 }
3347                         }
3348                 }
3349                 return 0;
3350         }
3351         if (context) {
3352                 *context = '\0';
3353                 mailboxnc = tmp;
3354                 context++;
3355         } else {
3356                 context = "default";
3357                 mailboxnc = (char *)mailbox_context;
3358         }
3359         if (newmsgs) {
3360                 if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
3361                         return -1;
3362         }
3363         if (oldmsgs) {
3364                 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
3365                         return -1;
3366         }
3367         if (urgentmsgs) {
3368                 if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
3369                         return -1;
3370         }
3371         return 0;
3372 }
3373         
3374
3375 /** 
3376  * \brief Determines if the given folder has messages.
3377  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3378  * \param folder the folder to look in
3379  *
3380  * This function is used when the mailbox is stored in an IMAP back end.
3381  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3382  * \return 1 if the folder has one or more messages. zero otherwise.
3383  */
3384 static int has_voicemail(const char *mailbox, const char *folder)
3385 {
3386         char tmp[256], *tmp2, *mbox, *context;
3387         ast_copy_string(tmp, mailbox, sizeof(tmp));
3388         tmp2 = tmp;
3389         if (strchr(tmp2, ',')) {
3390                 while ((mbox = strsep(&tmp2, ","))) {
3391                         if (!ast_strlen_zero(mbox)) {
3392                                 if (has_voicemail(mbox, folder))
3393                                         return 1;
3394                         }
3395                 }
3396         }
3397         if ((context= strchr(tmp, '@')))
3398                 *context++ = '\0';
3399         else
3400                 context = "default";
3401         return messagecount(context, tmp, folder) ? 1 : 0;
3402 }
3403
3404 /*!
3405  * \brief Copies a message from one mailbox to another.
3406  * \param chan
3407  * \param vmu
3408  * \param imbox
3409  * \param msgnum
3410  * \param duration
3411  * \param recip
3412  * \param fmt
3413  * \param dir
3414  *
3415  * This works with IMAP storage based mailboxes.
3416  *
3417  * \return zero on success, -1 on error.
3418  */
3419 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
3420 {
3421         struct vm_state *sendvms = NULL, *destvms = NULL;
3422         char messagestring[10]; /* I guess this could be a problem if someone has more than 999,999,999 messages... */
3423         if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3424                 ast_log(AST_LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3425                 return -1;
3426         }
3427         if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
3428                 ast_log(AST_LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3429                 return -1;
3430         }
3431         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3432         if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
3433                 return 0;
3434         ast_log(AST_LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3435         return -1;
3436 }
3437
3438 #endif
3439 #ifndef IMAP_STORAGE
3440 /*! 
3441  * \brief Copies a message from one mailbox to another.
3442  * \param chan
3443  * \param vmu
3444  * \param imbox
3445  * \param msgnum
3446  * \param duration
3447  * \param recip
3448  * \param fmt
3449  * \param dir
3450  *
3451  * This is only used by file storage based mailboxes.
3452  *
3453  * \return zero on success, -1 on error.
3454  */
3455 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
3456 {
3457         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
3458         const char *frombox = mbox(imbox);
3459         int recipmsgnum;
3460
3461         ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
3462
3463         if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
3464                 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
3465         } else {
3466                 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3467         }
3468         
3469         if (!dir)
3470                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
3471         else
3472                 ast_copy_string(fromdir, dir, sizeof(fromdir));
3473
3474         make_file(frompath, sizeof(frompath), fromdir, msgnum);
3475         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3476
3477         if (vm_lock_path(todir))
3478                 return ERROR_LOCK_PATH;
3479
3480         recipmsgnum = last_message_index(recip, todir) + 1;
3481         if (recipmsgnum < recip->maxmsg) {
3482                 make_file(topath, sizeof(topath), todir, recipmsgnum);
3483                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
3484         } else {
3485                 ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
3486         }
3487         ast_unlock_path(todir);
3488         notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
3489         
3490         return 0;
3491 }
3492 #endif
3493 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
3494
3495 static int messagecount(const char *context, const char *mailbox, const char *folder)
3496 {
3497         return __has_voicemail(context, mailbox, folder, 0);
3498 }
3499
3500 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
3501 {
3502         DIR *dir;
3503         struct dirent *de;
3504         char fn[256];
3505         int ret = 0;
3506
3507         /* If no mailbox, return immediately */
3508         if (ast_strlen_zero(mailbox))
3509                 return 0;
3510
3511         if (ast_strlen_zero(folder))
3512                 folder = "INBOX";
3513         if (ast_strlen_zero(context))
3514                 context = "default";
3515
3516         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
3517
3518         if (!(dir = opendir(fn)))
3519                 return 0;
3520
3521         while ((de = readdir(dir))) {
3522                 if (!strncasecmp(de->d_name, "msg", 3)) {
3523                         if (shortcircuit) {
3524                                 ret = 1;
3525                                 break;
3526                         } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
3527                                 if (shortcircuit) return 1;
3528                                 ret++;
3529                         }
3530                 }
3531         }
3532
3533         closedir(dir);
3534
3535         /* If we are checking INBOX, we should check Urgent as well */
3536         if (strcmp(folder, "INBOX") == 0) {
3537                 return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
3538         } else {
3539                 return ret;
3540         }
3541 }
3542
3543 /** 
3544  * \brief Determines if the given folder has messages.
3545  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
3546  * \param folder the folder to look in
3547  *
3548  * This function is used when the mailbox is stored in a filesystem back end.
3549  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3550  * \return 1 if the folder has one or more messages. zero otherwise.
3551  */
3552 static int has_voicemail(const char *mailbox, const char *folder)
3553 {
3554         char tmp[256], *tmp2 = tmp, *mbox, *context;
3555         ast_copy_string(tmp, mailbox, sizeof(tmp));
3556         while ((mbox = strsep(&tmp2, ","))) {
3557                 if ((context = strchr(mbox, '@')))
3558                         *context++ = '\0';
3559                 else
3560                         context = "default";
3561                 if (__has_voicemail(context, mbox, folder, 1))
3562                         return 1;
3563         }
3564         return 0;
3565 }
3566
3567
3568 static int inboxcount(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3569 {
3570         char tmp[256];
3571         char *context;
3572
3573         /* If no mailbox, return immediately */
3574         if (ast_strlen_zero(mailbox))
3575                 return 0;
3576
3577         if (newmsgs)
3578                 *newmsgs = 0;
3579         if (oldmsgs)
3580                 *oldmsgs = 0;
3581         if (urgentmsgs)
3582                 *urgentmsgs = 0;
3583