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