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