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