46c57ecce06d4387afc406d609f8397b52711ab5
[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")) {
738                 if (vmu->maxsecs <= 0) {
739                         ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %i\n", value, vmmaxsecs);
740                         vmu->maxsecs = vmmaxsecs;
741                 } else {
742                         vmu->maxsecs = atoi(value);
743                 }
744         } else if (!strcasecmp(var, "maxmsg")) {
745                 vmu->maxmsg = atoi(value);
746                 if (vmu->maxmsg <= 0) {
747                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
748                         vmu->maxmsg = MAXMSG;
749                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
750                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
751                         vmu->maxmsg = MAXMSGLIMIT;
752                 }
753         } else if (!strcasecmp(var, "volgain")) {
754                 sscanf(value, "%lf", &vmu->volgain);
755         } else if (!strcasecmp(var, "options")) {
756                 apply_options(vmu, value);
757         }
758 }
759
760 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
761 {
762         int res;
763         if (!ast_strlen_zero(vmu->uniqueid)) {
764                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
765                 if (res > 0) {
766                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
767                         res = 0;
768                 } else if (!res) {
769                         res = -1;
770                 }
771                 return res;
772         }
773         return -1;
774 }
775
776 static void apply_options(struct ast_vm_user *vmu, const char *options)
777 {       /* Destructively Parse options and apply */
778         char *stringp;
779         char *s;
780         char *var, *value;
781         stringp = ast_strdupa(options);
782         while ((s = strsep(&stringp, "|"))) {
783                 value = s;
784                 if ((var = strsep(&value, "=")) && value) {
785                         apply_option(vmu, var, value);
786                 }
787         }       
788 }
789
790 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
791 {
792         struct ast_variable *tmp;
793         tmp = var;
794         while (tmp) {
795                 if (!strcasecmp(tmp->name, "vmsecret")) {
796                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
797                 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
798                         if (ast_strlen_zero(retval->password))
799                                 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
800                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
801                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
802                 } else if (!strcasecmp(tmp->name, "pager")) {
803                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
804                 } else if (!strcasecmp(tmp->name, "email")) {
805                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
806                 } else if (!strcasecmp(tmp->name, "fullname")) {
807                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
808                 } else if (!strcasecmp(tmp->name, "context")) {
809                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
810 #ifdef IMAP_STORAGE
811                 } else if (!strcasecmp(tmp->name, "imapuser")) {
812                         ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
813                 } else if (!strcasecmp(tmp->name, "imappassword")) {
814                         ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
815 #endif
816                 } else
817                         apply_option(retval, tmp->name, tmp->value);
818                 tmp = tmp->next;
819         } 
820 }
821
822 static int is_valid_dtmf(const char *key)
823 {
824         int i;
825         char *local_key = ast_strdupa(key);
826
827         for(i = 0; i < strlen(key); ++i) {
828                 if(!strchr(VALID_DTMF, *local_key)) {
829                         ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
830                         return 0;
831                 }
832                 local_key++;
833         }
834         return 1;
835 }
836
837 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
838 {
839         struct ast_variable *var;
840         struct ast_vm_user *retval;
841
842         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
843                 if (!ivm)
844                         ast_set_flag(retval, VM_ALLOCED);       
845                 else
846                         memset(retval, 0, sizeof(*retval));
847                 if (mailbox) 
848                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
849                 populate_defaults(retval);
850                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
851                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
852                 else
853                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
854                 if (var) {
855                         apply_options_full(retval, var);
856                         ast_variables_destroy(var);
857                 } else { 
858                         if (!ivm) 
859                                 ast_free(retval);
860                         retval = NULL;
861                 }       
862         } 
863         return retval;
864 }
865
866 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
867 {
868         /* This function could be made to generate one from a database, too */
869         struct ast_vm_user *vmu=NULL, *cur;
870         AST_LIST_LOCK(&users);
871
872         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
873                 context = "default";
874
875         AST_LIST_TRAVERSE(&users, cur, list) {
876                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
877                         break;
878                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
879                         break;
880         }
881         if (cur) {
882                 /* Make a copy, so that on a reload, we have no race */
883                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
884                         memcpy(vmu, cur, sizeof(*vmu));
885                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
886                         AST_LIST_NEXT(vmu, list) = NULL;
887                 }
888         } else
889                 vmu = find_user_realtime(ivm, context, mailbox);
890         AST_LIST_UNLOCK(&users);
891         return vmu;
892 }
893
894 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
895 {
896         /* This function could be made to generate one from a database, too */
897         struct ast_vm_user *cur;
898         int res = -1;
899         AST_LIST_LOCK(&users);
900         AST_LIST_TRAVERSE(&users, cur, list) {
901                 if ((!context || !strcasecmp(context, cur->context)) &&
902                         (!strcasecmp(mailbox, cur->mailbox)))
903                                 break;
904         }
905         if (cur) {
906                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
907                 res = 0;
908         }
909         AST_LIST_UNLOCK(&users);
910         return res;
911 }
912
913 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
914 {
915         struct ast_config   *cfg=NULL;
916         struct ast_variable *var=NULL;
917         struct ast_category *cat=NULL;
918         char *category=NULL, *value=NULL, *new=NULL;
919         const char *tmp=NULL;
920         struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
921                                         
922         if (!change_password_realtime(vmu, newpassword))
923                 return;
924
925         /* check voicemail.conf */
926         if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
927                 while ((category = ast_category_browse(cfg, category))) {
928                         if (!strcasecmp(category, vmu->context)) {
929                                 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
930                                         ast_log(LOG_WARNING, "We could not find the mailbox.\n");
931                                         break;
932                                 }
933                                 value = strstr(tmp,",");
934                                 if (!value) {
935                                         ast_log(LOG_WARNING, "variable has bad format.\n");
936                                         break;
937                                 }
938                                 new = alloca((strlen(value)+strlen(newpassword)+1));
939                                 sprintf(new,"%s%s", newpassword, value);
940                                 if (!(cat = ast_category_get(cfg, category))) {
941                                         ast_log(LOG_WARNING, "Failed to get category structure.\n");
942                                         break;
943                                 }
944                                 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
945                         }
946                 }
947                 /* save the results */
948                 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
949                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
950                 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
951         }
952         category = NULL;
953         var = NULL;
954         /* check users.conf and update the password stored for the mailbox*/
955         /* if no vmsecret entry exists create one. */
956         if ((cfg = ast_config_load("users.conf", config_flags))) {
957                 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
958                 while ((category = ast_category_browse(cfg, category))) {
959                         ast_debug(4, "users.conf: %s\n", category);
960                         if (!strcasecmp(category, vmu->mailbox)) {
961                                 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
962                                         ast_debug(3, "looks like we need to make vmsecret!\n");
963                                         var = ast_variable_new("vmsecret", newpassword);
964                                 } 
965                                 new = alloca(strlen(newpassword)+1);
966                                 sprintf(new, "%s", newpassword);
967                                 if (!(cat = ast_category_get(cfg, category))) {
968                                         ast_debug(4, "failed to get category!\n");
969                                         break;
970                                 }
971                                 if (!var)               
972                                         ast_variable_update(cat, "vmsecret", new, NULL, 0);
973                                 else
974                                         ast_variable_append(cat, var);
975                         }
976                 }
977                 /* save the results and clean things up */
978                 reset_user_pw(vmu->context, vmu->mailbox, newpassword); 
979                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
980                 config_text_file_save("users.conf", cfg, "AppVoicemail");
981         }
982 }
983
984 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
985 {
986         char buf[255];
987         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
988         if (!ast_safe_system(buf))
989                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
990 }
991
992 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
993 {
994         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
995 }
996
997 #ifdef IMAP_STORAGE
998 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
999 {
1000         int res;
1001         if ((res = ast_mkdir(dir, 01777))) {
1002                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
1003                 return sprintf(dest, "%s/msg%04d", dir, num);
1004         }
1005         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
1006         return sprintf(dest, "%s/msg%04d", dir, num);
1007 }
1008
1009 static void vm_imap_delete(int msgnum, struct vm_state *vms)
1010 {
1011         unsigned long messageNum = 0;
1012         char arg[10];
1013
1014         /* find real message number based on msgnum */
1015         /* this may be an index into vms->msgArray based on the msgnum. */
1016
1017         messageNum = vms->msgArray[msgnum];
1018         if (messageNum == 0) {
1019                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1020                 return;
1021         }
1022         ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1023         /* delete message */
1024         sprintf (arg,"%lu",messageNum);
1025         mail_setflag (vms->mailstream,arg,"\\DELETED");
1026 }
1027
1028 #endif
1029 static int make_file(char *dest, int len, char *dir, int num)
1030 {
1031         return snprintf(dest, len, "%s/msg%04d", dir, num);
1032 }
1033
1034 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1035  * \param dest    String. base directory.
1036  * \param len     Length of dest.
1037  * \param context String. Ignored if is null or empty string.
1038  * \param ext     String. Ignored if is null or empty string.
1039  * \param folder  String. Ignored if is null or empty string. 
1040  * \return -1 on failure, 0 on success.
1041  */
1042 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1043 {
1044         mode_t  mode = VOICEMAIL_DIR_MODE;
1045         int res;
1046
1047         make_dir(dest, len, context, ext, folder);
1048         if ((res = ast_mkdir(dest, mode))) {
1049                 ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1050                 return -1;
1051         }
1052         return 0;
1053 }
1054
1055 /*! \brief Lock file path
1056     only return failure if ast_lock_path returns 'timeout',
1057    not if the path does not exist or any other reason
1058 */
1059 static int vm_lock_path(const char *path)
1060 {
1061         switch (ast_lock_path(path)) {
1062         case AST_LOCK_TIMEOUT:
1063                 return -1;
1064         default:
1065                 return 0;
1066         }
1067 }
1068
1069
1070 #ifdef ODBC_STORAGE
1071 struct generic_prepare_struct {
1072         char *sql;
1073         int argc;
1074         char **argv;
1075 };
1076
1077 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
1078 {
1079         struct generic_prepare_struct *gps = data;
1080         int res, i;
1081         SQLHSTMT stmt;
1082
1083         res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1084         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1085                 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1086                 return NULL;
1087         }
1088         res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
1089         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1090                 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
1091                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1092                 return NULL;
1093         }
1094         for (i = 0; i < gps->argc; i++)
1095                 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
1096
1097         return stmt;
1098 }
1099
1100 static int retrieve_file(char *dir, int msgnum)
1101 {
1102         int x = 0;
1103         int res;
1104         int fd=-1;
1105         size_t fdlen = 0;
1106         void *fdm = MAP_FAILED;
1107         SQLSMALLINT colcount=0;
1108         SQLHSTMT stmt;
1109         char sql[PATH_MAX];
1110         char fmt[80]="";
1111         char *c;
1112         char coltitle[256];
1113         SQLSMALLINT collen;
1114         SQLSMALLINT datatype;
1115         SQLSMALLINT decimaldigits;
1116         SQLSMALLINT nullable;
1117         SQLULEN colsize;
1118         SQLLEN colsize2;
1119         FILE *f=NULL;
1120         char rowdata[80];
1121         char fn[PATH_MAX];
1122         char full_fn[PATH_MAX];
1123         char msgnums[80];
1124         char *argv[] = { dir, msgnums };
1125         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1126
1127         struct odbc_obj *obj;
1128         obj = ast_odbc_request_obj(odbc_database, 0);
1129         if (obj) {
1130                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1131                 c = strchr(fmt, '|');
1132                 if (c)
1133                         *c = '\0';
1134                 if (!strcasecmp(fmt, "wav49"))
1135                         strcpy(fmt, "WAV");
1136                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1137                 if (msgnum > -1)
1138                         make_file(fn, sizeof(fn), dir, msgnum);
1139                 else
1140                         ast_copy_string(fn, dir, sizeof(fn));
1141                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1142                 
1143                 if (!(f = fopen(full_fn, "w+"))) {
1144                         ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
1145                         goto yuck;
1146                 }
1147                 
1148                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1149                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1150                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1151                 if (!stmt) {
1152                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1153                         ast_odbc_release_obj(obj);
1154                         goto yuck;
1155                 }
1156                 res = SQLFetch(stmt);
1157                 if (res == SQL_NO_DATA) {
1158                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1159                         ast_odbc_release_obj(obj);
1160                         goto yuck;
1161                 }
1162                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1163                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1164                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1165                         ast_odbc_release_obj(obj);
1166                         goto yuck;
1167                 }
1168                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
1169                 if (fd < 0) {
1170                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1171                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1172                         ast_odbc_release_obj(obj);
1173                         goto yuck;
1174                 }
1175                 res = SQLNumResultCols(stmt, &colcount);
1176                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1177                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1178                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1179                         ast_odbc_release_obj(obj);
1180                         goto yuck;
1181                 }
1182                 if (f) 
1183                         fprintf(f, "[message]\n");
1184                 for (x=0;x<colcount;x++) {
1185                         rowdata[0] = '\0';
1186                         collen = sizeof(coltitle);
1187                         res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
1188                                                 &datatype, &colsize, &decimaldigits, &nullable);
1189                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1190                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1191                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1192                                 ast_odbc_release_obj(obj);
1193                                 goto yuck;
1194                         }
1195                         if (!strcasecmp(coltitle, "recording")) {
1196                                 off_t offset;
1197                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
1198                                 fdlen = colsize2;
1199                                 if (fd > -1) {
1200                                         char tmp[1]="";
1201                                         lseek(fd, fdlen - 1, SEEK_SET);
1202                                         if (write(fd, tmp, 1) != 1) {
1203                                                 close(fd);
1204                                                 fd = -1;
1205                                                 continue;
1206                                         }
1207                                         /* Read out in small chunks */
1208                                         for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
1209                                                 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
1210                                                         ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
1211                                                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1212                                                         ast_odbc_release_obj(obj);
1213                                                         goto yuck;
1214                                                 } else {
1215                                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
1216                                                         munmap(fdm, CHUNKSIZE);
1217                                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1218                                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1219                                                                 unlink(full_fn);
1220                                                                 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1221                                                                 ast_odbc_release_obj(obj);
1222                                                                 goto yuck;
1223                                                         }
1224                                                 }
1225                                         }
1226                                         truncate(full_fn, fdlen);
1227                                 }
1228                         } else {
1229                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1230                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1231                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1232                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1233                                         ast_odbc_release_obj(obj);
1234                                         goto yuck;
1235                                 }
1236                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1237                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1238                         }
1239                 }
1240                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1241                 ast_odbc_release_obj(obj);
1242         } else
1243                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1244 yuck:   
1245         if (f)
1246                 fclose(f);
1247         if (fd > -1)
1248                 close(fd);
1249         return x - 1;
1250 }
1251
1252 static int remove_file(char *dir, int msgnum)
1253 {
1254         char fn[PATH_MAX];
1255         char full_fn[PATH_MAX];
1256         char msgnums[80];
1257         
1258         if (msgnum > -1) {
1259                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1260                 make_file(fn, sizeof(fn), dir, msgnum);
1261         } else
1262                 ast_copy_string(fn, dir, sizeof(fn));
1263         ast_filedelete(fn, NULL);       
1264         if (ast_check_realtime("voicemail_data")) {
1265                 ast_destroy_realtime("voicemail_data", "filename", fn, NULL);
1266         }
1267         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1268         unlink(full_fn);
1269         return 0;
1270 }
1271
1272 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1273 {
1274         int x = 0;
1275         int res;
1276         SQLHSTMT stmt;
1277         char sql[PATH_MAX];
1278         char rowdata[20];
1279         char *argv[] = { dir };
1280         struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
1281
1282         struct odbc_obj *obj;
1283         obj = ast_odbc_request_obj(odbc_database, 0);
1284         if (obj) {
1285                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1286                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1287                 if (!stmt) {
1288                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1289                         ast_odbc_release_obj(obj);
1290                         goto yuck;
1291                 }
1292                 res = SQLFetch(stmt);
1293                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1294                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1295                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1296                         ast_odbc_release_obj(obj);
1297                         goto yuck;
1298                 }
1299                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1300                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1302                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303                         ast_odbc_release_obj(obj);
1304                         goto yuck;
1305                 }
1306                 if (sscanf(rowdata, "%d", &x) != 1)
1307                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1308                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1309                 ast_odbc_release_obj(obj);
1310         } else
1311                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1312 yuck:   
1313         return x - 1;
1314 }
1315
1316 static int message_exists(char *dir, int msgnum)
1317 {
1318         int x = 0;
1319         int res;
1320         SQLHSTMT stmt;
1321         char sql[PATH_MAX];
1322         char rowdata[20];
1323         char msgnums[20];
1324         char *argv[] = { dir, msgnums };
1325         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1326
1327         struct odbc_obj *obj;
1328         obj = ast_odbc_request_obj(odbc_database, 0);
1329         if (obj) {
1330                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1331                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1332                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1333                 if (!stmt) {
1334                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1335                         ast_odbc_release_obj(obj);
1336                         goto yuck;
1337                 }
1338                 res = SQLFetch(stmt);
1339                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1340                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1341                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1342                         ast_odbc_release_obj(obj);
1343                         goto yuck;
1344                 }
1345                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1346                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1347                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1348                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1349                         ast_odbc_release_obj(obj);
1350                         goto yuck;
1351                 }
1352                 if (sscanf(rowdata, "%d", &x) != 1)
1353                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1354                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1355                 ast_odbc_release_obj(obj);
1356         } else
1357                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1358 yuck:   
1359         return x;
1360 }
1361
1362 static int count_messages(struct ast_vm_user *vmu, char *dir)
1363 {
1364         return last_message_index(vmu, dir) + 1;
1365 }
1366
1367 static void delete_file(char *sdir, int smsg)
1368 {
1369         SQLHSTMT stmt;
1370         char sql[PATH_MAX];
1371         char msgnums[20];
1372         char *argv[] = { sdir, msgnums };
1373         struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
1374
1375         struct odbc_obj *obj;
1376         obj = ast_odbc_request_obj(odbc_database, 0);
1377         if (obj) {
1378                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1379                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1380                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1381                 if (!stmt)
1382                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1383                 else
1384                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1385                 ast_odbc_release_obj(obj);
1386         } else
1387                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1388         return; 
1389 }
1390
1391 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1392 {
1393         SQLHSTMT stmt;
1394         char sql[512];
1395         char msgnums[20];
1396         char msgnumd[20];
1397         struct odbc_obj *obj;
1398         char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
1399         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1400
1401         delete_file(ddir, dmsg);
1402         obj = ast_odbc_request_obj(odbc_database, 0);
1403         if (obj) {
1404                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1405                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1406                 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
1407                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1408                 if (!stmt)
1409                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1410                 else
1411                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1412                 ast_odbc_release_obj(obj);
1413         } else
1414                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1415         return; 
1416 }
1417
1418 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1419 {
1420         int x = 0;
1421         int res;
1422         int fd = -1;
1423         void *fdm = MAP_FAILED;
1424         size_t fdlen = -1;
1425         SQLHSTMT stmt;
1426         SQLLEN len;
1427         char sql[PATH_MAX];
1428         char msgnums[20];
1429         char fn[PATH_MAX];
1430         char full_fn[PATH_MAX];
1431         char fmt[80]="";
1432         char *c;
1433         const char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1434         const char *category = "";
1435         struct ast_config *cfg=NULL;
1436         struct odbc_obj *obj;
1437         struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
1438
1439         delete_file(dir, msgnum);
1440         obj = ast_odbc_request_obj(odbc_database, 0);
1441         if (obj) {
1442                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1443                 c = strchr(fmt, '|');
1444                 if (c)
1445                         *c = '\0';
1446                 if (!strcasecmp(fmt, "wav49"))
1447                         strcpy(fmt, "WAV");
1448                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1449                 if (msgnum > -1)
1450                         make_file(fn, sizeof(fn), dir, msgnum);
1451                 else
1452                         ast_copy_string(fn, dir, sizeof(fn));
1453                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1454                 cfg = ast_config_load(full_fn, config_flags);
1455                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1456                 fd = open(full_fn, O_RDWR);
1457                 if (fd < 0) {
1458                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1459                         ast_odbc_release_obj(obj);
1460                         goto yuck;
1461                 }
1462                 if (cfg) {
1463                         context = ast_variable_retrieve(cfg, "message", "context");
1464                         if (!context) context = "";
1465                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1466                         if (!macrocontext) macrocontext = "";
1467                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1468                         if (!callerid) callerid = "";
1469                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1470                         if (!origtime) origtime = "";
1471                         duration = ast_variable_retrieve(cfg, "message", "duration");
1472                         if (!duration) duration = "";
1473                         category = ast_variable_retrieve(cfg, "message", "category");
1474                         if (!category) category = "";
1475                 }
1476                 fdlen = lseek(fd, 0, SEEK_END);
1477                 lseek(fd, 0, SEEK_SET);
1478                 printf("Length is %zd\n", fdlen);
1479                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1480                 if (fdm == MAP_FAILED) {
1481                         ast_log(LOG_WARNING, "Memory map failed!\n");
1482                         ast_odbc_release_obj(obj);
1483                         goto yuck;
1484                 } 
1485                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1486                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1487                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1488                         ast_odbc_release_obj(obj);
1489                         goto yuck;
1490                 }
1491                 if (!ast_strlen_zero(category)) 
1492                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1493                 else
1494                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1495                 res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
1496                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1497                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1498                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1499                         ast_odbc_release_obj(obj);
1500                         goto yuck;
1501                 }
1502                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1503                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1504                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1505                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1506                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1507                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1508                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1509                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1510                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1511                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1512                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1513                 if (!ast_strlen_zero(category))
1514                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1515                 res = ast_odbc_smart_execute(obj, stmt);
1516                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1517                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1518                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1519                         ast_odbc_release_obj(obj);
1520                         goto yuck;
1521                 }
1522                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1523                 ast_odbc_release_obj(obj);
1524         } else
1525                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1526 yuck:   
1527         if (cfg)
1528                 ast_config_destroy(cfg);
1529         if (fdm != MAP_FAILED)
1530                 munmap(fdm, fdlen);
1531         if (fd > -1)
1532                 close(fd);
1533         return x;
1534 }
1535
1536 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1537 {
1538         SQLHSTMT stmt;
1539         char sql[PATH_MAX];
1540         char msgnums[20];
1541         char msgnumd[20];
1542         struct odbc_obj *obj;
1543         char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
1544         struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
1545
1546         delete_file(ddir, dmsg);
1547         obj = ast_odbc_request_obj(odbc_database, 0);
1548         if (obj) {
1549                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1550                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1551                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1552                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
1553                 if (!stmt)
1554                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1555                 else
1556                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
1557                 ast_odbc_release_obj(obj);
1558         } else
1559                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1560         return; 
1561 }
1562
1563 #else
1564 #ifndef IMAP_STORAGE
1565 static int count_messages(struct ast_vm_user *vmu, char *dir)
1566 {
1567         /* Find all .txt files - even if they are not in sequence from 0000 */
1568
1569         int vmcount = 0;
1570         DIR *vmdir = NULL;
1571         struct dirent *vment = NULL;
1572
1573         if (vm_lock_path(dir))
1574                 return ERROR_LOCK_PATH;
1575
1576         if ((vmdir = opendir(dir))) {
1577                 while ((vment = readdir(vmdir))) {
1578                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1579                                 vmcount++;
1580                 }
1581                 closedir(vmdir);
1582         }
1583         ast_unlock_path(dir);
1584         
1585         return vmcount;
1586 }
1587
1588 static void rename_file(char *sfn, char *dfn)
1589 {
1590         char stxt[PATH_MAX];
1591         char dtxt[PATH_MAX];
1592         ast_filerename(sfn,dfn,NULL);
1593         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1594         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1595         if (ast_check_realtime("voicemail_data")) {
1596                 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
1597         }
1598         rename(stxt, dtxt);
1599 }
1600
1601 static int copy(char *infile, char *outfile)
1602 {
1603         int ifd;
1604         int ofd;
1605         int res;
1606         int len;
1607         char buf[4096];
1608
1609 #ifdef HARDLINK_WHEN_POSSIBLE
1610         /* Hard link if possible; saves disk space & is faster */
1611         if (link(infile, outfile)) {
1612 #endif
1613                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1614                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1615                         return -1;
1616                 }
1617                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1618                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1619                         close(ifd);
1620                         return -1;
1621                 }
1622                 do {
1623                         len = read(ifd, buf, sizeof(buf));
1624                         if (len < 0) {
1625                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1626                                 close(ifd);
1627                                 close(ofd);
1628                                 unlink(outfile);
1629                         }
1630                         if (len) {
1631                                 res = write(ofd, buf, len);
1632                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1633                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1634                                         close(ifd);
1635                                         close(ofd);
1636                                         unlink(outfile);
1637                                 }
1638                         }
1639                 } while (len);
1640                 close(ifd);
1641                 close(ofd);
1642                 return 0;
1643 #ifdef HARDLINK_WHEN_POSSIBLE
1644         } else {
1645                 /* Hard link succeeded */
1646                 return 0;
1647         }
1648 #endif
1649 }
1650
1651 static void copy_file(char *frompath, char *topath)
1652 {
1653         char frompath2[PATH_MAX], topath2[PATH_MAX];
1654         struct ast_variable *tmp,*var = NULL;
1655         char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
1656         ast_filecopy(frompath, topath, NULL);
1657         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1658         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1659         if (ast_check_realtime("voicemail_data")) {
1660                 var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
1661                 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
1662                 for (tmp = var; tmp; tmp = tmp->next) {
1663                         if (!strcasecmp(tmp->name, "origmailbox")) {
1664                                 origmailbox = tmp->value;
1665                         } else if (!strcasecmp(tmp->name, "context")) {
1666                                 context = tmp->value;
1667                         } else if (!strcasecmp(tmp->name, "macrocontext")) {
1668                                 macrocontext = tmp->value;
1669                         } else if (!strcasecmp(tmp->name, "exten")) {
1670                                 exten = tmp->value;
1671                         } else if (!strcasecmp(tmp->name, "priority")) {
1672                                 priority = tmp->value;
1673                         } else if (!strcasecmp(tmp->name, "callerchan")) {
1674                                 callerchan = tmp->value;
1675                         } else if (!strcasecmp(tmp->name, "callerid")) {
1676                                 callerid = tmp->value;
1677                         } else if (!strcasecmp(tmp->name, "origdate")) {
1678                                 origdate = tmp->value;
1679                         } else if (!strcasecmp(tmp->name, "origtime")) {
1680                                 origtime = tmp->value;
1681                         } else if (!strcasecmp(tmp->name, "category")) {
1682                                 category = tmp->value;
1683                         } else if (!strcasecmp(tmp->name, "duration")) {
1684                                 duration = tmp->value;
1685                         }
1686                 }
1687                 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
1688         }
1689         copy(frompath2, topath2);
1690         ast_variables_destroy(var);
1691 }
1692 /*! \brief
1693  * A negative return value indicates an error.
1694  * \note Should always be called with a lock already set on dir.
1695  */
1696 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1697 {
1698         int x;
1699         unsigned char map[MAXMSGLIMIT] = "";
1700         DIR *msgdir;
1701         struct dirent *msgdirent;
1702         int msgdirint;
1703
1704         /* Reading the entire directory into a file map scales better than
1705          * doing a stat repeatedly on a predicted sequence.  I suspect this
1706          * is partially due to stat(2) internally doing a readdir(2) itself to
1707          * find each file. */
1708         msgdir = opendir(dir);
1709         while ((msgdirent = readdir(msgdir))) {
1710                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1711                         map[msgdirint] = 1;
1712         }
1713         closedir(msgdir);
1714
1715         for (x = 0; x < vmu->maxmsg; x++) {
1716                 if (map[x] == 0)
1717                         break;
1718         }
1719
1720         return x - 1;
1721 }
1722
1723 #endif /*#ifndef IMAP_STORAGE*/
1724 #endif /*#else of #ifdef ODBC_STORAGE*/
1725 #ifndef ODBC_STORAGE
1726 static int vm_delete(char *file)
1727 {
1728         char *txt;
1729         int txtsize = 0;
1730
1731         txtsize = (strlen(file) + 5)*sizeof(char);
1732         txt = alloca(txtsize);
1733         /* Sprintf here would safe because we alloca'd exactly the right length,
1734          * but trying to eliminate all sprintf's anyhow
1735          */
1736         if (ast_check_realtime("voicemail_data")) {
1737                 ast_destroy_realtime("voicemail_data", "filename", file, NULL);
1738         }
1739         snprintf(txt, txtsize, "%s.txt", file);
1740         unlink(txt);
1741         return ast_filedelete(file, NULL);
1742 }
1743 #endif
1744
1745 static int inbuf(struct baseio *bio, FILE *fi)
1746 {
1747         int l;
1748
1749         if (bio->ateof)
1750                 return 0;
1751
1752         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1753                 if (ferror(fi))
1754                         return -1;
1755
1756                 bio->ateof = 1;
1757                 return 0;
1758         }
1759
1760         bio->iolen= l;
1761         bio->iocp= 0;
1762
1763         return 1;
1764 }
1765
1766 static int inchar(struct baseio *bio, FILE *fi)
1767 {
1768         if (bio->iocp>=bio->iolen) {
1769                 if (!inbuf(bio, fi))
1770                         return EOF;
1771         }
1772
1773         return bio->iobuf[bio->iocp++];
1774 }
1775
1776 static int ochar(struct baseio *bio, int c, FILE *so)
1777 {
1778         if (bio->linelength >= BASELINELEN) {
1779                 if (fputs(eol,so) == EOF)
1780                         return -1;
1781
1782                 bio->linelength= 0;
1783         }
1784
1785         if (putc(((unsigned char)c),so) == EOF)
1786                 return -1;
1787
1788         bio->linelength++;
1789
1790         return 1;
1791 }
1792
1793 static int base_encode(char *filename, FILE *so)
1794 {
1795         unsigned char dtable[BASEMAXINLINE];
1796         int i,hiteof= 0;
1797         FILE *fi;
1798         struct baseio bio;
1799
1800         memset(&bio, 0, sizeof(bio));
1801         bio.iocp = BASEMAXINLINE;
1802
1803         if (!(fi = fopen(filename, "rb"))) {
1804                 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
1805                 return -1;
1806         }
1807
1808         for (i= 0; i<9; i++) {
1809                 dtable[i]= 'A'+i;
1810                 dtable[i+9]= 'J'+i;
1811                 dtable[26+i]= 'a'+i;
1812                 dtable[26+i+9]= 'j'+i;
1813         }
1814         for (i= 0; i<8; i++) {
1815                 dtable[i+18]= 'S'+i;
1816                 dtable[26+i+18]= 's'+i;
1817         }
1818         for (i= 0; i<10; i++) {
1819                 dtable[52+i]= '0'+i;
1820         }
1821         dtable[62]= '+';
1822         dtable[63]= '/';
1823
1824         while (!hiteof){
1825                 unsigned char igroup[3], ogroup[4];
1826                 int c,n;
1827
1828                 igroup[0]= igroup[1]= igroup[2]= 0;
1829
1830                 for (n= 0;n<3;n++) {
1831                         if ((c = inchar(&bio, fi)) == EOF) {
1832                                 hiteof= 1;
1833                                 break;
1834                         }
1835
1836                         igroup[n]= (unsigned char)c;
1837                 }
1838
1839                 if (n> 0) {
1840                         ogroup[0]= dtable[igroup[0]>>2];
1841                         ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
1842                         ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
1843                         ogroup[3]= dtable[igroup[2]&0x3F];
1844
1845                         if (n<3) {
1846                                 ogroup[3]= '=';
1847
1848                                 if (n<2)
1849                                         ogroup[2]= '=';
1850                         }
1851
1852                         for (i= 0;i<4;i++)
1853                                 ochar(&bio, ogroup[i], so);
1854                 }
1855         }
1856
1857         if (fputs(eol,so) == EOF)
1858                 return 0;
1859
1860         fclose(fi);
1861
1862         return 1;
1863 }
1864
1865 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)
1866 {
1867         char callerid[256];
1868         /* Prepare variables for substitution in email body and subject */
1869         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1870         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1871         snprintf(passdata, passdatasize, "%d", msgnum);
1872         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1873         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1874         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1875         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1876         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1877         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1878         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1879         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1880 }
1881
1882 static char *quote(const char *from, char *to, size_t len)
1883 {
1884         char *ptr = to;
1885         *ptr++ = '"';
1886         for (; ptr < to + len - 1; from++) {
1887                 if (*from == '"')
1888                         *ptr++ = '\\';
1889                 else if (*from == '\0')
1890                         break;
1891                 *ptr++ = *from;
1892         }
1893         if (ptr < to + len - 1)
1894                 *ptr++ = '"';
1895         *ptr = '\0';
1896         return to;
1897 }
1898
1899 /*! \brief
1900  * fill in *tm for current time according to the proper timezone, if any.
1901  * Return tm so it can be used as a function argument.
1902  */
1903 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
1904 {
1905         const struct vm_zone *z = NULL;
1906         struct timeval t = ast_tvnow();
1907
1908         /* Does this user have a timezone specified? */
1909         if (!ast_strlen_zero(vmu->zonetag)) {
1910                 /* Find the zone in the list */
1911                 AST_LIST_LOCK(&zones);
1912                 AST_LIST_TRAVERSE(&zones, z, list) {
1913                         if (!strcmp(z->name, vmu->zonetag))
1914                                 break;
1915                 }
1916                 AST_LIST_UNLOCK(&zones);
1917         }
1918         ast_localtime(&t, tm, z ? z->timezone : NULL);
1919         return tm;
1920 }
1921
1922 /*! \brief same as mkstemp, but return a FILE * */
1923 static FILE *vm_mkftemp(char *template)
1924 {
1925         FILE *p = NULL;
1926         int pfd = mkstemp(template);
1927         chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1928         if (pfd > -1) {
1929                 p = fdopen(pfd, "w+");
1930                 if (!p) {
1931                         close(pfd);
1932                         pfd = -1;
1933                 }
1934         }
1935         return p;
1936 }
1937
1938 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)
1939 {
1940         char date[256];
1941         char host[MAXHOSTNAMELEN] = "";
1942         char who[256];
1943         char bound[256];
1944         char fname[256];
1945         char dur[256];
1946         char tmpcmd[256];
1947         struct ast_tm tm;
1948         char *passdata2;
1949         size_t len_passdata;
1950         char *greeting_attachment;
1951
1952 #ifdef IMAP_STORAGE
1953 #define ENDL "\r\n"
1954 #else
1955 #define ENDL "\n"
1956 #endif
1957
1958         gethostname(host, sizeof(host)-1);
1959         if (strchr(srcemail, '@'))
1960                 ast_copy_string(who, srcemail, sizeof(who));
1961         else 
1962                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1963         
1964         greeting_attachment = strrchr(ast_strdupa(attach), '/');
1965         if (greeting_attachment)
1966                 *greeting_attachment++ = '\0';
1967
1968         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1969         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1970         fprintf(p, "Date: %s" ENDL, date);
1971
1972         /* Set date format for voicemail mail */
1973         ast_strftime(date, sizeof(date), emaildateformat, &tm);
1974
1975         if (!ast_strlen_zero(fromstring)) {
1976                 struct ast_channel *ast;
1977                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
1978                         char *passdata;
1979                         int vmlen = strlen(fromstring)*3 + 200;
1980                         if ((passdata = alloca(vmlen))) {
1981                                 memset(passdata, 0, vmlen);
1982                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1983                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1984                                 len_passdata = strlen(passdata) * 2 + 3;
1985                                 passdata2 = alloca(len_passdata);
1986                                 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
1987                         } else
1988                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1989                         ast_channel_free(ast);
1990                 } else
1991                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1992         } else
1993                 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
1994         len_passdata = strlen(vmu->fullname) * 2 + 3;
1995         passdata2 = alloca(len_passdata);
1996         fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
1997         if (!ast_strlen_zero(emailsubject)) {
1998                 struct ast_channel *ast;
1999                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2000                         char *passdata;
2001                         int vmlen = strlen(emailsubject) * 3 + 200;
2002                         if ((passdata = alloca(vmlen))) {
2003                                 memset(passdata, 0, vmlen);
2004                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2005                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
2006                                 fprintf(p, "Subject: %s" ENDL, passdata);
2007                         } else
2008                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2009                         ast_channel_free(ast);
2010                 } else
2011                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2012         } else  if (!ast_strlen_zero(emailtitle)) {
2013                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
2014                 fprintf(p, ENDL) ;
2015         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
2016                 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2017         else
2018                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
2019         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
2020         if(imap) {
2021                 /* additional information needed for IMAP searching */
2022                 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
2023                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
2024                 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
2025                 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
2026                 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
2027                 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
2028                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
2029                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, cidnum);
2030                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, cidname);
2031                 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
2032                 if (!ast_strlen_zero(category)) 
2033                         fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
2034                 fprintf(p, "X-Asterisk-VM-Message-Type: %s\n", msgnum > -1 ? "Message" : greeting_attachment);
2035                 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
2036                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
2037         }
2038         if (!ast_strlen_zero(cidnum))
2039                 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, cidnum);
2040         if (!ast_strlen_zero(cidname))
2041                 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, cidname);
2042         fprintf(p, "MIME-Version: 1.0" ENDL);
2043         if (attach_user_voicemail) {
2044                 /* Something unique. */
2045                 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
2046
2047                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
2048                 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
2049                 fprintf(p, "--%s" ENDL, bound);
2050         }
2051         fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
2052         if (emailbody) {
2053                 struct ast_channel *ast;
2054                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2055                         char *passdata;
2056                         int vmlen = strlen(emailbody)*3 + 200;
2057                         if ((passdata = alloca(vmlen))) {
2058                                 memset(passdata, 0, vmlen);
2059                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2060                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
2061                                 fprintf(p, "%s" ENDL, passdata);
2062                         } else
2063                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2064                         ast_channel_free(ast);
2065                 } else
2066                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2067         } else if (msgnum > -1){
2068                 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
2069
2070                 "in mailbox %s from %s, on %s so you might" ENDL
2071                 "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, 
2072                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
2073         } else {
2074                 fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
2075                                 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
2076         }
2077         if (attach_user_voicemail) {
2078                 /* Eww. We want formats to tell us their own MIME type */
2079                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
2080                 char tmpdir[256], newtmp[256];
2081                 int tmpfd = -1;
2082         
2083                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
2084                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
2085                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
2086                         tmpfd = mkstemp(newtmp);
2087                         chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
2088                         ast_debug(3, "newtmp: %s\n", newtmp);
2089                         if (tmpfd > -1) {
2090                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
2091                                 ast_safe_system(tmpcmd);
2092                                 attach = newtmp;
2093                                 ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
2094                         }
2095                 }
2096                 fprintf(p, "--%s" ENDL, bound);
2097                 if (msgnum > -1)
2098                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
2099                 else
2100                         fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
2101                 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
2102                 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
2103                 if (msgnum > -1)
2104                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
2105                 else
2106                         fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
2107                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2108                 base_encode(fname, p);
2109                 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
2110                 if (tmpfd > -1) {
2111                         unlink(fname);
2112                         close(tmpfd);
2113                         unlink(newtmp);
2114                 }
2115         }
2116 #undef ENDL
2117 }
2118
2119 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)
2120 {
2121         FILE *p=NULL;
2122         char tmp[80] = "/tmp/astmail-XXXXXX";
2123         char tmp2[256];
2124
2125         if (vmu && ast_strlen_zero(vmu->email)) {
2126                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2127                 return(0);
2128         }
2129         if (!strcmp(format, "wav49"))
2130                 format = "WAV";
2131         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));
2132         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2133            command hangs */
2134         if ((p = vm_mkftemp(tmp)) == NULL) {
2135                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2136                 return -1;
2137         } else {
2138                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2139                 fclose(p);
2140                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2141                 ast_safe_system(tmp2);
2142                 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2143         }
2144         return 0;
2145 }
2146
2147 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)
2148 {
2149         char date[256];
2150         char host[MAXHOSTNAMELEN] = "";
2151         char who[256];
2152         char dur[PATH_MAX];
2153         char tmp[80] = "/tmp/astmail-XXXXXX";
2154         char tmp2[PATH_MAX];
2155         struct ast_tm tm;
2156         FILE *p;
2157
2158         if ((p = vm_mkftemp(tmp)) == NULL) {
2159                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2160                 return -1;
2161         }
2162         gethostname(host, sizeof(host)-1);
2163         if (strchr(srcemail, '@'))
2164                 ast_copy_string(who, srcemail, sizeof(who));
2165         else 
2166                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2167         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2168         ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2169         fprintf(p, "Date: %s\n", date);
2170
2171         if (*pagerfromstring) {
2172                 struct ast_channel *ast;
2173                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2174                         char *passdata;
2175                         int vmlen = strlen(fromstring)*3 + 200;
2176                         if ((passdata = alloca(vmlen))) {
2177                                 memset(passdata, 0, vmlen);
2178                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2179                                 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2180                                 fprintf(p, "From: %s <%s>\n", passdata, who);
2181                         } else 
2182                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2183                         ast_channel_free(ast);
2184                 } else 
2185                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2186         } else
2187                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2188         fprintf(p, "To: %s\n", pager);
2189         if (pagersubject) {
2190                 struct ast_channel *ast;
2191                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2192                         char *passdata;
2193                         int vmlen = strlen(pagersubject) * 3 + 200;
2194                         if ((passdata = alloca(vmlen))) {
2195                                 memset(passdata, 0, vmlen);
2196                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2197                                 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2198                                 fprintf(p, "Subject: %s\n\n", passdata);
2199                         } else
2200                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2201                         ast_channel_free(ast);
2202                 } else
2203                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2204         } else
2205                 fprintf(p, "Subject: New VM\n\n");
2206
2207         ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2208         if (pagerbody) {
2209                 struct ast_channel *ast;
2210                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
2211                         char *passdata;
2212                         int vmlen = strlen(pagerbody) * 3 + 200;
2213                         passdata = alloca(vmlen);
2214                         memset(passdata, 0, vmlen);
2215                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2216                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2217                         fprintf(p, "%s\n", passdata);
2218                         ast_channel_free(ast);
2219                 } else
2220                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2221         } else {
2222                 fprintf(p, "New %s long msg in box %s\n"
2223                                 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2224         }
2225         fclose(p);
2226         snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2227         ast_safe_system(tmp2);
2228         ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
2229         return 0;
2230 }
2231
2232 static int get_date(char *s, int len)
2233 {
2234         struct ast_tm tm;
2235         struct timeval t = ast_tvnow();
2236         ast_localtime(&t, &tm, NULL);
2237         return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2238 }
2239
2240 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2241 {
2242         int res;
2243         char fn[PATH_MAX];
2244         char dest[PATH_MAX];
2245
2246         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2247
2248         if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
2249                 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
2250                 return -1;
2251         }
2252
2253         RETRIEVE(fn, -1, ext, context);
2254         if (ast_fileexists(fn, NULL, NULL) > 0) {
2255                 res = ast_stream_and_wait(chan, fn, ecodes);
2256                 if (res) {
2257                         DISPOSE(fn, -1);
2258                         return res;
2259                 }
2260         } else {
2261                 /* Dispose just in case */
2262                 DISPOSE(fn, -1);
2263                 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
2264                 if (res)
2265                         return res;
2266                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2267                 if (res)
2268                         return res;
2269         }
2270         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
2271         return res;
2272 }
2273
2274 static void free_user(struct ast_vm_user *vmu)
2275 {
2276         if (!ast_test_flag(vmu, VM_ALLOCED))
2277                 return;
2278
2279         ast_free(vmu);
2280 }
2281
2282 static void free_zone(struct vm_zone *z)
2283 {
2284         ast_free(z);
2285 }
2286
2287 static const char *mbox(int id)
2288 {
2289         static const char *msgs[] = {
2290                 "INBOX",
2291                 "Old",
2292                 "Work",
2293                 "Family",
2294                 "Friends",
2295                 "Cust1",
2296                 "Cust2",
2297                 "Cust3",
2298                 "Cust4",
2299                 "Cust5",
2300         };
2301         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2302 }
2303 #ifdef IMAP_STORAGE
2304 static int folder_int(const char *folder)
2305 {
2306         /*assume a NULL folder means INBOX*/
2307         if (!folder)
2308                 return 0;
2309         if(!strcasecmp(folder, "INBOX"))
2310                 return 0;
2311         else if (!strcasecmp(folder, "Old"))
2312                 return 1;
2313         else if (!strcasecmp(folder, "Work"))
2314                 return 2;
2315         else if (!strcasecmp(folder, "Family"))
2316                 return 3;
2317         else if (!strcasecmp(folder, "Friends"))
2318                 return 4;
2319         else if (!strcasecmp(folder, "Cust1"))
2320                 return 5;
2321         else if (!strcasecmp(folder, "Cust2"))
2322                 return 6;
2323         else if (!strcasecmp(folder, "Cust3"))
2324                 return 7;
2325         else if (!strcasecmp(folder, "Cust4"))
2326                 return 8;
2327         else if (!strcasecmp(folder, "Cust5"))
2328                 return 9;
2329         else /*assume they meant INBOX if folder is not found otherwise*/
2330                 return 0;
2331 }
2332 #endif
2333
2334 #ifdef ODBC_STORAGE
2335 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2336 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2337 {
2338         int x = -1;
2339         int res;
2340         SQLHSTMT stmt;
2341         char sql[PATH_MAX];
2342         char rowdata[20];
2343         char tmp[PATH_MAX] = "";
2344         struct odbc_obj *obj;
2345         char *context;
2346         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2347
2348         if (newmsgs)
2349                 *newmsgs = 0;
2350         if (oldmsgs)
2351                 *oldmsgs = 0;
2352
2353         /* If no mailbox, return immediately */
2354         if (ast_strlen_zero(mailbox))
2355                 return 0;
2356
2357         ast_copy_string(tmp, mailbox, sizeof(tmp));
2358         
2359         context = strchr(tmp, '@');
2360         if (context) {
2361                 *context = '\0';
2362                 context++;
2363         } else
2364                 context = "default";
2365         
2366         obj = ast_odbc_request_obj(odbc_database, 0);
2367         if (obj) {
2368                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2369                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2370                 if (!stmt) {
2371                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2372                         ast_odbc_release_obj(obj);
2373                         goto yuck;
2374                 }
2375                 res = SQLFetch(stmt);
2376                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2377                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2378                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2379                         ast_odbc_release_obj(obj);
2380                         goto yuck;
2381                 }
2382                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2383                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2384                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2385                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2386                         ast_odbc_release_obj(obj);
2387                         goto yuck;
2388                 }
2389                 *newmsgs = atoi(rowdata);
2390                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2391
2392                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2393                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2394                 if (!stmt) {
2395                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2396                         ast_odbc_release_obj(obj);
2397                         goto yuck;
2398                 }
2399                 res = SQLFetch(stmt);
2400                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2401                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2402                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2403                         ast_odbc_release_obj(obj);
2404                         goto yuck;
2405                 }
2406                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2407                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2408                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2409                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2410                         ast_odbc_release_obj(obj);
2411                         goto yuck;
2412                 }
2413                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2414                 ast_odbc_release_obj(obj);
2415                 *oldmsgs = atoi(rowdata);
2416                 x = 0;
2417         } else
2418                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2419                 
2420 yuck:   
2421         return x;
2422 }
2423
2424 static int messagecount(const char *context, const char *mailbox, const char *folder)
2425 {
2426         struct odbc_obj *obj = NULL;
2427         int nummsgs = 0;
2428         int res;
2429         SQLHSTMT stmt = NULL;
2430         char sql[PATH_MAX];
2431         char rowdata[20];
2432         struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
2433         if (!folder)
2434                 folder = "INBOX";
2435         /* If no mailbox, return immediately */
2436         if (ast_strlen_zero(mailbox))
2437                 return 0;
2438
2439         obj = ast_odbc_request_obj(odbc_database, 0);
2440         if (obj) {
2441                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2442                 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2443                 if (!stmt) {
2444                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2445                         goto yuck;
2446                 }
2447                 res = SQLFetch(stmt);
2448                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2449                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2450                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2451                         goto yuck;
2452                 }
2453                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2454                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2455                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2456                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2457                         goto yuck;
2458                 }
2459                 nummsgs = atoi(rowdata);
2460                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2461         } else
2462                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2463
2464 yuck:
2465         if (obj)
2466                 ast_odbc_release_obj(obj);
2467         return nummsgs;
2468 }
2469
2470 static int has_voicemail(const char *mailbox, const char *folder)
2471 {
2472         char tmp[256], *tmp2 = tmp, *mbox, *context;
2473         ast_copy_string(tmp, mailbox, sizeof(tmp));
2474         while ((context = mbox = strsep(&tmp2, ","))) {
2475                 strsep(&context, "@");
2476                 if (ast_strlen_zero(context))
2477                         context = "default";
2478                 if (messagecount(context, mbox, folder))
2479                         return 1;
2480         }
2481         return 0;
2482 }
2483
2484 #elif defined(IMAP_STORAGE)
2485
2486 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)
2487 {
2488         char *myserveremail = serveremail;
2489         char fn[PATH_MAX];
2490         char mailbox[256];
2491         char *stringp;
2492         FILE *p=NULL;
2493         char tmp[80] = "/tmp/astmail-XXXXXX";
2494         long len;
2495         void *buf;
2496         STRING str;
2497         
2498         /* Attach only the first format */
2499         fmt = ast_strdupa(fmt);
2500         stringp = fmt;
2501         strsep(&stringp, "|");
2502
2503         if (!ast_strlen_zero(vmu->serveremail))
2504                 myserveremail = vmu->serveremail;
2505
2506         if (msgnum > -1)
2507                 make_file(fn, sizeof(fn), dir, msgnum);
2508         else
2509                 ast_copy_string (fn, dir, sizeof(fn));
2510         
2511         if (ast_strlen_zero(vmu->email))
2512                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2513
2514         if (!strcmp(fmt, "wav49"))
2515                 fmt = "WAV";
2516         ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2517
2518         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2519            command hangs */
2520         if (!(p = vm_mkftemp(tmp))) {
2521                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2522                 return -1;
2523
2524         }
2525
2526         if (msgnum < 0 && imapgreetings) {
2527                 init_mailstream(vms, GREETINGS_FOLDER);
2528                 imap_delete_old_greeting(fn, vms);
2529         }
2530         
2531         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);
2532         /* read mail file to memory */          
2533         len = ftell(p);
2534         rewind(p);
2535         if (!(buf = ast_malloc(len+1))) {
2536                 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2537                 return -1;
2538         }
2539         fread(buf, len, 1, p);
2540         ((char *)buf)[len] = '\0';
2541         INIT(&str, mail_string, buf, len);
2542         init_mailstream(vms, NEW_FOLDER);
2543         imap_mailbox_name(mailbox, vms, NEW_FOLDER, 1);
2544         if(!mail_append(vms->mailstream, mailbox, &str))
2545                 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2546         fclose(p);
2547         unlink(tmp);
2548         ast_free(buf);
2549         ast_debug(3, "%s stored\n", fn);
2550         return 0;
2551
2552 }
2553
2554 static int messagecount(const char *context, const char *mailbox, const char *folder)
2555 {
2556         SEARCHPGM *pgm;
2557         SEARCHHEADER *hdr;
2558  
2559         struct ast_vm_user *vmu, vmus;
2560         struct vm_state *vms_p;
2561         int ret = 0;
2562         int fold = folder_int(folder);
2563         
2564         if (ast_strlen_zero(mailbox))
2565                 return 0;
2566
2567         /* We have to get the user before we can open the stream! */
2568         /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2569         vmu = find_user(&vmus, context, mailbox);
2570         if (!vmu) {
2571                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
2572                 return -1;
2573         } else {
2574                 /* No IMAP account available */
2575                 if (vmu->imapuser[0] == '\0') {
2576                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2577                         return -1;
2578                 }
2579         }
2580         
2581         /* No IMAP account available */
2582         if (vmu->imapuser[0] == '\0') {
2583                 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2584                 free_user(vmu);
2585                 return -1;
2586         }
2587
2588         /* check if someone is accessing this box right now... */
2589         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2590         if (!vms_p) {
2591                 vms_p = get_vm_state_by_mailbox(mailbox,1);
2592         }
2593         if (vms_p) {
2594                 ast_debug(3, "Returning before search - user is logged in\n");
2595                 if(fold == 0) {/*INBOX*/
2596                         return vms_p->newmessages;
2597                 }
2598                 if(fold == 1) {/*Old messages*/
2599                         return vms_p->oldmessages;
2600                 }
2601         }
2602
2603         /* add one if not there... */
2604         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2605         if (!vms_p) {
2606                 vms_p = get_vm_state_by_mailbox(mailbox,0);
2607         }
2608
2609         if (!vms_p) {
2610                 ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
2611                 if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
2612                         return -1;
2613                 }
2614                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2615                 ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2616                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2617                 ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2618                 vms_p->updated = 1;
2619                 /* set mailbox to INBOX! */
2620                 ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
2621                 init_vm_state(vms_p);
2622                 vmstate_insert(vms_p);
2623         }
2624         ret = init_mailstream(vms_p, fold);
2625         if (!vms_p->mailstream) {
2626                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2627                 return -1;
2628         }
2629         if (ret == 0) {
2630                 pgm = mail_newsearchpgm ();
2631                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
2632                 pgm->header = hdr;
2633                 if (fold != 1) {
2634                         pgm->unseen = 1;
2635                         pgm->seen = 0;
2636                 }
2637                 /* In the special case where fold is 1 (old messages) we have to do things a bit
2638                  * differently. Old messages are stored in the INBOX but are marked as "seen"
2639                  */
2640                 else {
2641                         pgm->unseen = 0;
2642                         pgm->seen = 1;
2643                 }
2644                 pgm->undeleted = 1;
2645                 pgm->deleted = 0;
2646
2647                 vms_p->vmArrayIndex = 0;
2648                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2649                 if(fold == 0)
2650                         vms_p->newmessages = vms_p->vmArrayIndex;
2651                 if(fold == 1)
2652                         vms_p->oldmessages = vms_p->vmArrayIndex;
2653                 /*Freeing the searchpgm also frees the searchhdr*/
2654                 mail_free_searchpgm(&pgm);
2655                 vms_p->updated = 0;
2656                 return vms_p->vmArrayIndex;
2657         } else {  
2658                 mail_ping(vms_p->mailstream);
2659         }
2660         return 0;
2661 }
2662 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2663 {
2664         char tmp[PATH_MAX] = "";
2665         char *mailboxnc;        
2666         char *context;
2667         char *mb;
2668         char *cur;
2669         if (newmsgs)
2670                 *newmsgs = 0;
2671         if (oldmsgs)
2672                 *oldmsgs = 0;
2673
2674         ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2675         /* If no mailbox, return immediately */
2676         if (ast_strlen_zero(mailbox_context))
2677                 return 0;
2678         
2679         ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2680         context = strchr(tmp, '@');
2681         if (strchr(mailbox_context, ',')) {
2682                 int tmpnew, tmpold;
2683                 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2684                 mb = tmp;
2685                 while((cur = strsep(&mb, ", "))) {
2686                         if (!ast_strlen_zero(cur)) {
2687                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2688                                         return -1;
2689                                 else {
2690                                         if (newmsgs)
2691                                                 *newmsgs += tmpnew; 
2692                                         if (oldmsgs)
2693                                                 *oldmsgs += tmpold;
2694                                 }
2695                         }
2696                 }
2697                 return 0;
2698         }
2699         if (context) {
2700                 *context = '\0';
2701                 mailboxnc = tmp;
2702                 context++;
2703         } else {
2704                 context = "default";
2705                 mailboxnc = (char *)mailbox_context;
2706         }
2707         if (newmsgs) {
2708                 if((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
2709                         return -1;
2710         }
2711         if (oldmsgs) {
2712                 if((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2713                         return -1;
2714         }
2715         return 0;
2716 }
2717         
2718
2719 static int has_voicemail(const char *mailbox, const char *folder)
2720 {
2721         char tmp[256], *tmp2, *mbox, *context;
2722         ast_copy_string(tmp, mailbox, sizeof(tmp));
2723         tmp2 = tmp;
2724         if(strchr(tmp2, ',')) {
2725                 while((mbox = strsep(&tmp2, ","))) {
2726                         if(!ast_strlen_zero(mbox)) {
2727                                 if (has_voicemail(mbox, folder))
2728                                         return 1;
2729                         }
2730                 }
2731         }
2732         if ((context= strchr(tmp, '@')))
2733                 *context++ = '\0';
2734         else
2735                 context = "default";
2736         return messagecount(context, tmp, folder) ? 1 : 0;
2737 }
2738
2739 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)
2740 {
2741         char dest[256];
2742         struct vm_state *sendvms = NULL, *destvms = NULL;
2743         char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2744         if(!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 2)))
2745         {
2746                 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2747                 return -1;
2748         }
2749         if(!(destvms = get_vm_state_by_imapuser(recip->imapuser, 2)))
2750         {
2751                 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2752                 return -1;
2753         }
2754         imap_mailbox_name(dest, destvms, imbox, 1);
2755         snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2756         if((mail_copy(sendvms->mailstream, messagestring, dest) == T))
2757                 return 0;
2758         ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2759         return -1;
2760 }
2761
2762 #endif
2763 #ifndef IMAP_STORAGE
2764 /* copy message only used by file storage */
2765 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)
2766 {
2767         char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
2768         const char *frombox = mbox(imbox);
2769         int recipmsgnum;
2770
2771         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2772
2773         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2774         
2775         if (!dir)
2776                 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2777         else
2778                 ast_copy_string(fromdir, dir, sizeof(fromdir));
2779
2780         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2781         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2782
2783         if (vm_lock_path(todir))
2784                 return ERROR_LOCK_PATH;
2785
2786         recipmsgnum = last_message_index(recip, todir) + 1;
2787         if (recipmsgnum < recip->maxmsg) {
2788                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2789                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2790         } else {
2791                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2792         }
2793         ast_unlock_path(todir);
2794         notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
2795         
2796         return 0;
2797 }
2798 #endif
2799 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2800 static int messagecount(const char *context, const char *mailbox, const char *folder)
2801 {
2802         return __has_voicemail(context, mailbox, folder, 0);
2803 }
2804
2805
2806 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2807 {
2808         DIR *dir;
2809         struct dirent *de;
2810         char fn[256];
2811         int ret = 0;
2812
2813         /* If no mailbox, return immediately */
2814         if (ast_strlen_zero(mailbox))
2815                 return 0;
2816
2817         if (ast_strlen_zero(folder))
2818                 folder = "INBOX";
2819         if (ast_strlen_zero(context))
2820                 context = "default";
2821
2822         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2823
2824         if (!(dir = opendir(fn)))
2825                 return 0;
2826
2827         while ((de = readdir(dir))) {
2828                 if (!strncasecmp(de->d_name, "msg", 3)) {
2829                         if (shortcircuit) {
2830                                 ret = 1;
2831                                 break;
2832                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2833                                 ret++;
2834                 }
2835         }
2836
2837         closedir(dir);
2838
2839         return ret;
2840 }
2841
2842
2843 static int has_voicemail(const char *mailbox, const char *folder)
2844 {
2845         char tmp[256], *tmp2 = tmp, *mbox, *context;
2846         ast_copy_string(tmp, mailbox, sizeof(tmp));
2847         while ((mbox = strsep(&tmp2, ","))) {
2848                 if ((context = strchr(mbox, '@')))
2849                         *context++ = '\0';
2850                 else
2851                         context = "default";
2852                 if (__has_voicemail(context, mbox, folder, 1))
2853                         return 1;
2854         }
2855         return 0;
2856 }
2857
2858
2859 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2860 {
2861         char tmp[256];
2862         char *context;
2863
2864         /* If no mailbox, return immediately */
2865         if (ast_strlen_zero(mailbox))
2866                 return 0;
2867
2868         if (newmsgs)
2869                 *newmsgs = 0;
2870         if (oldmsgs)
2871                 *oldmsgs = 0;
2872
2873         if (strchr(mailbox, ',')) {
2874                 int tmpnew, tmpold;
2875                 char *mb, *cur;
2876
2877                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2878                 mb = tmp;
2879                 while ((cur = strsep(&mb, ", "))) {
2880                         if (!ast_strlen_zero(cur)) {
2881                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2882                                         return -1;
2883                                 else {
2884                                         if (newmsgs)
2885                                                 *newmsgs += tmpnew; 
2886                                         if (oldmsgs)
2887                                                 *oldmsgs += tmpold;
2888                                 }
2889                         }
2890                 }
2891                 return 0;
2892         }
2893
2894         ast_copy_string(tmp, mailbox, sizeof(tmp));
2895         
2896         if ((context = strchr(tmp, '@')))
2897                 *context++ = '\0';
2898         else
2899                 context = "default";
2900
2901         if (newmsgs)
2902                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2903         if (oldmsgs)
2904                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2905
2906         return 0;
2907 }
2908
2909 #endif
2910
2911 static void run_externnotify(char *context, char *extension)
2912 {
2913         char arguments[255];
2914         char ext_context[256] = "";
2915         int newvoicemails = 0, oldvoicemails = 0;
2916         struct ast_smdi_mwi_message *mwi_msg;
2917
2918         if (!ast_strlen_zero(context))
2919                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2920         else
2921                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2922
2923         if (smdi_iface) {
2924                 if (ast_app_has_voicemail(ext_context, NULL)) 
2925                         ast_smdi_mwi_set(smdi_iface, extension);
2926                 else
2927                         ast_smdi_mwi_unset(smdi_iface, extension);
2928
2929                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2930                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2931                         if (!strncmp(mwi_msg->cause, "INV", 3))
2932                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2933                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2934                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2935                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2936                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2937                 } else {
2938                         ast_debug(1, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2939                 }
2940         }
2941
2942         if (!ast_strlen_zero(externnotify)) {
2943                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2944                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2945                 } else {
2946                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2947                         ast_debug(1, "Executing %s\n", arguments);
2948                         ast_safe_system(arguments);
2949                 }
2950         }
2951 }
2952
2953 struct leave_vm_options {
2954         unsigned int flags;
2955         signed char record_gain;
2956 };
2957
2958 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2959 {
2960 #ifdef IMAP_STORAGE
2961         int newmsgs, oldmsgs;
2962         struct vm_state *vms = NULL;
2963 #endif
2964         char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
2965         char callerid[256];
2966         FILE *txt;
2967         char date[256];
2968         int txtdes;
2969         int res = 0;
2970         int msgnum;
2971         int duration = 0;
2972         int ausemacro = 0;
2973         int ousemacro = 0;
2974         int ouseexten = 0;
2975         int rtmsgid = 0;
2976         char tmpid[16];
2977         char tmpdur[16];
2978         char priority[16];
2979         char origtime[16];
2980         char dir[PATH_MAX], tmpdir[PATH_MAX];
2981         char fn[PATH_MAX];
2982         char prefile[PATH_MAX] = "";
2983         char tempfile[PATH_MAX] = "";
2984         char ext_context[256] = "";
2985         char fmt[80];
2986         char *context;
2987         char ecodes[16] = "#";
2988         char tmp[1024] = "", *tmpptr;
2989         struct ast_vm_user *vmu;
2990         struct ast_vm_user svm;
2991         const char *category = NULL;
2992
2993         ast_copy_string(tmp, ext, sizeof(tmp));
2994         ext = tmp;
2995         if ((context = strchr(tmp, '@'))) {
2996                 *context++ = '\0';
2997                 tmpptr = strchr(context, '&');
2998         } else {
2999                 tmpptr = strchr(ext, '&');
3000         }
3001
3002         if (tmpptr)
3003                 *tmpptr++ = '\0';
3004
3005         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3006
3007         ast_debug(3, "Before find_user\n");
3008         if (!(vmu = find_user(&svm, context, ext))) {
3009                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3010                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3011                 return res;
3012         }
3013         /* Setup pre-file if appropriate */
3014         if (strcmp(vmu->context, "default"))
3015                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3016         else
3017                 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3018         if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3019                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3020         } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3021                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3022         }
3023         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3024         if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
3025                 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3026                 return -1;
3027         }
3028         RETRIEVE(tempfile, -1, ext, context);
3029         if (ast_fileexists(tempfile, NULL, NULL) > 0)
3030                 ast_copy_string(prefile, tempfile, sizeof(prefile));
3031         DISPOSE(tempfile, -1);
3032         /* It's easier just to try to make it than to check for its existence */
3033         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3034
3035         /* Check current or macro-calling context for special extensions */
3036         if (ast_test_flag(vmu, VM_OPERATOR)) {
3037                 if (!ast_strlen_zero(vmu->exit)) {
3038                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3039                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3040                                 ouseexten = 1;
3041                         }
3042                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3043                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3044                         ouseexten = 1;
3045                 }
3046                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3047                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3048                 ousemacro = 1;
3049                 }
3050         }
3051
3052         if (!ast_strlen_zero(vmu->exit)) {
3053                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3054                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3055         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3056                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3057         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3058                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3059                 ausemacro = 1;
3060         }
3061
3062         /* Play the beginning intro if desired */
3063         if (!ast_strlen_zero(prefile)) {
3064 #ifdef ODBC_STORAGE
3065                 int success = 
3066 #endif
3067                         RETRIEVE(prefile, -1, ext, context);
3068                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
3069                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
3070                                 res = ast_waitstream(chan, ecodes);
3071 #ifdef ODBC_STORAGE
3072                         if (success == -1) {
3073                                 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3074                                 ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3075                                 store_file(prefile, vmu->mailbox, vmu->context, -1);
3076                         }
3077 #endif
3078                 } else {
3079                         ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
3080                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3081                 }
3082                 DISPOSE(prefile, -1);
3083                 if (res < 0) {
3084                         ast_debug(1, "Hang up during prefile playback\n");
3085                         free_user(vmu);
3086                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3087                         return -1;
3088                 }
3089         }
3090         if (res == '#') {
3091                 /* On a '#' we skip the instructions */
3092                 ast_set_flag(options, OPT_SILENT);
3093                 res = 0;
3094         }
3095         if (!res && !ast_test_flag(options, OPT_SILENT)) {
3096                 res = ast_stream_and_wait(chan, INTRO, ecodes);
3097                 if (res == '#') {
3098                         ast_set_flag(options, OPT_SILENT);
3099                         res = 0;
3100                 }
3101         }
3102         if (res > 0)
3103                 ast_stopstream(chan);
3104         /* Check for a '*' here in case the caller wants to escape from voicemail to something
3105          other than the operator -- an automated attendant or mailbox login for example */
3106         if (res == '*') {
3107                 chan->exten[0] = 'a';
3108                 chan->exten[1] = '\0';
3109                 if (!ast_strlen_zero(vmu->exit)) {
3110                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3111                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3112                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3113                 }
3114                 chan->priority = 0;
3115                 free_user(vmu);
3116                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3117                 return 0;
3118         }
3119
3120         /* Check for a '0' here */
3121         if (res == '0') {
3122         transfer:
3123                 if (ouseexten || ousemacro) {
3124                         chan->exten[0] = 'o';
3125                         chan->exten[1] = '\0';
3126                         if (!ast_strlen_zero(vmu->exit)) {
3127                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3128                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3129                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3130                         }
3131                         ast_play_and_wait(chan, "transfer");
3132                         chan->priority = 0;
3133                         free_user(vmu);
3134                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3135                 }
3136                 return 0;
3137         }
3138         if (res < 0) {
3139                 free_user(vmu);
3140                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3141                 return -1;
3142         }
3143         /* The meat of recording the message...  All the announcements and beeps have been played*/
3144         ast_copy_string(fmt, vmfmts, sizeof(fmt));
3145         if (!ast_strlen_zero(fmt)) {
3146                 msgnum = 0;
3147
3148 #ifdef IMAP_STORAGE
3149                 /* Is ext a mailbox? */
3150                 /* must open stream for this user to get info! */
3151                 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3152                 if(res < 0) {
3153                         ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3154                         return -1;
3155                 }
3156                 if(!(vms = get_vm_state_by_mailbox(ext,0))) {
3157                 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3158                  * rarely be used*/
3159                         if (!(vms = ast_calloc(1, sizeof(*vms)))) {
3160                                 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3161                                 return -1;
3162                         }
3163                         ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3164                         ast_copy_string(vms->username, ext, sizeof(vms->username));
3165                         vms->mailstream = NIL;
3166                         ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
3167                         vms->updated=1;
3168                         ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
3169                         init_vm_state(vms);
3170                         vmstate_insert(vms);
3171                         vms = get_vm_state_by_mailbox(ext,0);
3172                 }
3173                 vms->newmessages++;
3174                 
3175                 /* here is a big difference! We add one to it later */
3176                 msgnum = newmsgs + oldmsgs;
3177                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3178                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3179                 /* set variable for compatibility */
3180                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3181
3182                 /* Check if mailbox is full */
3183                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3184                         ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3185                         ast_play_and_wait(chan, "vm-mailboxfull");
3186                         return -1;
3187                 }
3188                 /* here is a big difference! We add one to it later */
3189                 ast_debug(3, "Messagecount set to %d\n",msgnum);
3190
3191 #else
3192                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3193                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3194                         if (!res)
3195                                 res = ast_waitstream(chan, "");
3196                         ast_log(LOG_WARNING, "No more messages possible\n");
3197                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3198                         goto leave_vm_out;
3199                 }
3200
3201 #endif
3202                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3203                 txtdes = mkstemp(tmptxtfile);
3204                 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3205                 if (txtdes < 0) {
3206                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3207                         if (!res)
3208                                 res = ast_waitstream(chan, "");
3209                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3210                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3211                         goto leave_vm_out;
3212                 }
3213
3214                 /* Now play the beep once we have the message number for our next message. */
3215                 if (res >= 0) {
3216                         /* Unless we're *really* silent, try to send the beep */
3217                         res = ast_stream_and_wait(chan, "beep", "");
3218                 }
3219                                 
3220                 /* Store information in real-time storage */
3221                 if (ast_check_realtime("voicemail_data")) {
3222                         snprintf(priority, sizeof(priority), "%d", chan->priority);
3223                         snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
3224                         get_date(date, sizeof(date));
3225                         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);
3226                 }
3227
3228                 /* Store information */
3229                 txt = fdopen(txtdes, "w+");
3230                 if (txt) {
3231                         get_date(date, sizeof(date));
3232                         fprintf(txt, 
3233                                 ";\n"
3234                                 "; Message Information file\n"
3235                                 ";\n"
3236                                 "[message]\n"
3237                                 "origmailbox=%s\n"
3238                                 "context=%s\n"
3239                                 "macrocontext=%s\n"
3240                                 "exten=%s\n"
3241                                 "priority=%d\n"
3242                                 "callerchan=%s\n"
3243                                 "callerid=%s\n"
3244                                 "origdate=%s\n"
3245                                 "origtime=%ld\n"
3246                                 "category=%s\n",
3247                                 ext,
3248                                 chan->context,
3249                                 chan->macrocontext, 
3250                                 chan->exten,
3251                                 chan->priority,
3252                                 chan->name,
3253                                 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3254                                 date, (long)time(NULL),
3255                                 category ? category : ""); 
3256                 } else
3257                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3258 #ifdef IMAP_STORAGE
3259                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3260 #else
3261                 res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL);
3262 #endif
3263
3264                 if (txt) {
3265                         if (duration < vmminsecs) {
3266                                 if (option_verbose > 2) 
3267                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
3268                                 ast_filedelete(tmptxtfile, NULL);
3269                                 unlink(tmptxtfile);
3270                                 if (ast_check_realtime("voicemail_data")) {
3271                                         snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3272                                         ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3273                                 }
3274                         } else {
3275                                 fprintf(txt, "duration=%d\n", duration);
3276                                 fclose(txt);
3277                                 if (vm_lock_path(dir)) {
3278                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3279                                         /* Delete files */
3280                                         ast_filedelete(tmptxtfile, NULL);
3281                                         unlink(tmptxtfile);
3282                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3283                                         ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
3284                                         unlink(tmptxtfile);
3285                                         ast_unlock_path(dir);
3286                                         if (ast_check_realtime("voicemail_data")) {
3287                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3288                                                 ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
3289                                         }
3290                                 } else {
3291 #ifndef IMAP_STORAGE
3292                                         msgnum = last_message_index(vmu, dir) + 1;
3293 #endif
3294                                         make_file(fn, sizeof(fn), dir, msgnum);
3295
3296                                         /* assign a variable with the name of the voicemail file */ 
3297 #ifndef IMAP_STORAGE
3298                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3299 #else
3300                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3301 #endif
3302
3303                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3304                                         ast_filerename(tmptxtfile, fn, NULL);
3305                                         rename(tmptxtfile, txtfile);
3306
3307                                         /* Properly set permissions on voicemail text descriptor file.
3308                                            Unfortunately mkstemp() makes this file 0600 on most unix systems. */
3309                                         if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
3310                                                 ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
3311
3312                                         ast_unlock_path(dir);
3313                                         if (ast_check_realtime("voicemail_data")) {
3314                                                 snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
3315                                                 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
3316                                                 ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
3317                                         }
3318                                         /* We must store the file first, before copying the message, because
3319                                          * ODBC storage does the entire copy with SQL.
3320                                          */
3321                                         if (ast_fileexists(fn, NULL, NULL) > 0) {
3322                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3323                                         }
3324
3325                                         /* Are there to be more recipients of this message? */
3326                                         while (tmpptr) {
3327                                                 struct ast_vm_user recipu, *recip;
3328                                                 char *exten, *context;
3329                                         
3330                                                 exten = strsep(&tmpptr, "&");
3331                                                 context = strchr(exten, '@');
3332                                                 if (context) {
3333                                                         *context = '\0';
3334                                                         context++;
3335                                                 }
3336                                                 if ((recip = find_user(&recipu, context, exten))) {
3337                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
3338                                                         free_user(recip);
3339                                                 }
3340                                         }
3341                                         /* Notification and disposal needs to happen after the copy, though. */
3342                                         if (ast_fileexists(fn, NULL, NULL)) {
3343                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3344                                                 DISPOSE(dir, msgnum);
3345                                         }
3346                                 }
3347                         }
3348                 }
3349                 if (res == '0') {
3350                         goto transfer;
3351                 } else if (res > 0)
3352                         res = 0;
3353
3354                 if (duration < vmminsecs)
3355                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3356                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3357                 else
3358                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3359         } else
3360                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3361 leave_vm_out:
3362         free_user(vmu);
3363         
3364         return res;
3365 }
3366
3367 #ifndef IMAP_STORAGE
3368 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3369 {
3370         /* we know max messages, so stop process when number is hit */
3371
3372         int x,dest;
3373         char sfn[PATH_MAX];
3374         char dfn[PATH_MAX];
3375
3376         if (vm_lock_path(dir))
3377                 return ERROR_LOCK_PATH;
3378
3379         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3380                 make_file(sfn, sizeof(sfn), dir, x);
3381                 if (EXISTS(dir, x, sfn, NULL)) {
3382                         
3383                         if (x != dest) {
3384                                 make_file(dfn, sizeof(dfn), dir, dest);
3385                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3386                         }
3387                         
3388                         dest++;
3389                 }
3390         }
3391         ast_unlock_path(dir);
3392
3393         return 0;
3394 }
3395 #endif
3396
3397 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3398 {
3399         int d;
3400         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3401         return d;
3402 }
3403
3404 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3405 {
3406 #ifdef IMAP_STORAGE
3407         /* we must use mbox(x) folder names, and copy the message there */
3408         /* simple. huh? */
3409         char dbox[256];
3410         long res;
3411         char sequence[10];
3412
3413         /* if save to Old folder, just leave in INBOX */
3414         if (box == 1) return 10;
3415         /* get the real IMAP message number for this message */
3416         sprintf(sequence,"%ld",vms->msgArray[msg]);
3417         imap_mailbox_name(dbox, vms, box, 1);
3418         ast_debug(3, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3419         res = mail_copy(vms->mailstream, sequence, dbox);
3420         if (res == 1) return 0;
3421         return 1;
3422 #else
3423         char *dir = vms->curdir;
3424         char *username = vms->username;
3425         char *context = vmu->context;
3426         char sfn[PATH_MAX];
3427         char dfn[PATH_MAX];
3428         char ddir[PATH_MAX];
3429         const char *dbox = mbox(box);
3430         int x;
3431         make_file(sfn, sizeof(sfn), dir, msg);
3432         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3433
3434         if (vm_lock_path(ddir))
3435                 return ERROR_LOCK_PATH;
3436
3437         x = last_message_index(vmu, ddir) + 1;
3438         make_file(dfn, sizeof(dfn), ddir, x);
3439
3440         if (x >= vmu->maxmsg) {
3441                 ast_unlock_path(ddir);
3442                 return -1;
3443         }
3444         if (strcmp(sfn, dfn)) {
3445                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3446         }
3447         ast_unlock_path(ddir);
3448 #endif
3449         return 0;
3450 }
3451
3452 static int adsi_logo(unsigned char *buf)
3453 {
3454         int bytes = 0;
3455         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3456         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
3457         return bytes;
3458 }
3459
3460 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3461 {
3462         unsigned char buf[256];
3463         int bytes=0;
3464         int x;
3465         char num[5];
3466
3467         *useadsi = 0;
3468         bytes += ast_adsi_data_mode(buf + bytes);
3469         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3470
3471         bytes = 0;
3472         bytes += adsi_logo(buf);
3473         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3474 #ifdef DISPLAY
3475         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3476 #endif
3477         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3478         bytes += ast_adsi_data_mode(buf + bytes);
3479         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3480
3481         if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3482                 bytes = 0;
3483                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3484                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3485                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3486                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3487                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3488                 return 0;
3489         }
3490
3491 #ifdef DISPLAY
3492         /* Add a dot */
3493         bytes = 0;
3494         bytes += ast_adsi_logo(buf);
3495         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3496         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3497         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3498         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3499 #endif
3500         bytes = 0;
3501         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);