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