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