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