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