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