5321466406f661ed13225cb68f068fa69413b553
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- A telephony toolkit for Linux.
3  *
4  * Voicemail System (did you ever think it could be so easy?)
5  * 
6  * Copyright (C) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
9  *
10  * This program is free software, distributed under the terms of
11  * the GNU General Public License
12  */
13
14 #include <asterisk/file.h>
15 #include <asterisk/logger.h>
16 #include <asterisk/channel.h>
17 #include <asterisk/pbx.h>
18 #include <asterisk/options.h>
19 #include <asterisk/config.h>
20 #include <asterisk/say.h>
21 #include <asterisk/module.h>
22 #include <stdlib.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <time.h>
31
32 #include <pthread.h>
33 #include "../asterisk.h"
34
35 #define COMMAND_TIMEOUT 5000
36
37 #define VOICEMAIL_CONFIG "voicemail.conf"
38 #define ASTERISK_USERNAME "asterisk"
39
40 #define SENDMAIL "/usr/sbin/sendmail -t"
41
42 #define INTRO "vm-intro"
43
44 #define MAXMSG 100
45
46 #define MAX_OTHER_FORMATS 10
47
48 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
49
50
51 static char *tdesc = "Comedian Mail (Voicemail System)";
52
53 static char *synopsis_vm =
54 "Leave a voicemail message";
55
56 static char *descrip_vm =
57 "  VoiceMail([s]extension): Leaves voicemail for a given  extension (must be\n"
58 "configured in voicemail.conf). If the extension is preceeded by an 's' then\n"
59 "instructions for leaving the message will be skipped. Returns  -1 on  error\n"
60 "or mailbox not found, or if the user hangs up. Otherwise, it returns 0. \n";
61
62 static char *synopsis_vmain =
63 "Enter voicemail system";
64
65 static char *descrip_vmain =
66 "  VoiceMailMain(): Enters the main voicemail system for the checking of voicemail.  Returns\n"
67 "  -1 if the user hangs up or 0 otherwise.\n";
68
69 /* Leave a message */
70 static char *app = "VoiceMail";
71
72 /* Check mail, control, etc */
73 static char *app2 = "VoiceMailMain";
74
75 STANDARD_LOCAL_USER;
76
77 LOCAL_USER_DECL;
78
79 static int make_dir(char *dest, int len, char *ext, char *mailbox)
80 {
81         return snprintf(dest, len, "%s/%s/%s", VM_SPOOL_DIR, ext, mailbox);
82 }
83
84 static int make_file(char *dest, int len, char *dir, int num)
85 {
86         return snprintf(dest, len, "%s/msg%04d", dir, num);
87 }
88
89 #if 0
90
91 static int announce_message(struct ast_channel *chan, char *dir, int msgcnt)
92 {
93         char *fn;
94         int res;
95         res = ast_streamfile(chan, "vm-message", chan->language);
96         if (!res) {
97                 res = ast_waitstream(chan, AST_DIGIT_ANY);
98                 if (!res) {
99                         res = ast_say_number(chan, msgcnt+1, chan->language);
100                         if (!res) {
101                                 fn = get_fn(dir, msgcnt);
102                                 if (fn) {
103                                         res = ast_streamfile(chan, fn, chan->language);
104                                         free(fn);
105                                 }
106                         }
107                 }
108         }
109         if (res < 0)
110                 ast_log(LOG_WARNING, "Unable to announce message\n");
111         return res;
112 }
113 #endif
114
115 static int sendmail(char *srcemail, char *email, char *name, int msgnum, char *mailbox, char *callerid)
116 {
117         FILE *p;
118         char date[256];
119         char host[256];
120         char who[256];
121         time_t t;
122         struct tm *tm;
123         p = popen(SENDMAIL, "w");
124         if (p) {
125                 if (strchr(srcemail, '@'))
126                         strncpy(who, srcemail, sizeof(who));
127                 else {
128                         gethostname(host, sizeof(host));
129                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
130                 }
131                 time(&t);
132                 tm = localtime(&t);
133                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", tm);
134                 fprintf(p, "Date: %s\n", date);
135                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
136                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
137                 fprintf(p, "To: %s <%s>\n", name, email);
138                 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n\n", msgnum, mailbox);
139                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", tm);
140                 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a message (number %d)\n"
141                            "in mailbox %s from %s, on %s so you might\n"
142                                    "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n", name, 
143                         msgnum, mailbox, (callerid ? callerid : "an unknown caller"), date);
144                 fprintf(p, ".\n");
145                 pclose(p);
146         } else {
147                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
148                 return -1;
149         }
150         return 0;
151 }
152
153 static int get_date(char *s, int len)
154 {
155         struct tm *tm;
156         time_t t;
157         t = time(0);
158         tm = localtime(&t);
159         return strftime(s, len, "%a %b %e %r %Z %Y", tm);
160 }
161
162 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent)
163 {
164         struct ast_config *cfg;
165         char *copy, *name, *passwd, *email, *fmt, *fmts;
166         char comment[256];
167         struct ast_filestream *writer=NULL, *others[MAX_OTHER_FORMATS];
168         char *sfmt[MAX_OTHER_FORMATS];
169         char txtfile[256];
170         FILE *txt;
171         int res = -1, fmtcnt=0, x;
172         int msgnum;
173         int outmsg=0;
174         struct ast_frame *f;
175         char date[256];
176         char dir[256];
177         char fn[256];
178         char *astemail;
179         
180         cfg = ast_load(VOICEMAIL_CONFIG);
181         if (!cfg) {
182                 ast_log(LOG_WARNING, "No such configuration file %s\n", VOICEMAIL_CONFIG);
183                 return -1;
184         }
185         if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
186                 astemail = ASTERISK_USERNAME;
187         if ((copy = ast_variable_retrieve(cfg, NULL, ext))) {
188                 /* Make sure they have an entry in the config */
189                 copy = strdup(copy);
190                 passwd = strtok(copy, ",");
191                 name = strtok(NULL, ",");
192                 email = strtok(NULL, ",");
193                 make_dir(dir, sizeof(dir), ext, "");
194                 /* It's easier just to try to make it than to check for its existence */
195                 if (mkdir(dir, 0700) && (errno != EEXIST))
196                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
197                 make_dir(dir, sizeof(dir), ext, "INBOX");
198                 if (mkdir(dir, 0700) && (errno != EEXIST))
199                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
200                 /* Stream an info message */
201                 if (silent || !ast_streamfile(chan, INTRO, chan->language)) {
202                         /* Wait for the message to finish */
203                         if (silent || !ast_waitstream(chan, "")) {
204                                 fmt = ast_variable_retrieve(cfg, "general", "format");
205                                 if (fmt) {
206                                         fmts = strdup(fmt);
207                                         fmt = strtok(fmts, "|");
208                                         msgnum = 0;
209                                         do {
210                                                 make_file(fn, sizeof(fn), dir, msgnum);
211                                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
212                                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
213                                                                                         name, ext, chan->name);
214                                                 if (ast_fileexists(fn, NULL, chan->language) > 0) {
215                                                         msgnum++;
216                                                         continue;
217                                                 }
218                                                 writer = ast_writefile(fn, fmt, comment, O_EXCL, 1 /* check for other formats */, 0700);
219                                                 if (!writer)
220                                                         break;
221                                                 msgnum++;
222                                         } while(!writer && (msgnum < MAXMSG));
223                                         if (writer) {
224                                                 /* Store information */
225                                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
226                                                 txt = fopen(txtfile, "w+");
227                                                 if (txt) {
228                                                         get_date(date, sizeof(date));
229                                                         fprintf(txt, 
230 "#\n"
231 "# Message Information file\n"
232 "#\n"
233 "origmailbox=%s\n"
234 "context=%s\n"
235 "exten=%s\n"
236 "priority=%d\n"
237 "callerchan=%s\n"
238 "callerid=%s\n"
239 "origdate=%s\n",
240         ext,
241         chan->context,
242         chan->exten,
243         chan->priority,
244         chan->name,
245         chan->callerid ? chan->callerid : "Unknown",
246         date);
247                                                         fclose(txt);
248                                                 } else
249                                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
250         
251                                                 /* We need to reset these values */
252                                                 free(fmts);
253                                                 fmt = ast_variable_retrieve(cfg, "general", "format");
254                                                 fmts = strdup(fmt);
255                                                 strtok(fmts, "|");
256                                                 while((fmt = strtok(NULL, "|"))) {
257                                                         if (fmtcnt > MAX_OTHER_FORMATS - 1) {
258                                                                 ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
259                                                                 break;
260                                                         }
261                                                         sfmt[fmtcnt++] = strdup(fmt);
262                                                 }
263                                                 for (x=0;x<fmtcnt;x++) {
264                                                         others[x] = ast_writefile(fn, sfmt[x], comment, 0, 0, 0700);
265                                                         if (!others[x]) {
266                                                                 /* Ick, the other format didn't work, but be sure not
267                                                                    to leak memory here */
268                                                                 int y;
269                                                                 for(y=x+1;y < fmtcnt;y++)
270                                                                         free(sfmt[y]);
271                                                                 break;
272                                                         }
273                                                         free(sfmt[x]);
274                                                 }
275                                                 if (x == fmtcnt) {
276                                                         /* Loop forever, writing the packets we read to the writer(s), until
277                                                            we read a # or get a hangup */
278                                                         if (option_verbose > 2) 
279                                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording to %s\n", fn);
280                                                         while((f = ast_read(chan))) {
281                                                                 if (f->frametype == AST_FRAME_VOICE) {
282                                                                         /* Write the primary format */
283                                                                         res = ast_writestream(writer, f);
284                                                                         if (res) {
285                                                                                 ast_log(LOG_WARNING, "Error writing primary frame\n");
286                                                                                 break;
287                                                                         }
288                                                                         /* And each of the others */
289                                                                         for (x=0;x<fmtcnt;x++) {
290                                                                                 res |= ast_writestream(others[x], f);
291                                                                         }
292                                                                         ast_frfree(f);
293                                                                         /* Exit on any error */
294                                                                         if (res) {
295                                                                                 ast_log(LOG_WARNING, "Error writing frame\n");
296                                                                                 break;
297                                                                         }
298                                                                 }
299                                                                 if (f->frametype == AST_FRAME_DTMF) {
300                                                                         if (f->subclass == '#') {
301                                                                                 if (option_verbose > 2) 
302                                                                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
303                                                                                 outmsg=2;
304                                                                                 break;
305                                                                         }
306                                                                 }
307                                                         }
308                                                         if (!f) {
309                                                                 if (option_verbose > 2) 
310                                                                         ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
311                                                                 res = -1;
312                                                                 outmsg=1;
313                                                         }
314                                                 } else {
315                                                         ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", fn, sfmt[x]); 
316                                                         free(sfmt[x]);
317                                                 }
318                                                 ast_closestream(writer);
319                                                 for (x=0;x<fmtcnt;x++) {
320                                                         if (!others[x])
321                                                                 break;
322                                                         ast_closestream(others[x]);
323                                                 }
324                                                 if (outmsg) {
325                                                         if (outmsg > 1) {
326                                                                 /* Let them know it worked */
327                                                                 ast_streamfile(chan, "vm-msgsaved", chan->language);
328                                                                 ast_waitstream(chan, "");
329                                                         }
330                                                         /* Send e-mail if applicable */
331                                                         if (email) 
332                                                                 sendmail(astemail, email, name, msgnum, ext, chan->callerid);
333                                                 }
334                                         } else {
335                                                 if (msgnum < MAXMSG)
336                                                         ast_log(LOG_WARNING, "Error writing to mailbox %s\n", ext);
337                                                 else
338                                                         ast_log(LOG_WARNING, "Too many messages in mailbox %s\n", ext);
339                                         }
340                                         free(fmts);
341                                 } else 
342                                         ast_log(LOG_WARNING, "No format to save messages in \n");
343                         }
344                 } else
345                         ast_log(LOG_WARNING, "Unable to playback instructions\n");
346                         
347                 free(copy);
348         } else
349                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
350         ast_destroy(cfg);
351         /* Leave voicemail for someone */
352         return res;
353 }
354
355 static char *mbox(int id)
356 {
357         switch(id) {
358         case 0:
359                 return "INBOX";
360         case 1:
361                 return "Old";
362         case 2:
363                 return "Work";
364         case 3:
365                 return "Family";
366         case 4:
367                 return "Friends";
368         case 5:
369                 return "Cust1";
370         case 6:
371                 return "Cust2";
372         case 7:
373                 return "Cust3";
374         case 8:
375                 return "Cust4";
376         case 9:
377                 return "Cust5";
378         default:
379                 return "Unknown";
380         }
381 }
382
383 static int count_messages(char *dir)
384 {
385         int x;
386         char fn[256];
387         for (x=0;x<MAXMSG;x++) {
388                 make_file(fn, sizeof(fn), dir, x);
389                 if (ast_fileexists(fn, NULL, NULL) < 1)
390                         break;
391         }
392         return x;
393 }
394
395 static int play_and_wait(struct ast_channel *chan, char *fn)
396 {
397         int d;
398         d = ast_streamfile(chan, fn, chan->language);
399         if (d)
400                 return d;
401         d = ast_waitstream(chan, AST_DIGIT_ANY);
402         return d;
403 }
404
405 static int say_and_wait(struct ast_channel *chan, int num)
406 {
407         int d;
408         d = ast_say_number(chan, num, chan->language);
409         return d;
410 }
411
412 static int copy(char *infile, char *outfile)
413 {
414         int ifd;
415         int ofd;
416         int res;
417         int len;
418         char buf[4096];
419         if ((ifd = open(infile, O_RDONLY)) < 0) {
420                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
421                 return -1;
422         }
423         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
424                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
425                 close(ifd);
426                 return -1;
427         }
428         do {
429                 len = read(ifd, buf, sizeof(buf));
430                 if (len < 0) {
431                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
432                         close(ifd);
433                         close(ofd);
434                         unlink(outfile);
435                 }
436                 if (len) {
437                         res = write(ofd, buf, len);
438                         if (res != len) {
439                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
440                                 close(ifd);
441                                 close(ofd);
442                                 unlink(outfile);
443                         }
444                 }
445         } while(len);
446         close(ifd);
447         close(ofd);
448         return 0;
449 }
450
451 static int save_to_folder(char *dir, int msg, char *username, int box)
452 {
453         char sfn[256];
454         char dfn[256];
455         char ddir[256];
456         char txt[256];
457         char ntxt[256];
458         char *dbox = mbox(box);
459         int x;
460         make_file(sfn, sizeof(sfn), dir, msg);
461         make_dir(ddir, sizeof(ddir), username, dbox);
462         mkdir(ddir, 0700);
463         for (x=0;x<MAXMSG;x++) {
464                 make_file(dfn, sizeof(dfn), ddir, x);
465                 if (ast_fileexists(dfn, NULL, NULL) < 0)
466                         break;
467         }
468         if (x >= MAXMSG)
469                 return -1;
470         ast_filecopy(sfn, dfn, NULL);
471         if (strcmp(sfn, dfn)) {
472                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
473                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
474                 copy(txt, ntxt);
475         }
476         return 0;
477 }
478
479 static int get_folder(struct ast_channel *chan, int start)
480 {
481         int x;
482         int d;
483         char fn[256];
484         d = play_and_wait(chan, "vm-press");
485         if (d)
486                 return d;
487         for (x = start; x< 5; x++) {
488                 if ((d = ast_say_number(chan, x, chan->language)))
489                         return d;
490                 d = play_and_wait(chan, "vm-for");
491                 if (d)
492                         return d;
493                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
494                 d = play_and_wait(chan, fn);
495                 if (d)
496                         return d;
497                 d = play_and_wait(chan, "vm-messages");
498                 if (d)
499                         return d;
500                 d = ast_waitfordigit(chan, 500);
501                 if (d)
502                         return d;
503         }
504         d = play_and_wait(chan, "vm-tocancel");
505         if (d)
506                 return d;
507         d = ast_waitfordigit(chan, 4000);
508         return d;
509 }
510
511 #define WAITCMD(a) do { \
512         d = (a); \
513         if (d < 0) \
514                 goto out; \
515         if (d) \
516                 goto cmd; \
517 } while(0)
518
519 #define WAITFILE2(file) do { \
520         if (ast_streamfile(chan, file, chan->language)) \
521                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
522         d = ast_waitstream(chan, AST_DIGIT_ANY); \
523         if (d < 0) { \
524                 goto out; \
525         }\
526 } while(0)
527
528 #define WAITFILE(file) do { \
529         if (ast_streamfile(chan, file, chan->language)) \
530                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); \
531         d = ast_waitstream(chan, AST_DIGIT_ANY); \
532         if (!d) { \
533                 repeats = 0; \
534                 goto instructions; \
535         } else if (d < 0) { \
536                 goto out; \
537         } else goto cmd;\
538 } while(0)
539
540 #define PLAYMSG(a) do { \
541         starting = 0; \
542         if (!a) \
543                 WAITFILE2("vm-first"); \
544         else if (a == lastmsg) \
545                 WAITFILE2("vm-last"); \
546         WAITFILE2("vm-message"); \
547         if (a && (a != lastmsg)) { \
548                 d = ast_say_number(chan, a + 1, chan->language); \
549                 if (d < 0) goto out; \
550                 if (d) goto cmd; \
551         } \
552         make_file(fn, sizeof(fn), curdir, a); \
553         heard[a] = 1; \
554         WAITFILE(fn); \
555 } while(0)
556
557 #define CLOSE_MAILBOX do { \
558         if (lastmsg > -1) { \
559                 /* Get the deleted messages fixed */ \
560                 curmsg = -1; \
561                 for (x=0;x<=lastmsg;x++) { \
562                         if (!deleted[x] && (strcasecmp(curbox, "INBOX") || !heard[x])) { \
563                                 /* Save this message.  It's not in INBOX or hasn't been heard */ \
564                                 curmsg++; \
565                                 make_file(fn, sizeof(fn), curdir, x); \
566                                 make_file(fn2, sizeof(fn2), curdir, curmsg); \
567                                 if (strcmp(fn, fn2)) { \
568                                         snprintf(txt, sizeof(txt), "%s.txt", fn); \
569                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", fn2); \
570                                         ast_filerename(fn, fn2, NULL); \
571                                         rename(txt, ntxt); \
572                                 } \
573                         } else if (!strcasecmp(curbox, "INBOX") && heard[x] && !deleted[x]) { \
574                                 /* Move to old folder before deleting */ \
575                                 save_to_folder(curdir, x, username, 1); \
576                         } \
577                 } \
578                 for (x = curmsg + 1; x<=lastmsg; x++) { \
579                         make_file(fn, sizeof(fn), curdir, x); \
580                         snprintf(txt, sizeof(txt), "%s.txt", fn); \
581                         ast_filedelete(fn, NULL); \
582                         unlink(txt); \
583                 } \
584         } \
585         memset(deleted, 0, sizeof(deleted)); \
586         memset(heard, 0, sizeof(heard)); \
587 } while(0)
588
589 #define OPEN_MAILBOX(a) do { \
590         strcpy(curbox, mbox(a)); \
591         make_dir(curdir, sizeof(curdir), username, curbox); \
592         lastmsg = count_messages(curdir) - 1; \
593         snprintf(vmbox, sizeof(vmbox), "vm-%s", curbox); \
594 } while (0)
595
596 static int vm_execmain(struct ast_channel *chan, void *data)
597 {
598         /* XXX This is, admittedly, some pretty horrendus code.  For some
599            reason it just seemed a lot easier to do with GOTO's.  I feel
600            like I'm back in my GWBASIC days. XXX */
601         int res=-1;
602         int valid = 0;
603         char d;
604         struct localuser *u;
605         char username[80];
606         char password[80], *copy;
607         char curbox[80];
608         char curdir[256];
609         char vmbox[256];
610         char fn[256];
611         char fn2[256];
612         int x;
613         char ntxt[256];
614         char txt[256];
615         int deleted[MAXMSG] = { 0, };
616         int heard[MAXMSG] = { 0, };
617         int newmessages;
618         int oldmessages;
619         int repeats = 0;
620         int curmsg = 0;
621         int lastmsg = 0;
622         int starting = 1;
623         int box;
624         struct ast_config *cfg;
625         
626         LOCAL_USER_ADD(u);
627         cfg = ast_load(VOICEMAIL_CONFIG);
628         if (!cfg) {
629                 ast_log(LOG_WARNING, "No voicemail configuration\n");
630                 goto out;
631         }
632         if (chan->state != AST_STATE_UP)
633                 ast_answer(chan);
634         if (ast_streamfile(chan, "vm-login", chan->language)) {
635                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
636                 goto out;
637         }
638         
639         /* Authenticate them and get their mailbox/password */
640         
641         do {
642                 /* Prompt for, and read in the username */
643                 if (ast_readstring(chan, username, sizeof(username), 2000, 10000, "#") < 0) {
644                         ast_log(LOG_WARNING, "Couldn't read username\n");
645                         goto out;
646                 }                       
647                 if (!strlen(username)) {
648                         if (option_verbose > 2)
649                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
650                         res = 0;
651                         goto out;
652                 }
653                 if (ast_streamfile(chan, "vm-password", chan->language)) {
654                         ast_log(LOG_WARNING, "Unable to stream password file\n");
655                         goto out;
656                 }
657                 if (ast_readstring(chan, password, sizeof(password), 2000, 10000, "#") < 0) {
658                         ast_log(LOG_WARNING, "Unable to read password\n");
659                         goto out;
660                 }
661                 copy = ast_variable_retrieve(cfg, NULL, username);
662                 if (copy) {
663                         copy = strdup(copy);
664                         strtok(copy, ",");
665                         if (!strcmp(password,copy))
666                                 valid++;
667                         else if (option_verbose > 2)
668                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s'\n", password, username);
669                         free(copy);
670                 } else if (option_verbose > 2)
671                         ast_verbose( VERBOSE_PREFIX_3 "No such user '%s' in config file\n", username);
672                 if (!valid) {
673                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
674                                 break;
675                         if (ast_waitstream(chan, ""))
676                                 break;
677                 }
678         } while (!valid);
679
680         if (valid) {
681                 OPEN_MAILBOX(1);
682                 oldmessages = lastmsg + 1;
683                 /* Start in INBOX */
684                 OPEN_MAILBOX(0);
685                 newmessages = lastmsg + 1;
686                 
687                 WAITCMD(play_and_wait(chan, "vm-youhave"));
688                 if (newmessages) {
689                         WAITCMD(say_and_wait(chan, newmessages));
690                         WAITCMD(play_and_wait(chan, "vm-INBOX"));
691                         if (newmessages == 1)
692                                 WAITCMD(play_and_wait(chan, "vm-message"));
693                         else
694                                 WAITCMD(play_and_wait(chan, "vm-messages"));
695                                 
696                         if (oldmessages)
697                                 WAITCMD(play_and_wait(chan, "vm-and"));
698                 }
699                 if (oldmessages) {
700                         WAITCMD(say_and_wait(chan, oldmessages));
701                         WAITCMD(play_and_wait(chan, "vm-Old"));
702                         if (oldmessages == 1)
703                                 WAITCMD(play_and_wait(chan, "vm-message"));
704                         else
705                                 WAITCMD(play_and_wait(chan, "vm-messages"));
706                 }
707                 if (!oldmessages && !newmessages) {
708                         WAITCMD(play_and_wait(chan, "vm-no"));
709                         WAITCMD(play_and_wait(chan, "vm-messages"));
710                 }
711                 if (!newmessages && oldmessages) {
712                         /* If we only have old messages start here */
713                         OPEN_MAILBOX(1);
714                 }
715                 repeats = 0;
716                 starting = 1;
717 instructions:
718                 if (starting) {
719                         if (lastmsg > -1) {
720                                 WAITCMD(play_and_wait(chan, "vm-onefor"));
721                                 WAITCMD(play_and_wait(chan, vmbox));
722                                 WAITCMD(play_and_wait(chan, "vm-messages"));
723                         }
724                         WAITCMD(play_and_wait(chan, "vm-opts"));
725                 } else {
726                         if (curmsg)
727                                 WAITCMD(play_and_wait(chan, "vm-prev"));
728                         WAITCMD(play_and_wait(chan, "vm-repeat"));
729                         if (curmsg != lastmsg)
730                                 WAITCMD(play_and_wait(chan, "vm-next"));
731                         if (!deleted[curmsg])
732                                 WAITCMD(play_and_wait(chan, "vm-delete"));
733                         else
734                                 WAITCMD(play_and_wait(chan, "vm-undelete"));
735                         WAITCMD(play_and_wait(chan, "vm-toforward"));
736                         WAITCMD(play_and_wait(chan, "vm-savemessage"));
737                 }
738                 WAITCMD(play_and_wait(chan, "vm-helpexit"));
739                 d = ast_waitfordigit(chan, 6000);
740                 if (d < 0)
741                         goto out;
742                 if (!d) {
743                         repeats++;
744                         if (repeats > 2) {
745                                 play_and_wait(chan, "vm-goodbye");
746                                 goto out;
747                         }
748                         goto instructions;
749                 }
750 cmd:
751                 switch(d) {
752                 case '2':
753                         box = play_and_wait(chan, "vm-changeto");
754                         if (box < 0)
755                                 goto out;
756                         while((box < '0') || (box > '9')) {
757                                 box = get_folder(chan, 0);
758                                 if (box < 0)
759                                         goto out;
760                                 if (box == '#')
761                                         goto instructions;
762                         } 
763                         box = box - '0';
764                         CLOSE_MAILBOX;
765                         OPEN_MAILBOX(box);
766                         WAITCMD(play_and_wait(chan, vmbox));
767                         WAITCMD(play_and_wait(chan, "vm-messages"));
768                         starting = 1;
769                         goto instructions;
770                 case '4':
771                         if (curmsg) {
772                                 curmsg--;
773                                 PLAYMSG(curmsg);
774                         } else {
775                                 WAITCMD(play_and_wait(chan, "vm-nomore"));
776                                 goto instructions;
777                         }
778                 case '1':
779                                 curmsg = 0;
780                                 /* Fall through */
781                 case '5':
782                         if (lastmsg > -1) {
783                                 PLAYMSG(curmsg);
784                         } else {
785                                 WAITCMD(play_and_wait(chan, "vm-youhave"));
786                                 WAITCMD(play_and_wait(chan, "vm-no"));
787                                 snprintf(fn, sizeof(fn), "vm-%s", curbox);
788                                 WAITCMD(play_and_wait(chan, fn));
789                                 WAITCMD(play_and_wait(chan, "vm-messages"));
790                                 goto instructions;
791                         }
792                 case '6':
793                         if (curmsg < lastmsg) {
794                                 curmsg++;
795                                 PLAYMSG(curmsg);
796                         } else {
797                                 WAITCMD(play_and_wait(chan, "vm-nomore"));
798                                 goto instructions;
799                         }
800                 case '7':
801                         deleted[curmsg] = !deleted[curmsg];
802                         if (deleted[curmsg]) 
803                                 WAITCMD(play_and_wait(chan, "vm-deleted"));
804                         else
805                                 WAITCMD(play_and_wait(chan, "vm-undeleted"));
806                         goto instructions;
807                 case '9':
808                         box = play_and_wait(chan, "vm-savefolder");
809                         if (box < 0)
810                                 goto out;
811                         while((box < '1') || (box > '9')) {
812                                 box = get_folder(chan, 1);
813                                 if (box < 0)
814                                         goto out;
815                                 if (box == '#')
816                                         goto instructions;
817                         } 
818                         box = box - '0';
819                         ast_log(LOG_DEBUG, "Save to folder: %s (%d)\n", mbox(box), box);
820                         if (save_to_folder(curdir, curmsg, username, box))
821                                 goto out;
822                         deleted[curmsg]=1;
823                         WAITCMD(play_and_wait(chan, "vm-message"));
824                         WAITCMD(say_and_wait(chan, curmsg + 1) );
825                         WAITCMD(play_and_wait(chan, "vm-savedto"));
826                         snprintf(fn, sizeof(fn), "vm-%s", mbox(box));
827                         WAITCMD(play_and_wait(chan, fn));
828                         WAITCMD(play_and_wait(chan, "vm-messages"));
829                         goto instructions;
830                 case '*':
831                         if (!starting) {
832                                 WAITCMD(play_and_wait(chan, "vm-onefor"));
833                                 WAITCMD(play_and_wait(chan, vmbox));
834                                 WAITCMD(play_and_wait(chan, "vm-messages"));
835                                 WAITCMD(play_and_wait(chan, "vm-opts"));
836                         }
837                         goto instructions;
838                 case '#':
839                         play_and_wait(chan, "vm-goodbye");
840                         goto out;
841                 default:
842                         goto instructions;
843                 }
844         }
845 out:
846         CLOSE_MAILBOX;
847         ast_stopstream(chan);
848         if (cfg)
849                 ast_destroy(cfg);
850         LOCAL_USER_REMOVE(u);
851         return res;
852 }
853
854 static int vm_exec(struct ast_channel *chan, void *data)
855 {
856         int res=0, silent=0;
857         struct localuser *u;
858         char *ext = (char *)data;
859         
860         if (!data) {
861                 ast_log(LOG_WARNING, "vm requires an argument (extension)\n");
862                 return -1;
863         }
864         LOCAL_USER_ADD(u);
865         if (*ext == 's') {
866                 silent++;
867                 ext++;
868         }
869         if (chan->state != AST_STATE_UP)
870                 ast_answer(chan);
871         res = leave_voicemail(chan, ext, silent);
872         LOCAL_USER_REMOVE(u);
873         return res;
874 }
875
876 int unload_module(void)
877 {
878         int res;
879         STANDARD_HANGUP_LOCALUSERS;
880         res = ast_unregister_application(app);
881         res |= ast_unregister_application(app2);
882         return res;
883 }
884
885 int load_module(void)
886 {
887         int res;
888         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
889         if (!res)
890                 res = ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
891         return res;
892 }
893
894 char *description(void)
895 {
896         return tdesc;
897 }
898
899 int usecount(void)
900 {
901         int res;
902         STANDARD_USECOUNT(res);
903         return res;
904 }
905
906 char *key()
907 {
908         return ASTERISK_GPL_KEY;
909 }