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