Hold the lock properly on async goto now that we have recursive mutex support.
[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 int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1376 {
1377         char comment[256];
1378         char txtfile[256];
1379         FILE *txt;
1380         int res = 0;
1381         int msgnum;
1382         int fd;
1383         char date[256];
1384         char dir[256];
1385         char fn[256];
1386         char prefile[256]="";
1387         char fmt[80];
1388         char *context;
1389         char *ecodes = "#";
1390         char *stringp;
1391         time_t start;
1392         time_t end;
1393         char tmp[256] = "";
1394         struct ast_vm_user *vmu;
1395         struct ast_vm_user svm;
1396         
1397         strncpy(tmp, ext, sizeof(tmp) - 1);
1398         ext = tmp;
1399         context = strchr(tmp, '@');
1400         if (context) {
1401                 *context = '\0';
1402                 context++;
1403         }
1404
1405         if ((vmu = find_user(&svm, context, ext))) {
1406                 /* Setup pre-file if appropriate */
1407                 if (busy)
1408                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1409                 else if (unavail)
1410                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1411                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1412                 /* It's easier just to try to make it than to check for its existence */
1413                 if (mkdir(dir, 0700) && (errno != EEXIST))
1414                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1415                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1416                 /* It's easier just to try to make it than to check for its existence */
1417                 if (mkdir(dir, 0700) && (errno != EEXIST))
1418                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1419                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1420                 if (mkdir(dir, 0700) && (errno != EEXIST))
1421                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1422                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1423                         ecodes = "#0";
1424                 /* Play the beginning intro if desired */
1425                 if (strlen(prefile)) {
1426                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1427                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1428                                     res = ast_waitstream(chan, "#0");
1429                         } else {
1430                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1431                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1432                         }
1433                         if (res < 0) {
1434                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1435                                 free_user(vmu);
1436                                 return -1;
1437                         }
1438                 }
1439                 if (res == '#') {
1440                         /* On a '#' we skip the instructions */
1441                         silent = 1;
1442                         res = 0;
1443                 }
1444                 if (!res && !silent) {
1445                         res = ast_streamfile(chan, INTRO, chan->language);
1446                         if (!res)
1447                                 res = ast_waitstream(chan, ecodes);
1448                         if (res == '#') {
1449                                 silent = 1;
1450                                 res = 0;
1451                         }
1452                 }
1453                 /* Check for a '0' here */
1454                 if (res == '0') {
1455                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1456                         if (strlen(chan->macrocontext))
1457                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1458                         chan->priority = 0;
1459                         free_user(vmu);
1460                         return 0;
1461                 }
1462                 if (res >= 0) {
1463                         /* Unless we're *really* silent, try to send the beep */
1464                         res = ast_streamfile(chan, "beep", chan->language);
1465                         if (!res)
1466                                 res = ast_waitstream(chan, "");
1467                 }
1468                 if (res < 0) {
1469                         free_user(vmu);
1470                         return -1;
1471                 }
1472                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1473                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1474                 if (strlen(fmt)) {
1475                         msgnum = 0;
1476                         do {
1477                                 make_file(fn, sizeof(fn), dir, msgnum);
1478                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1479                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1480                                                                         vmu->fullname, ext, chan->name);
1481                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1482                                         break;
1483                                 msgnum++;
1484                         } while(msgnum < MAXMSG);
1485                         if (msgnum < MAXMSG) {
1486                                 /* Store information */
1487                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1488                                 txt = fopen(txtfile, "w+");
1489                                 if (txt) {
1490                                         get_date(date, sizeof(date));
1491                                         time(&start);
1492                                         fprintf(txt, 
1493 ";\n"
1494 "; Message Information file\n"
1495 ";\n"
1496 "[message]\n"
1497 "origmailbox=%s\n"
1498 "context=%s\n"
1499 "exten=%s\n"
1500 "priority=%d\n"
1501 "callerchan=%s\n"
1502 "callerid=%s\n"
1503 "origdate=%s\n"
1504 "origtime=%ld\n",
1505         ext,
1506         chan->context,
1507         chan->exten,
1508         chan->priority,
1509         chan->name,
1510         chan->callerid ? chan->callerid : "Unknown",
1511         date, (long)time(NULL));
1512                                         fclose(txt);
1513                                 } else
1514                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1515                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1516                                 if (res > 0)
1517                                         res = 0;
1518                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1519                                 if (fd > -1) {
1520                                         txt = fdopen(fd, "a");
1521                                         if (txt) {
1522                                                 time(&end);
1523                                                 fprintf(txt, "duration=%ld\n", (long)(end-start));
1524                                                 fclose(txt);
1525                                         } else
1526                                                 close(fd);
1527                                 }
1528                                 if (end - start < vmminmessage) {
1529                                         ast_filedelete(fn, NULL);
1530                                         goto leave_vm_out;
1531                                 }
1532                                 stringp = fmt;
1533                                 strsep(&stringp, "|");
1534                                 /* Send e-mail if applicable */
1535                                 if (strlen(vmu->email)) {
1536                                         int attach_user_voicemail = attach_voicemail;
1537                                         char *myserveremail = serveremail;
1538                                         if (vmu->attach > -1)
1539                                                 attach_user_voicemail = vmu->attach;
1540                                         if (strlen(vmu->serveremail))
1541                                                 myserveremail = vmu->serveremail;
1542                                         sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1543                                 }
1544                                 if (strlen(vmu->pager)) {
1545                                         char *myserveremail = serveremail;
1546                                         if (strlen(vmu->serveremail))
1547                                                 myserveremail = vmu->serveremail;
1548                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1549                                 }
1550                         } else {
1551                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1552                                 if (!res)
1553                                         res = ast_waitstream(chan, "");
1554                                 ast_log(LOG_WARNING, "No more messages possible\n");
1555                         }
1556                 } else
1557                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1558 leave_vm_out:
1559                 free_user(vmu);
1560         } else {
1561                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1562                         /*Send the call to n+101 priority, where n is the current priority*/
1563                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1564                                 chan->priority+=100;
1565         }
1566         /* Leave voicemail for someone */
1567         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1568
1569         /* If an external program is specified to be run after leaving a voicemail */
1570         if (externnotify[0]) {
1571                 char arguments[255];
1572                 ast_log(LOG_DEBUG, "App voicemail ran the external notify program\n");
1573                 strncpy(tmp, externnotify, sizeof(tmp));
1574                 snprintf(arguments, sizeof(arguments)-1, " %s %s %d&", chan->context, ext, ast_app_has_voicemail(ext));
1575                 strncat(tmp, arguments, sizeof(tmp) - strlen(arguments));
1576                 tmp[sizeof(tmp) - 1] = '\0';
1577 //              ast_verbose(VERBOSE_PREFIX_3,"Executing %s\n", tmp);
1578                 ast_safe_system(tmp);
1579         }
1580
1581         return res;
1582 }
1583
1584 static char *mbox(int id)
1585 {
1586         switch(id) {
1587         case 0:
1588                 return "INBOX";
1589         case 1:
1590                 return "Old";
1591         case 2:
1592                 return "Work";
1593         case 3:
1594                 return "Family";
1595         case 4:
1596                 return "Friends";
1597         case 5:
1598                 return "Cust1";
1599         case 6:
1600                 return "Cust2";
1601         case 7:
1602                 return "Cust3";
1603         case 8:
1604                 return "Cust4";
1605         case 9:
1606                 return "Cust5";
1607         default:
1608                 return "Unknown";
1609         }
1610 }
1611
1612 static int count_messages(char *dir)
1613 {
1614         int x;
1615         char fn[256];
1616         for (x=0;x<MAXMSG;x++) {
1617                 make_file(fn, sizeof(fn), dir, x);
1618                 if (ast_fileexists(fn, NULL, NULL) < 1)
1619                         break;
1620         }
1621         return x;
1622 }
1623
1624 static int say_and_wait(struct ast_channel *chan, int num)
1625 {
1626         int d;
1627         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1628         return d;
1629 }
1630
1631 static int copy(char *infile, char *outfile)
1632 {
1633         int ifd;
1634         int ofd;
1635         int res;
1636         int len;
1637         char buf[4096];
1638         if ((ifd = open(infile, O_RDONLY)) < 0) {
1639                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1640                 return -1;
1641         }
1642         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1643                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1644                 close(ifd);
1645                 return -1;
1646         }
1647         do {
1648                 len = read(ifd, buf, sizeof(buf));
1649                 if (len < 0) {
1650                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1651                         close(ifd);
1652                         close(ofd);
1653                         unlink(outfile);
1654                 }
1655                 if (len) {
1656                         res = write(ofd, buf, len);
1657                         if (res != len) {
1658                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1659                                 close(ifd);
1660                                 close(ofd);
1661                                 unlink(outfile);
1662                         }
1663                 }
1664         } while(len);
1665         close(ifd);
1666         close(ofd);
1667         return 0;
1668 }
1669
1670 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1671 {
1672         char sfn[256];
1673         char dfn[256];
1674         char ddir[256];
1675         char txt[256];
1676         char ntxt[256];
1677         char *dbox = mbox(box);
1678         int x;
1679         make_file(sfn, sizeof(sfn), dir, msg);
1680         make_dir(ddir, sizeof(ddir), context, username, dbox);
1681         mkdir(ddir, 0700);
1682         for (x=0;x<MAXMSG;x++) {
1683                 make_file(dfn, sizeof(dfn), ddir, x);
1684                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1685                         break;
1686         }
1687         if (x >= MAXMSG)
1688                 return -1;
1689         ast_filecopy(sfn, dfn, NULL);
1690         if (strcmp(sfn, dfn)) {
1691                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1692                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1693                 copy(txt, ntxt);
1694         }
1695         return 0;
1696 }
1697
1698 static int adsi_logo(unsigned char *buf)
1699 {
1700         int bytes = 0;
1701         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1702         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1703         return bytes;
1704 }
1705
1706 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1707 {
1708         char buf[256];
1709         int bytes=0;
1710         int x;
1711         char num[5];
1712
1713         *useadsi = 0;
1714         bytes += adsi_data_mode(buf + bytes);
1715         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1716
1717         bytes = 0;
1718         bytes += adsi_logo(buf);
1719         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1720 #ifdef DISPLAY
1721         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1722 #endif
1723         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1724         bytes += adsi_data_mode(buf + bytes);
1725         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1726
1727         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1728                 bytes = 0;
1729                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1730                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1731                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1732                 bytes += adsi_voice_mode(buf + bytes, 0);
1733                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1734                 return 0;
1735         }
1736
1737 #ifdef DISPLAY
1738         /* Add a dot */
1739         bytes = 0;
1740         bytes += adsi_logo(buf);
1741         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1742         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1743         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1744         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1745 #endif
1746         bytes = 0;
1747         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1748         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1749         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1750         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1751         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1752         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1753         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1754
1755 #ifdef DISPLAY
1756         /* Add another dot */
1757         bytes = 0;
1758         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1759       bytes += adsi_voice_mode(buf + bytes, 0);
1760
1761         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1762         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1763 #endif
1764
1765         bytes = 0;
1766         /* These buttons we load but don't use yet */
1767         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1768         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1769         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1770         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1771         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1772         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1773         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1774
1775 #ifdef DISPLAY
1776         /* Add another dot */
1777         bytes = 0;
1778         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1779         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1780         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1781 #endif
1782
1783         bytes = 0;
1784         for (x=0;x<5;x++) {
1785                 snprintf(num, sizeof(num), "%d", x);
1786                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1787         }
1788         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1789         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1790
1791 #ifdef DISPLAY
1792         /* Add another dot */
1793         bytes = 0;
1794         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1795         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1796         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1797 #endif
1798
1799         if (adsi_end_download(chan)) {
1800                 bytes = 0;
1801                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1802                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1803                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1804                 bytes += adsi_voice_mode(buf + bytes, 0);
1805                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1806                 return 0;
1807         }
1808         bytes = 0;
1809         bytes += adsi_download_disconnect(buf + bytes);
1810         bytes += adsi_voice_mode(buf + bytes, 0);
1811         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1812
1813         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1814
1815 #ifdef DISPLAY
1816         /* Add last dot */
1817         bytes = 0;
1818         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1819         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1820 #endif
1821         ast_log(LOG_DEBUG, "Restarting session...\n");
1822
1823         bytes = 0;
1824         /* Load the session now */
1825         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1826                 *useadsi = 1;
1827                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1828         } else
1829                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1830
1831         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1832         return 0;
1833 }
1834
1835 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1836 {
1837         int x;
1838         if (!adsi_available(chan))
1839           return;
1840         x = adsi_load_session(chan, adapp, adver, 1);
1841         if (x < 0)
1842                 return;
1843         if (!x) {
1844                 if (adsi_load_vmail(chan, useadsi)) {
1845                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1846                         return;
1847                 }
1848         } else
1849                 *useadsi = 1;
1850 }
1851
1852 static void adsi_login(struct ast_channel *chan)
1853 {
1854         char buf[256];
1855         int bytes=0;
1856         unsigned char keys[8];
1857         int x;
1858         if (!adsi_available(chan))
1859                 return;
1860
1861         for (x=0;x<8;x++)
1862                 keys[x] = 0;
1863         /* Set one key for next */
1864         keys[3] = ADSI_KEY_APPS + 3;
1865
1866         bytes += adsi_logo(buf + bytes);
1867         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1868         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1869         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1870         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1871         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1872         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1873         bytes += adsi_set_keys(buf + bytes, keys);
1874         bytes += adsi_voice_mode(buf + bytes, 0);
1875         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1876 }
1877
1878 static void adsi_password(struct ast_channel *chan)
1879 {
1880         char buf[256];
1881         int bytes=0;
1882         unsigned char keys[8];
1883         int x;
1884         if (!adsi_available(chan))
1885                 return;
1886
1887         for (x=0;x<8;x++)
1888                 keys[x] = 0;
1889         /* Set one key for next */
1890         keys[3] = ADSI_KEY_APPS + 3;
1891
1892         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1893         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1894         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1895         bytes += adsi_set_keys(buf + bytes, keys);
1896         bytes += adsi_voice_mode(buf + bytes, 0);
1897         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1898 }
1899
1900 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1901 {
1902         char buf[256];
1903         int bytes=0;
1904         unsigned char keys[8];
1905         int x,y;
1906
1907         if (!adsi_available(chan))
1908                 return;
1909
1910         for (x=0;x<5;x++) {
1911                 y = ADSI_KEY_APPS + 12 + start + x;
1912                 if (y > ADSI_KEY_APPS + 12 + 4)
1913                         y = 0;
1914                 keys[x] = ADSI_KEY_SKT | y;
1915         }
1916         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1917         keys[6] = 0;
1918         keys[7] = 0;
1919
1920         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1921         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1922         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1923         bytes += adsi_set_keys(buf + bytes, keys);
1924         bytes += adsi_voice_mode(buf + bytes, 0);
1925
1926         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1927 }
1928
1929 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1930 {
1931         int bytes=0;
1932         char buf[256], buf1[256], buf2[256];
1933         char fn2[256];
1934
1935         char cid[256]="";
1936         char *val;
1937         char *name, *num;
1938         char datetime[21]="";
1939         FILE *f;
1940
1941         unsigned char keys[8];
1942
1943         int x;
1944
1945         if (!adsi_available(chan))
1946                 return;
1947
1948         /* Retrieve important info */
1949         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1950         f = fopen(fn2, "r");
1951         if (f) {
1952                 while(!feof(f)) {       
1953                         fgets(buf, sizeof(buf), f);
1954                         if (!feof(f)) {
1955                                 char *stringp=NULL;
1956                                 stringp=buf;
1957                                 strsep(&stringp, "=");
1958                                 val = strsep(&stringp, "=");
1959                                 if (val && strlen(val)) {
1960                                         if (!strcmp(buf, "callerid"))
1961                                                 strncpy(cid, val, sizeof(cid) - 1);
1962                                         if (!strcmp(buf, "origdate"))
1963                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1964                                 }
1965                         }
1966                 }
1967                 fclose(f);
1968         }
1969         /* New meaning for keys */
1970         for (x=0;x<5;x++)
1971                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1972         keys[6] = 0x0;
1973         keys[7] = 0x0;
1974
1975         if (!msg) {
1976                 /* No prev key, provide "Folder" instead */
1977                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1978         }
1979         if (msg >= last) {
1980                 /* If last message ... */
1981                 if (msg) {
1982                         /* but not only message, provide "Folder" instead */
1983                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1984       bytes += adsi_voice_mode(buf + bytes, 0);
1985
1986                 } else {
1987                         /* Otherwise if only message, leave blank */
1988                         keys[3] = 1;
1989                 }
1990         }
1991
1992         if (strlen(cid)) {
1993                 ast_callerid_parse(cid, &name, &num);
1994                 if (!name)
1995                         name = num;
1996         } else
1997                 name = "Unknown Caller";
1998
1999         /* If deleted, show "undeleted" */
2000
2001         if (deleted)
2002                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2003
2004         /* Except "Exit" */
2005         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2006         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2007                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2008         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2009
2010         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2011         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2012         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2013         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2014         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2015         bytes += adsi_set_keys(buf + bytes, keys);
2016         bytes += adsi_voice_mode(buf + bytes, 0);
2017
2018         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2019 }
2020
2021 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2022 {
2023         int bytes=0;
2024         char buf[256];
2025         unsigned char keys[8];
2026
2027         int x;
2028
2029         if (!adsi_available(chan))
2030                 return;
2031
2032         /* New meaning for keys */
2033         for (x=0;x<5;x++)
2034                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2035
2036         keys[6] = 0x0;
2037         keys[7] = 0x0;
2038
2039         if (!msg) {
2040                 /* No prev key, provide "Folder" instead */
2041                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2042         }
2043         if (msg >= last) {
2044                 /* If last message ... */
2045                 if (msg) {
2046                         /* but not only message, provide "Folder" instead */
2047                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2048                 } else {
2049                         /* Otherwise if only message, leave blank */
2050                         keys[3] = 1;
2051                 }
2052         }
2053
2054         /* If deleted, show "undeleted" */
2055         if (deleted) 
2056                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2057
2058         /* Except "Exit" */
2059         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2060         bytes += adsi_set_keys(buf + bytes, keys);
2061         bytes += adsi_voice_mode(buf + bytes, 0);
2062
2063         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2064 }
2065
2066 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2067 {
2068         char buf[256], buf1[256], buf2[256];
2069         int bytes=0;
2070         unsigned char keys[8];
2071         int x;
2072
2073         char *newm = (new == 1) ? "message" : "messages";
2074         char *oldm = (old == 1) ? "message" : "messages";
2075         if (!adsi_available(chan))
2076                 return;
2077         if (new) {
2078                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2079                 if (old) {
2080                         strcat(buf1, " and");
2081                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2082                 } else {
2083                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2084                 }
2085         } else if (old) {
2086                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2087                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2088         } else {
2089                 strcpy(buf1, "You have no messages.");
2090                 strcpy(buf2, " ");
2091         }
2092         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2093         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2094         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2095
2096         for (x=0;x<6;x++)
2097                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2098         keys[6] = 0;
2099         keys[7] = 0;
2100
2101         /* Don't let them listen if there are none */
2102         if (lastmsg < 0)
2103                 keys[0] = 1;
2104         bytes += adsi_set_keys(buf + bytes, keys);
2105
2106         bytes += adsi_voice_mode(buf + bytes, 0);
2107
2108         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2109 }
2110
2111 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2112 {
2113         char buf[256], buf1[256], buf2[256];
2114         int bytes=0;
2115         unsigned char keys[8];
2116         int x;
2117
2118         char *mess = (messages == 1) ? "message" : "messages";
2119
2120         if (!adsi_available(chan))
2121                 return;
2122
2123         /* Original command keys */
2124         for (x=0;x<6;x++)
2125                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2126
2127         keys[6] = 0;
2128         keys[7] = 0;
2129
2130         if (messages < 1)
2131                 keys[0] = 0;
2132
2133         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2134                         strcasecmp(folder, "INBOX") ? " folder" : "");
2135
2136         if (messages)
2137                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2138         else
2139                 strcpy(buf2, "no messages.");
2140         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2141         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2142         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2143         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2144         bytes += adsi_set_keys(buf + bytes, keys);
2145
2146         bytes += adsi_voice_mode(buf + bytes, 0);
2147
2148         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2149         
2150 }
2151
2152 /*
2153 static void adsi_clear(struct ast_channel *chan)
2154 {
2155         char buf[256];
2156         int bytes=0;
2157         if (!adsi_available(chan))
2158                 return;
2159         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2160         bytes += adsi_voice_mode(buf + bytes, 0);
2161
2162         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2163 }
2164 */
2165
2166 static void adsi_goodbye(struct ast_channel *chan)
2167 {
2168         char buf[256];
2169         int bytes=0;
2170
2171         if (!adsi_available(chan))
2172                 return;
2173         bytes += adsi_logo(buf + bytes);
2174         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2175         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2176         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2177         bytes += adsi_voice_mode(buf + bytes, 0);
2178
2179         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2180 }
2181
2182 static int get_folder(struct ast_channel *chan, int start)
2183 {
2184         int x;
2185         int d;
2186         char fn[256];
2187         d = play_and_wait(chan, "vm-press");
2188         if (d)
2189                 return d;
2190         for (x = start; x< 5; x++) {
2191                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
2192                         return d;
2193                 d = play_and_wait(chan, "vm-for");
2194                 if (d)
2195                         return d;
2196                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2197                 d = play_and_wait(chan, fn);
2198                 if (d)
2199                         return d;
2200                 d = play_and_wait(chan, "vm-messages");
2201                 if (d)
2202                         return d;
2203                 d = ast_waitfordigit(chan, 500);
2204                 if (d)
2205                         return d;
2206         }
2207         d = play_and_wait(chan, "vm-tocancel");
2208         if (d)
2209                 return d;
2210         d = ast_waitfordigit(chan, 4000);
2211         return d;
2212 }
2213
2214 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2215 {
2216         int res = 0;
2217         res = play_and_wait(chan, fn);
2218         while (((res < '0') || (res > '9')) &&
2219                         (res != '#') && (res >= 0)) {
2220                 res = get_folder(chan, 0);
2221         }
2222         return res;
2223 }
2224
2225 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2226 {
2227         int cmd = 0;
2228         int retries = 0;
2229
2230         while((cmd >= 0) && (cmd != 't') && (cmd != '#')) {
2231                 if (cmd)
2232                         retries = 0;
2233                 switch (cmd) {
2234                 case '1': 
2235                         /* prepend a message to the current message and return */
2236                 {
2237                         char file[200];
2238                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2239                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, 1);
2240                         break;
2241                 }
2242                 case '2': 
2243                         cmd = 't';
2244                         break;
2245                 case '#':
2246                         cmd = '#';
2247                         break;
2248                 default: 
2249                         cmd = play_and_wait(chan,"vm-forwardoptions");
2250                         if (!cmd)
2251                                 cmd = ast_waitfordigit(chan,6000);
2252                         if (!cmd)
2253                                 retries++;
2254                         if (retries > 3)
2255                                 cmd = 't';
2256                  }
2257         }
2258         if (cmd == 't')
2259                 cmd = 0;
2260         return cmd;
2261 }
2262
2263 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2264 {
2265         char username[70];
2266         char sys[256];
2267         char todir[256];
2268         int todircount=0;
2269         long duration;
2270         struct ast_config *mif;
2271         char miffile[256];
2272         char fn[256];
2273         char callerid[512];
2274         int res = 0, cmd = 0;
2275         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2276         char tmp[256];
2277         char *stringp, *s;
2278         int saved_messages = 0, found = 0;
2279         int valid_extensions = 0;
2280         while (!res && !valid_extensions) {
2281                 res = ast_streamfile(chan, "vm-extension", chan->language);
2282                 if (res)
2283                         break;
2284                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2285                         break;
2286                 /* start all over if no username */
2287                 if (!strlen(username))
2288                         continue;
2289                 stringp = username;
2290                 s = strsep(&stringp, "*");
2291                 /* start optimistic */
2292                 valid_extensions = 1;
2293                 while (s) {
2294                         /* find_user is going to malloc since we have a NULL as first argument */
2295                         if ((receiver = find_user(NULL, context, s))) {
2296                                 if (!extensions)
2297                                         vmtmp = extensions = receiver;
2298                                 else {
2299                                         vmtmp->next = receiver;
2300                                         vmtmp = receiver;
2301                                 }
2302                                 found++;
2303                         } else {
2304                                 valid_extensions = 0;
2305                                 break;
2306                         }
2307                         s = strsep(&stringp, "*");
2308                 }
2309                 /* break from the loop of reading the extensions */
2310                 if (valid_extensions)
2311                         break;
2312                 /* invalid extension, try again */
2313                 res = play_and_wait(chan, "pbx-invalid");
2314         }
2315         /* check if we're clear to proceed */
2316         if (!extensions || !valid_extensions)
2317                 return res;
2318         vmtmp = extensions;
2319         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2320
2321         while(!res && vmtmp) {
2322                 /* if (play_and_wait(chan, "vm-savedto"))
2323                         break;
2324                 */
2325                 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2326                 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2327                 ast_log(LOG_DEBUG, sys);
2328                 ast_safe_system(sys);
2329
2330                 todircount = count_messages(todir);
2331                 strncpy(tmp, fmt, sizeof(tmp) - 1);
2332                 stringp = tmp;
2333                 while((s = strsep(&stringp, "|"))) {
2334                         /* XXX This is a hack -- we should use build_filename or similar XXX */
2335                         if (!strcasecmp(s, "wav49"))
2336                                 s = "WAV";
2337                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2338                         ast_log(LOG_DEBUG, sys);
2339                         ast_safe_system(sys);
2340                 }
2341                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2342                 ast_log(LOG_DEBUG, sys);
2343                 ast_safe_system(sys);
2344                 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2345
2346                 /* load the information on the source message so we can send an e-mail like a new message */
2347                 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2348                 if ((mif=ast_load(miffile))) {
2349
2350                         /* set callerid and duration variables */
2351                         snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2352                         s = ast_variable_retrieve(mif, NULL, "duration");
2353                         if (s)
2354                                 duration = atol(s);
2355                         else
2356                                 duration = 0;
2357                         if (strlen(vmtmp->email)) {
2358                                 int attach_user_voicemail = attach_voicemail;
2359                                 char *myserveremail = serveremail;
2360                                 if (vmtmp->attach > -1)
2361                                         attach_user_voicemail = vmtmp->attach;
2362                                 if (strlen(vmtmp->serveremail))
2363                                         myserveremail = vmtmp->serveremail;
2364                                 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2365                         }
2366                      
2367                         if (strlen(vmtmp->pager)) {
2368                                 char *myserveremail = serveremail;
2369                                 if (strlen(vmtmp->serveremail))
2370                                         myserveremail = vmtmp->serveremail;
2371                                 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2372                         }
2373                           
2374                         ast_destroy(mif); /* or here */
2375                 }
2376                 /* Leave voicemail for someone */
2377                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2378                 if (externnotify[0]) {
2379                         char arguments[255];
2380                         strncpy(tmp, externnotify, sizeof(tmp));
2381                         snprintf(arguments, sizeof(arguments)-1, " %s %s %d&", chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2382                         strncat(tmp, arguments, sizeof(tmp) - strlen(arguments));
2383                         tmp[sizeof(tmp) - 1] = '\0';
2384                 //      ast_verbose(VERBOSE_PREFIX_3, "executing %s\n", tmp);
2385                         ast_safe_system(tmp);
2386                 }
2387
2388                 saved_messages++;
2389                 vmfree = vmtmp;
2390                 vmtmp = vmtmp->next;
2391                 free_user(vmfree);
2392         }
2393         if (saved_messages > 0) {
2394                 /* give confirmatopm that the message was saved */
2395                 if (saved_messages == 1)
2396                         res = play_and_wait(chan, "vm-message");
2397                 else
2398                         res = play_and_wait(chan, "vm-messages");
2399                 if (!res)
2400                         res = play_and_wait(chan, "vm-saved");
2401         }
2402         return res ? res : cmd;
2403 }
2404
2405
2406 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2407 {
2408         int res;
2409         if ((res = ast_streamfile(chan, file, chan->language))) 
2410                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2411         if (!res)
2412                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2413         return res;
2414 }
2415
2416 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2417 {
2418         int res;
2419         if ((res = ast_streamfile(chan, file, chan->language)))
2420                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2421         if (!res)
2422                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2423         return res;
2424 }
2425
2426 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2427 {
2428         int res = 0;
2429         char filename[256], *origtime;
2430         struct vm_zone *the_zone = NULL;
2431         struct ast_config *msg_cfg;
2432         time_t t;
2433         long tin;
2434
2435         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2436         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2437         msg_cfg = ast_load(filename);
2438         if (!msg_cfg) {
2439                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2440                 return 0;
2441         }
2442
2443         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2444                 return 0;
2445         if (sscanf(origtime,"%ld",&tin) < 1) {
2446                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2447                 return 0;
2448         }
2449         t = tin;
2450         ast_destroy(msg_cfg);
2451
2452         /* Does this user have a timezone specified? */
2453         if (strlen(vmu->zonetag)) {
2454                 /* Find the zone in the list */
2455                 struct vm_zone *z;
2456                 z = zones;
2457                 while (z) {
2458                         if (!strcmp(z->name, vmu->zonetag)) {
2459                                 the_zone = z;
2460                                 break;
2461                         }
2462                         z = z->next;
2463                 }
2464         }
2465
2466 /* No internal variable parsing for now, so we'll comment it out for the time being */
2467 #if 0
2468         /* Set the DIFF_* variables */
2469         localtime_r(&t, &time_now);
2470         gettimeofday(&tv_now,NULL);
2471         tnow = tv_now.tv_sec;
2472         localtime_r(&tnow,&time_then);
2473
2474         /* Day difference */
2475         if (time_now.tm_year == time_then.tm_year)
2476                 sprintf(temp,"%d",time_now.tm_yday);
2477         else
2478                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2479         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2480
2481         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2482 #endif
2483         if (the_zone)
2484                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2485         else
2486                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2487 #if 0
2488         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2489 #endif
2490         return res;
2491 }
2492
2493 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2494 {
2495         int res = 0;
2496         vms->starting = 0; 
2497         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2498         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2499         if (!msg)
2500                 res = wait_file2(chan, vms, "vm-first");
2501         else if (msg == vms->lastmsg)
2502                 res = wait_file2(chan, vms, "vm-last");
2503         if (!res) {
2504                 res = wait_file2(chan, vms, "vm-message");
2505                 if (msg && (msg != vms->lastmsg)) {
2506                         if (!res)
2507                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2508                 }
2509         }
2510
2511         if (!res)
2512                 res = play_message_datetime(chan,vmu,vms);
2513
2514         if (!res) {
2515                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2516                 vms->heard[msg] = 1;
2517                 res = wait_file(chan, vms, vms->fn);
2518         }
2519         return res;
2520 }
2521
2522 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2523 {
2524         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2525         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2526         vms->lastmsg = count_messages(vms->curdir) - 1;
2527         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2528 }
2529
2530 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2531 {
2532         int x;
2533         char ntxt[256] = "";
2534         char txt[256] = "";
2535         if (vms->lastmsg > -1) { 
2536                 /* Get the deleted messages fixed */ 
2537                 vms->curmsg = -1; 
2538                 for (x=0;x < MAXMSG;x++) { 
2539                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2540                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2541                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2542                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2543                                         break;
2544                                 vms->curmsg++; 
2545                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2546                                 if (strcmp(vms->fn, vms->fn2)) { 
2547                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2548                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2549                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2550                                         rename(txt, ntxt); 
2551                                 } 
2552                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2553                                 /* Move to old folder before deleting */ 
2554                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2555                         } 
2556                 } 
2557                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2558                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2559                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2560                                 break;
2561                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2562                         ast_filedelete(vms->fn, NULL); 
2563                         unlink(txt); 
2564                 } 
2565         } 
2566         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2567         memset(vms->heard, 0, sizeof(vms->heard)); 
2568 }
2569
2570 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2571 {
2572         /* Introduce messages they have */
2573         int res;
2574         res = play_and_wait(chan, "vm-youhave");
2575         if (!res) {
2576                 if (vms->newmessages) {
2577                         res = say_and_wait(chan, vms->newmessages);
2578                         if (!res)
2579                                 res = play_and_wait(chan, "vm-INBOX");
2580                         if (vms->oldmessages && !res)
2581                                 res = play_and_wait(chan, "vm-and");
2582                         else if (!res) {
2583                                 if ((vms->newmessages == 1))
2584                                         res = play_and_wait(chan, "vm-message");
2585                                 else
2586                                         res = play_and_wait(chan, "vm-messages");
2587                         }
2588                                 
2589                 }
2590                 if (!res && vms->oldmessages) {
2591                         res = say_and_wait(chan, vms->oldmessages);
2592                         if (!res)
2593                                 res = play_and_wait(chan, "vm-Old");
2594                         if (!res) {
2595                                 if (vms->oldmessages == 1)
2596                                         res = play_and_wait(chan, "vm-message");
2597                                 else
2598                                         res = play_and_wait(chan, "vm-messages");
2599                         }
2600                 }
2601                 if (!res) {
2602                         if (!vms->oldmessages && !vms->newmessages) {
2603                                 res = play_and_wait(chan, "vm-no");
2604                                 if (!res)
2605                                         res = play_and_wait(chan, "vm-messages");
2606                         }
2607                 }
2608         }
2609         return res;
2610 }
2611
2612 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2613 {
2614         int res = 0;
2615         /* Play instructions and wait for new command */
2616         while(!res) {
2617                 if (vms->starting) {
2618                         if (vms->lastmsg > -1) {
2619                                 res = play_and_wait(chan, "vm-onefor");
2620                                 if (!res)
2621                                         res = play_and_wait(chan, vms->vmbox);
2622                                 if (!res)
2623                                         res = play_and_wait(chan, "vm-messages");
2624                         }
2625                         if (!res)
2626                                 res = play_and_wait(chan, "vm-opts");
2627                 } else {
2628                         if (vms->curmsg)
2629                                 res = play_and_wait(chan, "vm-prev");
2630                         if (!res)
2631                                 res = play_and_wait(chan, "vm-repeat");
2632                         if (!res && (vms->curmsg != vms->lastmsg))
2633                                 res = play_and_wait(chan, "vm-next");
2634                         if (!res) {
2635                                 if (!vms->deleted[vms->curmsg])
2636                                         res = play_and_wait(chan, "vm-delete");
2637                                 else
2638                                         res = play_and_wait(chan, "vm-undelete");
2639                                 if (!res)
2640                                         res = play_and_wait(chan, "vm-toforward");
2641                                 if (!res)
2642                                         res = play_and_wait(chan, "vm-savemessage");
2643                         }
2644                 }
2645                 if (!res)
2646                         res = play_and_wait(chan, "vm-helpexit");
2647                 if (!res)
2648                         res = ast_waitfordigit(chan, 6000);
2649                 if (!res) {
2650                         vms->repeats++;
2651                         if (vms->repeats > 2) {
2652                                 res = 't';
2653                         }
2654                 }
2655         }
2656         return res;
2657 }
2658
2659 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2660 {
2661         int cmd = 0;
2662         int retries = 0;
2663         char newpassword[80] = "";
2664         char newpassword2[80] = "";
2665         char prefile[256]="";
2666         char buf[256];
2667         int bytes=0;
2668
2669         if (adsi_available(chan))
2670         {
2671                 bytes += adsi_logo(buf + bytes);
2672                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2673                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2674                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2675                 bytes += adsi_voice_mode(buf + bytes, 0);
2676                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2677         }
2678         while((cmd >= 0) && (cmd != 't')) {
2679                 if (cmd)
2680                         retries = 0;
2681                 switch (cmd) {
2682                 case '1':
2683                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2684                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2685                         break;
2686                 case '2': 
2687                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2688                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2689                         break;
2690                 case '3': 
2691                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2692                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2693                         break;
2694                 case '4':
2695                         newpassword[1] = '\0';
2696                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2697                         if (cmd < 0)
2698                                 break;
2699                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2700                                 break;
2701             }
2702                         newpassword2[1] = '\0';
2703                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2704                         if (cmd < 0)
2705                                 break;
2706
2707                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2708                                 break;
2709             }
2710                         if (strcmp(newpassword, newpassword2)) {
2711                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2712                                 cmd = play_and_wait(chan, "vm-mismatch");
2713                                 break;
2714                         }
2715                         vm_change_password(vmu,newpassword);
2716                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2717                         cmd = play_and_wait(chan,"vm-passchanged");
2718                         break;
2719                 case '*': 
2720                         cmd = 't';
2721                         break;
2722                 default: 
2723                         cmd = play_and_wait(chan,"vm-options");
2724                         if (!cmd)
2725                                 cmd = ast_waitfordigit(chan,6000);
2726                         if (!cmd)
2727                                 retries++;
2728                         if (retries > 3)
2729                                 cmd = 't';
2730                  }
2731         }
2732         if (cmd == 't')
2733                 cmd = 0;
2734         return cmd;
2735 }
2736
2737 static int vm_execmain(struct ast_channel *chan, void *data)
2738 {
2739         /* XXX This is, admittedly, some pretty horrendus code.  For some
2740            reason it just seemed a lot easier to do with GOTO's.  I feel
2741            like I'm back in my GWBASIC days. XXX */
2742         int res=-1;
2743         int valid = 0;
2744         int prefix = 0;
2745         int cmd=0;
2746         struct localuser *u;
2747         char prefixstr[80] ="";
2748         char empty[80] = "";
2749         int box;
2750         int useadsi = 0;
2751         int skipuser = 0;
2752         char tmp[256], *ext;
2753         char fmtc[256] = "";
2754         char password[80];
2755         struct vm_state vms;
2756         int logretries = 0;
2757         struct ast_vm_user *vmu = NULL, vmus;
2758         char *context=NULL;
2759
2760         LOCAL_USER_ADD(u);
2761         memset(&vms, 0, sizeof(vms));
2762         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2763         if (chan->_state != AST_STATE_UP)
2764                 ast_answer(chan);
2765
2766         if (data && strlen(data)) {
2767                 strncpy(tmp, data, sizeof(tmp) - 1);
2768                 ext = tmp;
2769
2770                 switch (*ext) {
2771                         case 's':
2772                  /* We should skip the user's password */
2773                                 valid++;
2774                                 ext++;
2775                                 break;
2776                         case 'p':
2777                  /* We should prefix the mailbox with the supplied data */
2778                                 prefix++;
2779                                 ext++;
2780                                 break;
2781                 }
2782
2783                 context = strchr(ext, '@');
2784                 if (context) {
2785                         *context = '\0';
2786                         context++;
2787                 }
2788
2789                 if (prefix)
2790                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2791                 else
2792                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2793                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2794                         skipuser++;
2795                 else
2796                         valid = 0;
2797
2798         }
2799
2800         /* If ADSI is supported, setup login screen */
2801         adsi_begin(chan, &useadsi);
2802         if (!skipuser && useadsi)
2803                 adsi_login(chan);
2804         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2805                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2806                 goto out;
2807         }
2808         
2809         /* Authenticate them and get their mailbox/password */
2810         
2811         while (!valid && (logretries < maxlogins)) {
2812                 /* Prompt for, and read in the username */
2813                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2814                         ast_log(LOG_WARNING, "Couldn't read username\n");
2815                         goto out;
2816                 }
2817                 if (!strlen(vms.username)) {
2818                         if (option_verbose > 2)
2819                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2820                         res = 0;
2821                         goto out;
2822                 }
2823                 if (useadsi)
2824                         adsi_password(chan);
2825                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2826                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2827                         goto out;
2828                 }
2829                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2830                         ast_log(LOG_WARNING, "Unable to read password\n");
2831                         goto out;
2832                 }
2833                 if (prefix) {
2834                         char fullusername[80] = "";
2835                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2836                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2837                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2838                 }
2839                 if (!skipuser) 
2840                         vmu = find_user(&vmus, context, vms.username);
2841                 if (vmu && !strcmp(vmu->password, password)) 
2842                         valid++;
2843                 else {
2844                         if (option_verbose > 2)
2845                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2846                         if (prefix)
2847                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2848                 }
2849                 if (!valid) {
2850                         if (useadsi)
2851                                 adsi_login(chan);
2852                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2853                                 break;
2854                 }
2855                 logretries++;
2856         }
2857         if (!valid && (logretries >= maxlogins)) {
2858                 ast_stopstream(chan);
2859                 res = play_and_wait(chan, "vm-goodbye");
2860                 if (res > 0)
2861                         res = 0;
2862         }
2863
2864         if (valid) {
2865                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2866                 mkdir(vms.curdir, 0700);
2867                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2868                 mkdir(vms.curdir, 0700);
2869                 /* Retrieve old and new message counts */
2870                 open_mailbox(&vms, vmu, 1);
2871                 vms.oldmessages = vms.lastmsg + 1;
2872                 /* Start in INBOX */
2873                 open_mailbox(&vms, vmu, 0);
2874                 vms.newmessages = vms.lastmsg + 1;
2875                 
2876
2877                 /* Select proper mailbox FIRST!! */
2878                 if (!vms.newmessages && vms.oldmessages) {
2879                         /* If we only have old messages start here */
2880                         open_mailbox(&vms, vmu, 1);
2881                 }
2882
2883                 if (useadsi)
2884                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2885                 res = 0;
2886                 cmd = vm_intro(chan, &vms);
2887                 vms.repeats = 0;
2888                 vms.starting = 1;
2889                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2890                         /* Run main menu */
2891                         switch(cmd) {
2892                         case '1':
2893                                 vms.curmsg = 0;
2894                                 /* Fall through */
2895                         case '5':
2896                                 if (vms.lastmsg > -1) {
2897                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2898                                 } else {
2899                                         cmd = play_and_wait(chan, "vm-youhave");
2900                                         if (!cmd) 
2901                                                 cmd = play_and_wait(chan, "vm-no");
2902                                         if (!cmd) {
2903                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2904                                                 cmd = play_and_wait(chan, vms.fn);
2905                                         }
2906                                         if (!cmd)
2907                                                 cmd = play_and_wait(chan, "vm-messages");
2908                                 }
2909                                 break;
2910                         case '2': /* Change folders */
2911                                 if (useadsi)
2912                                         adsi_folders(chan, 0, "Change to folder...");
2913                                 cmd = get_folder2(chan, "vm-changeto", 0);
2914                                 if (cmd == '#') {
2915                                         cmd = 0;
2916                                 } else if (cmd > 0) {
2917                                         cmd = cmd - '0';
2918                                         close_mailbox(&vms, vmu);
2919                                         open_mailbox(&vms, vmu, cmd);
2920                                         cmd = 0;
2921                                 }
2922                                 if (useadsi)
2923                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2924                                 if (!cmd)
2925                                         cmd = play_and_wait(chan, vms.vmbox);
2926                                 if (!cmd)
2927                                         cmd = play_and_wait(chan, "vm-messages");
2928                                 vms.starting = 1;
2929                                 break;
2930                         case '4':
2931                                 if (vms.curmsg) {
2932                                         vms.curmsg--;
2933                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2934                                 } else {
2935                                         cmd = play_and_wait(chan, "vm-nomore");
2936                                 }
2937                                 break;
2938                         case '6':
2939                                 if (vms.curmsg < vms.lastmsg) {
2940                                         vms.curmsg++;
2941                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2942                                 } else {
2943                                         cmd = play_and_wait(chan, "vm-nomore");
2944                                 }
2945                                 break;
2946                         case '7':
2947                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2948                                 if (useadsi)
2949                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2950                                 if (vms.deleted[vms.curmsg]) 
2951                                         cmd = play_and_wait(chan, "vm-deleted");
2952                                 else
2953                                         cmd = play_and_wait(chan, "vm-undeleted");
2954                                 break;
2955              &n