a7b43a88d7ec4c16979bef79b7c9b3bbd57f0509
[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                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
1932                                 if (!strcasecmp(s, "wav49"))
1933                                         s = "WAV";
1934                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1935                                 ast_log(LOG_DEBUG, sys);
1936                                 system(sys);
1937                         }
1938                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1939                         ast_log(LOG_DEBUG, sys);
1940                         system(sys);
1941                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1942
1943                         /* load the information on the source message so we can send an e-mail like a new message */
1944                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1945                         if ((mif=ast_load(miffile))) {
1946
1947               /* set callerid and duration variables */
1948               snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1949               s = ast_variable_retrieve(mif, NULL, "duration");
1950               if (s)
1951                       duration = atol(s);
1952               else
1953                       duration = 0;
1954               if (strlen(receiver->email)) {
1955                                 int attach_user_voicemail = attach_voicemail;
1956                                 char *myserveremail = serveremail;
1957                                 if (receiver->attach > -1)
1958                                         attach_user_voicemail = receiver->attach;
1959                                 if (strlen(receiver->serveremail))
1960                                         myserveremail = receiver->serveremail;
1961                       sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, duration, attach_user_voicemail);
1962               }
1963              
1964                         if (strlen(receiver->pager)) {
1965                                 char *myserveremail = serveremail;
1966                                 if (strlen(receiver->serveremail))
1967                                         myserveremail = receiver->serveremail;
1968                                 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1969                         }
1970                           
1971                           ast_destroy(mif); /* or here */
1972                         }
1973                         /* Leave voicemail for someone */
1974                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1975
1976                         /* give confirmatopm that the message was saved */
1977                         res = play_and_wait(chan, "vm-message");
1978                         if (!res)
1979                                 res = play_and_wait(chan, "vm-saved");
1980                         free_user(receiver);
1981                         break;
1982                 } else {
1983                         res = play_and_wait(chan, "pbx-invalid");
1984                 }
1985         }
1986         return res;
1987 }
1988
1989 struct vm_state {
1990         char curbox[80];
1991         char username[80];
1992         char curdir[256];
1993         char vmbox[256];
1994         char fn[256];
1995         char fn2[256];
1996         int deleted[MAXMSG];
1997         int heard[MAXMSG];
1998         int curmsg;
1999         int lastmsg;
2000         int newmessages;
2001         int oldmessages;
2002         int starting;
2003         int repeats;
2004 };
2005
2006
2007 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2008 {
2009         int res;
2010         if ((res = ast_streamfile(chan, file, chan->language))) 
2011                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2012         if (!res)
2013                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2014         return res;
2015 }
2016
2017 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2018 {
2019         int res;
2020         if ((res = ast_streamfile(chan, file, chan->language)))
2021                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2022         if (!res)
2023                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2024         return res;
2025 }
2026
2027 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2028 {
2029         int res = 0;
2030         char filename[256], *origtime;
2031         struct vm_zone *the_zone = NULL;
2032         struct ast_config *msg_cfg;
2033         time_t t;
2034         long tin;
2035
2036         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2037         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2038         msg_cfg = ast_load(filename);
2039         if (!msg_cfg) {
2040                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2041                 return 0;
2042         }
2043
2044         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2045                 return 0;
2046         if (sscanf(origtime,"%ld",&tin) < 1) {
2047                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2048                 return 0;
2049         }
2050         t = tin;
2051         ast_destroy(msg_cfg);
2052
2053         /* Does this user have a timezone specified? */
2054         if (strlen(vmu->zonetag)) {
2055                 /* Find the zone in the list */
2056                 struct vm_zone *z;
2057                 z = zones;
2058                 while (z) {
2059                         if (!strcmp(z->name, vmu->zonetag)) {
2060                                 the_zone = z;
2061                                 break;
2062                         }
2063                         z = z->next;
2064                 }
2065         }
2066
2067 /* No internal variable parsing for now, so we'll comment it out for the time being */
2068 #if 0
2069         /* Set the DIFF_* variables */
2070         localtime_r(&t, &time_now);
2071         gettimeofday(&tv_now,NULL);
2072         tnow = tv_now.tv_sec;
2073         localtime_r(&tnow,&time_then);
2074
2075         /* Day difference */
2076         if (time_now.tm_year == time_then.tm_year)
2077                 sprintf(temp,"%d",time_now.tm_yday);
2078         else
2079                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2080         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2081
2082         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2083 #endif
2084         if (the_zone)
2085                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2086         else
2087                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2088 #if 0
2089         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2090 #endif
2091         return res;
2092 }
2093
2094 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2095 {
2096         int res = 0;
2097         vms->starting = 0; 
2098         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2099         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2100         if (!msg)
2101                 res = wait_file2(chan, vms, "vm-first");
2102         else if (msg == vms->lastmsg)
2103                 res = wait_file2(chan, vms, "vm-last");
2104         if (!res) {
2105                 res = wait_file2(chan, vms, "vm-message");
2106                 if (msg && (msg != vms->lastmsg)) {
2107                         if (!res)
2108                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2109                 }
2110         }
2111
2112         if (!res)
2113                 res = play_message_datetime(chan,vmu,vms);
2114
2115         if (!res) {
2116                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2117                 vms->heard[msg] = 1;
2118                 res = wait_file(chan, vms, vms->fn);
2119         }
2120         return res;
2121 }
2122
2123 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2124 {
2125         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2126         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2127         vms->lastmsg = count_messages(vms->curdir) - 1;
2128         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2129 }
2130
2131 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2132 {
2133         int x;
2134         char ntxt[256] = "";
2135         char txt[256] = "";
2136         if (vms->lastmsg > -1) { 
2137                 /* Get the deleted messages fixed */ 
2138                 vms->curmsg = -1; 
2139                 for (x=0;x < MAXMSG;x++) { 
2140                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2141                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2142                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2143                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2144                                         break;
2145                                 vms->curmsg++; 
2146                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2147                                 if (strcmp(vms->fn, vms->fn2)) { 
2148                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2149                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2150                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2151                                         rename(txt, ntxt); 
2152                                 } 
2153                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2154                                 /* Move to old folder before deleting */ 
2155                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2156                         } 
2157                 } 
2158                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2159                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2160                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2161                                 break;
2162                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2163                         ast_filedelete(vms->fn, NULL); 
2164                         unlink(txt); 
2165                 } 
2166         } 
2167         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2168         memset(vms->heard, 0, sizeof(vms->heard)); 
2169 }
2170
2171 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2172 {
2173         /* Introduce messages they have */
2174         int res;
2175         res = play_and_wait(chan, "vm-youhave");
2176         if (!res) {
2177                 if (vms->newmessages) {
2178                         res = say_and_wait(chan, vms->newmessages);
2179                         if (!res)
2180                                 res = play_and_wait(chan, "vm-INBOX");
2181                         if (vms->oldmessages && !res)
2182                                 res = play_and_wait(chan, "vm-and");
2183                         else if (!res) {
2184                                 if ((vms->newmessages == 1))
2185                                         res = play_and_wait(chan, "vm-message");
2186                                 else
2187                                         res = play_and_wait(chan, "vm-messages");
2188                         }
2189                                 
2190                 }
2191                 if (!res && vms->oldmessages) {
2192                         res = say_and_wait(chan, vms->oldmessages);
2193                         if (!res)
2194                                 res = play_and_wait(chan, "vm-Old");
2195                         if (!res) {
2196                                 if (vms->oldmessages == 1)
2197                                         res = play_and_wait(chan, "vm-message");
2198                                 else
2199                                         res = play_and_wait(chan, "vm-messages");
2200                         }
2201                 }
2202                 if (!res) {
2203                         if (!vms->oldmessages && !vms->newmessages) {
2204                                 res = play_and_wait(chan, "vm-no");
2205                                 if (!res)
2206                                         res = play_and_wait(chan, "vm-messages");
2207                         }
2208                 }
2209         }
2210         return res;
2211 }
2212
2213 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2214 {
2215         int res = 0;
2216         /* Play instructions and wait for new command */
2217         while(!res) {
2218                 if (vms->starting) {
2219                         if (vms->lastmsg > -1) {
2220                                 res = play_and_wait(chan, "vm-onefor");
2221                                 if (!res)
2222                                         res = play_and_wait(chan, vms->vmbox);
2223                                 if (!res)
2224                                         res = play_and_wait(chan, "vm-messages");
2225                         }
2226                         if (!res)
2227                                 res = play_and_wait(chan, "vm-opts");
2228                 } else {
2229                         if (vms->curmsg)
2230                                 res = play_and_wait(chan, "vm-prev");
2231                         if (!res)
2232                                 res = play_and_wait(chan, "vm-repeat");
2233                         if (!res && (vms->curmsg != vms->lastmsg))
2234                                 res = play_and_wait(chan, "vm-next");
2235                         if (!res) {
2236                                 if (!vms->deleted[vms->curmsg])
2237                                         res = play_and_wait(chan, "vm-delete");
2238                                 else
2239                                         res = play_and_wait(chan, "vm-undelete");
2240                                 if (!res)
2241                                         res = play_and_wait(chan, "vm-toforward");
2242                                 if (!res)
2243                                         res = play_and_wait(chan, "vm-savemessage");
2244                         }
2245                 }
2246                 if (!res)
2247                         res = play_and_wait(chan, "vm-helpexit");
2248                 if (!res)
2249                         res = ast_waitfordigit(chan, 6000);
2250                 if (!res) {
2251                         vms->repeats++;
2252                         if (vms->repeats > 2) {
2253                                 res = play_and_wait(chan, "vm-goodbye");
2254                                 if (!res)
2255                                         res = 't';
2256                         }
2257                 }
2258         }
2259         return res;
2260 }
2261
2262 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2263 {
2264         int cmd = 0;
2265         int retries = 0;
2266         char newpassword[80] = "";
2267         char newpassword2[80] = "";
2268         char prefile[256]="";
2269         char buf[256];
2270         int bytes=0;
2271
2272         if (adsi_available(chan))
2273         {
2274                 bytes += adsi_logo(buf + bytes);
2275                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2276                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2277                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2278                 bytes += adsi_voice_mode(buf + bytes, 0);
2279                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2280         }
2281         while((cmd >= 0) && (cmd != 't')) {
2282                 if (cmd)
2283                         retries = 0;
2284                 switch (cmd) {
2285                 case '1':
2286                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2287                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2288                         break;
2289                 case '2': 
2290                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2291                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2292                         break;
2293                 case '3': 
2294                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2295                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2296                         break;
2297                 case '4':
2298                         newpassword[1] = '\0';
2299                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2300                         if (cmd < 0)
2301                                 break;
2302                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2303                                 break;
2304             }
2305                         newpassword2[1] = '\0';
2306                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2307                         if (cmd < 0)
2308                                 break;
2309
2310                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2311                                 break;
2312             }
2313                         if (strcmp(newpassword, newpassword2)) {
2314                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2315                                 cmd = play_and_wait(chan, "vm-mismatch");
2316                                 break;
2317                         }
2318                         vm_change_password(vmu,newpassword);
2319                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2320                         cmd = play_and_wait(chan,"vm-passchanged");
2321                         break;
2322                 case '*': 
2323                         cmd = 't';
2324                         break;
2325                 default: 
2326                         cmd = play_and_wait(chan,"vm-options");
2327                         if (!cmd)
2328                                 cmd = ast_waitfordigit(chan,6000);
2329                         if (!cmd)
2330                                 retries++;
2331                         if (retries > 3)
2332                                 cmd = 't';
2333                  }
2334         }
2335         if (cmd == 't')
2336                 cmd = 0;
2337         return cmd;
2338 }
2339
2340 static int vm_execmain(struct ast_channel *chan, void *data)
2341 {
2342         /* XXX This is, admittedly, some pretty horrendus code.  For some
2343            reason it just seemed a lot easier to do with GOTO's.  I feel
2344            like I'm back in my GWBASIC days. XXX */
2345         int res=-1;
2346         int valid = 0;
2347         int prefix = 0;
2348         int cmd=0;
2349         struct localuser *u;
2350         char prefixstr[80] ="";
2351         char empty[80] = "";
2352         int box;
2353         int useadsi = 0;
2354         int skipuser = 0;
2355         char tmp[256], *ext;
2356         char fmtc[256] = "";
2357         char password[80];
2358         struct vm_state vms;
2359         int logretries = 0;
2360         struct ast_vm_user *vmu = NULL, vmus;
2361         char *context=NULL;
2362
2363         LOCAL_USER_ADD(u);
2364         memset(&vms, 0, sizeof(vms));
2365         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2366         if (chan->_state != AST_STATE_UP)
2367                 ast_answer(chan);
2368
2369         if (data && strlen(data)) {
2370                 strncpy(tmp, data, sizeof(tmp) - 1);
2371                 ext = tmp;
2372
2373                 switch (*ext) {
2374                         case 's':
2375                  /* We should skip the user's password */
2376                                 valid++;
2377                                 ext++;
2378                                 break;
2379                         case 'p':
2380                  /* We should prefix the mailbox with the supplied data */
2381                                 prefix++;
2382                                 ext++;
2383                                 break;
2384                 }
2385
2386                 context = strchr(ext, '@');
2387                 if (context) {
2388                         *context = '\0';
2389                         context++;
2390                 }
2391
2392                 if (prefix)
2393                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2394                 else
2395                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2396                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2397                         skipuser++;
2398                 else
2399                         valid = 0;
2400
2401         }
2402
2403         /* If ADSI is supported, setup login screen */
2404         adsi_begin(chan, &useadsi);
2405         if (!skipuser && useadsi)
2406                 adsi_login(chan);
2407         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2408                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2409                 goto out;
2410         }
2411         
2412         /* Authenticate them and get their mailbox/password */
2413         
2414         while (!valid && (logretries < maxlogins)) {
2415                 /* Prompt for, and read in the username */
2416                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2417                         ast_log(LOG_WARNING, "Couldn't read username\n");
2418                         goto out;
2419                 }
2420                 if (!strlen(vms.username)) {
2421                         if (option_verbose > 2)
2422                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2423                         res = 0;
2424                         goto out;
2425                 }
2426                 if (useadsi)
2427                         adsi_password(chan);
2428                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2429                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2430                         goto out;
2431                 }
2432                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2433                         ast_log(LOG_WARNING, "Unable to read password\n");
2434                         goto out;
2435                 }
2436                 if (prefix) {
2437                         char fullusername[80] = "";
2438                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2439                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2440                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2441                 }
2442                 if (!skipuser) 
2443                         vmu = find_user(&vmus, context, vms.username);
2444                 if (vmu && !strcmp(vmu->password, password)) 
2445                         valid++;
2446                 else {
2447                         if (option_verbose > 2)
2448                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2449                         if (prefix)
2450                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2451                 }
2452                 if (!valid) {
2453                         if (useadsi)
2454                                 adsi_login(chan);
2455                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2456                                 break;
2457                 }
2458                 logretries++;
2459         }
2460         if (!valid && (logretries >= maxlogins)) {
2461                 ast_stopstream(chan);
2462                 res = play_and_wait(chan, "vm-goodbye");
2463                 if (res > 0)
2464                         res = 0;
2465         }
2466
2467         if (valid) {
2468                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2469                 mkdir(vms.curdir, 0700);
2470                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2471                 mkdir(vms.curdir, 0700);
2472                 /* Retrieve old and new message counts */
2473                 open_mailbox(&vms, vmu, 1);
2474                 vms.oldmessages = vms.lastmsg + 1;
2475                 /* Start in INBOX */
2476                 open_mailbox(&vms, vmu, 0);
2477                 vms.newmessages = vms.lastmsg + 1;
2478                 
2479
2480                 /* Select proper mailbox FIRST!! */
2481                 if (!vms.newmessages && vms.oldmessages) {
2482                         /* If we only have old messages start here */
2483                         open_mailbox(&vms, vmu, 1);
2484                 }
2485
2486                 if (useadsi)
2487                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2488                 res = 0;
2489                 cmd = vm_intro(chan, &vms);
2490                 vms.repeats = 0;
2491                 vms.starting = 1;
2492                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2493                         /* Run main menu */
2494                         switch(cmd) {
2495                         case '1':
2496                                 vms.curmsg = 0;
2497                                 /* Fall through */
2498                         case '5':
2499                                 if (vms.lastmsg > -1) {
2500                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2501                                 } else {
2502                                         cmd = play_and_wait(chan, "vm-youhave");
2503                                         if (!cmd) 
2504                                                 cmd = play_and_wait(chan, "vm-no");
2505                                         if (!cmd) {
2506                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2507                                                 cmd = play_and_wait(chan, vms.fn);
2508                                         }
2509                                         if (!cmd)
2510                                                 cmd = play_and_wait(chan, "vm-messages");
2511                                 }
2512                                 break;
2513                         case '2': /* Change folders */
2514                                 if (useadsi)
2515                                         adsi_folders(chan, 0, "Change to folder...");
2516                                 cmd = get_folder2(chan, "vm-changeto", 0);
2517                                 if (cmd == '#') {
2518                                         cmd = 0;
2519                                 } else if (cmd > 0) {
2520                                         cmd = cmd - '0';
2521                                         close_mailbox(&vms, vmu);
2522                                         open_mailbox(&vms, vmu, cmd);
2523                                         cmd = 0;
2524                                 }
2525                                 if (useadsi)
2526                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2527                                 if (!cmd)
2528                                         cmd = play_and_wait(chan, vms.vmbox);
2529                                 if (!cmd)
2530                                         cmd = play_and_wait(chan, "vm-messages");
2531                                 vms.starting = 1;
2532                                 break;
2533                         case '4':
2534                                 if (vms.curmsg) {
2535                                         vms.curmsg--;
2536                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2537                                 } else {
2538                                         cmd = play_and_wait(chan, "vm-nomore");
2539                                 }
2540                                 break;
2541                         case '6':
2542                                 if (vms.curmsg < vms.lastmsg) {
2543                                         vms.curmsg++;
2544                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2545                                 } else {
2546                                         cmd = play_and_wait(chan, "vm-nomore");
2547                                 }
2548                                 break;
2549                         case '7':
2550                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2551                                 if (useadsi)
2552                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2553                                 if (vms.deleted[vms.curmsg]) 
2554                                         cmd = play_and_wait(chan, "vm-deleted");
2555                                 else
2556                                         cmd = play_and_wait(chan, "vm-undeleted");
2557                                 break;
2558                         case '8':
2559                                 if(vms.lastmsg > -1)
2560                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2561                                 else
2562                                         cmd = play_and_wait(chan, "vm-nomore");
2563                                 break;
2564                         case '9':
2565                                 if (useadsi)
2566                                         adsi_folders(chan, 1, "Save to folder...");
2567                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2568                                 box = 0;        /* Shut up compiler */
2569                                 if (cmd == '#') {
2570                                         cmd = 0;
2571                                         break;
2572                                 } else if (cmd > 0) {
2573                                         box = cmd = cmd - '0';
2574                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2575                                         vms.deleted[vms.curmsg]=1;
2576                                 }
2577                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2578                                 if (useadsi)
2579                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2580                                 if (!cmd)
2581                                         cmd = play_and_wait(chan, "vm-message");
2582                                 if (!cmd)
2583                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2584                                 if (!cmd)
2585                                         cmd = play_and_wait(chan, "vm-savedto");
2586                                 if (!cmd) {
2587                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2588                                         cmd = play_and_wait(chan, vms.fn);
2589                                 }
2590                                 if (!cmd)
2591                                         cmd = play_and_wait(chan, "vm-messages");
2592                                 break;
2593                         case '*':
2594                                 if (!vms.starting) {
2595                                         cmd = play_and_wait(chan, "vm-onefor");
2596                                         if (!cmd)
2597                                                 cmd = play_and_wait(chan, vms.vmbox);
2598                                         if (!cmd)
2599                                                 cmd = play_and_wait(chan, "vm-messages");
2600                                         if (!cmd)
2601                                                 cmd = play_and_wait(chan, "vm-opts");
2602                                 } else
2603                                         cmd = 0;
2604                                 break;
2605                         case '0':
2606                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2607                                 if (useadsi)
2608                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2609                                 break;
2610                         default:        /* Nothing */
2611                                 cmd = vm_instructions(chan, &vms);
2612                                 break;
2613                         }
2614                 }
2615                 if ((cmd == 't') || (cmd == '#')) {
2616                         /* Timeout */
2617                         res = 0;
2618                 } else {
2619                         /* Hangup */
2620                         res = -1;
2621                 }
2622         }
2623 out:
2624         if (res > -1) {
2625                 ast_stopstream(chan);
2626                 adsi_goodbye(chan);
2627                 if(valid) {
2628                         res = play_and_wait(chan, "vm-goodbye");
2629                         if (res > 0)
2630                                 res = 0;
2631                 }
2632                 if (useadsi)
2633                         adsi_unload_session(chan);
2634         }
2635         if (vmu)
2636                 close_mailbox(&vms, vmu);
2637         if (vmu)
2638                 free_user(vmu);
2639         if (valid) {
2640                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2641         }
2642         LOCAL_USER_REMOVE(u);
2643         return res;
2644
2645 }
2646
2647 static int vm_exec(struct ast_channel *chan, void *data)
2648 {
2649         int res=0, silent=0, busy=0, unavail=0;
2650         struct localuser *u;
2651         char tmp[256], *ext;
2652         
2653         LOCAL_USER_ADD(u);
2654         if (chan->_state != AST_STATE_UP)
2655                 ast_answer(chan);
2656         if (data && strlen(data))
2657                 strncpy(tmp, data, sizeof(tmp) - 1);
2658         else {
2659                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2660                 if (res < 0)
2661                         return res;
2662                 if (!strlen(tmp))
2663                         return 0;
2664         }
2665         ext = tmp;
2666         while(*ext) {
2667                 if (*ext == 's') {
2668                         silent = 2;
2669                         ext++;
2670                 } else if (*ext == 'b') {
2671                         busy=1;
2672                         ext++;
2673                 } else if (*ext == 'u') {
2674                         unavail=1;
2675                         ext++;
2676                 } else 
2677                         break;
2678         }
2679         res = leave_voicemail(chan, ext, silent, busy, unavail);
2680         LOCAL_USER_REMOVE(u);
2681         return res;
2682 }
2683
2684 static int append_mailbox(char *context, char *mbox, char *data)
2685 {
2686         /* Assumes lock is already held */
2687         char tmp[256] = "";
2688         char *stringp;
2689         char *s;
2690         struct ast_vm_user *vmu;
2691         strncpy(tmp, data, sizeof(tmp));
2692         vmu = malloc(sizeof(struct ast_vm_user));
2693         if (vmu) {
2694                 memset(vmu, 0, sizeof(struct ast_vm_user));
2695                 strncpy(vmu->context, context, sizeof(vmu->context));
2696                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2697                 vmu->attach = -1;
2698                 stringp = tmp;
2699                 if ((s = strsep(&stringp, ","))) 
2700                         strncpy(vmu->password, s, sizeof(vmu->password));
2701                 if (stringp && (s = strsep(&stringp, ","))) 
2702                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2703                 if (stringp && (s = strsep(&stringp, ","))) 
2704                         strncpy(vmu->email, s, sizeof(vmu->email));
2705                 if (stringp && (s = strsep(&stringp, ","))) 
2706                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2707                 if (stringp && (s = strsep(&stringp, ","))) 
2708                         apply_options(vmu, s);
2709                 vmu->next = NULL;
2710                 if (usersl)
2711                         usersl->next = vmu;
2712                 else
2713                         users = vmu;
2714                 usersl = vmu;
2715         }
2716         return 0;
2717 }
2718
2719 static int load_config(void)
2720 {
2721         struct ast_vm_user *cur, *l;
2722         struct vm_zone *zcur, *zl;
2723         struct ast_config *cfg;
2724         char *cat;
2725         struct ast_variable *var;
2726         char *astattach;
2727         char *silencestr;
2728         char *thresholdstr;
2729         char *fmt;
2730         char *astemail;
2731         char *s;
2732         int x;
2733
2734         cfg = ast_load(VOICEMAIL_CONFIG);
2735         ast_mutex_lock(&vmlock);
2736         cur = users;
2737         while(cur) {
2738                 l = cur;
2739                 cur = cur->next;
2740                 free_user(l);
2741         }
2742         zcur = zones;
2743         while(zcur) {
2744                 zl = zcur;
2745                 zcur = zcur->next;
2746                 free_zone(zl);
2747         }
2748         zones = NULL;
2749         zonesl = NULL;
2750         users = NULL;
2751         usersl = NULL;
2752         if (cfg) {
2753                 /* General settings */
2754                 attach_voicemail = 1;
2755                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
2756                         astattach = "yes";
2757                 attach_voicemail = ast_true(astattach);
2758                 maxsilence = 0;
2759                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2760                         maxsilence = atoi(silencestr);
2761                         if (maxsilence > 0)
2762                                 maxsilence *= 1000;
2763                 }
2764                 
2765                 silencethreshold = 256;
2766                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2767                         silencethreshold = atoi(thresholdstr);
2768                 
2769                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
2770                         astemail = ASTERISK_USERNAME;
2771                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2772                 
2773                 vmmaxmessage = 0;
2774                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2775                         if (sscanf(s, "%d", &x) == 1) {
2776                                 vmmaxmessage = x;
2777                         } else {
2778                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
2779                         }
2780                 }
2781                 fmt = ast_variable_retrieve(cfg, "general", "format");
2782                 if (!fmt)
2783                         fmt = "wav";    
2784                 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2785
2786                 skipms = 3000;
2787                 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2788                         if (sscanf(s, "%d", &x) == 1) {
2789                                 maxgreet = x;
2790                         } else {
2791                                 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2792                         }
2793                 }
2794
2795                 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2796                         if (sscanf(s, "%d", &x) == 1) {
2797                                 skipms = x;
2798                         } else {
2799                                 ast_log(LOG_WARNING, "Invalid skipms value\n");
2800                         }
2801                 }
2802
2803                 maxlogins = 3;
2804                 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2805                         if (sscanf(s, "%d", &x) == 1) {
2806                                 maxlogins = x;
2807                         } else {
2808                                 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2809                         }
2810                 }
2811
2812 #ifdef USEMYSQLVM
2813                 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2814                         strcpy(dbuser, "test");
2815                 } else {
2816                         strcpy(dbuser, s);
2817                 }
2818                 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2819                         strcpy(dbpass, "test");
2820                 } else {
2821                         strcpy(dbpass, s);
2822                 }
2823                 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2824                         strcpy(dbhost, "");
2825                 } else {
2826                         strcpy(dbhost, s);
2827                 }
2828                 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2829                         strcpy(dbname, "vmdb");
2830                 } else {
2831                         strcpy(dbname, s);
2832                 }
2833 #endif
2834
2835 #ifdef USEPOSTGRESVM
2836                 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2837                         strcpy(dboption, "dboption not-specified in voicemail.conf");
2838                 } else {
2839                         strcpy(dboption, s);
2840                 }
2841 #endif
2842                 cat = ast_category_browse(cfg, NULL);
2843                 while(cat) {
2844                         if (strcasecmp(cat, "general")) {
2845                                 var = ast_variable_browse(cfg, cat);
2846                                 if (strcasecmp(cat, "zonemessages")) {
2847 #ifndef USESQLVM
2848                                         /* Process mailboxes in this context */
2849                                         while(var) {
2850                                                 append_mailbox(cat, var->name, var->value);
2851                                                 var = var->next;
2852                                         }
2853 #endif
2854                                 } else {
2855                                         /* Timezones in this context */
2856                                         while(var) {
2857                                                 struct vm_zone *z;
2858                                                 z = malloc(sizeof(struct vm_zone));
2859                                                 if (z != NULL) {
2860                                                         char *msg_format, *timezone;
2861                                                         msg_format = ast_strdupa(var->value);
2862                                                         if (msg_format != NULL) {
2863                                                                 timezone = strsep(&msg_format, "|");
2864                                                                 strncpy(z->name, var->name, sizeof(z->name) - 1);
2865                                                                 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2866                                                                 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2867                                                                 z->next = NULL;
2868                                                                 if (zones) {
2869                                                                         zonesl->next = z;
2870                                                                         zonesl = z;
2871                                                                 } else {
2872                                                                         zones = z;
2873                                                                         zonesl = z;
2874                                                                 }
2875                                                         } else {
2876                                                                 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2877                                                                 free(z);
2878                                                                 return -1;
2879                                                         }
2880                                                 } else {
2881                                                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2882                                                         return -1;
2883                                                 }
2884                                                 var = var->next;
2885                                         }
2886                                 }
2887                         }
2888                         cat = ast_category_browse(cfg, cat);
2889                 }
2890                 memset(fromstring,0,sizeof(fromstring));
2891                 memset(emailtitle,0,sizeof(emailtitle));
2892                 if (emailbody) {
2893                         free(emailbody);
2894                         emailbody = NULL;
2895                 }
2896                 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2897                         pbxskip = ast_true(s);
2898                 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2899                         strncpy(fromstring,s,sizeof(fromstring)-1);
2900                 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2901                         strncpy(emailtitle,s,sizeof(emailtitle)-1);
2902                 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2903                         char *tmpread, *tmpwrite;
2904                         emailbody = strdup(s);
2905
2906                         /* substitute strings \t and \n into the apropriate characters */
2907                         tmpread = tmpwrite = emailbody;
2908                         while ((tmpwrite = strchr(tmpread,'\\'))) {
2909                                 int len = strlen("\n");
2910                                 switch (tmpwrite[1]) {
2911                                         case 'n':
2912                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2913                                                 strncpy(tmpwrite,"\n",len);
2914                                                 break;
2915                                         case 't':
2916                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2917                                                 strncpy(tmpwrite,"\t",len);
2918                                                 break;
2919                                         default:
2920                                                 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2921                                 }
2922                                 tmpread = tmpwrite+len;
2923                         }
2924                 }
2925                 ast_destroy(cfg);
2926                 ast_mutex_unlock(&vmlock);
2927                 return 0;
2928         } else {
2929                 ast_mutex_unlock(&vmlock);
2930                 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2931                 return -1;
2932         }
2933 }
2934
2935 int reload(void)
2936 {
2937         return(load_config());
2938 }
2939
2940 int unload_module(void)
2941 {
2942