Implement remaining queue strategies, ADSI fixes, and queue config updates
[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 pthread_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 = 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 pthread_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         pthread_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                 pthread_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                                 pthread_mutex_unlock(&mysqllock);
268                                 return(retval);
269                         } else {
270                                 mysql_free_result(result);
271                                 pthread_mutex_unlock(&mysqllock);
272                                 free(retval);
273                                 return(NULL);
274                         }
275                 }
276                 pthread_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         pthread_mutex_lock(&mysqllock);
292         mysql_query(dbhandler, query);
293         strcpy(vmu->password, password);
294         pthread_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         pthread_mutex_lock(&mysqllock);
307         mysql_query(dbhandler, query);
308         pthread_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_pthread_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_pthread_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_pthread_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_pthread_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], 1000);
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 %d to channel '%s'\n", 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                         printf("Got %d\n", atoi(username));
1797                         /* if (play_and_wait(chan, "vm-savedto"))
1798                                 break;
1799                         */
1800
1801                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1802                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1803                         ast_log(LOG_DEBUG, sys);
1804                         system(sys);
1805
1806                         todircount = count_messages(todir);
1807                         strncpy(tmp, fmt, sizeof(tmp));
1808                         stringp = tmp;
1809                         while((s = strsep(&stringp, "|"))) {
1810                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1811                                 ast_log(LOG_DEBUG, sys);
1812                                 system(sys);
1813                         }
1814                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1815
1816                         /* load the information on the source message so we can send an e-mail like a new message */
1817                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1818                         if ((mif=ast_load(miffile))) {
1819
1820               /* set callerid and duration variables */
1821               snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1822               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1823                         
1824               if (strlen(receiver->email)) {
1825                                 int attach_user_voicemail = attach_voicemail;
1826                                 char *myserveremail = serveremail;
1827                                 if (receiver->attach > -1)
1828                                         attach_user_voicemail = receiver->attach;
1829                                 if (strlen(receiver->serveremail))
1830                                         myserveremail = receiver->serveremail;
1831                       sendmail(myserveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1832               }
1833              
1834                         if (strlen(receiver->pager)) {
1835                                 char *myserveremail = serveremail;
1836                                 if (strlen(receiver->serveremail))
1837                                         myserveremail = receiver->serveremail;
1838                                 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration);
1839                         }
1840                           
1841                           ast_destroy(mif); /* or here */
1842                         }
1843
1844                         /* give confirmatopm that the message was saved */
1845                         res = play_and_wait(chan, "vm-message");
1846                         if (!res)
1847                                 res = play_and_wait(chan, "vm-saved");
1848                         free_user(receiver);
1849                         break;
1850                 } else {
1851                         res = play_and_wait(chan, "pbx-invalid");
1852                 }
1853         }
1854         return res;
1855 }
1856
1857 struct vm_state {
1858         char curbox[80];
1859         char username[80];
1860         char curdir[256];
1861         char vmbox[256];
1862         char fn[256];
1863         char fn2[256];
1864         int deleted[MAXMSG];
1865         int heard[MAXMSG];
1866         int curmsg;
1867         int lastmsg;
1868         int newmessages;
1869         int oldmessages;
1870         int starting;
1871         int repeats;
1872 };
1873
1874
1875 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1876 {
1877         int res;
1878         if ((res = ast_streamfile(chan, file, chan->language))) 
1879                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
1880         if (!res)
1881                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1882         return res;
1883 }
1884
1885 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
1886 {
1887         int res;
1888         if ((res = ast_streamfile(chan, file, chan->language)))
1889                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1890         if (!res)
1891                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
1892         return res;
1893 }
1894
1895 static int play_datetime_format(struct ast_channel *chan, time_t time, struct vm_state *vms, struct vm_zone *zone)
1896 {
1897         int d = 0, offset = 0, sndoffset = 0;
1898         char sndfile[256], nextmsg[256];
1899         struct tm tm;
1900         char *tzenv, current_tz[256] = "", *qmark;
1901
1902         tzenv = getenv("TZ");
1903         if (tzenv != NULL)
1904                 strncpy(current_tz, tzenv, sizeof(current_tz) - 1);
1905         if (zone->timezone && strcmp(current_tz,zone->timezone)) {
1906                 setenv("TZ", zone->timezone, 1);
1907                 tzset();
1908                 localtime_r(&time, &tm);
1909                 if (tzenv != NULL)
1910                         setenv("TZ", current_tz, 1);
1911                 else
1912                         unsetenv("TZ");
1913         } else {
1914                 /* No need to change the timezone */
1915                 localtime_r(&time, &tm);
1916         }
1917
1918         /* Check for a subexpression */
1919         if ((qmark = index(zone->msg_format, '?'))) {
1920                 /* TODO Allow subexpressions - we probably need to implement a parser here. */
1921         }
1922
1923         for (offset=0 ; zone->msg_format[offset] != '\0' ; offset++) {
1924                 ast_log(LOG_NOTICE, "Parsing %c in %s\n", zone->msg_format[offset], zone->msg_format);
1925                 switch (zone->msg_format[offset]) {
1926                         /* NOTE:  if you add more options here, please try to be consistent with strftime(3) */
1927                         case '\'':
1928                                 /* Literal name of a sound file */
1929                                 sndoffset=0;
1930                                 for (sndoffset=0 ; zone->msg_format[++offset] != '\'' ; sndoffset++)
1931                                         sndfile[sndoffset] = zone->msg_format[offset];
1932                                 sndfile[sndoffset] = '\0';
1933                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/%s", sndfile);
1934                                 d = wait_file(chan,vms,nextmsg);
1935                                 break;
1936                         case '$':
1937                                 /* Ooooh, variables and/or expressions */
1938                                 {
1939                                         struct vm_zone z;
1940                                         memcpy(&z,zone,sizeof(struct vm_zone));
1941                                         pbx_substitute_variables_helper(chan, zone->msg_format + offset, z.msg_format, sizeof(z.msg_format));
1942                                         d = play_datetime_format(chan, time, vms, &z);
1943                                         /* Subtract one, so that when the for loop increments, we point at the nil */
1944                                         offset = strlen(zone->msg_format) - 1;
1945                                 }
1946                                 break;
1947                         case 'A':
1948                         case 'a':
1949                                 /* Sunday - Saturday */
1950                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "day-%d", tm.tm_wday);
1951                                 d = wait_file(chan,vms,nextmsg);
1952                                 break;
1953                         case 'B':
1954                         case 'b':
1955                         case 'h':
1956                                 /* January - December */
1957                                 snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "mon-%d", tm.tm_mon);
1958                                 d = wait_file(chan,vms,nextmsg);
1959                                 break;
1960                         case 'd':
1961                         case 'e':
1962                                 /* First - Thirtyfirst */
1963                                 if ((tm.tm_mday < 21) || (tm.tm_mday == 30)) {
1964                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "h-%d", tm.tm_mday);
1965                                         d = wait_file(chan,vms,nextmsg);
1966                                 } else if (tm.tm_mday == 31) {
1967                                         /* "Thirty" and "first" */
1968                                         d = wait_file(chan,vms,DIGITS_DIR "30");
1969                                         if (!d) {
1970                                                 d = wait_file(chan,vms,DIGITS_DIR "h-1");
1971                                         }
1972                                 } else {
1973                                         /* Between 21 and 29 - two sounds */
1974                                         d = wait_file(chan,vms,DIGITS_DIR "20");
1975                                         if (!d) {
1976                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "h-%d", tm.tm_mday - 20);
1977                                                 d = wait_file(chan,vms,nextmsg);
1978                                         }
1979                                 }
1980                                 break;
1981                         case 'Y':
1982                                 /* Year */
1983                                 if (tm.tm_year > 99) {
1984                                         d = wait_file(chan,vms,DIGITS_DIR "2");
1985                                         if (!d) {
1986                                                 d = wait_file(chan,vms,DIGITS_DIR "thousand");
1987                                         }
1988                                         if (tm.tm_year > 100) {
1989                                                 if (!d) {
1990                                                         /* This works until the end of 2020 */
1991                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year - 100);
1992                                                         d = wait_file(chan,vms,nextmsg);
1993                                                 }
1994                                         }
1995                                 } else {
1996                                         if (tm.tm_year < 1) {
1997                                                 /* I'm not going to handle 1900 and prior */
1998                                                 /* We'll just be silent on the year, instead of bombing out. */
1999                                         } else {
2000                                                 d = wait_file(chan,vms,DIGITS_DIR "19");
2001                                                 if (!d) {
2002                                                         if (tm.tm_year < 20) {
2003                                                                 /* 1901 - 1920 */
2004                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_year);
2005                                                                 d = wait_file(chan,vms,nextmsg);
2006                                                         } else {
2007                                                                 /* 1921 - 1999 */
2008                                                                 int ten, one;
2009                                                                 ten = tm.tm_year / 10;
2010                                                                 one = tm.tm_year % 10;
2011                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten * 10);
2012                                                                 d = wait_file(chan,vms,nextmsg);
2013                                                                 if (!d) {
2014                                                                         if (one != 0) {
2015                                                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
2016                                                                                 d = wait_file(chan,vms,nextmsg);
2017                                                                         }
2018                                                                 }
2019                                                         }
2020                                                 }
2021                                         }
2022                                 }
2023                                 break;
2024                         case 'I':
2025                         case 'l':
2026                                 /* 12-Hour */
2027                                 if (tm.tm_hour == 0)
2028                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "12");
2029                                 else if (tm.tm_hour > 12)
2030                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour - 12);
2031                                 else
2032                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_hour);
2033                                 d = wait_file(chan,vms,nextmsg);
2034                                 break;
2035                         case 'H':
2036                         case 'k':
2037                                 /* 24-Hour */
2038                                 if (zone->msg_format[offset] == 'H') {
2039                                         /* e.g. oh-eight */
2040                                         if (tm.tm_hour < 10) {
2041                                                 d = wait_file(chan,vms,DIGITS_DIR "oh");
2042                                         }
2043                                 } else {
2044                                         /* e.g. eight */
2045                                         if (tm.tm_hour == 0) {
2046                                                 d = wait_file(chan,vms,DIGITS_DIR "oh");
2047                                         }
2048                                 }
2049                                 if (!d) {
2050                                         if (tm.tm_hour != 0) {
2051                                                 snprintf(nextmsg,sizeof(nextmsg), AST_SOUNDS "/digits/%d", tm.tm_hour);
2052                                                 d = wait_file(chan,vms,nextmsg);
2053                                         }
2054                                 }
2055                                 break;
2056                         case 'M':
2057                                 /* Minute */
2058                                 if (tm.tm_min == 0) {
2059                                         d = wait_file(chan,vms,DIGITS_DIR "oclock");
2060                                 } else if (tm.tm_min < 10) {
2061                                         d = wait_file(chan,vms,DIGITS_DIR "oh");
2062                                         if (!d) {
2063                                                 snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
2064                                                 d = wait_file(chan,vms,nextmsg);
2065                                         }
2066                                 } else if ((tm.tm_min < 21) || (tm.tm_min % 10 == 0)) {
2067                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", tm.tm_min);
2068                                         d = wait_file(chan,vms,nextmsg);
2069                                 } else {
2070                                         int ten, one;
2071                                         ten = (tm.tm_min / 10) * 10;
2072                                         one = (tm.tm_min % 10);
2073                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", ten);
2074                                         d = wait_file(chan,vms,nextmsg);
2075                                         if (!d) {
2076                                                 /* Fifty, not fifty-zero */
2077                                                 if (one != 0) {
2078                                                         snprintf(nextmsg,sizeof(nextmsg),DIGITS_DIR "%d", one);
2079                                                         d = wait_file(chan,vms,nextmsg);
2080                                                 }
2081                                         }
2082                                 }
2083                                 break;
2084                         case 'P':
2085                         case 'p':
2086                                 /* AM/PM */
2087                                 if ((tm.tm_hour == 0) || (tm.tm_hour > 12))
2088                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "p-m");
2089                                 else
2090                                         snprintf(nextmsg,sizeof(nextmsg), DIGITS_DIR "a-m");
2091                                 d = wait_file(chan,vms,nextmsg);
2092                                 break;
2093                         case 'Q':
2094                                 /* Shorthand for "Today", "Yesterday", or ABdY */
2095                                 {
2096                                         struct timeval now;
2097                                         struct tm tmnow;
2098                                         time_t beg_today;
2099
2100                                         gettimeofday(&now,NULL);
2101                                         localtime_r(&now.tv_sec,&tmnow);
2102                                         tmnow.tm_hour = 0;
2103                                         tmnow.tm_min = 0;
2104                                         tmnow.tm_sec = 0;
2105                                         beg_today = mktime(&tmnow);
2106                                         if (beg_today < time) {
2107                                                 /* Today */
2108                                                 d = wait_file(chan,vms,DIGITS_DIR "today");
2109                                         } else if (beg_today - 86400 < time) {
2110                                                 /* Yesterday */
2111                                                 d = wait_file(chan,vms,DIGITS_DIR "yesterday");
2112                                         } else {
2113                                                 struct vm_zone z;
2114                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2115                                                 strcpy(z.msg_format, "ABdY");
2116                                                 d = play_datetime_format(chan, time, vms, &z);
2117                                         }
2118                                 }
2119                                 break;
2120                         case 'q':
2121                                 /* Shorthand for "" (today), "Yesterday", A (weekday), or ABdY */
2122                                 {
2123                                         struct timeval now;
2124                                         struct tm tmnow;
2125                                         time_t beg_today;
2126
2127                                         gettimeofday(&now,NULL);
2128                                         localtime_r(&now.tv_sec,&tmnow);
2129                                         tmnow.tm_hour = 0;
2130                                         tmnow.tm_min = 0;
2131                                         tmnow.tm_sec = 0;
2132                                         beg_today = mktime(&tmnow);
2133                                         if (beg_today < time) {
2134                                                 /* Today */
2135                                         } else if (beg_today - 86400 < time) {
2136                                                 /* Yesterday */
2137                                                 d = wait_file(chan,vms,DIGITS_DIR "yesterday");
2138                                         } else if (beg_today - 86400 * 6 < time) {
2139                                                 /* Within the last week */
2140                                                 struct vm_zone z;
2141                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2142                                                 strcpy(z.msg_format, "A");
2143                                                 d = play_datetime_format(chan, time, vms, &z);
2144                                         } else {
2145                                                 struct vm_zone z;
2146                                                 memcpy(&z, zone, sizeof(struct vm_zone));
2147                                                 strcpy(z.msg_format, "ABdY");
2148                                                 d = play_datetime_format(chan, time, vms, &z);
2149                                         }
2150                                 }
2151                                 break;
2152                         case 'R':
2153                                 {
2154                                         struct vm_zone z;
2155                                         memcpy(&z, zone, sizeof(struct vm_zone));
2156                                         strcpy(z.msg_format, "HM");
2157                                         d = play_datetime_format(chan, time, vms, &z);
2158                                 }
2159                                 break;
2160                         case ' ':
2161                         case '  ':
2162                                 /* Just ignore spaces and tabs */
2163                                 break;
2164                         default:
2165                                 /* Unknown character */
2166                                 ast_log(LOG_WARNING, "Unknown character in datetime format %s: %c\n", zone->msg_format, zone->msg_format[offset]);
2167                 }
2168                 /* Jump out on DTMF */
2169                 if (d) {
2170                         break;
2171                 }
2172         }
2173         return d;
2174 }
2175
2176 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2177 {
2178         int res = 0;
2179         char filename[256], *origtime, temp[256];
2180         struct vm_zone *the_zone = NULL;
2181         struct ast_config *msg_cfg;
2182         time_t t;
2183         struct timeval tv_now;
2184         struct tm time_now, time_then;
2185
2186         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2187         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2188         msg_cfg = ast_load(filename);
2189         if (!msg_cfg) {
2190                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2191                 return 0;
2192         }
2193
2194         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2195                 return 0;
2196         if (sscanf(origtime,"%ld",&t) < 1) {
2197                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2198                 return 0;
2199         }
2200         ast_destroy(msg_cfg);
2201
2202         /* Does this user have a timezone specified? */
2203         if (strlen(vmu->zonetag)) {
2204                 /* Find the zone in the list */
2205                 struct vm_zone *z;
2206                 z = zones;
2207                 while (z) {
2208                         if (!strcmp(z->name, vmu->zonetag)) {
2209                                 the_zone = z;
2210                                 break;
2211                         }
2212                         z = z->next;
2213                 }
2214         }
2215
2216         /* If no zone, use a default */
2217         if (!the_zone) {
2218                 the_zone = alloca(sizeof(struct vm_zone));
2219                 memset(the_zone,0,sizeof(struct vm_zone));
2220                 strncpy(the_zone->msg_format, "'vm-received' q 'digits/at' IMp", sizeof(the_zone->msg_format) - 1);
2221         }
2222
2223         /* Set the DIFF_* variables */
2224         localtime_r(&t, &time_now);
2225         gettimeofday(&tv_now,NULL);
2226         localtime_r(&tv_now.tv_sec,&time_then);
2227
2228         /* Day difference */
2229         if (time_now.tm_year == time_then.tm_year)
2230                 sprintf(temp,"%d",time_now.tm_yday);
2231         else
2232                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2233         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2234
2235         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2236
2237         res = play_datetime_format(chan, t, vms, the_zone);
2238         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2239         return res;
2240 }
2241
2242 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2243 {
2244         int res = 0;
2245         vms->starting = 0; 
2246         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2247         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2248         if (!msg)
2249                 res = wait_file2(chan, vms, "vm-first");
2250         else if (msg == vms->lastmsg)
2251                 res = wait_file2(chan, vms, "vm-last");
2252         if (!res) {
2253                 res = wait_file2(chan, vms, "vm-message");
2254                 if (msg && (msg != vms->lastmsg)) {
2255                         if (!res)
2256                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2257                 }
2258         }
2259
2260         if (!res)
2261                 res = play_message_datetime(chan,vmu,vms);
2262
2263         if (!res) {
2264                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2265                 vms->heard[msg] = 1;
2266                 res = wait_file(chan, vms, vms->fn);
2267         }
2268         return res;
2269 }
2270
2271 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2272 {
2273         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2274         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2275         vms->lastmsg = count_messages(vms->curdir) - 1;
2276         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2277 }
2278
2279 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2280 {
2281         int x;
2282         char ntxt[256] = "";
2283         char txt[256] = "";
2284         if (vms->lastmsg > -1) { 
2285                 /* Get the deleted messages fixed */ 
2286                 vms->curmsg = -1; 
2287                 for (x=0;x < MAXMSG;x++) { 
2288                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2289                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2290                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2291                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2292                                         break;
2293                                 vms->curmsg++; 
2294                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2295                                 if (strcmp(vms->fn, vms->fn2)) { 
2296                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2297                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2298                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2299                                         rename(txt, ntxt); 
2300                                 } 
2301                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2302                                 /* Move to old folder before deleting */ 
2303                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2304                         } 
2305                 } 
2306                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2307                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2308                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2309                                 break;
2310                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2311                         ast_filedelete(vms->fn, NULL); 
2312                         unlink(txt); 
2313                 } 
2314         } 
2315         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2316         memset(vms->heard, 0, sizeof(vms->heard)); 
2317 }
2318
2319 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2320 {
2321         /* Introduce messages they have */
2322         int res;
2323         res = play_and_wait(chan, "vm-youhave");
2324         if (!res) {
2325                 if (vms->newmessages) {
2326                         res = say_and_wait(chan, vms->newmessages);
2327                         if (!res)
2328                                 res = play_and_wait(chan, "vm-INBOX");
2329                         if (vms->oldmessages && !res)
2330                                 res = play_and_wait(chan, "vm-and");
2331                         else if (!res) {
2332                                 if ((vms->newmessages == 1))
2333                                         res = play_and_wait(chan, "vm-message");
2334                                 else
2335                                         res = play_and_wait(chan, "vm-messages");
2336                         }
2337                                 
2338                 }
2339                 if (!res && vms->oldmessages) {
2340                         res = say_and_wait(chan, vms->oldmessages);
2341                         if (!res)
2342                                 res = play_and_wait(chan, "vm-Old");
2343                         if (!res) {
2344                                 if (vms->oldmessages == 1)
2345                                         res = play_and_wait(chan, "vm-message");
2346                                 else
2347                                         res = play_and_wait(chan, "vm-messages");
2348                         }
2349                 }
2350                 if (!res) {
2351                         if (!vms->oldmessages && !vms->newmessages) {
2352                                 res = play_and_wait(chan, "vm-no");
2353                                 if (!res)
2354                                         res = play_and_wait(chan, "vm-messages");
2355                         }
2356                 }
2357         }
2358         return res;
2359 }
2360
2361 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2362 {
2363         int res = 0;
2364         /* Play instructions and wait for new command */
2365         while(!res) {
2366                 if (vms->starting) {
2367                         if (vms->lastmsg > -1) {
2368                                 res = play_and_wait(chan, "vm-onefor");
2369                                 if (!res)
2370                                         res = play_and_wait(chan, vms->vmbox);
2371                                 if (!res)
2372                                         res = play_and_wait(chan, "vm-messages");
2373                         }
2374                         if (!res)
2375                                 res = play_and_wait(chan, "vm-opts");
2376                 } else {
2377                         if (vms->curmsg)
2378                                 res = play_and_wait(chan, "vm-prev");
2379                         if (!res)
2380                                 res = play_and_wait(chan, "vm-repeat");
2381                         if (!res && (vms->curmsg != vms->lastmsg))
2382                                 res = play_and_wait(chan, "vm-next");
2383                         if (!res) {
2384                                 if (!vms->deleted[vms->curmsg])
2385                                         res = play_and_wait(chan, "vm-delete");
2386                                 else
2387                                         res = play_and_wait(chan, "vm-undelete");
2388                                 if (!res)
2389                                         res = play_and_wait(chan, "vm-toforward");
2390                                 if (!res)
2391                                         res = play_and_wait(chan, "vm-savemessage");
2392                         }
2393                 }
2394                 if (!res)
2395                         res = play_and_wait(chan, "vm-helpexit");
2396                 if (!res)
2397                         res = ast_waitfordigit(chan, 6000);
2398                 if (!res) {
2399                         vms->repeats++;
2400                         if (vms->repeats > 2) {
2401                                 res = play_and_wait(chan, "vm-goodbye");
2402                                 if (!res)
2403                                         res = 't';
2404                         }
2405                 }
2406         }
2407         return res;
2408 }
2409
2410 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2411 {
2412         int cmd = 0;
2413         int retries = 0;
2414         char newpassword[80] = "";
2415         char newpassword2[80] = "";
2416         char prefile[256]="";
2417         char buf[256];
2418         int bytes=0;
2419
2420         if (adsi_available(chan))
2421         {
2422                 bytes += adsi_logo(buf + bytes);
2423                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2424                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2425                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2426                 bytes += adsi_voice_mode(buf + bytes, 0);
2427                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2428         }
2429         while((cmd >= 0) && (cmd != 't')) {
2430                 if (cmd)
2431                         retries = 0;
2432                 switch (cmd) {
2433                 case '1':
2434                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2435                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2436                         break;
2437                 case '2': 
2438                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2439                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2440                         break;
2441                 case '3': 
2442                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2443                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2444                         break;
2445                 case '4':
2446                         newpassword[1] = '\0';
2447                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2448                         if (cmd < 0)
2449                                 break;
2450                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2451                                 break;
2452             }
2453                         newpassword2[1] = '\0';
2454                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2455                         if (cmd < 0)
2456                                 break;
2457
2458                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2459                                 break;
2460             }
2461                         if (strcmp(newpassword, newpassword2)) {
2462                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2463                                 cmd = play_and_wait(chan, "vm-mismatch");
2464                                 break;
2465                         }
2466                         vm_change_password(vmu,newpassword);
2467                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2468                         cmd = play_and_wait(chan,"vm-passchanged");
2469                         break;
2470                 case '*': 
2471                         cmd = 't';
2472                         break;
2473                 default: 
2474                         cmd = play_and_wait(chan,"vm-options");
2475                         if (!cmd)
2476                                 cmd = ast_waitfordigit(chan,6000);
2477                         if (!cmd)
2478                                 retries++;
2479                         if (retries > 3)
2480                                 cmd = 't';
2481                  }
2482         }
2483         if (cmd == 't')
2484                 cmd = 0;
2485         return cmd;
2486 }
2487
2488 static int vm_execmain(struct ast_channel *chan, void *data)
2489 {
2490         /* XXX This is, admittedly, some pretty horrendus code.  For some
2491            reason it just seemed a lot easier to do with GOTO's.  I feel
2492            like I'm back in my GWBASIC days. XXX */
2493         int res=-1;
2494         int valid = 0;
2495         int prefix = 0;
2496         int cmd=0;
2497         struct localuser *u;
2498         char prefixstr[80] ="";
2499         char empty[80] = "";
2500         int box;
2501         int useadsi = 0;
2502         int skipuser = 0;
2503         char tmp[256], *ext;
2504         char fmtc[256] = "";
2505         char password[80];
2506         struct vm_state vms;
2507         int logretries = 0;
2508         struct ast_vm_user *vmu = NULL, vmus;
2509         char *context=NULL;
2510
2511         LOCAL_USER_ADD(u);
2512         memset(&vms, 0, sizeof(vms));
2513         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2514         if (chan->_state != AST_STATE_UP)
2515                 ast_answer(chan);
2516
2517         if (data && strlen(data)) {
2518                 strncpy(tmp, data, sizeof(tmp) - 1);
2519                 ext = tmp;
2520
2521                 switch (*ext) {
2522                         case 's':
2523                  /* We should skip the user's password */
2524                                 valid++;
2525                                 ext++;
2526                                 break;
2527                         case 'p':
2528                  /* We should prefix the mailbox with the supplied data */
2529                                 prefix++;
2530                                 ext++;
2531                                 break;
2532                 }
2533
2534                 context = strchr(ext, '@');
2535                 if (context) {
2536                         *context = '\0';
2537                         context++;
2538                 }
2539
2540                 if (prefix)
2541                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2542                 else
2543                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2544                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2545                         skipuser++;
2546                 else
2547                         valid = 0;
2548
2549         }
2550
2551         /* If ADSI is supported, setup login screen */
2552         adsi_begin(chan, &useadsi);
2553         if (!skipuser && useadsi)
2554                 adsi_login(chan);
2555         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2556                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2557                 goto out;
2558         }
2559         
2560         /* Authenticate them and get their mailbox/password */
2561         
2562         while (!valid && (logretries < maxlogins)) {
2563                 /* Prompt for, and read in the username */
2564                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2565                         ast_log(LOG_WARNING, "Couldn't read username\n");
2566                         goto out;
2567                 }
2568                 if (!strlen(vms.username)) {
2569                         if (option_verbose > 2)
2570                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2571                         res = 0;
2572                         goto out;
2573                 }
2574                 if (useadsi)
2575                         adsi_password(chan);
2576                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2577                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2578                         goto out;
2579                 }
2580                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2581                         ast_log(LOG_WARNING, "Unable to read password\n");
2582                         goto out;
2583                 }
2584                 if (prefix) {
2585                         char fullusername[80] = "";
2586                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2587                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2588                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2589                 }
2590                 if (!skipuser) 
2591                         vmu = find_user(&vmus, context, vms.username);
2592                 if (vmu && !strcmp(vmu->password, password)) 
2593                         valid++;
2594                 else {
2595                         if (option_verbose > 2)
2596                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2597                         if (prefix)
2598                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2599                 }
2600                 if (!valid) {
2601                         if (useadsi)
2602                                 adsi_login(chan);
2603                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2604                                 break;
2605                 }
2606                 logretries++;
2607         }
2608         if (!valid && (logretries >= maxlogins)) {
2609                 ast_stopstream(chan);
2610                 res = play_and_wait(chan, "vm-goodbye");
2611                 if (res > 0)
2612                         res = 0;
2613         }
2614
2615         if (valid) {
2616                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2617                 mkdir(vms.curdir, 0700);
2618                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2619                 mkdir(vms.curdir, 0700);
2620                 /* Retrieve old and new message counts */
2621                 open_mailbox(&vms, vmu, 1);
2622                 vms.oldmessages = vms.lastmsg + 1;
2623                 /* Start in INBOX */
2624                 open_mailbox(&vms, vmu, 0);
2625                 vms.newmessages = vms.lastmsg + 1;
2626                 
2627
2628                 /* Select proper mailbox FIRST!! */
2629                 if (!vms.newmessages && vms.oldmessages) {
2630                         /* If we only have old messages start here */
2631                         open_mailbox(&vms, vmu, 1);
2632                 }
2633
2634                 if (useadsi)
2635                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2636                 res = 0;
2637                 cmd = vm_intro(chan, &vms);
2638                 vms.repeats = 0;
2639                 vms.starting = 1;
2640                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2641                         /* Run main menu */
2642                         switch(cmd) {
2643                         case '1':
2644                                 vms.curmsg = 0;
2645                                 /* Fall through */
2646                         case '5':
2647                                 if (vms.lastmsg > -1) {
2648                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2649                                 } else {
2650                                         cmd = play_and_wait(chan, "vm-youhave");
2651                                         if (!cmd) 
2652                                                 cmd = play_and_wait(chan, "vm-no");
2653                                         if (!cmd) {
2654                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2655                                                 cmd = play_and_wait(chan, vms.fn);
2656                                         }
2657                                         if (!cmd)
2658                                                 cmd = play_and_wait(chan, "vm-messages");
2659                                 }
2660                                 break;
2661                         case '2': /* Change folders */
2662                                 if (useadsi)
2663                                         adsi_folders(chan, 0, "Change to folder...");
2664                                 cmd = get_folder2(chan, "vm-changeto", 0);
2665                                 if (cmd == '#') {
2666                                         cmd = 0;
2667                                 } else if (cmd > 0) {
2668                                         cmd = cmd - '0';
2669                                         close_mailbox(&vms, vmu);
2670                                         open_mailbox(&vms, vmu, cmd);
2671                                         cmd = 0;
2672                                 }
2673                                 if (useadsi)
2674                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2675                                 if (!cmd)
2676                                         cmd = play_and_wait(chan, vms.vmbox);
2677                                 if (!cmd)
2678                                         cmd = play_and_wait(chan, "vm-messages");
2679                                 vms.starting = 1;
2680                                 break;
2681                         case '4':
2682                                 if (vms.curmsg) {
2683                                         vms.curmsg--;
2684                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2685                                 } else {
2686                                         cmd = play_and_wait(chan, "vm-nomore");
2687                                 }
2688                                 break;
2689                         case '6':
2690                                 if (vms.curmsg < vms.lastmsg) {
2691                                         vms.curmsg++;
2692                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2693                                 } else {
2694                                         cmd = play_and_wait(chan, "vm-nomore");
2695                                 }
2696                                 break;
2697                         case '7':
2698                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2699                                 if (useadsi)
2700                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2701                                 if (vms.deleted[vms.curmsg]) 
2702                                         cmd = play_and_wait(chan, "vm-deleted");
2703                                 else
2704                                         cmd = play_and_wait(chan, "vm-undeleted");
2705                                 break;
2706                         case '8':
2707                                 if(vms.lastmsg > -1)
2708                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2709                                 break;
2710                         case '9':
2711                                 if (useadsi)
2712                                         adsi_folders(chan, 1, "Save to folder...");
2713                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2714                                 box = 0;        /* Shut up compiler */
2715                                 if (cmd == '#') {
2716                                         cmd = 0;
2717                                         break;
2718                                 } else if (cmd > 0) {
2719                                         box = cmd = cmd - '0';
2720                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2721                                         vms.deleted[vms.curmsg]=1;
2722                                 }
2723                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2724                                 if (useadsi)
2725                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2726                                 if (!cmd)
2727                                         cmd = play_and_wait(chan, "vm-message");
2728                                 if (!cmd)
2729                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2730                                 if (!cmd)
2731                                         cmd = play_and_wait(chan, "vm-savedto");
2732                                 if (!cmd) {
2733                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2734                                         cmd = play_and_wait(chan, vms.fn);
2735                                 }
2736                                 if (!cmd)
2737                                         cmd = play_and_wait(chan, "vm-messages");
2738                                 break;
2739                         case '*':
2740                                 if (!vms.starting) {
2741                                         cmd = play_and_wait(chan, "vm-onefor");
2742                                         if (!cmd)
2743                                                 cmd = play_and_wait(chan, vms.vmbox);
2744                                         if (!cmd)
2745                                                 cmd = play_and_wait(chan, "vm-messages");
2746                                         if (!cmd)
2747                                                 cmd = play_and_wait(chan, "vm-opts");
2748                                 } else
2749                                         cmd = 0;
2750                                 break;
2751                         case '0':
2752                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2753                                 if (useadsi)
2754                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2755                                 break;
2756                         default:        /* Nothing */
2757                                 cmd = vm_instructions(chan, &vms);
2758                                 break;
2759                         }
2760                 }
2761                 if ((cmd == 't') || (cmd == '#')) {
2762                         /* Timeout */
2763                         res = 0;
2764                 } else {
2765                         /* Hangup */
2766                         res = -1;
2767                 }
2768         }
2769 out:
2770         if (res > -1) {
2771                 ast_stopstream(chan);
2772                 adsi_goodbye(chan);
2773                 res = play_and_wait(chan, "vm-goodbye");
2774                 if (res > 0)
2775                         res = 0;
2776                 if (useadsi)
2777                         adsi_unload_session(chan);
2778         }
2779         if (vmu)
2780                 close_mailbox(&vms, vmu);
2781         if (vmu)
2782                 free_user(vmu);
2783         if (valid) {
2784                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2785         }
2786         LOCAL_USER_REMOVE(u);
2787         return res;
2788
2789 }
2790
2791 static int vm_exec(struct ast_channel *chan, void *data)
2792 {
2793         int res=0, silent=0, busy=0, unavail=0;
2794         struct localuser *u;
2795         char tmp[256], *ext;
2796         
2797         LOCAL_USER_ADD(u);
2798         if (chan->_state != AST_STATE_UP)
2799                 ast_answer(chan);
2800         if (data)
2801                 strncpy(tmp, data, sizeof(tmp) - 1);
2802         else {
2803                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2804                 if (res < 0)
2805                         return res;
2806                 if (!strlen(tmp))
2807                         return 0;
2808         }
2809         ext = tmp;
2810         while(*ext) {
2811                 if (*ext == 's') {
2812                         silent = 2;
2813                         ext++;
2814                 } else if (*ext == 'b') {
2815                         busy=1;
2816                         ext++;
2817                 } else if (*ext == 'u') {
2818                         unavail=1;
2819                         ext++;
2820                 } else 
2821                         break;
2822         }
2823         res = leave_voicemail(chan, ext, silent, busy, unavail);
2824         LOCAL_USER_REMOVE(u);
2825         return res;
2826 }
2827
2828 static int append_mailbox(char *context, char *mbox, char *data)
2829 {
2830         /* Assumes lock is already held */
2831         char tmp[256] = "";
2832         char *stringp;
2833         char *s;
2834         struct ast_vm_user *vmu;
2835         strncpy(tmp, data, sizeof(tmp));
2836         vmu = malloc(sizeof(struct ast_vm_user));
2837         if (vmu) {
2838                 memset(vmu, 0, sizeof(struct ast_vm_user));
2839                 strncpy(vmu->context, context, sizeof(vmu->context));
2840                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2841                 vmu->attach = -1;
2842                 stringp = tmp;
2843                 if ((s = strsep(&stringp, ","))) 
2844                         strncpy(vmu->password, s, sizeof(vmu->password));
2845                 if (stringp && (s = strsep(&stringp, ","))) 
2846                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2847                 if (stringp && (s = strsep(&stringp, ","))) 
2848                         strncpy(vmu->email, s, sizeof(vmu->email));
2849                 if (stringp && (s = strsep(&stringp, ","))) 
2850                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2851                 if (stringp && (s = strsep(&stringp, ","))) 
2852                         apply_options(vmu, s);
2853                 vmu->next = NULL;
2854                 if (usersl)
2855                         usersl->next = vmu;
2856                 else
2857                         users = vmu;
2858                 usersl = vmu;
2859         }
2860         return 0;
2861 }
2862
2863 static int load_config(void)