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