Merged revisions 47391 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         FILE *f=NULL;
1012         char rowdata[80];
1013         char fn[256];
1014         char full_fn[256];
1015         char msgnums[80];
1016         
1017         struct odbc_obj *obj;
1018         obj = odbc_request_obj(odbc_database, 0);
1019         if (obj) {
1020                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1021                 c = strchr(fmt, '|');
1022                 if (c)
1023                         *c = '\0';
1024                 if (!strcasecmp(fmt, "wav49"))
1025                         strcpy(fmt, "WAV");
1026                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1027                 if (msgnum > -1)
1028                         make_file(fn, sizeof(fn), dir, msgnum);
1029                 else
1030                         ast_copy_string(fn, dir, sizeof(fn));
1031                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1032                 f = fopen(full_fn, "w+");
1033                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1034                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1035                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1036                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1037                         odbc_release_obj(obj);
1038                         goto yuck;
1039                 }
1040                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1041                 res = SQLPrepare(stmt, sql, SQL_NTS);
1042                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1043                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1044                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1045                         odbc_release_obj(obj);
1046                         goto yuck;
1047                 }
1048                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1049                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1050                 res = odbc_smart_execute(obj, stmt);
1051                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1052                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1053                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1054                         odbc_release_obj(obj);
1055                         goto yuck;
1056                 }
1057                 res = SQLFetch(stmt);
1058                 if (res == SQL_NO_DATA) {
1059                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1060                         odbc_release_obj(obj);
1061                         goto yuck;
1062                 }
1063                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1064                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1065                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1066                         odbc_release_obj(obj);
1067                         goto yuck;
1068                 }
1069                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1070                 if (fd < 0) {
1071                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1072                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1073                         odbc_release_obj(obj);
1074                         goto yuck;
1075                 }
1076                 res = SQLNumResultCols(stmt, &colcount);
1077                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1078                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1079                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1080                         odbc_release_obj(obj);
1081                         goto yuck;
1082                 }
1083                 if (f) 
1084                         fprintf(f, "[message]\n");
1085                 for (x=0;x<colcount;x++) {
1086                         rowdata[0] = '\0';
1087                         collen = sizeof(coltitle);
1088                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
1089                                                 &datatype, &colsize, &decimaldigits, &nullable);
1090                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1091                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1092                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1093                                 odbc_release_obj(obj);
1094                                 goto yuck;
1095                         }
1096                         if (!strcasecmp(coltitle, "recording")) {
1097                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
1098                                 fdlen = colsize;
1099                                 if (fd > -1) {
1100                                         char tmp[1]="";
1101                                         lseek(fd, fdlen - 1, SEEK_SET);
1102                                         if (write(fd, tmp, 1) != 1) {
1103                                                 close(fd);
1104                                                 fd = -1;
1105                                                 continue;
1106                                         }
1107                                         if (fd > -1)
1108                                                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1109                                 }
1110                                 if (fdm) {
1111                                         memset(fdm, 0, fdlen);
1112                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
1113                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1114                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1115                                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1116                                                 odbc_release_obj(obj);
1117                                                 goto yuck;
1118                                         }
1119                                 }
1120                         } else {
1121                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1122                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1123                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1124                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1125                                         odbc_release_obj(obj);
1126                                         goto yuck;
1127                                 }
1128                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1129                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1130                         }
1131                 }
1132                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1133                 odbc_release_obj(obj);
1134         } else
1135                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1136 yuck:   
1137         if (f)
1138                 fclose(f);
1139         if (fdm)
1140                 munmap(fdm, fdlen);
1141         if (fd > -1)
1142                 close(fd);
1143         return x - 1;
1144 }
1145
1146 static int remove_file(char *dir, int msgnum)
1147 {
1148         char fn[256];
1149         char full_fn[256];
1150         char msgnums[80];
1151         
1152         if (msgnum > -1) {
1153                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1154                 make_file(fn, sizeof(fn), dir, msgnum);
1155         } else
1156                 ast_copy_string(fn, dir, sizeof(fn));
1157         ast_filedelete(fn, NULL);       
1158         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1159         unlink(full_fn);
1160         return 0;
1161 }
1162
1163 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1164 {
1165         int x = 0;
1166         int res;
1167         SQLHSTMT stmt;
1168         char sql[256];
1169         char rowdata[20];
1170         
1171         struct odbc_obj *obj;
1172         obj = odbc_request_obj(odbc_database, 0);
1173         if (obj) {
1174                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1175                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1176                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1177                         odbc_release_obj(obj);
1178                         goto yuck;
1179                 }
1180                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1181                 res = SQLPrepare(stmt, sql, SQL_NTS);
1182                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1183                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1184                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1185                         odbc_release_obj(obj);
1186                         goto yuck;
1187                 }
1188                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1189                 res = odbc_smart_execute(obj, stmt);
1190                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1191                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1192                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1193                         odbc_release_obj(obj);
1194                         goto yuck;
1195                 }
1196                 res = SQLFetch(stmt);
1197                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1198                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1199                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1200                         odbc_release_obj(obj);
1201                         goto yuck;
1202                 }
1203                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1204                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1205                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1206                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1207                         odbc_release_obj(obj);
1208                         goto yuck;
1209                 }
1210                 if (sscanf(rowdata, "%d", &x) != 1)
1211                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1212                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1213                 odbc_release_obj(obj);
1214         } else
1215                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1216 yuck:   
1217         return x - 1;
1218 }
1219
1220 static int message_exists(char *dir, int msgnum)
1221 {
1222         int x = 0;
1223         int res;
1224         SQLHSTMT stmt;
1225         char sql[256];
1226         char rowdata[20];
1227         char msgnums[20];
1228         
1229         struct odbc_obj *obj;
1230         obj = odbc_request_obj(odbc_database, 0);
1231         if (obj) {
1232                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1233                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1234                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1235                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1236                         odbc_release_obj(obj);
1237                         goto yuck;
1238                 }
1239                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1240                 res = SQLPrepare(stmt, sql, SQL_NTS);
1241                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1242                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1243                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1244                         odbc_release_obj(obj);
1245                         goto yuck;
1246                 }
1247                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1248                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1249                 res = odbc_smart_execute(obj, stmt);
1250                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1251                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1252                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1253                         odbc_release_obj(obj);
1254                         goto yuck;
1255                 }
1256                 res = SQLFetch(stmt);
1257                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1258                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1259                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1260                         odbc_release_obj(obj);
1261                         goto yuck;
1262                 }
1263                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1264                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1265                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1266                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1267                         odbc_release_obj(obj);
1268                         goto yuck;
1269                 }
1270                 if (sscanf(rowdata, "%d", &x) != 1)
1271                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1272                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1273                 odbc_release_obj(obj);
1274         } else
1275                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1276 yuck:   
1277         return x;
1278 }
1279
1280 static int count_messages(struct ast_vm_user *vmu, char *dir)
1281 {
1282         return last_message_index(vmu, dir) + 1;
1283 }
1284
1285 static void delete_file(char *sdir, int smsg)
1286 {
1287         int res;
1288         SQLHSTMT stmt;
1289         char sql[256];
1290         char msgnums[20];
1291         
1292         struct odbc_obj *obj;
1293         obj = odbc_request_obj(odbc_database, 0);
1294         if (obj) {
1295                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1296                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1297                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1298                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1299                         odbc_release_obj(obj);
1300                         goto yuck;
1301                 }
1302                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1303                 res = SQLPrepare(stmt, sql, SQL_NTS);
1304                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1305                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1306                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1307                         odbc_release_obj(obj);
1308                         goto yuck;
1309                 }
1310                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1311                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1312                 res = odbc_smart_execute(obj, stmt);
1313                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1314                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1315                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1316                         odbc_release_obj(obj);
1317                         goto yuck;
1318                 }
1319                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1320                 odbc_release_obj(obj);
1321         } else
1322                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1323 yuck:
1324         return; 
1325 }
1326
1327 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1328 {
1329         int res;
1330         SQLHSTMT stmt;
1331         char sql[512];
1332         char msgnums[20];
1333         char msgnumd[20];
1334         struct odbc_obj *obj;
1335
1336         delete_file(ddir, dmsg);
1337         obj = odbc_request_obj(odbc_database, 0);
1338         if (obj) {
1339                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1340                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1341                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1342                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1343                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1344                         odbc_release_obj(obj);
1345                         goto yuck;
1346                 }
1347                 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); 
1348                 res = SQLPrepare(stmt, sql, SQL_NTS);
1349                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1350                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1351                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1352                         odbc_release_obj(obj);
1353                         goto yuck;
1354                 }
1355                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1356                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1357                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1358                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1359                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1360                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1361                 res = odbc_smart_execute(obj, stmt);
1362                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1363                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1364                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1365                         odbc_release_obj(obj);
1366                         goto yuck;
1367                 }
1368                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1369                 odbc_release_obj(obj);
1370         } else
1371                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1372 yuck:
1373         return; 
1374 }
1375
1376 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1377 {
1378         int x = 0;
1379         int res;
1380         int fd = -1;
1381         void *fdm=NULL;
1382         size_t fdlen = -1;
1383         SQLHSTMT stmt;
1384         SQLINTEGER len;
1385         char sql[256];
1386         char msgnums[20];
1387         char fn[256];
1388         char full_fn[256];
1389         char fmt[80]="";
1390         char *c;
1391         char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1392         char *category = "";
1393         struct ast_config *cfg=NULL;
1394         struct odbc_obj *obj;
1395
1396         delete_file(dir, msgnum);
1397         obj = odbc_request_obj(odbc_database, 0);
1398         if (obj) {
1399                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1400                 c = strchr(fmt, '|');
1401                 if (c)
1402                         *c = '\0';
1403                 if (!strcasecmp(fmt, "wav49"))
1404                         strcpy(fmt, "WAV");
1405                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1406                 if (msgnum > -1)
1407                         make_file(fn, sizeof(fn), dir, msgnum);
1408                 else
1409                         ast_copy_string(fn, dir, sizeof(fn));
1410                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1411                 cfg = ast_config_load(full_fn);
1412                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1413                 fd = open(full_fn, O_RDWR);
1414                 if (fd < 0) {
1415                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1416                         odbc_release_obj(obj);
1417                         goto yuck;
1418                 }
1419                 if (cfg) {
1420                         context = ast_variable_retrieve(cfg, "message", "context");
1421                         if (!context) context = "";
1422                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1423                         if (!macrocontext) macrocontext = "";
1424                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1425                         if (!callerid) callerid = "";
1426                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1427                         if (!origtime) origtime = "";
1428                         duration = ast_variable_retrieve(cfg, "message", "duration");
1429                         if (!duration) duration = "";
1430                         category = ast_variable_retrieve(cfg, "message", "category");
1431                         if (!category) category = "";
1432                 }
1433                 fdlen = lseek(fd, 0, SEEK_END);
1434                 lseek(fd, 0, SEEK_SET);
1435                 printf("Length is %zd\n", fdlen);
1436                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1437                 if (!fdm) {
1438                         ast_log(LOG_WARNING, "Memory map failed!\n");
1439                         odbc_release_obj(obj);
1440                         goto yuck;
1441                 } 
1442                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1443                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1444                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1445                         odbc_release_obj(obj);
1446                         goto yuck;
1447                 }
1448                 if (!ast_strlen_zero(category)) 
1449                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1450                 else
1451                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1452                 res = SQLPrepare(stmt, sql, SQL_NTS);
1453                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1454                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1455                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1456                         odbc_release_obj(obj);
1457                         goto yuck;
1458                 }
1459                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1460                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1461                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1462                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1463                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1464                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1465                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1466                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1467                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1468                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1469                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1470                 if (!ast_strlen_zero(category))
1471                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1472                 res = odbc_smart_execute(obj, stmt);
1473                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1474                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1475                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1476                         odbc_release_obj(obj);
1477                         goto yuck;
1478                 }
1479                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1480                 odbc_release_obj(obj);
1481         } else
1482                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1483 yuck:   
1484         if (cfg)
1485                 ast_config_destroy(cfg);
1486         if (fdm)
1487                 munmap(fdm, fdlen);
1488         if (fd > -1)
1489                 close(fd);
1490         return x;
1491 }
1492
1493 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1494 {
1495         int res;
1496         SQLHSTMT stmt;
1497         char sql[256];
1498         char msgnums[20];
1499         char msgnumd[20];
1500         struct odbc_obj *obj;
1501
1502         delete_file(ddir, dmsg);
1503         obj = odbc_request_obj(odbc_database, 0);
1504         if (obj) {
1505                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1506                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1507                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1508                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1509                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1510                         odbc_release_obj(obj);
1511                         goto yuck;
1512                 }
1513                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1514                 res = SQLPrepare(stmt, sql, SQL_NTS);
1515                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1516                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1517                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1518                         odbc_release_obj(obj);
1519                         goto yuck;
1520                 }
1521                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1522                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1523                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1524                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1525                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1526                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1527                 res = odbc_smart_execute(obj, stmt);
1528                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1529                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1530                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1531                         odbc_release_obj(obj);
1532                         goto yuck;
1533                 }
1534                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1535                 odbc_release_obj(obj);
1536         } else
1537                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1538 yuck:
1539         return; 
1540 }
1541
1542 #else
1543 #ifndef IMAP_STORAGE
1544 static int count_messages(struct ast_vm_user *vmu, char *dir)
1545 {
1546         /* Find all .txt files - even if they are not in sequence from 0000 */
1547
1548         int vmcount = 0;
1549         DIR *vmdir = NULL;
1550         struct dirent *vment = NULL;
1551
1552         if (vm_lock_path(dir))
1553                 return ERROR_LOCK_PATH;
1554
1555         if ((vmdir = opendir(dir))) {
1556                 while ((vment = readdir(vmdir))) {
1557                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1558                                 vmcount++;
1559                 }
1560                 closedir(vmdir);
1561         }
1562         ast_unlock_path(dir);
1563         
1564         return vmcount;
1565 }
1566
1567 static void rename_file(char *sfn, char *dfn)
1568 {
1569         char stxt[256];
1570         char dtxt[256];
1571         ast_filerename(sfn,dfn,NULL);
1572         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1573         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1574         rename(stxt, dtxt);
1575 }
1576
1577 static int copy(char *infile, char *outfile)
1578 {
1579         int ifd;
1580         int ofd;
1581         int res;
1582         int len;
1583         char buf[4096];
1584
1585 #ifdef HARDLINK_WHEN_POSSIBLE
1586         /* Hard link if possible; saves disk space & is faster */
1587         if (link(infile, outfile)) {
1588 #endif
1589                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1590                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1591                         return -1;
1592                 }
1593                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1594                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1595                         close(ifd);
1596                         return -1;
1597                 }
1598                 do {
1599                         len = read(ifd, buf, sizeof(buf));
1600                         if (len < 0) {
1601                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1602                                 close(ifd);
1603                                 close(ofd);
1604                                 unlink(outfile);
1605                         }
1606                         if (len) {
1607                                 res = write(ofd, buf, len);
1608                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1609                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1610                                         close(ifd);
1611                                         close(ofd);
1612                                         unlink(outfile);
1613                                 }
1614                         }
1615                 } while (len);
1616                 close(ifd);
1617                 close(ofd);
1618                 return 0;
1619 #ifdef HARDLINK_WHEN_POSSIBLE
1620         } else {
1621                 /* Hard link succeeded */
1622                 return 0;
1623         }
1624 #endif
1625 }
1626
1627 static void copy_file(char *frompath, char *topath)
1628 {
1629         char frompath2[256],topath2[256];
1630         ast_filecopy(frompath, topath, NULL);
1631         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1632         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1633         copy(frompath2, topath2);
1634 }
1635 #endif
1636 /*
1637  * A negative return value indicates an error.
1638  * \note Should always be called with a lock already set on dir.
1639  */
1640 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1641 {
1642         int x;
1643         unsigned char map[MAXMSGLIMIT] = "";
1644         DIR *msgdir;
1645         struct dirent *msgdirent;
1646         int msgdirint;
1647
1648         /* Reading the entire directory into a file map scales better than
1649          * doing a stat repeatedly on a predicted sequence.  I suspect this
1650          * is partially due to stat(2) internally doing a readdir(2) itself to
1651          * find each file. */
1652         msgdir = opendir(dir);
1653         while ((msgdirent = readdir(msgdir))) {
1654                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1655                         map[msgdirint] = 1;
1656         }
1657         closedir(msgdir);
1658
1659         for (x = 0; x < vmu->maxmsg; x++) {
1660                 if (map[x] == 0)
1661                         break;
1662         }
1663
1664         return x - 1;
1665 }
1666
1667 static int vm_delete(char *file)
1668 {
1669         char *txt;
1670         int txtsize = 0;
1671
1672         txtsize = (strlen(file) + 5)*sizeof(char);
1673         txt = alloca(txtsize);
1674         /* Sprintf here would safe because we alloca'd exactly the right length,
1675          * but trying to eliminate all sprintf's anyhow
1676          */
1677         snprintf(txt, txtsize, "%s.txt", file);
1678         unlink(txt);
1679         return ast_filedelete(file, NULL);
1680 }
1681
1682
1683 #endif
1684 static int inbuf(struct baseio *bio, FILE *fi)
1685 {
1686         int l;
1687
1688         if (bio->ateof)
1689                 return 0;
1690
1691         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1692                 if (ferror(fi))
1693                         return -1;
1694
1695                 bio->ateof = 1;
1696                 return 0;
1697         }
1698
1699         bio->iolen= l;
1700         bio->iocp= 0;
1701
1702         return 1;
1703 }
1704
1705 static int inchar(struct baseio *bio, FILE *fi)
1706 {
1707         if (bio->iocp>=bio->iolen) {
1708                 if (!inbuf(bio, fi))
1709                         return EOF;
1710         }
1711
1712         return bio->iobuf[bio->iocp++];
1713 }
1714
1715 static int ochar(struct baseio *bio, int c, FILE *so)
1716 {
1717         if (bio->linelength>=BASELINELEN) {
1718                 if (fputs(eol,so)==EOF)
1719                         return -1;
1720
1721                 bio->linelength= 0;
1722         }
1723
1724         if (putc(((unsigned char)c),so)==EOF)
1725                 return -1;
1726
1727         bio->linelength++;
1728
1729         return 1;
1730 }
1731
1732 static int base_encode(char *filename, FILE *so)
1733 {
1734         unsigned char dtable[BASEMAXINLINE];
1735         int i,hiteof= 0;
1736         FILE *fi;
1737         struct baseio bio;
1738
1739         memset(&bio, 0, sizeof(bio));
1740         bio.iocp = BASEMAXINLINE;
1741
1742         if (!(fi = fopen(filename, "rb"))) {
1743                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1744                 return -1;
1745         }
1746
1747         for (i= 0;i<9;i++) {
1748                 dtable[i]= 'A'+i;
1749                 dtable[i+9]= 'J'+i;
1750                 dtable[26+i]= 'a'+i;
1751                 dtable[26+i+9]= 'j'+i;
1752         }
1753         for (i= 0;i<8;i++) {
1754                 dtable[i+18]= 'S'+i;
1755                 dtable[26+i+18]= 's'+i;
1756         }
1757         for (i= 0;i<10;i++) {
1758                 dtable[52+i]= '0'+i;
1759         }
1760         dtable[62]= '+';
1761         dtable[63]= '/';
1762
1763         while (!hiteof){
1764                 unsigned char igroup[3],ogroup[4];
1765                 int c,n;
1766
1767                 igroup[0]= igroup[1]= igroup[2]= 0;
1768
1769                 for (n= 0;n<3;n++) {
1770                         if ((c = inchar(&bio, fi)) == EOF) {
1771                                 hiteof= 1;
1772                                 break;
1773                         }
1774
1775                         igroup[n]= (unsigned char)c;
1776                 }
1777
1778                 if (n> 0) {
1779                         ogroup[0]= dtable[igroup[0]>>2];
1780                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1781                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1782                         ogroup[3]= dtable[igroup[2]&0x3F];
1783
1784                         if (n<3) {
1785                                 ogroup[3]= '=';
1786
1787                                 if (n<2)
1788                                         ogroup[2]= '=';
1789                         }
1790
1791                         for (i= 0;i<4;i++)
1792                                 ochar(&bio, ogroup[i], so);
1793                 }
1794         }
1795
1796         if (fputs(eol,so)==EOF)
1797                 return 0;
1798
1799         fclose(fi);
1800
1801         return 1;
1802 }
1803
1804 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)
1805 {
1806         char callerid[256];
1807         /* Prepare variables for substitution in email body and subject */
1808         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1809         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1810         snprintf(passdata, passdatasize, "%d", msgnum);
1811         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1812         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1813         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1814         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1815         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1816         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1817         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1818         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1819 }
1820
1821 /*
1822  * fill in *tm for current time according to the proper timezone, if any.
1823  * Return tm so it can be used as a function argument.
1824  */
1825 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1826 {
1827         const struct vm_zone *z = NULL;
1828         time_t t = time(NULL);
1829
1830         /* Does this user have a timezone specified? */
1831         if (!ast_strlen_zero(vmu->zonetag)) {
1832                 /* Find the zone in the list */
1833                 AST_LIST_LOCK(&zones);
1834                 AST_LIST_TRAVERSE(&zones, z, list) {
1835                         if (!strcmp(z->name, vmu->zonetag))
1836                                 break;
1837                 }
1838                 AST_LIST_UNLOCK(&zones);
1839         }
1840         ast_localtime(&t, tm, z ? z->timezone : NULL);
1841         return tm;
1842 }
1843
1844 /* same as mkstemp, but return a FILE * */
1845 static FILE *vm_mkftemp(char *template)
1846 {
1847         FILE *p = NULL;
1848         int pfd = mkstemp(template);
1849         if (pfd > -1) {
1850                 p = fdopen(pfd, "w+");
1851                 if (!p) {
1852                         close(pfd);
1853                         pfd = -1;
1854                 }
1855         }
1856         return p;
1857 }
1858
1859 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)
1860 {
1861         char date[256];
1862         char host[MAXHOSTNAMELEN] = "";
1863         char who[256];
1864         char bound[256];
1865         char fname[256];
1866         char dur[256];
1867         char tmpcmd[256];
1868         struct tm tm;
1869
1870         gethostname(host, sizeof(host)-1);
1871         if (strchr(srcemail, '@'))
1872                 ast_copy_string(who, srcemail, sizeof(who));
1873         else {
1874                 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1875         }
1876         snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1877         strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1878         fprintf(p, "Date: %s\r\n", date);
1879
1880         /* Set date format for voicemail mail */
1881         strftime(date, sizeof(date), emaildateformat, &tm);
1882
1883         if (*fromstring) {
1884                 struct ast_channel *ast;
1885                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1886                         char *passdata;
1887                         int vmlen = strlen(fromstring)*3 + 200;
1888                         if ((passdata = alloca(vmlen))) {
1889                                 memset(passdata, 0, vmlen);
1890                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1891                                 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1892                                 fprintf(p, "From: %s <%s>\r\n",passdata,who);
1893                         } else
1894                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1895                         ast_channel_free(ast);
1896                 } else
1897                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1898         } else
1899                 fprintf(p, "From: Asterisk PBX <%s>\r\n", who);
1900         fprintf(p, "To: %s <%s>\r\n", vmu->fullname, vmu->email);
1901         if (emailsubject) {
1902                 struct ast_channel *ast;
1903                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1904                         char *passdata;
1905                         int vmlen = strlen(emailsubject)*3 + 200;
1906                         if ((passdata = alloca(vmlen))) {
1907                                 memset(passdata, 0, vmlen);
1908                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1909                                 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1910                                 fprintf(p, "Subject: %s\r\n", passdata);
1911                         } else
1912                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1913                         ast_channel_free(ast);
1914                 } else
1915                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1916         } else  if (*emailtitle) {
1917                 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1918                 fprintf(p,"\r\n") ;
1919         } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1920                 fprintf(p, "Subject: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1921         else
1922                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum + 1, mailbox);
1923         fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1924         if(imap) {
1925                 /* additional information needed for IMAP searching */
1926                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum + 1);
1927                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1928                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring);
1929                 fprintf(p, "X-Asterisk-VM-Context: %s\r\n", context);
1930                 fprintf(p, "X-Asterisk-VM-Extension: %s\r\n", mailbox);
1931                 fprintf(p, "X-Asterisk-VM-Priority: %d\r\n", chan->priority);
1932                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\r\n", chan->name);
1933                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum);
1934                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname);
1935                 fprintf(p, "X-Asterisk-VM-Duration: %d\r\n", duration);
1936                 if (!ast_strlen_zero(category))
1937                         fprintf(p, "X-Asterisk-VM-Category: %s\r\n", category);
1938                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\r\n", date);
1939                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL));
1940         }
1941         if (!ast_strlen_zero(cidnum))
1942                 fprintf(p, "X-Asterisk-CallerID: %s\r\n", cidnum);
1943         if (!ast_strlen_zero(cidname))
1944                 fprintf(p, "X-Asterisk-CallerIDName: %s\r\n", cidname);
1945         fprintf(p, "MIME-Version: 1.0\r\n");
1946         if (attach_user_voicemail) {
1947                 /* Something unique. */
1948                 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1949
1950                 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound);
1951
1952                 fprintf(p, "--%s\r\n", bound);
1953         }
1954         fprintf(p, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset);
1955         if (emailbody) {
1956                 struct ast_channel *ast;
1957                 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
1958                         char *passdata;
1959                         int vmlen = strlen(emailbody)*3 + 200;
1960                         if ((passdata = alloca(vmlen))) {
1961                                 memset(passdata, 0, vmlen);
1962                                 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1963                                 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1964                                 fprintf(p, "%s\r\n", passdata);
1965                         } else
1966                                 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1967                         ast_channel_free(ast);
1968                 } else
1969                         ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1970         } else {
1971                 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"
1972
1973                 "in mailbox %s from %s, on %s so you might\r\n"
1974                 "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, 
1975                 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1976         }
1977         if (attach_user_voicemail) {
1978                 /* Eww. We want formats to tell us their own MIME type */
1979                 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1980                 char tmpdir[256], newtmp[256];
1981                 int tmpfd;
1982         
1983                 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1984                 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1985                 tmpfd = mkstemp(newtmp);
1986                 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1987                 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1988                         snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1989                         ast_safe_system(tmpcmd);
1990                         attach = newtmp;
1991                         ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1992                 }
1993                 fprintf(p, "--%s\r\n", bound);
1994                 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype, format, msgnum, format);
1995                 fprintf(p, "Content-Transfer-Encoding: base64\r\n");
1996                 fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
1997                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum, format);
1998                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1999                 base_encode(fname, p);
2000                 /* only attach if necessary */
2001                 if (imap && strcmp(format, "gsm")) {
2002                         fprintf(p, "--%s\r\n", bound);
2003                         fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum, format);
2004                         fprintf(p, "Content-Transfer-Encoding: base64\r\n");
2005                         fprintf(p, "Content-Description: Voicemail sound attachment.\r\n");
2006                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum);
2007                         snprintf(fname, sizeof(fname), "%s.gsm", attach);
2008                         base_encode(fname, p);
2009                 }
2010                 fprintf(p, "\r\n\r\n--%s--\r\n.\r\n", bound);
2011                 if (tmpfd > -1)
2012                         close(tmpfd);
2013                 unlink(newtmp);
2014         }
2015 }
2016 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)
2017 {
2018         FILE *p=NULL;
2019         char tmp[80] = "/tmp/astmail-XXXXXX";
2020         char tmp2[256];
2021
2022         if (vmu && ast_strlen_zero(vmu->email)) {
2023                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
2024                 return(0);
2025         }
2026         if (!strcmp(format, "wav49"))
2027                 format = "WAV";
2028         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));
2029         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2030            command hangs */
2031         if ((p = vm_mkftemp(tmp)) == NULL) {
2032                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2033                 return -1;
2034         } else {
2035                 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
2036                 fclose(p);
2037                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2038                 ast_safe_system(tmp2);
2039                 if (option_debug)
2040                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2041         }
2042         return 0;
2043 }
2044
2045 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)
2046 {
2047         char date[256];
2048         char host[MAXHOSTNAMELEN]="";
2049         char who[256];
2050         char dur[256];
2051         char tmp[80] = "/tmp/astmail-XXXXXX";
2052         char tmp2[256];
2053         struct tm tm;
2054         FILE *p;
2055
2056         if ((p = vm_mkftemp(tmp)) == NULL) {
2057                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2058                 return -1;
2059         } else {
2060                 gethostname(host, sizeof(host)-1);
2061                 if (strchr(srcemail, '@'))
2062                         ast_copy_string(who, srcemail, sizeof(who));
2063                 else {
2064                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2065                 }
2066                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2067                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2068                 fprintf(p, "Date: %s\n", date);
2069
2070                 if (*pagerfromstring) {
2071                         struct ast_channel *ast;
2072                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2073                                 char *passdata;
2074                                 int vmlen = strlen(fromstring)*3 + 200;
2075                                 if ((passdata = alloca(vmlen))) {
2076                                         memset(passdata, 0, vmlen);
2077                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2078                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2079                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2080                                 } else 
2081                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2082                                 ast_channel_free(ast);
2083                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2084                 } else
2085                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2086                 fprintf(p, "To: %s\n", pager);
2087                 if (pagersubject) {
2088                         struct ast_channel *ast;
2089                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2090                                 char *passdata;
2091                                 int vmlen = strlen(pagersubject) * 3 + 200;
2092                                 if ((passdata = alloca(vmlen))) {
2093                                         memset(passdata, 0, vmlen);
2094                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2095                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2096                                         fprintf(p, "Subject: %s\n\n", passdata);
2097                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2098                                 ast_channel_free(ast);
2099                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2100                 } else
2101                         fprintf(p, "Subject: New VM\n\n");
2102                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2103                 if (pagerbody) {
2104                         struct ast_channel *ast;
2105                         if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, 0))) {
2106                                 char *passdata;
2107                                 int vmlen = strlen(pagerbody)*3 + 200;
2108                                 if ((passdata = alloca(vmlen))) {
2109                                         memset(passdata, 0, vmlen);
2110                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2111                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2112                                         fprintf(p, "%s\n", passdata);
2113                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2114                         ast_channel_free(ast);
2115                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2116                 } else {
2117                         fprintf(p, "New %s long msg in box %s\n"
2118                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2119                 }
2120                 fclose(p);
2121                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2122                 ast_safe_system(tmp2);
2123                 if (option_debug)
2124                         ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2125         }
2126         return 0;
2127 }
2128
2129 static int get_date(char *s, int len)
2130 {
2131         struct tm tm;
2132         time_t t;
2133         t = time(0);
2134         localtime_r(&t,&tm);
2135         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2136 }
2137
2138 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2139 {
2140         int res;
2141         char fn[256];
2142         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2143         RETRIEVE(fn, -1);
2144         if (ast_fileexists(fn, NULL, NULL) > 0) {
2145                 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2146                 if (res) {
2147                         DISPOSE(fn, -1);
2148                         return res;
2149                 }
2150         } else {
2151                 /* Dispose just in case */
2152                 DISPOSE(fn, -1);
2153                 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2154                 if (res)
2155                         return res;
2156                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2157                 if (res)
2158                         return res;
2159         }
2160         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2161         return res;
2162 }
2163
2164 static void free_user(struct ast_vm_user *vmu)
2165 {
2166         if (ast_test_flag(vmu, VM_ALLOCED))
2167                 free(vmu);
2168 }
2169
2170 static void free_zone(struct vm_zone *z)
2171 {
2172         free(z);
2173 }
2174
2175 static const char *mbox(int id)
2176 {
2177         static const char *msgs[] = {
2178                 "INBOX",
2179                 "Old",
2180                 "Work",
2181                 "Family",
2182                 "Friends",
2183                 "Cust1",
2184                 "Cust2",
2185                 "Cust3",
2186                 "Cust4",
2187                 "Cust5",
2188         };
2189         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2190 }
2191
2192 #ifdef ODBC_STORAGE
2193 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2194 {
2195         int x = -1;
2196         int res;
2197         SQLHSTMT stmt;
2198         char sql[256];
2199         char rowdata[20];
2200         char tmp[256]="";
2201         struct odbc_obj *obj;
2202         char *context;
2203
2204         if (newmsgs)
2205                 *newmsgs = 0;
2206         if (oldmsgs)
2207                 *oldmsgs = 0;
2208
2209         /* If no mailbox, return immediately */
2210         if (ast_strlen_zero(mailbox))
2211                 return 0;
2212
2213         ast_copy_string(tmp, mailbox, sizeof(tmp));
2214         
2215         context = strchr(tmp, '@');
2216         if (context) {
2217                 *context = '\0';
2218                 context++;
2219         } else
2220                 context = "default";
2221         
2222         obj = odbc_request_obj(odbc_database, 0);
2223         if (obj) {
2224                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2225                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2226                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2227                         odbc_release_obj(obj);
2228                         goto yuck;
2229                 }
2230                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2231                 res = SQLPrepare(stmt, sql, SQL_NTS);
2232                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2233                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2234                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2235                         odbc_release_obj(obj);
2236                         goto yuck;
2237                 }
2238                 res = odbc_smart_execute(obj, stmt);
2239                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2241                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2242                         odbc_release_obj(obj);
2243                         goto yuck;
2244                 }
2245                 res = SQLFetch(stmt);
2246                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2247                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2248                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2249                         odbc_release_obj(obj);
2250                         goto yuck;
2251                 }
2252                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2253                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2254                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2255                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2256                         odbc_release_obj(obj);
2257                         goto yuck;
2258                 }
2259                 *newmsgs = atoi(rowdata);
2260                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2261
2262                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2263                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2265                         odbc_release_obj(obj);
2266                         goto yuck;
2267                 }
2268                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2269                 res = SQLPrepare(stmt, sql, SQL_NTS);
2270                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2271                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2272                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2273                         odbc_release_obj(obj);
2274                         goto yuck;
2275                 }
2276                 res = odbc_smart_execute(obj, stmt);
2277                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2278                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2279                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2280                         odbc_release_obj(obj);
2281                         goto yuck;
2282                 }
2283                 res = SQLFetch(stmt);
2284                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2285                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2286                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2287                         odbc_release_obj(obj);
2288                         goto yuck;
2289                 }
2290                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2291                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2292                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2293                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2294                         odbc_release_obj(obj);
2295                         goto yuck;
2296                 }
2297                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2298                 odbc_release_obj(obj);
2299                 *oldmsgs = atoi(rowdata);
2300                 x = 0;
2301         } else
2302                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2303                 
2304 yuck:   
2305         return x;
2306 }
2307
2308 static int messagecount(const char *context, const char *mailbox, const char *folder)
2309 {
2310         struct odbc_obj *obj = NULL;
2311         int nummsgs = 0;
2312         int res;
2313         SQLHSTMT stmt = NULL;
2314         char sql[256];
2315         char rowdata[20];
2316         if (!folder)
2317                 folder = "INBOX";
2318         /* If no mailbox, return immediately */
2319         if (ast_strlen_zero(mailbox))
2320                 return 0;
2321
2322         obj = odbc_request_obj(odbc_database, 0);
2323         if (obj) {
2324                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2325                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2326                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2327                         goto yuck;
2328                 }
2329                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2330                 res = SQLPrepare(stmt, sql, SQL_NTS);
2331                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2332                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2333                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2334                         goto yuck;
2335                 }
2336                 res = odbc_smart_execute(obj, stmt);
2337                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2338                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2339                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2340                         goto yuck;
2341                 }
2342                 res = SQLFetch(stmt);
2343                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2344                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2345                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2346                         goto yuck;
2347                 }
2348                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2349                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2350                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2351                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2352                         goto yuck;
2353                 }
2354                 nummsgs = atoi(rowdata);
2355                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2356         } else
2357                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2358
2359 yuck:
2360         if (obj)
2361                 odbc_release_obj(obj);
2362         return nummsgs;
2363 }
2364
2365 static int has_voicemail(const char *mailbox, const char *folder)
2366 {
2367         char *context, tmp[256];
2368         ast_copy_string(tmp, mailbox, sizeof(tmp));
2369         if ((context = strchr(tmp, '@')))
2370                 *context++ = '\0';
2371         else
2372                 context = "default";
2373
2374         if (messagecount(context, tmp, folder))
2375                 return 1;
2376         else
2377                 return 0;
2378 }
2379
2380 #elif defined(IMAP_STORAGE)
2381
2382 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)
2383 {
2384         char *myserveremail = serveremail;
2385         char fn[256];
2386         char mailbox[256];
2387         char *stringp;
2388         FILE *p=NULL;
2389         char tmp[80] = "/tmp/astmail-XXXXXX";
2390         long len;
2391         void *buf;
2392         STRING str;
2393         
2394         /* Attach only the first format */
2395         fmt = ast_strdupa(fmt);
2396         stringp = fmt;
2397         strsep(&stringp, "|");
2398
2399         if (!ast_strlen_zero(vmu->serveremail))
2400                 myserveremail = vmu->serveremail;
2401
2402         make_file(fn, sizeof(fn), dir, msgnum);
2403
2404         if (ast_strlen_zero(vmu->email))
2405                 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2406
2407         if (!strcmp(fmt, "wav49"))
2408                 fmt = "WAV";
2409         if(option_debug > 2)
2410                 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
2411         /* Make a temporary file instead of piping directly to sendmail, in case the mail
2412            command hangs */
2413         if ((p = vm_mkftemp(tmp)) == NULL) {
2414                 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2415                 return -1;
2416         } else {
2417                 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);
2418                 /* read mail file to memory */          
2419                 len = ftell(p);
2420                 rewind(p);
2421                 if((buf = ast_malloc(len+1)) == NIL) {
2422                         ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
2423                         return -1;
2424                 }
2425                 fread(buf, len, 1, p);
2426                 ((char *)buf)[len] = '\0';
2427                 INIT(&str, mail_string, buf, len);
2428                 imap_mailbox_name(mailbox, vms, 0, 1);
2429                 if(!mail_append(vms->mailstream, mailbox, &str))
2430                         ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2431                 fclose(p);
2432                 unlink(tmp);
2433                 ast_free(buf);
2434                 if(option_debug > 2)
2435                         ast_log(LOG_DEBUG, "%s stored\n", fn);
2436         }
2437         return 0;
2438
2439 }
2440
2441 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2442 {
2443         SEARCHPGM *pgm;
2444         SEARCHHEADER *hdr;
2445  
2446         struct ast_vm_user *vmu;
2447         struct vm_state *vms_p;
2448         char tmp[256]="";
2449         char *mb, *cur;
2450         char *mailboxnc; 
2451         char *context;
2452         int ret = 0;
2453         if (newmsgs)
2454                 *newmsgs = 0;
2455         if (oldmsgs)
2456                 *oldmsgs = 0;
2457  
2458         if(option_debug > 2)
2459                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2460         /* If no mailbox, return immediately */
2461         if (ast_strlen_zero(mailbox))
2462                 return 0;
2463         if (strchr(mailbox, ',')) {
2464                 int tmpnew, tmpold;
2465                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2466                 mb = tmp;
2467                 ret = 0;
2468                 while((cur = strsep(&mb, ", "))) {
2469                         if (!ast_strlen_zero(cur)) {
2470                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2471                                         return -1;
2472                                 else {
2473                                         if (newmsgs)
2474                                                 *newmsgs += tmpnew; 
2475                                         if (oldmsgs)
2476                                                 *oldmsgs += tmpold;
2477                                 }
2478                         }
2479                 }
2480                 return 0;
2481         }
2482         ast_copy_string(tmp, mailbox, sizeof(tmp));
2483         context = strchr(tmp, '@');
2484         if (context) {
2485                 *context = '\0';
2486                 mailboxnc = tmp;
2487                 context++;
2488         } else {
2489                 context = "default";
2490                 mailboxnc = (char *)mailbox;
2491         }
2492  
2493         /* We have to get the user before we can open the stream! */
2494         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2495         vmu = find_user(NULL, context, mailboxnc);
2496         if (!vmu) {
2497                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2498                 return -1;
2499         } else {
2500                 /* No IMAP account available */
2501                 if (vmu->imapuser[0] == '\0') {
2502                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2503                         return -1;
2504                 }
2505         }
2506  
2507         /* check if someone is accessing this box right now... */
2508         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2509         if (!vms_p) {
2510                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2511         }
2512         if (vms_p) {
2513                 if(option_debug > 2)
2514                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2515                 *newmsgs = vms_p->newmessages;
2516                 *oldmsgs = vms_p->oldmessages;
2517                 return 0;
2518         }
2519  
2520         /* add one if not there... */
2521         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2522         if (!vms_p) {
2523                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2524         }
2525  
2526         if (!vms_p) {
2527                 if(option_debug > 2)
2528                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2529                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2530                 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
2531                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2532                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2533                 if(option_debug > 2)
2534                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2535                 vms_p->updated = 1;
2536                 vms_p->interactive = 0;
2537                 /* set mailbox to INBOX! */
2538                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2539                 init_vm_state(vms_p);
2540                 vmstate_insert(vms_p);
2541         }
2542         if (!vms_p->mailstream)
2543                 ret = init_mailstream(vms_p, 0);
2544         if (!vms_p->mailstream) {
2545                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2546                 return -1;
2547         }
2548         if (newmsgs && ret==0 && vms_p->updated==1 ) {
2549                 pgm = mail_newsearchpgm ();
2550                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2551                 pgm->header = hdr;
2552                 pgm->unseen = 1;
2553                 pgm->seen = 0;
2554                 pgm->undeleted = 1;
2555                 pgm->deleted = 0;
2556  
2557                 vms_p->vmArrayIndex = 0;
2558         
2559                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2560                 *newmsgs = vms_p->vmArrayIndex;
2561                 vms_p->newmessages = vms_p->vmArrayIndex;
2562                 mail_free_searchpgm(&pgm);
2563         }
2564         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2565                 pgm = mail_newsearchpgm ();
2566                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2567                 pgm->header = hdr;
2568                 pgm->unseen = 0;
2569                 pgm->seen = 1;
2570                 pgm->deleted = 0;
2571                 pgm->undeleted = 1;
2572  
2573                 vms_p->vmArrayIndex = 0;
2574         
2575                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2576                 *oldmsgs = vms_p->vmArrayIndex;
2577                 vms_p->oldmessages = vms_p->vmArrayIndex;
2578                 mail_free_searchpgm(&pgm);
2579         }
2580         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2581                 vms_p->updated = 0;
2582         } else if (vms_p->updated > 1) {  /* decrement delay count */
2583                 vms_p->updated--;
2584         } else {  /* no changes, so don't search */
2585                 mail_ping(vms_p->mailstream);
2586                 /* Keep the old data */
2587                 *newmsgs = vms_p->newmessages;
2588                 *oldmsgs = vms_p->oldmessages;
2589         }
2590         return 0;
2591  }
2592
2593 static int has_voicemail(const char *mailbox, const char *folder)
2594 {
2595         int newmsgs, oldmsgs;
2596         
2597         if(inboxcount(mailbox, &newmsgs, &oldmsgs))
2598                 return folder? oldmsgs: newmsgs;
2599         else
2600                 return 0;
2601 }
2602
2603 static int messagecount(const char *context, const char *mailbox, const char *folder)
2604 {
2605         int newmsgs, oldmsgs;
2606         char tmp[256]="";
2607         
2608         if (ast_strlen_zero(mailbox))
2609                 return 0;
2610         sprintf(tmp,"%s@%s", mailbox, ast_strlen_zero(context)? "default": context);
2611
2612         if(inboxcount(tmp, &newmsgs, &oldmsgs))
2613                 return folder? oldmsgs: newmsgs;
2614         else
2615                 return 0;
2616 }
2617
2618 #endif
2619 #ifndef IMAP_STORAGE
2620 /* copy message only used by file storage */
2621 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)
2622 {
2623         
2624         char fromdir[256], todir[256], frompath[256], topath[256];
2625         const char *frombox = mbox(imbox);
2626         int recipmsgnum;
2627
2628         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2629
2630         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2631
2632         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2633         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2634         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, frombox);
2635
2636         if (vm_lock_path(todir))
2637                 return ERROR_LOCK_PATH;
2638
2639         recipmsgnum = last_message_index(recip, todir) + 1;
2640         if (recipmsgnum < recip->maxmsg) {
2641                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2642                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2643         } else {
2644                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2645         }
2646         ast_unlock_path(todir);
2647         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2648         
2649         return 0;
2650 }
2651 #endif
2652 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2653 static int messagecount(const char *context, const char *mailbox, const char *folder)
2654 {
2655         return __has_voicemail(context, mailbox, folder, 0);
2656 }
2657
2658
2659 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2660 {
2661         DIR *dir;
2662         struct dirent *de;
2663         char fn[256];
2664         int ret = 0;
2665         if (!folder)
2666                 folder = "INBOX";
2667         /* If no mailbox, return immediately */
2668         if (ast_strlen_zero(mailbox))
2669                 return 0;
2670         if (!context)
2671                 context = "default";
2672         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2673         dir = opendir(fn);
2674         if (!dir)
2675                 return 0;
2676         while ((de = readdir(dir))) {
2677                 if (!strncasecmp(de->d_name, "msg", 3)) {
2678                         if (shortcircuit) {
2679                                 ret = 1;
2680                                 break;
2681                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2682                                 ret++;
2683                 }
2684         }
2685         closedir(dir);
2686         return ret;
2687 }
2688
2689
2690 static int has_voicemail(const char *mailbox, const char *folder)
2691 {
2692         char tmp[256], *tmp2 = tmp, *mbox, *context;
2693         ast_copy_string(tmp, mailbox, sizeof(tmp));
2694         while ((mbox = strsep(&tmp2, ","))) {
2695                 if ((context = strchr(mbox, '@')))
2696                         *context++ = '\0';
2697                 else
2698                         context = "default";
2699                 if (__has_voicemail(context, mbox, folder, 1))
2700                         return 1;
2701         }
2702         return 0;
2703 }
2704
2705
2706 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2707 {
2708         char tmp[256];
2709         char *context;
2710
2711         if (newmsgs)
2712                 *newmsgs = 0;
2713         if (oldmsgs)
2714                 *oldmsgs = 0;
2715         /* If no mailbox, return immediately */
2716         if (ast_strlen_zero(mailbox))
2717                 return 0;
2718         if (strchr(mailbox, ',')) {
2719                 int tmpnew, tmpold;
2720                 char *mb, *cur;
2721
2722                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2723                 mb = tmp;
2724                 while ((cur = strsep(&mb, ", "))) {
2725                         if (!ast_strlen_zero(cur)) {
2726                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2727                                         return -1;
2728                                 else {
2729                                         if (newmsgs)
2730                                                 *newmsgs += tmpnew; 
2731                                         if (oldmsgs)
2732                                                 *oldmsgs += tmpold;
2733                                 }
2734                         }
2735                 }
2736                 return 0;
2737         }
2738         ast_copy_string(tmp, mailbox, sizeof(tmp));
2739         context = strchr(tmp, '@');
2740         if (context) {
2741                 *context = '\0';
2742                 context++;
2743         } else
2744                 context = "default";
2745         if (newmsgs)
2746                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2747         if (oldmsgs)
2748                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2749         return 0;
2750 }
2751
2752 #endif
2753
2754 static void run_externnotify(char *context, char *extension)
2755 {
2756         char arguments[255];
2757         char ext_context[256] = "";
2758         int newvoicemails = 0, oldvoicemails = 0;
2759         struct ast_smdi_mwi_message *mwi_msg;
2760
2761         if (!ast_strlen_zero(context))
2762                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2763         else
2764                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2765
2766         if (!strcasecmp(externnotify, "smdi")) {
2767                 if (ast_app_has_voicemail(ext_context, NULL)) 
2768                         ast_smdi_mwi_set(smdi_iface, extension);
2769                 else
2770                         ast_smdi_mwi_unset(smdi_iface, extension);
2771
2772                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2773                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2774                         if (!strncmp(mwi_msg->cause, "INV", 3))
2775                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2776                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2777                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2778                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2779                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2780                 } else {
2781                         if (option_debug)
2782                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2783                 }
2784         } else if (!ast_strlen_zero(externnotify)) {
2785                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2786                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2787                 } else {
2788                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2789                         if (option_debug)
2790                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2791                         ast_safe_system(arguments);
2792                 }
2793         }
2794 }
2795
2796 struct leave_vm_options {
2797         unsigned int flags;
2798         signed char record_gain;
2799 };
2800
2801 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2802 {
2803 #ifdef IMAP_STORAGE
2804         int newmsgs, oldmsgs;
2805         struct vm_state *vms = NULL;
2806 #endif
2807         char tmptxtfile[256], txtfile[256];
2808         char callerid[256];
2809         FILE *txt;
2810         char date[256];
2811         int txtdes;
2812         int res = 0;
2813         int msgnum;
2814         int duration = 0;
2815         int ausemacro = 0;
2816         int ousemacro = 0;
2817         int ouseexten = 0;
2818         char dir[256], tmpdir[260];
2819         char fn[256];
2820         char prefile[256]="";
2821         char tempfile[256]="";
2822         char ext_context[256] = "";
2823         char fmt[80];
2824         char *context;
2825         char ecodes[16] = "#";
2826         char tmp[256] = "", *tmpptr;
2827         struct ast_vm_user *vmu;
2828         struct ast_vm_user svm;
2829         const char *category = NULL;
2830
2831         ast_copy_string(tmp, ext, sizeof(tmp));
2832         ext = tmp;
2833         context = strchr(tmp, '@');
2834         if (context) {
2835                 *context++ = '\0';
2836                 tmpptr = strchr(context, '&');
2837         } else {
2838                 tmpptr = strchr(ext, '&');
2839         }
2840
2841         if (tmpptr)
2842                 *tmpptr++ = '\0';
2843
2844         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2845
2846         if(option_debug > 2)
2847                 ast_log(LOG_DEBUG, "Before find_user\n");
2848         if (!(vmu = find_user(&svm, context, ext))) {
2849                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2850                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2851                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2852                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2853                 return res;
2854         }
2855
2856         /* Setup pre-file if appropriate */
2857         if (strcmp(vmu->context, "default"))
2858                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2859         else
2860                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2861         if (ast_test_flag(options, OPT_BUSY_GREETING))
2862                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2863         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2864                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2865         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2866         RETRIEVE(tempfile, -1);
2867         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2868                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2869         DISPOSE(tempfile, -1);
2870         /* It's easier just to try to make it than to check for its existence */
2871         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2872         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2873
2874         /* Check current or macro-calling context for special extensions */
2875         if (ast_test_flag(vmu, VM_OPERATOR)) {
2876                 if (!ast_strlen_zero(vmu->exit)) {
2877                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2878                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2879                                 ouseexten = 1;
2880                         }
2881                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2882                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2883                         ouseexten = 1;
2884                 }
2885                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2886                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2887                 ousemacro = 1;
2888                 }
2889         }
2890
2891         if (!ast_strlen_zero(vmu->exit)) {
2892                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2893                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2894         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2895                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2896         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2897                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2898                 ausemacro = 1;
2899         }
2900
2901         /* Play the beginning intro if desired */
2902         if (!ast_strlen_zero(prefile)) {
2903                 RETRIEVE(prefile, -1);
2904                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2905                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2906                                 res = ast_waitstream(chan, ecodes);
2907                 } else {
2908                         if (option_debug)
2909                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2910                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2911                 }
2912                 DISPOSE(prefile, -1);
2913                 if (res < 0) {
2914                         if (option_debug)
2915                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2916                         free_user(vmu);
2917                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2918                         return -1;
2919                 }
2920         }
2921         if (res == '#') {
2922                 /* On a '#' we skip the instructions */
2923                 ast_set_flag(options, OPT_SILENT);
2924                 res = 0;
2925         }
2926         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2927                 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2928                 if (res == '#') {
2929                         ast_set_flag(options, OPT_SILENT);
2930                         res = 0;
2931                 }
2932         }
2933         if (res > 0)
2934                 ast_stopstream(chan);
2935         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2936          other than the operator -- an automated attendant or mailbox login for example */
2937         if (res == '*') {
2938                 chan->exten[0] = 'a';
2939                 chan->exten[1] = '\0';
2940                 if (!ast_strlen_zero(vmu->exit)) {
2941                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2942                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2943                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2944                 }
2945                 chan->priority = 0;
2946                 free_user(vmu);
2947                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2948                 return 0;
2949         }
2950
2951         /* Check for a '0' here */
2952         if (res == '0') {
2953         transfer:
2954                 if (ouseexten || ousemacro) {
2955                         chan->exten[0] = 'o';
2956                         chan->exten[1] = '\0';
2957                         if (!ast_strlen_zero(vmu->exit)) {
2958                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2959                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2960                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2961                         }
2962                         ast_play_and_wait(chan, "transfer");
2963                         chan->priority = 0;
2964                         free_user(vmu);
2965                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2966                 }
2967                 return 0;
2968         }
2969         if (res < 0) {
2970                 free_user(vmu);
2971                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2972                 return -1;
2973         }
2974         /* The meat of recording the message...  All the announcements and beeps have been played*/
2975         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2976         if (!ast_strlen_zero(fmt)) {
2977                 msgnum = 0;
2978
2979 #ifdef IMAP_STORAGE
2980                 /* Is ext a mailbox? */
2981                 /* must open stream for this user to get info! */
2982                 vms = get_vm_state_by_mailbox(ext,0);
2983                 if (vms) {
2984                         if(option_debug > 2)
2985                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2986                         newmsgs = vms->newmessages++;
2987                         oldmsgs = vms->oldmessages;
2988                 } else {
2989                         res = inboxcount(ext, &newmsgs, &oldmsgs);
2990                         if(res < 0) {
2991                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2992                                 return -1;
2993                         }
2994                         vms = get_vm_state_by_mailbox(ext,0);
2995                 }
2996                 /* here is a big difference! We add one to it later */
2997                 msgnum = newmsgs + oldmsgs;
2998                 if(option_debug > 2)
2999                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3000                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3001                 /* set variable for compatibility */
3002                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3003
3004                 /* Check if mailbox is full */
3005                 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3006                         if (option_debug)
3007                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3008                         ast_play_and_wait(chan, "vm-mailboxfull");
3009                         return -1;
3010                 }
3011                 /* here is a big difference! We add one to it later */
3012                 msgnum = newmsgs + oldmsgs;
3013                 if(option_debug > 2)
3014                         ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3015
3016 #else
3017                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3018                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3019                         if (!res)
3020                                 res = ast_waitstream(chan, "");
3021                         ast_log(LOG_WARNING, "No more messages possible\n");
3022                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3023                         goto leave_vm_out;
3024                 }
3025
3026 #endif
3027                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3028                 txtdes = mkstemp(tmptxtfile);
3029                 if (txtdes < 0) {
3030                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3031                         if (!res)
3032                                 res = ast_waitstream(chan, "");
3033                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3034                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3035                         goto leave_vm_out;
3036                 }
3037
3038                 /* Now play the beep once we have the message number for our next message. */
3039                 if (res >= 0) {
3040                         /* Unless we're *really* silent, try to send the beep */
3041                         res = ast_stream_and_wait(chan, "beep", chan->language, "");
3042                 }
3043                                 
3044                 /* Store information */
3045                 txt = fdopen(txtdes, "w+");
3046                 if (txt) {
3047                         get_date(date, sizeof(date));
3048                         fprintf(txt, 
3049                                 ";\n"
3050                                 "; Message Information file\n"
3051                                 ";\n"
3052                                 "[message]\n"
3053                                 "origmailbox=%s\n"
3054                                 "context=%s\n"
3055                                 "macrocontext=%s\n"
3056                                 "exten=%s\n"
3057                                 "priority=%d\n"
3058                                 "callerchan=%s\n"
3059                                 "callerid=%s\n"
3060                                 "origdate=%s\n"
3061                                 "origtime=%ld\n"
3062                                 "category=%s\n",
3063                                 ext,
3064                                 chan->context,
3065                                 chan->macrocontext, 
3066                                 chan->exten,
3067                                 chan->priority,
3068                                 chan->name,
3069                                 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
3070                                 date, (long)time(NULL),
3071                                 category ? category : ""); 
3072                 } else
3073                         ast_log(LOG_WARNING, "Error opening text file for output\n");
3074                 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
3075                                 
3076                 if (txt) {
3077                         if (duration < vmminmessage) {
3078                                 if (option_verbose > 2) 
3079                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3080                                 ast_filedelete(tmptxtfile, NULL);
3081                                 unlink(tmptxtfile);
3082                         } else {
3083                                 fprintf(txt, "duration=%d\n", duration);
3084                                 fclose(txt);
3085                                 if (vm_lock_path(dir)) {
3086                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3087                                         /* Delete files */
3088                                         ast_filedelete(tmptxtfile, NULL);
3089                                         unlink(tmptxtfile);
3090                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3091                                         if (option_debug) 
3092                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3093                                         unlink(tmptxtfile);
3094                                         ast_unlock_path(dir);
3095                                 } else {
3096                                         msgnum = last_message_index(vmu, dir) + 1;
3097                                         make_file(fn, sizeof(fn), dir, msgnum);
3098
3099                                         /* assign a variable with the name of the voicemail file */ 
3100 #ifndef IMAP_STORAGE
3101                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3102 #else
3103                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3104 #endif
3105
3106                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3107                                         ast_filerename(tmptxtfile, fn, NULL);
3108                                         rename(tmptxtfile, txtfile);
3109
3110                                         ast_unlock_path(dir);
3111 #ifndef IMAP_STORAGE
3112                                         /* Are there to be more recipients of this message? */
3113                                         while (tmpptr) {
3114                                                 struct ast_vm_user recipu, *recip;
3115                                                 char *exten, *context;
3116                                         
3117                                                 exten = strsep(&tmpptr, "&");
3118                                                 context = strchr(exten, '@');
3119                                                 if (context) {
3120                                                         *context = '\0';
3121                                                         context++;
3122                                                 }
3123                                                 if ((recip = find_user(&recipu, context, exten))) {
3124                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3125                                                         free_user(recip);
3126                                                 }
3127                                         }
3128 #endif
3129                                         if (ast_fileexists(fn, NULL, NULL)) {
3130                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
3131                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
3132                                                 DISPOSE(dir, msgnum);
3133                                         }
3134                                 }
3135                         }
3136                 }
3137                 if (res == '0') {
3138                         goto transfer;
3139                 } else if (res > 0)
3140                         res = 0;
3141
3142                 if (duration < vmminmessage)
3143                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3144                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3145                 else
3146                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3147         } else
3148                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3149 leave_vm_out:
3150         free_user(vmu);
3151         
3152         return res;
3153 }
3154
3155 #ifndef IMAP_STORAGE
3156 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3157 {
3158         /* we know max messages, so stop process when number is hit */
3159
3160         int x,dest;
3161         char sfn[256];
3162         char dfn[256];
3163
3164         if (vm_lock_path(dir))
3165                 return ERROR_LOCK_PATH;
3166
3167         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3168                 make_file(sfn, sizeof(sfn), dir, x);
3169                 if (EXISTS(dir, x, sfn, NULL)) {
3170                         
3171                         if (x != dest) {
3172                                 make_file(dfn, sizeof(dfn), dir, dest);
3173                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3174                         }
3175                         
3176                         dest++;
3177                 }
3178         }
3179         ast_unlock_path(dir);
3180
3181         return 0;
3182 }
3183 #endif
3184
3185 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3186 {
3187         int d;
3188         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3189         return d;
3190 }
3191
3192 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3193 {
3194 #ifdef IMAP_STORAGE
3195         /* we must use mbox(x) folder names, and copy the message there */
3196         /* simple. huh? */
3197         char dbox[256];
3198         long res;
3199         char sequence[10];
3200
3201         /* if save to Old folder, just leave in INBOX */
3202         if (box == 1) return 10;
3203         /* get the real IMAP message number for this message */
3204         sprintf(sequence,"%ld",vms->msgArray[msg]);
3205         imap_mailbox_name(dbox, vms, box, 1);
3206         if (option_debug > 2)
3207                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3208         res = mail_copy(vms->mailstream, sequence, dbox);
3209         if (res == 1) return 0;
3210         return 1;
3211 #else
3212         char *dir = vms->curdir;
3213         char *username = vms->username;
3214         char *context = vmu->context;
3215         char sfn[256];
3216         char dfn[256];
3217         char ddir[256];
3218         const char *dbox = mbox(box);
3219         int x;
3220         make_file(sfn, sizeof(sfn), dir, msg);
3221         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3222
3223         if (vm_lock_path(ddir))
3224                 return ERROR_LOCK_PATH;
3225
3226         x = last_message_index(vmu, ddir) + 1;
3227         make_file(dfn, sizeof(dfn), ddir, x);
3228
3229         if (x >= vmu->maxmsg) {
3230                 ast_unlock_path(ddir);
3231                 return -1;
3232         }
3233         if (strcmp(sfn, dfn)) {
3234                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3235         }
3236         ast_unlock_path(ddir);
3237 #endif
3238         return 0;
3239 }
3240
3241 static int adsi_logo(unsigned char *buf)
3242 {
3243         int bytes = 0;
3244         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3245         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
3246         return bytes;
3247 }
3248
3249 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3250 {
3251         unsigned char buf[256];
3252         int bytes=0;
3253         int x;
3254         char num[5];
3255
3256         *useadsi = 0;
3257         bytes += ast_adsi_data_mode(buf + bytes);
3258         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3259
3260         bytes = 0;
3261         bytes += adsi_logo(buf);
3262         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3263 #ifdef DISPLAY
3264         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3265 #endif
3266         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3267         bytes += ast_adsi_data_mode(buf + bytes);
3268         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3269
3270         if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3271                 bytes = 0;
3272                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3273                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3274                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3275                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3276                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3277                 return 0;
3278         }
3279
3280 #ifdef DISPLAY
3281         /* Add a dot */
3282         bytes = 0;
3283         bytes += ast_adsi_logo(buf);
3284         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3285         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3286         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3287         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3288 #endif
3289         bytes = 0;
3290         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3291         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3292         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3293         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3294         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3295         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3296         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3297
3298 #ifdef DISPLAY
3299         /* Add another dot */
3300         bytes = 0;
3301         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
3302         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3303
3304         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3305         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3306 #endif
3307
3308         bytes = 0;
3309         /* These buttons we load but don't use yet */
3310         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3311         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3312         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3313         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3314         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3315         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3316         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3317
3318 #ifdef DISPLAY
3319         /* Add another dot */
3320         bytes = 0;
3321         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
3322         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3323         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3324 #endif
3325
3326         bytes = 0;
3327         for (x=0;x<5;x++) {
3328                 snprintf(num, sizeof(num), "%d", x);
3329                 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3330         }
3331         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3332         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3333
3334 #ifdef DISPLAY
3335         /* Add another dot */
3336         bytes = 0;
3337         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
3338         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3339         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3340 #endif
3341
3342         if (ast_adsi_end_download(chan)) {
3343                 bytes = 0;
3344                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3345                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3346                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3347                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3348                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3349                 return 0;
3350         }
3351         bytes = 0;
3352         bytes += ast_adsi_download_disconnect(buf + bytes);
3353         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3354         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3355
3356         if (option_debug)
3357                 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3358
3359 #ifdef DISPLAY
3360         /* Add last dot */
3361         bytes = 0;
3362         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
3363         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3364 #endif
3365         if (option_debug)
3366                 ast_log(LOG_DEBUG, "Restarting session...\n");
3367
3368         bytes = 0;
3369         /* Load the session now */
3370         if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3371                 *useadsi = 1;
3372                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3373         } else
3374                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3375
3376         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3377         return 0;
3378 }
3379
3380 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3381 {
3382         int x;
3383         if (!ast_adsi_available(chan))
3384                 return;
3385         x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3386         if (x < 0)
3387                 return;
3388         if (!x) {
3389                 if (adsi_load_vmail(chan, useadsi)) {
3390                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3391                         return;
3392                 }
3393         } else
3394                 *useadsi = 1;
3395 }
3396
3397 static void adsi_login(struct ast_channel *chan)
3398 {
3399         unsigned char buf[256];
3400         int bytes=0;
3401         unsigned char keys[8];
3402         int x;
3403         if (!ast_adsi_available(chan))
3404                 return;
3405
3406         for (x=0;x<8;x++)
3407                 keys[x] = 0;
3408         /* Set one key for next */
3409         keys[3] = ADSI_KEY_APPS + 3;
3410
3411         bytes += adsi_logo(buf + bytes);
3412         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3413         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3414         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3415         bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3416         bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3417         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3418         bytes += ast_adsi_set_keys(buf + bytes, keys);
3419         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3420         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3421 }
3422
3423 static void adsi_password(struct ast_channel *chan)
3424 {
3425         unsigned char buf[256];
3426         int bytes=0;
3427         unsigned char keys[8];
3428         int x;
3429         if (!ast_adsi_available(chan))
3430                 return;
3431
3432         for (x=0;x<8;x++)
3433                 keys[x] = 0;
3434         /* Set one key for next */
3435         keys[3] = ADSI_KEY_APPS + 3;
3436
3437         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3438         bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3439         bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3440         bytes += ast_adsi_set_keys(buf + bytes, keys);
3441         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3442         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3443 }
3444
3445 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3446 {
3447         unsigned char buf[256];
3448         int bytes=0;
3449         unsigned char keys[8];
3450         int x,y;
3451
3452         if (!ast_adsi_available(chan))
3453                 return;
3454
3455         for (x=0;x<5;x++) {
3456                 y = ADSI_KEY_APPS + 12 + start + x;
3457                 if (y > ADSI_KEY_APPS + 12 + 4)
3458                         y = 0;
3459                 keys[x] = ADSI_KEY_SKT | y;
3460         }
3461         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3462         keys[6] = 0;
3463         keys[7] = 0;
3464
3465         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3466         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3467         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3468         bytes += ast_adsi_set_keys(buf + bytes, keys);
3469         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3470
3471         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3472 }
3473
3474 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3475 {
3476         int bytes=0;
3477         unsigned char buf[256]; 
3478         char buf