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