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