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