Fix VM "goodbye" for real
[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         
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 (gotsilence)
721                         ast_stream_rewind(others[x], totalsilence-1000);
722                 else
723                         ast_stream_rewind(others[x], 1000);
724                 ast_truncstream(others[x]);
725                 ast_closestream(others[x]);
726         }
727         if (outmsg) {
728                 if (outmsg > 1) {
729                 /* Let them know it worked */
730                         ast_streamfile(chan, "vm-msgsaved", chan->language);
731                         ast_waitstream(chan, "");
732                 }
733         }       
734
735         
736         return res;
737 }
738
739 static void free_user(struct ast_vm_user *vmu)
740 {
741         if (vmu->alloced)
742                 free(vmu);
743 }
744
745 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
746 {
747         char comment[256];
748         char txtfile[256];
749         FILE *txt;
750         int res = 0;
751         int msgnum;
752         char date[256];
753         char dir[256];
754         char fn[256];
755         char prefile[256]="";
756         char fmt[80];
757         char *context;
758         char *ecodes = "#";
759         char *stringp;
760         time_t start;
761         time_t end;
762         char tmp[256] = "";
763         struct ast_vm_user *vmu;
764         struct ast_vm_user svm;
765         
766         strncpy(tmp, ext, sizeof(tmp) - 1);
767         ext = tmp;
768         context = strchr(tmp, '@');
769         if (context) {
770                 *context = '\0';
771                 context++;
772         }
773
774         if ((vmu = find_user(&svm, context, ext))) {
775                 /* Setup pre-file if appropriate */
776                 if (busy)
777                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
778                 else if (unavail)
779                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
780                 make_dir(dir, sizeof(dir), vmu->context, "", "");
781                 /* It's easier just to try to make it than to check for its existence */
782                 if (mkdir(dir, 0700) && (errno != EEXIST))
783                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
784                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
785                 /* It's easier just to try to make it than to check for its existence */
786                 if (mkdir(dir, 0700) && (errno != EEXIST))
787                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
788                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
789                 if (mkdir(dir, 0700) && (errno != EEXIST))
790                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
791                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
792                         ecodes = "#0";
793                 /* Play the beginning intro if desired */
794                 if (strlen(prefile)) {
795                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
796                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
797                                     res = ast_waitstream(chan, "#0");
798                         } else {
799                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
800                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
801                         }
802                         if (res < 0) {
803                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
804                                 free_user(vmu);
805                                 return -1;
806                         }
807                 }
808                 if (res == '#') {
809                         /* On a '#' we skip the instructions */
810                         silent = 1;
811                         res = 0;
812                 }
813                 if (!res && !silent) {
814                         res = ast_streamfile(chan, INTRO, chan->language);
815                         if (!res)
816                                 res = ast_waitstream(chan, ecodes);
817                         if (res == '#') {
818                                 silent = 1;
819                                 res = 0;
820                         }
821                 }
822                 /* Check for a '0' here */
823                 if (res == '0') {
824                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
825                         if (strlen(chan->macrocontext))
826                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
827                         chan->priority = 0;
828                         free_user(vmu);
829                         return 0;
830                 }
831                 if (res >= 0) {
832                         /* Unless we're *really* silent, try to send the beep */
833                         res = ast_streamfile(chan, "beep", chan->language);
834                         if (!res)
835                                 res = ast_waitstream(chan, "");
836                 }
837                 if (res < 0) {
838                         free_user(vmu);
839                         return -1;
840                 }
841                 /* The meat of recording the message...  All the announcements and beeps have been played*/
842                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
843                 if (strlen(fmt)) {
844                         msgnum = 0;
845                         do {
846                                 make_file(fn, sizeof(fn), dir, msgnum);
847                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
848                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
849                                                                         vmu->fullname, ext, chan->name);
850                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
851                                         break;
852                                 msgnum++;
853                         } while(msgnum < MAXMSG);
854                         if (msgnum < MAXMSG) {
855                                 /* Store information */
856                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
857                                 txt = fopen(txtfile, "w+");
858                                 if (txt) {
859                                         get_date(date, sizeof(date));
860                                         time(&start);
861                                         fprintf(txt, 
862 ";\n"
863 "; Message Information file\n"
864 ";\n"
865 "[message]\n"
866 "origmailbox=%s\n"
867 "context=%s\n"
868 "exten=%s\n"
869 "priority=%d\n"
870 "callerchan=%s\n"
871 "callerid=%s\n"
872 "origdate=%s\n"
873 "origtime=%ld\n",
874         ext,
875         chan->context,
876         chan->exten,
877         chan->priority,
878         chan->name,
879         chan->callerid ? chan->callerid : "Unknown",
880         date, time(NULL));
881                                         fclose(txt);
882                                 } else
883                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
884                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
885                                 if (res > 0)
886                                         res = 0;
887                                 txt = fopen(txtfile, "a");
888                                 if (txt) {
889                                         time(&end);
890                                         fprintf(txt, "duration=%ld\n", end-start);
891                                         fclose(txt);
892                                 }
893                                 stringp = fmt;
894                                 strsep(&stringp, "|");
895                                 /* Send e-mail if applicable */
896                                 if (strlen(vmu->email))
897                                         sendmail(serveremail, vmu->email, vmu->fullname, msgnum, ext, chan->callerid, fn, fmt, end - start);
898                                 if (strlen(vmu->pager))
899                                         sendpage(serveremail, vmu->pager, msgnum, ext, chan->callerid, end - start);
900                         } else
901                                 ast_log(LOG_WARNING, "No more messages possible\n");
902                 } else
903                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
904                 free_user(vmu);
905         } else
906                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
907         /* Leave voicemail for someone */
908         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
909         return res;
910 }
911
912 static char *mbox(int id)
913 {
914         switch(id) {
915         case 0:
916                 return "INBOX";
917         case 1:
918                 return "Old";
919         case 2:
920                 return "Work";
921         case 3:
922                 return "Family";
923         case 4:
924                 return "Friends";
925         case 5:
926                 return "Cust1";
927         case 6:
928                 return "Cust2";
929         case 7:
930                 return "Cust3";
931         case 8:
932                 return "Cust4";
933         case 9:
934                 return "Cust5";
935         default:
936                 return "Unknown";
937         }
938 }
939
940 static int count_messages(char *dir)
941 {
942         int x;
943         char fn[256];
944         for (x=0;x<MAXMSG;x++) {
945                 make_file(fn, sizeof(fn), dir, x);
946                 if (ast_fileexists(fn, NULL, NULL) < 1)
947                         break;
948         }
949         return x;
950 }
951
952 static int say_and_wait(struct ast_channel *chan, int num)
953 {
954         int d;
955         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
956         return d;
957 }
958
959 static int copy(char *infile, char *outfile)
960 {
961         int ifd;
962         int ofd;
963         int res;
964         int len;
965         char buf[4096];
966         if ((ifd = open(infile, O_RDONLY)) < 0) {
967                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
968                 return -1;
969         }
970         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
971                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
972                 close(ifd);
973                 return -1;
974         }
975         do {
976                 len = read(ifd, buf, sizeof(buf));
977                 if (len < 0) {
978                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
979                         close(ifd);
980                         close(ofd);
981                         unlink(outfile);
982                 }
983                 if (len) {
984                         res = write(ofd, buf, len);
985                         if (res != len) {
986                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
987                                 close(ifd);
988                                 close(ofd);
989                                 unlink(outfile);
990                         }
991                 }
992         } while(len);
993         close(ifd);
994         close(ofd);
995         return 0;
996 }
997
998 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
999 {
1000         char sfn[256];
1001         char dfn[256];
1002         char ddir[256];
1003         char txt[256];
1004         char ntxt[256];
1005         char *dbox = mbox(box);
1006         int x;
1007         make_file(sfn, sizeof(sfn), dir, msg);
1008         make_dir(ddir, sizeof(ddir), context, username, dbox);
1009         mkdir(ddir, 0700);
1010         for (x=0;x<MAXMSG;x++) {
1011                 make_file(dfn, sizeof(dfn), ddir, x);
1012                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1013                         break;
1014         }
1015         if (x >= MAXMSG)
1016                 return -1;
1017         ast_filecopy(sfn, dfn, NULL);
1018         if (strcmp(sfn, dfn)) {
1019                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1020                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1021                 copy(txt, ntxt);
1022         }
1023         return 0;
1024 }
1025
1026 static int adsi_logo(unsigned char *buf)
1027 {
1028         int bytes = 0;
1029         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1030         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1031         return bytes;
1032 }
1033
1034 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1035 {
1036         char buf[256];
1037         int bytes=0;
1038         int x;
1039         char num[5];
1040
1041         *useadsi = 0;
1042         bytes += adsi_data_mode(buf + bytes);
1043         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1044
1045         bytes = 0;
1046         bytes += adsi_logo(buf);
1047         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1048 #ifdef DISPLAY
1049         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1050 #endif
1051         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1052         bytes += adsi_data_mode(buf + bytes);
1053         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1054
1055         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1056                 bytes = 0;
1057                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1058                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1059                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1060                 bytes += adsi_voice_mode(buf + bytes, 0);
1061                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1062                 return 0;
1063         }
1064
1065 #ifdef DISPLAY
1066         /* Add a dot */
1067         bytes = 0;
1068         bytes += adsi_logo(buf);
1069         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1070         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1071         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1072         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1073 #endif
1074         bytes = 0;
1075         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1076         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1077         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1078         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
1079         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1080         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1081         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1082
1083 #ifdef DISPLAY
1084         /* Add another dot */
1085         bytes = 0;
1086         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1087         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1088         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1089 #endif
1090
1091         bytes = 0;
1092         /* These buttons we load but don't use yet */
1093         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1094         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1095         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1096         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1097         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1098         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1099         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1100
1101 #ifdef DISPLAY
1102         /* Add another dot */
1103         bytes = 0;
1104         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1105         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1106         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1107 #endif
1108
1109         bytes = 0;
1110         for (x=0;x<5;x++) {
1111                 snprintf(num, sizeof(num), "%d", x);
1112                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1113         }
1114         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1115         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1116
1117 #ifdef DISPLAY
1118         /* Add another dot */
1119         bytes = 0;
1120         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1121         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1122         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1123 #endif
1124
1125         if (adsi_end_download(chan)) {
1126                 bytes = 0;
1127                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1128                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1129                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1130                 bytes += adsi_voice_mode(buf + bytes, 0);
1131                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1132                 return 0;
1133         }
1134         bytes = 0;
1135         bytes += adsi_download_disconnect(buf + bytes);
1136         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1137
1138         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1139
1140 #ifdef DISPLAY
1141         /* Add last dot */
1142         bytes = 0;
1143         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1144         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1145 #endif
1146         ast_log(LOG_DEBUG, "Restarting session...\n");
1147
1148         bytes = 0;
1149         /* Load the session now */
1150         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1151                 *useadsi = 1;
1152                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1153         } else
1154                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1155
1156         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1157         return 0;
1158 }
1159
1160 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1161 {
1162         int x;
1163         if (!adsi_available(chan))
1164           return;
1165         x = adsi_load_session(chan, adapp, adver, 1);
1166         if (x < 0)
1167                 return;
1168         if (!x) {
1169                 if (adsi_load_vmail(chan, useadsi)) {
1170                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1171                         return;
1172                 }
1173         } else
1174                 *useadsi = 1;
1175 }
1176
1177 static void adsi_login(struct ast_channel *chan)
1178 {
1179         char buf[256];
1180         int bytes=0;
1181         unsigned char keys[8];
1182         int x;
1183         if (!adsi_available(chan))
1184                 return;
1185
1186         for (x=0;x<8;x++)
1187                 keys[x] = 0;
1188         /* Set one key for next */
1189         keys[3] = ADSI_KEY_APPS + 3;
1190
1191         bytes += adsi_logo(buf + bytes);
1192         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1193         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1194         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1195         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1196         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1197         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1198         bytes += adsi_set_keys(buf + bytes, keys);
1199         bytes += adsi_voice_mode(buf + bytes, 0);
1200         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1201 }
1202
1203 static void adsi_password(struct ast_channel *chan)
1204 {
1205         char buf[256];
1206         int bytes=0;
1207         unsigned char keys[8];
1208         int x;
1209         if (!adsi_available(chan))
1210                 return;
1211
1212         for (x=0;x<8;x++)
1213                 keys[x] = 0;
1214         /* Set one key for next */
1215         keys[3] = ADSI_KEY_APPS + 3;
1216
1217         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1218         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1219         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1220         bytes += adsi_set_keys(buf + bytes, keys);
1221         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1222 }
1223
1224 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1225 {
1226         char buf[256];
1227         int bytes=0;
1228         unsigned char keys[8];
1229         int x,y;
1230
1231         if (!adsi_available(chan))
1232                 return;
1233
1234         for (x=0;x<5;x++) {
1235                 y = ADSI_KEY_APPS + 12 + start + x;
1236                 if (y > ADSI_KEY_APPS + 12 + 4)
1237                         y = 0;
1238                 keys[x] = ADSI_KEY_SKT | y;
1239         }
1240         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1241         keys[6] = 0;
1242         keys[7] = 0;
1243
1244         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1245         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1246         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1247         bytes += adsi_set_keys(buf + bytes, keys);
1248         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1249 }
1250
1251 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1252 {
1253         int bytes=0;
1254         char buf[256], buf1[256], buf2[256];
1255         char fn2[256];
1256         char cid[256]="";
1257         char *val;
1258         char *name, *num;
1259         char datetime[21]="";
1260         FILE *f;
1261
1262         unsigned char keys[8];
1263
1264         int x;
1265
1266         if (!adsi_available(chan))
1267                 return;
1268
1269         /* Retrieve important info */
1270         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1271         f = fopen(fn2, "r");
1272         if (f) {
1273                 while(!feof(f)) {       
1274                         fgets(buf, sizeof(buf), f);
1275                         if (!feof(f)) {
1276                                 char *stringp=NULL;
1277                                 stringp=buf;
1278                                 strsep(&stringp, "=");
1279                                 val = strsep(&stringp, "=");
1280                                 if (val && strlen(val)) {
1281                                         if (!strcmp(buf, "callerid"))
1282                                                 strncpy(cid, val, sizeof(cid) - 1);
1283                                         if (!strcmp(buf, "origdate"))
1284                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1285                                 }
1286                         }
1287                 }
1288                 fclose(f);
1289         }
1290         /* New meaning for keys */
1291         for (x=0;x<5;x++)
1292                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1293         keys[6] = 0x0;
1294         keys[7] = 0x0;
1295
1296         if (!msg) {
1297                 /* No prev key, provide "Folder" instead */
1298                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1299         }
1300         if (msg >= last) {
1301                 /* If last message ... */
1302                 if (msg) {
1303                         /* but not only message, provide "Folder" instead */
1304                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1305                 } else {
1306                         /* Otherwise if only message, leave blank */
1307                         keys[3] = 1;
1308                 }
1309         }
1310
1311         if (strlen(cid)) {
1312                 ast_callerid_parse(cid, &name, &num);
1313                 if (!name)
1314                         name = num;
1315         } else
1316                 name = "Unknown Caller";
1317
1318         /* If deleted, show "undeleted" */
1319         if (deleted)
1320                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1321
1322         /* Except "Exit" */
1323         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1324         snprintf(buf1, sizeof(buf1), "%s%s", folder,
1325                  strcasecmp(folder, "INBOX") ? " Messages" : "");
1326         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1327
1328         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1329         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1330         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1331         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1332         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1333         bytes += adsi_set_keys(buf + bytes, keys);
1334         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1335 }
1336
1337 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1338 {
1339         int bytes=0;
1340         char buf[256];
1341         unsigned char keys[8];
1342
1343         int x;
1344
1345         if (!adsi_available(chan))
1346                 return;
1347
1348         /* New meaning for keys */
1349         for (x=0;x<5;x++)
1350                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1351
1352         keys[6] = 0x0;
1353         keys[7] = 0x0;
1354
1355         if (!msg) {
1356                 /* No prev key, provide "Folder" instead */
1357                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1358         }
1359         if (msg >= last) {
1360                 /* If last message ... */
1361                 if (msg) {
1362                         /* but not only message, provide "Folder" instead */
1363                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1364                 } else {
1365                         /* Otherwise if only message, leave blank */
1366                         keys[3] = 1;
1367                 }
1368         }
1369
1370         /* If deleted, show "undeleted" */
1371         if (deleted) 
1372                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1373
1374         /* Except "Exit" */
1375         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1376         bytes += adsi_set_keys(buf + bytes, keys);
1377         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1378 }
1379
1380 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1381 {
1382         char buf[256], buf1[256], buf2[256];
1383         int bytes=0;
1384         unsigned char keys[8];
1385         int x;
1386
1387         char *newm = (new == 1) ? "message" : "messages";
1388         char *oldm = (old == 1) ? "message" : "messages";
1389         if (!adsi_available(chan))
1390                 return;
1391         if (new) {
1392                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1393                 if (old) {
1394                         strcat(buf1, " and");
1395                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1396                 } else {
1397                         snprintf(buf2, sizeof(buf2), "%s.", newm);
1398                 }
1399         } else if (old) {
1400                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1401                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1402         } else {
1403                 strcpy(buf1, "You have no messages.");
1404                 strcpy(buf2, " ");
1405         }
1406         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1407         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1408         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1409
1410         for (x=0;x<6;x++)
1411                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1412         keys[6] = 0;
1413         keys[7] = 0;
1414
1415         /* Don't let them listen if there are none */
1416         if (lastmsg < 0)
1417                 keys[0] = 1;
1418         bytes += adsi_set_keys(buf + bytes, keys);
1419
1420         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1421 }
1422
1423 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1424 {
1425         char buf[256], buf1[256], buf2[256];
1426         int bytes=0;
1427         unsigned char keys[8];
1428         int x;
1429
1430         char *mess = (messages == 1) ? "message" : "messages";
1431
1432         if (!adsi_available(chan))
1433                 return;
1434
1435         /* Original command keys */
1436         for (x=0;x<6;x++)
1437                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1438
1439         keys[6] = 0;
1440         keys[7] = 0;
1441
1442         if (messages < 1)
1443                 keys[0] = 0;
1444
1445         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1446                         strcasecmp(folder, "INBOX") ? " folder" : "");
1447
1448         if (messages)
1449                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1450         else
1451                 strcpy(buf2, "no messages.");
1452         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1453         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1454         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1455         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1456         bytes += adsi_set_keys(buf + bytes, keys);
1457
1458         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1459         
1460 }
1461
1462 static void adsi_clear(struct ast_channel *chan)
1463 {
1464         char buf[256];
1465         int bytes=0;
1466         if (!adsi_available(chan))
1467                 return;
1468         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1469         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1470 }
1471
1472 static void adsi_goodbye(struct ast_channel *chan)
1473 {
1474         char buf[256];
1475         int bytes=0;
1476
1477         if (!adsi_available(chan))
1478                 return;
1479         bytes += adsi_logo(buf + bytes);
1480         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1481         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1482         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1483         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1484 }
1485
1486 static int get_folder(struct ast_channel *chan, int start)
1487 {
1488         int x;
1489         int d;
1490         char fn[256];
1491         d = play_and_wait(chan, "vm-press");
1492         if (d)
1493                 return d;
1494         for (x = start; x< 5; x++) {
1495                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1496                         return d;
1497                 d = play_and_wait(chan, "vm-for");
1498                 if (d)
1499                         return d;
1500                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1501                 d = play_and_wait(chan, fn);
1502                 if (d)
1503                         return d;
1504                 d = play_and_wait(chan, "vm-messages");
1505                 if (d)
1506                         return d;
1507                 d = ast_waitfordigit(chan, 500);
1508                 if (d)
1509                         return d;
1510         }
1511         d = play_and_wait(chan, "vm-tocancel");
1512         if (d)
1513                 return d;
1514         d = ast_waitfordigit(chan, 4000);
1515         return d;
1516 }
1517
1518 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1519 {
1520         int res = 0;
1521         res = play_and_wait(chan, fn);
1522         while (((res < '0') || (res > '9')) &&
1523                         (res != '#') && (res >= 0)) {
1524                 res = get_folder(chan, 0);
1525         }
1526         return res;
1527 }
1528
1529 static int
1530 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1531 {
1532         char username[70];
1533         char sys[256];
1534         char todir[256];
1535         int todircount=0;
1536         long duration;
1537         struct ast_config *mif;
1538         char miffile[256];
1539         char fn[256];
1540         char callerid[512];
1541         int res = 0;
1542         struct ast_vm_user *receiver, srec;
1543         char tmp[256];
1544         char *stringp, *s;
1545         
1546         while(!res) {
1547                 res = ast_streamfile(chan, "vm-extension", chan->language);
1548                 if (res)
1549                         break;
1550                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1551                         break;
1552                 if ((receiver = find_user(&srec, context, username))) {
1553                         printf("Got %d\n", atoi(username));
1554                         /* if (play_and_wait(chan, "vm-savedto"))
1555                                 break;
1556                         */
1557
1558                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1559                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1560                         ast_log(LOG_DEBUG, sys);
1561                         system(sys);
1562
1563                         todircount = count_messages(todir);
1564                         strncpy(tmp, fmt, sizeof(tmp));
1565                         stringp = tmp;
1566                         while((s = strsep(&stringp, "|"))) {
1567                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1568                                 ast_log(LOG_DEBUG, sys);
1569                                 system(sys);
1570                         }
1571                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1572
1573                         /* load the information on the source message so we can send an e-mail like a new message */
1574                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1575                         if ((mif=ast_load(miffile))) {
1576
1577               /* set callerid and duration variables */
1578               snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1579               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1580                         
1581                           if (strlen(receiver->email))
1582                             sendmail(serveremail, receiver->email, receiver->fullname, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")));
1583                                      
1584                           if (strlen(receiver->pager))
1585                                 sendpage(serveremail, receiver->pager, todircount, username, callerid, duration);
1586                           
1587                           ast_destroy(mif); /* or here */
1588                         }
1589
1590                         /* give confirmatopm that the message was saved */
1591                         res = play_and_wait(chan, "vm-message");
1592                         if (!res)
1593                                 res = play_and_wait(chan, "vm-saved");
1594                         free_user(receiver);
1595                         break;
1596                 } else {
1597                         res = play_and_wait(chan, "pbx-invalid");
1598                 }
1599         }
1600         return res;
1601 }
1602
1603 struct vm_state {
1604         char curbox[80];
1605         char username[80];
1606         char curdir[256];
1607         char vmbox[256];
1608         char fn[256];
1609         char fn2[256];
1610         int deleted[MAXMSG];
1611         int heard[MAXMSG];
1612         int curmsg;
1613         int lastmsg;
1614         int newmessages;
1615         int oldmessages;
1616         int starting;
1617         int repeats;
1618 };
1619
1620
1621 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1622 {
1623         int res;
1624         if ((res = ast_streamfile(chan, file, chan->language))) 
1625                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
1626         if (!res)
1627                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1628         return res;
1629 }
1630
1631 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
1632 {
1633         int res;
1634         if ((res = ast_streamfile(chan, file, chan->language)))
1635                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
1636         if (!res)
1637                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
1638         return res;
1639 }
1640
1641 static int play_message(struct ast_channel *chan, struct vm_state *vms, int msg)
1642 {
1643         int res = 0;
1644         vms->starting = 0; 
1645         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1646         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
1647         if (!msg)
1648                 res = wait_file2(chan, vms, "vm-first");
1649         else if (msg == vms->lastmsg)
1650                 res = wait_file2(chan, vms, "vm-last");
1651         if (!res) {
1652                 res = wait_file2(chan, vms, "vm-message");
1653                 if (msg && (msg != vms->lastmsg)) {
1654                         if (!res)
1655                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
1656                 }
1657         }
1658         
1659         if (!res) {
1660                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
1661                 vms->heard[msg] = 1;
1662                 res = wait_file(chan, vms, vms->fn);
1663         }
1664         return res;
1665 }
1666
1667 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
1668 {
1669         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
1670         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
1671         vms->lastmsg = count_messages(vms->curdir) - 1;
1672         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
1673 }
1674
1675 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
1676 {
1677         int x;
1678         char ntxt[256] = "";
1679         char txt[256] = "";
1680         if (vms->lastmsg > -1) { 
1681                 /* Get the deleted messages fixed */ 
1682                 vms->curmsg = -1; 
1683                 for (x=0;x<=vms->lastmsg;x++) { 
1684                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
1685                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
1686                                 vms->curmsg++; 
1687                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
1688                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
1689                                 if (strcmp(vms->fn, vms->fn2)) { 
1690                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
1691                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
1692                                         ast_filerename(vms->fn, vms->fn2, NULL); 
1693                                         rename(txt, ntxt); 
1694                                 } 
1695                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
1696                                 /* Move to old folder before deleting */ 
1697                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
1698                         } 
1699                 } 
1700                 for (x = vms->curmsg + 1; x<=vms->lastmsg; x++) { 
1701                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
1702                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
1703                         ast_filedelete(vms->fn, NULL); 
1704                         unlink(txt); 
1705                 } 
1706         } 
1707         memset(vms->deleted, 0, sizeof(vms->deleted)); 
1708         memset(vms->heard, 0, sizeof(vms->heard)); 
1709 }
1710
1711 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
1712 {
1713         /* Introduce messages they have */
1714         int res;
1715         res = play_and_wait(chan, "vm-youhave");
1716         if (!res) {
1717                 if (vms->newmessages) {
1718                         res = say_and_wait(chan, vms->newmessages);
1719                         if (!res)
1720                                 res = play_and_wait(chan, "vm-INBOX");
1721                         if (vms->oldmessages && !res)
1722                                 res = play_and_wait(chan, "vm-and");
1723                         else if (!res) {
1724                                 if ((vms->newmessages == 1))
1725                                         res = play_and_wait(chan, "vm-message");
1726                                 else
1727                                         res = play_and_wait(chan, "vm-messages");
1728                         }
1729                                 
1730                 }
1731                 if (!res && vms->oldmessages) {
1732                         res = say_and_wait(chan, vms->oldmessages);
1733                         if (!res)
1734                                 res = play_and_wait(chan, "vm-Old");
1735                         if (!res) {
1736                                 if (vms->oldmessages == 1)
1737                                         res = play_and_wait(chan, "vm-message");
1738                                 else
1739                                         res = play_and_wait(chan, "vm-messages");
1740                         }
1741                 }
1742                 if (!res) {
1743                         if (!vms->oldmessages && !vms->newmessages) {
1744                                 res = play_and_wait(chan, "vm-no");
1745                                 if (!res)
1746                                         res = play_and_wait(chan, "vm-messages");
1747                         }
1748                 }
1749         }
1750         return res;
1751 }
1752
1753 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
1754 {
1755         int res = 0;
1756         /* Play instructions and wait for new command */
1757         while(!res) {
1758                 if (vms->starting) {
1759                         if (vms->lastmsg > -1) {
1760                                 res = play_and_wait(chan, "vm-onefor");
1761                                 if (!res)
1762                                         res = play_and_wait(chan, vms->vmbox);
1763                                 if (!res)
1764                                         res = play_and_wait(chan, "vm-messages");
1765                         }
1766                         if (!res)
1767                                 res = play_and_wait(chan, "vm-opts");
1768                 } else {
1769                         if (vms->curmsg)
1770                                 res = play_and_wait(chan, "vm-prev");
1771                         if (!res)
1772                                 res = play_and_wait(chan, "vm-repeat");
1773                         if (!res && (vms->curmsg != vms->lastmsg))
1774                                 res = play_and_wait(chan, "vm-next");
1775                         if (!res) {
1776                                 if (!vms->deleted[vms->curmsg])
1777                                         res = play_and_wait(chan, "vm-delete");
1778                                 else
1779                                         res = play_and_wait(chan, "vm-undelete");
1780                                 if (!res)
1781                                         res = play_and_wait(chan, "vm-toforward");
1782                                 if (!res)
1783                                         res = play_and_wait(chan, "vm-savemessage");
1784                         }
1785                 }
1786                 if (!res)
1787                         res = play_and_wait(chan, "vm-helpexit");
1788                 if (!res)
1789                         res = ast_waitfordigit(chan, 6000);
1790                 if (!res) {
1791                         vms->repeats++;
1792                         if (vms->repeats > 2) {
1793                                 res = play_and_wait(chan, "vm-goodbye");
1794                                 if (!res)
1795                                         res = 't';
1796                         }
1797                 }
1798         }
1799         return res;
1800 }
1801
1802 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
1803 {
1804         int cmd = 0;
1805         int retries = 0;
1806         char newpassword[80] = "";
1807         char newpassword2[80] = "";
1808         char prefile[256]="";
1809         while((cmd >= 0) && (cmd != 't')) {
1810                 if (cmd)
1811                         retries = 0;
1812                 switch (cmd) {
1813                 case '1':
1814                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
1815                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
1816                         break;
1817                 case '2': 
1818                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
1819                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
1820                         break;
1821                 case '3': 
1822                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
1823                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
1824                         break;
1825                 case '4':
1826                         newpassword[1] = '\0';
1827                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
1828                         if (cmd < 0)
1829                                 break;
1830                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
1831                                 break;
1832             }
1833                         newpassword2[1] = '\0';
1834                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
1835                         if (cmd < 0)
1836                                 break;
1837
1838                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
1839                                 break;
1840             }
1841                         if (strcmp(newpassword, newpassword2)) {
1842                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
1843                                 cmd = play_and_wait(chan, "vm-mismatch");
1844                                 break;
1845                         }
1846                         if (vm_change_password(vmu,newpassword) < 0)
1847                         {
1848                                 ast_log(LOG_DEBUG,"Failed to set new password of user %s\n",vms->username);
1849                         } else
1850                 ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
1851                         cmd = play_and_wait(chan,"vm-passchanged");
1852                         break;
1853                 case '*': 
1854                         cmd = 't';
1855                         break;
1856                 default: 
1857                         cmd = play_and_wait(chan,"vm-options");
1858                         if (!cmd)
1859                                 cmd = ast_waitfordigit(chan,6000);
1860                         if (!cmd)
1861                                 retries++;
1862                         if (retries > 3)
1863                                 cmd = 't';
1864                  }
1865         }
1866         if (cmd == 't')
1867                 cmd = 0;
1868         return cmd;
1869 }
1870
1871 static int vm_execmain(struct ast_channel *chan, void *data)
1872 {
1873         /* XXX This is, admittedly, some pretty horrendus code.  For some
1874            reason it just seemed a lot easier to do with GOTO's.  I feel
1875            like I'm back in my GWBASIC days. XXX */
1876         int res=-1;
1877         int valid = 0;
1878         int prefix = 0;
1879         int cmd=0;
1880         struct localuser *u;
1881         char prefixstr[80] ="";
1882         char empty[80] = "";
1883         int box;
1884         int useadsi = 0;
1885         int skipuser = 0;
1886         char tmp[256], *ext;
1887         char fmtc[256] = "";
1888         char password[80];
1889         struct vm_state vms;
1890         int logretries = 0;
1891         struct ast_vm_user *vmu = NULL, vmus;
1892         char *context=NULL;
1893
1894         LOCAL_USER_ADD(u);
1895         memset(&vms, 0, sizeof(vms));
1896         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
1897         if (chan->_state != AST_STATE_UP)
1898                 ast_answer(chan);
1899
1900         if (data && strlen(data)) {
1901                 strncpy(tmp, data, sizeof(tmp) - 1);
1902                 ext = tmp;
1903
1904                 switch (*ext) {
1905                         case 's':
1906                  /* We should skip the user's password */
1907                                 valid++;
1908                                 ext++;
1909                                 break;
1910                         case 'p':
1911                  /* We should prefix the mailbox with the supplied data */
1912                                 prefix++;
1913                                 ext++;
1914                                 break;
1915                 }
1916
1917                 context = strchr(ext, '@');
1918                 if (context) {
1919                         *context = '\0';
1920                         context++;
1921                 }
1922
1923                 if (prefix)
1924                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
1925                 else
1926                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
1927                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
1928                         skipuser++;
1929                 else
1930                         valid = 0;
1931
1932         }
1933
1934         /* If ADSI is supported, setup login screen */
1935         adsi_begin(chan, &useadsi);
1936         if (!skipuser && useadsi)
1937                 adsi_login(chan);
1938         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
1939                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
1940                 goto out;
1941         }
1942         
1943         /* Authenticate them and get their mailbox/password */
1944         
1945         while (!valid && (logretries < maxlogins)) {
1946                 /* Prompt for, and read in the username */
1947                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
1948                         ast_log(LOG_WARNING, "Couldn't read username\n");
1949                         goto out;
1950                 }
1951                 if (!strlen(vms.username)) {
1952                         if (option_verbose > 2)
1953                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
1954                         res = 0;
1955                         goto out;
1956                 }
1957                 if (useadsi)
1958                         adsi_password(chan);
1959                 if (ast_streamfile(chan, "vm-password", chan->language)) {
1960                         ast_log(LOG_WARNING, "Unable to stream password file\n");
1961                         goto out;
1962                 }
1963                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
1964                         ast_log(LOG_WARNING, "Unable to read password\n");
1965                         goto out;
1966                 }
1967                 if (prefix) {
1968                         char fullusername[80] = "";
1969                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
1970                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
1971                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
1972                 }
1973                 if (!skipuser) 
1974                         vmu = find_user(&vmus, context, vms.username);
1975                 if (vmu && !strcmp(vmu->password, password)) 
1976                         valid++;
1977                 else {
1978                         if (option_verbose > 2)
1979                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
1980                         if (prefix)
1981                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
1982                 }
1983                 if (!valid) {
1984                         if (useadsi)
1985                                 adsi_login(chan);
1986                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
1987                                 break;
1988                 }
1989                 logretries++;
1990         }
1991         if (!valid && (logretries >= maxlogins)) {
1992                 ast_stopstream(chan);
1993                 res = play_and_wait(chan, "vm-goodbye");
1994                 if (res > 0)
1995                         res = 0;
1996         }
1997
1998         if (valid) {
1999                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2000                 mkdir(vms.curdir, 0700);
2001                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2002                 mkdir(vms.curdir, 0700);
2003                 /* Retrieve old and new message counts */
2004                 open_mailbox(&vms, vmu, 1);
2005                 vms.oldmessages = vms.lastmsg + 1;
2006                 /* Start in INBOX */
2007                 open_mailbox(&vms, vmu, 0);
2008                 vms.newmessages = vms.lastmsg + 1;
2009                 
2010
2011                 /* Select proper mailbox FIRST!! */
2012                 if (!vms.newmessages && vms.oldmessages) {
2013                         /* If we only have old messages start here */
2014                         open_mailbox(&vms, vmu, 1);
2015                 }
2016
2017                 if (useadsi)
2018                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2019                 res = 0;
2020                 cmd = vm_intro(chan, &vms);
2021                 vms.repeats = 0;
2022                 vms.starting = 1;
2023                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2024                         /* Run main menu */
2025                         switch(cmd) {
2026                         case '1':
2027                                 vms.curmsg = 0;
2028                                 /* Fall through */
2029                         case '5':
2030                                 if (vms.lastmsg > -1) {
2031                                         cmd = play_message(chan, &vms, vms.curmsg);
2032                                 } else {
2033                                         cmd = play_and_wait(chan, "vm-youhave");
2034                                         if (!cmd) 
2035                                                 cmd = play_and_wait(chan, "vm-no");
2036                                         if (!cmd) {
2037                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2038                                                 cmd = play_and_wait(chan, vms.fn);
2039                                         }
2040                                         if (!cmd)
2041                                                 cmd = play_and_wait(chan, "vm-messages");
2042                                 }
2043                                 break;
2044                         case '2': /* Change folders */
2045                                 if (useadsi)
2046                                         adsi_folders(chan, 0, "Change to folder...");
2047                                 cmd = get_folder2(chan, "vm-changeto", 0);
2048                                 if (cmd == '#') {
2049                                         cmd = 0;
2050                                 } else if (cmd > 0) {
2051                                         cmd = cmd - '0';
2052                                         close_mailbox(&vms, vmu);
2053                                         open_mailbox(&vms, vmu, cmd);
2054                                         cmd = 0;
2055                                 }
2056                                 if (useadsi)
2057                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2058                                 if (!cmd)
2059                                         cmd = play_and_wait(chan, vms.vmbox);
2060                                 if (!cmd)
2061                                         cmd = play_and_wait(chan, "vm-messages");
2062                                 vms.starting = 1;
2063                                 break;
2064                         case '4':
2065                                 if (vms.curmsg) {
2066                                         vms.curmsg--;
2067                                         cmd = play_message(chan, &vms, vms.curmsg);
2068                                 } else {
2069                                         cmd = play_and_wait(chan, "vm-nomore");
2070                                 }
2071                                 break;
2072                         case '6':
2073                                 if (vms.curmsg < vms.lastmsg) {
2074                                         vms.curmsg++;
2075                                         cmd = play_message(chan, &vms, vms.curmsg);
2076                                 } else {
2077                                         cmd = play_and_wait(chan, "vm-nomore");
2078                                 }
2079                                 break;
2080                         case '7':
2081                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2082                                 if (useadsi)
2083                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2084                                 if (vms.deleted[vms.curmsg]) 
2085                                         cmd = play_and_wait(chan, "vm-deleted");
2086                                 else
2087                                         cmd = play_and_wait(chan, "vm-undeleted");
2088                                 break;
2089                         case '8':
2090                                 if(vms.lastmsg > -1)
2091                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2092                                 break;
2093                         case '9':
2094                                 if (useadsi)
2095                                         adsi_folders(chan, 1, "Save to folder...");
2096                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2097                                 box = 0;        /* Shut up compiler */
2098                                 if (cmd == '#') {
2099                                         cmd = 0;
2100                                         break;
2101                                 } else if (cmd > 0) {
2102                                         box = cmd = cmd - '0';
2103                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2104                                         vms.deleted[vms.curmsg]=1;
2105                                 }
2106                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2107                                 if (useadsi)
2108                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2109                                 if (!cmd)
2110                                         cmd = play_and_wait(chan, "vm-message");
2111                                 if (!cmd)
2112                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2113                                 if (!cmd)
2114                                         cmd = play_and_wait(chan, "vm-savedto");
2115                                 if (!cmd) {
2116                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2117                                         cmd = play_and_wait(chan, vms.fn);
2118                                 }
2119                                 if (!cmd)
2120                                         cmd = play_and_wait(chan, "vm-messages");
2121                                 break;
2122                         case '*':
2123                                 if (!vms.starting) {
2124                                         cmd = play_and_wait(chan, "vm-onefor");
2125                                         if (!cmd)
2126                                                 cmd = play_and_wait(chan, vms.vmbox);
2127                                         if (!cmd)
2128                                                 cmd = play_and_wait(chan, "vm-messages");
2129                                         if (!cmd)
2130                                                 cmd = play_and_wait(chan, "vm-opts");
2131                                 } else
2132                                         cmd = 0;
2133                                 break;
2134                         case '0':
2135                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2136                                 break;
2137                         default:        /* Nothing */
2138                                 cmd = vm_instructions(chan, &vms);
2139                                 break;
2140                         }
2141                 }
2142                 if ((cmd == 't') || (cmd == '#')) {
2143                         /* Timeout */
2144                         res = 0;
2145                 } else {
2146                         /* Hangup */
2147                         res = -1;
2148                 }
2149         }
2150 out:
2151         if (res > -1) {
2152                 ast_stopstream(chan);
2153                 adsi_goodbye(chan);
2154                 res = play_and_wait(chan, "vm-goodbye");
2155                 if (res > 0)
2156                         res = 0;
2157                 if (useadsi)
2158                         adsi_unload_session(chan);
2159         }
2160         if (vmu)
2161                 close_mailbox(&vms, vmu);
2162         if (vmu)
2163                 free_user(vmu);
2164         if (valid) {
2165                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2166         }
2167         LOCAL_USER_REMOVE(u);
2168         return res;
2169
2170 }
2171
2172 static int vm_exec(struct ast_channel *chan, void *data)
2173 {
2174         int res=0, silent=0, busy=0, unavail=0;
2175         struct localuser *u;
2176         char tmp[256], *ext;
2177         
2178         LOCAL_USER_ADD(u);
2179         if (chan->_state != AST_STATE_UP)
2180                 ast_answer(chan);
2181         if (data)
2182                 strncpy(tmp, data, sizeof(tmp) - 1);
2183         else {
2184                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2185                 if (res < 0)
2186                         return res;
2187                 if (!strlen(tmp))
2188                         return 0;
2189         }
2190         ext = tmp;
2191         while(*ext) {
2192                 if (*ext == 's') {
2193                         silent = 2;
2194                         ext++;
2195                 } else if (*ext == 'b') {
2196                         busy=1;
2197                         ext++;
2198                 } else if (*ext == 'u') {
2199                         unavail=1;
2200                         ext++;
2201                 } else 
2202                         break;
2203         }
2204         res = leave_voicemail(chan, ext, silent, busy, unavail);
2205         LOCAL_USER_REMOVE(u);
2206         return res;
2207 }
2208
2209 static int append_mailbox(char *context, char *mbox, char *data)
2210 {
2211         /* Assumes lock is already held */
2212         char tmp[256] = "";
2213         char *stringp;
2214         char *s;
2215         struct ast_vm_user *vmu;
2216         strncpy(tmp, data, sizeof(tmp));
2217         vmu = malloc(sizeof(struct ast_vm_user));
2218         if (vmu) {
2219                 memset(vmu, 0, sizeof(struct ast_vm_user));
2220                 strncpy(vmu->context, context, sizeof(vmu->context));
2221                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2222                 stringp = tmp;
2223                 if ((s = strsep(&stringp, ","))) 
2224                         strncpy(vmu->password, s, sizeof(vmu->password));
2225                 if ((s = strsep(&stringp, ","))) 
2226                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2227                 if ((s = strsep(&stringp, ","))) 
2228                         strncpy(vmu->email, s, sizeof(vmu->email));
2229                 if ((s = strsep(&stringp, ","))) 
2230                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2231                 vmu->next = NULL;
2232                 if (usersl)
2233                         usersl->next = vmu;
2234                 else
2235                         users = vmu;
2236                 usersl = vmu;
2237         }
2238         return 0;
2239 }
2240
2241 static int load_users(void)
2242 {
2243         struct ast_vm_user *cur, *l;
2244         struct ast_config *cfg;
2245         char *cat;
2246         struct ast_variable *var;
2247         char *astattach;
2248         char *silencestr;
2249         char *thresholdstr;
2250         char *fmt;
2251         char *astemail;
2252         char *s;
2253         int x;
2254         cfg = ast_load(VOICEMAIL_CONFIG);
2255         ast_pthread_mutex_lock(&vmlock);
2256         cur = users;
2257         while(cur) {
2258                 l = cur;
2259                 cur = cur->next;
2260                 free_user(l);
2261         }
2262         users = NULL;
2263         usersl = NULL;
2264         if (cfg) {
2265                 /* General settings */
2266                 attach_voicemail = 1;
2267                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
2268                         astattach = "yes";
2269                 attach_voicemail = ast_true(astattach);
2270                 maxsilence = 0;
2271                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2272                         maxsilence = atoi(silencestr);
2273                         if (maxsilence > 0)
2274                                 maxsilence *= 1000;
2275                 }
2276                 
2277                 silencethreshold = 256;
2278                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2279                         silencethreshold = atoi(thresholdstr);
2280                 
2281                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
2282                         astemail = ASTERISK_USERNAME;
2283                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2284                 
2285                 vmmaxmessage = 0;
2286                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2287                         if (sscanf(s, "%d", &x) == 1) {
2288                                 vmmaxmessage = x;
2289                         } else {
2290                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
2291                         }
2292                 }
2293                 fmt = ast_variable_retrieve(cfg, "general", "format");
2294                 if (!fmt)
2295                         fmt = "wav";    
2296                 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2297
2298                 skipms = 3000;
2299                 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2300                         if (sscanf(s, "%d", &x) == 1) {
2301                                 maxgreet = x;
2302                         } else {
2303                                 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2304                         }
2305                 }
2306
2307                 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2308                         if (sscanf(s, "%d", &x) == 1) {
2309                                 skipms = x;
2310                         } else {
2311                                 ast_log(LOG_WARNING, "Invalid skipms value\n");
2312                         }
2313                 }
2314
2315                 maxlogins = 3;
2316                 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2317                         if (sscanf(s, "%d", &x) == 1) {
2318                                 maxlogins = x;
2319                         } else {
2320                                 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2321                         }
2322                 }
2323
2324                 cat = ast_category_browse(cfg, NULL);
2325                 while(cat) {
2326                         if (strcasecmp(cat, "general")) {
2327                                 /* Process mailboxes in this context */
2328                                 var = ast_variable_browse(cfg, cat);
2329                                 while(var) {
2330                                         append_mailbox(cat, var->name, var->value);
2331                                         var = var->next;
2332                                 }
2333                         }
2334                         cat = ast_category_browse(cfg, cat);
2335                 }
2336                 ast_destroy(cfg);
2337         }
2338         ast_pthread_mutex_unlock(&vmlock);
2339         return 0;
2340 }
2341
2342 int reload(void)
2343 {
2344         load_users();
2345         return 0;
2346 }
2347
2348 int unload_module(void)
2349 {
2350         int res;
2351         STANDARD_HANGUP_LOCALUSERS;
2352         res = ast_unregister_application(app);
2353         res |= ast_unregister_application(app2);
2354         return res;
2355 }
2356
2357 int load_module(void)
2358 {
2359         int res;
2360         load_users();
2361         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2362         if (!res)
2363                 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2364         return res;
2365 }
2366
2367 char *description(void)
2368 {
2369         return tdesc;
2370 }
2371
2372 int usecount(void)
2373 {
2374         int res;
2375         STANDARD_USECOUNT(res);
2376         return res;
2377 }
2378
2379 char *key()
2380 {
2381         return ASTERISK_GPL_KEY;
2382 }