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