7fb3b4388f5e0b743a43bc546cc8473cb1955e00
[asterisk/asterisk.git] / apps / app_voicemail2.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Voicemail System (did you ever think it could be so easy?)
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/lock.h>
15 #include <asterisk/file.h>
16 #include <asterisk/logger.h>
17 #include <asterisk/channel.h>
18 #include <asterisk/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <sys/time.h>
35 #include <sys/stat.h>
36 #include <time.h>
37 #ifdef USEMYSQLVM
38 #include <mysql.h>
39 #endif
40
41 #include <pthread.h>
42 #include "../asterisk.h"
43 #include "../astconf.h"
44
45 #define COMMAND_TIMEOUT 5000
46
47 #define VOICEMAIL_CONFIG "voicemail.conf"
48 #define ASTERISK_USERNAME "asterisk"
49
50 #define SENDMAIL "/usr/sbin/sendmail -t"
51
52 #define INTRO "vm-intro"
53
54 #define MAXMSG 100
55 #define MAX_OTHER_FORMATS 10
56
57 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
58
59 #define BASEMAXINLINE 256
60 #define BASELINELEN 72
61 #define BASEMAXINLINE 256
62 #define eol "\r\n"
63
64 #define MAX_DATETIME_FORMAT     512
65 #define DIGITS_DIR      AST_SOUNDS "/digits/"
66 struct baseio {
67         int iocp;
68         int iolen;
69         int linelength;
70         int ateof;
71         unsigned char iobuf[BASEMAXINLINE];
72 };
73
74 struct ast_vm_user {
75         char context[80];
76         char mailbox[80];
77         char password[80];
78         char fullname[80];
79         char email[80];
80         char pager[80];
81         char serveremail[80];
82         char zonetag[80];
83         int attach;
84         int alloced;
85         struct ast_vm_user *next;
86 };
87
88 struct vm_zone {
89         char name[80];
90         char timezone[80];
91         char msg_format[512];
92         struct vm_zone *next;
93 };
94
95 static char *tdesc = "Comedian Mail (Voicemail System)";
96
97 static char *adapp = "CoMa";
98
99 static char *adsec = "_AST";
100
101 static char *addesc = "Comedian Mail";
102
103 static int adver = 1;
104
105 static char *synopsis_vm =
106 "Leave a voicemail message";
107
108 static char *descrip_vm =
109 "  VoiceMail([s|u|b]extension[@context]): Leaves voicemail for a given  extension (must\n"
110 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
111 "then instructions for leaving the message will be skipped.  If the extension\n"
112 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
113 "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.  If the extension\n"
114 "is preceeded by a 'b' then the the busy message will be played (that is,\n"
115 "busy instead of unavail). \n"
116 "Returns  -1 on  error or mailbox not found, or if the user hangs up. \n"
117 "Otherwise, it returns 0. \n";
118
119 static char *synopsis_vmain =
120 "Enter voicemail system";
121
122 static char *descrip_vmain =
123 "  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system for the checking of\n"
124 "voicemail.  The mailbox can be passed as the option, which will stop the\n"
125 "voicemail system from prompting the user for the mailbox.  If the mailbox\n"
126 "is preceded by 's' then the password check will be skipped.  If a context is\n"
127 "specified, logins are considered in that context only. Returns -1 if\n"
128 "the user hangs up or 0 otherwise.\n";
129
130 /* Leave a message */
131 static char *app = "VoiceMail2";
132
133 /* Check mail, control, etc */
134 static char *app2 = "VoiceMailMain2";
135
136 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
137 struct ast_vm_user *users;
138 struct ast_vm_user *usersl;
139 struct vm_zone *zones = NULL;
140 struct vm_zone *zonesl = NULL;
141 static int attach_voicemail;
142 static int maxsilence;
143 static int silencethreshold = 128;
144 static char serveremail[80];
145 static char vmfmts[80];
146 static int vmmaxmessage;
147 static int maxgreet;
148 static int skipms;
149 static int maxlogins;
150
151 static char *emailbody = NULL;
152 static int pbxskip = 0;
153 static char fromstring[15];
154 static char emailtitle[100];
155
156 STANDARD_LOCAL_USER;
157
158 LOCAL_USER_DECL;
159
160 static void apply_options(struct ast_vm_user *vmu, char *options)
161 {
162         /* Destructively Parse options and apply */
163         char *stringp = ast_strdupa(options);
164         char *s;
165         char *var, *value;
166         while((s = strsep(&stringp, "|"))) {
167                 value = stringp;
168                 if ((var = strsep(&value, "=")) && value) {
169                         if (!strcasecmp(var, "attach")) {
170                                 if (ast_true(value))
171                                         vmu->attach = 1;
172                                 else
173                                         vmu->attach = 0;
174                         } else if (!strcasecmp(var, "serveremail")) {
175                                 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
176                         } else if (!strcasecmp(var, "tz")) {
177                                 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
178                         }
179                 }
180         }
181         
182 }
183
184 #ifdef USEMYSQLVM
185 MYSQL *dbhandler=NULL;
186 ast_mutex_t mysqllock;
187 char dbuser[80];
188 char dbpass[80];
189 char dbhost[80];
190 char dbname[80];
191
192 static int mysql_login(void)
193 {
194         ast_verbose( VERBOSE_PREFIX_3 "Logging into database with user %s, password %s, and database %s\n", dbuser, dbpass, dbname);
195
196         dbhandler=mysql_init(NULL);
197         if (!mysql_real_connect(dbhandler, dbhost[0] ? dbhost : NULL, dbuser, dbpass, dbname, 0, NULL, 0)) {
198                 ast_log(LOG_WARNING, "Error Logging into database\n");
199                 return(-1);
200         }
201         ast_mutex_init(&mysqllock);
202         return(0);
203 }
204
205 static void mysql_logout(void)
206 {
207         mysql_close(dbhandler);
208 }
209
210 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
211 {
212         MYSQL_RES *result;
213         MYSQL_ROW rowval;
214         MYSQL_FIELD *fields;
215         int numFields, i;
216         char query[240];
217         char options[160] = "";
218         struct ast_vm_user *retval;
219
220         retval=malloc(sizeof(struct ast_vm_user));
221
222         if (retval) {
223                 *retval->mailbox='\0';
224                 *retval->context='\0';
225                 *retval->password='\0';
226                 *retval->fullname='\0';
227                 *retval->email='\0';
228                 *retval->pager='\0';
229                 *retval->serveremail='\0';
230                 retval->attach=-1;
231                 retval->alloced=1;
232                 retval->next=NULL;
233                 if (mailbox) {
234                         strcpy(retval->mailbox, mailbox);
235                 }
236                 if (context) {
237                         strcpy(retval->context, context);
238                 }
239
240                 if (*retval->context) {
241                         sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE context='%s' AND mailbox='%s'", context, mailbox);
242                 } else {
243                         sprintf(query, "SELECT password,fullname,email,pager,options FROM users WHERE mailbox='%s'", mailbox);
244                 }
245                 ast_mutex_lock(&mysqllock);
246                 mysql_query(dbhandler, query);
247                 if ((result=mysql_store_result(dbhandler))!=NULL) {
248                         if ((rowval=mysql_fetch_row(result))!=NULL) {
249                                 numFields=mysql_num_fields(result);
250                                 fields=mysql_fetch_fields(result);
251                                 for (i=0; i<numFields; i++) {
252                                         if (rowval[i]) {
253                                                 if (!strcmp(fields[i].name, "password")) {
254                                                         strcpy(retval->password, rowval[i]);
255                                                 } else if (!strcmp(fields[i].name, "fullname")) {
256                                                         strcpy(retval->fullname, rowval[i]);
257                                                 } else if (!strcmp(fields[i].name, "email")) {
258                                                         strcpy(retval->email, rowval[i]);
259                                                 } else if (!strcmp(fields[i].name, "pager")) {
260                                                         strcpy(retval->pager, rowval[i]);
261                                                 } else if (!strcmp(fields[i].name, "options")) {
262                                                         strncpy(options, rowval[i], sizeof(options) - 1);
263                                                         apply_options(retval, options);
264                                                 }
265                                         }
266                                 }
267                                 mysql_free_result(result);
268                                 ast_mutex_unlock(&mysqllock);
269                                 return(retval);
270                         } else {
271                                 mysql_free_result(result);
272                                 ast_mutex_unlock(&mysqllock);
273                                 free(retval);
274                                 return(NULL);
275                         }
276                 }
277                 ast_mutex_unlock(&mysqllock);
278                 free(retval);
279         }
280         return(NULL);
281 }
282
283 static void vm_change_password(struct ast_vm_user *vmu, char *password)
284 {
285         char query[400];
286
287         if (*vmu->context) {
288                 sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s' AND password='%s'", password, vmu->context, vmu->mailbox, vmu->password);
289         } else {
290                 sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s' AND password='%s'", password, vmu->mailbox, vmu->password);
291         }
292         ast_mutex_lock(&mysqllock);
293         mysql_query(dbhandler, query);
294         strcpy(vmu->password, password);
295         ast_mutex_unlock(&mysqllock);
296 }
297
298 static void reset_user_pw(char *context, char *mailbox, char *password)
299 {
300         char query[320];
301
302         if (context) {
303                 sprintf(query, "UPDATE users SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
304         } else {
305                 sprintf(query, "UPDATE users SET password='%s' WHERE mailbox='%s'", password, mailbox);
306         }
307         ast_mutex_lock(&mysqllock);
308         mysql_query(dbhandler, query);
309         ast_mutex_unlock(&mysqllock);
310 }
311 #else
312
313 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
314 {
315         /* This function could be made to generate one from a database, too */
316         struct ast_vm_user *vmu=NULL, *cur;
317         ast_mutex_lock(&vmlock);
318         cur = users;
319         while(cur) {
320                 if ((!context || !strcasecmp(context, cur->context)) &&
321                         (!strcasecmp(mailbox, cur->mailbox)))
322                                 break;
323                 cur=cur->next;
324         }
325         if (cur) {
326                 if (ivm)
327                         vmu = ivm;
328                 else
329                         /* Make a copy, so that on a reload, we have no race */
330                         vmu = malloc(sizeof(struct ast_vm_user));
331                 if (vmu) {
332                         memcpy(vmu, cur, sizeof(struct ast_vm_user));
333                         if (ivm)
334                                 vmu->alloced = 0;
335                         else
336                                 vmu->alloced = 1;
337                         vmu->next = NULL;
338                 }
339         }
340         ast_mutex_unlock(&vmlock);
341         return vmu;
342 }
343
344 static int reset_user_pw(char *context, char *mailbox, char *newpass)
345 {
346         /* This function could be made to generate one from a database, too */
347         struct ast_vm_user *cur;
348         int res = -1;
349         ast_mutex_lock(&vmlock);
350         cur = users;
351         while(cur) {
352                 if ((!context || !strcasecmp(context, cur->context)) &&
353                         (!strcasecmp(mailbox, cur->mailbox)))
354                                 break;
355                 cur=cur->next;
356         }
357         if (cur) {
358                 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
359                 res = 0;
360         }
361         ast_mutex_unlock(&vmlock);
362         return res;
363 }
364
365 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
366 {
367         /*  There's probably a better way of doing this. */
368         /*  That's why I've put the password change in a separate function. */
369                 /*  This could also be done with a database function */
370         
371         FILE *configin;
372         FILE *configout;
373                 char inbuf[256];
374                 char orig[256];
375                 char tmpin[AST_CONFIG_MAX_PATH];
376                 char tmpout[AST_CONFIG_MAX_PATH];
377                 char *user, *pass, *rest, *trim;
378         snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
379         snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
380         configin = fopen((char *)tmpin,"r");
381         configout = fopen((char *)tmpout,"w+");
382
383         while (!feof(configin)) {
384                         /* Read in the line */
385                         fgets(inbuf, sizeof(inbuf), configin);
386                         if (!feof(configin)) {
387                                 /* Make a backup of it */
388                                 memcpy(orig, inbuf, sizeof(orig));
389                                 /* Strip trailing \n and comment */
390                                 inbuf[strlen(inbuf) - 1] = '\0';
391                                 user = strchr(inbuf, ';');
392                                 if (user)
393                                         *user = '\0';
394                                 user=inbuf;
395                                 while(*user < 33)
396                                         user++;
397                                 pass = strchr(user, '=');
398                                 if (pass > user) {
399                                         trim = pass - 1;
400                                         while(*trim && *trim < 33) {
401                                                 *trim = '\0';
402                                                 trim--;
403                                         }
404                                 }
405                                 if (pass) {
406                                         *pass = '\0';
407                                         pass++;
408                                         if (*pass == '>')
409                                                 pass++;
410                                         while(*pass && *pass < 33)
411                                                 pass++;
412                                 }
413                                 if (pass) {
414                                         rest = strchr(pass,',');
415                                         if (rest) {
416                                                 *rest = '\0';
417                                                 rest++;
418                                         }
419                                 } else
420                                         rest = NULL;
421                                 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
422                                         /* This is the line */
423                                         if (rest) {
424                                                 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
425                                         } else {
426                                                 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
427                                         }
428                                 } else {
429                                         /* Put it back like it was */
430                                         fprintf(configout, orig);
431                                 }
432                         }
433         }
434         fclose(configin);
435         fclose(configout);
436
437         unlink((char *)tmpin);
438         rename((char *)tmpout,(char *)tmpin);
439         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
440         strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
441 }
442 #endif
443
444 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
445 {
446         return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
447 }
448
449 static int make_file(char *dest, int len, char *dir, int num)
450 {
451         return snprintf(dest, len, "%s/msg%04d", dir, num);
452 }
453
454 static int
455 inbuf(struct baseio *bio, FILE *fi)
456 {
457         int l;
458
459         if(bio->ateof)
460                 return 0;
461
462         if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
463                 if(ferror(fi))
464                         return -1;
465
466                 bio->ateof = 1;
467                 return 0;
468         }
469
470         bio->iolen= l;
471         bio->iocp= 0;
472
473         return 1;
474 }
475
476 static int 
477 inchar(struct baseio *bio, FILE *fi)
478 {
479         if(bio->iocp>=bio->iolen)
480                 if(!inbuf(bio, fi))
481                         return EOF;
482
483         return bio->iobuf[bio->iocp++];
484 }
485
486 static int
487 ochar(struct baseio *bio, int c, FILE *so)
488 {
489         if(bio->linelength>=BASELINELEN) {
490                 if(fputs(eol,so)==EOF)
491                         return -1;
492
493                 bio->linelength= 0;
494         }
495
496         if(putc(((unsigned char)c),so)==EOF)
497                 return -1;
498
499         bio->linelength++;
500
501         return 1;
502 }
503
504 static int base_encode(char *filename, FILE *so)
505 {
506         unsigned char dtable[BASEMAXINLINE];
507         int i,hiteof= 0;
508         FILE *fi;
509         struct baseio bio;
510
511         memset(&bio, 0, sizeof(bio));
512         bio.iocp = BASEMAXINLINE;
513
514         if ( !(fi = fopen(filename, "rb"))) {
515                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
516                 return -1;
517         }
518
519         for(i= 0;i<9;i++){
520                 dtable[i]= 'A'+i;
521                 dtable[i+9]= 'J'+i;
522                 dtable[26+i]= 'a'+i;
523                 dtable[26+i+9]= 'j'+i;
524         }
525         for(i= 0;i<8;i++){
526                 dtable[i+18]= 'S'+i;
527                 dtable[26+i+18]= 's'+i;
528         }
529         for(i= 0;i<10;i++){
530                 dtable[52+i]= '0'+i;
531         }
532         dtable[62]= '+';
533         dtable[63]= '/';
534
535         while(!hiteof){
536                 unsigned char igroup[3],ogroup[4];
537                 int c,n;
538
539                 igroup[0]= igroup[1]= igroup[2]= 0;
540
541                 for(n= 0;n<3;n++){
542                         if ( (c = inchar(&bio, fi)) == EOF) {
543                                 hiteof= 1;
544                                 break;
545                         }
546
547                         igroup[n]= (unsigned char)c;
548                 }
549
550                 if(n> 0){
551                         ogroup[0]= dtable[igroup[0]>>2];
552                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
553                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
554                         ogroup[3]= dtable[igroup[2]&0x3F];
555
556                         if(n<3) {
557                                 ogroup[3]= '=';
558
559                                 if(n<2)
560                                         ogroup[2]= '=';
561                         }
562
563                         for(i= 0;i<4;i++)
564                                 ochar(&bio, ogroup[i], so);
565                 }
566         }
567
568         if(fputs(eol,so)==EOF)
569                 return 0;
570
571         fclose(fi);
572
573         return 1;
574 }
575
576 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration, int attach_user_voicemail)
577 {
578         FILE *p;
579         char date[256];
580         char host[256];
581         char who[256];
582         char bound[256];
583         char fname[256];
584         char dur[256];
585         time_t t;
586         struct tm tm;
587         if (!strcmp(format, "wav49"))
588                 format = "WAV";
589         ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
590         p = popen(SENDMAIL, "w");
591         if (p) {
592                 gethostname(host, sizeof(host));
593                 if (strchr(srcemail, '@'))
594                         strncpy(who, srcemail, sizeof(who)-1);
595                 else {
596                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
597                 }
598                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
599                 time(&t);
600                 localtime_r(&t,&tm);
601                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
602                 fprintf(p, "Date: %s\n", date);
603                 
604                 if (*fromstring)
605                         fprintf(p, "From: %s <%s>\n", fromstring, who);
606                 else
607                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
608                 fprintf(p, "To: %s <%s>\n", name, email);
609
610                 if( *emailtitle)
611                 {
612                         fprintf(p, emailtitle, msgnum, mailbox) ;
613                         fprintf(p,"\n") ;
614                 }
615                 else
616                 if (pbxskip)
617                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum, mailbox);
618                 else
619                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
620                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
621                 fprintf(p, "MIME-Version: 1.0\n");
622                 if (attach_user_voicemail) {
623                         // Something unique.
624                         snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
625
626                         fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
627
628                         fprintf(p, "--%s\n", bound);
629                 }
630                 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
631                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
632                 if (emailbody) {
633                         struct ast_channel *ast = ast_channel_alloc(0);
634                         if (ast) {
635                                 char *passdata;
636                                 int vmlen = strlen(emailbody)*2;
637                                 if (vmlen < 20)
638                                         vmlen = 100;
639                                 passdata = alloca(vmlen);
640                                 bzero( passdata, vmlen );
641                                 pbx_builtin_setvar_helper(ast, "VM_NAME", name);
642                                 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
643                                 sprintf(passdata,"%d",msgnum);
644                                 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
645                                 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
646                                 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
647                                 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
648                                 pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
649                                 fprintf(p, "%s\n",passdata);
650                                 ast_channel_free(ast);
651                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
652                 } else {
653                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
654
655                         "in mailbox %s from %s, on %s so you might\n"
656                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", name, 
657                         dur, msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
658                 }
659                 if (attach_user_voicemail) {
660                         fprintf(p, "--%s\n", bound);
661                         fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum, format);
662                         fprintf(p, "Content-Transfer-Encoding: BASE64\n");
663                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
664                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
665
666                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
667                         base_encode(fname, p);
668                         fprintf(p, "\n\n--%s--\n.\n", bound);
669                 }
670                 pclose(p);
671         } else {
672                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
673                 return -1;
674         }
675         return 0;
676 }
677
678 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration)
679 {
680         FILE *p;
681         char date[256];
682         char host[256];
683         char who[256];
684         char dur[256];
685         time_t t;
686         struct tm tm;
687         p = popen(SENDMAIL, "w");
688
689         if (p) {
690                 gethostname(host, sizeof(host));
691                 if (strchr(srcemail, '@'))
692                         strncpy(who, srcemail, sizeof(who)-1);
693                 else {
694                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
695                 }
696                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
697                 time(&t);
698                 localtime_r(&t,&tm);
699                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
700                 fprintf(p, "Date: %s\n", date);
701                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
702                 fprintf(p, "To: %s\n", pager);
703                 fprintf(p, "Subject: New VM\n\n");
704                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
705                 fprintf(p, "New %s long msg in box %s\n"
706                            "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
707                 pclose(p);
708         } else {
709                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
710                 return -1;
711         }
712         return 0;
713 }
714
715 static int get_date(char *s, int len)
716 {
717         struct tm tm;
718         time_t t;
719         t = time(0);
720         localtime_r(&t,&tm);
721         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
722 }
723
724 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
725 {
726         int res;
727         char fn[256];
728         snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
729         if (ast_fileexists(fn, NULL, NULL) > 0) {
730                 res = ast_streamfile(chan, fn, chan->language);
731                 if (res)
732                         return -1;
733                 res = ast_waitstream(chan, ecodes);
734                 if (res)
735                         return res;
736         } else {
737                 res = ast_streamfile(chan, "vm-theperson", chan->language);
738                 if (res)
739                         return -1;
740                 res = ast_waitstream(chan, ecodes);
741                 if (res)
742                         return res;
743                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
744                 if (res)
745                         return res;
746         }
747         if (busy)
748                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
749         else
750                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
751         if (res)
752                 return -1;
753         res = ast_waitstream(chan, ecodes);
754         return res;
755 }
756
757 static int play_and_wait(struct ast_channel *chan, char *fn)
758 {
759         int d;
760         d = ast_streamfile(chan, fn, chan->language);
761         if (d)
762                 return d;
763         d = ast_waitstream(chan, AST_DIGIT_ANY);
764         return d;
765 }
766
767 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
768 {
769         char d, *fmts;
770         char comment[256];
771         int x, fmtcnt=1, res=-1,outmsg=0;
772         struct ast_frame *f;
773         struct ast_filestream *others[MAX_OTHER_FORMATS];
774         char *sfmt[MAX_OTHER_FORMATS];
775         char *stringp=NULL;
776         time_t start, end;
777         struct ast_dsp *sildet;         /* silence detector dsp */
778         int totalsilence = 0;
779         int dspsilence = 0;
780         int gotsilence = 0;             /* did we timeout for silence? */
781         int rfmt=0;     
782         
783         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
784         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
785
786         if (playfile) { 
787                 d = play_and_wait(chan, playfile);
788                 if (!d)
789                         d = ast_streamfile(chan, "beep",chan->language);
790                 if (!d)
791                         d = ast_waitstream(chan,"");
792                 if (d < 0)
793                         return -1;
794         }
795         
796         fmts = ast_strdupa(fmt);
797         
798         stringp=fmts;
799         strsep(&stringp, "|");
800         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
801         sfmt[0] = ast_strdupa(fmts);
802         
803         while((fmt = strsep(&stringp, "|"))) {
804                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
805                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
806                         break;
807                 }
808                 sfmt[fmtcnt++] = ast_strdupa(fmt);
809         }
810
811         if (maxtime)
812                 time(&start);
813         for (x=0;x<fmtcnt;x++) {
814                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
815                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
816                         
817                 if (!others[x]) {
818                         break;
819                 }
820         }
821         
822         sildet = ast_dsp_new(); //Create the silence detector
823         if (!sildet) {
824                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
825                 return -1;
826         }
827         ast_dsp_set_threshold(sildet, silencethreshold);
828         
829         if (maxsilence > 0) {
830                 rfmt = chan->readformat;
831                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
832                 if (res < 0) {
833                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
834                         return -1;
835                 }
836         }
837                                                 
838         if (x == fmtcnt) {
839         /* Loop forever, writing the packets we read to the writer(s), until
840            we read a # or get a hangup */
841                 f = NULL;
842                 for(;;) {
843                         res = ast_waitfor(chan, 2000);
844                         if (!res) {
845                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
846                                 /* Try one more time in case of masq */
847                                 res = ast_waitfor(chan, 2000);
848                                 if (!res) {
849                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
850                                         res = -1;
851                                 }
852                         }
853                         
854                         if (res < 0) {
855                                 f = NULL;
856                                 break;
857                         }
858                         f = ast_read(chan);
859                         if (!f)
860                                 break;
861                         if (f->frametype == AST_FRAME_VOICE) {
862                                 /* write each format */
863                                 for (x=0;x<fmtcnt;x++) {
864                                         res = ast_writestream(others[x], f);
865                                 }
866                                 
867                                 /* Silence Detection */
868                                 if (maxsilence > 0) {
869                                         dspsilence = 0;
870                                         ast_dsp_silence(sildet, f, &dspsilence);
871                                         if (dspsilence)
872                                                 totalsilence = dspsilence;
873                                         else
874                                                 totalsilence = 0;
875                                         
876                                         if (totalsilence > maxsilence) {
877                                         /* Ended happily with silence */
878                                         ast_frfree(f);
879                                         gotsilence = 1;
880                                         outmsg=2;
881                                         break;
882                                         }
883                                 }
884                                 /* Exit on any error */
885                                 if (res) {
886                                         ast_log(LOG_WARNING, "Error writing frame\n");
887                                         ast_frfree(f);
888                                         break;
889                                 }
890                         } else if (f->frametype == AST_FRAME_VIDEO) {
891                                 /* Write only once */
892                                 ast_writestream(others[0], f);
893                         } else if (f->frametype == AST_FRAME_DTMF) {
894                                 if (f->subclass == '#') {
895                                         if (option_verbose > 2) 
896                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
897                                         res = '#';
898                                         outmsg = 2;
899                                         ast_frfree(f);
900                                         break;
901                                 }
902                         }
903                         if (maxtime) {
904                                 time(&end);
905                                 if (maxtime < (end - start)) {
906                                         if (option_verbose > 2)
907                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
908                                         res = 't';
909                                         ast_frfree(f);
910                                         break;
911                                 }
912                         }
913                         ast_frfree(f);
914                 }
915                 if (!f) {
916                         if (option_verbose > 2) 
917                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
918                         res = -1;
919                         outmsg=1;
920                 }
921         } else {
922                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
923         }
924
925         for (x=0;x<fmtcnt;x++) {
926                 if (!others[x])
927                         break;
928                 if (totalsilence)
929                         ast_stream_rewind(others[x], totalsilence-200);
930                 else
931                         ast_stream_rewind(others[x], 200);
932                 ast_truncstream(others[x]);
933                 ast_closestream(others[x]);
934         }
935         if (rfmt) {
936                 if (ast_set_read_format(chan, rfmt)) {
937                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
938                 }
939         }
940         if (outmsg) {
941                 if (outmsg > 1) {
942                 /* Let them know it worked */
943                         ast_streamfile(chan, "vm-msgsaved", chan->language);
944                         ast_waitstream(chan, "");
945                 }
946         }       
947
948         
949         return res;
950 }
951
952 static void free_user(struct ast_vm_user *vmu)
953 {
954         if (vmu->alloced)
955                 free(vmu);
956 }
957
958 static void free_zone(struct vm_zone *z)
959 {
960         free(z);
961 }
962
963 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
964 {
965         char comment[256];
966         char txtfile[256];
967         FILE *txt;
968         int res = 0;
969         int msgnum;
970         char date[256];
971         char dir[256];
972         char fn[256];
973         char prefile[256]="";
974         char fmt[80];
975         char *context;
976         char *ecodes = "#";
977         char *stringp;
978         time_t start;
979         time_t end;
980         char tmp[256] = "";
981         struct ast_vm_user *vmu;
982         struct ast_vm_user svm;
983         
984         strncpy(tmp, ext, sizeof(tmp) - 1);
985         ext = tmp;
986         context = strchr(tmp, '@');
987         if (context) {
988                 *context = '\0';
989                 context++;
990         }
991
992         if ((vmu = find_user(&svm, context, ext))) {
993                 /* Setup pre-file if appropriate */
994                 if (busy)
995                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
996                 else if (unavail)
997                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
998                 make_dir(dir, sizeof(dir), vmu->context, "", "");
999                 /* It's easier just to try to make it than to check for its existence */
1000                 if (mkdir(dir, 0700) && (errno != EEXIST))
1001                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1002                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1003                 /* It's easier just to try to make it than to check for its existence */
1004                 if (mkdir(dir, 0700) && (errno != EEXIST))
1005                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1006                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1007                 if (mkdir(dir, 0700) && (errno != EEXIST))
1008                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1009                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1010                         ecodes = "#0";
1011                 /* Play the beginning intro if desired */
1012                 if (strlen(prefile)) {
1013                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1014                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1015                                     res = ast_waitstream(chan, "#0");
1016                         } else {
1017                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1018                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1019                         }
1020                         if (res < 0) {
1021                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1022                                 free_user(vmu);
1023                                 return -1;
1024                         }
1025                 }
1026                 if (res == '#') {
1027                         /* On a '#' we skip the instructions */
1028                         silent = 1;
1029                         res = 0;
1030                 }
1031                 if (!res && !silent) {
1032                         res = ast_streamfile(chan, INTRO, chan->language);
1033                         if (!res)
1034                                 res = ast_waitstream(chan, ecodes);
1035                         if (res == '#') {
1036                                 silent = 1;
1037                                 res = 0;
1038                         }
1039                 }
1040                 /* Check for a '0' here */
1041                 if (res == '0') {
1042                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1043                         if (strlen(chan->macrocontext))
1044                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1045                         chan->priority = 0;
1046                         free_user(vmu);
1047                         return 0;
1048                 }
1049                 if (res >= 0) {
1050                         /* Unless we're *really* silent, try to send the beep */
1051                         res = ast_streamfile(chan, "beep", chan->language);
1052                         if (!res)
1053                                 res = ast_waitstream(chan, "");
1054                 }
1055                 if (res < 0) {
1056                         free_user(vmu);
1057                         return -1;
1058                 }
1059                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1060                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1061                 if (strlen(fmt)) {
1062                         msgnum = 0;
1063                         do {
1064                                 make_file(fn, sizeof(fn), dir, msgnum);
1065                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1066                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1067                                                                         vmu->fullname, ext, chan->name);
1068                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1069                                         break;
1070                                 msgnum++;
1071                         } while(msgnum < MAXMSG);
1072                         if (msgnum < MAXMSG) {
1073                                 /* Store information */
1074                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1075                                 txt = fopen(txtfile, "w+");
1076                                 if (txt) {
1077                                         get_date(date, sizeof(date));
1078                                         time(&start);
1079                                         fprintf(txt, 
1080 ";\n"
1081 "; Message Information file\n"
1082 ";\n"
1083 "[message]\n"
1084 "origmailbox=%s\n"
1085 "context=%s\n"
1086 "exten=%s\n"
1087 "priority=%d\n"
1088 "callerchan=%s\n"
1089 "callerid=%s\n"
1090 "origdate=%s\n"
1091 "origtime=%ld\n",
1092         ext,
1093         chan->context,
1094         chan->exten,
1095         chan->priority,
1096         chan->name,
1097         chan->callerid ? chan->callerid : "Unknown",
1098         date, time(NULL));
1099                                         fclose(txt);
1100                                 } else
1101                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1102                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1103                                 if (res > 0)
1104                                         res = 0;
1105                                 txt = fopen(txtfile, "a");
1106                                 if (txt) {
1107                                         time(&end);
1108                                         fprintf(txt, "duration=%ld\n", end-start);
1109                                         fclose(txt);
1110                                 }
1111                                 stringp = fmt;
1112                                 strsep(&stringp, "|");
1113                                 /* Send e-mail if applicable */
1114                                 if (strlen(vmu->email)) {
1115                                         int attach_user_voicemail = attach_voicemail;
1116                                         char *myserveremail = serveremail;
1117                                         if (vmu->attach > -1)
1118                                                 attach_user_voicemail = vmu->attach;
1119                                         if (strlen(vmu->serveremail))
1120                                                 myserveremail = vmu->serveremail;
1121                                         sendmail(myserveremail, vmu->email, vmu->fullname, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1122                                 }
1123                                 if (strlen(vmu->pager)) {
1124                                         char *myserveremail = serveremail;
1125                                         if (strlen(vmu->serveremail))
1126                                                 myserveremail = vmu->serveremail;
1127                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start);
1128                                 }
1129                         } else
1130                                 ast_log(LOG_WARNING, "No more messages possible\n");
1131                 } else
1132                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1133                 free_user(vmu);
1134         } else
1135                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1136         /* Leave voicemail for someone */
1137         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1138         return res;
1139 }
1140
1141 static char *mbox(int id)
1142 {
1143         switch(id) {
1144         case 0:
1145                 return "INBOX";
1146         case 1:
1147                 return "Old";
1148         case 2:
1149                 return "Work";
1150         case 3:
1151                 return "Family";
1152         case 4:
1153                 return "Friends";
1154         case 5:
1155                 return "Cust1";
1156         case 6:
1157                 return "Cust2";
1158         case 7:
1159                 return "Cust3";
1160         case 8:
1161                 return "Cust4";
1162         case 9:
1163                 return "Cust5";
1164         default:
1165                 return "Unknown";
1166         }
1167 }
1168
1169 static int count_messages(char *dir)
1170 {
1171         int x;
1172         char fn[256];
1173         for (x=0;x<MAXMSG;x++) {
1174                 make_file(fn, sizeof(fn), dir, x);
1175                 if (ast_fileexists(fn, NULL, NULL) < 1)
1176                         break;
1177         }
1178         return x;
1179 }
1180
1181 static int say_and_wait(struct ast_channel *chan, int num)
1182 {
1183         int d;
1184         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1185         return d;
1186 }
1187
1188 static int copy(char *infile, char *outfile)
1189 {
1190         int ifd;
1191         int ofd;
1192         int res;
1193         int len;
1194         char buf[4096];
1195         if ((ifd = open(infile, O_RDONLY)) < 0) {
1196                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1197                 return -1;
1198         }
1199         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1200                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1201                 close(ifd);
1202                 return -1;
1203         }
1204         do {
1205                 len = read(ifd, buf, sizeof(buf));
1206                 if (len < 0) {
1207                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1208                         close(ifd);
1209                         close(ofd);
1210                         unlink(outfile);
1211                 }
1212                 if (len) {
1213                         res = write(ofd, buf, len);
1214                         if (res != len) {
1215                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1216                                 close(ifd);
1217                                 close(ofd);
1218                                 unlink(outfile);
1219                         }
1220                 }
1221         } while(len);
1222         close(ifd);
1223         close(ofd);
1224         return 0;
1225 }
1226
1227 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1228 {
1229         char sfn[256];
1230         char dfn[256];
1231         char ddir[256];
1232         char txt[256];
1233         char ntxt[256];
1234         char *dbox = mbox(box);
1235         int x;
1236         make_file(sfn, sizeof(sfn), dir, msg);
1237         make_dir(ddir, sizeof(ddir), context, username, dbox);
1238         mkdir(ddir, 0700);
1239         for (x=0;x<MAXMSG;x++) {
1240                 make_file(dfn, sizeof(dfn), ddir, x);
1241                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1242                         break;
1243         }
1244         if (x >= MAXMSG)
1245                 return -1;
1246         ast_filecopy(sfn, dfn, NULL);
1247         if (strcmp(sfn, dfn)) {
1248                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1249                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1250                 copy(txt, ntxt);
1251         }
1252         return 0;
1253 }
1254
1255 static int adsi_logo(unsigned char *buf)
1256 {
1257         int bytes = 0;
1258         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1259         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1260         return bytes;
1261 }
1262
1263 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1264 {
1265         char buf[256];
1266         int bytes=0;
1267         int x;
1268         char num[5];
1269
1270         *useadsi = 0;
1271         bytes += adsi_data_mode(buf + bytes);
1272         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1273
1274         bytes = 0;
1275         bytes += adsi_logo(buf);
1276         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1277 #ifdef DISPLAY
1278         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1279 #endif
1280         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1281         bytes += adsi_data_mode(buf + bytes);
1282         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1283
1284         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1285                 bytes = 0;
1286                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1287                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1288                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1289                 bytes += adsi_voice_mode(buf + bytes, 0);
1290                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1291                 return 0;
1292         }
1293
1294 #ifdef DISPLAY
1295         /* Add a dot */
1296         bytes = 0;
1297         bytes += adsi_logo(buf);
1298         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1299         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1300         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1301         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1302 #endif
1303         bytes = 0;
1304         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1305         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1306         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1307         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1308         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1309         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1310         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1311
1312 #ifdef DISPLAY
1313         /* Add another dot */
1314         bytes = 0;
1315         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1316         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1317         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1318 #endif
1319
1320         bytes = 0;
1321         /* These buttons we load but don't use yet */
1322         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1323         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1324         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1325         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1326         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1327         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1328         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1329
1330 #ifdef DISPLAY
1331         /* Add another dot */
1332         bytes = 0;
1333         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1334         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1335         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1336 #endif
1337
1338         bytes = 0;
1339         for (x=0;x<5;x++) {
1340                 snprintf(num, sizeof(num), "%d", x);
1341                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1342         }
1343         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1344         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1345
1346 #ifdef DISPLAY
1347         /* Add another dot */
1348         bytes = 0;
1349         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1350         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1351         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1352 #endif
1353
1354         if (adsi_end_download(chan)) {
1355                 bytes = 0;
1356                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1357                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1358                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1359                 bytes += adsi_voice_mode(buf + bytes, 0);
1360                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1361                 return 0;
1362         }
1363         bytes = 0;
1364         bytes += adsi_download_disconnect(buf + bytes);
1365         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1366
1367         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1368
1369 #ifdef DISPLAY
1370         /* Add last dot */
1371         bytes = 0;
1372         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1373         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1374 #endif
1375         ast_log(LOG_DEBUG, "Restarting session...\n");
1376
1377         bytes = 0;
1378         /* Load the session now */
1379         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1380                 *useadsi = 1;
1381                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1382         } else
1383                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1384
1385         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1386         return 0;
1387 }
1388
1389 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1390 {
1391         int x;
1392         if (!adsi_available(chan))
1393           return;
1394         x = adsi_load_session(chan, adapp, adver, 1);
1395         if (x < 0)
1396                 return;
1397         if (!x) {
1398                 if (adsi_load_vmail(chan, useadsi)) {
1399                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1400                         return;
1401                 }
1402         } else
1403                 *useadsi = 1;
1404 }
1405
1406 static void adsi_login(struct ast_channel *chan)
1407 {
1408         char buf[256];
1409         int bytes=0;
1410         unsigned char keys[8];
1411         int x;
1412         if (!adsi_available(chan))
1413                 return;
1414
1415         for (x=0;x<8;x++)
1416                 keys[x] = 0;
1417         /* Set one key for next */
1418         keys[3] = ADSI_KEY_APPS + 3;
1419
1420         bytes += adsi_logo(buf + bytes);
1421         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1422         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1423         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1424         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1425         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1426         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1427         bytes += adsi_set_keys(buf + bytes, keys);
1428         bytes += adsi_voice_mode(buf + bytes, 0);
1429         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1430 }
1431
1432 static void adsi_password(struct ast_channel *chan)
1433 {
1434         char buf[256];
1435         int bytes=0;
1436         unsigned char keys[8];
1437         int x;
1438         if (!adsi_available(chan))
1439                 return;
1440
1441         for (x=0;x<8;x++)
1442                 keys[x] = 0;
1443         /* Set one key for next */
1444         keys[3] = ADSI_KEY_APPS + 3;
1445
1446         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1447         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1448         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1449         bytes += adsi_set_keys(buf + bytes, keys);
1450         bytes += adsi_voice_mode(buf + bytes, 0);
1451         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1452 }
1453
1454 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1455 {
1456         char buf[256];
1457         int bytes=0;
1458         unsigned char keys[8];
1459         int x,y;
1460
1461         if (!adsi_available(chan))
1462                 return;
1463
1464         for (x=0;x<5;x++) {
1465                 y = ADSI_KEY_APPS + 12 + start + x;
1466                 if (y > ADSI_KEY_APPS + 12 + 4)
1467                         y = 0;
1468                 keys[x] = ADSI_KEY_SKT | y;
1469         }
1470         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1471         keys[6] = 0;
1472         keys[7] = 0;
1473
1474         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1475         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1476         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1477         bytes += adsi_set_keys(buf + bytes, keys);
1478         bytes += adsi_voice_mode(buf + bytes, 0);
1479
1480         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1481 }
1482
1483 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1484 {
1485         int bytes=0;
1486         char buf[256], buf1[256], buf2[256];
1487         char fn2[256];
1488         char cid[256]="";
1489         char *val;
1490         char *name, *num;
1491         char datetime[21]="";
1492         FILE *f;
1493
1494         unsigned char keys[8];
1495
1496         int x;
1497
1498         if (!adsi_available(chan))
1499                 return;
1500
1501         /* Retrieve important info */
1502         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1503         f = fopen(fn2, "r");
1504         if (f) {
1505                 while(!feof(f)) {       
1506                         fgets(buf, sizeof(buf), f);
1507                         if (!feof(f)) {
1508                                 char *stringp=NULL;
1509                                 stringp=buf;
1510                                 strsep(&stringp, "=");
1511                                 val = strsep(&stringp, "=");
1512                                 if (val && strlen(val)) {
1513                                         if (!strcmp(buf, "callerid"))
1514                                                 strncpy(cid, val, sizeof(cid) - 1);
1515                                         if (!strcmp(buf, "origdate"))
1516                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1517                                 }
1518                         }
1519                 }
1520                 fclose(f);
1521         }
1522         /* New meaning for keys */
1523         for (x=0;x<5;x++)
1524                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1525         keys[6] = 0x0;
1526         keys[7] = 0x0;
1527
1528         if (!msg) {
1529                 /* No prev key, provide "Folder" instead */
1530                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1531         }
1532         if (msg >= last) {
1533                 /* If last message ... */
1534                 if (msg) {
1535                         /* but not only message, provide "Folder" instead */
1536                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1537                 } else {
1538                         /* Otherwise if only message, leave blank */
1539                         keys[3] = 1;
1540                 }
1541         }
1542
1543         if (strlen(cid)) {
1544                 ast_callerid_parse(cid, &name, &num);
1545                 if (!name)
1546                         name = num;
1547         } else
1548                 name = "Unknown Caller";
1549
1550         /* If deleted, show "undeleted" */
1551         if (deleted)
1552                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1553
1554         /* Except "Exit" */
1555         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1556         snprintf(buf1, sizeof(buf1), "%s%s", folder,
1557                  strcasecmp(folder, "INBOX") ? " Messages" : "");
1558         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1559
1560         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1561         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1562         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1563         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1564         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1565         bytes += adsi_set_keys(buf + bytes, keys);
1566         bytes += adsi_voice_mode(buf + bytes, 0);
1567
1568         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1569 }
1570
1571 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1572 {
1573         int bytes=0;
1574         char buf[256];
1575         unsigned char keys[8];
1576
1577         int x;
1578
1579         if (!adsi_available(chan))
1580                 return;
1581
1582         /* New meaning for keys */
1583         for (x=0;x<5;x++)
1584                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1585
1586         keys[6] = 0x0;
1587         keys[7] = 0x0;
1588
1589         if (!msg) {
1590                 /* No prev key, provide "Folder" instead */
1591                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1592         }
1593         if (msg >= last) {
1594                 /* If last message ... */
1595                 if (msg) {
1596                         /* but not only message, provide "Folder" instead */
1597                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1598                 } else {
1599                         /* Otherwise if only message, leave blank */
1600                         keys[3] = 1;
1601                 }
1602         }
1603
1604         /* If deleted, show "undeleted" */
1605         if (deleted) 
1606                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1607
1608         /* Except "Exit" */
1609         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1610         bytes += adsi_set_keys(buf + bytes, keys);
1611         bytes += adsi_voice_mode(buf + bytes, 0);
1612
1613         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1614 }
1615
1616 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1617 {
1618         char buf[256], buf1[256], buf2[256];
1619         int bytes=0;
1620         unsigned char keys[8];
1621         int x;
1622
1623         char *newm = (new == 1) ? "message" : "messages";
1624         char *oldm = (old == 1) ? "message" : "messages";
1625         if (!adsi_available(chan))
1626                 return;
1627         if (new) {
1628                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1629                 if (old) {
1630                         strcat(buf1, " and");
1631                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1632                 } else {
1633                         snprintf(buf2, sizeof(buf2), "%s.", newm);
1634                 }
1635         } else if (old) {
1636                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1637                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1638         } else {
1639                 strcpy(buf1, "You have no messages.");
1640                 strcpy(buf2, " ");
1641         }
1642         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1643         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1644         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1645
1646         for (x=0;x<6;x++)
1647                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1648         keys[6] = 0;
1649         keys[7] = 0;
1650
1651         /* Don't let them listen if there are none */
1652         if (lastmsg < 0)
1653                 keys[0] = 1;
1654         bytes += adsi_set_keys(buf + bytes, keys);
1655
1656         bytes += adsi_voice_mode(buf + bytes, 0);
1657
1658         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1659 }
1660
1661 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1662 {
1663         char buf[256], buf1[256], buf2[256];
1664         int bytes=0;
1665         unsigned char keys[8];
1666         int x;
1667
1668         char *mess = (messages == 1) ? "message" : "messages";
1669
1670         if (!adsi_available(chan))
1671                 return;
1672
1673         /* Original command keys */
1674         for (x=0;x<6;x++)
1675                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1676
1677         keys[6] = 0;
1678         keys[7] = 0;
1679
1680         if (messages < 1)
1681                 keys[0] = 0;
1682
1683         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1684                         strcasecmp(folder, "INBOX") ? " folder" : "");
1685
1686         if (messages)
1687                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1688         else
1689                 strcpy(buf2, "no messages.");
1690         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1691         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1692         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1693         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1694         bytes += adsi_set_keys(buf + bytes, keys);
1695
1696         bytes += adsi_voice_mode(buf + bytes, 0);
1697
1698         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1699         
1700 }
1701
1702 static void adsi_clear(struct ast_channel *chan)
1703 {
1704         char buf[256];
1705         int bytes=0;
1706         if (!adsi_available(chan))
1707                 return;
1708         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1709         bytes += adsi_voice_mode(buf + bytes, 0);
1710
1711         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1712 }
1713
1714 static void adsi_goodbye(struct ast_channel *chan)
1715 {
1716         char buf[256];
1717         int bytes=0;
1718
1719         if (!adsi_available(chan))
1720                 return;
1721         bytes += adsi_logo(buf + bytes);
1722         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1723         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1724         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1725         bytes += adsi_voice_mode(buf + bytes, 0);
1726
1727         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1728 }
1729
1730 static int get_folder(struct ast_channel *chan, int start)
1731 {
1732         int x;
1733         int d;
1734         char fn[256];
1735         d = play_and_wait(chan, "vm-press");
1736         if (d)
1737                 return d;
1738         for (x = start; x< 5; x++) {
1739                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1740                         return d;
1741                 d = play_and_wait(chan, "vm-for");
1742                 if (d)
1743                         return d;
1744                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1745                 d = play_and_wait(chan, fn);
1746                 if (d)
1747                         return d;
1748                 d = play_and_wait(chan, "vm-messages");
1749                 if (d)
1750                         return d;
1751                 d = ast_waitfordigit(chan, 500);
1752                 if (d)
1753                         return d;
1754         }
1755         d = play_and_wait(chan, "vm-tocancel");
1756         if (d)
1757                 return d;
1758         d = ast_waitfordigit(chan, 4000);
1759         return d;
1760 }
1761
1762 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1763 {
1764         int res = 0;
1765         res = play_and_wait(chan, fn);
1766         while (((res < '0') || (res > '9')) &&
1767                         (res != '#') && (res >= 0)) {
1768                 res = get_folder(chan, 0);
1769         }
1770         return res;
1771 }
1772
1773 static int
1774 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1775 {
1776         char username[70];
1777         char sys[256];
1778         char todir[256];
1779         int todircount=0;
1780         long duration;
1781         struct ast_config *mif;
1782         char miffile[256];
1783         char fn[256];
1784         char callerid[512];
1785         int res = 0;
1786         struct ast_vm_user *receiver, srec;
1787         char tmp[256];
1788         char *stringp, *s;
1789         
1790         while(!res) {
1791                 res = ast_streamfile(chan, "vm-extension", chan->language);
1792                 if (res)
1793                         break;
1794                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1795                         break;
1796                 if ((receiver = find_user(&srec, context, username))) {
1797                         /* if (play_and_wait(chan, "vm-savedto"))
1798                                 break;
1799                         */
1800
1801                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1802                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1803                         ast_log(LOG_DEBUG, sys);
1804                         system(sys);
1805
1806                         todircount = count_messages(todir);
1807                         strncpy(tmp, fmt, sizeof(tmp));
1808                         stringp = tmp;
1809                         while((s = strsep(&stringp, "|"))) {
1810                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1811                                 ast_log(LOG_DEBUG, sys);
1812                                 system(sys);
1813                         }
1814                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1815                         ast_log(LOG_DEBUG, sys);
1816                         system(sys);
1817                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1818
1819                         /* load the information on the source message so we can send an e-mail like a new message */
1820                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1821                         if ((mif=ast_load(miffile))) {
1822
1823               /* set callerid and duration variables */
1824               snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1825               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1826                         
1827               if (strlen(receiver->email)) {
1828                                 int attach_user_voicemail = attach_voicemail;
1829                                 char *myserveremail = serveremail;
1830                                 if (receiver->attach > -1)
1831                                         attach_user_voicemail = receiver->attach;
1832                                 if (strlen(receiver->serveremail))
1833                                         myserveremail = receiver->serveremail;
1834                       sendmail(myserveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1835               }
1836              
1837                         if (strlen(receiver->pager)) {
1838                                 char *myserveremail = serveremail;
1839                                 if (strlen(receiver->serveremail))
1840                                         myserveremail = receiver->serveremail;
1841                                 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration);
1842                         }
1843                           
1844                           ast_destroy(mif); /* or here */
1845                         }
1846                         /* Leave voicemail for someone */
1847                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1848
1849                         /* give confirmatopm that the message was saved */
1850                         res = play_and_wait(chan, "vm-message");
1851                         if (!res)
1852                                 res = play_and_wait(chan, "vm-saved");
1853                         free_user(receiver);
1854                         break;
1855                 } else {
1856                         res = play_and_wait(chan, "pbx-invalid");
1857                 }
1858         }
1859         return res;
1860 }
1861
1862 struct vm_state {
1863         char curbox[80];
1864         char username[80];
1865         char curdir[256];
1866         char vmbox[256];
1867         char fn[256];
1868         char fn2[256];
1869         int deleted[MAXMSG];
1870         int heard[MAXMSG];
1871         int curmsg;
1872         int lastmsg;
1873         int newmessages;
1874         int oldmessages;
1875         int starting;
1876         int repeats;
1877 };
1878
1879
1880 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1881 {
1882         int res;
1883         if ((res = ast_streamfile(chan, file, chan->language))) 
1884                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
1885         if (!res)
1886                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1887         return res;
1888 }
1889
1890 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
1891 {
1892         int res;
1893         if ((res = ast_streamfile(chan, file, chan->language)))
1894                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1895         if (!res)
1896                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
1897         return res;
1898 }
1899
1900 static int play_datetime_format(struct ast_channel *chan, time_t time, struct vm_state *vms, struct vm_zone *zone)
1901 {
1902         int d = 0, offset = 0, sndoffset = 0;
1903         char sndfile[256], nextmsg[256];
1904         struct tm tm;
1905         char *tzenv, current_tz[256] = "", *qmark;
1906
1907         tzenv = getenv("TZ");
1908         if (tzenv != NULL)
1909                 strncpy(current_tz, tzenv, sizeof(current_tz) - 1);
1910         if (zone->timezone && strcmp(current_tz,zone->timezone)) {
1911                 setenv("TZ", zone->timezone, 1);
1912                 tzset();
1913                 localtime_r(&time, &tm);
1914                 if (tzenv != NULL)
1915                         setenv("TZ", current_tz, 1);
1916                 else
1917                         unsetenv("TZ");
1918         } else {
1919                 /* No need to change the timezone */
1920                 localtime_r(&time, &tm);
1921         }
1922
1923         /* Check for a subexpression */
1924         if ((qmark = index(zone->msg_format, '?'))) {
1925                 /* TODO Allow subexpressions - we probably need to implement a parser here. */
1926         }
1927
1928         for (offset=0 ; zone->msg_format[offset] != '\0' ; offset++) {
1929                 ast_log(LOG_NOTICE, "Parsing %c in %s\n", zone->msg_format[offset], zone->msg_format);
1930                 switch (zone->msg_format[offset]) {
1931                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1932                         case '\'':
1933                                 /* Literal name of a sound file */
1934                                 sndoffset=0;
1935                                 for (sndoffset=0 ; zone->msg_format[++offset] != '\'' ; sndoffset++)
1936                                         sndfile[sndoffset] = zone->msg_format[offset];
1937                                 sndfile[sndoffset] = '\0';
1938                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1939                                 d = wait_file(chan,vms,nextmsg);
1940                                 break;
1941                         case '$':
1942                                 /* Ooooh, variables and/or expressions */
1943                                 {
1944                                         struct vm_zone z;
1945                                         memcpy(&z,zone,sizeof(struct vm_zone));
1946                                         pbx_substitute_variables_helper(chan, zone->msg_format + offset, z.msg_format, sizeof(z.msg_format));
1947                                         d = play_datetime_format(chan, time, vms, &z);
1948                                         /* Subtract one, so that when the for loop increments, we point at the nil */
1949                                         offset = strlen(zone->msg_format) - 1;
1950                                 }
1951                                 break;
1952                         case 'A':
1953                         case 'a':
1954                                 /* Sunday - Saturday */
1955                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "day-%d", tm.tm_wday);
1956                                 d = wait_file(chan,vms,nextmsg);
1957                                 break;
1958                         case 'B':
1959                         case 'b':
1960                         case 'h':
1961                                 /* January - December */
1962                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "mon-%d", tm.tm_mon);
1963                                 d = wait_file(chan,vms,nextmsg);
1964                                 break;
1965                         case 'd':
1966                         case 'e':
1967                                 /* First - Thirtyfirst */
1968                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1969                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "h-%d", tm.tm_mday);
1970                                         d = wait_file(chan,vms,nextmsg);
1971                                 } else if (tm.tm_mday == 31) {
1972                                         /* "Thirty" and "first" */
1973                                         d = wait_file(chan,vms,DIGITS_DIR "30");
1974                                         if (!d) {
1975                                                 d = wait_file(chan,vms,DIGITS_DIR "h-1");
1976                                         }
1977                                 } else {
1978                                         /* Between 21 and 29 - two sounds */
1979                                         d = wait_file(chan,vms,DIGITS_DIR "20");
1980                                         if (!d) {
1981                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "h-%d", tm.tm_mday - 20);
1982                                                 d = wait_file(chan,vms,nextmsg);
1983                                         }
1984                                 }
1985                                 break;
1986                         case 'Y':
1987                                 /* Year */
1988                                 if (tm.tm_year > 99) {
1989                                         d = wait_file(chan,vms,DIGITS_DIR "2");
1990                                         if (!d) {
1991                                                 d = wait_file(chan,vms,DIGITS_DIR "thousand");
1992                                         }
1993                                         if (tm.tm_year > 100) {
1994                                                 if (!d) {
1995                                                         /* This works until the end of 2020 */
1996                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year - 100);
1997                                                         d = wait_file(chan,vms,nextmsg);
1998                                                 }
1999                                         }
2000                                 } else {
2001                                         if (tm.tm_year < 1) {
2002                                                 /* I'm not going to handle 1900 and prior */
2003                                                 /* We'll just be silent on the year, instead of bombing out. */
2004                                         } else {
2005                                                 d = wait_file(chan,vms,DIGITS_DIR "19");
2006                                                 if (!d) {
2007                                                         if (tm.tm_year < 20) {
2008                                                                 /* 1901 - 1920 */
2009                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year);
2010                                                                 d = wait_file(chan,vms,nextmsg);
2011                                                         } else {
2012                                                                 /* 1921 - 1999 */
2013                                                                 int ten, one;
2014                                                                 ten = tm.tm_year / 10;
2015                                                                 one = tm.tm_year % 10;
2016                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten * 10);
2017                                                                 d = wait_file(chan,vms,nextmsg);
2018                                                                 if (!d) {
2019                                                                         if (one != 0) {
2020                                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
2021                                                                                 d = wait_file(chan,vms,nextmsg);
2022                                                                         }
2023                                                                 }
2024                                                         }
2025                                                 }
2026                                         }
2027                                 }
2028                                 break;
2029                         case 'I':
2030                         case 'l':
2031                                 /* 12-Hour */
2032                                 if (tm.tm_hour == 0)
2033                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "12");
2034                                 else if (tm.tm_hour > 12)
2035                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour - 12);
2036                                 else
2037                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour);
2038                                 d = wait_file(chan,vms,nextmsg);
2039                                 break;
2040                         case 'H':
2041                         case 'k':
2042                                 /* 24-Hour */
2043                                 if (zone->msg_format[offset] == 'H') {
2044                                         /* e.g. oh-eight */
2045                                         if (tm.tm_hour < 10) {
2046                                                 d = wait_file(chan,vms,DIGITS_DIR "oh");
2047                                         }
2048                                 } else {
2049                                         /* e.g. eight */
2050                                         if (tm.tm_hour == 0) {
2051                                                 d = wait_file(chan,vms,DIGITS_DIR "oh");
2052                                         }
2053                                 }
2054                                 if (!d) {
2055                                         if (tm.tm_hour != 0) {
2056                                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/digits/%d", tm.tm_hour);
2057                                                 d = wait_file(chan,vms,nextmsg);
2058                                         }
2059                                 }
2060                                 break;
2061                         case 'M':
2062                                 /* Minute */
2063                                 if (tm.tm_min == 0) {
2064                                         d = wait_file(chan,vms,DIGITS_DIR "oclock");
2065                                 } else if (tm.tm_min < 10) {
2066                                         d = wait_file(chan,vms,DIGITS_DIR "oh");
2067                                         if (!d) {
2068                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
2069                                                 d = wait_file(chan,vms,nextmsg);
2070                                         }
2071                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
2072                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
2073                                         d = wait_file(chan,vms,nextmsg);
2074                                 } else {
2075                                         int ten, one;
2076                                         ten = (tm.tm_min / 10) * 10;
2077                                         one = (tm.tm_min % 10);
2078                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten);
2079                                         d = wait_file(chan,vms,nextmsg);
2080                                         if (!d) {
2081                                                 /* Fifty, not fifty-zero */
2082                                                 if (one != 0) {
2083                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
2084                                                         d = wait_file(chan,vms,nextmsg);
2085                                                 }
2086                                         }
2087                                 }
2088                                 break;
2089                         case 'P':
2090                         case 'p':
2091                                 /* AM/PM */
2092                                 if ((tm.tm_hour == 0) || (tm.tm_hour > 11))
2093                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "p-m");
2094                                 else
2095                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "a-m");
2096                                 d = wait_file(chan,vms,nextmsg);
2097                                 break;
2098                         case 'Q':
2099                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2100                                 {
2101                                         struct timeval now;
2102                                         struct tm tmnow;
2103                                         time_t beg_today;
2104
2105                                         gettimeofday(&now,NULL);
2106                                         localtime_r(&now.tv_sec,&tmnow);
2107                                         tmnow.tm_hour = 0;
2108                                         tmnow.tm_min = 0;
2109                                         tmnow.tm_sec = 0;
2110                                         beg_today = mktime(&tmnow);
2111                                         if (beg_today < time) {
2112                                                 /* Today */
2113                                                 d = wait_file(chan,vms,DIGITS_DIR "today");
2114                                         } else if (beg_today - 86400 < time) {
2115                                                 /* Yesterday */
2116                                                 d = wait_file(chan,vms,DIGITS_DIR "yesterday");
2117                                         } else {
2118                                                 struct vm_zone z;
2119                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2120                                                 strcpy(z.msg_format, "ABdY");
2121                                                 d = play_datetime_format(chan, time, vms, &z);
2122                                         }
2123                                 }
2124                                 break;
2125                         case 'q':
2126                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2127                                 {
2128                                         struct timeval now;
2129                                         struct tm tmnow;
2130                                         time_t beg_today;
2131
2132                                         gettimeofday(&now,NULL);
2133                                         localtime_r(&now.tv_sec,&tmnow);
2134                                         tmnow.tm_hour = 0;
2135                                         tmnow.tm_min = 0;
2136                                         tmnow.tm_sec = 0;
2137                                         beg_today = mktime(&tmnow);
2138                                         if (beg_today < time) {
2139                                                 /* Today */
2140                                         } else if (beg_today - 86400 < time) {
2141                                                 /* Yesterday */
2142                                                 d = wait_file(chan,vms,DIGITS_DIR "yesterday");
2143                                         } else if (beg_today - 86400 * 6 < time) {
2144                                                 /* Within the last week */
2145                                                 struct vm_zone z;
2146                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2147                                                 strcpy(z.msg_format, "A");
2148                                                 d = play_datetime_format(chan, time, vms, &z);
2149                                         } else {
2150                                                 struct vm_zone z;
2151                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2152                                                 strcpy(z.msg_format, "ABdY");
2153                                                 d = play_datetime_format(chan, time, vms, &z);
2154                                         }
2155                                 }
2156                                 break;
2157                         case 'R':
2158                                 {
2159                                         struct vm_zone z;
2160                                         memcpy(&z, zone, sizeof(struct vm_zone));
2161                                         strcpy(z.msg_format, "HM");
2162                                         d = play_datetime_format(chan, time, vms, &z);
2163                                 }
2164                                 break;
2165                         case ' ':
2166                         case '  ':
2167                                 /* Just ignore spaces and tabs */
2168                                 break;
2169                         default:
2170                                 /* Unknown character */
2171                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c\n", zone->msg_format, zone->msg_format[offset]);
2172                 }
2173                 /* Jump out on DTMF */
2174                 if (d) {
2175                         break;
2176                 }
2177         }
2178         return d;
2179 }
2180
2181 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2182 {
2183         int res = 0;
2184         char filename[256], *origtime, temp[256];
2185         struct vm_zone *the_zone = NULL;
2186         struct ast_config *msg_cfg;
2187         time_t t;
2188         struct timeval tv_now;
2189         struct tm time_now, time_then;
2190
2191         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2192         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2193         msg_cfg = ast_load(filename);
2194         if (!msg_cfg) {
2195                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2196                 return 0;
2197         }
2198
2199         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2200                 return 0;
2201         if (sscanf(origtime,"%ld",&t) < 1) {
2202                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2203                 return 0;
2204         }
2205         ast_destroy(msg_cfg);
2206
2207         /* Does this user have a timezone specified? */
2208         if (strlen(vmu->zonetag)) {
2209                 /* Find the zone in the list */
2210                 struct vm_zone *z;
2211                 z = zones;
2212                 while (z) {
2213                         if (!strcmp(z->name, vmu->zonetag)) {
2214                                 the_zone = z;
2215                                 break;
2216                         }
2217                         z = z->next;
2218                 }
2219         }
2220
2221         /* If no zone, use a default */
2222         if (!the_zone) {
2223                 the_zone = alloca(sizeof(struct vm_zone));
2224                 memset(the_zone,0,sizeof(struct vm_zone));
2225                 strncpy(the_zone->msg_format, "'vm-received' q 'digits/at' IMp", sizeof(the_zone->msg_format) - 1);
2226         }
2227
2228         /* Set the DIFF_* variables */
2229         localtime_r(&t, &time_now);
2230         gettimeofday(&tv_now,NULL);
2231         localtime_r(&tv_now.tv_sec,&time_then);
2232
2233         /* Day difference */
2234         if (time_now.tm_year == time_then.tm_year)
2235                 sprintf(temp,"%d",time_now.tm_yday);
2236         else
2237                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2238         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2239
2240         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2241
2242         res = play_datetime_format(chan, t, vms, the_zone);
2243         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2244         return res;
2245 }
2246
2247 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2248 {
2249         int res = 0;
2250         vms->starting = 0; 
2251         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2252         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2253         if (!msg)
2254                 res = wait_file2(chan, vms, "vm-first");
2255         else if (msg == vms->lastmsg)
2256                 res = wait_file2(chan, vms, "vm-last");
2257         if (!res) {
2258                 res = wait_file2(chan, vms, "vm-message");
2259                 if (msg && (msg != vms->lastmsg)) {
2260                         if (!res)
2261                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2262                 }
2263         }
2264
2265         if (!res)
2266                 res = play_message_datetime(chan,vmu,vms);
2267
2268         if (!res) {
2269                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2270                 vms->heard[msg] = 1;
2271                 res = wait_file(chan, vms, vms->fn);
2272         }
2273         return res;
2274 }
2275
2276 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2277 {
2278         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2279         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2280         vms->lastmsg = count_messages(vms->curdir) - 1;
2281         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2282 }
2283
2284 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2285 {
2286         int x;
2287         char ntxt[256] = "";
2288         char txt[256] = "";
2289         if (vms->lastmsg > -1) { 
2290                 /* Get the deleted messages fixed */ 
2291                 vms->curmsg = -1; 
2292                 for (x=0;x < MAXMSG;x++) { 
2293                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2294                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2295                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2296                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2297                                         break;
2298                                 vms->curmsg++; 
2299                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2300                                 if (strcmp(vms->fn, vms->fn2)) { 
2301                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2302                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2303                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2304                                         rename(txt, ntxt); 
2305                                 } 
2306                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2307                                 /* Move to old folder before deleting */ 
2308                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2309                         } 
2310                 } 
2311                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2312                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2313                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2314                                 break;
2315                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2316                         ast_filedelete(vms->fn, NULL); 
2317                         unlink(txt); 
2318                 } 
2319         } 
2320         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2321         memset(vms->heard, 0, sizeof(vms->heard)); 
2322 }
2323
2324 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2325 {
2326         /* Introduce messages they have */
2327         int res;
2328         res = play_and_wait(chan, "vm-youhave");
2329         if (!res) {
2330                 if (vms->newmessages) {
2331                         res = say_and_wait(chan, vms->newmessages);
2332                         if (!res)
2333                                 res = play_and_wait(chan, "vm-INBOX");
2334                         if (vms->oldmessages && !res)
2335                                 res = play_and_wait(chan, "vm-and");
2336                         else if (!res) {
2337                                 if ((vms->newmessages == 1))
2338                                         res = play_and_wait(chan, "vm-message");
2339                                 else
2340                                         res = play_and_wait(chan, "vm-messages");
2341                         }
2342                                 
2343                 }
2344                 if (!res && vms->oldmessages) {
2345                         res = say_and_wait(chan, vms->oldmessages);
2346                         if (!res)
2347                                 res = play_and_wait(chan, "vm-Old");
2348                         if (!res) {
2349                                 if (vms->oldmessages == 1)
2350                                         res = play_and_wait(chan, "vm-message");
2351                                 else
2352                                         res = play_and_wait(chan, "vm-messages");
2353                         }
2354                 }
2355                 if (!res) {
2356                         if (!vms->oldmessages && !vms->newmessages) {
2357                                 res = play_and_wait(chan, "vm-no");
2358                                 if (!res)
2359                                         res = play_and_wait(chan, "vm-messages");
2360                         }
2361                 }
2362         }
2363         return res;
2364 }
2365
2366 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2367 {
2368         int res = 0;
2369         /* Play instructions and wait for new command */
2370         while(!res) {
2371                 if (vms->starting) {
2372                         if (vms->lastmsg > -1) {
2373                                 res = play_and_wait(chan, "vm-onefor");
2374                                 if (!res)
2375                                         res = play_and_wait(chan, vms->vmbox);
2376                                 if (!res)
2377                                         res = play_and_wait(chan, "vm-messages");
2378                         }
2379                         if (!res)
2380                                 res = play_and_wait(chan, "vm-opts");
2381                 } else {
2382                         if (vms->curmsg)
2383                                 res = play_and_wait(chan, "vm-prev");
2384                         if (!res)
2385                                 res = play_and_wait(chan, "vm-repeat");
2386                         if (!res && (vms->curmsg != vms->lastmsg))
2387                                 res = play_and_wait(chan, "vm-next");
2388                         if (!res) {
2389                                 if (!vms->deleted[vms->curmsg])
2390                                         res = play_and_wait(chan, "vm-delete");
2391                                 else
2392                                         res = play_and_wait(chan, "vm-undelete");
2393                                 if (!res)
2394                                         res = play_and_wait(chan, "vm-toforward");
2395                                 if (!res)
2396                                         res = play_and_wait(chan, "vm-savemessage");
2397                         }
2398                 }
2399                 if (!res)
2400                         res = play_and_wait(chan, "vm-helpexit");
2401                 if (!res)
2402                         res = ast_waitfordigit(chan, 6000);
2403                 if (!res) {
2404                         vms->repeats++;
2405                         if (vms->repeats > 2) {
2406                                 res = play_and_wait(chan, "vm-goodbye");
2407                                 if (!res)
2408                                         res = 't';
2409                         }
2410                 }
2411         }
2412         return res;
2413 }
2414
2415 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2416 {
2417         int cmd = 0;
2418         int retries = 0;
2419         char newpassword[80] = "";
2420         char newpassword2[80] = "";
2421         char prefile[256]="";
2422         char buf[256];
2423         int bytes=0;
2424
2425         if (adsi_available(chan))
2426         {
2427                 bytes += adsi_logo(buf + bytes);
2428                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2429                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2430                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2431                 bytes += adsi_voice_mode(buf + bytes, 0);
2432                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2433         }
2434         while((cmd >= 0) && (cmd != 't')) {
2435                 if (cmd)
2436                         retries = 0;
2437                 switch (cmd) {
2438                 case '1':
2439                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2440                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2441                         break;
2442                 case '2': 
2443                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2444                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2445                         break;
2446                 case '3': 
2447                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2448                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2449                         break;
2450                 case '4':
2451                         newpassword[1] = '\0';
2452                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2453                         if (cmd < 0)
2454                                 break;
2455                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2456                                 break;
2457             }
2458                         newpassword2[1] = '\0';
2459                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2460                         if (cmd < 0)
2461                                 break;
2462
2463                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2464                                 break;
2465             }
2466                         if (strcmp(newpassword, newpassword2)) {
2467                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2468                                 cmd = play_and_wait(chan, "vm-mismatch");
2469                                 break;
2470                         }
2471                         vm_change_password(vmu,newpassword);
2472                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2473                         cmd = play_and_wait(chan,"vm-passchanged");
2474                         break;
2475                 case '*': 
2476                         cmd = 't';
2477                         break;
2478                 default: 
2479                         cmd = play_and_wait(chan,"vm-options");
2480                         if (!cmd)
2481                                 cmd = ast_waitfordigit(chan,6000);
2482                         if (!cmd)
2483                                 retries++;
2484                         if (retries > 3)
2485                                 cmd = 't';
2486                  }
2487         }
2488         if (cmd == 't')
2489                 cmd = 0;
2490         return cmd;
2491 }
2492
2493 static int vm_execmain(struct ast_channel *chan, void *data)
2494 {
2495         /* XXX This is, admittedly, some pretty horrendus code.  For some
2496            reason it just seemed a lot easier to do with GOTO's.  I feel
2497            like I'm back in my GWBASIC days. XXX */
2498         int res=-1;
2499         int valid = 0;
2500         int prefix = 0;
2501         int cmd=0;
2502         struct localuser *u;
2503         char prefixstr[80] ="";
2504         char empty[80] = "";
2505         int box;
2506         int useadsi = 0;
2507         int skipuser = 0;
2508         char tmp[256], *ext;
2509         char fmtc[256] = "";
2510         char password[80];
2511         struct vm_state vms;
2512         int logretries = 0;
2513         struct ast_vm_user *vmu = NULL, vmus;
2514         char *context=NULL;
2515
2516         LOCAL_USER_ADD(u);
2517         memset(&vms, 0, sizeof(vms));
2518         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2519         if (chan->_state != AST_STATE_UP)
2520                 ast_answer(chan);
2521
2522         if (data && strlen(data)) {
2523                 strncpy(tmp, data, sizeof(tmp) - 1);
2524                 ext = tmp;
2525
2526                 switch (*ext) {
2527                         case 's':
2528                  /* We should skip the user's password */
2529                                 valid++;
2530                                 ext++;
2531                                 break;
2532                         case 'p':
2533                  /* We should prefix the mailbox with the supplied data */
2534                                 prefix++;
2535                                 ext++;
2536                                 break;
2537                 }
2538
2539                 context = strchr(ext, '@');
2540                 if (context) {
2541                         *context = '\0';
2542                         context++;
2543                 }
2544
2545                 if (prefix)
2546                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2547                 else
2548                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2549                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2550                         skipuser++;
2551                 else
2552                         valid = 0;
2553
2554         }
2555
2556         /* If ADSI is supported, setup login screen */
2557         adsi_begin(chan, &useadsi);
2558         if (!skipuser && useadsi)
2559                 adsi_login(chan);
2560         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2561                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2562                 goto out;
2563         }
2564         
2565         /* Authenticate them and get their mailbox/password */
2566         
2567         while (!valid && (logretries < maxlogins)) {
2568                 /* Prompt for, and read in the username */
2569                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2570                         ast_log(LOG_WARNING, "Couldn't read username\n");
2571                         goto out;
2572                 }
2573                 if (!strlen(vms.username)) {
2574                         if (option_verbose > 2)
2575                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2576                         res = 0;
2577                         goto out;
2578                 }
2579                 if (useadsi)
2580                         adsi_password(chan);
2581                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2582                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2583                         goto out;
2584                 }
2585                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2586                         ast_log(LOG_WARNING, "Unable to read password\n");
2587                         goto out;
2588                 }
2589                 if (prefix) {
2590                         char fullusername[80] = "";
2591                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2592                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2593                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2594                 }
2595                 if (!skipuser) 
2596                         vmu = find_user(&vmus, context, vms.username);
2597                 if (vmu && !strcmp(vmu->password, password)) 
2598                         valid++;
2599                 else {
2600                         if (option_verbose > 2)
2601                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2602                         if (prefix)
2603                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2604                 }
2605                 if (!valid) {
2606                         if (useadsi)
2607                                 adsi_login(chan);
2608                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2609                                 break;
2610                 }
2611                 logretries++;
2612         }
2613         if (!valid && (logretries >= maxlogins)) {
2614                 ast_stopstream(chan);
2615                 res = play_and_wait(chan, "vm-goodbye");
2616                 if (res > 0)
2617                         res = 0;
2618         }
2619
2620         if (valid) {
2621                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2622                 mkdir(vms.curdir, 0700);
2623                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2624                 mkdir(vms.curdir, 0700);
2625                 /* Retrieve old and new message counts */
2626                 open_mailbox(&vms, vmu, 1);
2627                 vms.oldmessages = vms.lastmsg + 1;
2628                 /* Start in INBOX */
2629                 open_mailbox(&vms, vmu, 0);
2630                 vms.newmessages = vms.lastmsg + 1;
2631                 
2632
2633                 /* Select proper mailbox FIRST!! */
2634                 if (!vms.newmessages && vms.oldmessages) {
2635                         /* If we only have old messages start here */
2636                         open_mailbox(&vms, vmu, 1);
2637                 }
2638
2639                 if (useadsi)
2640                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2641                 res = 0;
2642                 cmd = vm_intro(chan, &vms);
2643                 vms.repeats = 0;
2644                 vms.starting = 1;
2645                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2646                         /* Run main menu */
2647                         switch(cmd) {
2648                         case '1':
2649                                 vms.curmsg = 0;
2650                                 /* Fall through */
2651                         case '5':
2652                                 if (vms.lastmsg > -1) {
2653                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2654                                 } else {
2655                                         cmd = play_and_wait(chan, "vm-youhave");
2656                                         if (!cmd) 
2657                                                 cmd = play_and_wait(chan, "vm-no");
2658                                         if (!cmd) {
2659                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2660                                                 cmd = play_and_wait(chan, vms.fn);
2661                                         }
2662                                         if (!cmd)
2663                                                 cmd = play_and_wait(chan, "vm-messages");
2664                                 }
2665                                 break;
2666                         case '2': /* Change folders */
2667                                 if (useadsi)
2668                                         adsi_folders(chan, 0, "Change to folder...");
2669                                 cmd = get_folder2(chan, "vm-changeto", 0);
2670                                 if (cmd == '#') {
2671                                         cmd = 0;
2672                                 } else if (cmd > 0) {
2673                                         cmd = cmd - '0';
2674                                         close_mailbox(&vms, vmu);
2675                                         open_mailbox(&vms, vmu, cmd);
2676                                         cmd = 0;
2677                                 }
2678                                 if (useadsi)
2679                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2680                                 if (!cmd)
2681                                         cmd = play_and_wait(chan, vms.vmbox);
2682                                 if (!cmd)
2683                                         cmd = play_and_wait(chan, "vm-messages");
2684                                 vms.starting = 1;
2685                                 break;
2686                         case '4':
2687                                 if (vms.curmsg) {
2688                                         vms.curmsg--;
2689                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2690                                 } else {
2691                                         cmd = play_and_wait(chan, "vm-nomore");
2692                                 }
2693                                 break;
2694                         case '6':
2695                                 if (vms.curmsg < vms.lastmsg) {
2696                                         vms.curmsg++;
2697                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2698                                 } else {
2699                                         cmd = play_and_wait(chan, "vm-nomore");
2700                                 }
2701                                 break;
2702                         case '7':
2703                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2704                                 if (useadsi)
2705                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2706                                 if (vms.deleted[vms.curmsg]) 
2707                                         cmd = play_and_wait(chan, "vm-deleted");
2708                                 else
2709                                         cmd = play_and_wait(chan, "vm-undeleted");
2710                                 break;
2711                         case '8':
2712                                 if(vms.lastmsg > -1)
2713                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2714                                 break;
2715                         case '9':
2716                                 if (useadsi)
2717                                         adsi_folders(chan, 1, "Save to folder...");
2718                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2719                                 box = 0;        /* Shut up compiler */
2720                                 if (cmd == '#') {
2721                                         cmd = 0;
2722                                         break;
2723                                 } else if (cmd > 0) {
2724                                         box = cmd = cmd - '0';
2725                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2726                                         vms.deleted[vms.curmsg]=1;
2727                                 }
2728                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2729                                 if (useadsi)
2730                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2731                                 if (!cmd)
2732                                         cmd = play_and_wait(chan, "vm-message");
2733                                 if (!cmd)
2734                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2735                                 if (!cmd)
2736                                         cmd = play_and_wait(chan, "vm-savedto");
2737                                 if (!cmd) {
2738                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2739                                         cmd = play_and_wait(chan, vms.fn);
2740                                 }
2741                                 if (!cmd)
2742                                         cmd = play_and_wait(chan, "vm-messages");
2743                                 break;
2744                         case '*':
2745                                 if (!vms.starting) {
2746                                         cmd = play_and_wait(chan, "vm-onefor");
2747                                         if (!cmd)
2748                                                 cmd = play_and_wait(chan, vms.vmbox);
2749                                         if (!cmd)
2750                                                 cmd = play_and_wait(chan, "vm-messages");
2751                                         if (!cmd)
2752                                                 cmd = play_and_wait(chan, "vm-opts");
2753                                 } else
2754                                         cmd = 0;
2755                                 break;
2756                         case '0':
2757                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2758                                 if (useadsi)
2759                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2760                                 break;
2761                         default:        /* Nothing */
2762                                 cmd = vm_instructions(chan, &vms);
2763                                 break;
2764                         }
2765                 }
2766                 if ((cmd == 't') || (cmd == '#')) {
2767                         /* Timeout */
2768                         res = 0;
2769                 } else {
2770                         /* Hangup */
2771                         res = -1;
2772                 }
2773         }
2774 out:
2775         if (res > -1) {
2776                 ast_stopstream(chan);
2777                 adsi_goodbye(chan);
2778                 res = play_and_wait(chan, "vm-goodbye");
2779                 if (res > 0)
2780                         res = 0;
2781                 if (useadsi)
2782                         adsi_unload_session(chan);
2783         }
2784         if (vmu)
2785                 close_mailbox(&vms, vmu);
2786         if (vmu)
2787                 free_user(vmu);
2788         if (valid) {
2789                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2790         }
2791         LOCAL_USER_REMOVE(u);
2792         return res;
2793
2794 }
2795
2796 static int vm_exec(struct ast_channel *chan, void *data)
2797 {
2798         int res=0, silent=0, busy=0, unavail=0;
2799         struct localuser *u;
2800         char tmp[256], *ext;
2801         
2802         LOCAL_USER_ADD(u);
2803         if (chan->_state != AST_STATE_UP)
2804                 ast_answer(chan);
2805         if (data)
2806                 strncpy(tmp, data, sizeof(tmp) - 1);
2807         else {
2808                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2809                 if (res < 0)
2810                         return res;
2811                 if (!strlen(tmp))
2812                         return 0;
2813         }
2814         ext = tmp;
2815         while(*ext) {
2816                 if (*ext == 's') {
2817                         silent = 2;
2818                         ext++;
2819                 } else if (*ext == 'b') {
2820                         busy=1;
2821                         ext++;
2822                 } else if (*ext == 'u') {
2823                         unavail=1;
2824                         ext++;
2825                 } else 
2826                         break;
2827         }
2828         res = leave_voicemail(chan, ext, silent, busy, unavail);
2829         LOCAL_USER_REMOVE(u);
2830         return res;
2831 }
2832
2833 static int append_mailbox(char *context, char *mbox, char *data)
2834 {
2835         /* Assumes lock is already held */
2836         char tmp[256] = "";
2837         char *stringp;
2838         char *s;
2839         struct ast_vm_user *vmu;
2840         strncpy(tmp, data, sizeof(tmp));
2841         vmu = malloc(sizeof(struct ast_vm_user));
2842         if (vmu) {
2843                 memset(vmu, 0, sizeof(struct ast_vm_user));
2844                 strncpy(vmu->context, context, sizeof(vmu->context));
2845                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2846                 vmu->attach = -1;
2847                 stringp = tmp;
2848                 if ((s = strsep(&stringp, ","))) 
2849                         strncpy(vmu->password, s, sizeof(vmu->password));
2850                 if (stringp && (s = strsep(&stringp, ","))) 
2851                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2852                 if (stringp && (s = strsep(&stringp, ","))) 
2853                         strncpy(vmu->email, s, sizeof(vmu->email));
2854                 if (stringp && (s = strsep(&stringp, ","))) 
2855                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2856                 if (stringp && (s = strsep(&stringp, ","))) 
2857                         apply_options(vmu, s);
2858                 vmu->next = NULL;
2859                 if (usersl)
2860                         usersl->next = vmu;
2861                 else
2862                         users = vmu;
2863                 usersl = vmu;
2864         }
2865         return 0;
2866 }
2867
2868 static int load_config(void)
2869 {
2870         struct ast_vm_user *cur, *l;
2871         struct vm_zone *zcur, *zl;
2872         struct ast_config *cfg;
2873         char *cat;
2874         struct ast_variable *var;
2875         char *astattach;
2876         char *silencestr;
2877         char *thresholdstr;
2878         char *fmt;
2879         char *astemail;
2880         char *s;
2881         int x;
2882
2883         cfg = ast_load(VOICEMAIL_CONFIG);
2884         ast_mutex_lock(&vmlock);
2885         cur = users;
2886         while(cur) {
2887                 l = cur;
2888                 cur = cur->next;
2889                 free_user(l);
2890         }
2891         zcur = zones;
2892         while(zcur) {
2893                 zl = zcur;
2894                 zcur = zcur->next;
2895                 free_zone(zl);
2896         }
2897         zones = NULL;
2898         zonesl = NULL;
2899         users = NULL;
2900         usersl = NULL;
2901         if (cfg) {
2902                 /* General settings */
2903                 attach_voicemail = 1;
2904                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
2905                         astattach = "yes";
2906                 attach_voicemail = ast_true(astattach);
2907                 maxsilence = 0;
2908                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2909                         maxsilence = atoi(silencestr);
2910                         if (maxsilence > 0)
2911                                 maxsilence *= 1000;
2912                 }
2913                 
2914                 silencethreshold = 256;
2915                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2916                         silencethreshold = atoi(thresholdstr);
2917                 
2918                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
2919                         astemail = ASTERISK_USERNAME;
2920                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2921                 
2922                 vmmaxmessage = 0;
2923                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2924                         if (sscanf(s, "%d", &x) == 1) {
2925                                 vmmaxmessage = x;
2926                         } else {
2927                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
2928                         }
2929                 }
2930                 fmt = ast_variable_retrieve(cfg, "general", "format");
2931                 if (!fmt)
2932                         fmt = "wav";    
2933                 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2934
2935                 skipms = 3000;
2936                 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2937                         if (sscanf(s, "%d", &x) == 1) {
2938                                 maxgreet = x;
2939                         } else {
2940                                 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2941                         }
2942                 }
2943
2944                 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2945                         if (sscanf(s, "%d", &x) == 1) {
2946                                 skipms = x;
2947                         } else {
2948                                 ast_log(LOG_WARNING, "Invalid skipms value\n");
2949                         }
2950                 }
2951
2952                 maxlogins = 3;
2953                 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2954                         if (sscanf(s, "%d", &x) == 1) {
2955                                 maxlogins = x;
2956                         } else {
2957                                 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2958                         }
2959                 }
2960
2961 #ifdef USEMYSQLVM
2962                 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2963                         strcpy(dbuser, "test");
2964                 } else {
2965                         strcpy(dbuser, s);
2966                 }
2967                 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2968                         strcpy(dbpass, "test");
2969                 } else {
2970                         strcpy(dbpass, s);
2971                 }
2972                 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2973                         strcpy(dbhost, "");
2974                 } else {
2975                         strcpy(dbhost, s);
2976                 }
2977                 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2978                         strcpy(dbname, "vmdb");
2979                 } else {
2980                         strcpy(dbname, s);
2981                 }
2982 #endif
2983                 cat = ast_category_browse(cfg, NULL);
2984                 while(cat) {
2985                         if (strcasecmp(cat, "general")) {
2986                                 var = ast_variable_browse(cfg, cat);
2987                                 if (strcasecmp(cat, "zonemessages")) {
2988 #ifndef USEMYSQLVM
2989                                         /* Process mailboxes in this context */
2990                                         while(var) {
2991                                                 append_mailbox(cat, var->name, var->value);
2992                                                 var = var->next;
2993                                         }
2994 #endif
2995                                 } else {
2996                                         /* Timezones in this context */
2997                                         while(var) {
2998                                                 struct vm_zone *z;
2999                                                 z = malloc(sizeof(struct vm_zone));
3000                                                 if (z != NULL) {
3001                                                         char *msg_format, *timezone;
3002                                                         msg_format = ast_strdupa(var->value);
3003                                                         if (msg_format != NULL) {
3004                                                                 timezone = strsep(&msg_format, "|");
3005                                                                 strncpy(z->name, var->name, sizeof(z->name) - 1);
3006                                                                 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
3007                                                                 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
3008                                                                 z->next = NULL;
3009                                                                 if (zones) {
3010                                                                         zonesl->next = z;
3011                                                                         zonesl = z;
3012                                                                 } else {
3013                                                                         zones = z;
3014                                                                         zonesl = z;
3015                                                                 }
3016                                                         } else {
3017                                                                 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3018                                                                 free(z);
3019                                                                 return -1;
3020                                                         }
3021                                                 } else {
3022                                                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
3023                                                         return -1;
3024                                                 }
3025                                                 var = var->next;
3026                                         }
3027                                 }
3028                         }
3029                         cat = ast_category_browse(cfg, cat);
3030                 }
3031                 memset(fromstring,0,sizeof(fromstring));
3032                 memset(emailtitle,0,sizeof(emailtitle));
3033                 if (emailbody) {
3034                         free(emailbody);
3035                         emailbody = NULL;
3036                 }
3037                 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
3038                         pbxskip = ast_true(s);
3039                 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
3040                         strncpy(fromstring,s,sizeof(fromstring)-1);
3041                 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
3042                         strncpy(emailtitle,s,sizeof(emailtitle)-1);
3043                 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
3044                         char *tmpread, *tmpwrite;
3045                         emailbody = strdup(s);
3046
3047                         /* substitute strings \t and \n into the apropriate characters */
3048                         tmpread = tmpwrite = emailbody;
3049                         while ((tmpwrite = strchr(tmpread,'\\'))) {
3050                                 int len = strlen("\n");
3051                                 switch (tmpwrite[1]) {
3052                                         case 'n':
3053                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3054                                                 strncpy(tmpwrite,"\n",len);
3055                                                 break;
3056                                         case 't':
3057                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
3058                                                 strncpy(tmpwrite,"\t",len);
3059                                                 break;
3060                                         default:
3061                                                 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
3062                                 }
3063                                 tmpread = tmpwrite+len;
3064                         }
3065                 }
3066                 ast_destroy(cfg);
3067                 ast_mutex_unlock(&vmlock);
3068                 return 0;
3069         } else {
3070                 ast_mutex_unlock(&vmlock);
3071                 ast_log(LOG_WARNING, "Error reading voicemail config\n");
3072                 return -1;
3073         }
3074 }
3075
3076 int reload(void)
3077 {
3078         return(load_config());
3079 }
3080
3081 int unload_module(void)
3082 {
3083         int res;
3084         STANDARD_HANGUP_LOCALUSERS;
3085         res = ast_unregister_application(app);
3086         res |= ast_unregister_application(app2);
3087 #ifdef USEMYSQLVM
3088         mysql_logout();
3089 #endif
3090         return res;
3091 }
3092
3093 int load_module(void)
3094 {
3095         int res;
3096         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
3097         res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
3098         if (res)
3099                 return(res);
3100
3101         if ((res=load_config())) {
3102                 return(res);
3103         }
3104 #ifdef USEMYSQLVM
3105         if ((res=mysql_login())) {
3106                 return(res);
3107         }
3108 #endif
3109         return res;
3110 }
3111
3112 char *description(void)
3113 {
3114         return tdesc;
3115 }
3116
3117 int usecount(void)
3118 {
3119         int res;
3120         STANDARD_USECOUNT(res);
3121         return res;
3122 }
3123
3124 char *key()
3125 {
3126         return ASTERISK_GPL_KEY;
3127 }