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