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