4359ee7e65493f8c5c26570333efff3fd83d87e7
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  *
25  * \extref Unixodbc - http://www.unixodbc.org
26  * \extref A source distribution of University of Washington's IMAP
27 c-client (http://www.washington.edu/imap/
28  * 
29  * \par See also
30  * \arg \ref Config_vm
31  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32  * \ingroup applications
33  * \note This module requires res_adsi to load. This needs to be optional
34  * during compilation.
35  *
36  *
37  *
38  * \note  This file is now almost impossible to work with, due to all \#ifdefs.
39  *        Feels like the database code before realtime. Someone - please come up
40  *        with a plan to clean this up.
41  */
42
43 /*** MODULEINFO
44         <depend>res_smdi</depend>
45  ***/
46
47 /*** MAKEOPTS
48 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
49         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
50                 <depend>unixodbc</depend>
51                 <depend>ltdl</depend>
52                 <conflict>IMAP_STORAGE</conflict>
53                 <defaultenabled>no</defaultenabled>
54         </member>
55         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
56                 <depend>imap_tk</depend>
57                 <conflict>ODBC_STORAGE</conflict>
58                 <use>ssl</use>
59                 <defaultenabled>no</defaultenabled>
60         </member>
61 </category>
62  ***/
63
64 #include "asterisk.h"
65
66 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
67
68 #include <stdlib.h>
69 #include <errno.h>
70 #include <unistd.h>
71 #include <string.h>
72 #include <stdio.h>
73 #include <sys/time.h>
74 #include <sys/stat.h>
75 #include <sys/types.h>
76 #include <sys/mman.h>
77 #include <time.h>
78 #include <dirent.h>
79
80 #ifdef IMAP_STORAGE
81 #include <ctype.h>
82 #include <signal.h>
83 #include <pwd.h>
84 #ifdef USE_SYSTEM_IMAP
85 #include <imap/c-client.h>
86 #include <imap/imap4r1.h>
87 #include <imap/linkage.h>
88 #else
89 #include "c-client.h"
90 #include "imap4r1.h"
91 #include "linkage.h"
92 #endif
93 #endif
94
95 #include "asterisk/lock.h"
96 #include "asterisk/file.h"
97 #include "asterisk/logger.h"
98 #include "asterisk/channel.h"
99 #include "asterisk/pbx.h"
100 #include "asterisk/options.h"
101 #include "asterisk/config.h"
102 #include "asterisk/say.h"
103 #include "asterisk/module.h"
104 #include "asterisk/adsi.h"
105 #include "asterisk/app.h"
106 #include "asterisk/manager.h"
107 #include "asterisk/dsp.h"
108 #include "asterisk/localtime.h"
109 #include "asterisk/cli.h"
110 #include "asterisk/utils.h"
111 #include "asterisk/stringfields.h"
112 #include "asterisk/smdi.h"
113 #include "asterisk/event.h"
114
115 #ifdef ODBC_STORAGE
116 #include "asterisk/res_odbc.h"
117 #endif
118
119 #ifdef IMAP_STORAGE
120 static char imapserver[48];
121 static char imapport[8];
122 static char imapflags[128];
123 static char imapfolder[64];
124 static char greetingfolder[64];
125 static char authuser[32];
126 static char authpassword[42];
127
128 static int expungeonhangup = 1;
129 static int imapgreetings = 0;
130 static char delimiter = '\0';
131
132 struct vm_state;
133 struct ast_vm_user;
134
135 /* Forward declarations for IMAP */
136 static int init_mailstream(struct vm_state *vms, int box);
137 static void write_file(char *filename, char *buffer, unsigned long len);
138 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
139 static void vm_imap_delete(int msgnum, struct vm_state *vms);
140 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
141 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
142 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
143 static void vmstate_insert(struct vm_state *vms);
144 static void vmstate_delete(struct vm_state *vms);
145 static void set_update(MAILSTREAM * stream);
146 static void init_vm_state(struct vm_state *vms);
147 static void check_msgArray(struct vm_state *vms);
148 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
149 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
150 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num);
151 static void get_mailbox_delimiter(MAILSTREAM *stream);
152 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
153 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
154 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);
155 static void update_messages_by_imapuser(const char *user, unsigned long number);
156
157 static int imap_remove_file (char *dir, int msgnum);
158 static int imap_retrieve_file (char *dir, int msgnum, char *mailbox, char *context);
159 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
160 struct vmstate {
161         struct vm_state *vms;
162         AST_LIST_ENTRY(vmstate) list;
163 };
164
165 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
166
167 #endif
168
169 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
170
171 #define COMMAND_TIMEOUT 5000
172 /* Don't modify these here; set your umask at runtime instead */
173 #define VOICEMAIL_DIR_MODE      0777
174 #define VOICEMAIL_FILE_MODE     0666
175 #define CHUNKSIZE       65536
176
177 #define VOICEMAIL_CONFIG "voicemail.conf"
178 #define ASTERISK_USERNAME "asterisk"
179
180 /* Define fast-forward, pause, restart, and reverse keys
181    while listening to a voicemail message - these are
182    strings, not characters */
183 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
184 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
185 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
186 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
187 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
188 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
189
190 /* Default mail command to mail voicemail. Change it with the
191     mailcmd= command in voicemail.conf */
192 #define SENDMAIL "/usr/sbin/sendmail -t"
193
194 #define INTRO "vm-intro"
195
196 #define MAXMSG 100
197 #define MAXMSGLIMIT 9999
198
199 #define BASEMAXINLINE 256
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                                         
924         if (!change_password_realtime(vmu, newpassword))
925                 return;
926
927         /* check voicemail.conf */
928         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
929                 while ((category = ast_category_browse(cfg, category))) {
930                         if (!strcasecmp(category, vmu->context)) {
931                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
932                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
933                                         break;
934                                 }
935                                 value = strstr(tmp,",");
936                                 if (!value) {
937                                         ast_log(LOG_WARNING, "variable has bad format.\n");
938                                         break;
939                                 }
940                                 new = alloca((strlen(value)+strlen(newpassword)+1));
941                                 sprintf(new,"%s%s", newpassword, value);
942                                 if (!(cat = ast_category_get(cfg, category))) {
943                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
944                                         break;
945                                 }
946                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
947                         }
948                 }
949                 /* save the results */
950                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
951                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
952                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
953         }
954         category = NULL;
955         var = NULL;
956         /* check users.conf and update the password stored for the mailbox*/
957         /* if no vmsecret entry exists create one. */
958         if ((cfg = ast_config_load("users.conf", config_flags))) {
959                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
960                 while ((category = ast_category_browse(cfg, category))) {
961                         ast_debug(4, "users.conf: %s\n", category);
962                         if (!strcasecmp(category, vmu->mailbox)) {
963                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
964                                         ast_debug(3, "looks like we need to make vmsecret!\n");
965                                         var = ast_variable_new("vmsecret", newpassword, "");
966                                 } 
967                                 new = alloca(strlen(newpassword)+1);
968                                 sprintf(new, "%s", newpassword);
969                                 if (!(cat = ast_category_get(cfg, category))) {
970                                         ast_debug(4, "failed to get category!\n");
971                                         break;
972                                 }
973                                 if (!var)               
974                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
975                                 else
976                                         ast_variable_append(cat, var);
977                         }
978                 }
979                 /* save the results and clean things up */
980                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
981                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
982                 config_text_file_save("users.conf", cfg, "AppVoicemail");
983         }
984 }
985
986 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
987 {
988         char buf[255];
989         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
990         if (!ast_safe_system(buf))
991                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
992 }
993
994 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
995 {
996         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
997 }
998
999 #ifdef IMAP_STORAGE
1000 static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num)
1001 {
1002         int res;
1003         if ((res = ast_mkdir(dir, 01777))) {
1004                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1005                 return snprintf(dest, len, "%s/msg%04d", dir, num);
1006         }
1007         return snprintf(dest, len, "%s/msg%04d", dir, num);
1008 }
1009
1010 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1011 {
1012         unsigned long messageNum = 0;
1013         char arg[10];
1014
1015         /* find real message number based on msgnum */
1016         /* this may be an index into vms->msgArray based on the msgnum. */
1017
1018         messageNum = vms->msgArray[msgnum];
1019         if (messageNum == 0) {
1020                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1021                 return;
1022         }
1023         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1024         /* delete message */
1025         snprintf (arg, sizeof(arg), "%lu",messageNum);
1026         mail_setflag (vms->mailstream,arg,"\\DELETED");
1027 }
1028
1029 #endif
1030 static int make_file(char *dest, int len, char *dir, int num)
1031 {
1032         return snprintf(dest, len, "%s/msg%04d", dir, num);
1033 }
1034
1035 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1036  * \param dest    String. base directory.
1037  * \param len     Length of dest.
1038  * \param context String. Ignored if is null or empty string.
1039  * \param ext     String. Ignored if is null or empty string.
1040  * \param folder  String. Ignored if is null or empty string. 
1041  * \return -1 on failure, 0 on success.
1042  */
1043 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1044 {
1045         mode_t  mode = VOICEMAIL_DIR_MODE;
1046         int res;
1047
1048         make_dir(dest, len, context, ext, folder);
1049         if ((res = ast_mkdir(dest, mode))) {
1050                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1051                 return -1;
1052         }
1053         return 0;
1054 }
1055
1056 /*! \brief Lock file path
1057     only return failure if ast_lock_path returns 'timeout',
1058    not if the path does not exist or any other reason
1059 */
1060 static int vm_lock_path(const char *path)
1061 {
1062         switch (ast_lock_path(path)) {
1063         case AST_LOCK_TIMEOUT:
1064                 return -1;
1065         default:
1066                 return 0;
1067         }
1068 }
1069
1070
1071 #ifdef ODBC_STORAGE
1072 struct generic_prepare_struct {
1073         char *sql;
1074         int argc;
1075         char **argv;
1076 };
1077
1078 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1079 {
1080         struct generic_prepare_struct *gps = data;
1081         int res, i;
1082         SQLHSTMT stmt;
1083
1084         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1085         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1086                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1087                 return NULL;
1088         }
1089         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1090         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1091                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1092                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1093                 return NULL;
1094         }
1095         for (i = 0; i < gps->argc; i++)
1096                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1097
1098         return stmt;
1099 }
1100
1101 static int retrieve_file(char *dir, int msgnum)
1102 {
1103         int x = 0;
1104         int res;
1105         int fd=-1;
1106         size_t fdlen = 0;
1107         void *fdm = MAP_FAILED;
1108         SQLSMALLINT colcount=0;
1109         SQLHSTMT stmt;
1110         char sql[PATH_MAX];
1111         char fmt[80]="";
1112         char *c;
1113         char coltitle[256];
1114         SQLSMALLINT collen;
1115         SQLSMALLINT datatype;
1116         SQLSMALLINT decimaldigits;
1117         SQLSMALLINT nullable;
1118         SQLULEN colsize;
1119         SQLLEN colsize2;
1120         FILE *f=NULL;
1121         char rowdata[80];
1122         char fn[PATH_MAX];
1123         char full_fn[PATH_MAX];
1124         char msgnums[80];
1125         char *argv[] = { dir, msgnums };
1126         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1127
1128         struct odbc_obj *obj;
1129         obj = ast_odbc_request_obj(odbc_database, 0);
1130         if (obj) {
1131                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1132                 c = strchr(fmt, '|');
1133                 if (c)
1134                         *c = '\0';
1135                 if (!strcasecmp(fmt, "wav49"))
1136                         strcpy(fmt, "WAV");
1137                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1138                 if (msgnum > -1)
1139                         make_file(fn, sizeof(fn), dir, msgnum);
1140                 else
1141                         ast_copy_string(fn, dir, sizeof(fn));
1142                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1143                 
1144                 if (!(f = fopen(full_fn, "w+"))) {
1145                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1146                         goto yuck;
1147                 }
1148                 
1149                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1150                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1151                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1152                 if (!stmt) {
1153                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1154                         ast_odbc_release_obj(obj);
1155                         goto yuck;
1156                 }
1157                 res = SQLFetch(stmt);
1158                 if (res == SQL_NO_DATA) {
1159                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1160                         ast_odbc_release_obj(obj);
1161                         goto yuck;
1162                 }
1163                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1164                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1165                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1166                         ast_odbc_release_obj(obj);
1167                         goto yuck;
1168                 }
1169                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1170                 if (fd < 0) {
1171                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1172                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1173                         ast_odbc_release_obj(obj);
1174                         goto yuck;
1175                 }
1176                 res = SQLNumResultCols(stmt, &colcount);
1177                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1178                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1179                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1180                         ast_odbc_release_obj(obj);
1181                         goto yuck;
1182                 }
1183                 if (f) 
1184                         fprintf(f, "[message]\n");
1185                 for (x=0;x<colcount;x++) {
1186                         rowdata[0] = '\0';
1187                         collen = sizeof(coltitle);
1188                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1189                                                 &datatype, &colsize, &decimaldigits, &nullable);
1190                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1192                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193                                 ast_odbc_release_obj(obj);
1194                                 goto yuck;
1195                         }
1196                         if (!strcasecmp(coltitle, "recording")) {
1197                                 off_t offset;
1198                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1199                                 fdlen = colsize2;
1200                                 if (fd > -1) {
1201                                         char tmp[1]="";
1202                                         lseek(fd, fdlen - 1, SEEK_SET);
1203                                         if (write(fd, tmp, 1) != 1) {
1204                                                 close(fd);
1205                                                 fd = -1;
1206                                                 continue;
1207                                         }
1208                                         /* Read out in small chunks */
1209                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1210                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1211                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1212                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1213                                                         ast_odbc_release_obj(obj);
1214                                                         goto yuck;
1215                                                 } else {
1216                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1217                                                         munmap(fdm, CHUNKSIZE);
1218                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1219                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1220                                                                 unlink(full_fn);
1221                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1222                                                                 ast_odbc_release_obj(obj);
1223                                                                 goto yuck;
1224                                                         }
1225                                                 }
1226                                         }
1227                                         truncate(full_fn, fdlen);
1228                                 }
1229                         } else {
1230                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1231                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1232                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1233                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1234                                         ast_odbc_release_obj(obj);
1235                                         goto yuck;
1236                                 }
1237                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1238                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1239                         }
1240                 }
1241                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1242                 ast_odbc_release_obj(obj);
1243         } else
1244                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1245 yuck:   
1246         if (f)
1247                 fclose(f);
1248         if (fd > -1)
1249                 close(fd);
1250         return x - 1;
1251 }
1252
1253 static int remove_file(char *dir, int msgnum)
1254 {
1255         char fn[PATH_MAX];
1256         char full_fn[PATH_MAX];
1257         char msgnums[80];
1258         
1259         if (msgnum > -1) {
1260                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1261                 make_file(fn, sizeof(fn), dir, msgnum);
1262         } else
1263                 ast_copy_string(fn, dir, sizeof(fn));
1264         ast_filedelete(fn, NULL);       
1265         if (ast_check_realtime("voicemail_data")) {
1266                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1267         }
1268         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1269         unlink(full_fn);
1270         return 0;
1271 }
1272
1273 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1274 {
1275         int x = 0;
1276         int res;
1277         SQLHSTMT stmt;
1278         char sql[PATH_MAX];
1279         char rowdata[20];
1280         char *argv[] = { dir };
1281         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1282
1283         struct odbc_obj *obj;
1284         obj = ast_odbc_request_obj(odbc_database, 0);
1285         if (obj) {
1286                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1287                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1288                 if (!stmt) {
1289                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1290                         ast_odbc_release_obj(obj);
1291                         goto yuck;
1292                 }
1293                 res = SQLFetch(stmt);
1294                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1295                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1296                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1297                         ast_odbc_release_obj(obj);
1298                         goto yuck;
1299                 }
1300                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1301                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1302                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1303                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1304                         ast_odbc_release_obj(obj);
1305                         goto yuck;
1306                 }
1307                 if (sscanf(rowdata, "%d", &x) != 1)
1308                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1309                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1310                 ast_odbc_release_obj(obj);
1311         } else
1312                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1313 yuck:   
1314         return x - 1;
1315 }
1316
1317 static int message_exists(char *dir, int msgnum)
1318 {
1319         int x = 0;
1320         int res;
1321         SQLHSTMT stmt;
1322         char sql[PATH_MAX];
1323         char rowdata[20];
1324         char msgnums[20];
1325         char *argv[] = { dir, msgnums };
1326         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1327
1328         struct odbc_obj *obj;
1329         obj = ast_odbc_request_obj(odbc_database, 0);
1330         if (obj) {
1331                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1332                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1333                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1334                 if (!stmt) {
1335                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1336                         ast_odbc_release_obj(obj);
1337                         goto yuck;
1338                 }
1339                 res = SQLFetch(stmt);
1340                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1341                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1342                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1343                         ast_odbc_release_obj(obj);
1344                         goto yuck;
1345                 }
1346                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1347                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1348                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1349                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1350                         ast_odbc_release_obj(obj);
1351                         goto yuck;
1352                 }
1353                 if (sscanf(rowdata, "%d", &x) != 1)
1354                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1355                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356                 ast_odbc_release_obj(obj);
1357         } else
1358                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1359 yuck:   
1360         return x;
1361 }
1362
1363 static int count_messages(struct ast_vm_user *vmu, char *dir)
1364 {
1365         return last_message_index(vmu, dir) + 1;
1366 }
1367
1368 static void delete_file(char *sdir, int smsg)
1369 {
1370         SQLHSTMT stmt;
1371         char sql[PATH_MAX];
1372         char msgnums[20];
1373         char *argv[] = { sdir, msgnums };
1374         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1375
1376         struct odbc_obj *obj;
1377         obj = ast_odbc_request_obj(odbc_database, 0);
1378         if (obj) {
1379                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1380                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1381                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1382                 if (!stmt)
1383                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1384                 else
1385                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1386                 ast_odbc_release_obj(obj);
1387         } else
1388                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1389         return; 
1390 }
1391
1392 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1393 {
1394         SQLHSTMT stmt;
1395         char sql[512];
1396         char msgnums[20];
1397         char msgnumd[20];
1398         struct odbc_obj *obj;
1399         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1400         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1401
1402         delete_file(ddir, dmsg);
1403         obj = ast_odbc_request_obj(odbc_database, 0);
1404         if (obj) {
1405                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1406                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1407                 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
1408                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1409                 if (!stmt)
1410                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1411                 else
1412                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1413                 ast_odbc_release_obj(obj);
1414         } else
1415                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1416         return; 
1417 }
1418
1419 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1420 {
1421         int x = 0;
1422         int res;
1423         int fd = -1;
1424         void *fdm = MAP_FAILED;
1425         size_t fdlen = -1;
1426         SQLHSTMT stmt;
1427         SQLLEN len;
1428         char sql[PATH_MAX];
1429         char msgnums[20];
1430         char fn[PATH_MAX];
1431         char full_fn[PATH_MAX];
1432         char fmt[80]="";
1433         char *c;
1434         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1435         const char *category = "";
1436         struct ast_config *cfg=NULL;
1437         struct odbc_obj *obj;
1438         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1439
1440         delete_file(dir, msgnum);
1441         obj = ast_odbc_request_obj(odbc_database, 0);
1442         if (obj) {
1443                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1444                 c = strchr(fmt, '|');
1445                 if (c)
1446                         *c = '\0';
1447                 if (!strcasecmp(fmt, "wav49"))
1448                         strcpy(fmt, "WAV");
1449                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1450                 if (msgnum > -1)
1451                         make_file(fn, sizeof(fn), dir, msgnum);
1452                 else
1453                         ast_copy_string(fn, dir, sizeof(fn));
1454                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1455                 cfg = ast_config_load(full_fn, config_flags);
1456                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1457                 fd = open(full_fn, O_RDWR);
1458                 if (fd < 0) {
1459                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1460                         ast_odbc_release_obj(obj);
1461                         goto yuck;
1462                 }
1463                 if (cfg) {
1464                         context = ast_variable_retrieve(cfg, "message", "context");
1465                         if (!context) context = "";
1466                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1467                         if (!macrocontext) macrocontext = "";
1468                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1469                         if (!callerid) callerid = "";
1470                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1471                         if (!origtime) origtime = "";
1472                         duration = ast_variable_retrieve(cfg, "message", "duration");
1473                         if (!duration) duration = "";
1474                         category = ast_variable_retrieve(cfg, "message", "category");
1475                         if (!category) category = "";
1476                 }
1477                 fdlen = lseek(fd, 0, SEEK_END);
1478                 lseek(fd, 0, SEEK_SET);
1479                 printf("Length is %zd\n", fdlen);
1480                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1481                 if (fdm == MAP_FAILED) {
1482                         ast_log(LOG_WARNING, "Memory map failed!\n");
1483                         ast_odbc_release_obj(obj);
1484                         goto yuck;
1485                 } 
1486                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1487                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1488                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1489                         ast_odbc_release_obj(obj);
1490                         goto yuck;
1491                 }
1492                 if (!ast_strlen_zero(category)) 
1493                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1494                 else
1495                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1496                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1497                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1498                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1499                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1500                         ast_odbc_release_obj(obj);
1501                         goto yuck;
1502                 }
1503                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1504                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1505                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1506                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1507                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1508                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1509                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1510                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1511                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1512                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1513                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1514                 if (!ast_strlen_zero(category))
1515                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1516                 res = ast_odbc_smart_execute(obj, stmt);
1517                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1518                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1519                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1520                         ast_odbc_release_obj(obj);
1521                         goto yuck;
1522                 }
1523                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1524                 ast_odbc_release_obj(obj);
1525         } else
1526                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1527 yuck:   
1528         if (cfg)
1529                 ast_config_destroy(cfg);
1530         if (fdm != MAP_FAILED)
1531                 munmap(fdm, fdlen);
1532         if (fd > -1)
1533                 close(fd);
1534         return x;
1535 }
1536
1537 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1538 {
1539         SQLHSTMT stmt;
1540         char sql[PATH_MAX];
1541         char msgnums[20];
1542         char msgnumd[20];
1543         struct odbc_obj *obj;
1544         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1545         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1546
1547         delete_file(ddir, dmsg);
1548         obj = ast_odbc_request_obj(odbc_database, 0);
1549         if (obj) {
1550                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1551                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1552                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1553                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1554                 if (!stmt)
1555                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1556                 else
1557                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1558                 ast_odbc_release_obj(obj);
1559         } else
1560                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1561         return; 
1562 }
1563
1564 #else
1565 #ifndef IMAP_STORAGE
1566 static int count_messages(struct ast_vm_user *vmu, char *dir)
1567 {
1568         /* Find all .txt files - even if they are not in sequence from 0000 */
1569
1570         int vmcount = 0;
1571         DIR *vmdir = NULL;
1572         struct dirent *vment = NULL;
1573
1574         if (vm_lock_path(dir))
1575                 return ERROR_LOCK_PATH;
1576
1577         if ((vmdir = opendir(dir))) {
1578                 while ((vment = readdir(vmdir))) {
1579                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1580                                 vmcount++;
1581                 }
1582                 closedir(vmdir);
1583         }
1584         ast_unlock_path(dir);
1585         
1586         return vmcount;
1587 }
1588
1589 static void rename_file(char *sfn, char *dfn)
1590 {
1591         char stxt[PATH_MAX];
1592         char dtxt[PATH_MAX];
1593         ast_filerename(sfn,dfn,NULL);
1594         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1595         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1596         if (ast_check_realtime("voicemail_data")) {
1597                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1598         }
1599         rename(stxt, dtxt);
1600 }
1601
1602 static int copy(char *infile, char *outfile)
1603 {
1604         int ifd;
1605         int ofd;
1606         int res;
1607         int len;
1608         char buf[4096];
1609
1610 #ifdef HARDLINK_WHEN_POSSIBLE
1611         /* Hard link if possible; saves disk space & is faster */
1612         if (link(infile, outfile)) {
1613 #endif
1614                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1615                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1616                         return -1;
1617                 }
1618                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1619                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1620                         close(ifd);
1621                         return -1;
1622                 }
1623                 do {
1624                         len = read(ifd, buf, sizeof(buf));
1625                         if (len < 0) {
1626                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1627                                 close(ifd);
1628                                 close(ofd);
1629                                 unlink(outfile);
1630                         }
1631                         if (len) {
1632                                 res = write(ofd, buf, len);
1633                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1634                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1635                                         close(ifd);
1636                                         close(ofd);
1637                                         unlink(outfile);
1638                                 }
1639                         }
1640                 } while (len);
1641                 close(ifd);
1642                 close(ofd);
1643                 return 0;
1644 #ifdef HARDLINK_WHEN_POSSIBLE
1645         } else {
1646                 /* Hard link succeeded */
1647                 return 0;
1648         }
1649 #endif
1650 }
1651
1652 static void copy_file(char *frompath, char *topath)
1653 {
1654         char frompath2[PATH_MAX], topath2[PATH_MAX];
1655         struct ast_variable *tmp,*var = NULL;
1656         char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1657         ast_filecopy(frompath, topath, NULL);
1658         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1659         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1660         if (ast_check_realtime("voicemail_data")) {
1661                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1662                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1663                 for (tmp = var; tmp; tmp = tmp->next) {
1664                         if (!strcasecmp(tmp->name, "origmailbox")) {
1665                                 origmailbox = tmp->value;
1666                         } else if (!strcasecmp(tmp->name, "context")) {
1667                                 context = tmp->value;
1668                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
1669                                 macrocontext = tmp->value;
1670                         } else if (!strcasecmp(tmp->name, "exten")) {
1671                                 exten = tmp->value;
1672                         } else if (!strcasecmp(tmp->name, "priority")) {
1673                                 priority = tmp->value;
1674                         } else if (!strcasecmp(tmp->name, "callerchan")) {
1675                                 callerchan = tmp->value;
1676                         } else if (!strcasecmp(tmp->name, "callerid")) {
1677                                 callerid = tmp->value;
1678                         } else if (!strcasecmp(tmp->name, "origdate")) {
1679                                 origdate = tmp->value;
1680                         } else if (!strcasecmp(tmp->name, "origtime")) {
1681                                 origtime = tmp->value;
1682                         } else if (!strcasecmp(tmp->name, "category")) {
1683                                 category = tmp->value;
1684                         } else if (!strcasecmp(tmp->name, "duration")) {
1685                                 duration = tmp->value;
1686                         }
1687                 }
1688                 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
1689         }
1690         copy(frompath2, topath2);
1691         ast_variables_destroy(var);
1692 }
1693 /*! \brief
1694  * A negative return value indicates an error.
1695  * \note Should always be called with a lock already set on dir.
1696  */
1697 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1698 {
1699         int x;
1700         unsigned char map[MAXMSGLIMIT] = "";
1701         DIR *msgdir;
1702         struct dirent *msgdirent;
1703         int msgdirint;
1704
1705         /* Reading the entire directory into a file map scales better than
1706          * doing a stat repeatedly on a predicted sequence.  I suspect this
1707          * is partially due to stat(2) internally doing a readdir(2) itself to
1708          * find each file. */
1709         msgdir = opendir(dir);
1710         while ((msgdirent = readdir(msgdir))) {
1711                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1712                         map[msgdirint] = 1;
1713         }
1714         closedir(msgdir);
1715
1716         for (x = 0; x < vmu->maxmsg; x++) {
1717                 if (map[x] == 0)
1718                         break;
1719         }
1720
1721         return x - 1;
1722 }
1723
1724 #endif /*#ifndef IMAP_STORAGE*/
1725 #endif /*#else of #ifdef ODBC_STORAGE*/
1726 #ifndef ODBC_STORAGE
1727 static int vm_delete(char *file)
1728 {
1729         char *txt;
1730         int txtsize = 0;
1731
1732         txtsize = (strlen(file) + 5)*sizeof(char);
1733         txt = alloca(txtsize);
1734         /* Sprintf here would safe because we alloca'd exactly the right length,
1735          * but trying to eliminate all sprintf's anyhow
1736          */
1737         if (ast_check_realtime("voicemail_data")) {
1738                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1739         }
1740         snprintf(txt, txtsize, "%s.txt", file);
1741         unlink(txt);
1742         return ast_filedelete(file, NULL);
1743 }
1744 #endif
1745
1746 static int inbuf(struct baseio *bio, FILE *fi)
1747 {
1748         int l;
1749
1750         if (bio->ateof)
1751                 return 0;
1752
1753         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1754                 if (ferror(fi))
1755                         return -1;
1756
1757                 bio->ateof = 1;
1758                 return 0;
1759         }
1760
1761         bio->iolen= l;
1762         bio->iocp= 0;
1763
1764         return 1;
1765 }
1766
1767 static int inchar(struct baseio *bio, FILE *fi)
1768 {
1769         if (bio->iocp>=bio->iolen) {
1770                 if (!inbuf(bio, fi))
1771                         return EOF;
1772         }
1773
1774         return bio->iobuf[bio->iocp++];
1775 }
1776
1777 static int ochar(struct baseio *bio, int c, FILE *so)
1778 {
1779         if (bio->linelength >= BASELINELEN) {
1780                 if (fputs(eol,so) == EOF)
1781                         return -1;
1782
1783                 bio->linelength= 0;
1784         }
1785
1786         if (putc(((unsigned char)c),so) == EOF)
1787                 return -1;
1788
1789         bio->linelength++;
1790
1791         return 1;
1792 }
1793
1794 static int base_encode(char *filename, FILE *so)
1795 {
1796         unsigned char dtable[BASEMAXINLINE];
1797         int i,hiteof= 0;
1798         FILE *fi;
1799         struct baseio bio;
1800
1801         memset(&bio, 0, sizeof(bio));
1802         bio.iocp = BASEMAXINLINE;
1803
1804         if (!(fi = fopen(filename, "rb"))) {
1805                 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1806                 return -1;
1807         }
1808
1809         for (i= 0; i<9; i++) {
1810                 dtable[i]= 'A'+i;
1811                 dtable[i+9]= 'J'+i;
1812                 dtable[26+i]= 'a'+i;
1813                 dtable[26+i+9]= 'j'+i;
1814         }
1815         for (i= 0; i<8; i++) {
1816                 dtable[i+18]= 'S'+i;
1817                 dtable[26+i+18]= 's'+i;
1818         }
1819         for (i= 0; i<10; i++) {
1820                 dtable[52+i]= '0'+i;
1821         }
1822         dtable[62]= '+';
1823         dtable[63]= '/';
1824
1825         while (!hiteof){
1826                 unsigned char igroup[3], ogroup[4];
1827                 int c,n;
1828
1829                 igroup[0]= igroup[1]= igroup[2]= 0;
1830
1831                 for (n= 0;n<3;n++) {
1832                         if ((c = inchar(&bio, fi)) == EOF) {
1833                                 hiteof= 1;
1834                                 break;
1835                         }
1836
1837                         igroup[n]= (unsigned char)c;
1838                 }
1839
1840                 if (n> 0) {
1841                         ogroup[0]= dtable[igroup[0]>>2];
1842                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1843                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1844                         ogroup[3]= dtable[igroup[2]&0x3F];
1845
1846                         if (n<3) {
1847                                 ogroup[3]= '=';
1848
1849                                 if (n<2)
1850                                         ogroup[2]= '=';
1851                         }
1852
1853                         for (i= 0;i<4;i++)
1854                                 ochar(&bio, ogroup[i], so);
1855                 }
1856         }
1857
1858         if (fputs(eol,so) == EOF)
1859                 return 0;
1860
1861         fclose(fi);
1862
1863         return 1;
1864 }
1865
1866 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
1867 {
1868         char callerid[256];
1869         /* Prepare variables for substitution in email body and subject */
1870         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1871         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1872         snprintf(passdata, passdatasize, "%d", msgnum);
1873         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1874         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1875         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1876         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1877         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1878         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1879         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1880         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1881 }
1882
1883 static char *quote(const char *from, char *to, size_t len)
1884 {
1885         char *ptr = to;
1886         *ptr++ = '"';
1887         for (; ptr < to + len - 1; from++) {
1888                 if (*from == '"')
1889                         *ptr++ = '\\';
1890                 else if (*from == '\0')
1891                         break;
1892                 *ptr++ = *from;
1893         }
1894         if (ptr < to + len - 1)
1895                 *ptr++ = '"';
1896         *ptr = '\0';
1897         return to;
1898 }
1899
1900 /*! \brief
1901  * fill in *tm for current time according to the proper timezone, if any.
1902  * Return tm so it can be used as a function argument.
1903  */
1904 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1905 {
1906         const struct vm_zone *z = NULL;
1907         struct timeval t = ast_tvnow();
1908
1909         /* Does this user have a timezone specified? */
1910         if (!ast_strlen_zero(vmu->zonetag)) {
1911                 /* Find the zone in the list */
1912                 AST_LIST_LOCK(&zones);
1913                 AST_LIST_TRAVERSE(&zones, z, list) {
1914                         if (!strcmp(z->name, vmu->zonetag))
1915                                 break;
1916                 }
1917                 AST_LIST_UNLOCK(&zones);
1918         }
1919         ast_localtime(&t, tm, z ? z->timezone : NULL);
1920         return tm;
1921 }
1922
1923 /*! \brief same as mkstemp, but return a FILE * */
1924 static FILE *vm_mkftemp(char *template)
1925 {
1926         FILE *p = NULL;
1927         int pfd = mkstemp(template);
1928         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1929         if (pfd > -1) {
1930                 p = fdopen(pfd, "w+");
1931                 if (!p) {
1932                         close(pfd);
1933                         pfd = -1;
1934                 }
1935         }
1936         return p;
1937 }
1938
1939 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
1940 {
1941         char date[256];
1942         char host[MAXHOSTNAMELEN] = "";
1943         char who[256];
1944         char bound[256];
1945         char fname[256];
1946         char dur[256];
1947         char tmpcmd[256];
1948         struct ast_tm tm;
1949         char *passdata2;
1950         size_t len_passdata;
1951         char *greeting_attachment;
1952
1953 #ifdef IMAP_STORAGE
1954 #define ENDL "\r\n"
1955 #else
1956 #define ENDL "\n"
1957 #endif
1958
1959         gethostname(host, sizeof(host)-1);
1960
1961         if (strchr(srcemail, '@'))
1962                 ast_copy_string(who, srcemail, sizeof(who));
1963         else 
1964                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1965         
1966         greeting_attachment = strrchr(ast_strdupa(attach), '/');
1967         if (greeting_attachment)
1968                 *greeting_attachment++ = '\0';
1969
1970         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1971         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1972         fprintf(p, "Date: %s" ENDL, date);
1973
1974         /* Set date format for voicemail mail */
1975         ast_strftime(date, sizeof(date), emaildateformat, &tm);
1976
1977         if (!ast_strlen_zero(fromstring)) {
1978                 struct ast_channel *ast;
1979                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1980                         char *passdata;
1981                         int vmlen = strlen(fromstring)*3 + 200;
1982                         if ((passdata = alloca(vmlen))) {
1983                                 memset(passdata, 0, vmlen);
1984                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1985                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1986                                 len_passdata = strlen(passdata) * 2 + 3;
1987                                 passdata2 = alloca(len_passdata);
1988                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1989                         } else
1990                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1991                         ast_channel_free(ast);
1992                 } else
1993                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1994         } else
1995                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1996         len_passdata = strlen(vmu->fullname) * 2 + 3;
1997         passdata2 = alloca(len_passdata);
1998         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1999         if (!ast_strlen_zero(emailsubject)) {
2000                 struct ast_channel *ast;
2001                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2002                         char *passdata;
2003                         int vmlen = strlen(emailsubject) * 3 + 200;
2004                         if ((passdata = alloca(vmlen))) {
2005                                 memset(passdata, 0, vmlen);
2006                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2007                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2008                                 fprintf(p, "Subject: %s" ENDL, passdata);
2009                         } else
2010                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2011                         ast_channel_free(ast);
2012                 } else
2013                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2014         } else  if (!ast_strlen_zero(emailtitle)) {
2015                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2016                 fprintf(p, ENDL) ;
2017         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2018                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2019         else
2020                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2021         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2022         if(imap) {
2023                 /* additional information needed for IMAP searching */
2024                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2025                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2026                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2027                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2028                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2029                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2030                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2031                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2032                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2033                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2034                 if (!ast_strlen_zero(category)) 
2035                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2036                 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2037                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2038                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2039         }
2040         if (!ast_strlen_zero(cidnum))
2041                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2042         if (!ast_strlen_zero(cidname))
2043                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2044         fprintf(p, "MIME-Version: 1.0" ENDL);
2045         if (attach_user_voicemail) {
2046                 /* Something unique. */
2047                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2048
2049                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2050                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2051                 fprintf(p, "--%s" ENDL, bound);
2052         }
2053         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2054         if (emailbody) {
2055                 struct ast_channel *ast;
2056                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2057                         char *passdata;
2058                         int vmlen = strlen(emailbody)*3 + 200;
2059                         if ((passdata = alloca(vmlen))) {
2060                                 memset(passdata, 0, vmlen);
2061                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2062                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2063                                 fprintf(p, "%s" ENDL, passdata);
2064                         } else
2065                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2066                         ast_channel_free(ast);
2067                 } else
2068                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2069         } else if (msgnum > -1){
2070                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2071
2072                 "in mailbox %s from %s, on %s so you might" ENDL
2073                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2074                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2075         } else {
2076                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2077                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2078         }
2079         if (attach_user_voicemail) {
2080                 /* Eww. We want formats to tell us their own MIME type */
2081                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2082                 char tmpdir[256], newtmp[256];
2083                 int tmpfd = -1;
2084         
2085                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2086                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2087                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2088                         tmpfd = mkstemp(newtmp);
2089                         chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2090                         ast_debug(3, "newtmp: %s\n", newtmp);
2091                         if (tmpfd > -1) {
2092                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2093                                 ast_safe_system(tmpcmd);
2094                                 attach = newtmp;
2095                                 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2096                         }
2097                 }
2098                 fprintf(p, "--%s" ENDL, bound);
2099                 if (msgnum > -1)
2100                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2101                 else
2102                         fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2103                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2104                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2105                 if (msgnum > -1)
2106                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2107                 else
2108                         fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2109                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2110                 base_encode(fname, p);
2111                 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2112                 if (tmpfd > -1) {
2113                         unlink(fname);
2114                         close(tmpfd);
2115                         unlink(newtmp);
2116                 }
2117         }
2118 #undef ENDL
2119 }
2120
2121 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)
2122 {
2123         FILE *p=NULL;
2124         char tmp[80] = "/tmp/astmail-XXXXXX";
2125         char tmp2[256];
2126
2127         if (vmu && ast_strlen_zero(vmu->email)) {
2128                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2129                 return(0);
2130         }
2131         if (!strcmp(format, "wav49"))
2132                 format = "WAV";
2133         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));
2134         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2135            command hangs */
2136         if ((p = vm_mkftemp(tmp)) == NULL) {
2137                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2138                 return -1;
2139         } else {
2140                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2141                 fclose(p);
2142                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2143                 ast_safe_system(tmp2);
2144                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2145         }
2146         return 0;
2147 }
2148
2149 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)
2150 {
2151         char date[256];
2152         char host[MAXHOSTNAMELEN] = "";
2153         char who[256];
2154         char dur[PATH_MAX];
2155         char tmp[80] = "/tmp/astmail-XXXXXX";
2156         char tmp2[PATH_MAX];
2157         struct ast_tm tm;
2158         FILE *p;
2159
2160         if ((p = vm_mkftemp(tmp)) == NULL) {
2161                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2162                 return -1;
2163         }
2164         gethostname(host, sizeof(host)-1);
2165         if (strchr(srcemail, '@'))
2166                 ast_copy_string(who, srcemail, sizeof(who));
2167         else 
2168                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2169         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2170         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2171         fprintf(p, "Date: %s\n", date);
2172
2173         if (*pagerfromstring) {
2174                 struct ast_channel *ast;
2175                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2176                         char *passdata;
2177                         int vmlen = strlen(fromstring)*3 + 200;
2178                         if ((passdata = alloca(vmlen))) {
2179                                 memset(passdata, 0, vmlen);
2180                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2181                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2182                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2183                         } else 
2184                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2185                         ast_channel_free(ast);
2186                 } else 
2187                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2188         } else
2189                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2190         fprintf(p, "To: %s\n", pager);
2191         if (pagersubject) {
2192                 struct ast_channel *ast;
2193                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2194                         char *passdata;
2195                         int vmlen = strlen(pagersubject) * 3 + 200;
2196                         if ((passdata = alloca(vmlen))) {
2197                                 memset(passdata, 0, vmlen);
2198                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2199                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2200                                 fprintf(p, "Subject: %s\n\n", passdata);
2201                         } else
2202                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2203                         ast_channel_free(ast);
2204                 } else
2205                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2206         } else
2207                 fprintf(p, "Subject: New VM\n\n");
2208
2209         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2210         if (pagerbody) {
2211                 struct ast_channel *ast;
2212                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2213                         char *passdata;
2214                         int vmlen = strlen(pagerbody) * 3 + 200;
2215                         passdata = alloca(vmlen);
2216                         memset(passdata, 0, vmlen);
2217                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2218                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2219                         fprintf(p, "%s\n", passdata);
2220                         ast_channel_free(ast);
2221                 } else
2222                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2223         } else {
2224                 fprintf(p, "New %s long msg in box %s\n"
2225                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2226         }
2227         fclose(p);
2228         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2229         ast_safe_system(tmp2);
2230         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2231         return 0;
2232 }
2233
2234 static int get_date(char *s, int len)
2235 {
2236         struct ast_tm tm;
2237         struct timeval t = ast_tvnow();
2238         
2239         ast_localtime(&t, &tm, NULL);
2240
2241         return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2242 }
2243
2244 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2245 {
2246         int res;
2247         char fn[PATH_MAX];
2248         char dest[PATH_MAX];
2249
2250         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2251
2252         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2253                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2254                 return -1;
2255         }
2256
2257         RETRIEVE(fn, -1, ext, context);
2258         if (ast_fileexists(fn, NULL, NULL) > 0) {
2259                 res = ast_stream_and_wait(chan, fn, ecodes);
2260                 if (res) {
2261                         DISPOSE(fn, -1);
2262                         return res;
2263                 }
2264         } else {
2265                 /* Dispose just in case */
2266                 DISPOSE(fn, -1);
2267                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2268                 if (res)
2269                         return res;
2270                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2271                 if (res)
2272                         return res;
2273         }
2274         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2275         return res;
2276 }
2277
2278 static void free_user(struct ast_vm_user *vmu)
2279 {
2280         if (!ast_test_flag(vmu, VM_ALLOCED))
2281                 return;
2282
2283         ast_free(vmu);
2284 }
2285
2286 static void free_zone(struct vm_zone *z)
2287 {
2288         ast_free(z);
2289 }
2290
2291 static const char *mbox(int id)
2292 {
2293         static const char *msgs[] = {
2294                 "INBOX",
2295                 "Old",
2296                 "Work",
2297                 "Family",
2298                 "Friends",
2299                 "Cust1",
2300                 "Cust2",
2301                 "Cust3",
2302                 "Cust4",
2303                 "Cust5",
2304         };
2305         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2306 }
2307 #ifdef IMAP_STORAGE
2308 static int folder_int(const char *folder)
2309 {
2310         /*assume a NULL folder means INBOX*/
2311         if (!folder)
2312                 return 0;
2313         if(!strcasecmp(folder, "INBOX"))
2314                 return 0;
2315         else if (!strcasecmp(folder, "Old"))
2316                 return 1;
2317         else if (!strcasecmp(folder, "Work"))
2318                 return 2;
2319         else if (!strcasecmp(folder, "Family"))
2320                 return 3;
2321         else if (!strcasecmp(folder, "Friends"))
2322                 return 4;
2323         else if (!strcasecmp(folder, "Cust1"))
2324                 return 5;
2325         else if (!strcasecmp(folder, "Cust2"))
2326                 return 6;
2327         else if (!strcasecmp(folder, "Cust3"))
2328                 return 7;
2329         else if (!strcasecmp(folder, "Cust4"))
2330                 return 8;
2331         else if (!strcasecmp(folder, "Cust5"))
2332                 return 9;
2333         else /*assume they meant INBOX if folder is not found otherwise*/
2334                 return 0;
2335 }
2336 #endif
2337
2338 #ifdef ODBC_STORAGE
2339 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2340 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2341 {
2342         int x = -1;
2343         int res;
2344         SQLHSTMT stmt;
2345         char sql[PATH_MAX];
2346         char rowdata[20];
2347         char tmp[PATH_MAX] = "";
2348         struct odbc_obj *obj;
2349         char *context;
2350         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2351
2352         if (newmsgs)
2353                 *newmsgs = 0;
2354         if (oldmsgs)
2355                 *oldmsgs = 0;
2356
2357         /* If no mailbox, return immediately */
2358         if (ast_strlen_zero(mailbox))
2359                 return 0;
2360
2361         ast_copy_string(tmp, mailbox, sizeof(tmp));
2362         
2363         context = strchr(tmp, '@');
2364         if (context) {
2365                 *context = '\0';
2366                 context++;
2367         } else
2368                 context = "default";
2369         
2370         obj = ast_odbc_request_obj(odbc_database, 0);
2371         if (obj) {
2372                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2373                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2374                 if (!stmt) {
2375                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2376                         ast_odbc_release_obj(obj);
2377                         goto yuck;
2378                 }
2379                 res = SQLFetch(stmt);
2380                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2381                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2382                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2383                         ast_odbc_release_obj(obj);
2384                         goto yuck;
2385                 }
2386                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2387                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2388                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2389                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2390                         ast_odbc_release_obj(obj);
2391                         goto yuck;
2392                 }
2393                 *newmsgs = atoi(rowdata);
2394                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2395
2396                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2397                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2398                 if (!stmt) {
2399                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2400                         ast_odbc_release_obj(obj);
2401                         goto yuck;
2402                 }
2403                 res = SQLFetch(stmt);
2404                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2405                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2406                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2407                         ast_odbc_release_obj(obj);
2408                         goto yuck;
2409                 }
2410                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2411                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2412                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2413                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2414                         ast_odbc_release_obj(obj);
2415                         goto yuck;
2416                 }
2417                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2418                 ast_odbc_release_obj(obj);
2419                 *oldmsgs = atoi(rowdata);
2420                 x = 0;
2421         } else
2422                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2423                 
2424 yuck:   
2425         return x;
2426 }
2427
2428 static int messagecount(const char *context, const char *mailbox, const char *folder)
2429 {
2430         struct odbc_obj *obj = NULL;
2431         int nummsgs = 0;
2432         int res;
2433         SQLHSTMT stmt = NULL;
2434         char sql[PATH_MAX];
2435         char rowdata[20];
2436         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2437         if (!folder)
2438                 folder = "INBOX";
2439         /* If no mailbox, return immediately */
2440         if (ast_strlen_zero(mailbox))
2441                 return 0;
2442
2443         obj = ast_odbc_request_obj(odbc_database, 0);
2444         if (obj) {
2445                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2446                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2447                 if (!stmt) {
2448                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2449                         goto yuck;
2450                 }
2451                 res = SQLFetch(stmt);
2452                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2453                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2454                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2455                         goto yuck;
2456                 }
2457                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2458                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2459                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2460                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2461                         goto yuck;
2462                 }
2463                 nummsgs = atoi(rowdata);
2464                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2465         } else
2466                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2467
2468 yuck:
2469         if (obj)
2470                 ast_odbc_release_obj(obj);
2471         return nummsgs;
2472 }
2473
2474 static int has_voicemail(const char *mailbox, const char *folder)
2475 {
2476         char tmp[256], *tmp2 = tmp, *mbox, *context;
2477         ast_copy_string(tmp, mailbox, sizeof(tmp));
2478         while ((context = mbox = strsep(&tmp2, ","))) {
2479                 strsep(&context, "@");
2480                 if (ast_strlen_zero(context))
2481                         context = "default";
2482                 if (messagecount(context, mbox, folder))
2483                         return 1;
2484         }
2485         return 0;
2486 }
2487
2488 #elif defined(IMAP_STORAGE)
2489
2490 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)
2491 {
2492         char *myserveremail = serveremail;
2493         char fn[PATH_MAX];
2494         char mailbox[256];
2495         char *stringp;
2496         FILE *p=NULL;
2497         char tmp[80] = "/tmp/astmail-XXXXXX";
2498         long len;
2499         void *buf;
2500         STRING str;
2501         
2502         /* Attach only the first format */
2503         fmt = ast_strdupa(fmt);
2504         stringp = fmt;
2505         strsep(&stringp, "|");
2506
2507         if (!ast_strlen_zero(vmu->serveremail))
2508                 myserveremail = vmu->serveremail;
2509
2510         if (msgnum > -1)
2511                 make_file(fn, sizeof(fn), dir, msgnum);
2512         else
2513                 ast_copy_string (fn, dir, sizeof(fn));
2514         
2515         if (ast_strlen_zero(vmu->email))
2516                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2517
2518         if (!strcmp(fmt, "wav49"))
2519                 fmt = "WAV";
2520         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2521
2522         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2523            command hangs */
2524         if (!(p = vm_mkftemp(tmp))) {
2525                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2526                 return -1;
2527         }
2528
2529         if (msgnum < 0 && imapgreetings) {
2530                 init_mailstream(vms, GREETINGS_FOLDER);
2531                 imap_delete_old_greeting(fn, vms);
2532         }
2533         
2534         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);
2535         /* read mail file to memory */          
2536         len = ftell(p);
2537         rewind(p);
2538         if (!(buf = ast_malloc(len+1))) {
2539                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2540                 return -1;
2541         }
2542         fread(buf, len, 1, p);
2543         ((char *)buf)[len] = '\0';
2544         INIT(&str, mail_string, buf, len);
2545         init_mailstream(vms, NEW_FOLDER);
2546         imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
2547         if(!mail_append(vms->mailstream, mailbox, &str))
2548                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2549         fclose(p);
2550         unlink(tmp);
2551         ast_free(buf);
2552         ast_debug(3, "%s stored\n", fn);
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, "INBOX")) < 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         char dest[256];
2745         struct vm_state *sendvms = NULL, *destvms = NULL;
2746         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2747         if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2748         {
2749                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2750                 return -1;
2751         }
2752         if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2753         {
2754                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2755                 return -1;
2756         }
2757         imap_mailbox_name(dest, sizeof(dest), destvms, imbox, 1);
2758         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2759         if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2760                 return 0;
2761         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2762         return -1;
2763 }
2764
2765 #endif
2766 #ifndef IMAP_STORAGE
2767 /* copy message only used by file storage */
2768 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)
2769 {
2770         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2771         const char *frombox = mbox(imbox);
2772         int recipmsgnum;
2773
2774         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2775
2776         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2777         
2778         if (!dir)
2779                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2780         else
2781                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2782
2783         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2784         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2785
2786         if (vm_lock_path(todir))
2787                 return ERROR_LOCK_PATH;
2788
2789         recipmsgnum = last_message_index(recip, todir) + 1;
2790         if (recipmsgnum < recip->maxmsg) {
2791                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2792                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2793         } else {
2794                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2795         }
2796         ast_unlock_path(todir);
2797         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2798         
2799         return 0;
2800 }
2801 #endif
2802 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2803 static int messagecount(const char *context, const char *mailbox, const char *folder)
2804 {
2805         return __has_voicemail(context, mailbox, folder, 0);
2806 }
2807
2808
2809 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2810 {
2811         DIR *dir;
2812         struct dirent *de;
2813         char fn[256];
2814         int ret = 0;
2815
2816         /* If no mailbox, return immediately */
2817         if (ast_strlen_zero(mailbox))
2818                 return 0;
2819
2820         if (ast_strlen_zero(folder))
2821                 folder = "INBOX";
2822         if (ast_strlen_zero(context))
2823                 context = "default";
2824
2825         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2826
2827         if (!(dir = opendir(fn)))
2828                 return 0;
2829
2830         while ((de = readdir(dir))) {
2831                 if (!strncasecmp(de->d_name, "msg", 3)) {
2832                         if (shortcircuit) {
2833                                 ret = 1;
2834                                 break;
2835                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2836                                 ret++;
2837                 }
2838         }
2839
2840         closedir(dir);
2841
2842         return ret;
2843 }
2844
2845
2846 static int has_voicemail(const char *mailbox, const char *folder)
2847 {
2848         char tmp[256], *tmp2 = tmp, *mbox, *context;
2849         ast_copy_string(tmp, mailbox, sizeof(tmp));
2850         while ((mbox = strsep(&tmp2, ","))) {
2851                 if ((context = strchr(mbox, '@')))
2852                         *context++ = '\0';
2853                 else
2854                         context = "default";
2855                 if (__has_voicemail(context, mbox, folder, 1))
2856                         return 1;
2857         }
2858         return 0;
2859 }
2860
2861
2862 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2863 {
2864         char tmp[256];
2865         char *context;
2866
2867         /* If no mailbox, return immediately */
2868         if (ast_strlen_zero(mailbox))
2869                 return 0;
2870
2871         if (newmsgs)
2872                 *newmsgs = 0;
2873         if (oldmsgs)
2874                 *oldmsgs = 0;
2875
2876         if (strchr(mailbox, ',')) {
2877                 int tmpnew, tmpold;
2878                 char *mb, *cur;
2879
2880                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2881                 mb = tmp;
2882                 while ((cur = strsep(&mb, ", "))) {
2883                         if (!ast_strlen_zero(cur)) {
2884                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2885                                         return -1;
2886                                 else {
2887                                         if (newmsgs)
2888                                                 *newmsgs += tmpnew; 
2889                                         if (oldmsgs)
2890                                                 *oldmsgs += tmpold;
2891                                 }
2892                         }
2893                 }
2894                 return 0;
2895         }
2896
2897         ast_copy_string(tmp, mailbox, sizeof(tmp));
2898         
2899         if ((context = strchr(tmp, '@')))
2900                 *context++ = '\0';
2901         else
2902                 context = "default";
2903
2904         if (newmsgs)
2905                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2906         if (oldmsgs)
2907                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2908
2909         return 0;
2910 }
2911
2912 #endif
2913
2914 static void run_externnotify(char *context, char *extension)
2915 {
2916         char arguments[255];
2917         char ext_context[256] = "";
2918         int newvoicemails = 0, oldvoicemails = 0;
2919         struct ast_smdi_mwi_message *mwi_msg;
2920
2921         if (!ast_strlen_zero(context))
2922                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2923         else
2924                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2925
2926         if (smdi_iface) {
2927                 if (ast_app_has_voicemail(ext_context, NULL)) 
2928                         ast_smdi_mwi_set(smdi_iface, extension);
2929                 else
2930                         ast_smdi_mwi_unset(smdi_iface, extension);
2931
2932                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2933                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2934                         if (!strncmp(mwi_msg->cause, "INV", 3))
2935                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2936                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2937                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2938                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2939                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2940                 } else {
2941                         ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2942                 }
2943         }
2944
2945         if (!ast_strlen_zero(externnotify)) {
2946                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2947                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2948                 } else {
2949                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2950                         ast_debug(1, "Executing %s\n", arguments);
2951                         ast_safe_system(arguments);
2952                 }
2953         }
2954 }
2955
2956 struct leave_vm_options {
2957         unsigned int flags;
2958         signed char record_gain;
2959 };
2960
2961 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2962 {
2963 #ifdef IMAP_STORAGE
2964         int newmsgs, oldmsgs;
2965         struct vm_state *vms = NULL;
2966 #endif
2967         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2968         char callerid[256];
2969         FILE *txt;
2970         char date[256];
2971         int txtdes;
2972         int res = 0;
2973         int msgnum;
2974         int duration = 0;
2975         int ausemacro = 0;
2976         int ousemacro = 0;
2977         int ouseexten = 0;
2978         int rtmsgid = 0;
2979         char tmpid[16];
2980         char tmpdur[16];
2981         char priority[16];
2982         char origtime[16];
2983         char dir[PATH_MAX], tmpdir[PATH_MAX];
2984         char fn[PATH_MAX];
2985         char prefile[PATH_MAX] = "";
2986         char tempfile[PATH_MAX] = "";
2987         char ext_context[256] = "";
2988         char fmt[80];
2989         char *context;
2990         char ecodes[16] = "#";
2991         char tmp[1024] = "", *tmpptr;
2992         struct ast_vm_user *vmu;
2993         struct ast_vm_user svm;
2994         const char *category = NULL;
2995
2996         ast_copy_string(tmp, ext, sizeof(tmp));
2997         ext = tmp;
2998         if ((context = strchr(tmp, '@'))) {
2999                 *context++ = '\0';
3000                 tmpptr = strchr(context, '&');
3001         } else {
3002                 tmpptr = strchr(ext, '&');
3003         }
3004
3005         if (tmpptr)
3006                 *tmpptr++ = '\0';
3007
3008         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3009
3010         ast_debug(3, "Before find_user\n");
3011         if (!(vmu = find_user(&svm, context, ext))) {
3012                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3013                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3014                 return res;
3015         }
3016         /* Setup pre-file if appropriate */
3017         if (strcmp(vmu->context, "default"))
3018                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3019         else
3020                 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3021         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3022                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3023         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3024                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3025         }
3026         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3027         if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3028                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3029                 return -1;
3030         }
3031         RETRIEVE(tempfile, -1, ext, context);
3032         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3033                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3034         DISPOSE(tempfile, -1);
3035         /* It's easier just to try to make it than to check for its existence */
3036         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3037
3038         /* Check current or macro-calling context for special extensions */
3039         if (ast_test_flag(vmu, VM_OPERATOR)) {
3040                 if (!ast_strlen_zero(vmu->exit)) {
3041                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3042                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3043                                 ouseexten = 1;
3044                         }
3045                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3046                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3047                         ouseexten = 1;
3048                 }
3049                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3050                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3051                 ousemacro = 1;
3052                 }
3053         }
3054
3055         if (!ast_strlen_zero(vmu->exit)) {
3056                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3057                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3058         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3059                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3060         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3061                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3062                 ausemacro = 1;
3063         }
3064
3065         /* Play the beginning intro if desired */
3066         if (!ast_strlen_zero(prefile)) {
3067 #ifdef ODBC_STORAGE
3068                 int success = 
3069 #endif
3070                         RETRIEVE(prefile, -1, ext, context);
3071                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3072                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3073                                 res = ast_waitstream(chan, ecodes);
3074 #ifdef ODBC_STORAGE
3075                         if (success == -1) {
3076                                 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3077                                 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3078                                 store_file(prefile, vmu->mailbox, vmu->context, -1);
3079                         }
3080 #endif
3081                 } else {
3082                         ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3083                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3084                 }
3085                 DISPOSE(prefile, -1);
3086                 if (res < 0) {
3087                         ast_debug(1, "Hang up during prefile playback\n");
3088                         free_user(vmu);
3089                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3090                         return -1;
3091                 }
3092         }
3093         if (res == '#') {
3094                 /* On a '#' we skip the instructions */
3095                 ast_set_flag(options, OPT_SILENT);
3096                 res = 0;
3097         }
3098         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3099                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3100                 if (res == '#') {
3101                         ast_set_flag(options, OPT_SILENT);
3102                         res = 0;
3103                 }
3104         }
3105         if (res > 0)
3106                 ast_stopstream(chan);
3107         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3108          other than the operator -- an automated attendant or mailbox login for example */
3109         if (res == '*') {
3110                 chan->exten[0] = 'a';
3111                 chan->exten[1] = '\0';
3112                 if (!ast_strlen_zero(vmu->exit)) {
3113                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3114                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3115                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3116                 }
3117                 chan->priority = 0;
3118                 free_user(vmu);
3119                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3120                 return 0;
3121         }
3122
3123         /* Check for a '0' here */
3124         if (res == '0') {
3125         transfer:
3126                 if (ouseexten || ousemacro) {
3127                         chan->exten[0] = 'o';
3128                         chan->exten[1] = '\0';
3129                         if (!ast_strlen_zero(vmu->exit)) {
3130                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3131                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3132                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3133                         }
3134                         ast_play_and_wait(chan, "transfer");
3135                         chan->priority = 0;
3136                         free_user(vmu);
3137                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3138                 }
3139                 return 0;
3140         }
3141         if (res < 0) {
3142                 free_user(vmu);
3143                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3144                 return -1;
3145         }
3146         /* The meat of recording the message...  All the announcements and beeps have been played*/
3147         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3148         if (!ast_strlen_zero(fmt)) {
3149                 msgnum = 0;
3150
3151 #ifdef IMAP_STORAGE
3152                 /* Is ext a mailbox? */
3153                 /* must open stream for this user to get info! */
3154                 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3155                 if(res < 0) {
3156                         ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3157                         return -1;
3158                 }
3159                 if(!(vms = get_vm_state_by_mailbox(ext,0))) {
3160                 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3161                  * rarely be used*/
3162                         if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3163                                 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3164                                 return -1;
3165                         }
3166                         ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3167                         ast_copy_string(vms->username, ext, sizeof(vms->username));
3168                         vms->mailstream = NIL;
3169                         ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3170                         vms->updated=1;
3171                         ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3172                         init_vm_state(vms);
3173                         vmstate_insert(vms);
3174                         vms = get_vm_state_by_mailbox(ext,0);
3175                 }
3176                 vms->newmessages++;
3177                 
3178                 /* here is a big difference! We add one to it later */
3179                 msgnum = newmsgs + oldmsgs;
3180                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3181                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3182                 /* set variable for compatibility */
3183                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3184
3185                 /* Check if mailbox is full */
3186                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3187                         ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3188                         ast_play_and_wait(chan, "vm-mailboxfull");
3189                         return -1;
3190                 }
3191                 /* here is a big difference! We add one to it later */
3192                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3193
3194 #else
3195                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3196                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3197                         if (!res)
3198                                 res = ast_waitstream(chan, "");
3199                         ast_log(LOG_WARNING, "No more messages possible\n");
3200                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3201                         goto leave_vm_out;
3202                 }
3203
3204 #endif
3205                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3206                 txtdes = mkstemp(tmptxtfile);
3207                 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3208                 if (txtdes < 0) {
3209                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3210                         if (!res)
3211                                 res = ast_waitstream(chan, "");
3212                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3213                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3214                         goto leave_vm_out;
3215                 }
3216
3217                 /* Now play the beep once we have the message number for our next message. */
3218                 if (res >= 0) {
3219                         /* Unless we're *really* silent, try to send the beep */
3220                         res = ast_stream_and_wait(chan, "beep", "");
3221                 }
3222                                 
3223                 /* Store information in real-time storage */
3224                 if (ast_check_realtime("voicemail_data")) {
3225                         snprintf(priority, sizeof(priority), "%d", chan->priority);
3226                         snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3227                         get_date(date, sizeof(date));
3228                         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);
3229                 }
3230
3231                 /* Store information */
3232                 txt = fdopen(txtdes, "w+");
3233                 if (txt) {
3234                         get_date(date, sizeof(date));
3235                         fprintf(txt, 
3236                                 ";\n"
3237                                 "; Message Information file\n"
3238                                 ";\n"
3239                                 "[message]\n"
3240                                 "origmailbox=%s\n"
3241                                 "context=%s\n"
3242                                 "macrocontext=%s\n"
3243                                 "exten=%s\n"
3244                                 "priority=%d\n"
3245                                 "callerchan=%s\n"
3246                                 "callerid=%s\n"
3247                                 "origdate=%s\n"
3248                                 "origtime=%ld\n"
3249                                 "category=%s\n",
3250                                 ext,
3251                                 chan->context,
3252                                 chan->macrocontext, 
3253                                 chan->exten,
3254                                 chan->priority,
3255                                 chan->name,
3256                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3257                                 date, (long)time(NULL),
3258                                 category ? category : ""); 
3259                 } else
3260                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3261 #ifdef IMAP_STORAGE
3262                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3263 #else
3264                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3265 #endif
3266
3267                 if (txt) {
3268                         if (duration < vmminsecs) {
3269                                 fclose(txt);
3270                                 if (option_verbose > 2) 
3271                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3272                                 ast_filedelete(tmptxtfile, NULL);
3273                                 unlink(tmptxtfile);
3274                                 if (ast_check_realtime("voicemail_data")) {
3275                                         snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3276                                         ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3277                                 }
3278                         } else {
3279                                 fprintf(txt, "duration=%d\n", duration);
3280                                 fclose(txt);
3281                                 if (vm_lock_path(dir)) {
3282                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3283                                         /* Delete files */
3284                                         ast_filedelete(tmptxtfile, NULL);
3285                                         unlink(tmptxtfile);
3286                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3287                                         ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
3288                                         unlink(tmptxtfile);
3289                                         ast_unlock_path(dir);
3290                                         if (ast_check_realtime("voicemail_data")) {
3291                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3292                                                 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3293                                         }
3294                                 } else {
3295 #ifndef IMAP_STORAGE
3296                                         msgnum = last_message_index(vmu, dir) + 1;
3297 #endif
3298                                         make_file(fn, sizeof(fn), dir, msgnum);
3299
3300                                         /* assign a variable with the name of the voicemail file */ 
3301 #ifndef IMAP_STORAGE
3302                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3303 #else
3304                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3305 #endif
3306
3307                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3308                                         ast_filerename(tmptxtfile, fn, NULL);
3309                                         rename(tmptxtfile, txtfile);
3310
3311                                         /* Properly set permissions on voicemail text descriptor file.
3312                                            Unfortunately mkstemp() makes this file 0600 on most unix systems. */
3313                                         if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
3314                                                 ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
3315
3316                                         ast_unlock_path(dir);
3317                                         if (ast_check_realtime("voicemail_data")) {
3318                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3319                                                 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3320                                                 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3321                                         }
3322                                         /* We must store the file first, before copying the message, because
3323                                          * ODBC storage does the entire copy with SQL.
3324                                          */
3325                                         if (ast_fileexists(fn, NULL, NULL) > 0) {
3326                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3327                                         }
3328
3329                                         /* Are there to be more recipients of this message? */
3330                                         while (tmpptr) {
3331                                                 struct ast_vm_user recipu, *recip;
3332                                                 char *exten, *context;
3333                                         
3334                                                 exten = strsep(&tmpptr, "&");
3335                                                 context = strchr(exten, '@');
3336                                                 if (context) {
3337                                                         *context = '\0';
3338                                                         context++;
3339                                                 }
3340                                                 if ((recip = find_user(&recipu, context, exten))) {
3341                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3342                                                         free_user(recip);
3343                                                 }
3344                                         }
3345                                         /* Notification and disposal needs to happen after the copy, though. */
3346                                         if (ast_fileexists(fn, NULL, NULL)) {
3347                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3348                                                 DISPOSE(dir, msgnum);
3349                                         }
3350                                 }
3351                         }
3352                 }
3353                 if (res == '0') {
3354                         goto transfer;
3355                 } else if (res > 0)
3356                         res = 0;
3357
3358                 if (duration < vmminsecs)
3359                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3360                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3361                 else
3362                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3363         } else
3364                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3365 leave_vm_out:
3366         free_user(vmu);
3367         
3368         return res;
3369 }
3370
3371 #ifndef IMAP_STORAGE
3372 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3373 {
3374         /* we know max messages, so stop process when number is hit */
3375
3376         int x,dest;
3377         char sfn[PATH_MAX];
3378         char dfn[PATH_MAX];
3379
3380         if (vm_lock_path(dir))
3381                 return ERROR_LOCK_PATH;
3382
3383         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3384                 make_file(sfn, sizeof(sfn), dir, x);
3385                 if (EXISTS(dir, x, sfn, NULL)) {
3386                         
3387                         if (x != dest) {
3388                                 make_file(dfn, sizeof(dfn), dir, dest);
3389                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3390                         }
3391                         
3392                         dest++;
3393                 }
3394         }
3395         ast_unlock_path(dir);
3396
3397         return 0;
3398 }
3399 #endif
3400
3401 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3402 {
3403         int d;
3404         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3405         return d;
3406 }
3407
3408 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3409 {
3410 #ifdef IMAP_STORAGE
3411         /* we must use mbox(x) folder names, and copy the message there */
3412         /* simple. huh? */
3413         char dbox[256];
3414         long res;
3415         char sequence[10];
3416
3417         /* if save to Old folder, just leave in INBOX */
3418         if (box == 1) return 10;
3419         /* get the real IMAP message number for this message */
3420         snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
3421         imap_mailbox_name(dbox, sizeof(dbox), vms, box, 1);
3422         ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3423         res = mail_copy(vms->mailstream, sequence, dbox);
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,&