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