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