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