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