3cdf23984cf06853615b676137dbe223d69ea37e
[asterisk/asterisk.git] / apps / app_voicemail2.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) 1999, Mark Spencer
7  *
8  * Mark Spencer <markster@linux-support.net>
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 *app = "VoiceMail2";
149
150 /* Check mail, control, etc */
151 static char *app2 = "VoiceMailMain2";
152
153 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
154 struct ast_vm_user *users;
155 struct ast_vm_user *usersl;
156 struct vm_zone *zones = NULL;
157 struct vm_zone *zonesl = NULL;
158 static int attach_voicemail;
159 static int maxsilence;
160 static int silencethreshold = 128;
161 static char serveremail[80];
162 static char vmfmts[80];
163 static int vmmaxmessage;
164 static int maxgreet;
165 static int skipms;
166 static int maxlogins;
167
168 static char *emailbody = NULL;
169 static int pbxskip = 0;
170 static char fromstring[100];
171 static char emailtitle[100];
172
173 STANDARD_LOCAL_USER;
174
175 LOCAL_USER_DECL;
176
177 static void apply_options(struct ast_vm_user *vmu, char *options)
178 {
179         /* Destructively Parse options and apply */
180         char *stringp = ast_strdupa(options);
181         char *s;
182         char *var, *value;
183         while((s = strsep(&stringp, "|"))) {
184                 value = s;
185                 if ((var = strsep(&value, "=")) && value) {
186                         if (!strcasecmp(var, "attach")) {
187                                 if (ast_true(value))
188                                         vmu->attach = 1;
189                                 else
190                                         vmu->attach = 0;
191                         } else if (!strcasecmp(var, "serveremail")) {
192                                 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
193                         } else if (!strcasecmp(var, "tz")) {
194                                 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
195                         }
196                 }
197         }
198         
199 }
200
201 #ifdef USEMYSQLVM
202 #include "mysql-vm-routines.h"
203 #endif
204
205 #ifdef USEPOSTGRESVM
206
207 PGconn *dbhandler;
208 char    dboption[256];
209 ast_mutex_t postgreslock;
210
211 static int sql_init(void)
212 {
213         ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
214 /*      fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
215
216         dbhandler=PQconnectdb(dboption);
217         if (PQstatus(dbhandler) == CONNECTION_BAD) {
218                 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
219                 return(-1);
220         }
221         ast_mutex_init(&postgreslock);
222
223 /*      fprintf(stderr,"postgres login OK\n"); */
224         return(0);
225 }
226
227 static void sql_close(void)
228 {
229         PQfinish(dbhandler);
230 }
231
232
233 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
234 {
235         PGresult *PGSQLres;
236
237
238         int numFields, i;
239         char *fname;
240         char query[240];
241         char options[160] = "";
242         struct ast_vm_user *retval;
243
244         retval=malloc(sizeof(struct ast_vm_user));
245
246 /*      fprintf(stderr,"postgres find_user:\n"); */
247
248         if (retval) {
249                 *retval->mailbox='\0';
250                 *retval->context='\0';
251                 *retval->password='\0';
252                 *retval->fullname='\0';
253                 *retval->email='\0';
254                 *retval->pager='\0';
255                 *retval->serveremail='\0';
256                 retval->attach=-1;
257                 retval->alloced=1;
258                 retval->next=NULL;
259                 if (mailbox) {
260                         strcpy(retval->mailbox, mailbox);
261                 }
262                 if (context) {
263                         strcpy(retval->context, context);
264                 }
265
266                 if (*retval->context) {
267                         sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE context='%s' AND mailbox='%s'", context, mailbox);
268                 } else {
269                         sprintf(query, "SELECT password,fullname,email,pager,options FROM voicemail WHERE mailbox='%s'", mailbox);
270                 }
271 /*      fprintf(stderr,"postgres find_user: query = %s\n",query); */
272                 ast_mutex_lock(&postgreslock);
273                 PGSQLres=PQexec(dbhandler,query);
274                 if (PGSQLres!=NULL) {
275                         if (PQresultStatus(PGSQLres) == PGRES_BAD_RESPONSE ||
276                                 PQresultStatus(PGSQLres) == PGRES_NONFATAL_ERROR ||
277                                 PQresultStatus(PGSQLres) == PGRES_FATAL_ERROR) {
278
279                                 ast_log(LOG_WARNING,"PGSQL_query: Query Error (%s) Calling PQreset\n",PQcmdStatus(PGSQLres));
280                                 PQclear(PGSQLres);
281                                 PQreset(dbhandler);
282                                 ast_mutex_unlock(&postgreslock);
283                                 free(retval);
284                                 return(NULL);
285                         } else {
286                         numFields = PQnfields(PGSQLres);
287 /*      fprintf(stderr,"postgres find_user: query found %d rows with %d fields\n",PQntuples(PGSQLres), numFields); */
288                         if (PQntuples(PGSQLres) != 1) {
289                                 ast_log(LOG_WARNING,"PGSQL_query: Did not find a unique mailbox for %s\n",mailbox);
290                                 PQclear(PGSQLres);
291                                 ast_mutex_unlock(&postgreslock);
292                                 free(retval);
293                                 return(NULL);
294                         }
295                         for (i=0; i<numFields; i++) {
296                                 fname = PQfname(PGSQLres,i);
297                                 if (!strcmp(fname, "password")) {
298                                         strncpy(retval->password, PQgetvalue(PGSQLres,0,i),sizeof(retval->password) - 1);
299                                 } else if (!strcmp(fname, "fullname")) {
300                                         strncpy(retval->fullname, PQgetvalue(PGSQLres,0,i),sizeof(retval->fullname) - 1);
301                                 } else if (!strcmp(fname, "email")) {
302                                         strncpy(retval->email, PQgetvalue(PGSQLres,0,i),sizeof(retval->email) - 1);
303                                 } else if (!strcmp(fname, "pager")) {
304                                         strncpy(retval->pager, PQgetvalue(PGSQLres,0,i),sizeof(retval->pager) - 1);
305                                 } else if (!strcmp(fname, "options")) {
306                                         strncpy(options, PQgetvalue(PGSQLres,0,i), sizeof(options) - 1);
307                                         apply_options(retval, options);
308                                 }
309                         }
310                         }
311                         PQclear(PGSQLres);
312                         ast_mutex_unlock(&postgreslock);
313                         return(retval);
314                 }
315                 else {
316                         ast_log(LOG_WARNING,"PGSQL_query: Connection Error (%s)\n",PQerrorMessage(dbhandler));
317                         ast_mutex_unlock(&postgreslock);
318                         free(retval);
319                         return(NULL);
320                 }
321                 /* not reached */
322         } /* malloc() retval */
323         return(NULL);
324 }
325
326
327 static void vm_change_password(struct ast_vm_user *vmu, char *password)
328 {
329         char query[400];
330
331         if (*vmu->context) {
332                 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);
333         } else {
334                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s' AND (password='%s' OR password IS NULL)", password, vmu->mailbox, vmu->password);
335         }
336 /*      fprintf(stderr,"postgres change_password: query = %s\n",query); */
337         ast_mutex_lock(&postgreslock);
338         PQexec(dbhandler, query);
339         strcpy(vmu->password, password);
340         ast_mutex_unlock(&postgreslock);
341 }
342
343 static void reset_user_pw(char *context, char *mailbox, char *password)
344 {
345         char query[320];
346
347         if (context) {
348                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE context='%s' AND mailbox='%s'", password, context, mailbox);
349         } else {
350                 sprintf(query, "UPDATE voicemail SET password='%s' WHERE mailbox='%s'", password, mailbox);
351         }
352         ast_mutex_lock(&postgreslock);
353 /*      fprintf(stderr,"postgres reset_user_pw: query = %s\n",query); */
354         PQexec(dbhandler, query);
355         ast_mutex_unlock(&postgreslock);
356 }
357
358 #endif  /* Postgres */
359
360 #ifndef USESQLVM
361 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
362 {
363         /* This function could be made to generate one from a database, too */
364         struct ast_vm_user *vmu=NULL, *cur;
365         ast_mutex_lock(&vmlock);
366         cur = users;
367         while(cur) {
368                 if ((!context || !strcasecmp(context, cur->context)) &&
369                         (!strcasecmp(mailbox, cur->mailbox)))
370                                 break;
371                 cur=cur->next;
372         }
373         if (cur) {
374                 if (ivm)
375                         vmu = ivm;
376                 else
377                         /* Make a copy, so that on a reload, we have no race */
378                         vmu = malloc(sizeof(struct ast_vm_user));
379                 if (vmu) {
380                         memcpy(vmu, cur, sizeof(struct ast_vm_user));
381                         if (ivm)
382                                 vmu->alloced = 0;
383                         else
384                                 vmu->alloced = 1;
385                         vmu->next = NULL;
386                 }
387         }
388         ast_mutex_unlock(&vmlock);
389         return vmu;
390 }
391
392 static int reset_user_pw(char *context, char *mailbox, char *newpass)
393 {
394         /* This function could be made to generate one from a database, too */
395         struct ast_vm_user *cur;
396         int res = -1;
397         ast_mutex_lock(&vmlock);
398         cur = users;
399         while(cur) {
400                 if ((!context || !strcasecmp(context, cur->context)) &&
401                         (!strcasecmp(mailbox, cur->mailbox)))
402                                 break;
403                 cur=cur->next;
404         }
405         if (cur) {
406                 strncpy(cur->password, newpass, sizeof(cur->password) - 1);
407                 res = 0;
408         }
409         ast_mutex_unlock(&vmlock);
410         return res;
411 }
412
413 static void vm_change_password(struct ast_vm_user *vmu, char *newpassword)
414 {
415         /*  There's probably a better way of doing this. */
416         /*  That's why I've put the password change in a separate function. */
417                 /*  This could also be done with a database function */
418         
419         FILE *configin;
420         FILE *configout;
421                 char inbuf[256];
422                 char orig[256];
423                 char tmpin[AST_CONFIG_MAX_PATH];
424                 char tmpout[AST_CONFIG_MAX_PATH];
425                 char *user, *pass, *rest, *trim;
426                 snprintf((char *)tmpin, sizeof(tmpin)-1, "%s/voicemail.conf",(char *)ast_config_AST_CONFIG_DIR);
427                 snprintf((char *)tmpout, sizeof(tmpout)-1, "%s/voicemail.conf.new",(char *)ast_config_AST_CONFIG_DIR);
428         configin = fopen((char *)tmpin,"r");
429                 if (configin)
430                 configout = fopen((char *)tmpout,"w+");
431                 else
432                         configout = NULL;
433                 if(!configin || !configout) {
434                         if (configin)
435                                 fclose(configin);
436                         else
437                                 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
438                         if (configout)
439                                 fclose(configout);
440                         else
441                                 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
442                         return;
443                 }
444
445         while (!feof(configin)) {
446                         /* Read in the line */
447                         fgets(inbuf, sizeof(inbuf), configin);
448                         if (!feof(configin)) {
449                                 /* Make a backup of it */
450                                 memcpy(orig, inbuf, sizeof(orig));
451                                 /* Strip trailing \n and comment */
452                                 inbuf[strlen(inbuf) - 1] = '\0';
453                                 user = strchr(inbuf, ';');
454                                 if (user)
455                                         *user = '\0';
456                                 user=inbuf;
457                                 while(*user < 33)
458                                         user++;
459                                 pass = strchr(user, '=');
460                                 if (pass > user) {
461                                         trim = pass - 1;
462                                         while(*trim && *trim < 33) {
463                                                 *trim = '\0';
464                                                 trim--;
465                                         }
466                                 }
467                                 if (pass) {
468                                         *pass = '\0';
469                                         pass++;
470                                         if (*pass == '>')
471                                                 pass++;
472                                         while(*pass && *pass < 33)
473                                                 pass++;
474                                 }
475                                 if (pass) {
476                                         rest = strchr(pass,',');
477                                         if (rest) {
478                                                 *rest = '\0';
479                                                 rest++;
480                                         }
481                                 } else
482                                         rest = NULL;
483                                 if (user && pass && *user && *pass && !strcmp(user, vmu->mailbox) && !strcmp(pass, vmu->password)) {
484                                         /* This is the line */
485                                         if (rest) {
486                                                 fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
487                                         } else {
488                                                 fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
489                                         }
490                                 } else {
491                                         /* Put it back like it was */
492                                         fprintf(configout, orig);
493                                 }
494                         }
495         }
496         fclose(configin);
497         fclose(configout);
498
499         unlink((char *)tmpin);
500         rename((char *)tmpout,(char *)tmpin);
501         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
502         strncpy(vmu->password, newpassword, sizeof(vmu->password) - 1);
503 }
504 #endif
505
506 static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
507 {
508         return snprintf(dest, len, "%s/voicemail/%s/%s/%s", (char *)ast_config_AST_SPOOL_DIR,context, ext, mailbox);
509 }
510
511 static int make_file(char *dest, int len, char *dir, int num)
512 {
513         return snprintf(dest, len, "%s/msg%04d", dir, num);
514 }
515
516 static int
517 inbuf(struct baseio *bio, FILE *fi)
518 {
519         int l;
520
521         if(bio->ateof)
522                 return 0;
523
524         if ( (l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
525                 if(ferror(fi))
526                         return -1;
527
528                 bio->ateof = 1;
529                 return 0;
530         }
531
532         bio->iolen= l;
533         bio->iocp= 0;
534
535         return 1;
536 }
537
538 static int 
539 inchar(struct baseio *bio, FILE *fi)
540 {
541         if(bio->iocp>=bio->iolen)
542                 if(!inbuf(bio, fi))
543                         return EOF;
544
545         return bio->iobuf[bio->iocp++];
546 }
547
548 static int
549 ochar(struct baseio *bio, int c, FILE *so)
550 {
551         if(bio->linelength>=BASELINELEN) {
552                 if(fputs(eol,so)==EOF)
553                         return -1;
554
555                 bio->linelength= 0;
556         }
557
558         if(putc(((unsigned char)c),so)==EOF)
559                 return -1;
560
561         bio->linelength++;
562
563         return 1;
564 }
565
566 static int base_encode(char *filename, FILE *so)
567 {
568         unsigned char dtable[BASEMAXINLINE];
569         int i,hiteof= 0;
570         FILE *fi;
571         struct baseio bio;
572
573         memset(&bio, 0, sizeof(bio));
574         bio.iocp = BASEMAXINLINE;
575
576         if ( !(fi = fopen(filename, "rb"))) {
577                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
578                 return -1;
579         }
580
581         for(i= 0;i<9;i++){
582                 dtable[i]= 'A'+i;
583                 dtable[i+9]= 'J'+i;
584                 dtable[26+i]= 'a'+i;
585                 dtable[26+i+9]= 'j'+i;
586         }
587         for(i= 0;i<8;i++){
588                 dtable[i+18]= 'S'+i;
589                 dtable[26+i+18]= 's'+i;
590         }
591         for(i= 0;i<10;i++){
592                 dtable[52+i]= '0'+i;
593         }
594         dtable[62]= '+';
595         dtable[63]= '/';
596
597         while(!hiteof){
598                 unsigned char igroup[3],ogroup[4];
599                 int c,n;
600
601                 igroup[0]= igroup[1]= igroup[2]= 0;
602
603                 for(n= 0;n<3;n++){
604                         if ( (c = inchar(&bio, fi)) == EOF) {
605                                 hiteof= 1;
606                                 break;
607                         }
608
609                         igroup[n]= (unsigned char)c;
610                 }
611
612                 if(n> 0){
613                         ogroup[0]= dtable[igroup[0]>>2];
614                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
615                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
616                         ogroup[3]= dtable[igroup[2]&0x3F];
617
618                         if(n<3) {
619                                 ogroup[3]= '=';
620
621                                 if(n<2)
622                                         ogroup[2]= '=';
623                         }
624
625                         for(i= 0;i<4;i++)
626                                 ochar(&bio, ogroup[i], so);
627                 }
628         }
629
630         if(fputs(eol,so)==EOF)
631                 return 0;
632
633         fclose(fi);
634
635         return 1;
636 }
637
638 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)
639 {
640         FILE *p;
641         char date[256];
642         char host[256];
643         char who[256];
644         char bound[256];
645         char fname[256];
646         char dur[256];
647         time_t t;
648         struct tm tm;
649         struct vm_zone *the_zone = NULL;
650
651         if (!strcmp(format, "wav49"))
652                 format = "WAV";
653         ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
654         p = popen(SENDMAIL, "w");
655         if (p) {
656                 gethostname(host, sizeof(host));
657                 if (strchr(srcemail, '@'))
658                         strncpy(who, srcemail, sizeof(who)-1);
659                 else {
660                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
661                 }
662                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
663                 time(&t);
664
665                 /* Does this user have a timezone specified? */
666                 if (strlen(vmu->zonetag)) {
667                         /* Find the zone in the list */
668                         struct vm_zone *z;
669                         z = zones;
670                         while (z) {
671                                 if (!strcmp(z->name, vmu->zonetag)) {
672                                         the_zone = z;
673                                         break;
674                                 }
675                                 z = z->next;
676                         }
677                 }
678
679                 if (the_zone)
680                         ast_localtime(&t,&tm,the_zone->timezone);
681                 else
682                         ast_localtime(&t,&tm,NULL);
683                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
684                 fprintf(p, "Date: %s\n", date);
685                 
686                 if (*fromstring)
687                         fprintf(p, "From: %s <%s>\n", fromstring, who);
688                 else
689                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
690                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
691
692                 if( *emailtitle)
693                 {
694                         fprintf(p, emailtitle, msgnum, mailbox) ;
695                         fprintf(p,"\n") ;
696                 }
697                 else
698                 if (pbxskip)
699                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
700                 else
701                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
702                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
703                 fprintf(p, "MIME-Version: 1.0\n");
704                 if (attach_user_voicemail) {
705                         // Something unique.
706                         snprintf(bound, sizeof(bound), "Boundary=%d%s%d", msgnum, mailbox, getpid());
707
708                         fprintf(p, "Content-Type: MULTIPART/MIXED; BOUNDARY=\"%s\"\n\n\n", bound);
709
710                         fprintf(p, "--%s\n", bound);
711                 }
712                 fprintf(p, "Content-Type: TEXT/PLAIN; charset=US-ASCII\n\n");
713                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
714                 if (emailbody) {
715                         struct ast_channel *ast = ast_channel_alloc(0);
716                         if (ast) {
717                                 char *passdata;
718                                 int vmlen = strlen(emailbody)*3 + 200;
719                                 if ((passdata = alloca(vmlen))) {
720                                         memset(passdata, 0, vmlen);
721                                         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
722                                         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
723                                         sprintf(passdata,"%d",msgnum);
724                                         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
725                                         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
726                                         pbx_builtin_setvar_helper(ast, "VM_CALLERID", (callerid ? callerid : "an unknown caller"));
727                                         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
728                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
729                                         fprintf(p, "%s\n",passdata);
730                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
731                                 ast_channel_free(ast);
732                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
733                 } else {
734                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
735
736                         "in mailbox %s from %s, on %s so you might\n"
737                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
738                         dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
739                 }
740                 if (attach_user_voicemail) {
741                         fprintf(p, "--%s\n", bound);
742                         fprintf(p, "Content-Type: audio/x-wav; name=\"msg%04d.%s\"\n", msgnum + 1, format);
743                         fprintf(p, "Content-Transfer-Encoding: BASE64\n");
744                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
745                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
746
747                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
748                         base_encode(fname, p);
749                         fprintf(p, "\n\n--%s--\n.\n", bound);
750                 }
751                 pclose(p);
752         } else {
753                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
754                 return -1;
755         }
756         return 0;
757 }
758
759 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, long duration, struct ast_vm_user *vmu)
760 {
761         FILE *p;
762         char date[256];
763         char host[256];
764         char who[256];
765         char dur[256];
766         time_t t;
767         struct tm tm;
768         struct vm_zone *the_zone = NULL;
769         p = popen(SENDMAIL, "w");
770
771         if (p) {
772                 gethostname(host, sizeof(host));
773                 if (strchr(srcemail, '@'))
774                         strncpy(who, srcemail, sizeof(who)-1);
775                 else {
776                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
777                 }
778                 snprintf(dur, sizeof(dur), "%ld:%02ld", duration / 60, duration % 60);
779                 time(&t);
780
781                 /* Does this user have a timezone specified? */
782                 if (strlen(vmu->zonetag)) {
783                         /* Find the zone in the list */
784                         struct vm_zone *z;
785                         z = zones;
786                         while (z) {
787                                 if (!strcmp(z->name, vmu->zonetag)) {
788                                         the_zone = z;
789                                         break;
790                                 }
791                                 z = z->next;
792                         }
793                 }
794
795                 if (the_zone)
796                         ast_localtime(&t,&tm,the_zone->timezone);
797                 else
798                         ast_localtime(&t,&tm,NULL);
799
800                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
801                 fprintf(p, "Date: %s\n", date);
802                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
803                 fprintf(p, "To: %s\n", pager);
804                 fprintf(p, "Subject: New VM\n\n");
805                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
806                 fprintf(p, "New %s long msg in box %s\n"
807                            "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
808                 pclose(p);
809         } else {
810                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", SENDMAIL);
811                 return -1;
812         }
813         return 0;
814 }
815
816 static int get_date(char *s, int len)
817 {
818         struct tm tm;
819         time_t t;
820         t = time(0);
821         localtime_r(&t,&tm);
822         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
823 }
824
825 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
826 {
827         int res;
828         char fn[256];
829         snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
830         if (ast_fileexists(fn, NULL, NULL) > 0) {
831                 res = ast_streamfile(chan, fn, chan->language);
832                 if (res)
833                         return -1;
834                 res = ast_waitstream(chan, ecodes);
835                 if (res)
836                         return res;
837         } else {
838                 res = ast_streamfile(chan, "vm-theperson", chan->language);
839                 if (res)
840                         return -1;
841                 res = ast_waitstream(chan, ecodes);
842                 if (res)
843                         return res;
844                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
845                 if (res)
846                         return res;
847         }
848         if (busy)
849                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
850         else
851                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
852         if (res)
853                 return -1;
854         res = ast_waitstream(chan, ecodes);
855         return res;
856 }
857
858 static int play_and_wait(struct ast_channel *chan, char *fn)
859 {
860         int d;
861         d = ast_streamfile(chan, fn, chan->language);
862         if (d)
863                 return d;
864         d = ast_waitstream(chan, AST_DIGIT_ANY);
865         return d;
866 }
867
868 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
869 {
870         char d, *fmts;
871         char comment[256];
872         int x, fmtcnt=1, res=-1,outmsg=0;
873         struct ast_frame *f;
874         struct ast_filestream *others[MAX_OTHER_FORMATS];
875         char *sfmt[MAX_OTHER_FORMATS];
876         char *stringp=NULL;
877         time_t start, end;
878         struct ast_dsp *sildet;         /* silence detector dsp */
879         int totalsilence = 0;
880         int dspsilence = 0;
881         int gotsilence = 0;             /* did we timeout for silence? */
882         int rfmt=0;     
883         
884         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
885         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
886
887         if (playfile) { 
888                 d = play_and_wait(chan, playfile);
889                 if (!d)
890                         d = ast_streamfile(chan, "beep",chan->language);
891                 if (!d)
892                         d = ast_waitstream(chan,"");
893                 if (d < 0)
894                         return -1;
895         }
896         
897         fmts = ast_strdupa(fmt);
898         
899         stringp=fmts;
900         strsep(&stringp, "|");
901         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
902         sfmt[0] = ast_strdupa(fmts);
903         
904         while((fmt = strsep(&stringp, "|"))) {
905                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
906                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
907                         break;
908                 }
909                 sfmt[fmtcnt++] = ast_strdupa(fmt);
910         }
911
912         if (maxtime)
913                 time(&start);
914         for (x=0;x<fmtcnt;x++) {
915                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
916                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
917                         
918                 if (!others[x]) {
919                         break;
920                 }
921         }
922         
923         sildet = ast_dsp_new(); //Create the silence detector
924         if (!sildet) {
925                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
926                 return -1;
927         }
928         ast_dsp_set_threshold(sildet, silencethreshold);
929         
930         if (maxsilence > 0) {
931                 rfmt = chan->readformat;
932                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
933                 if (res < 0) {
934                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
935                         return -1;
936                 }
937         }
938                                                 
939         if (x == fmtcnt) {
940         /* Loop forever, writing the packets we read to the writer(s), until
941            we read a # or get a hangup */
942                 f = NULL;
943                 for(;;) {
944                         res = ast_waitfor(chan, 2000);
945                         if (!res) {
946                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
947                                 /* Try one more time in case of masq */
948                                 res = ast_waitfor(chan, 2000);
949                                 if (!res) {
950                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
951                                         res = -1;
952                                 }
953                         }
954                         
955                         if (res < 0) {
956                                 f = NULL;
957                                 break;
958                         }
959                         f = ast_read(chan);
960                         if (!f)
961                                 break;
962                         if (f->frametype == AST_FRAME_VOICE) {
963                                 /* write each format */
964                                 for (x=0;x<fmtcnt;x++) {
965                                         res = ast_writestream(others[x], f);
966                                 }
967                                 
968                                 /* Silence Detection */
969                                 if (maxsilence > 0) {
970                                         dspsilence = 0;
971                                         ast_dsp_silence(sildet, f, &dspsilence);
972                                         if (dspsilence)
973                                                 totalsilence = dspsilence;
974                                         else
975                                                 totalsilence = 0;
976                                         
977                                         if (totalsilence > maxsilence) {
978                                         /* Ended happily with silence */
979                                         ast_frfree(f);
980                                         gotsilence = 1;
981                                         outmsg=2;
982                                         break;
983                                         }
984                                 }
985                                 /* Exit on any error */
986                                 if (res) {
987                                         ast_log(LOG_WARNING, "Error writing frame\n");
988                                         ast_frfree(f);
989                                         break;
990                                 }
991                         } else if (f->frametype == AST_FRAME_VIDEO) {
992                                 /* Write only once */
993                                 ast_writestream(others[0], f);
994                         } else if (f->frametype == AST_FRAME_DTMF) {
995                                 if (f->subclass == '#') {
996                                         if (option_verbose > 2) 
997                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
998                                         res = '#';
999                                         outmsg = 2;
1000                                         ast_frfree(f);
1001                                         break;
1002                                 }
1003                         }
1004                         if (maxtime) {
1005                                 time(&end);
1006                                 if (maxtime < (end - start)) {
1007                                         if (option_verbose > 2)
1008                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1009                                         res = 't';
1010                                         ast_frfree(f);
1011                                         break;
1012                                 }
1013                         }
1014                         ast_frfree(f);
1015                 }
1016                 if (!f) {
1017                         if (option_verbose > 2) 
1018                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1019                         res = -1;
1020                         outmsg=1;
1021                 }
1022         } else {
1023                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
1024         }
1025
1026         for (x=0;x<fmtcnt;x++) {
1027                 if (!others[x])
1028                         break;
1029                 if (totalsilence)
1030                         ast_stream_rewind(others[x], totalsilence-200);
1031                 else
1032                         ast_stream_rewind(others[x], 200);
1033                 ast_truncstream(others[x]);
1034                 ast_closestream(others[x]);
1035         }
1036         if (rfmt) {
1037                 if (ast_set_read_format(chan, rfmt)) {
1038                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1039                 }
1040         }
1041         if (outmsg) {
1042                 if (outmsg > 1) {
1043                 /* Let them know it worked */
1044                         ast_streamfile(chan, "vm-msgsaved", chan->language);
1045                         ast_waitstream(chan, "");
1046                 }
1047         }       
1048
1049         
1050         return res;
1051 }
1052
1053 static void free_user(struct ast_vm_user *vmu)
1054 {
1055         if (vmu->alloced)
1056                 free(vmu);
1057 }
1058
1059 static void free_zone(struct vm_zone *z)
1060 {
1061         free(z);
1062 }
1063
1064 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1065 {
1066         char comment[256];
1067         char txtfile[256];
1068         FILE *txt;
1069         int res = 0;
1070         int msgnum;
1071         char date[256];
1072         char dir[256];
1073         char fn[256];
1074         char prefile[256]="";
1075         char fmt[80];
1076         char *context;
1077         char *ecodes = "#";
1078         char *stringp;
1079         time_t start;
1080         time_t end;
1081         char tmp[256] = "";
1082         struct ast_vm_user *vmu;
1083         struct ast_vm_user svm;
1084         
1085         strncpy(tmp, ext, sizeof(tmp) - 1);
1086         ext = tmp;
1087         context = strchr(tmp, '@');
1088         if (context) {
1089                 *context = '\0';
1090                 context++;
1091         }
1092
1093         if ((vmu = find_user(&svm, context, ext))) {
1094                 /* Setup pre-file if appropriate */
1095                 if (busy)
1096                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1097                 else if (unavail)
1098                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1099                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1100                 /* It's easier just to try to make it than to check for its existence */
1101                 if (mkdir(dir, 0700) && (errno != EEXIST))
1102                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1103                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1104                 /* It's easier just to try to make it than to check for its existence */
1105                 if (mkdir(dir, 0700) && (errno != EEXIST))
1106                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1107                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1108                 if (mkdir(dir, 0700) && (errno != EEXIST))
1109                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1110                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1111                         ecodes = "#0";
1112                 /* Play the beginning intro if desired */
1113                 if (strlen(prefile)) {
1114                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1115                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1116                                     res = ast_waitstream(chan, "#0");
1117                         } else {
1118                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1119                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1120                         }
1121                         if (res < 0) {
1122                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1123                                 free_user(vmu);
1124                                 return -1;
1125                         }
1126                 }
1127                 if (res == '#') {
1128                         /* On a '#' we skip the instructions */
1129                         silent = 1;
1130                         res = 0;
1131                 }
1132                 if (!res && !silent) {
1133                         res = ast_streamfile(chan, INTRO, chan->language);
1134                         if (!res)
1135                                 res = ast_waitstream(chan, ecodes);
1136                         if (res == '#') {
1137                                 silent = 1;
1138                                 res = 0;
1139                         }
1140                 }
1141                 /* Check for a '0' here */
1142                 if (res == '0') {
1143                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1144                         if (strlen(chan->macrocontext))
1145                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1146                         chan->priority = 0;
1147                         free_user(vmu);
1148                         return 0;
1149                 }
1150                 if (res >= 0) {
1151                         /* Unless we're *really* silent, try to send the beep */
1152                         res = ast_streamfile(chan, "beep", chan->language);
1153                         if (!res)
1154                                 res = ast_waitstream(chan, "");
1155                 }
1156                 if (res < 0) {
1157                         free_user(vmu);
1158                         return -1;
1159                 }
1160                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1161                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1162                 if (strlen(fmt)) {
1163                         msgnum = 0;
1164                         do {
1165                                 make_file(fn, sizeof(fn), dir, msgnum);
1166                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1167                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1168                                                                         vmu->fullname, ext, chan->name);
1169                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1170                                         break;
1171                                 msgnum++;
1172                         } while(msgnum < MAXMSG);
1173                         if (msgnum < MAXMSG) {
1174                                 /* Store information */
1175                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1176                                 txt = fopen(txtfile, "w+");
1177                                 if (txt) {
1178                                         get_date(date, sizeof(date));
1179                                         time(&start);
1180                                         fprintf(txt, 
1181 ";\n"
1182 "; Message Information file\n"
1183 ";\n"
1184 "[message]\n"
1185 "origmailbox=%s\n"
1186 "context=%s\n"
1187 "exten=%s\n"
1188 "priority=%d\n"
1189 "callerchan=%s\n"
1190 "callerid=%s\n"
1191 "origdate=%s\n"
1192 "origtime=%ld\n",
1193         ext,
1194         chan->context,
1195         chan->exten,
1196         chan->priority,
1197         chan->name,
1198         chan->callerid ? chan->callerid : "Unknown",
1199         date, (long)time(NULL));
1200                                         fclose(txt);
1201                                 } else
1202                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1203                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1204                                 if (res > 0)
1205                                         res = 0;
1206                                 txt = fopen(txtfile, "a");
1207                                 if (txt) {
1208                                         time(&end);
1209                                         fprintf(txt, "duration=%ld\n", (long)(end-start));
1210                                         fclose(txt);
1211                                 }
1212                                 stringp = fmt;
1213                                 strsep(&stringp, "|");
1214                                 /* Send e-mail if applicable */
1215                                 if (strlen(vmu->email)) {
1216                                         int attach_user_voicemail = attach_voicemail;
1217                                         char *myserveremail = serveremail;
1218                                         if (vmu->attach > -1)
1219                                                 attach_user_voicemail = vmu->attach;
1220                                         if (strlen(vmu->serveremail))
1221                                                 myserveremail = vmu->serveremail;
1222                                         sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1223                                 }
1224                                 if (strlen(vmu->pager)) {
1225                                         char *myserveremail = serveremail;
1226                                         if (strlen(vmu->serveremail))
1227                                                 myserveremail = vmu->serveremail;
1228                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1229                                 }
1230                         } else
1231                                 ast_log(LOG_WARNING, "No more messages possible\n");
1232                 } else
1233                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1234                 free_user(vmu);
1235         } else
1236                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1237         /* Leave voicemail for someone */
1238         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1239         return res;
1240 }
1241
1242 static char *mbox(int id)
1243 {
1244         switch(id) {
1245         case 0:
1246                 return "INBOX";
1247         case 1:
1248                 return "Old";
1249         case 2:
1250                 return "Work";
1251         case 3:
1252                 return "Family";
1253         case 4:
1254                 return "Friends";
1255         case 5:
1256                 return "Cust1";
1257         case 6:
1258                 return "Cust2";
1259         case 7:
1260                 return "Cust3";
1261         case 8:
1262                 return "Cust4";
1263         case 9:
1264                 return "Cust5";
1265         default:
1266                 return "Unknown";
1267         }
1268 }
1269
1270 static int count_messages(char *dir)
1271 {
1272         int x;
1273         char fn[256];
1274         for (x=0;x<MAXMSG;x++) {
1275                 make_file(fn, sizeof(fn), dir, x);
1276                 if (ast_fileexists(fn, NULL, NULL) < 1)
1277                         break;
1278         }
1279         return x;
1280 }
1281
1282 static int say_and_wait(struct ast_channel *chan, int num)
1283 {
1284         int d;
1285         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1286         return d;
1287 }
1288
1289 static int copy(char *infile, char *outfile)
1290 {
1291         int ifd;
1292         int ofd;
1293         int res;
1294         int len;
1295         char buf[4096];
1296         if ((ifd = open(infile, O_RDONLY)) < 0) {
1297                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1298                 return -1;
1299         }
1300         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1301                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1302                 close(ifd);
1303                 return -1;
1304         }
1305         do {
1306                 len = read(ifd, buf, sizeof(buf));
1307                 if (len < 0) {
1308                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1309                         close(ifd);
1310                         close(ofd);
1311                         unlink(outfile);
1312                 }
1313                 if (len) {
1314                         res = write(ofd, buf, len);
1315                         if (res != len) {
1316                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1317                                 close(ifd);
1318                                 close(ofd);
1319                                 unlink(outfile);
1320                         }
1321                 }
1322         } while(len);
1323         close(ifd);
1324         close(ofd);
1325         return 0;
1326 }
1327
1328 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1329 {
1330         char sfn[256];
1331         char dfn[256];
1332         char ddir[256];
1333         char txt[256];
1334         char ntxt[256];
1335         char *dbox = mbox(box);
1336         int x;
1337         make_file(sfn, sizeof(sfn), dir, msg);
1338         make_dir(ddir, sizeof(ddir), context, username, dbox);
1339         mkdir(ddir, 0700);
1340         for (x=0;x<MAXMSG;x++) {
1341                 make_file(dfn, sizeof(dfn), ddir, x);
1342                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1343                         break;
1344         }
1345         if (x >= MAXMSG)
1346                 return -1;
1347         ast_filecopy(sfn, dfn, NULL);
1348         if (strcmp(sfn, dfn)) {
1349                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1350                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1351                 copy(txt, ntxt);
1352         }
1353         return 0;
1354 }
1355
1356 static int adsi_logo(unsigned char *buf)
1357 {
1358         int bytes = 0;
1359         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1360         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1361         return bytes;
1362 }
1363
1364 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1365 {
1366         char buf[256];
1367         int bytes=0;
1368         int x;
1369         char num[5];
1370
1371         *useadsi = 0;
1372         bytes += adsi_data_mode(buf + bytes);
1373         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1374
1375         bytes = 0;
1376         bytes += adsi_logo(buf);
1377         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1378 #ifdef DISPLAY
1379         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1380 #endif
1381         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1382         bytes += adsi_data_mode(buf + bytes);
1383         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1384
1385         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1386                 bytes = 0;
1387                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1388                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1389                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1390       bytes += adsi_voice_mode(buf + bytes, 0);
1391                 bytes += adsi_voice_mode(buf + bytes, 0);
1392                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1393                 return 0;
1394         }
1395
1396 #ifdef DISPLAY
1397         /* Add a dot */
1398         bytes = 0;
1399         bytes += adsi_logo(buf);
1400         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1401         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1402         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1403         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1404 #endif
1405         bytes = 0;
1406         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1407         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1408         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1409         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1410         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1411         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1412         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1413
1414 #ifdef DISPLAY
1415         /* Add another dot */
1416         bytes = 0;
1417         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1418       bytes += adsi_voice_mode(buf + bytes, 0);
1419
1420         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1421         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1422 #endif
1423
1424         bytes = 0;
1425         /* These buttons we load but don't use yet */
1426         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1427         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1428         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1429         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1430         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1431         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1432         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1433
1434 #ifdef DISPLAY
1435         /* Add another dot */
1436         bytes = 0;
1437         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1438         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1439         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1440 #endif
1441
1442         bytes = 0;
1443         for (x=0;x<5;x++) {
1444                 snprintf(num, sizeof(num), "%d", x);
1445                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1446         }
1447         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1448         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1449
1450 #ifdef DISPLAY
1451         /* Add another dot */
1452         bytes = 0;
1453         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1454         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1455         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1456 #endif
1457
1458         if (adsi_end_download(chan)) {
1459                 bytes = 0;
1460                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1461                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1462                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1463                 bytes += adsi_voice_mode(buf + bytes, 0);
1464                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1465                 return 0;
1466         }
1467         bytes = 0;
1468         bytes += adsi_download_disconnect(buf + bytes);
1469         bytes += adsi_voice_mode(buf + bytes, 0);
1470         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1471
1472         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1473
1474 #ifdef DISPLAY
1475         /* Add last dot */
1476         bytes = 0;
1477         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1478         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1479 #endif
1480         ast_log(LOG_DEBUG, "Restarting session...\n");
1481
1482         bytes = 0;
1483         /* Load the session now */
1484         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1485                 *useadsi = 1;
1486                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1487         } else
1488                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1489
1490         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1491         return 0;
1492 }
1493
1494 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1495 {
1496         int x;
1497         if (!adsi_available(chan))
1498           return;
1499         x = adsi_load_session(chan, adapp, adver, 1);
1500         if (x < 0)
1501                 return;
1502         if (!x) {
1503                 if (adsi_load_vmail(chan, useadsi)) {
1504                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1505                         return;
1506                 }
1507         } else
1508                 *useadsi = 1;
1509 }
1510
1511 static void adsi_login(struct ast_channel *chan)
1512 {
1513         char buf[256];
1514         int bytes=0;
1515         unsigned char keys[8];
1516         int x;
1517         if (!adsi_available(chan))
1518                 return;
1519
1520         for (x=0;x<8;x++)
1521                 keys[x] = 0;
1522         /* Set one key for next */
1523         keys[3] = ADSI_KEY_APPS + 3;
1524
1525         bytes += adsi_logo(buf + bytes);
1526         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1527         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1528         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1529         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1530         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1531         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1532         bytes += adsi_set_keys(buf + bytes, keys);
1533         bytes += adsi_voice_mode(buf + bytes, 0);
1534         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1535 }
1536
1537 static void adsi_password(struct ast_channel *chan)
1538 {
1539         char buf[256];
1540         int bytes=0;
1541         unsigned char keys[8];
1542         int x;
1543         if (!adsi_available(chan))
1544                 return;
1545
1546         for (x=0;x<8;x++)
1547                 keys[x] = 0;
1548         /* Set one key for next */
1549         keys[3] = ADSI_KEY_APPS + 3;
1550       bytes += adsi_voice_mode(buf + bytes, 0);
1551
1552
1553         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1554         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1555         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1556         bytes += adsi_set_keys(buf + bytes, keys);
1557         bytes += adsi_voice_mode(buf + bytes, 0);
1558         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1559 }
1560
1561 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1562 {
1563         char buf[256];
1564         int bytes=0;
1565         unsigned char keys[8];
1566         int x,y;
1567
1568         if (!adsi_available(chan))
1569                 return;
1570
1571         for (x=0;x<5;x++) {
1572                 y = ADSI_KEY_APPS + 12 + start + x;
1573                 if (y > ADSI_KEY_APPS + 12 + 4)
1574                         y = 0;
1575                 keys[x] = ADSI_KEY_SKT | y;
1576         }
1577         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1578         keys[6] = 0;
1579         keys[7] = 0;
1580
1581         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1582         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1583         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1584         bytes += adsi_set_keys(buf + bytes, keys);
1585         bytes += adsi_voice_mode(buf + bytes, 0);
1586
1587         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1588 }
1589
1590 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1591 {
1592         int bytes=0;
1593         char buf[256], buf1[256], buf2[256];
1594         char fn2[256];
1595       bytes += adsi_voice_mode(buf + bytes, 0);
1596
1597         char cid[256]="";
1598         char *val;
1599         char *name, *num;
1600         char datetime[21]="";
1601         FILE *f;
1602
1603         unsigned char keys[8];
1604
1605         int x;
1606
1607         if (!adsi_available(chan))
1608                 return;
1609
1610         /* Retrieve important info */
1611         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1612         f = fopen(fn2, "r");
1613         if (f) {
1614                 while(!feof(f)) {       
1615                         fgets(buf, sizeof(buf), f);
1616                         if (!feof(f)) {
1617                                 char *stringp=NULL;
1618                                 stringp=buf;
1619                                 strsep(&stringp, "=");
1620                                 val = strsep(&stringp, "=");
1621                                 if (val && strlen(val)) {
1622                                         if (!strcmp(buf, "callerid"))
1623                                                 strncpy(cid, val, sizeof(cid) - 1);
1624                                         if (!strcmp(buf, "origdate"))
1625                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1626                                 }
1627                         }
1628                 }
1629                 fclose(f);
1630         }
1631         /* New meaning for keys */
1632         for (x=0;x<5;x++)
1633                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1634         keys[6] = 0x0;
1635       bytes += adsi_voice_mode(buf + bytes, 0);
1636
1637         keys[7] = 0x0;
1638
1639         if (!msg) {
1640                 /* No prev key, provide "Folder" instead */
1641                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1642         }
1643         if (msg >= last) {
1644                 /* If last message ... */
1645                 if (msg) {
1646                         /* but not only message, provide "Folder" instead */
1647                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1648       bytes += adsi_voice_mode(buf + bytes, 0);
1649
1650                 } else {
1651                         /* Otherwise if only message, leave blank */
1652                         keys[3] = 1;
1653                 }
1654         }
1655
1656         if (strlen(cid)) {
1657                 ast_callerid_parse(cid, &name, &num);
1658                 if (!name)
1659                         name = num;
1660         } else
1661                 name = "Unknown Caller";
1662
1663         /* If deleted, show "undeleted" */
1664       bytes += adsi_voice_mode(buf + bytes, 0);
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                                 break;
2550                         case '9':
2551                                 if (useadsi)
2552                                         adsi_folders(chan, 1, "Save to folder...");
2553                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2554                                 box = 0;        /* Shut up compiler */
2555                                 if (cmd == '#') {
2556                                         cmd = 0;
2557                                         break;
2558                                 } else if (cmd > 0) {
2559                                         box = cmd = cmd - '0';
2560                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2561                                         vms.deleted[vms.curmsg]=1;
2562                                 }
2563                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2564                                 if (useadsi)
2565                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2566                                 if (!cmd)
2567                                         cmd = play_and_wait(chan, "vm-message");
2568                                 if (!cmd)
2569                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2570                                 if (!cmd)
2571                                         cmd = play_and_wait(chan, "vm-savedto");
2572                                 if (!cmd) {
2573                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2574                                         cmd = play_and_wait(chan, vms.fn);
2575                                 }
2576                                 if (!cmd)
2577                                         cmd = play_and_wait(chan, "vm-messages");
2578                                 break;
2579                         case '*':
2580                                 if (!vms.starting) {
2581                                         cmd = play_and_wait(chan, "vm-onefor");
2582                                         if (!cmd)
2583                                                 cmd = play_and_wait(chan, vms.vmbox);
2584                                         if (!cmd)
2585                                                 cmd = play_and_wait(chan, "vm-messages");
2586                                         if (!cmd)
2587                                                 cmd = play_and_wait(chan, "vm-opts");
2588                                 } else
2589                                         cmd = 0;
2590                                 break;
2591                         case '0':
2592                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2593                                 if (useadsi)
2594                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2595                                 break;
2596                         default:        /* Nothing */
2597                                 cmd = vm_instructions(chan, &vms);
2598                                 break;
2599                         }
2600                 }
2601                 if ((cmd == 't') || (cmd == '#')) {
2602                         /* Timeout */
2603                         res = 0;
2604                 } else {
2605                         /* Hangup */
2606                         res = -1;
2607                 }
2608         }
2609 out:
2610         if (res > -1) {
2611                 ast_stopstream(chan);
2612                 adsi_goodbye(chan);
2613                 res = play_and_wait(chan, "vm-goodbye");
2614                 if (res > 0)
2615                         res = 0;
2616                 if (useadsi)
2617                         adsi_unload_session(chan);
2618         }
2619         if (vmu)
2620                 close_mailbox(&vms, vmu);
2621         if (vmu)
2622                 free_user(vmu);
2623         if (valid) {
2624                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2625         }
2626         LOCAL_USER_REMOVE(u);
2627         return res;
2628
2629 }
2630
2631 static int vm_exec(struct ast_channel *chan, void *data)
2632 {
2633         int res=0, silent=0, busy=0, unavail=0;
2634         struct localuser *u;
2635         char tmp[256], *ext;
2636         
2637         LOCAL_USER_ADD(u);
2638         if (chan->_state != AST_STATE_UP)
2639                 ast_answer(chan);
2640         if (data)
2641                 strncpy(tmp, data, sizeof(tmp) - 1);
2642         else {
2643                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2644                 if (res < 0)
2645                         return res;
2646                 if (!strlen(tmp))
2647                         return 0;
2648         }
2649         ext = tmp;
2650         while(*ext) {
2651                 if (*ext == 's') {
2652                         silent = 2;
2653                         ext++;
2654                 } else if (*ext == 'b') {
2655                         busy=1;
2656                         ext++;
2657                 } else if (*ext == 'u') {
2658                         unavail=1;
2659                         ext++;
2660                 } else 
2661                         break;
2662         }
2663         res = leave_voicemail(chan, ext, silent, busy, unavail);
2664         LOCAL_USER_REMOVE(u);
2665         return res;
2666 }
2667
2668 static int append_mailbox(char *context, char *mbox, char *data)
2669 {
2670         /* Assumes lock is already held */
2671         char tmp[256] = "";
2672         char *stringp;
2673         char *s;
2674         struct ast_vm_user *vmu;
2675         strncpy(tmp, data, sizeof(tmp));
2676         vmu = malloc(sizeof(struct ast_vm_user));
2677         if (vmu) {
2678                 memset(vmu, 0, sizeof(struct ast_vm_user));
2679                 strncpy(vmu->context, context, sizeof(vmu->context));
2680                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2681                 vmu->attach = -1;
2682                 stringp = tmp;
2683                 if ((s = strsep(&stringp, ","))) 
2684                         strncpy(vmu->password, s, sizeof(vmu->password));
2685                 if (stringp && (s = strsep(&stringp, ","))) 
2686                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2687                 if (stringp && (s = strsep(&stringp, ","))) 
2688                         strncpy(vmu->email, s, sizeof(vmu->email));
2689                 if (stringp && (s = strsep(&stringp, ","))) 
2690                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2691                 if (stringp && (s = strsep(&stringp, ","))) 
2692                         apply_options(vmu, s);
2693                 vmu->next = NULL;
2694                 if (usersl)
2695                         usersl->next = vmu;
2696                 else
2697                         users = vmu;
2698                 usersl = vmu;
2699         }
2700         return 0;
2701 }
2702
2703 static int load_config(void)
2704 {
2705         struct ast_vm_user *cur, *l;
2706         struct vm_zone *zcur, *zl;
2707         struct ast_config *cfg;
2708         char *cat;
2709         struct ast_variable *var;
2710         char *astattach;
2711         char *silencestr;
2712         char *thresholdstr;
2713         char *fmt;
2714         char *astemail;
2715         char *s;
2716         int x;
2717
2718         cfg = ast_load(VOICEMAIL_CONFIG);
2719         ast_mutex_lock(&vmlock);
2720         cur = users;
2721         while(cur) {
2722                 l = cur;
2723                 cur = cur->next;
2724                 free_user(l);
2725         }
2726         zcur = zones;
2727         while(zcur) {
2728                 zl = zcur;
2729                 zcur = zcur->next;
2730                 free_zone(zl);
2731         }
2732         zones = NULL;
2733         zonesl = NULL;
2734         users = NULL;
2735         usersl = NULL;
2736         if (cfg) {
2737                 /* General settings */
2738                 attach_voicemail = 1;
2739                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
2740                         astattach = "yes";
2741                 attach_voicemail = ast_true(astattach);
2742                 maxsilence = 0;
2743                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2744                         maxsilence = atoi(silencestr);
2745                         if (maxsilence > 0)
2746                                 maxsilence *= 1000;
2747                 }
2748                 
2749                 silencethreshold = 256;
2750                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2751                         silencethreshold = atoi(thresholdstr);
2752                 
2753                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
2754                         astemail = ASTERISK_USERNAME;
2755                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2756                 
2757                 vmmaxmessage = 0;
2758                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2759                         if (sscanf(s, "%d", &x) == 1) {
2760                                 vmmaxmessage = x;
2761                         } else {
2762                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
2763                         }
2764                 }
2765                 fmt = ast_variable_retrieve(cfg, "general", "format");
2766                 if (!fmt)
2767                         fmt = "wav";    
2768                 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2769
2770                 skipms = 3000;
2771                 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2772                         if (sscanf(s, "%d", &x) == 1) {
2773                                 maxgreet = x;
2774                         } else {
2775                                 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2776                         }
2777                 }
2778
2779                 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2780                         if (sscanf(s, "%d", &x) == 1) {
2781                                 skipms = x;
2782                         } else {
2783                                 ast_log(LOG_WARNING, "Invalid skipms value\n");
2784                         }
2785                 }
2786
2787                 maxlogins = 3;
2788                 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2789                         if (sscanf(s, "%d", &x) == 1) {
2790                                 maxlogins = x;
2791                         } else {
2792                                 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2793                         }
2794                 }
2795
2796 #ifdef USEMYSQLVM
2797                 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2798                         strcpy(dbuser, "test");
2799                 } else {
2800                         strcpy(dbuser, s);
2801                 }
2802                 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2803                         strcpy(dbpass, "test");
2804                 } else {
2805                         strcpy(dbpass, s);
2806                 }
2807                 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2808                         strcpy(dbhost, "");
2809                 } else {
2810                         strcpy(dbhost, s);
2811                 }
2812                 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2813                         strcpy(dbname, "vmdb");
2814                 } else {
2815                         strcpy(dbname, s);
2816                 }
2817 #endif
2818
2819 #ifdef USEPOSTGRESVM
2820                 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2821                         strcpy(dboption, "dboption not-specified in voicemail.conf");
2822                 } else {
2823                         strcpy(dboption, s);
2824                 }
2825 #endif
2826                 cat = ast_category_browse(cfg, NULL);
2827                 while(cat) {
2828                         if (strcasecmp(cat, "general")) {
2829                                 var = ast_variable_browse(cfg, cat);
2830                                 if (strcasecmp(cat, "zonemessages")) {
2831 #ifndef USESQLVM
2832                                         /* Process mailboxes in this context */
2833                                         while(var) {
2834                                                 append_mailbox(cat, var->name, var->value);
2835                                                 var = var->next;
2836                                         }
2837 #endif
2838                                 } else {
2839                                         /* Timezones in this context */
2840                                         while(var) {
2841                                                 struct vm_zone *z;
2842                                                 z = malloc(sizeof(struct vm_zone));
2843                                                 if (z != NULL) {
2844                                                         char *msg_format, *timezone;
2845                                                         msg_format = ast_strdupa(var->value);
2846                                                         if (msg_format != NULL) {
2847                                                                 timezone = strsep(&msg_format, "|");
2848                                                                 strncpy(z->name, var->name, sizeof(z->name) - 1);
2849                                                                 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2850                                                                 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2851                                                                 z->next = NULL;
2852                                                                 if (zones) {
2853                                                                         zonesl->next = z;
2854                                                                         zonesl = z;
2855                                                                 } else {
2856                                                                         zones = z;
2857                                                                         zonesl = z;
2858                                                                 }
2859                                                         } else {
2860                                                                 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2861                                                                 free(z);
2862                                                                 return -1;
2863                                                         }
2864                                                 } else {
2865                                                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2866                                                         return -1;
2867                                                 }
2868                                                 var = var->next;
2869                                         }
2870                                 }
2871                         }
2872                         cat = ast_category_browse(cfg, cat);
2873                 }
2874                 memset(fromstring,0,sizeof(fromstring));
2875                 memset(emailtitle,0,sizeof(emailtitle));
2876                 if (emailbody) {
2877                         free(emailbody);
2878                         emailbody = NULL;
2879                 }
2880                 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2881                         pbxskip = ast_true(s);
2882                 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2883                         strncpy(fromstring,s,sizeof(fromstring)-1);
2884                 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2885                         strncpy(emailtitle,s,sizeof(emailtitle)-1);
2886                 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2887                         char *tmpread, *tmpwrite;
2888                         emailbody = strdup(s);
2889
2890                         /* substitute strings \t and \n into the apropriate characters */
2891                         tmpread = tmpwrite = emailbody;
2892                         while ((tmpwrite = strchr(tmpread,'\\'))) {
2893                                 int len = strlen("\n");
2894                                 switch (tmpwrite[1]) {
2895                                         case 'n':
2896                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2897                                                 strncpy(tmpwrite,"\n",len);
2898                                                 break;
2899                                         case 't':
2900                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2901                                                 strncpy(tmpwrite,"\t",len);
2902                                                 break;
2903                                         default:
2904                                                 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2905                                 }
2906                                 tmpread = tmpwrite+len;
2907                         }
2908                 }
2909                 ast_destroy(cfg);
2910                 ast_mutex_unlock(&vmlock);
2911                 return 0;
2912         } else {
2913                 ast_mutex_unlock(&vmlock);
2914                 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2915                 return -1;
2916         }
2917 }
2918
2919 int reload(void)
2920 {
2921         return(load_config());
2922 }
2923
2924 int unload_module(void)
2925 {
2926         int res;
2927         STANDARD_HANGUP_LOCALUSERS;
2928         res = ast_unregister_application(app);
2929         res |= ast_unregister_application(app2);
2930         sql_close();
2931         return res;
2932 }
2933
2934 int load_module(void)
2935 {
2936         int res;
2937         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2938         res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2939         if (res)
2940                 return(res);
2941
2942         if ((res=load_config())) {
2943                 return(res);
2944         }
2945
2946         if ((res = sql_init())) {
2947                 ast_log(LOG_WARNING, "SQL init\n");
2948                 return res;
2949         }
2950         return res;
2951 }
2952
2953 char *description(void)
2954 {
2955         return tdesc;
2956 }
2957
2958 int usecount(void)
2959 {
2960         int res;
2961         STANDARD_USECOUNT(res);
2962         return res;
2963 }
2964
2965 char *key()
2966 {
2967         return ASTERISK_GPL_KEY;
2968 }