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