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