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