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