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