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