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