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