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