c5db096e9f8e0aa37c6546fabe0b13f6da04b4b4
[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) 2003, Digium Inc.
7  *
8  * Mark Spencer <markster@digium.com>
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/channel_pvt.h>
19 #include <asterisk/pbx.h>
20 #include <asterisk/options.h>
21 #include <asterisk/config.h>
22 #include <asterisk/say.h>
23 #include <asterisk/module.h>
24 #include <asterisk/adsi.h>
25 #include <asterisk/app.h>
26 #include <asterisk/manager.h>
27 #include <asterisk/dsp.h>
28 #include <asterisk/localtime.h>
29 #include <asterisk/cli.h>
30 #include <stdlib.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <sys/time.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 #include <time.h>
40 #include <dirent.h>
41
42 /* we define USESQLVM when we have MySQL or POSTGRES */
43 #ifdef USEMYSQLVM
44 #include <mysql/mysql.h>
45 #define USESQLVM 1
46 #endif
47
48 #ifdef USEPOSTGRESVM
49 /*
50  * PostgreSQL routines written by Otmar Lendl <lendl@nic.at>
51  */
52 #include <postgresql/libpq-fe.h>
53 #define USESQLVM 1
54 #endif
55
56 #ifndef USESQLVM
57 static inline int sql_init(void) { return 0; }
58 static inline void sql_close(void) { }
59 #endif
60
61 #include <pthread.h>
62 #include "../asterisk.h"
63 #include "../astconf.h"
64
65 #define COMMAND_TIMEOUT 5000
66
67 #define VOICEMAIL_CONFIG "voicemail.conf"
68 #define ASTERISK_USERNAME "asterisk"
69
70 /* Default mail command to mail voicemail. Change it with the
71     mailcmd= command in voicemail.conf */
72 #define SENDMAIL "/usr/sbin/sendmail -t"
73
74 #define INTRO "vm-intro"
75
76 #define MAXMSG 100
77 #define MAX_OTHER_FORMATS 10
78
79 #define VM_SPOOL_DIR AST_SPOOL_DIR "/vm"
80
81 #define BASEMAXINLINE 256
82 #define BASELINELEN 72
83 #define BASEMAXINLINE 256
84 #define eol "\r\n"
85
86 #define MAX_DATETIME_FORMAT     512
87 #define DIGITS_DIR      AST_SOUNDS "/digits/"
88 struct baseio {
89         int iocp;
90         int iolen;
91         int linelength;
92         int ateof;
93         unsigned char iobuf[BASEMAXINLINE];
94 };
95
96 /* Structure for linked list of users */
97 struct ast_vm_user {
98         char context[80];
99         char mailbox[80];
100         char password[80];
101         char fullname[80];
102         char email[80];
103         char pager[80];
104         char serveremail[80];
105         char mailcmd[160];      /* Configurable mail command */
106         char zonetag[80];
107         int attach;
108         int alloced;
109         struct ast_vm_user *next;
110 };
111
112 struct vm_zone {
113         char name[80];
114         char timezone[80];
115         char msg_format[512];
116         struct vm_zone *next;
117 };
118
119 struct vm_state {
120         char curbox[80];
121         char username[80];
122         char curdir[256];
123         char vmbox[256];
124         char fn[256];
125         char fn2[256];
126         int deleted[MAXMSG];
127         int heard[MAXMSG];
128         int curmsg;
129         int lastmsg;
130         int newmessages;
131         int oldmessages;
132         int starting;
133         int repeats;
134 };
135
136 static char *tdesc = "Comedian Mail (Voicemail System)";
137
138 static char *adapp = "CoMa";
139
140 static char *adsec = "_AST";
141
142 static char *addesc = "Comedian Mail";
143
144 static int adver = 1;
145
146 static char *synopsis_vm =
147 "Leave a voicemail message";
148
149 static char *descrip_vm =
150 "  VoiceMail([s|u|b]extension[@context]):  Leaves voicemail for a given\n"
151 "extension (must be configured in voicemail.conf).\n"
152 " If the extension is preceded by \n"
153 "* 's' then instructions for leaving the message will be skipped.\n"
154 "* 'u' then the \"unavailable\" message will be played.\n"
155 "  (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
156 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
157 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
158 "priority 'o' in the current context.\n"
159 "If the requested mailbox does not exist, and there exists a priority\n"
160 "n + 101, then that priority will be taken next.\n"
161 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
162 "Otherwise, it returns 0.\n";
163
164 static char *synopsis_vmain =
165 "Enter voicemail system";
166
167 static char *descrip_vmain =
168 "  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
169 "for the checking of voicemail.  The mailbox can be passed as the option,\n"
170 "which will stop the voicemail system from prompting the user for the mailbox.\n"
171 "If the mailbox is preceded by 's' then the password check will be skipped.  If\n"
172 "a context is specified, logins are considered in that voicemail context only.\n"
173 "Returns -1 if the user hangs up or 0 otherwise.\n";
174
175 static char *synopsis_vm_box_exists =
176 "Check if vmbox exists";
177
178 static char *descrip_vm_box_exists =
179 "  MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
180 "if the specified voice mailbox exists.\n";
181
182
183 /* Leave a message */
184 static char *capp = "VoiceMail2";
185 static char *app = "VoiceMail";
186
187 /* Check mail, control, etc */
188 static char *capp2 = "VoiceMailMain2";
189 static char *app2 = "VoiceMailMain";
190
191 static char *app3 = "MailboxExists";
192
193 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
194 struct ast_vm_user *users;
195 struct ast_vm_user *usersl;
196 struct vm_zone *zones = NULL;
197 struct vm_zone *zonesl = NULL;
198 static int attach_voicemail;
199 static int maxsilence;
200 static int silencethreshold = 128;
201 static char serveremail[80];
202 static char mailcmd[160];       /* Configurable mail cmd */
203 static char externnotify[160]; 
204
205 static char vmfmts[80];
206 static int vmminmessage;
207 static int vmmaxmessage;
208 static int maxgreet;
209 static int skipms;
210 static int maxlogins;
211
212 static char *emailbody = NULL;
213 static int pbxskip = 0;
214 static char fromstring[100];
215 static char emailtitle[100];
216
217
218 STANDARD_LOCAL_USER;
219
220 LOCAL_USER_DECL;
221
222 static void apply_options(struct ast_vm_user *vmu, char *options)
223 {
224         /* Destructively Parse options and apply */
225         char *stringp = ast_strdupa(options);
226         char *s;
227         char *var, *value;
228         while((s = strsep(&stringp, "|"))) {
229                 value = s;
230                 if ((var = strsep(&value, "=")) && value) {
231                         if (!strcasecmp(var, "attach")) {
232                                 if (ast_true(value))
233                                         vmu->attach = 1;
234                                 else
235                                         vmu->attach = 0;
236                         } else if (!strcasecmp(var, "serveremail")) {
237                                 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
238                         } else if (!strcasecmp(var, "tz")) {
239                                 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
240                         }
241                 }
242         }
243         
244 }
245
246 #ifdef USEMYSQLVM
247 #include "mysql-vm-routines.h"
248 #endif
249
250 #ifdef USEPOSTGRESVM
251
252 PGconn *dbhandler;
253 char    dboption[256];
254 ast_mutex_t postgreslock;
255
256 static int sql_init(void)
257 {
258         ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
259 /*      fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
260
261         dbhandler=PQconnectdb(dboption);
262         if (PQstatus(dbhandler) == CONNECTION_BAD) {
263                 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
264                 return(-1);
265         }
266         ast_mutex_init(&postgreslock);
267
268 /*      fprintf(stderr,"postgres login OK\n"); */
269         return(0);
270 }
271
272 static void sql_close(void)
273 {
274         PQfinish(dbhandler);
275 }
276
277
278 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
279 {
280         PGresult *PGSQLres;
281
282
283         int numFields, i;
284         char *fname;
285         char query[240];
286         char options[160] = "";
287         struct ast_vm_user *retval;
288
289         retval=malloc(sizeof(struct ast_vm_user));
290
291 /*      fprintf(stderr,"postgres find_user:\n"); */
292
293         if (retval) {
294                 *retval->mailbox='\0';
295                 *retval->context='\0';
296                 strcpy(retval->password, "NULL");
297                 *retval->fullname='\0';
298                 *retval->email='\0';
299                 *retval->pager='\0';
300                 *retval->serveremail='\0';
301                 retval->attach=-1;
302                 retval->alloced=1;
303                 retval->next=NULL;
304                 if (mailbox) {
305                         strcpy(retval->mailbox, mailbox);
306                 }
307                 if (context) {
308                         strcpy(retval->context, context);
309                 }
310                 else
311                 {
312                         strcpy(retval->context, "default");
313                 }
314                 sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", retval->context, mailbox);
315                 
316 /*      fprintf(stderr,"postgres find_user: query = %s\n",query); */
317                 ast_mutex_lock(&postgreslock);
318                 PGSQLres=PQexec(dbhandler,query);
319                 if (PGSQLres!=NULL) {
320                         if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
321                                 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
322                                 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
323
324                                 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
325                                 PQclear(PGSQLres);
326                                 PQreset(dbhandler);
327                                 ast_mutex_unlock(&postgreslock);
328                                 free(retval);
329                                 return(NULL);
330                         } else {
331                         numFields = PQnfields(PGSQLres);
332 /*      fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
333                         if (PQntuples(PGSQLres) != 1) {
334                                 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
335                                 PQclear(PGSQLres);
336                                 ast_mutex_unlock(&postgreslock);
337                                 free(retval);
338                                 return(NULL);
339                         }
340                         for (i=0; i<numFields; i++) {
341                                 fname = PQfname(PGSQLres,i);
342                                 if (!strcmp(fname, "password") && !PQgetisnull (PGSQLres,0,i)) {
343                                         strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
344                                 } else if (!strcmp(fname, "fullname")) {
345                                         strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
346                                 } else if (!strcmp(fname, "email")) {
347                                         strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
348                                 } else if (!strcmp(fname, "pager")) {
349                                         strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
350                                 } else if (!strcmp(fname, "options")) {
351                                         strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
352                                         apply_options(retval, options);
353                                 }
354                         }
355                         }
356                         PQclear(PGSQLres);
357                         ast_mutex_unlock(&postgreslock);
358                         return(retval);
359                 }
360                 else {
361                         ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
362                         ast_mutex_unlock(&postgreslock);
363                         free(retval);
364                         return(NULL);
365                 }
366                 /* not reached */
367         } /* malloc() retval */
368         return(NULL);
369 }
370
371
372 static void vm_change_password(struct ast_vm_user *vmu, char *password)
373 {
374         char query[400];
375
376         if (*vmu->context) {
377                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->context, vmu->mailbox, vmu->password);
378         } else {
379                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
380         }
381 /*      fprintf(stderr,"postgres change_password: query = %s\n",query); */
382         ast_mutex_lock(&postgreslock);
383         PQexec(dbhandler, query);
384         strcpy(vmu->password, password);
385         ast_mutex_unlock(&postgreslock);
386 }
387
388 static void reset_user_pw(char *context, char *mailbox, char *password)
389 {
390         char query[320];
391
392         if (context) {
393                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
394         } else {
395                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
396         }
397         ast_mutex_lock(&postgreslock);
398 /*      fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
399         PQexec(dbhandler, query);
400         ast_mutex_unlock(&postgreslock);
401 }
402
403 #endif  /* Postgres */
404
405 #ifndef USESQLVM
406
407 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
408 {
409         /* This function could be made to generate one from a database, too */
410         struct ast_vm_user *vmu=NULL, *cur;
411         ast_mutex_lock(&vmlock);
412         cur = users;
413         while(cur) {
414                 if ((!context || !strcasecmp(context, cur->context)) &&
415                         (!strcasecmp(mailbox, cur->mailbox)))
416                                 break;
417                 cur=cur->next;
418         }
419         if (cur) {
420                 if (ivm)
421                         vmu = ivm;
422                 else
423                         /* Make a copy, so that on a reload, we have no race */
424                         vmu = malloc(sizeof(struct ast_vm_user));
425                 if (vmu) {
426                         memcpy(vmu, cur, sizeof(struct ast_vm_user));
427                         if (ivm)
428                                 vmu->alloced = 0;
429                         else
430                                 vmu->alloced = 1;
431                         vmu->next = NULL;
432                 }
433         }
434         ast_mutex_unlock(&vmlock);
435         return vmu;
436 }
437
438 static int reset_user_pw(char *context, char *mailbox, char *newpass)
439 {
440         /* This function could be made to generate one from a database, too */
441         struct ast_vm_user *cur;
442         int res = -1;
443         ast_mutex_lock(&vmlock);
444         cur = users;
445         while(cur) {
446                 if ((!context || !strcasecmp(context, cur->context)) &&
447                         (!strcasecmp(mailbox, cur->mailbox)))
448                                 break;
449                 cur=cur->next;
450         }
451         if (cur) {
452                 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
453                 res = 0;
454         }
455         ast_mutex_unlock(&vmlock);
456         return res;
457 }
458
459 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
460 {
461         /*  There's probably a better way of doing this. */
462         /*  That's why I've put the password change in a separate function. */
463                 /*  This could also be done with a database function */
464         
465         FILE *configin;
466         FILE *configout;
467                 int linenum=0;
468                 char inbuf[256];
469                 char orig[256];
470                 char currcontext[256] ="";
471                 char tmpin[AST_CONFIG_MAX_PATH];
472                 char tmpout[AST_CONFIG_MAX_PATH];
473                 char *user, *pass, *rest, *trim, *tempcontext;
474                 tempcontext = NULL;
475                 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
476                 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
477         configin = fopen((char *)tmpin,"r");
478                 if (configin)
479                 configout = fopen((char *)tmpout,"w+");
480                 else
481                         configout = NULL;
482                 if(!configin || !configout) {
483                         if (configin)
484                                 fclose(configin);
485                         else
486                                 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
487                         if (configout)
488                                 fclose(configout);
489                         else
490                                 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
491                         return;
492                 }
493
494         while (!feof(configin)) {
495                         /* Read in the line */
496                         fgets(inbuf, sizeof(inbuf), configin);
497                         linenum++;
498                         if (!feof(configin)) {
499                                 /* Make a backup of it */
500                                 memcpy(orig, inbuf, sizeof(orig));
501                                 /* Strip trailing \n and comment */
502                                 inbuf[strlen(inbuf) - 1] = '\0';
503                                 user = strchr(inbuf, ';');
504                                 if (user)
505                                         *user = '\0';
506                                 user=inbuf;
507                                 while(*user < 33)
508                                         user++;
509                                 /* check for '[' (opening of context name ) */
510                                 tempcontext = strchr(user, '[');
511                                 if (tempcontext) {
512                                         strncpy(currcontext, tempcontext +1,
513                                                  sizeof(currcontext) - 1);
514                                         /* now check for ']' */
515                                         tempcontext = strchr(currcontext, ']');
516                                         if (tempcontext) 
517                                                 *tempcontext = '\0';
518                                         else
519                                                 currcontext[0] = '\0';
520                                 }
521                                 pass = strchr(user, '=');
522                                 if (pass > user) {
523                                         trim = pass - 1;
524                                         while(*trim && *trim < 33) {
525                                                 *trim = '\0';
526                                                 trim--;
527                                         }
528                                 }
529                                 if (pass) {
530                                         *pass = '\0';
531                                         pass++;
532                                         if (*pass == '>')
533                                                 pass++;
534                                         while(*pass && *pass < 33)
535                                                 pass++;
536                                 }
537                                 if (pass) {
538                                         rest = strchr(pass,',');
539                                         if (rest) {
540                                                 *rest = '\0';
541                                                 rest++;
542                                         }
543                                 } else
544                                         rest = NULL;
545                                 
546                                 /* Compare user, pass AND context */
547                                 if (user && *user && !strcmp(user, vmu->mailbox) &&
548                                          pass && *pass && !strcmp(pass, vmu->password) &&
549                                          currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
550                                         /* This is the line */
551                                         if (rest) {
552                                                 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
553                                         } else {
554                                                 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
555                                         }
556                                 } else {
557                                         /* Put it back like it was */
558                                         fprintf(configout, orig);
559                                 }
560                         }
561         }
562         fclose(configin);
563         fclose(configout);
564
565         unlink((char *)tmpin);
566         rename((char *)tmpout,(char *)tmpin);
567         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
568         strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
569 }
570 #endif
571
572 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
573 {
574         return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
575 }
576
577 static int make_file(char *dest, int len, char *dir, int num)
578 {
579         return snprintf(dest, len, "%s/msg%04d", dir, num);
580 }
581
582 static int
583 inbuf(struct baseio *bio, FILE *fi)
584 {
585         int l;
586
587         if(bio->ateof)
588                 return 0;
589
590         if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
591                 if(ferror(fi))
592                         return -1;
593
594                 bio->ateof = 1;
595                 return 0;
596         }
597
598         bio->iolen= l;
599         bio->iocp= 0;
600
601         return 1;
602 }
603
604 static int 
605 inchar(struct baseio *bio, FILE *fi)
606 {
607         if(bio->iocp>=bio->iolen)
608                 if(!inbuf(bio, fi))
609                         return EOF;
610
611         return bio->iobuf[bio->iocp++];
612 }
613
614 static int
615 ochar(struct baseio *bio, int c, FILE *so)
616 {
617         if(bio->linelength>=BASELINELEN) {
618                 if(fputs(eol,so)==EOF)
619                         return -1;
620
621                 bio->linelength= 0;
622         }
623
624         if(putc(((unsigned char)c),so)==EOF)
625                 return -1;
626
627         bio->linelength++;
628
629         return 1;
630 }
631
632 static int base_encode(char *filename, FILE *so)
633 {
634         unsigned char dtable[BASEMAXINLINE];
635         int i,hiteof= 0;
636         FILE *fi;
637         struct baseio bio;
638
639         memset(&bio, 0, sizeof(bio));
640         bio.iocp = BASEMAXINLINE;
641
642         if ( !(fi = fopen(filename, "rb"))) {
643                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
644                 return -1;
645         }
646
647         for(i= 0;i<9;i++){
648                 dtable[i]= 'A'+i;
649                 dtable[i+9]= 'J'+i;
650                 dtable[26+i]= 'a'+i;
651                 dtable[26+i+9]= 'j'+i;
652         }
653         for(i= 0;i<8;i++){
654                 dtable[i+18]= 'S'+i;
655                 dtable[26+i+18]= 's'+i;
656         }
657         for(i= 0;i<10;i++){
658                 dtable[52+i]= '0'+i;
659         }
660         dtable[62]= '+';
661         dtable[63]= '/';
662
663         while(!hiteof){
664                 unsigned char igroup[3],ogroup[4];
665                 int c,n;
666
667                 igroup[0]= igroup[1]= igroup[2]= 0;
668
669                 for(n= 0;n<3;n++){
670                         if ( (c = inchar(&bio, fi)) == EOF) {
671                                 hiteof= 1;
672                                 break;
673                         }
674
675                         igroup[n]= (unsigned char)c;
676                 }
677
678                 if(n> 0){
679                         ogroup[0]= dtable[igroup[0]>>2];
680                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
681                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
682                         ogroup[3]= dtable[igroup[2]&0x3F];
683
684                         if(n<3) {
685                                 ogroup[3]= '=';
686
687                                 if(n<2)
688                                         ogroup[2]= '=';
689                         }
690
691                         for(i= 0;i<4;i++)
692                                 ochar(&bio, ogroup[i], so);
693                 }
694         }
695
696         if(fputs(eol,so)==EOF)
697                 return 0;
698
699         fclose(fi);
700
701         return 1;
702 }
703
704 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *mailbox, char *callerid, char *attach, char *format, long duration, int attach_user_voicemail)
705 {
706         FILE *p=NULL;
707         int pfd;
708         char date[256];
709         char host[256];
710         char who[256];
711         char bound[256];
712         char fname[256];
713         char dur[256];
714         char tmp[80] = "/tmp/astmail-XXXXXX";
715         char tmp2[256];
716         time_t t;
717         struct tm tm;
718         struct vm_zone *the_zone = NULL;
719
720         if (!strcmp(format, "wav49"))
721                 format = "WAV";
722         ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
723         /* Make a temporary file instead of piping directly to sendmail, in case the mail
724            command hangs */
725         pfd = mkstemp(tmp);
726         if (pfd > -1) {
727                 p = fdopen(pfd, "w");
728                 if (!p) {
729                         close(pfd);
730                         pfd = -1;
731                 }
732         }
733         if (p) {
734                 gethostname(host, sizeof(host));
735                 if (strchr(srcemail, '@'))
736                         strncpy(who, srcemail, sizeof(who)-1);
737                 else {
738                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
739                 }
740                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
741                 time(&t);
742
743                 /* Does this user have a timezone specified? */
744                 if (strlen(vmu->zonetag)) {
745                         /* Find the zone in the list */
746                         struct vm_zone *z;
747                         z = zones;
748                         while (z) {
749                                 if (!strcmp(z->name, vmu->zonetag)) {
750                                         the_zone = z;
751                                         break;
752                                 }
753                                 z = z->next;
754                         }
755                 }
756
757                 if (the_zone)
758                         ast_localtime(&t,&tm,the_zone->timezone);
759                 else
760                         ast_localtime(&t,&tm,NULL);
761                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
762                 fprintf(p, "Date: %s\n", date);
763                 
764                 if (*fromstring)
765                         fprintf(p, "From: %s <%s>\n", fromstring, who);
766                 else
767                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
768                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
769
770                 if( *emailtitle)
771                 {
772                         fprintf(p, emailtitle, msgnum, mailbox) ;
773                         fprintf(p,"\n") ;
774                 }
775                 else
776                 if (pbxskip)
777                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
778                 else
779                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
780                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
781                 fprintf(p, "MIME-Version: 1.0\n");
782                 if (attach_user_voicemail) {
783                         // Something unique.
784                         snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
785
786                         fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
787
788                         fprintf(p, "--%s\n", bound);
789                 }
790                 fprintf(p, "Content-Type: text/plain; charset=ISO-8859-1\nContent-Transfer-Encoding: 8bit\n\n");
791                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
792                 if (emailbody) {
793                         struct ast_channel *ast = ast_channel_alloc(0);
794                         if (ast) {
795                                 char *passdata;
796                                 int vmlen = strlen(emailbody)*3 + 200;
797                                 if ((passdata = alloca(vmlen))) {
798                                         memset(passdata, 0, vmlen);
799                                         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
800                                         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
801                                         sprintf(passdata,"%d",msgnum);
802                                         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
803                                         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
804                                         pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
805                                         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
806                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
807                                         fprintf(p, "%s\n",passdata);
808                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
809                                 ast_channel_free(ast);
810                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
811                 } else {
812                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
813
814                         "in mailbox %s from %s, on %s so you might\n"
815                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
816                         dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
817                 }
818                 if (attach_user_voicemail) {
819                         fprintf(p, "--%s\n", bound);
820                         fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
821                         fprintf(p, "Content-Transfer-Encoding: base64\n");
822                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
823                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
824
825                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
826                         base_encode(fname, p);
827                         fprintf(p, "\n\n--%s--\n.\n", bound);
828                 }
829                 fclose(p);
830                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
831                 ast_safe_system(tmp2);
832                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
833         } else {
834                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
835                 return -1;
836         }
837         return 0;
838 }
839
840 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
841 {
842         FILE *p=NULL;
843         int pfd;
844         char date[256];
845         char host[256];
846         char who[256];
847         char dur[256];
848         char tmp[80] = "/tmp/astmail-XXXXXX";
849         char tmp2[256];
850         time_t t;
851         struct tm tm;
852         struct vm_zone *the_zone = NULL;
853         pfd = mkstemp(tmp);
854
855         if (pfd > -1) {
856                 p = fdopen(pfd, "w");
857                 if (!p) {
858                         close(pfd);
859                         pfd = -1;
860                 }
861         }
862
863         if (p) {
864                 gethostname(host, sizeof(host));
865                 if (strchr(srcemail, '@'))
866                         strncpy(who, srcemail, sizeof(who)-1);
867                 else {
868                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
869                 }
870                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
871                 time(&t);
872
873                 /* Does this user have a timezone specified? */
874                 if (strlen(vmu->zonetag)) {
875                         /* Find the zone in the list */
876                         struct vm_zone *z;
877                         z = zones;
878                         while (z) {
879                                 if (!strcmp(z->name, vmu->zonetag)) {
880                                         the_zone = z;
881                                         break;
882                                 }
883                                 z = z->next;
884                         }
885                 }
886
887                 if (the_zone)
888                         ast_localtime(&t,&tm,the_zone->timezone);
889                 else
890                         ast_localtime(&t,&tm,NULL);
891
892                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
893                 fprintf(p, "Date: %s\n", date);
894                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
895                 fprintf(p, "To: %s\n", pager);
896                 fprintf(p, "Subject: New VM\n\n");
897                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
898                 fprintf(p, "New %s long msg in box %s\n"
899                            "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
900                 fclose(p);
901                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
902                 ast_safe_system(tmp2);
903                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
904         } else {
905                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
906                 return -1;
907         }
908         return 0;
909 }
910
911 static int get_date(char *s, int len)
912 {
913         struct tm tm;
914         time_t t;
915         t = time(0);
916         localtime_r(&t,&tm);
917         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
918 }
919
920 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
921 {
922         int res;
923         char fn[256];
924         snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
925         if (ast_fileexists(fn, NULL, NULL) > 0) {
926                 res = ast_streamfile(chan, fn, chan->language);
927                 if (res)
928                         return -1;
929                 res = ast_waitstream(chan, ecodes);
930                 if (res)
931                         return res;
932         } else {
933                 res = ast_streamfile(chan, "vm-theperson", chan->language);
934                 if (res)
935                         return -1;
936                 res = ast_waitstream(chan, ecodes);
937                 if (res)
938                         return res;
939                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
940                 if (res)
941                         return res;
942         }
943         if (busy)
944                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
945         else
946                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
947         if (res)
948                 return -1;
949         res = ast_waitstream(chan, ecodes);
950         return res;
951 }
952
953 static int play_and_wait(struct ast_channel *chan, char *fn)
954 {
955         int d;
956         d = ast_streamfile(chan, fn, chan->language);
957         if (d)
958                 return d;
959         d = ast_waitstream(chan, AST_DIGIT_ANY);
960         ast_stopstream(chan);
961         return d;
962 }
963
964 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int beep)
965 {
966         char d = 0, *fmts;
967         char comment[256];
968         int x, fmtcnt=1, res=-1,outmsg=0;
969         struct ast_frame *f;
970         struct ast_filestream *others[MAX_OTHER_FORMATS];
971         struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
972         char *sfmt[MAX_OTHER_FORMATS];
973         char *stringp=NULL;
974         time_t start, end;
975         struct ast_dsp *sildet;         /* silence detector dsp */
976         int totalsilence = 0;
977         int dspsilence = 0;
978         int gotsilence = 0;             /* did we timeout for silence? */
979         int rfmt=0;     
980         char prependfile[80];
981         
982         ast_log(LOG_DEBUG,"play_and_preped: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
983         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
984
985         if (playfile || beep) { 
986                 if (!beep)
987                         d = play_and_wait(chan, playfile);
988                 if (d > -1)
989                         d = ast_streamfile(chan, "beep",chan->language);
990                 if (!d)
991                         d = ast_waitstream(chan,"");
992                 if (d < 0)
993                         return -1;
994         }
995         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
996         strcat(prependfile, "-prepend");
997                         
998         fmts = ast_strdupa(fmt);
999         
1000         stringp=fmts;
1001         strsep(&stringp, "|");
1002         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
1003         sfmt[0] = ast_strdupa(fmts);
1004         
1005         while((fmt = strsep(&stringp, "|"))) {
1006                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1007                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1008                         break;
1009                 }
1010                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1011         }
1012
1013         if (maxtime)
1014                 time(&start);
1015         for (x=0;x<fmtcnt;x++) {
1016                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1017                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1018                 if (!others[x]) {
1019                         break;
1020                 }
1021         }
1022         
1023         sildet = ast_dsp_new(); //Create the silence detector
1024         if (!sildet) {
1025                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1026                 return -1;
1027         }
1028         ast_dsp_set_threshold(sildet, silencethreshold);
1029         
1030         if (maxsilence > 0) {
1031                 rfmt = chan->readformat;
1032                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1033                 if (res < 0) {
1034                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1035                         return -1;
1036                 }
1037         }
1038                                                 
1039         if (x == fmtcnt) {
1040         /* Loop forever, writing the packets we read to the writer(s), until
1041            we read a # or get a hangup */
1042                 f = NULL;
1043                 for(;;) {
1044                         res = ast_waitfor(chan, 2000);
1045                         if (!res) {
1046                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1047                                 /* Try one more time in case of masq */
1048                                 res = ast_waitfor(chan, 2000);
1049                                 if (!res) {
1050                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1051                                         res = -1;
1052                                 }
1053                         }
1054                         
1055                         if (res < 0) {
1056                                 f = NULL;
1057                                 break;
1058                         }
1059                         f = ast_read(chan);
1060                         if (!f)
1061                                 break;
1062                         if (f->frametype == AST_FRAME_VOICE) {
1063                                 /* write each format */
1064                                 for (x=0;x<fmtcnt;x++) {
1065                                         if (!others[x])
1066                                                 break;
1067                                         res = ast_writestream(others[x], f);
1068                                 }
1069                                 
1070                                 /* Silence Detection */
1071                                 if (maxsilence > 0) {
1072                                         dspsilence = 0;
1073                                         ast_dsp_silence(sildet, f, &dspsilence);
1074                                         if (dspsilence)
1075                                                 totalsilence = dspsilence;
1076                                         else
1077                                                 totalsilence = 0;
1078                                         
1079                                         if (totalsilence > maxsilence) {
1080                                         /* Ended happily with silence */
1081                                         ast_frfree(f);
1082                                         gotsilence = 1;
1083                                         outmsg=2;
1084                                         break;
1085                                         }
1086                                 }
1087                                 /* Exit on any error */
1088                                 if (res) {
1089                                         ast_log(LOG_WARNING, "Error writing frame\n");
1090                                         ast_frfree(f);
1091                                         break;
1092                                 }
1093                         } else if (f->frametype == AST_FRAME_VIDEO) {
1094                                 /* Write only once */
1095                                 ast_writestream(others[0], f);
1096                         } else if (f->frametype == AST_FRAME_DTMF) {
1097                                 /* stop recording with any digit */
1098                                 if (option_verbose > 2) 
1099                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1100                                 res = f->subclass;
1101                                 outmsg = 2;
1102                                 ast_frfree(f);
1103                                 break;
1104                         }
1105                         if (maxtime) {
1106                                 time(&end);
1107                                 if (maxtime < (end - start)) {
1108                                         if (option_verbose > 2)
1109                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1110                                         res = 't';
1111                                         ast_frfree(f);
1112                                         break;
1113                                 }
1114                         }
1115                         ast_frfree(f);
1116                 }
1117                 if (!f) {
1118                         if (option_verbose > 2) 
1119                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1120                         res = -1;
1121                         outmsg=1;
1122 #if 0
1123                         /* delete all the prepend files */
1124                         for (x=0;x<fmtcnt;x++) {
1125                                 if (!others[x])
1126                                         break;
1127                                 ast_closestream(others[x]);
1128                                 ast_filedelete(prependfile, sfmt[x]);
1129                         }
1130 #endif
1131                 }
1132         } else {
1133                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
1134         }
1135 #if 0
1136         if (outmsg > 1) {
1137 #else
1138         if (outmsg) {
1139 #endif
1140                 struct ast_frame *fr;
1141                 for (x=0;x<fmtcnt;x++) {
1142                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1143                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1144                         if (!others[x] || !realfiles[x])
1145                                 break;
1146                         if (totalsilence)
1147                                 ast_stream_rewind(others[x], totalsilence-200);
1148                         else
1149                                 ast_stream_rewind(others[x], 200);
1150                         ast_truncstream(others[x]);
1151                         /* add the original file too */
1152                         while ((fr = ast_readframe(realfiles[x]))) {
1153                                 ast_writestream(others[x],fr);
1154                         }
1155                         ast_closestream(others[x]);
1156                         ast_closestream(realfiles[x]);
1157                         ast_filerename(prependfile, recordfile, sfmt[x]);
1158 #if 0
1159                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1160 #endif
1161                         ast_filedelete(prependfile, sfmt[x]);
1162                 }
1163         }
1164         if (rfmt) {
1165                 if (ast_set_read_format(chan, rfmt)) {
1166                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1167                 }
1168         }
1169         if (outmsg) {
1170                 if (outmsg > 1) {
1171                         /* Let them know it worked */
1172                         ast_streamfile(chan, "vm-msgsaved", chan->language);
1173                         ast_waitstream(chan, "");
1174                 }
1175         }       
1176         return res;
1177 }
1178
1179 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
1180 {
1181         char d, *fmts;
1182         char comment[256];
1183         int x, fmtcnt=1, res=-1,outmsg=0;
1184         struct ast_frame *f;
1185         struct ast_filestream *others[MAX_OTHER_FORMATS];
1186         char *sfmt[MAX_OTHER_FORMATS];
1187         char *stringp=NULL;
1188         time_t start, end;
1189         struct ast_dsp *sildet;         /* silence detector dsp */
1190         int totalsilence = 0;
1191         int dspsilence = 0;
1192         int gotsilence = 0;             /* did we timeout for silence? */
1193         int rfmt=0;     
1194         
1195         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1196         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1197
1198         if (playfile) { 
1199                 d = play_and_wait(chan, playfile);
1200                 if (d > -1)
1201                         d = ast_streamfile(chan, "beep",chan->language);
1202                 if (!d)
1203                         d = ast_waitstream(chan,"");
1204                 if (d < 0)
1205                         return -1;
1206         }
1207         
1208         fmts = ast_strdupa(fmt);
1209         
1210         stringp=fmts;
1211         strsep(&stringp, "|");
1212         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
1213         sfmt[0] = ast_strdupa(fmts);
1214         
1215         while((fmt = strsep(&stringp, "|"))) {
1216                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1217                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1218                         break;
1219                 }
1220                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1221         }
1222
1223         if (maxtime)
1224                 time(&start);
1225         for (x=0;x<fmtcnt;x++) {
1226                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1227                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1228                         
1229                 if (!others[x]) {
1230                         break;
1231                 }
1232         }
1233         
1234         sildet = ast_dsp_new(); //Create the silence detector
1235         if (!sildet) {
1236                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1237                 return -1;
1238         }
1239         ast_dsp_set_threshold(sildet, silencethreshold);
1240         
1241         if (maxsilence > 0) {
1242                 rfmt = chan->readformat;
1243                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1244                 if (res < 0) {
1245                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1246                         return -1;
1247                 }
1248         }
1249                                                 
1250         if (x == fmtcnt) {
1251         /* Loop forever, writing the packets we read to the writer(s), until
1252            we read a # or get a hangup */
1253                 f = NULL;
1254                 for(;;) {
1255                         res = ast_waitfor(chan, 2000);
1256                         if (!res) {
1257                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1258                                 /* Try one more time in case of masq */
1259                                 res = ast_waitfor(chan, 2000);
1260                                 if (!res) {
1261                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1262                                         res = -1;
1263                                 }
1264                         }
1265                         
1266                         if (res < 0) {
1267                                 f = NULL;
1268                                 break;
1269                         }
1270                         f = ast_read(chan);
1271                         if (!f)
1272                                 break;
1273                         if (f->frametype == AST_FRAME_VOICE) {
1274                                 /* write each format */
1275                                 for (x=0;x<fmtcnt;x++) {
1276                                         res = ast_writestream(others[x], f);
1277                                 }
1278                                 
1279                                 /* Silence Detection */
1280                                 if (maxsilence > 0) {
1281                                         dspsilence = 0;
1282                                         ast_dsp_silence(sildet, f, &dspsilence);
1283                                         if (dspsilence)
1284                                                 totalsilence = dspsilence;
1285                                         else
1286                                                 totalsilence = 0;
1287                                         
1288                                         if (totalsilence > maxsilence) {
1289                                         /* Ended happily with silence */
1290                                         ast_frfree(f);
1291                                         gotsilence = 1;
1292                                         outmsg=2;
1293                                         break;
1294                                         }
1295                                 }
1296                                 /* Exit on any error */
1297                                 if (res) {
1298                                         ast_log(LOG_WARNING, "Error writing frame\n");
1299                                         ast_frfree(f);
1300                                         break;
1301                                 }
1302                         } else if (f->frametype == AST_FRAME_VIDEO) {
1303                                 /* Write only once */
1304                                 ast_writestream(others[0], f);
1305                         } else if (f->frametype == AST_FRAME_DTMF) {
1306                                 if (f->subclass == '#') {
1307                                         if (option_verbose > 2) 
1308                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1309                                         res = '#';
1310                                         outmsg = 2;
1311                                         ast_frfree(f);
1312                                         break;
1313                                 }
1314                         }
1315                         if (maxtime) {
1316                                 time(&end);
1317                                 if (maxtime < (end - start)) {
1318                                         if (option_verbose > 2)
1319                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1320                                         res = 't';
1321                                         ast_frfree(f);
1322                                         break;
1323                                 }
1324                         }
1325                         ast_frfree(f);
1326                 }
1327                 if (!f) {
1328                         if (option_verbose > 2) 
1329                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1330                         res = -1;
1331                         outmsg=1;
1332                 }
1333         } else {
1334                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
1335         }
1336
1337         for (x=0;x<fmtcnt;x++) {
1338                 if (!others[x])
1339                         break;
1340                 if (totalsilence)
1341                         ast_stream_rewind(others[x], totalsilence-200);
1342                 else
1343                         ast_stream_rewind(others[x], 200);
1344                 ast_truncstream(others[x]);
1345                 ast_closestream(others[x]);
1346         }
1347         if (rfmt) {
1348                 if (ast_set_read_format(chan, rfmt)) {
1349                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1350                 }
1351         }
1352         if (outmsg) {
1353                 if (outmsg > 1) {
1354                 /* Let them know it worked */
1355                         ast_streamfile(chan, "vm-msgsaved", chan->language);
1356                         ast_waitstream(chan, "");
1357                 }
1358         }       
1359
1360         
1361         return res;
1362 }
1363
1364 static void free_user(struct ast_vm_user *vmu)
1365 {
1366         if (vmu->alloced)
1367                 free(vmu);
1368 }
1369
1370 static void free_zone(struct vm_zone *z)
1371 {
1372         free(z);
1373 }
1374
1375 static void run_externnotify(char *context, char *extension, int numvoicemails)
1376 {
1377         char arguments[255];
1378
1379         if(externnotify[0]) {
1380                 strncpy(arguments, externnotify, sizeof(arguments));
1381                 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1382                 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1383                 ast_safe_system(arguments);
1384         }
1385 }
1386
1387 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1388 {
1389         char comment[256];
1390         char txtfile[256];
1391         FILE *txt;
1392         int res = 0;
1393         int msgnum;
1394         int fd;
1395         char date[256];
1396         char dir[256];
1397         char fn[256];
1398         char prefile[256]="";
1399         char fmt[80];
1400         char *context;
1401         char *ecodes = "#";
1402         char *stringp;
1403         time_t start;
1404         time_t end;
1405         char tmp[256] = "";
1406         struct ast_vm_user *vmu;
1407         struct ast_vm_user svm;
1408         
1409         strncpy(tmp, ext, sizeof(tmp) - 1);
1410         ext = tmp;
1411         context = strchr(tmp, '@');
1412         if (context) {
1413                 *context = '\0';
1414                 context++;
1415         }
1416
1417         if ((vmu = find_user(&svm, context, ext))) {
1418                 /* Setup pre-file if appropriate */
1419                 if (busy)
1420                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1421                 else if (unavail)
1422                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1423                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1424                 /* It's easier just to try to make it than to check for its existence */
1425                 if (mkdir(dir, 0700) && (errno != EEXIST))
1426                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1427                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1428                 /* It's easier just to try to make it than to check for its existence */
1429                 if (mkdir(dir, 0700) && (errno != EEXIST))
1430                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1431                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1432                 if (mkdir(dir, 0700) && (errno != EEXIST))
1433                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1434                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1435                         ecodes = "#0";
1436                 /* Play the beginning intro if desired */
1437                 if (strlen(prefile)) {
1438                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1439                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1440                                     res = ast_waitstream(chan, "#0");
1441                         } else {
1442                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1443                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1444                         }
1445                         if (res < 0) {
1446                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1447                                 free_user(vmu);
1448                                 return -1;
1449                         }
1450                 }
1451                 if (res == '#') {
1452                         /* On a '#' we skip the instructions */
1453                         silent = 1;
1454                         res = 0;
1455                 }
1456                 if (!res && !silent) {
1457                         res = ast_streamfile(chan, INTRO, chan->language);
1458                         if (!res)
1459                                 res = ast_waitstream(chan, ecodes);
1460                         if (res == '#') {
1461                                 silent = 1;
1462                                 res = 0;
1463                         }
1464                 }
1465                 /* Check for a '0' here */
1466                 if (res == '0') {
1467                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1468                         if (strlen(chan->macrocontext))
1469                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1470                         chan->priority = 0;
1471                         free_user(vmu);
1472                         return 0;
1473                 }
1474                 if (res >= 0) {
1475                         /* Unless we're *really* silent, try to send the beep */
1476                         res = ast_streamfile(chan, "beep", chan->language);
1477                         if (!res)
1478                                 res = ast_waitstream(chan, "");
1479                 }
1480                 if (res < 0) {
1481                         free_user(vmu);
1482                         return -1;
1483                 }
1484                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1485                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1486                 if (strlen(fmt)) {
1487                         msgnum = 0;
1488                         do {
1489                                 make_file(fn, sizeof(fn), dir, msgnum);
1490                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1491                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1492                                                                         vmu->fullname, ext, chan->name);
1493                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1494                                         break;
1495                                 msgnum++;
1496                         } while(msgnum < MAXMSG);
1497                         if (msgnum < MAXMSG) {
1498                                 /* Store information */
1499                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1500                                 txt = fopen(txtfile, "w+");
1501                                 if (txt) {
1502                                         get_date(date, sizeof(date));
1503                                         time(&start);
1504                                         fprintf(txt, 
1505 ";\n"
1506 "; Message Information file\n"
1507 ";\n"
1508 "[message]\n"
1509 "origmailbox=%s\n"
1510 "context=%s\n"
1511 "exten=%s\n"
1512 "priority=%d\n"
1513 "callerchan=%s\n"
1514 "callerid=%s\n"
1515 "origdate=%s\n"
1516 "origtime=%ld\n",
1517         ext,
1518         chan->context,
1519         chan->exten,
1520         chan->priority,
1521         chan->name,
1522         chan->callerid ? chan->callerid : "Unknown",
1523         date, (long)time(NULL));
1524                                         fclose(txt);
1525                                 } else
1526                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1527                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1528                                 if (res > 0)
1529                                         res = 0;
1530                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1531                                 if (fd > -1) {
1532                                         txt = fdopen(fd, "a");
1533                                         if (txt) {
1534                                                 time(&end);
1535                                                 fprintf(txt, "duration=%ld\n", (long)(end-start));
1536                                                 fclose(txt);
1537                                         } else
1538                                                 close(fd);
1539                                 }
1540                                 if (end - start < vmminmessage) {
1541                                         ast_filedelete(fn, NULL);
1542                                         goto leave_vm_out;
1543                                 }
1544                                 stringp = fmt;
1545                                 strsep(&stringp, "|");
1546                                 /* Send e-mail if applicable */
1547                                 if (strlen(vmu->email)) {
1548                                         int attach_user_voicemail = attach_voicemail;
1549                                         char *myserveremail = serveremail;
1550                                         if (vmu->attach > -1)
1551                                                 attach_user_voicemail = vmu->attach;
1552                                         if (strlen(vmu->serveremail))
1553                                                 myserveremail = vmu->serveremail;
1554                                         sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1555                                 }
1556                                 if (strlen(vmu->pager)) {
1557                                         char *myserveremail = serveremail;
1558                                         if (strlen(vmu->serveremail))
1559                                                 myserveremail = vmu->serveremail;
1560                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1561                                 }
1562                         } else {
1563                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1564                                 if (!res)
1565                                         res = ast_waitstream(chan, "");
1566                                 ast_log(LOG_WARNING, "No more messages possible\n");
1567                         }
1568                 } else
1569                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1570 leave_vm_out:
1571                 free_user(vmu);
1572         } else {
1573                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1574                         /*Send the call to n+101 priority, where n is the current priority*/
1575                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1576                                 chan->priority+=100;
1577         }
1578         /* Leave voicemail for someone */
1579         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1580
1581         /* If an external program is specified to be run after leaving a voicemail */
1582         run_externnotify(chan->context, ext, ast_app_has_voicemail(ext));
1583
1584         return res;
1585 }
1586
1587 static char *mbox(int id)
1588 {
1589         switch(id) {
1590         case 0:
1591                 return "INBOX";
1592         case 1:
1593                 return "Old";
1594         case 2:
1595                 return "Work";
1596         case 3:
1597                 return "Family";
1598         case 4:
1599                 return "Friends";
1600         case 5:
1601                 return "Cust1";
1602         case 6:
1603                 return "Cust2";
1604         case 7:
1605                 return "Cust3";
1606         case 8:
1607                 return "Cust4";
1608         case 9:
1609                 return "Cust5";
1610         default:
1611                 return "Unknown";
1612         }
1613 }
1614
1615 static int count_messages(char *dir)
1616 {
1617         int x;
1618         char fn[256];
1619         for (x=0;x<MAXMSG;x++) {
1620                 make_file(fn, sizeof(fn), dir, x);
1621                 if (ast_fileexists(fn, NULL, NULL) < 1)
1622                         break;
1623         }
1624         return x;
1625 }
1626
1627 static int say_and_wait(struct ast_channel *chan, int num)
1628 {
1629         int d;
1630         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1631         return d;
1632 }
1633
1634 static int copy(char *infile, char *outfile)
1635 {
1636         int ifd;
1637         int ofd;
1638         int res;
1639         int len;
1640         char buf[4096];
1641         if ((ifd = open(infile, O_RDONLY)) < 0) {
1642                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1643                 return -1;
1644         }
1645         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1646                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1647                 close(ifd);
1648                 return -1;
1649         }
1650         do {
1651                 len = read(ifd, buf, sizeof(buf));
1652                 if (len < 0) {
1653                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1654                         close(ifd);
1655                         close(ofd);
1656                         unlink(outfile);
1657                 }
1658                 if (len) {
1659                         res = write(ofd, buf, len);
1660                         if (res != len) {
1661                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1662                                 close(ifd);
1663                                 close(ofd);
1664                                 unlink(outfile);
1665                         }
1666                 }
1667         } while(len);
1668         close(ifd);
1669         close(ofd);
1670         return 0;
1671 }
1672
1673 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1674 {
1675         char sfn[256];
1676         char dfn[256];
1677         char ddir[256];
1678         char txt[256];
1679         char ntxt[256];
1680         char *dbox = mbox(box);
1681         int x;
1682         make_file(sfn, sizeof(sfn), dir, msg);
1683         make_dir(ddir, sizeof(ddir), context, username, dbox);
1684         mkdir(ddir, 0700);
1685         for (x=0;x<MAXMSG;x++) {
1686                 make_file(dfn, sizeof(dfn), ddir, x);
1687                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1688                         break;
1689         }
1690         if (x >= MAXMSG)
1691                 return -1;
1692         ast_filecopy(sfn, dfn, NULL);
1693         if (strcmp(sfn, dfn)) {
1694                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1695                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1696                 copy(txt, ntxt);
1697         }
1698         return 0;
1699 }
1700
1701 static int adsi_logo(unsigned char *buf)
1702 {
1703         int bytes = 0;
1704         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1705         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1706         return bytes;
1707 }
1708
1709 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1710 {
1711         char buf[256];
1712         int bytes=0;
1713         int x;
1714         char num[5];
1715
1716         *useadsi = 0;
1717         bytes += adsi_data_mode(buf + bytes);
1718         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1719
1720         bytes = 0;
1721         bytes += adsi_logo(buf);
1722         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1723 #ifdef DISPLAY
1724         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1725 #endif
1726         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1727         bytes += adsi_data_mode(buf + bytes);
1728         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1729
1730         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1731                 bytes = 0;
1732                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1733                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1734                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1735                 bytes += adsi_voice_mode(buf + bytes, 0);
1736                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1737                 return 0;
1738         }
1739
1740 #ifdef DISPLAY
1741         /* Add a dot */
1742         bytes = 0;
1743         bytes += adsi_logo(buf);
1744         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1745         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1746         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1747         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1748 #endif
1749         bytes = 0;
1750         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1751         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1752         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1753         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1754         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1755         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1756         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1757
1758 #ifdef DISPLAY
1759         /* Add another dot */
1760         bytes = 0;
1761         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1762       bytes += adsi_voice_mode(buf + bytes, 0);
1763
1764         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1765         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1766 #endif
1767
1768         bytes = 0;
1769         /* These buttons we load but don't use yet */
1770         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1771         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1772         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1773         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1774         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1775         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1776         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1777
1778 #ifdef DISPLAY
1779         /* Add another dot */
1780         bytes = 0;
1781         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1782         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1783         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1784 #endif
1785
1786         bytes = 0;
1787         for (x=0;x<5;x++) {
1788                 snprintf(num, sizeof(num), "%d", x);
1789                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1790         }
1791         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1792         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1793
1794 #ifdef DISPLAY
1795         /* Add another dot */
1796         bytes = 0;
1797         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1798         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1799         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1800 #endif
1801
1802         if (adsi_end_download(chan)) {
1803                 bytes = 0;
1804                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1805                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1806                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1807                 bytes += adsi_voice_mode(buf + bytes, 0);
1808                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1809                 return 0;
1810         }
1811         bytes = 0;
1812         bytes += adsi_download_disconnect(buf + bytes);
1813         bytes += adsi_voice_mode(buf + bytes, 0);
1814         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1815
1816         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1817
1818 #ifdef DISPLAY
1819         /* Add last dot */
1820         bytes = 0;
1821         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1822         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1823 #endif
1824         ast_log(LOG_DEBUG, "Restarting session...\n");
1825
1826         bytes = 0;
1827         /* Load the session now */
1828         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1829                 *useadsi = 1;
1830                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1831         } else
1832                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1833
1834         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1835         return 0;
1836 }
1837
1838 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1839 {
1840         int x;
1841         if (!adsi_available(chan))
1842           return;
1843         x = adsi_load_session(chan, adapp, adver, 1);
1844         if (x < 0)
1845                 return;
1846         if (!x) {
1847                 if (adsi_load_vmail(chan, useadsi)) {
1848                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1849                         return;
1850                 }
1851         } else
1852                 *useadsi = 1;
1853 }
1854
1855 static void adsi_login(struct ast_channel *chan)
1856 {
1857         char buf[256];
1858         int bytes=0;
1859         unsigned char keys[8];
1860         int x;
1861         if (!adsi_available(chan))
1862                 return;
1863
1864         for (x=0;x<8;x++)
1865                 keys[x] = 0;
1866         /* Set one key for next */
1867         keys[3] = ADSI_KEY_APPS + 3;
1868
1869         bytes += adsi_logo(buf + bytes);
1870         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1871         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1872         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1873         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1874         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1875         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1876         bytes += adsi_set_keys(buf + bytes, keys);
1877         bytes += adsi_voice_mode(buf + bytes, 0);
1878         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1879 }
1880
1881 static void adsi_password(struct ast_channel *chan)
1882 {
1883         char buf[256];
1884         int bytes=0;
1885         unsigned char keys[8];
1886         int x;
1887         if (!adsi_available(chan))
1888                 return;
1889
1890         for (x=0;x<8;x++)
1891                 keys[x] = 0;
1892         /* Set one key for next */
1893         keys[3] = ADSI_KEY_APPS + 3;
1894
1895         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1896         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1897         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1898         bytes += adsi_set_keys(buf + bytes, keys);
1899         bytes += adsi_voice_mode(buf + bytes, 0);
1900         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1901 }
1902
1903 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1904 {
1905         char buf[256];
1906         int bytes=0;
1907         unsigned char keys[8];
1908         int x,y;
1909
1910         if (!adsi_available(chan))
1911                 return;
1912
1913         for (x=0;x<5;x++) {
1914                 y = ADSI_KEY_APPS + 12 + start + x;
1915                 if (y > ADSI_KEY_APPS + 12 + 4)
1916                         y = 0;
1917                 keys[x] = ADSI_KEY_SKT | y;
1918         }
1919         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1920         keys[6] = 0;
1921         keys[7] = 0;
1922
1923         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1924         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1925         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1926         bytes += adsi_set_keys(buf + bytes, keys);
1927         bytes += adsi_voice_mode(buf + bytes, 0);
1928
1929         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1930 }
1931
1932 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1933 {
1934         int bytes=0;
1935         char buf[256], buf1[256], buf2[256];
1936         char fn2[256];
1937
1938         char cid[256]="";
1939         char *val;
1940         char *name, *num;
1941         char datetime[21]="";
1942         FILE *f;
1943
1944         unsigned char keys[8];
1945
1946         int x;
1947
1948         if (!adsi_available(chan))
1949                 return;
1950
1951         /* Retrieve important info */
1952         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1953         f = fopen(fn2, "r");
1954         if (f) {
1955                 while(!feof(f)) {       
1956                         fgets(buf, sizeof(buf), f);
1957                         if (!feof(f)) {
1958                                 char *stringp=NULL;
1959                                 stringp=buf;
1960                                 strsep(&stringp, "=");
1961                                 val = strsep(&stringp, "=");
1962                                 if (val && strlen(val)) {
1963                                         if (!strcmp(buf, "callerid"))
1964                                                 strncpy(cid, val, sizeof(cid) - 1);
1965                                         if (!strcmp(buf, "origdate"))
1966                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1967                                 }
1968                         }
1969                 }
1970                 fclose(f);
1971         }
1972         /* New meaning for keys */
1973         for (x=0;x<5;x++)
1974                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1975         keys[6] = 0x0;
1976         keys[7] = 0x0;
1977
1978         if (!msg) {
1979                 /* No prev key, provide "Folder" instead */
1980                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1981         }
1982         if (msg >= last) {
1983                 /* If last message ... */
1984                 if (msg) {
1985                         /* but not only message, provide "Folder" instead */
1986                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1987       bytes += adsi_voice_mode(buf + bytes, 0);
1988
1989                 } else {
1990                         /* Otherwise if only message, leave blank */
1991                         keys[3] = 1;
1992                 }
1993         }
1994
1995         if (strlen(cid)) {
1996                 ast_callerid_parse(cid, &name, &num);
1997                 if (!name)
1998                         name = num;
1999         } else
2000                 name = "Unknown Caller";
2001
2002         /* If deleted, show "undeleted" */
2003
2004         if (deleted)
2005                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2006
2007         /* Except "Exit" */
2008         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2009         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2010                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2011         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2012
2013         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2014         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2015         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2016         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2017         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2018         bytes += adsi_set_keys(buf + bytes, keys);
2019         bytes += adsi_voice_mode(buf + bytes, 0);
2020
2021         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2022 }
2023
2024 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2025 {
2026         int bytes=0;
2027         char buf[256];
2028         unsigned char keys[8];
2029
2030         int x;
2031
2032         if (!adsi_available(chan))
2033                 return;
2034
2035         /* New meaning for keys */
2036         for (x=0;x<5;x++)
2037                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2038
2039         keys[6] = 0x0;
2040         keys[7] = 0x0;
2041
2042         if (!msg) {
2043                 /* No prev key, provide "Folder" instead */
2044                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2045         }
2046         if (msg >= last) {
2047                 /* If last message ... */
2048                 if (msg) {
2049                         /* but not only message, provide "Folder" instead */
2050                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2051                 } else {
2052                         /* Otherwise if only message, leave blank */
2053                         keys[3] = 1;
2054                 }
2055         }
2056
2057         /* If deleted, show "undeleted" */
2058         if (deleted) 
2059                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2060
2061         /* Except "Exit" */
2062         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2063         bytes += adsi_set_keys(buf + bytes, keys);
2064         bytes += adsi_voice_mode(buf + bytes, 0);
2065
2066         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2067 }
2068
2069 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2070 {
2071         char buf[256], buf1[256], buf2[256];
2072         int bytes=0;
2073         unsigned char keys[8];
2074         int x;
2075
2076         char *newm = (new == 1) ? "message" : "messages";
2077         char *oldm = (old == 1) ? "message" : "messages";
2078         if (!adsi_available(chan))
2079                 return;
2080         if (new) {
2081                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2082                 if (old) {
2083                         strcat(buf1, " and");
2084                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2085                 } else {
2086                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2087                 }
2088         } else if (old) {
2089                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2090                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2091         } else {
2092                 strcpy(buf1, "You have no messages.");
2093                 strcpy(buf2, " ");
2094         }
2095         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2096         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2097         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2098
2099         for (x=0;x<6;x++)
2100                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2101         keys[6] = 0;
2102         keys[7] = 0;
2103
2104         /* Don't let them listen if there are none */
2105         if (lastmsg < 0)
2106                 keys[0] = 1;
2107         bytes += adsi_set_keys(buf + bytes, keys);
2108
2109         bytes += adsi_voice_mode(buf + bytes, 0);
2110
2111         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2112 }
2113
2114 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2115 {
2116         char buf[256], buf1[256], buf2[256];
2117         int bytes=0;
2118         unsigned char keys[8];
2119         int x;
2120
2121         char *mess = (messages == 1) ? "message" : "messages";
2122
2123         if (!adsi_available(chan))
2124                 return;
2125
2126         /* Original command keys */
2127         for (x=0;x<6;x++)
2128                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2129
2130         keys[6] = 0;
2131         keys[7] = 0;
2132
2133         if (messages < 1)
2134                 keys[0] = 0;
2135
2136         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2137                         strcasecmp(folder, "INBOX") ? " folder" : "");
2138
2139         if (messages)
2140                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2141         else
2142                 strcpy(buf2, "no messages.");
2143         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2144         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2145         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2146         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2147         bytes += adsi_set_keys(buf + bytes, keys);
2148
2149         bytes += adsi_voice_mode(buf + bytes, 0);
2150
2151         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2152         
2153 }
2154
2155 /*
2156 static void adsi_clear(struct ast_channel *chan)
2157 {
2158         char buf[256];
2159         int bytes=0;
2160         if (!adsi_available(chan))
2161                 return;
2162         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2163         bytes += adsi_voice_mode(buf + bytes, 0);
2164
2165         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2166 }
2167 */
2168
2169 static void adsi_goodbye(struct ast_channel *chan)
2170 {
2171         char buf[256];
2172         int bytes=0;
2173
2174         if (!adsi_available(chan))
2175                 return;
2176         bytes += adsi_logo(buf + bytes);
2177         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2178         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2179         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2180         bytes += adsi_voice_mode(buf + bytes, 0);
2181
2182         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2183 }
2184
2185 static int get_folder(struct ast_channel *chan, int start)
2186 {
2187         int x;
2188         int d;
2189         char fn[256];
2190         d = play_and_wait(chan, "vm-press");
2191         if (d)
2192                 return d;
2193         for (x = start; x< 5; x++) {
2194                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2195                         return d;
2196                 d = play_and_wait(chan, "vm-for");
2197                 if (d)
2198                         return d;
2199                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2200                 d = play_and_wait(chan, fn);
2201                 if (d)
2202                         return d;
2203                 d = play_and_wait(chan, "vm-messages");
2204                 if (d)
2205                         return d;
2206                 d = ast_waitfordigit(chan, 500);
2207                 if (d)
2208                         return d;
2209         }
2210         d = play_and_wait(chan, "vm-tocancel");
2211         if (d)
2212                 return d;
2213         d = ast_waitfordigit(chan, 4000);
2214         return d;
2215 }
2216
2217 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2218 {
2219         int res = 0;
2220         res = play_and_wait(chan, fn);
2221         while (((res < '0') || (res > '9')) &&
2222                         (res != '#') && (res >= 0)) {
2223                 res = get_folder(chan, 0);
2224         }
2225         return res;
2226 }
2227
2228 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2229 {
2230         int cmd = 0;
2231         int retries = 0;
2232
2233         while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2234                 if (cmd)
2235                         retries = 0;
2236                 switch (cmd) {
2237                 case '1': 
2238                         /* prepend a message to the current message and return */
2239                 {
2240                         char file[200];
2241                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2242                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2243                         break;
2244                 }
2245                 case '2': 
2246                         cmd = 't';
2247                         break;
2248                 case '#':
2249                         cmd = '#';
2250                         break;
2251                 default: 
2252                         cmd = play_and_wait(chan,"vm-forwardoptions");
2253                         if (!cmd)
2254                                 cmd = ast_waitfordigit(chan,6000);
2255                         if (!cmd)
2256                                 retries++;
2257                         if (retries > 3)
2258                                 cmd = 't';
2259                  }
2260         }
2261         if (cmd == 't')
2262                 cmd = 0;
2263         return cmd;
2264 }
2265
2266 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2267 {
2268         char username[70];
2269         char sys[256];
2270         char todir[256];
2271         int todircount=0;
2272         long duration;
2273         struct ast_config *mif;
2274         char miffile[256];
2275         char fn[256];
2276         char callerid[512];
2277         int res = 0, cmd = 0;
2278         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2279         char tmp[256];
2280         char *stringp, *s;
2281         int saved_messages = 0, found = 0;
2282         int valid_extensions = 0;
2283         while (!res && !valid_extensions) {
2284                 res = ast_streamfile(chan, "vm-extension", chan->language);
2285                 if (res)
2286                         break;
2287                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2288                         break;
2289                 /* start all over if no username */
2290                 if (!strlen(username))
2291                         continue;
2292                 stringp = username;
2293                 s = strsep(&stringp, "*");
2294                 /* start optimistic */
2295                 valid_extensions = 1;
2296                 while (s) {
2297                         /* find_user is going to malloc since we have a NULL as first argument */
2298                         if ((receiver = find_user(NULL, context, s))) {
2299                                 if (!extensions)
2300                                         vmtmp = extensions = receiver;
2301                                 else {
2302                                         vmtmp->next = receiver;
2303                                         vmtmp = receiver;
2304                                 }
2305                                 found++;
2306                         } else {
2307                                 valid_extensions = 0;
2308                                 break;
2309                         }
2310                         s = strsep(&stringp, "*");
2311                 }
2312                 /* break from the loop of reading the extensions */
2313                 if (valid_extensions)
2314                         break;
2315                 /* invalid extension, try again */
2316                 res = play_and_wait(chan, "pbx-invalid");
2317         }
2318         /* check if we're clear to proceed */
2319         if (!extensions || !valid_extensions)
2320                 return res;
2321         vmtmp = extensions;
2322         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2323
2324         while(!res && vmtmp) {
2325                 /* if (play_and_wait(chan, "vm-savedto"))
2326                         break;
2327                 */
2328                 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2329                 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2330                 ast_log(LOG_DEBUG, sys);
2331                 ast_safe_system(sys);
2332
2333                 todircount = count_messages(todir);
2334                 strncpy(tmp, fmt, sizeof(tmp) - 1);
2335                 stringp = tmp;
2336                 while((s = strsep(&stringp, "|"))) {
2337                         /* XXX This is a hack -- we should use build_filename or similar XXX */
2338                         if (!strcasecmp(s, "wav49"))
2339                                 s = "WAV";
2340                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2341                         ast_log(LOG_DEBUG, sys);
2342                         ast_safe_system(sys);
2343                 }
2344                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2345                 ast_log(LOG_DEBUG, sys);
2346                 ast_safe_system(sys);
2347                 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2348
2349                 /* load the information on the source message so we can send an e-mail like a new message */
2350                 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2351                 if ((mif=ast_load(miffile))) {
2352
2353                         /* set callerid and duration variables */
2354                         snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2355                         s = ast_variable_retrieve(mif, NULL, "duration");
2356                         if (s)
2357                                 duration = atol(s);
2358                         else
2359                                 duration = 0;
2360                         if (strlen(vmtmp->email)) {
2361                                 int attach_user_voicemail = attach_voicemail;
2362                                 char *myserveremail = serveremail;
2363                                 if (vmtmp->attach > -1)
2364                                         attach_user_voicemail = vmtmp->attach;
2365                                 if (strlen(vmtmp->serveremail))
2366                                         myserveremail = vmtmp->serveremail;
2367                                 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2368                         }
2369                      
2370                         if (strlen(vmtmp->pager)) {
2371                                 char *myserveremail = serveremail;
2372                                 if (strlen(vmtmp->serveremail))
2373                                         myserveremail = vmtmp->serveremail;
2374                                 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2375                         }
2376                           
2377                         ast_destroy(mif); /* or here */
2378                 }
2379                 /* Leave voicemail for someone */
2380                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2381                 run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2382
2383                 saved_messages++;
2384                 vmfree = vmtmp;
2385                 vmtmp = vmtmp->next;
2386                 free_user(vmfree);
2387         }
2388         if (saved_messages > 0) {
2389                 /* give confirmatopm that the message was saved */
2390                 if (saved_messages == 1)
2391                         res = play_and_wait(chan, "vm-message");
2392                 else
2393                         res = play_and_wait(chan, "vm-messages");
2394                 if (!res)
2395                         res = play_and_wait(chan, "vm-saved");
2396         }
2397         return res ? res : cmd;
2398 }
2399
2400
2401 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2402 {
2403         int res;
2404         if ((res = ast_streamfile(chan, file, chan->language))) 
2405                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2406         if (!res)
2407                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2408         return res;
2409 }
2410
2411 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2412 {
2413         int res;
2414         if ((res = ast_streamfile(chan, file, chan->language)))
2415                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2416         if (!res)
2417                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2418         return res;
2419 }
2420
2421 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2422 {
2423         int res = 0;
2424         char filename[256], *origtime;
2425         struct vm_zone *the_zone = NULL;
2426         struct ast_config *msg_cfg;
2427         time_t t;
2428         long tin;
2429
2430         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2431         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2432         msg_cfg = ast_load(filename);
2433         if (!msg_cfg) {
2434                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2435                 return 0;
2436         }
2437
2438         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2439                 return 0;
2440         if (sscanf(origtime,"%ld",&tin) < 1) {
2441                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2442                 return 0;
2443         }
2444         t = tin;
2445         ast_destroy(msg_cfg);
2446
2447         /* Does this user have a timezone specified? */
2448         if (strlen(vmu->zonetag)) {
2449                 /* Find the zone in the list */
2450                 struct vm_zone *z;
2451                 z = zones;
2452                 while (z) {
2453                         if (!strcmp(z->name, vmu->zonetag)) {
2454                                 the_zone = z;
2455                                 break;
2456                         }
2457                         z = z->next;
2458                 }
2459         }
2460
2461 /* No internal variable parsing for now, so we'll comment it out for the time being */
2462 #if 0
2463         /* Set the DIFF_* variables */
2464         localtime_r(&t, &time_now);
2465         gettimeofday(&tv_now,NULL);
2466         tnow = tv_now.tv_sec;
2467         localtime_r(&tnow,&time_then);
2468
2469         /* Day difference */
2470         if (time_now.tm_year == time_then.tm_year)
2471                 sprintf(temp,"%d",time_now.tm_yday);
2472         else
2473                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2474         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2475
2476         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2477 #endif
2478         if (the_zone)
2479                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2480         else
2481                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2482 #if 0
2483         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2484 #endif
2485         return res;
2486 }
2487
2488 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2489 {
2490         int res = 0;
2491         vms->starting = 0; 
2492         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2493         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2494         if (!msg)
2495                 res = wait_file2(chan, vms, "vm-first");
2496         else if (msg == vms->lastmsg)
2497                 res = wait_file2(chan, vms, "vm-last");
2498         if (!res) {
2499                 res = wait_file2(chan, vms, "vm-message");
2500                 if (msg && (msg != vms->lastmsg)) {
2501                         if (!res)
2502                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2503                 }
2504         }
2505
2506         if (!res)
2507                 res = play_message_datetime(chan,vmu,vms);
2508
2509         if (!res) {
2510                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2511                 vms->heard[msg] = 1;
2512                 res = wait_file(chan, vms, vms->fn);
2513         }
2514         return res;
2515 }
2516
2517 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2518 {
2519         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2520         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2521         vms->lastmsg = count_messages(vms->curdir) - 1;
2522         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2523 }
2524
2525 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2526 {
2527         int x;
2528         char ntxt[256] = "";
2529         char txt[256] = "";
2530         if (vms->lastmsg > -1) { 
2531                 /* Get the deleted messages fixed */ 
2532                 vms->curmsg = -1; 
2533                 for (x=0;x < MAXMSG;x++) { 
2534                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2535                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2536                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2537                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2538                                         break;
2539                                 vms->curmsg++; 
2540                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2541                                 if (strcmp(vms->fn, vms->fn2)) { 
2542                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2543                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2544                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2545                                         rename(txt, ntxt); 
2546                                 } 
2547                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2548                                 /* Move to old folder before deleting */ 
2549                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2550                         } 
2551                 } 
2552                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2553                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2554                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2555                                 break;
2556                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2557                         ast_filedelete(vms->fn, NULL); 
2558                         unlink(txt); 
2559                 } 
2560         } 
2561         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2562         memset(vms->heard, 0, sizeof(vms->heard)); 
2563 }
2564
2565 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2566 {
2567         /* Introduce messages they have */
2568         int res;
2569         res = play_and_wait(chan, "vm-youhave");
2570         if (!res) {
2571                 if (vms->newmessages) {
2572                         res = say_and_wait(chan, vms->newmessages);
2573                         if (!res)
2574                                 res = play_and_wait(chan, "vm-INBOX");
2575                         if (vms->oldmessages && !res)
2576                                 res = play_and_wait(chan, "vm-and");
2577                         else if (!res) {
2578                                 if ((vms->newmessages == 1))
2579                                         res = play_and_wait(chan, "vm-message");
2580                                 else
2581                                         res = play_and_wait(chan, "vm-messages");
2582                         }
2583                                 
2584                 }
2585                 if (!res && vms->oldmessages) {
2586                         res = say_and_wait(chan, vms->oldmessages);
2587                         if (!res)
2588                                 res = play_and_wait(chan, "vm-Old");
2589                         if (!res) {
2590                                 if (vms->oldmessages == 1)
2591                                         res = play_and_wait(chan, "vm-message");
2592                                 else
2593                                         res = play_and_wait(chan, "vm-messages");
2594                         }
2595                 }
2596                 if (!res) {
2597                         if (!vms->oldmessages && !vms->newmessages) {
2598                                 res = play_and_wait(chan, "vm-no");
2599                                 if (!res)
2600                                         res = play_and_wait(chan, "vm-messages");
2601                         }
2602                 }
2603         }
2604         return res;
2605 }
2606
2607 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2608 {
2609         int res = 0;
2610         /* Play instructions and wait for new command */
2611         while(!res) {
2612                 if (vms->starting) {
2613                         if (vms->lastmsg > -1) {
2614                                 res = play_and_wait(chan, "vm-onefor");
2615                                 if (!res)
2616                                         res = play_and_wait(chan, vms->vmbox);
2617                                 if (!res)
2618                                         res = play_and_wait(chan, "vm-messages");
2619                         }
2620                         if (!res)
2621                                 res = play_and_wait(chan, "vm-opts");
2622                 } else {
2623                         if (vms->curmsg)
2624                                 res = play_and_wait(chan, "vm-prev");
2625                         if (!res)
2626                                 res = play_and_wait(chan, "vm-repeat");
2627                         if (!res && (vms->curmsg != vms->lastmsg))
2628                                 res = play_and_wait(chan, "vm-next");
2629                         if (!res) {
2630                                 if (!vms->deleted[vms->curmsg])
2631                                         res = play_and_wait(chan, "vm-delete");
2632                                 else
2633                                         res = play_and_wait(chan, "vm-undelete");
2634                                 if (!res)
2635                                         res = play_and_wait(chan, "vm-toforward");
2636                                 if (!res)
2637                                         res = play_and_wait(chan, "vm-savemessage");
2638                         }
2639                 }
2640                 if (!res)
2641                         res = play_and_wait(chan, "vm-helpexit");
2642                 if (!res)
2643                         res = ast_waitfordigit(chan, 6000);
2644                 if (!res) {
2645                         vms->repeats++;
2646                         if (vms->repeats > 2) {
2647                                 res = 't';
2648                         }
2649                 }
2650         }
2651         return res;
2652 }
2653
2654 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2655 {
2656         int cmd = 0;
2657         int retries = 0;
2658         char newpassword[80] = "";
2659         char newpassword2[80] = "";
2660         char prefile[256]="";
2661         char buf[256];
2662         int bytes=0;
2663
2664         if (adsi_available(chan))
2665         {
2666                 bytes += adsi_logo(buf + bytes);
2667                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2668                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2669                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2670                 bytes += adsi_voice_mode(buf + bytes, 0);
2671                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2672         }
2673         while((cmd >= 0) && (cmd != 't')) {
2674                 if (cmd)
2675                         retries = 0;
2676                 switch (cmd) {
2677                 case '1':
2678                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2679                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2680                         break;
2681                 case '2': 
2682                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2683                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2684                         break;
2685                 case '3': 
2686                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2687                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2688                         break;
2689                 case '4':
2690                         newpassword[1] = '\0';
2691                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2692                         if (cmd < 0)
2693                                 break;
2694                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2695                                 break;
2696             }
2697                         newpassword2[1] = '\0';
2698                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2699                         if (cmd < 0)
2700                                 break;
2701
2702                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2703                                 break;
2704             }
2705                         if (strcmp(newpassword, newpassword2)) {
2706                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2707                                 cmd = play_and_wait(chan, "vm-mismatch");
2708                                 break;
2709                         }
2710                         vm_change_password(vmu,newpassword);
2711                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2712                         cmd = play_and_wait(chan,"vm-passchanged");
2713                         break;
2714                 case '*': 
2715                         cmd = 't';
2716                         break;
2717                 default: 
2718                         cmd = play_and_wait(chan,"vm-options");
2719                         if (!cmd)
2720                                 cmd = ast_waitfordigit(chan,6000);
2721                         if (!cmd)
2722                                 retries++;
2723                         if (retries > 3)
2724                                 cmd = 't';
2725                  }
2726         }
2727         if (cmd == 't')
2728                 cmd = 0;
2729         return cmd;
2730 }
2731
2732 static int vm_execmain(struct ast_channel *chan, void *data)
2733 {
2734         /* XXX This is, admittedly, some pretty horrendus code.  For some
2735            reason it just seemed a lot easier to do with GOTO's.  I feel
2736            like I'm back in my GWBASIC days. XXX */
2737         int res=-1;
2738         int valid = 0;
2739         int prefix = 0;
2740         int cmd=0;
2741         struct localuser *u;
2742         char prefixstr[80] ="";
2743         char empty[80] = "";
2744         int box;
2745         int useadsi = 0;
2746         int skipuser = 0;
2747         char tmp[256], *ext;
2748         char fmtc[256] = "";
2749         char password[80];
2750         struct vm_state vms;
2751         int logretries = 0;
2752         struct ast_vm_user *vmu = NULL, vmus;
2753         char *context=NULL;
2754
2755         LOCAL_USER_ADD(u);
2756         memset(&vms, 0, sizeof(vms));
2757         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2758         if (chan->_state != AST_STATE_UP)
2759                 ast_answer(chan);
2760
2761         if (data && strlen(data)) {
2762                 strncpy(tmp, data, sizeof(tmp) - 1);
2763                 ext = tmp;
2764
2765                 switch (*ext) {
2766                         case 's':
2767                  /* We should skip the user's password */
2768                                 valid++;
2769                                 ext++;
2770                                 break;
2771                         case 'p':
2772                  /* We should prefix the mailbox with the supplied data */
2773                                 prefix++;
2774                                 ext++;
2775                                 break;
2776                 }
2777
2778                 context = strchr(ext, '@');
2779                 if (context) {
2780                         *context = '\0';
2781                         context++;
2782                 }
2783
2784                 if (prefix)
2785                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2786                 else
2787                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2788                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2789                         skipuser++;
2790                 else
2791                         valid = 0;
2792
2793         }
2794
2795         /* If ADSI is supported, setup login screen */
2796         adsi_begin(chan, &useadsi);
2797         if (!skipuser && useadsi)
2798                 adsi_login(chan);
2799         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2800                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2801                 goto out;
2802         }
2803         
2804         /* Authenticate them and get their mailbox/password */
2805         
2806         while (!valid && (logretries < maxlogins)) {
2807                 /* Prompt for, and read in the username */
2808                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2809                         ast_log(LOG_WARNING, "Couldn't read username\n");
2810                         goto out;
2811                 }
2812                 if (!strlen(vms.username)) {
2813                         if (option_verbose > 2)
2814                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2815                         res = 0;
2816                         goto out;
2817                 }
2818                 if (useadsi)
2819                         adsi_password(chan);
2820                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2821                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2822                         goto out;
2823                 }
2824                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2825                         ast_log(LOG_WARNING, "Unable to read password\n");
2826                         goto out;
2827                 }
2828                 if (prefix) {
2829                         char fullusername[80] = "";
2830                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2831                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2832                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2833                 }
2834                 if (!skipuser) 
2835                         vmu = find_user(&vmus, context, vms.username);
2836                 if (vmu && !strcmp(vmu->password, password)) 
2837                         valid++;
2838                 else {
2839                         if (option_verbose > 2)
2840                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2841                         if (prefix)
2842                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2843                 }
2844                 if (!valid) {
2845                         if (useadsi)
2846                                 adsi_login(chan);
2847                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2848                                 break;
2849                 }
2850                 logretries++;
2851         }
2852         if (!valid && (logretries >= maxlogins)) {
2853                 ast_stopstream(chan);
2854                 res = play_and_wait(chan, "vm-goodbye");
2855                 if (res > 0)
2856                         res = 0;
2857         }
2858
2859         if (valid) {
2860                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2861                 mkdir(vms.curdir, 0700);
2862                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2863                 mkdir(vms.curdir, 0700);
2864                 /* Retrieve old and new message counts */
2865                 open_mailbox(&vms, vmu, 1);
2866                 vms.oldmessages = vms.lastmsg + 1;
2867                 /* Start in INBOX */
2868                 open_mailbox(&vms, vmu, 0);
2869                 vms.newmessages = vms.lastmsg + 1;
2870                 
2871
2872                 /* Select proper mailbox FIRST!! */
2873                 if (!vms.newmessages && vms.oldmessages) {
2874                         /* If we only have old messages start here */
2875                         open_mailbox(&vms, vmu, 1);
2876                 }
2877
2878                 if (useadsi)
2879                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2880                 res = 0;
2881                 cmd = vm_intro(chan, &vms);
2882                 vms.repeats = 0;
2883                 vms.starting = 1;
2884                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2885                         /* Run main menu */
2886                         switch(cmd) {
2887                         case '1':
2888                                 vms.curmsg = 0;
2889                                 /* Fall through */
2890                         case '5':
2891                                 if (vms.lastmsg > -1) {
2892                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2893                                 } else {
2894                                         cmd = play_and_wait(chan, "vm-youhave");
2895                                         if (!cmd) 
2896                                                 cmd = play_and_wait(chan, "vm-no");
2897                                         if (!cmd) {
2898                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2899                                                 cmd = play_and_wait(chan, vms.fn);
2900                                         }
2901                                         if (!cmd)
2902                                                 cmd = play_and_wait(chan, "vm-messages");
2903                                 }
2904                                 break;
2905                         case '2': /* Change folders */
2906                                 if (useadsi)
2907                                         adsi_folders(chan, 0, "Change to folder...");
2908                                 cmd = get_folder2(chan, "vm-changeto", 0);
2909                                 if (cmd == '#') {
2910                                         cmd = 0;
2911                                 } else if (cmd > 0) {
2912                                         cmd = cmd - '0';
2913                                         close_mailbox(&vms, vmu);
2914                                         open_mailbox(&vms, vmu, cmd);
2915                                         cmd = 0;
2916                                 }
2917                                 if (useadsi)
2918                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2919                                 if (!cmd)
2920                                         cmd = play_and_wait(chan, vms.vmbox);
2921                                 if (!cmd)
2922                                         cmd = play_and_wait(chan, "vm-messages");
2923                                 vms.starting = 1;
2924                                 break;
2925                         case '4':
2926                                 if (vms.curmsg) {
2927                                         vms.curmsg--;
2928                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2929                                 } else {
2930                                         cmd = play_and_wait(chan, "vm-nomore");
2931                                 }
2932                                 break;
2933                         case '6':
2934                                 if (vms.curmsg < vms.lastmsg) {
2935                                         vms.curmsg++;
2936                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2937                                 } else {
2938                                         cmd = play_and_wait(chan, "vm-nomore");
2939                                 }
2940                                 break;
2941                         case '7':
2942                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2943                                 if (useadsi)
2944                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2945                                 if (vms.deleted[vms.curmsg]) 
2946                                         cmd = play_and_wait(chan, "vm-deleted");
2947                                 else
2948                                         cmd = play_and_wait(chan, "vm-undeleted");
2949                                 break;
2950                         case '8':
2951                                 if(vms.lastmsg > -1)
2952                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2953                                 else
2954                                         cmd = play_and_wait(chan, "vm-nomore");
2955                                 break;
2956                         case '9':
2957                                 if (useadsi)
2958                                         adsi_folders(chan, 1, "Save to folder...");
2959                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2960                                 box = 0;        /* Shut up compiler */
2961                                 if (cmd == '#') {
2962                                         cmd = 0;
2963                                         break;
2964                                 } else if (cmd > 0) {
2965                                         box = cmd = cmd - '0';
2966                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2967                                         vms.deleted[vms.curmsg]=1;
2968                                 }
2969                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2970                                 if (useadsi)
2971                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2972                                 if (!cmd)
2973                                         cmd = play_and_wait(chan, "vm-message");
2974                                 if (!cmd)
2975                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2976                                 if (!cmd)
2977                                         cmd = play_and_wait(chan, "vm-savedto");
2978                                 if (!cmd) {
2979                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2980                                         cmd = play_and_wait(chan, vms.fn);
2981                                 }
2982                                 if (!cmd)
2983                                         cmd = play_and_wait(chan, "vm-messages");
2984                                 break;
2985                         case '*':
2986                                 if (!vms.starting) {
2987                                         cmd = play_and_wait(chan, "vm-onefor");
2988                                         if (!cmd)
2989                                                 cmd = play_and_wait(chan, vms.vmbox);
2990                                         if (!cmd)
2991                                                 cmd = play_and_wait(chan, "vm-messages");
2992                                         if (!cmd)
2993                                                 cmd = play_and_wait(chan, "vm-opts");
2994                                 } else
2995                                         cmd = 0;
2996                                 break;
2997                         case '0':
2998                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2999                                 if (useadsi)
3000                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
3001                                 break;
3002                         default:        /* Nothing */
3003                                 cmd = vm_instructions(chan, &vms);
3004                                 break;
3005                         }
3006                 }
3007                 if ((cmd == 't') || (cmd == '#')) {
3008                         /* Timeout */
3009                         res = 0;
3010                 } else {
3011                         /* Hangup */
3012                         res = -1;
3013                 }
3014         }
3015 out:
3016         if (res > -1) {
3017                 ast_stopstream(chan);
3018                 adsi_goodbye(chan);
3019                 if(valid) {
3020                         res = play_and_wait(chan, "vm-goodbye");
3021                         if (res > 0)
3022                                 res = 0;
3023                 }
3024                 if (useadsi)
3025                         adsi_unload_session(chan);
3026         }
3027         if (vmu)
3028                 close_mailbox(&vms, vmu);
3029         if (vmu)
3030                 free_user(vmu);
3031         if (valid) {
3032                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
3033                 run_externnotify(chan->context, vms.username, ast_app_has_voicemail(vms.username));
3034
3035         }
3036         LOCAL_USER_REMOVE(u);
3037
3038         return res;
3039
3040 }
3041
3042 static int vm_exec(struct ast_channel *chan, void *data)
3043 {
3044         int res=0, silent=0, busy=0, unavail=0;
3045         struct localuser *u;
3046         char tmp[256], *ext;
3047         
3048         LOCAL_USER_ADD(u);
3049         if (chan->_state != AST_STATE_UP)
3050                 ast_answer(chan);
3051         if (data && strlen(data))
3052                 strncpy(tmp, data, sizeof(tmp) - 1);
3053         else {
3054                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
3055                 if (res < 0)
3056                         return res;
3057                 if (!strlen(tmp))
3058                         return 0;
3059         }
3060         ext = tmp;
3061         while(*ext) {
3062                 if (*ext == 's') {
3063                         silent = 2;
3064                         ext++;
3065                 } else if (*ext == 'b') {
3066                         busy=1;
3067                         ext++;
3068                 } else if (*ext == 'u') {
3069                         unavail=1;
3070                         ext++;
3071                 } else 
3072                         break;
3073         }
3074         res = leave_voicemail(chan, ext, silent, busy, unavail);
3075         LOCAL_USER_REMOVE(u);
3076         return res;
3077 }
3078
3079 static int append_mailbox(char *context, char *mbox, char *data)
3080 {
3081         /* Assumes lock is already held */
3082         char tmp[256] = "";
3083         char *stringp;
3084         char *s;
3085         struct ast_vm_user *vmu;
3086         strncpy(tmp, data, sizeof(tmp) - 1);
3087         vmu = malloc(sizeof(struct ast_vm_user));
3088         if (vmu) {
3089                 memset(vmu, 0, sizeof(struct ast_vm_user));
3090                 strncpy(vmu->context, context, sizeof(vmu->context) - 1);
3091                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox) - 1);
3092                 vmu->attach = -1;
3093                 stringp = tmp;
3094                 if ((s = strsep(&stringp, ","))) 
3095                         strncpy(vmu->password, s, sizeof(vmu->password) - 1);
3096                 if (stringp && (s = strsep(&stringp, ","))) 
3097                         strncpy(vmu->fullname, s, sizeof(vmu->fullname) - 1);
3098                 if (stringp && (s = strsep(&stringp, ","))) 
3099                         strncpy(vmu->email, s, sizeof(vmu->email) - 1);
3100                 if (stringp && (s = strsep(&stringp, ","))) 
3101                         strncpy(vmu->pager, s, sizeof(vmu->pager) - 1);
3102                 if (stringp && (s = strsep(&stringp, ","))) 
3103                         apply_options(vmu, s);
3104                 vmu->next = NULL;
3105                 if (usersl)
3106                         usersl->next = vmu;
3107                 else
3108                         users = vmu;
3109                 usersl = vmu;
3110         }
3111         return 0;
3112 }
3113
3114 static int vm_box_exists(struct ast_channel *chan, void *data) {
3115         struct localuser *u;
3116         struct ast_vm_user *user;
3117         char *context, *box;
3118         int branch=0;
3119
3120         if (!data) {
3121                 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context])\n");
3122                 return -1;
3123         }
3124
3125         LOCAL_USER_ADD(u);
3126         context = ast_strdupa(data);
3127         if (index(context, '@')) {
3128                 box = strsep(&context, "@");
3129         } else {
3130                 box = context;
3131                 context = "default";
3132         }
3133
3134         ast_mutex_lock(&vmlock);
3135         user = users;
3136         while (user) {
3137                 if ((!strcmp(box,user->mailbox)) && (!strcmp(context,user->context))) {
3138                         branch = 1;
3139                         break;
3140                 }
3141         }
3142         ast_mutex_unlock(&vmlock);
3143
3144         if (branch) {
3145                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid)) {
3146                         chan->priority += 100;
3147                 } else
3148                         ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
3149         }
3150         LOCAL_USER_REMOVE(u);
3151         return 0;
3152 }
3153
3154
3155 #ifndef USEMYSQLVM
3156 /* XXX TL Bug 690 */
3157 static char show_voicemail_users_help[] =
3158 "Usage: show voicemail users [for <context>]\n"
3159 "       Lists all mailboxes currently set up\n";
3160
3161 static char show_voicemail_zones_help[] =
3162 "Usage: show voicemail zones\n"
3163 "       Lists zone message formats\n";
3164
3165 static int handle_show_voicemail_users(int fd, int argc, char *argv[])
3166 {
3167         struct ast_vm_user *vmu = users;
3168         char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
3169
3170         if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
3171         else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
3172
3173         if (vmu) {
3174                 if (argc == 3)
3175                         ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
3176                 else {
3177                         int count = 0;
3178                         while (vmu) {
3179                                 if (!strcmp(argv[4],vmu->context))
3180                                         count++;
3181                                 vmu = vmu->next;
3182                         }
3183                         if (count) {
3184                                 vmu = users;
3185                                 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
3186                         } else {
3187                                 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
3188                                 return RESULT_FAILURE;
3189                         }
3190                 }
3191                 while (vmu) {
3192                         char dirname[256];
3193                         DIR *vmdir;
3194                         struct dirent *vment;
3195                         int vmcount = 0;
3196                         char count[12];
3197
3198                         if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
3199                                 make_dir(dirname, 255, vmu->context, vmu->mailbox, "INBOX");
3200                                 if ((vmdir = opendir(dirname))) {
3201                                         /* No matter what the format of VM, there will always be a .txt file for each message. */
3202                                         while ((vment = readdir(vmdir)))
3203                                                 if (!strncmp(vment->d_name + 7,".txt",4))
3204                                                         vmcount++;
3205                                         closedir(vmdir);
3206                                 }
3207                                 snprintf(count,11,"%d",vmcount);
3208                                 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
3209                         }
3210                         vmu = vmu->next;
3211                 }
3212         } else {
3213                 ast_cli(fd, "There are no voicemail users currently defined\n");
3214                 return RESULT_FAILURE;
3215         }
3216         return RESULT_SUCCESS;
3217 }
3218
3219 static int handle_show_voicemail_zones(int fd, int argc, char *argv[])
3220 {
3221         struct vm_zone *zone = zones;
3222         char *output_format = "%-15s %-20s %-45s\n";
3223
3224         if (argc != 3) return RESULT_SHOWUSAGE;
3225
3226         if (zone) {
3227                 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
3228                 while (zone) {
3229                         ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
3230                         zone = zone->next;
3231                 }
3232         } else {
3233                 ast_cli(fd, "There are no voicemail zones currently defined\n");
3234                 return RESULT_FAILURE;
3235         }
3236         return RESULT_SUCCESS;
3237 }
3238
3239 static char *complete_show_voicemail_users(char *line, char *word, int pos, int state)
3240 {
3241         int which = 0;
3242         struct ast_vm_user *vmu = users;
3243         char *context = "";
3244
3245         /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
3246         if (pos > 4)
3247                 return NULL;
3248         if (pos == 3) {
3249                 if (state == 0)
3250                         return strdup("for");
3251                 else
3252                         return NULL;
3253         }
3254         while (vmu) {
3255                 if (!strncasecmp(word, vmu->context, strlen(word))) {
3256                         if (context && strcmp(context, vmu->context)) {
3257                                 if (++which > state) {
3258                                         return strdup(vmu->context);
3259                                 }
3260                                 context = vmu->context;
3261                         }
3262                 }
3263                 vmu = vmu->next;
3264         }
3265         return NULL;
3266 }
3267
3268 static struct ast_cli_entry show_voicemail_users_cli =
3269         { { "show", "voicemail", "users", NULL },
3270         handle_show_voicemail_users, "List defined voicemail boxes",
3271         show_voicemail_users_help, complete_show_voicemail_users };
3272
3273 static struct ast_cli_entry show_voicemail_zones_cli =
3274         { { "show", "voicemail", "zones", NULL },
3275         handle_show_voicemail_zones, "List zone message formats",
3276         show_voicemail_zones_help, NULL };
3277
3278 #endif
3279
3280 static int load_config(void)
3281 {
3282         struct ast_vm_user *cur, *l;
3283         struct vm_zone *zcur, *zl;
3284         struct ast_config *cfg;
3285         char *cat;
3286         struct ast_variable *var;
3287         char *notifystr = NULL;
3288         char *astattach;
3289         char *silencestr;
3290         char *thresholdstr;
3291         char *fmt;
3292         char *astemail;
3293         char *astmailcmd = SENDMAIL;
3294         char *s;
3295
3296         int x;
3297
3298         cfg = ast_load(VOICEMAIL_CONFIG);
3299         ast_mutex_lock(&vmlock);
3300         cur = users;
3301         while(cur) {
3302                 l = cur;
3303                 cur = cur->next;
3304                 l->alloced = 1;
3305                 free_user(l);
3306         }
3307         zcur = zones;
3308         while(zcur) {
3309                 zl = zcur;
3310                 zcur = zcur->next;
3311                 free_zone(zl);
3312         }
3313         zones = NULL;
3314         zonesl = NULL;
3315         users = NULL;
3316         usersl = NULL;
3317         if (cfg) {
3318                 /* General settings */
3319
3320                 /* Attach voice message to mail message ? */
3321                 attach_voicemail = 1;
3322                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
3323                         astattach = "yes";
3324                 attach_voicemail = ast_true(astattach);
3325
3326                 /* Mail command */
3327                 strncpy(mailcmd, SENDMAIL, sizeof(mailcmd) - 1); /* Default */
3328                 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
3329                    strncpy(mailcmd, astmailcmd, sizeof(mailcmd) - 1); /* User setting */
3330
3331                 maxsilence = 0;
3332                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
3333                         maxsilence = atoi(silencestr);
3334                         if (maxsilence > 0)
3335                                 maxsilence *= 1000;
3336                 }
3337                 
3338                 /* External voicemail notify application */
3339                 
3340                 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
3341                         strncpy(externnotify, notifystr, sizeof(externnotify) - 1);
3342                         ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
3343                 } else {
3344                         externnotify[0] = '\0';
3345                 }
3346
3347                 /* Silence treshold */
3348                 silencethreshold = 256;
3349                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
3350                         silencethreshold = atoi(thresholdstr);
3351                 
3352                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
3353                         astemail = ASTERISK_USERNAME;
3354                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
3355                 
3356                 vmmaxmessage = 0;
3357                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
3358                         if (sscanf(s, "%d", &x) == 1) {
3359                                 vmmaxmessage = x;
3360                         } else {
3361                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
3362                         }
3363                 }
3364
3365                 vmminmessage = 0;
3366                 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
3367                         if (sscanf(s, "%d", &x) == 1) {
3368                                 vmminmessage = x;
3369                         } else {
3370                                 ast_log(LOG_WARNING, "Invalid min message time length\n");
3371                         }
3372                 }
3373                 fmt = ast_variable_retrieve(cfg, "general", "format");
3374               &nbs