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