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