cc2efe6e22f0b8abc06f570e1579bb881ad3cb50
[asterisk/asterisk.git] / apps / app_voicemail.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/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/options.h>
19 #include <asterisk/config.h>
20 #include <asterisk/say.h>
21 #include <asterisk/module.h>
22 #include <asterisk/adsi.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <sys/time.h>
30 #include <sys/stat.h>
31 #include <time.h>
32
33 #include <pthread.h>
34 #include "../asterisk.h"
35
36 #define COMMAND_TIMEOUT 5000
37
38 #define VOICEMAIL_CONFIG "voicemail.conf"
39 #define ASTERISK_USERNAME "asterisk"
40
41 #define SENDMAIL "/usr/sbin/sendmail -t"
42
43 #define INTRO "vm-intro"
44
45 #define MAXMSG 100
46
47 #define MAX_OTHER_FORMATS 10
48
49 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
50
51 #define BASEMAXINLINE 256
52
53 #define BASELINELEN 72
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58
59 #define BASEMAXINLINE 256
60 #define BASELINELEN 72
61 #define eol "\r\n"
62
63 int iocp;
64 int iolen;
65 int linelength;
66 int ateof;
67 unsigned char iobuf[BASEMAXINLINE];
68
69 static char *tdesc = "Comedian Mail (Voicemail System)";
70
71 static char *adapp = "CoMa";
72
73 static char *adsec = "_AST";
74
75 static char *addesc = "Comedian Mail";
76
77 static int adver = 1;
78
79 static char *synopsis_vm =
80 "Leave a voicemail message";
81
82 static char *descrip_vm =
83 "  VoiceMail([s|u|b]extension): Leaves voicemail for a given  extension (must\n"
84 "be configured in voicemail.conf). If the extension is preceeded by an 's'"
85 "then instructions for leaving the message will be skipped.  If the extension\n"
86 "is preceeded by 'u' then the \"unavailable\" message will be played (that is, \n"
87 "/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.  If the extension\n"
88 "is preceeded by a 'b' then the the busy message will be played (that is,\n"
89 "busy instead of unavail).  At most one of 's', 'u', or 'b' may be specified.\n"
90 "Returns  -1 on  error or mailbox not found, or if the user hangs up. \n"
91 "Otherwise, it returns 0. \n";
92
93 static char *synopsis_vmain =
94 "Enter voicemail system";
95
96 static char *descrip_vmain =
97 "  VoiceMailMain(): Enters the main voicemail system for the checking of voicemail.  Returns\n"
98 "  -1 if the user hangs up or 0 otherwise.\n";
99
100 /* Leave a message */
101 static char *app = "VoiceMail";
102
103 /* Check mail, control, etc */
104 static char *app2 = "VoiceMailMain";
105
106 STANDARD_LOCAL_USER;
107
108 LOCAL_USER_DECL;
109
110 static int make_dir(char *dest, int len, char *ext, char *mailbox)
111 {
112         return snprintf(dest, len, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
113 }
114
115 static int make_file(char *dest, int len, char *dir, int num)
116 {
117         return snprintf(dest, len, "%s/msg%04d", dir, num);
118 }
119
120 #if 0
121
122 static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
123 {
124         char *fn;
125         int res;
126
127         res = ast_streamfile(chan, "vm-message", chan->language);
128         if (!res) {
129                 res = ast_waitstream(chan, AST_DIGIT_ANY);
130                 if (!res) {
131                         res = ast_say_number(chan, msgcnt+1, chan->language);
132                         if (!res) {
133                                 fn = get_fn(dir, msgcnt);
134                                 if (fn) {
135                                         res = ast_streamfile(chan, fn, chan->language);
136                                         free(fn);
137                                 }
138                         }
139                 }
140         }
141         if (res < 0)
142                 ast_log(LOG_WARNING, "Unable to announce message\n");
143         return res;
144 }
145 #endif
146
147 static int
148 inbuf(FILE *fi)
149 {
150         int l;
151
152         if(ateof)
153                 return 0;
154
155         if ( (l = fread(iobuf,1,BASEMAXINLINE,fi)) <= 0) {
156                 if(ferror(fi))
157                         return -1;
158
159                 ateof = 1;
160                 return 0;
161         }
162
163         iolen= l;
164         iocp= 0;
165
166         return 1;
167 }
168
169 static int 
170 inchar(FILE *fi)
171 {
172         if(iocp>=iolen)
173                 if(!inbuf(fi))
174                         return EOF;
175
176         return iobuf[iocp++];
177 }
178
179 static int
180 ochar(int c, FILE *so)
181 {
182         if(linelength>=BASELINELEN) {
183                 if(fputs(eol,so)==EOF)
184                         return -1;
185
186                 linelength= 0;
187         }
188
189         if(putc(((unsigned char)c),so)==EOF)
190                 return -1;
191
192         linelength++;
193
194         return 1;
195 }
196
197 static int base_encode(char *filename, FILE *so)
198 {
199         unsigned char dtable[BASEMAXINLINE];
200         int i,hiteof= 0;
201         FILE *fi;
202
203         linelength = 0;
204         iocp = BASEMAXINLINE;
205         iolen = 0;
206         ateof = 0;
207
208         if ( !(fi = fopen(filename, "rb"))) {
209                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
210                 return -1;
211         }
212
213         for(i= 0;i<9;i++){
214                 dtable[i]= 'A'+i;
215                 dtable[i+9]= 'J'+i;
216                 dtable[26+i]= 'a'+i;
217                 dtable[26+i+9]= 'j'+i;
218         }
219         for(i= 0;i<8;i++){
220                 dtable[i+18]= 'S'+i;
221                 dtable[26+i+18]= 's'+i;
222         }
223         for(i= 0;i<10;i++){
224                 dtable[52+i]= '0'+i;
225         }
226         dtable[62]= '+';
227         dtable[63]= '/';
228
229         while(!hiteof){
230                 unsigned char igroup[3],ogroup[4];
231                 int c,n;
232
233                 igroup[0]= igroup[1]= igroup[2]= 0;
234
235                 for(n= 0;n<3;n++){
236                         if ( (c = inchar(fi)) == EOF) {
237                                 hiteof= 1;
238                                 break;
239                         }
240
241                         igroup[n]= (unsigned char)c;
242                 }
243
244                 if(n> 0){
245                         ogroup[0]= dtable[igroup[0]>>2];
246                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
247                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
248                         ogroup[3]= dtable[igroup[2]&0x3F];
249
250                         if(n<3) {
251                                 ogroup[3]= '=';
252
253                                 if(n<2)
254                                         ogroup[2]= '=';
255                         }
256
257                         for(i= 0;i<4;i++)
258                                 ochar(ogroup[i], so);
259                 }
260         }
261
262         if(fputs(eol,so)==EOF)
263                 return 0;
264
265         fclose(fi);
266
267         return 1;
268 }
269
270 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid, char *attach, char *format)
271 {
272         FILE *p;
273         char date[256];
274         char host[256];
275         char who[256];
276         char bound[256];
277         char fname[256];
278         time_t t;
279         struct tm *tm;
280         p = popen(SENDMAIL, "w");
281
282         if (p) {
283                 if (strchr(srcemail, '@'))
284                         strncpy(who, srcemail, sizeof(who)-1);
285                 else {
286                         gethostname(host, sizeof(host));
287                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
288                 }
289                 time(&t);
290                 tm = localtime(&t);
291                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
292                 fprintf(p, "Date: %s\n", date);
293                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
294                 fprintf(p, "To: %s <%s>\n", name, email);
295                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum, mailbox);
296                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
297                 fprintf(p, "MIME-Version: 1.0\n");
298
299                 // Something unique.
300                 snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
301
302                 fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
303
304                 fprintf(p, "--%s\n", bound);
305                 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
306                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
307                 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a message (number %d)\n"
308
309                            "in mailbox %s from %s, on %s so you might\n"
310                                    "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", name, 
311                         msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
312
313                 fprintf(p, "--%s\n", bound);
314                 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII; name=\"msg%04d\"\n", msgnum);
315                 fprintf(p, "Content-Transfer-Encoding: BASE64\n");
316                 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
317                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
318
319                 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
320                 base_encode(fname, p);
321                 fprintf(p, "\n\n--%s--\n.\n", bound);
322                 pclose(p);
323         } else {
324                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
325                 return -1;
326         }
327         return 0;
328 }
329
330 static int get_date(char *s, int len)
331 {
332         struct tm *tm;
333         time_t t;
334         t = time(0);
335         tm = localtime(&t);
336         return strftime(s, len, "%a %b %e %r %Z %Y", tm);
337 }
338
339 static int invent_message(struct ast_channel *chan, char *ext, int busy)
340 {
341         int res;
342         res = ast_streamfile(chan, "vm-theperson", chan->language);
343         if (res)
344                 return -1;
345         res = ast_waitstream(chan, "#");
346         if (res)
347                 return res;
348         res = ast_say_digit_str(chan, ext, "#", chan->language);
349         if (res)
350                 return res;
351         if (busy)
352                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
353         else
354                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
355         if (res)
356                 return -1;
357         res = ast_waitstream(chan, "#");
358         return res;
359 }
360
361 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
362 {
363         struct ast_config *cfg;
364         char *copy, *name, *passwd, *email, *fmt, *fmts;
365         char comment[256];
366         struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
367         char *sfmt[MAX_OTHER_FORMATS];
368         char txtfile[256];
369         FILE *txt;
370         int res = -1, fmtcnt=0, x;
371         int msgnum;
372         int outmsg=0;
373         int wavother=0;
374         struct ast_frame *f;
375         char date[256];
376         char dir[256];
377         char fn[256];
378         char prefile[256]="";
379         char *astemail;
380
381         cfg = ast_load(VOICEMAIL_CONFIG);
382         if (!cfg) {
383                 ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
384                 return -1;
385         }
386         if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
387                 astemail = ASTERISK_USERNAME;
388         if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
389                 /* Setup pre-file if appropriate */
390                 if (busy)
391                         snprintf(prefile, sizeof(prefile), "vm/%s/busy", ext);
392                 else if (unavail)
393                         snprintf(prefile, sizeof(prefile), "vm/%s/unavail", ext);
394                 /* Make sure they have an entry in the config */
395                 copy = strdup(copy);
396                 passwd = strtok(copy, ",");
397                 name = strtok(NULL, ",");
398                 email = strtok(NULL, ",");
399                 make_dir(dir, sizeof(dir), ext, "");
400                 /* It's easier just to try to make it than to check for its existence */
401                 if (mkdir(dir, 0700) && (errno != EEXIST))
402                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
403                 make_dir(dir, sizeof(dir), ext, "INBOX");
404                 if (mkdir(dir, 0700) && (errno != EEXIST))
405                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
406                 /* Play the beginning intro if desired */
407                 if (strlen(prefile)) {
408                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
409                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
410                                     silent = ast_waitstream(chan, "#");
411                         } else {
412                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
413                                 silent = invent_message(chan, ext, busy);
414                         }
415                         if (silent < 0) {
416                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
417                                 free(copy);
418                                 return -1;
419                         }
420                 }
421                 /* If they hit "#" we should still play the beep sound */
422                 if (silent == '#') {
423                         if (!ast_streamfile(chan, "beep", chan->language) < 0)
424                                 silent = 1;
425                         ast_waitstream(chan, "");
426                 }
427                 /* Stream an info message */
428                 if (silent || !ast_streamfile(chan, INTRO, chan->language)) {
429                         /* Wait for the message to finish */
430                         if (silent || !ast_waitstream(chan, "")) {
431                                 fmt = ast_variable_retrieve(cfg, "general", "format");
432                                 if (fmt) {
433                                         fmts = strdup(fmt);
434                                         fmt = strtok(fmts, "|");
435                                         msgnum = 0;
436                                         do {
437                                                 make_file(fn, sizeof(fn), dir, msgnum);
438                                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
439                                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
440                                                                                         name, ext, chan->name);
441                                                 if (ast_fileexists(fn, NULL, chan->language) > 0) {
442                                                         msgnum++;
443                                                         continue;
444                                                 }
445                                                 writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
446                                                 if (!writer)
447                                                         break;
448                                                 msgnum++;
449                                         } while(!writer && (msgnum < MAXMSG));
450                                         if (writer) {
451                                                 /* Store information */
452                                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
453                                                 txt = fopen(txtfile, "w+");
454                                                 if (txt) {
455                                                         get_date(date, sizeof(date));
456                                                         fprintf(txt, 
457 "#\n"
458 "# Message Information file\n"
459 "#\n"
460 "origmailbox=%s\n"
461 "context=%s\n"
462 "exten=%s\n"
463 "priority=%d\n"
464 "callerchan=%s\n"
465 "callerid=%s\n"
466 "origdate=%s\n",
467         ext,
468         chan->context,
469         chan->exten,
470         chan->priority,
471         chan->name,
472         chan->callerid ? chan->callerid : "Unknown",
473         date);
474                                                         fclose(txt);
475                                                 } else
476                                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
477         
478                                                 /* We need to reset these values */
479                                                 free(fmts);
480                                                 fmt = ast_variable_retrieve(cfg, "general", "format");
481                                                 fmts = strdup(fmt);
482                                                 strtok(fmts, "|");
483                                                 while((fmt = strtok(NULL, "|"))) {
484                                                         if (fmtcnt > MAX_OTHER_FORMATS - 1) {
485                                                                 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
486                                                                 break;
487                                                         }
488                                                         sfmt[fmtcnt++] = strdup(fmt);
489                                                 }
490                                                 for (x=0;x<fmtcnt;x++) {
491                                                         others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700);
492                                                         if (!others[x]) {
493                                                                 /* Ick, the other format didn't work, but be sure not
494                                                                    to leak memory here */
495                                                                 int y;
496                                                                 for(y=x+1;y < fmtcnt;y++)
497                                                                         free(sfmt[y]);
498                                                                 break;
499                                                         }
500                                                         if(!strcasecmp(sfmt[x], "wav"))
501                                                                 wavother++;
502                                                         free(sfmt[x]);
503                                                 }
504                                                 if (x == fmtcnt) {
505                                                         /* Loop forever, writing the packets we read to the writer(s), until
506                                                            we read a # or get a hangup */
507                                                         if (option_verbose > 2) 
508                                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn);
509                                                         f = NULL;
510                                                         for(;;) {
511                                                                 res = ast_waitfor(chan, 2000);
512                                                                 if (!res) {
513                                                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
514                                                                         res = -1;
515                                                                 }
516                                                                 
517                                                                 if (res < 0) {
518                                                                         f = NULL;
519                                                                         break;
520                                                                 }
521
522                                                                 f = ast_read(chan);
523                                                                 if (!f)
524                                                                         break;
525                                                                 if (f->frametype == AST_FRAME_VOICE) {
526                                                                         /* Write the primary format */
527                                                                         res = ast_writestream(writer, f);
528                                                                         if (res) {
529                                                                                 ast_log(LOG_WARNING, "Error writing primary frame\n");
530                                                                                 break;
531                                                                         }
532                                                                         /* And each of the others */
533                                                                         for (x=0;x<fmtcnt;x++) {
534                                                                                 res |= ast_writestream(others[x], f);
535                                                                         }
536                                                                         ast_frfree(f);
537                                                                         /* Exit on any error */
538                                                                         if (res) {
539                                                                                 ast_log(LOG_WARNING, "Error writing frame\n");
540                                                                                 break;
541                                                                         }
542                                                                 }
543                                                                 if (f->frametype == AST_FRAME_DTMF) {
544                                                                         if (f->subclass == '#') {
545                                                                                 if (option_verbose > 2) 
546                                                                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
547                                                                                 outmsg=2;
548                                                                                 break;
549                                                                         }
550                                                                 }
551                                                         }
552                                                         if (!f) {
553                                                                 if (option_verbose > 2) 
554                                                                         ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
555                                                                 res = -1;
556                                                                 outmsg=1;
557                                                         }
558                                                 } else {
559                                                         ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", fn, sfmt[x]); 
560                                                         free(sfmt[x]);
561                                                 }
562
563                                                 ast_closestream(writer);
564                                                 for (x=0;x<fmtcnt;x++) {
565                                                         if (!others[x])
566                                                                 break;
567                                                         ast_closestream(others[x]);
568                                                 }
569                                                 if (outmsg) {
570                                                         if (outmsg > 1) {
571                                                                 /* Let them know it worked */
572                                                                 ast_streamfile(chan, "vm-msgsaved", chan->language);
573                                                                 ast_waitstream(chan, "");
574                                                         }
575                                                         /* Send e-mail if applicable */
576                                                                 sendmail(astemail, email, name, msgnum, ext, chan->callerid, fn, wavother ? "wav" : fmts);
577                                                 }
578                                         } else {
579                                                 if (msgnum < MAXMSG)
580                                                         ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext);
581                                                 else
582                                                         ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
583                                         }
584                                         free(fmts);
585                                 } else 
586                                         ast_log(LOG_WARNING, "No format to save messages in \n");
587                         }
588                 } else
589                         ast_log(LOG_WARNING, "Unable to playback instructions\n");
590                         
591                 free(copy);
592         } else
593                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
594         ast_destroy(cfg);
595         /* Leave voicemail for someone */
596         return res;
597 }
598
599 static char *mbox(int id)
600 {
601         switch(id) {
602         case 0:
603                 return "INBOX";
604         case 1:
605                 return "Old";
606         case 2:
607                 return "Work";
608         case 3:
609                 return "Family";
610         case 4:
611                 return "Friends";
612         case 5:
613                 return "Cust1";
614         case 6:
615                 return "Cust2";
616         case 7:
617                 return "Cust3";
618         case 8:
619                 return "Cust4";
620         case 9:
621                 return "Cust5";
622         default:
623                 return "Unknown";
624         }
625 }
626
627 static int count_messages(char *dir)
628 {
629         int x;
630         char fn[256];
631         for (x=0;x<MAXMSG;x++) {
632                 make_file(fn, sizeof(fn), dir, x);
633                 if (ast_fileexists(fn, NULL, NULL) < 1)
634                         break;
635         }
636         return x;
637 }
638
639 static int play_and_wait(struct ast_channel *chan, char *fn)
640 {
641         int d;
642         d = ast_streamfile(chan, fn, chan->language);
643         if (d)
644                 return d;
645         d = ast_waitstream(chan, AST_DIGIT_ANY);
646         return d;
647 }
648
649 static int say_and_wait(struct ast_channel *chan, int num)
650 {
651         int d;
652         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
653         return d;
654 }
655
656 static int copy(char *infile, char *outfile)
657 {
658         int ifd;
659         int ofd;
660         int res;
661         int len;
662         char buf[4096];
663         if ((ifd = open(infile, O_RDONLY)) < 0) {
664                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
665                 return -1;
666         }
667         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
668                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
669                 close(ifd);
670                 return -1;
671         }
672         do {
673                 len = read(ifd, buf, sizeof(buf));
674                 if (len < 0) {
675                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
676                         close(ifd);
677                         close(ofd);
678                         unlink(outfile);
679                 }
680                 if (len) {
681                         res = write(ofd, buf, len);
682                         if (res != len) {
683                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
684                                 close(ifd);
685                                 close(ofd);
686                                 unlink(outfile);
687                         }
688                 }
689         } while(len);
690         close(ifd);
691         close(ofd);
692         return 0;
693 }
694
695 static int save_to_folder(char *dir, int msg, char *username, int box)
696 {
697         char sfn[256];
698         char dfn[256];
699         char ddir[256];
700         char txt[256];
701         char ntxt[256];
702         char *dbox = mbox(box);
703         int x;
704         make_file(sfn, sizeof(sfn), dir, msg);
705         make_dir(ddir, sizeof(ddir), username, dbox);
706         mkdir(ddir, 0700);
707         for (x=0;x<MAXMSG;x++) {
708                 make_file(dfn, sizeof(dfn), ddir, x);
709                 if (ast_fileexists(dfn, NULL, NULL) < 0)
710                         break;
711         }
712         if (x >= MAXMSG)
713                 return -1;
714         ast_filecopy(sfn, dfn, NULL);
715         if (strcmp(sfn, dfn)) {
716                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
717                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
718                 copy(txt, ntxt);
719         }
720         return 0;
721 }
722
723 static int adsi_logo(unsigned char *buf)
724 {
725         int bytes = 0;
726         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
727         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
728         return bytes;
729 }
730
731 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
732 {
733         char buf[256];
734         int bytes=0;
735         int x;
736         char num[5];
737
738         *useadsi = 0;
739         bytes += adsi_data_mode(buf + bytes);
740         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
741
742         bytes = 0;
743         bytes += adsi_logo(buf);
744         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
745 #ifdef DISPLAY
746         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
747 #endif
748         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
749         bytes += adsi_data_mode(buf + bytes);
750         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
751
752         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
753                 bytes = 0;
754                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
755                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
756                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
757                 bytes += adsi_voice_mode(buf + bytes, 0);
758                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
759                 return 0;
760         }
761
762 #ifdef DISPLAY
763         /* Add a dot */
764         bytes = 0;
765         bytes += adsi_logo(buf);
766         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
767         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
768         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
769         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
770 #endif
771         bytes = 0;
772         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
773         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
774         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
775         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "4", 1);
776         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
777         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
778         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
779
780 #ifdef DISPLAY
781         /* Add another dot */
782         bytes = 0;
783         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
784         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
785         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
786 #endif
787
788         bytes = 0;
789         /* These buttons we load but don't use yet */
790         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
791         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
792         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
793         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
794         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
795         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
796         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
797
798 #ifdef DISPLAY
799         /* Add another dot */
800         bytes = 0;
801         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
802         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
803         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
804 #endif
805
806         bytes = 0;
807         for (x=0;x<5;x++) {
808                 snprintf(num, sizeof(num), "%d", x);
809                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
810         }
811         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
812         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
813
814 #ifdef DISPLAY
815         /* Add another dot */
816         bytes = 0;
817         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
818         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
819         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
820 #endif
821
822         if (adsi_end_download(chan)) {
823                 bytes = 0;
824                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
825                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
826                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
827                 bytes += adsi_voice_mode(buf + bytes, 0);
828                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
829                 return 0;
830         }
831         bytes = 0;
832         bytes += adsi_download_disconnect(buf + bytes);
833         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
834
835         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
836
837 #ifdef DISPLAY
838         /* Add last dot */
839         bytes = 0;
840         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
841         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
842 #endif
843         ast_log(LOG_DEBUG, "Restarting session...\n");
844
845         bytes = 0;
846         /* Load the session now */
847         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
848                 *useadsi = 1;
849                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
850         } else
851                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
852
853         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
854         return 0;
855 }
856
857 static void adsi_begin(struct ast_channel *chan, int *useadsi)
858 {
859         int x;
860         x = adsi_load_session(chan, adapp, adver, 1);
861         if (x < 0)
862                 return;
863         if (!x) {
864                 if (adsi_load_vmail(chan, useadsi)) {
865                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
866                         return;
867                 }
868         } else
869                 *useadsi = 1;
870 }
871
872 static void adsi_login(struct ast_channel *chan)
873 {
874         char buf[256];
875         int bytes=0;
876         unsigned char keys[6];
877         int x;
878         if (!adsi_available(chan))
879                 return;
880
881         for (x=0;x<6;x++)
882                 keys[x] = 0;
883         /* Set one key for next */
884         keys[3] = ADSI_KEY_APPS + 3;
885
886         bytes += adsi_logo(buf + bytes);
887         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
888         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
889         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
890         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
891         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
892         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
893         bytes += adsi_set_keys(buf + bytes, keys);
894         bytes += adsi_voice_mode(buf + bytes, 0);
895         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
896 }
897
898 static void adsi_password(struct ast_channel *chan)
899 {
900         char buf[256];
901         int bytes=0;
902         unsigned char keys[6];
903         int x;
904         if (!adsi_available(chan))
905                 return;
906
907         for (x=0;x<6;x++)
908                 keys[x] = 0;
909         /* Set one key for next */
910         keys[3] = ADSI_KEY_APPS + 3;
911
912         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
913         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
914         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
915         bytes += adsi_set_keys(buf + bytes, keys);
916         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
917 }
918
919 static void adsi_folders(struct ast_channel *chan, int start, char *label)
920 {
921         char buf[256];
922         int bytes=0;
923         unsigned char keys[6];
924         int x,y;
925
926         if (!adsi_available(chan))
927                 return;
928
929         for (x=0;x<5;x++) {
930                 y = ADSI_KEY_APPS + 12 + start + x;
931                 if (y > ADSI_KEY_APPS + 12 + 4)
932                         y = 0;
933                 keys[x] = ADSI_KEY_SKT | y;
934         }
935         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
936
937         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
938         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
939         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
940         bytes += adsi_set_keys(buf + bytes, keys);
941         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
942 }
943
944 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
945 {
946         int bytes=0;
947         char buf[256], buf1[256], buf2[256];
948         char fn2[256];
949         char cid[256]="";
950         char *val;
951         char *name, *num;
952         char datetime[21]="";
953         FILE *f;
954
955         unsigned char keys[6];
956
957         int x;
958
959         if (!adsi_available(chan))
960                 return;
961
962         /* Retrieve important info */
963         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
964         f = fopen(fn2, "r");
965         if (f) {
966                 while(!feof(f)) {       
967                         fgets(buf, sizeof(buf), f);
968                         if (!feof(f)) {
969                                 strtok(buf, "=");
970                                 val = strtok(NULL, "=");
971                                 if (val && strlen(val)) {
972                                         if (!strcmp(buf, "callerid"))
973                                                 strncpy(cid, val, sizeof(cid) - 1);
974                                         if (!strcmp(buf, "origdate"))
975                                                 strncpy(datetime, val, sizeof(datetime) - 1);
976                                 }
977                         }
978                 }
979                 fclose(f);
980         }
981         /* New meaning for keys */
982         for (x=0;x<5;x++)
983                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
984
985         if (!msg) {
986                 /* No prev key, provide "Folder" instead */
987                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
988         }
989         if (msg >= last) {
990                 /* If last message ... */
991                 if (msg) {
992                         /* but not only message, provide "Folder" instead */
993                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
994                 } else {
995                         /* Otherwise if only message, leave blank */
996                         keys[3] = 1;
997                 }
998         }
999
1000         if (strlen(cid)) {
1001                 ast_callerid_parse(cid, &name, &num);
1002                 if (!name)
1003                         name = num;
1004         } else
1005                 name = "Unknown Caller";
1006
1007         /* If deleted, show "undeleted" */
1008         if (deleted)
1009                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1010
1011         /* Except "Exit" */
1012         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1013         snprintf(buf1, sizeof(buf1), "%s%s", folder,
1014                  strcasecmp(folder, "INBOX") ? " Messages" : "");
1015         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1016
1017         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1018         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1019         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1020         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1021         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1022         bytes += adsi_set_keys(buf + bytes, keys);
1023         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1024 }
1025
1026 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1027 {
1028         int bytes=0;
1029         char buf[256];
1030         unsigned char keys[6];
1031
1032         int x;
1033
1034         if (!adsi_available(chan))
1035                 return;
1036
1037         /* New meaning for keys */
1038         for (x=0;x<5;x++)
1039                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1040
1041         if (!msg) {
1042                 /* No prev key, provide "Folder" instead */
1043                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1044         }
1045         if (msg >= last) {
1046                 /* If last message ... */
1047                 if (msg) {
1048                         /* but not only message, provide "Folder" instead */
1049                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1050                 } else {
1051                         /* Otherwise if only message, leave blank */
1052                         keys[3] = 1;
1053                 }
1054         }
1055
1056         /* If deleted, show "undeleted" */
1057         if (deleted) 
1058                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1059
1060         /* Except "Exit" */
1061         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1062         bytes += adsi_set_keys(buf + bytes, keys);
1063         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1064 }
1065
1066 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1067 {
1068         char buf[256], buf1[256], buf2[256];
1069         int bytes=0;
1070         unsigned char keys[6];
1071         int x;
1072
1073         char *newm = (new == 1) ? "message" : "messages";
1074         char *oldm = (old == 1) ? "message" : "messages";
1075         if (!adsi_available(chan))
1076                 return;
1077         if (new) {
1078                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1079                 if (old) {
1080                         strcat(buf1, " and");
1081                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1082                 } else {
1083                         snprintf(buf2, sizeof(buf2), "%s.", newm);
1084                 }
1085         } else if (old) {
1086                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1087                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1088         } else {
1089                 strcpy(buf1, "You have no messages.");
1090                 strcpy(buf2, " ");
1091         }
1092         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1093         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1094         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1095
1096         for (x=0;x<6;x++)
1097                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1098
1099         /* Don't let them listen if there are none */
1100         if (lastmsg < 0)
1101                 keys[0] = 1;
1102         bytes += adsi_set_keys(buf + bytes, keys);
1103
1104         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1105 }
1106
1107 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1108 {
1109         char buf[256], buf1[256], buf2[256];
1110         int bytes=0;
1111         unsigned char keys[6];
1112         int x;
1113
1114         char *mess = (messages == 1) ? "message" : "messages";
1115
1116         if (!adsi_available(chan))
1117                 return;
1118
1119         /* Original command keys */
1120         for (x=0;x<6;x++)
1121                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1122
1123         if (messages < 1)
1124                 keys[0] = 0;
1125
1126         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1127                         strcasecmp(folder, "INBOX") ? " folder" : "");
1128
1129         if (messages)
1130                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1131         else
1132                 strcpy(buf2, "no messages.");
1133         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1134         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1135         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1136         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1137         bytes += adsi_set_keys(buf + bytes, keys);
1138
1139         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1140         
1141 }
1142
1143 static void adsi_clear(struct ast_channel *chan)
1144 {
1145         char buf[256];
1146         int bytes=0;
1147         if (!adsi_available(chan))
1148                 return;
1149         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1150         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1151 }
1152
1153 static void adsi_goodbye(struct ast_channel *chan)
1154 {
1155         char buf[256];
1156         int bytes=0;
1157         if (!adsi_available(chan))
1158                 return;
1159         bytes += adsi_logo(buf + bytes);
1160         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1161         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1162         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1163         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1164 }
1165
1166 static int get_folder(struct ast_channel *chan, int start)
1167 {
1168         int x;
1169         int d;
1170         char fn[256];
1171         d = play_and_wait(chan, "vm-press");
1172         if (d)
1173                 return d;
1174         for (x = start; x< 5; x++) {
1175                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1176                         return d;
1177                 d = play_and_wait(chan, "vm-for");
1178                 if (d)
1179                         return d;
1180                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1181                 d = play_and_wait(chan, fn);
1182                 if (d)
1183                         return d;
1184                 d = play_and_wait(chan, "vm-messages");
1185                 if (d)
1186                         return d;
1187                 d = ast_waitfordigit(chan, 500);
1188                 if (d)
1189                         return d;
1190         }
1191         d = play_and_wait(chan, "vm-tocancel");
1192         if (d)
1193                 return d;
1194         d = ast_waitfordigit(chan, 4000);
1195         return d;
1196 }
1197
1198 static int
1199 forward_message(struct ast_channel *chan, struct ast_config *cfg, char *dir, int curmsg)
1200 {
1201         char username[70];
1202         char sys[256];
1203         char todir[256];
1204         int todircount=0;
1205
1206         while(1) {
1207                 ast_streamfile(chan, "vm-extension", chan->language);
1208
1209                 if (ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0)
1210                         return 0;
1211                 if (ast_variable_retrieve(cfg, NULL, username)) {
1212                         printf("Got %d\n", atoi(username));
1213                         if (play_and_wait(chan, "vm-savedto"))
1214                                 break;
1215
1216                         snprintf(todir, sizeof(todir), "%s/%s/INBOX", VM_SPOOL_DIR, username);
1217                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1218                         puts(sys);
1219                         system(sys);
1220
1221                         todircount = count_messages(todir);
1222
1223                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.gsm %s/msg%04d.gsm\n", dir, curmsg, todir, todircount);
1224                         puts(sys);
1225                         system(sys);
1226
1227                         break;
1228                 } else {
1229                         if ( play_and_wait(chan, "pbx-invalid"))
1230                                 break;
1231                 }
1232         }
1233
1234         return 0;
1235 }
1236
1237 #define WAITCMD(a) do { \
1238         d = (a); \
1239         if (d < 0) \
1240                 goto out; \
1241         if (d) \
1242                 goto cmd; \
1243 } while(0)
1244
1245 #define WAITFILE2(file) do { \
1246         if (ast_streamfile(chan, file, chan->language)) \
1247                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
1248         d = ast_waitstream(chan, AST_DIGIT_ANY); \
1249         if (d < 0) { \
1250                 goto out; \
1251         }\
1252 } while(0)
1253
1254 #define WAITFILE(file) do { \
1255         if (ast_streamfile(chan, file, chan->language)) \
1256                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
1257         d = ast_waitstream(chan, AST_DIGIT_ANY); \
1258         if (!d) { \
1259                 repeats = 0; \
1260                 goto instructions; \
1261         } else if (d < 0) { \
1262                 goto out; \
1263         } else goto cmd;\
1264 } while(0)
1265
1266 #define PLAYMSG(a) do { \
1267         starting = 0; \
1268         make_file(fn, sizeof(fn), curdir, a); \
1269         adsi_message(chan, curbox, a, lastmsg, deleted[a], fn); \
1270         if (!a) \
1271                 WAITFILE2("vm-first"); \
1272         else if (a == lastmsg) \
1273                 WAITFILE2("vm-last"); \
1274         WAITFILE2("vm-message"); \
1275         if (a && (a != lastmsg)) { \
1276                 d = ast_say_number(chan, a + 1, AST_DIGIT_ANY, chan->language); \
1277                 if (d < 0) goto out; \
1278                 if (d) goto cmd; \
1279         } \
1280         make_file(fn, sizeof(fn), curdir, a); \
1281         heard[a] = 1; \
1282         WAITFILE(fn); \
1283 } while(0)
1284
1285 #define CLOSE_MAILBOX do { \
1286         if (lastmsg > -1) { \
1287                 /* Get the deleted messages fixed */ \
1288                 curmsg = -1; \
1289                 for (x=0;x<=lastmsg;x++) { \
1290                         if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \
1291                                 /* Save this message.  It's not in INBOX or hasn't been heard */ \
1292                                 curmsg++; \
1293                                 make_file(fn, sizeof(fn), curdir, x); \
1294                                 make_file(fn2, sizeof(fn2), curdir, curmsg); \
1295                                 if (strcmp(fn, fn2)) { \
1296                                         snprintf(txt, sizeof(txt), "%s.txt", fn); \
1297                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \
1298                                         ast_filerename(fn, fn2, NULL); \
1299                                         rename(txt, ntxt); \
1300                                 } \
1301                         } else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \
1302                                 /* Move to old folder before deleting */ \
1303                                 save_to_folder(curdir, x, username, 1); \
1304                         } \
1305                 } \
1306                 for (x = curmsg + 1; x<=lastmsg; x++) { \
1307                         make_file(fn, sizeof(fn), curdir, x); \
1308                         snprintf(txt, sizeof(txt), "%s.txt", fn); \
1309                         ast_filedelete(fn, NULL); \
1310                         unlink(txt); \
1311                 } \
1312         } \
1313         memset(deleted, 0, sizeof(deleted)); \
1314         memset(heard, 0, sizeof(heard)); \
1315 } while(0)
1316
1317 #define OPEN_MAILBOX(a) do { \
1318         strcpy(curbox, mbox(a)); \
1319         make_dir(curdir, sizeof(curdir), username, curbox); \
1320         lastmsg = count_messages(curdir) - 1; \
1321         snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \
1322 } while (0)
1323
1324
1325 static int vm_execmain(struct ast_channel *chan, void *data)
1326 {
1327         /* XXX This is, admittedly, some pretty horrendus code.  For some
1328            reason it just seemed a lot easier to do with GOTO's.  I feel
1329            like I'm back in my GWBASIC days. XXX */
1330         int res=-1;
1331         int valid = 0;
1332         char d;
1333         struct localuser *u;
1334         char username[80];
1335         char password[80], *copy;
1336         char curbox[80];
1337         char curdir[256];
1338         char vmbox[256];
1339         char fn[256];
1340         char fn2[256];
1341         int x;
1342         char ntxt[256];
1343         char txt[256];
1344         int deleted[MAXMSG] = { 0, };
1345         int heard[MAXMSG] = { 0, };
1346         int newmessages;
1347         int oldmessages;
1348         int repeats = 0;
1349         int curmsg = 0;
1350         int lastmsg = 0;
1351         int starting = 1;
1352         int box;
1353         int useadsi = 0;
1354         struct ast_config *cfg;
1355
1356         LOCAL_USER_ADD(u);
1357         cfg = ast_load(VOICEMAIL_CONFIG);
1358         if (!cfg) {
1359                 ast_log(LOG_WARNING, "No voicemail configuration\n");
1360                 goto out;
1361         }
1362         if (chan->state != AST_STATE_UP)
1363                 ast_answer(chan);
1364
1365         /* If ADSI is supported, setup login screen */
1366         adsi_begin(chan, &useadsi);
1367         if (useadsi)
1368                 adsi_login(chan);
1369         if (ast_streamfile(chan, "vm-login", chan->language)) {
1370                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
1371                 goto out;
1372         }
1373         
1374         /* Authenticate them and get their mailbox/password */
1375         
1376         do {
1377                 /* Prompt for, and read in the username */
1378                 if (ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0) {
1379                         ast_log(LOG_WARNING, "Couldn't read username\n");
1380                         goto out;
1381                 }                       
1382                 if (!strlen(username)) {
1383                         if (option_verbose > 2)
1384                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
1385                         res = 0;
1386                         goto out;
1387                 }
1388                 if (useadsi)
1389                         adsi_password(chan);
1390                 if (ast_streamfile(chan, "vm-password", chan->language)) {
1391                         ast_log(LOG_WARNING, "Unable to stream password file\n");
1392                         goto out;
1393                 }
1394                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
1395                         ast_log(LOG_WARNING, "Unable to read password\n");
1396                         goto out;
1397                 }
1398                 copy = ast_variable_retrieve(cfg, NULL, username);
1399                 if (copy) {
1400                         copy = strdup(copy);
1401                         strtok(copy, ",");
1402                         if (!strcmp(password,copy))
1403                                 valid++;
1404                         else if (option_verbose > 2)
1405                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", password, username);
1406                         free(copy);
1407                 } else if (option_verbose > 2)
1408                         ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", username);
1409                 if (!valid) {
1410                         if (useadsi)
1411                                 adsi_login(chan);
1412                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
1413                                 break;
1414 #if 0
1415                         if (ast_waitstream(chan, ""))
1416                                 break;
1417 #endif
1418                 }
1419         } while (!valid);
1420
1421         if (valid) {
1422                 OPEN_MAILBOX(1);
1423                 oldmessages = lastmsg + 1;
1424                 /* Start in INBOX */
1425                 OPEN_MAILBOX(0);
1426                 newmessages = lastmsg + 1;
1427                 
1428
1429                 /* Select proper mailbox FIRST!! */
1430                 if (!newmessages && oldmessages) {
1431                         /* If we only have old messages start here */
1432                         OPEN_MAILBOX(1);
1433                 }
1434
1435                 if (useadsi)
1436                         adsi_status(chan, newmessages, oldmessages, lastmsg);
1437
1438                 WAITCMD(play_and_wait(chan, "vm-youhave"));
1439                 if (newmessages) {
1440                         WAITCMD(say_and_wait(chan, newmessages));
1441                         WAITCMD(play_and_wait(chan, "vm-INBOX"));
1442
1443                         if (oldmessages)
1444                                 WAITCMD(play_and_wait(chan, "vm-and"));
1445                         else {
1446                                 if (newmessages == 1)
1447                                         WAITCMD(play_and_wait(chan, "vm-message"));
1448                                 else
1449                                         WAITCMD(play_and_wait(chan, "vm-messages"));
1450                         }
1451                                 
1452                 }
1453                 if (oldmessages) {
1454                         WAITCMD(say_and_wait(chan, oldmessages));
1455                         WAITCMD(play_and_wait(chan, "vm-Old"));
1456                         if (oldmessages == 1)
1457                                 WAITCMD(play_and_wait(chan, "vm-message"));
1458                         else
1459                                 WAITCMD(play_and_wait(chan, "vm-messages"));
1460                 }
1461                 if (!oldmessages && !newmessages) {
1462                         WAITCMD(play_and_wait(chan, "vm-no"));
1463                         WAITCMD(play_and_wait(chan, "vm-messages"));
1464                 }
1465                 repeats = 0;
1466                 starting = 1;
1467 instructions:
1468                 if (starting) {
1469                         if (lastmsg > -1) {
1470                                 WAITCMD(play_and_wait(chan, "vm-onefor"));
1471                                 WAITCMD(play_and_wait(chan, vmbox));
1472                                 WAITCMD(play_and_wait(chan, "vm-messages"));
1473                         }
1474                         WAITCMD(play_and_wait(chan, "vm-opts"));
1475                 } else {
1476                         if (curmsg)
1477                                 WAITCMD(play_and_wait(chan, "vm-prev"));
1478                         WAITCMD(play_and_wait(chan, "vm-repeat"));
1479                         if (curmsg != lastmsg)
1480                                 WAITCMD(play_and_wait(chan, "vm-next"));
1481                         if (!deleted[curmsg])
1482                                 WAITCMD(play_and_wait(chan, "vm-delete"));
1483                         else
1484                                 WAITCMD(play_and_wait(chan, "vm-undelete"));
1485                         WAITCMD(play_and_wait(chan, "vm-toforward"));
1486                         WAITCMD(play_and_wait(chan, "vm-savemessage"));
1487                 }
1488                 WAITCMD(play_and_wait(chan, "vm-helpexit"));
1489                 d = ast_waitfordigit(chan, 6000);
1490                 if (d < 0)
1491                         goto out;
1492                 if (!d) {
1493                         repeats++;
1494                         if (repeats > 2) {
1495                                 play_and_wait(chan, "vm-goodbye");
1496                                 goto out;
1497                         }
1498                         goto instructions;
1499                 }
1500 cmd:
1501                 switch(d) {
1502                 case '2':
1503                         if (useadsi)
1504                                 adsi_folders(chan, 0, "Change to folder...");
1505                         box = play_and_wait(chan, "vm-changeto");
1506                         if (box < 0)
1507                                 goto out;
1508                         while((box < '0') || (box > '9')) {
1509                                 box = get_folder(chan, 0);
1510                                 if (box < 0)
1511                                         goto out;
1512                                 if (box == '#')
1513                                         goto instructions;
1514                         } 
1515                         box = box - '0';
1516                         CLOSE_MAILBOX;
1517                         OPEN_MAILBOX(box);
1518                         if (useadsi)
1519                                 adsi_status2(chan, curbox, lastmsg + 1);
1520                         WAITCMD(play_and_wait(chan, vmbox));
1521                         WAITCMD(play_and_wait(chan, "vm-messages"));
1522                         starting = 1;
1523                         goto instructions;
1524                 case '4':
1525                         if (curmsg) {
1526                                 curmsg--;
1527                                 PLAYMSG(curmsg);
1528                         } else {
1529                                 WAITCMD(play_and_wait(chan, "vm-nomore"));
1530                                 goto instructions;
1531                         }
1532                 case '1':
1533                                 curmsg = 0;
1534                                 /* Fall through */
1535                 case '5':
1536                         if (lastmsg > -1) {
1537                                 PLAYMSG(curmsg);
1538                         } else {
1539                                 WAITCMD(play_and_wait(chan, "vm-youhave"));
1540                                 WAITCMD(play_and_wait(chan, "vm-no"));
1541                                 snprintf(fn, sizeof(fn), "vm-%s", curbox);
1542                                 WAITCMD(play_and_wait(chan, fn));
1543                                 WAITCMD(play_and_wait(chan, "vm-messages"));
1544                                 goto instructions;
1545                         }
1546                 case '6':
1547                         if (curmsg < lastmsg) {
1548                                 curmsg++;
1549                                 PLAYMSG(curmsg);
1550                         } else {
1551                                 WAITCMD(play_and_wait(chan, "vm-nomore"));
1552                                 goto instructions;
1553                         }
1554                 case '7':
1555                         deleted[curmsg] = !deleted[curmsg];
1556                         if (useadsi)
1557                                 adsi_delete(chan, curmsg, lastmsg, deleted[curmsg]);
1558                         if (deleted[curmsg]) 
1559                                 WAITCMD(play_and_wait(chan, "vm-deleted"));
1560                         else
1561                                 WAITCMD(play_and_wait(chan, "vm-undeleted"));
1562                         goto instructions;
1563                 case '8':
1564                         if(lastmsg > -1)
1565                                 if(forward_message(chan, cfg, curdir, curmsg) < 0)
1566                                         goto out;
1567                         goto instructions;
1568                 case '9':
1569                         if (useadsi)
1570                                 adsi_folders(chan, 1, "Save to folder...");
1571                         box = play_and_wait(chan, "vm-savefolder");
1572                         if (box < 0)
1573                                 goto out;
1574                         while((box < '1') || (box > '9')) {
1575                                 box = get_folder(chan, 1);
1576                                 if (box < 0)
1577                                         goto out;
1578                                 if (box == '#')
1579                                         goto instructions;
1580                         } 
1581                         box = box - '0';
1582                         if (option_debug)
1583                                 ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box);
1584                         if (save_to_folder(curdir, curmsg, username, box))
1585                                 goto out;
1586                         deleted[curmsg]=1;
1587                         make_file(fn, sizeof(fn), curdir, curmsg);
1588                         if (useadsi)
1589                                 adsi_message(chan, curbox, curmsg, lastmsg, deleted[curmsg], fn);
1590                         WAITCMD(play_and_wait(chan, "vm-message"));
1591                         WAITCMD(say_and_wait(chan, curmsg + 1) );
1592                         WAITCMD(play_and_wait(chan, "vm-savedto"));
1593                         snprintf(fn, sizeof(fn), "vm-%s", mbox(box));
1594                         WAITCMD(play_and_wait(chan, fn));
1595                         WAITCMD(play_and_wait(chan, "vm-messages"));
1596                         goto instructions;
1597                 case '*':
1598                         if (!starting) {
1599                                 WAITCMD(play_and_wait(chan, "vm-onefor"));
1600                                 WAITCMD(play_and_wait(chan, vmbox));
1601                                 WAITCMD(play_and_wait(chan, "vm-messages"));
1602                                 WAITCMD(play_and_wait(chan, "vm-opts"));
1603                         }
1604                         goto instructions;
1605                 case '#':
1606                         ast_stopstream(chan);
1607                         adsi_goodbye(chan);
1608                         play_and_wait(chan, "vm-goodbye");
1609                         res = 0;
1610                         goto out2;
1611                 default:
1612                         goto instructions;
1613                 }
1614         }
1615 out:
1616         adsi_goodbye(chan);
1617 out2:
1618         CLOSE_MAILBOX;
1619         ast_stopstream(chan);
1620         if (cfg)
1621                 ast_destroy(cfg);
1622         if (useadsi)
1623                 adsi_unload_session(chan);
1624         adsi_channel_init(chan);
1625         LOCAL_USER_REMOVE(u);
1626         return res;
1627 }
1628
1629 static int vm_exec(struct ast_channel *chan, void *data)
1630 {
1631         int res=0, silent=0, busy=0, unavail=0;
1632         struct localuser *u;
1633         char *ext = (char *)data;
1634         
1635
1636         if (!data) {
1637                 ast_log(LOG_WARNING, "vm requires an argument (extension)\n");
1638                 return -1;
1639         }
1640         LOCAL_USER_ADD(u);
1641         if (*ext == 's') {
1642                 silent++;
1643                 ext++;
1644         } else if (*ext == 'b') {
1645                 busy++;
1646                 ext++;
1647         } else if (*ext == 'u') {
1648                 unavail++;
1649                 ext++;
1650         }
1651         if (chan->state != AST_STATE_UP)
1652                 ast_answer(chan);
1653         res = leave_voicemail(chan, ext, silent, busy, unavail);
1654         LOCAL_USER_REMOVE(u);
1655         return res;
1656 }
1657
1658 int unload_module(void)
1659 {
1660         int res;
1661         STANDARD_HANGUP_LOCALUSERS;
1662         res = ast_unregister_application(app);
1663         res |= ast_unregister_application(app2);
1664         return res;
1665 }
1666
1667 int load_module(void)
1668 {
1669         int res;
1670         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
1671         if (!res)
1672                 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
1673         return res;
1674 }
1675
1676 char *description(void)
1677 {
1678         return tdesc;
1679 }
1680
1681 int usecount(void)
1682 {
1683         int res;
1684         STANDARD_USECOUNT(res);
1685         return res;
1686 }
1687
1688 char *key()
1689 {
1690         return ASTERISK_GPL_KEY;
1691 }