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