0b45b0133fa4a29045209ff6bf649d3a6312d84c
[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         return d;
868 }
869
870 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt)
871 {
872         char d, *fmts;
873         char comment[256];
874         int x, fmtcnt=1, res=-1,outmsg=0;
875         struct ast_frame *f;
876         struct ast_filestream *others[MAX_OTHER_FORMATS];
877         char *sfmt[MAX_OTHER_FORMATS];
878         char *stringp=NULL;
879         time_t start, end;
880         struct ast_dsp *sildet;         /* silence detector dsp */
881         int totalsilence = 0;
882         int dspsilence = 0;
883         int gotsilence = 0;             /* did we timeout for silence? */
884         int rfmt=0;     
885         
886         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
887         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
888
889         if (playfile) { 
890                 d = play_and_wait(chan, playfile);
891                 if (!d)
892                         d = ast_streamfile(chan, "beep",chan->language);
893                 if (!d)
894                         d = ast_waitstream(chan,"");
895                 if (d < 0)
896                         return -1;
897         }
898         
899         fmts = ast_strdupa(fmt);
900         
901         stringp=fmts;
902         strsep(&stringp, "|");
903         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
904         sfmt[0] = ast_strdupa(fmts);
905         
906         while((fmt = strsep(&stringp, "|"))) {
907                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
908                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
909                         break;
910                 }
911                 sfmt[fmtcnt++] = ast_strdupa(fmt);
912         }
913
914         if (maxtime)
915                 time(&start);
916         for (x=0;x<fmtcnt;x++) {
917                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
918                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
919                         
920                 if (!others[x]) {
921                         break;
922                 }
923         }
924         
925         sildet = ast_dsp_new(); //Create the silence detector
926         if (!sildet) {
927                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
928                 return -1;
929         }
930         ast_dsp_set_threshold(sildet, silencethreshold);
931         
932         if (maxsilence > 0) {
933                 rfmt = chan->readformat;
934                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
935                 if (res < 0) {
936                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
937                         return -1;
938                 }
939         }
940                                                 
941         if (x == fmtcnt) {
942         /* Loop forever, writing the packets we read to the writer(s), until
943            we read a # or get a hangup */
944                 f = NULL;
945                 for(;;) {
946                         res = ast_waitfor(chan, 2000);
947                         if (!res) {
948                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
949                                 /* Try one more time in case of masq */
950                                 res = ast_waitfor(chan, 2000);
951                                 if (!res) {
952                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
953                                         res = -1;
954                                 }
955                         }
956                         
957                         if (res < 0) {
958                                 f = NULL;
959                                 break;
960                         }
961                         f = ast_read(chan);
962                         if (!f)
963                                 break;
964                         if (f->frametype == AST_FRAME_VOICE) {
965                                 /* write each format */
966                                 for (x=0;x<fmtcnt;x++) {
967                                         res = ast_writestream(others[x], f);
968                                 }
969                                 
970                                 /* Silence Detection */
971                                 if (maxsilence > 0) {
972                                         dspsilence = 0;
973                                         ast_dsp_silence(sildet, f, &dspsilence);
974                                         if (dspsilence)
975                                                 totalsilence = dspsilence;
976                                         else
977                                                 totalsilence = 0;
978                                         
979                                         if (totalsilence > maxsilence) {
980                                         /* Ended happily with silence */
981                                         ast_frfree(f);
982                                         gotsilence = 1;
983                                         outmsg=2;
984                                         break;
985                                         }
986                                 }
987                                 /* Exit on any error */
988                                 if (res) {
989                                         ast_log(LOG_WARNING, "Error writing frame\n");
990                                         ast_frfree(f);
991                                         break;
992                                 }
993                         } else if (f->frametype == AST_FRAME_VIDEO) {
994                                 /* Write only once */
995                                 ast_writestream(others[0], f);
996                         } else if (f->frametype == AST_FRAME_DTMF) {
997                                 if (f->subclass == '#') {
998                                         if (option_verbose > 2) 
999                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1000                                         res = '#';
1001                                         outmsg = 2;
1002                                         ast_frfree(f);
1003                                         break;
1004                                 }
1005                         }
1006                         if (maxtime) {
1007                                 time(&end);
1008                                 if (maxtime < (end - start)) {
1009                                         if (option_verbose > 2)
1010                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1011                                         res = 't';
1012                                         ast_frfree(f);
1013                                         break;
1014                                 }
1015                         }
1016                         ast_frfree(f);
1017                 }
1018                 if (!f) {
1019                         if (option_verbose > 2) 
1020                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1021                         res = -1;
1022                         outmsg=1;
1023                 }
1024         } else {
1025                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]); 
1026         }
1027
1028         for (x=0;x<fmtcnt;x++) {
1029                 if (!others[x])
1030                         break;
1031                 if (totalsilence)
1032                         ast_stream_rewind(others[x], totalsilence-200);
1033                 else
1034                         ast_stream_rewind(others[x], 200);
1035                 ast_truncstream(others[x]);
1036                 ast_closestream(others[x]);
1037         }
1038         if (rfmt) {
1039                 if (ast_set_read_format(chan, rfmt)) {
1040                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1041                 }
1042         }
1043         if (outmsg) {
1044                 if (outmsg > 1) {
1045                 /* Let them know it worked */
1046                         ast_streamfile(chan, "vm-msgsaved", chan->language);
1047                         ast_waitstream(chan, "");
1048                 }
1049         }       
1050
1051         
1052         return res;
1053 }
1054
1055 static void free_user(struct ast_vm_user *vmu)
1056 {
1057         if (vmu->alloced)
1058                 free(vmu);
1059 }
1060
1061 static void free_zone(struct vm_zone *z)
1062 {
1063         free(z);
1064 }
1065
1066 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1067 {
1068         char comment[256];
1069         char txtfile[256];
1070         FILE *txt;
1071         int res = 0;
1072         int msgnum;
1073         char date[256];
1074         char dir[256];
1075         char fn[256];
1076         char prefile[256]="";
1077         char fmt[80];
1078         char *context;
1079         char *ecodes = "#";
1080         char *stringp;
1081         time_t start;
1082         time_t end;
1083         char tmp[256] = "";
1084         struct ast_vm_user *vmu;
1085         struct ast_vm_user svm;
1086         
1087         strncpy(tmp, ext, sizeof(tmp) - 1);
1088         ext = tmp;
1089         context = strchr(tmp, '@');
1090         if (context) {
1091                 *context = '\0';
1092                 context++;
1093         }
1094
1095         if ((vmu = find_user(&svm, context, ext))) {
1096                 /* Setup pre-file if appropriate */
1097                 if (busy)
1098                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1099                 else if (unavail)
1100                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1101                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1102                 /* It's easier just to try to make it than to check for its existence */
1103                 if (mkdir(dir, 0700) && (errno != EEXIST))
1104                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1105                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1106                 /* It's easier just to try to make it than to check for its existence */
1107                 if (mkdir(dir, 0700) && (errno != EEXIST))
1108                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1109                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1110                 if (mkdir(dir, 0700) && (errno != EEXIST))
1111                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1112                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1113                         ecodes = "#0";
1114                 /* Play the beginning intro if desired */
1115                 if (strlen(prefile)) {
1116                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1117                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1118                                     res = ast_waitstream(chan, "#0");
1119                         } else {
1120                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1121                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1122                         }
1123                         if (res < 0) {
1124                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1125                                 free_user(vmu);
1126                                 return -1;
1127                         }
1128                 }
1129                 if (res == '#') {
1130                         /* On a '#' we skip the instructions */
1131                         silent = 1;
1132                         res = 0;
1133                 }
1134                 if (!res && !silent) {
1135                         res = ast_streamfile(chan, INTRO, chan->language);
1136                         if (!res)
1137                                 res = ast_waitstream(chan, ecodes);
1138                         if (res == '#') {
1139                                 silent = 1;
1140                                 res = 0;
1141                         }
1142                 }
1143                 /* Check for a '0' here */
1144                 if (res == '0') {
1145                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1146                         if (strlen(chan->macrocontext))
1147                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1148                         chan->priority = 0;
1149                         free_user(vmu);
1150                         return 0;
1151                 }
1152                 if (res >= 0) {
1153                         /* Unless we're *really* silent, try to send the beep */
1154                         res = ast_streamfile(chan, "beep", chan->language);
1155                         if (!res)
1156                                 res = ast_waitstream(chan, "");
1157                 }
1158                 if (res < 0) {
1159                         free_user(vmu);
1160                         return -1;
1161                 }
1162                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1163                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1164                 if (strlen(fmt)) {
1165                         msgnum = 0;
1166                         do {
1167                                 make_file(fn, sizeof(fn), dir, msgnum);
1168                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1169                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1170                                                                         vmu->fullname, ext, chan->name);
1171                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1172                                         break;
1173                                 msgnum++;
1174                         } while(msgnum < MAXMSG);
1175                         if (msgnum < MAXMSG) {
1176                                 /* Store information */
1177                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1178                                 txt = fopen(txtfile, "w+");
1179                                 if (txt) {
1180                                         get_date(date, sizeof(date));
1181                                         time(&start);
1182                                         fprintf(txt, 
1183 ";\n"
1184 "; Message Information file\n"
1185 ";\n"
1186 "[message]\n"
1187 "origmailbox=%s\n"
1188 "context=%s\n"
1189 "exten=%s\n"
1190 "priority=%d\n"
1191 "callerchan=%s\n"
1192 "callerid=%s\n"
1193 "origdate=%s\n"
1194 "origtime=%ld\n",
1195         ext,
1196         chan->context,
1197         chan->exten,
1198         chan->priority,
1199         chan->name,
1200         chan->callerid ? chan->callerid : "Unknown",
1201         date, (long)time(NULL));
1202                                         fclose(txt);
1203                                 } else
1204                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1205                                 res = play_and_record(chan, NULL, fn, vmmaxmessage, fmt);
1206                                 if (res > 0)
1207                                         res = 0;
1208                                 txt = fopen(txtfile, "a");
1209                                 if (txt) {
1210                                         time(&end);
1211                                         fprintf(txt, "duration=%ld\n", (long)(end-start));
1212                                         fclose(txt);
1213                                 }
1214                                 stringp = fmt;
1215                                 strsep(&stringp, "|");
1216                                 /* Send e-mail if applicable */
1217                                 if (strlen(vmu->email)) {
1218                                         int attach_user_voicemail = attach_voicemail;
1219                                         char *myserveremail = serveremail;
1220                                         if (vmu->attach > -1)
1221                                                 attach_user_voicemail = vmu->attach;
1222                                         if (strlen(vmu->serveremail))
1223                                                 myserveremail = vmu->serveremail;
1224                                         sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, end - start, attach_user_voicemail);
1225                                 }
1226                                 if (strlen(vmu->pager)) {
1227                                         char *myserveremail = serveremail;
1228                                         if (strlen(vmu->serveremail))
1229                                                 myserveremail = vmu->serveremail;
1230                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, end - start, vmu);
1231                                 }
1232                         } else
1233                                 ast_log(LOG_WARNING, "No more messages possible\n");
1234                 } else
1235                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1236                 free_user(vmu);
1237         } else
1238                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1239         /* Leave voicemail for someone */
1240         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1241         return res;
1242 }
1243
1244 static char *mbox(int id)
1245 {
1246         switch(id) {
1247         case 0:
1248                 return "INBOX";
1249         case 1:
1250                 return "Old";
1251         case 2:
1252                 return "Work";
1253         case 3:
1254                 return "Family";
1255         case 4:
1256                 return "Friends";
1257         case 5:
1258                 return "Cust1";
1259         case 6:
1260                 return "Cust2";
1261         case 7:
1262                 return "Cust3";
1263         case 8:
1264                 return "Cust4";
1265         case 9:
1266                 return "Cust5";
1267         default:
1268                 return "Unknown";
1269         }
1270 }
1271
1272 static int count_messages(char *dir)
1273 {
1274         int x;
1275         char fn[256];
1276         for (x=0;x<MAXMSG;x++) {
1277                 make_file(fn, sizeof(fn), dir, x);
1278                 if (ast_fileexists(fn, NULL, NULL) < 1)
1279                         break;
1280         }
1281         return x;
1282 }
1283
1284 static int say_and_wait(struct ast_channel *chan, int num)
1285 {
1286         int d;
1287         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language);
1288         return d;
1289 }
1290
1291 static int copy(char *infile, char *outfile)
1292 {
1293         int ifd;
1294         int ofd;
1295         int res;
1296         int len;
1297         char buf[4096];
1298         if ((ifd = open(infile, O_RDONLY)) < 0) {
1299                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1300                 return -1;
1301         }
1302         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1303                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1304                 close(ifd);
1305                 return -1;
1306         }
1307         do {
1308                 len = read(ifd, buf, sizeof(buf));
1309                 if (len < 0) {
1310                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1311                         close(ifd);
1312                         close(ofd);
1313                         unlink(outfile);
1314                 }
1315                 if (len) {
1316                         res = write(ofd, buf, len);
1317                         if (res != len) {
1318                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1319                                 close(ifd);
1320                                 close(ofd);
1321                                 unlink(outfile);
1322                         }
1323                 }
1324         } while(len);
1325         close(ifd);
1326         close(ofd);
1327         return 0;
1328 }
1329
1330 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1331 {
1332         char sfn[256];
1333         char dfn[256];
1334         char ddir[256];
1335         char txt[256];
1336         char ntxt[256];
1337         char *dbox = mbox(box);
1338         int x;
1339         make_file(sfn, sizeof(sfn), dir, msg);
1340         make_dir(ddir, sizeof(ddir), context, username, dbox);
1341         mkdir(ddir, 0700);
1342         for (x=0;x<MAXMSG;x++) {
1343                 make_file(dfn, sizeof(dfn), ddir, x);
1344                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1345                         break;
1346         }
1347         if (x >= MAXMSG)
1348                 return -1;
1349         ast_filecopy(sfn, dfn, NULL);
1350         if (strcmp(sfn, dfn)) {
1351                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1352                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1353                 copy(txt, ntxt);
1354         }
1355         return 0;
1356 }
1357
1358 static int adsi_logo(unsigned char *buf)
1359 {
1360         int bytes = 0;
1361         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1362         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1363         return bytes;
1364 }
1365
1366 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1367 {
1368         char buf[256];
1369         int bytes=0;
1370         int x;
1371         char num[5];
1372
1373         *useadsi = 0;
1374         bytes += adsi_data_mode(buf + bytes);
1375         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1376
1377         bytes = 0;
1378         bytes += adsi_logo(buf);
1379         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1380 #ifdef DISPLAY
1381         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1382 #endif
1383         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1384         bytes += adsi_data_mode(buf + bytes);
1385         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1386
1387         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1388                 bytes = 0;
1389                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1390                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1391                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1392                 bytes += adsi_voice_mode(buf + bytes, 0);
1393                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1394                 return 0;
1395         }
1396
1397 #ifdef DISPLAY
1398         /* Add a dot */
1399         bytes = 0;
1400         bytes += adsi_logo(buf);
1401         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1402         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1403         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1404         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1405 #endif
1406         bytes = 0;
1407         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1408         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1409         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1410         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1411         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1412         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1413         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1414
1415 #ifdef DISPLAY
1416         /* Add another dot */
1417         bytes = 0;
1418         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1419       bytes += adsi_voice_mode(buf + bytes, 0);
1420
1421         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1422         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1423 #endif
1424
1425         bytes = 0;
1426         /* These buttons we load but don't use yet */
1427         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1428         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1429         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1430         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1431         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1432         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1433         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1434
1435 #ifdef DISPLAY
1436         /* Add another dot */
1437         bytes = 0;
1438         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1439         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1440         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1441 #endif
1442
1443         bytes = 0;
1444         for (x=0;x<5;x++) {
1445                 snprintf(num, sizeof(num), "%d", x);
1446                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1447         }
1448         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1449         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1450
1451 #ifdef DISPLAY
1452         /* Add another dot */
1453         bytes = 0;
1454         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1455         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1456         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1457 #endif
1458
1459         if (adsi_end_download(chan)) {
1460                 bytes = 0;
1461                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1462                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1463                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1464                 bytes += adsi_voice_mode(buf + bytes, 0);
1465                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1466                 return 0;
1467         }
1468         bytes = 0;
1469         bytes += adsi_download_disconnect(buf + bytes);
1470         bytes += adsi_voice_mode(buf + bytes, 0);
1471         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1472
1473         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1474
1475 #ifdef DISPLAY
1476         /* Add last dot */
1477         bytes = 0;
1478         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1479         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1480 #endif
1481         ast_log(LOG_DEBUG, "Restarting session...\n");
1482
1483         bytes = 0;
1484         /* Load the session now */
1485         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1486                 *useadsi = 1;
1487                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1488         } else
1489                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1490
1491         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1492         return 0;
1493 }
1494
1495 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1496 {
1497         int x;
1498         if (!adsi_available(chan))
1499           return;
1500         x = adsi_load_session(chan, adapp, adver, 1);
1501         if (x < 0)
1502                 return;
1503         if (!x) {
1504                 if (adsi_load_vmail(chan, useadsi)) {
1505                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1506                         return;
1507                 }
1508         } else
1509                 *useadsi = 1;
1510 }
1511
1512 static void adsi_login(struct ast_channel *chan)
1513 {
1514         char buf[256];
1515         int bytes=0;
1516         unsigned char keys[8];
1517         int x;
1518         if (!adsi_available(chan))
1519                 return;
1520
1521         for (x=0;x<8;x++)
1522                 keys[x] = 0;
1523         /* Set one key for next */
1524         keys[3] = ADSI_KEY_APPS + 3;
1525
1526         bytes += adsi_logo(buf + bytes);
1527         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1528         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1529         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1530         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1531         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1532         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1533         bytes += adsi_set_keys(buf + bytes, keys);
1534         bytes += adsi_voice_mode(buf + bytes, 0);
1535         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1536 }
1537
1538 static void adsi_password(struct ast_channel *chan)
1539 {
1540         char buf[256];
1541         int bytes=0;
1542         unsigned char keys[8];
1543         int x;
1544         if (!adsi_available(chan))
1545                 return;
1546
1547         for (x=0;x<8;x++)
1548                 keys[x] = 0;
1549         /* Set one key for next */
1550         keys[3] = ADSI_KEY_APPS + 3;
1551
1552         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1553         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1554         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1555         bytes += adsi_set_keys(buf + bytes, keys);
1556         bytes += adsi_voice_mode(buf + bytes, 0);
1557         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1558 }
1559
1560 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1561 {
1562         char buf[256];
1563         int bytes=0;
1564         unsigned char keys[8];
1565         int x,y;
1566
1567         if (!adsi_available(chan))
1568                 return;
1569
1570         for (x=0;x<5;x++) {
1571                 y = ADSI_KEY_APPS + 12 + start + x;
1572                 if (y > ADSI_KEY_APPS + 12 + 4)
1573                         y = 0;
1574                 keys[x] = ADSI_KEY_SKT | y;
1575         }
1576         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
1577         keys[6] = 0;
1578         keys[7] = 0;
1579
1580         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
1581         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
1582         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1583         bytes += adsi_set_keys(buf + bytes, keys);
1584         bytes += adsi_voice_mode(buf + bytes, 0);
1585
1586         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1587 }
1588
1589 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
1590 {
1591         int bytes=0;
1592         char buf[256], buf1[256], buf2[256];
1593         char fn2[256];
1594
1595         char cid[256]="";
1596         char *val;
1597         char *name, *num;
1598         char datetime[21]="";
1599         FILE *f;
1600
1601         unsigned char keys[8];
1602
1603         int x;
1604
1605         if (!adsi_available(chan))
1606                 return;
1607
1608         /* Retrieve important info */
1609         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
1610         f = fopen(fn2, "r");
1611         if (f) {
1612                 while(!feof(f)) {       
1613                         fgets(buf, sizeof(buf), f);
1614                         if (!feof(f)) {
1615                                 char *stringp=NULL;
1616                                 stringp=buf;
1617                                 strsep(&stringp, "=");
1618                                 val = strsep(&stringp, "=");
1619                                 if (val && strlen(val)) {
1620                                         if (!strcmp(buf, "callerid"))
1621                                                 strncpy(cid, val, sizeof(cid) - 1);
1622                                         if (!strcmp(buf, "origdate"))
1623                                                 strncpy(datetime, val, sizeof(datetime) - 1);
1624                                 }
1625                         }
1626                 }
1627                 fclose(f);
1628         }
1629         /* New meaning for keys */
1630         for (x=0;x<5;x++)
1631                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1632         keys[6] = 0x0;
1633         keys[7] = 0x0;
1634
1635         if (!msg) {
1636                 /* No prev key, provide "Folder" instead */
1637                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1638         }
1639         if (msg >= last) {
1640                 /* If last message ... */
1641                 if (msg) {
1642                         /* but not only message, provide "Folder" instead */
1643                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1644       bytes += adsi_voice_mode(buf + bytes, 0);
1645
1646                 } else {
1647                         /* Otherwise if only message, leave blank */
1648                         keys[3] = 1;
1649                 }
1650         }
1651
1652         if (strlen(cid)) {
1653                 ast_callerid_parse(cid, &name, &num);
1654                 if (!name)
1655                         name = num;
1656         } else
1657                 name = "Unknown Caller";
1658
1659         /* If deleted, show "undeleted" */
1660
1661         if (deleted)
1662                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1663
1664         /* Except "Exit" */
1665         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1666         snprintf(buf1, sizeof(buf1), "%s%s", folder,
1667                  strcasecmp(folder, "INBOX") ? " Messages" : "");
1668         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
1669
1670         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1671         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1672         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
1673         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
1674         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1675         bytes += adsi_set_keys(buf + bytes, keys);
1676         bytes += adsi_voice_mode(buf + bytes, 0);
1677
1678         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1679 }
1680
1681 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
1682 {
1683         int bytes=0;
1684         char buf[256];
1685         unsigned char keys[8];
1686
1687         int x;
1688
1689         if (!adsi_available(chan))
1690                 return;
1691
1692         /* New meaning for keys */
1693         for (x=0;x<5;x++)
1694                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
1695
1696         keys[6] = 0x0;
1697         keys[7] = 0x0;
1698
1699         if (!msg) {
1700                 /* No prev key, provide "Folder" instead */
1701                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1702         }
1703         if (msg >= last) {
1704                 /* If last message ... */
1705                 if (msg) {
1706                         /* but not only message, provide "Folder" instead */
1707                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
1708                 } else {
1709                         /* Otherwise if only message, leave blank */
1710                         keys[3] = 1;
1711                 }
1712         }
1713
1714         /* If deleted, show "undeleted" */
1715         if (deleted) 
1716                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
1717
1718         /* Except "Exit" */
1719         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
1720         bytes += adsi_set_keys(buf + bytes, keys);
1721         bytes += adsi_voice_mode(buf + bytes, 0);
1722
1723         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1724 }
1725
1726 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
1727 {
1728         char buf[256], buf1[256], buf2[256];
1729         int bytes=0;
1730         unsigned char keys[8];
1731         int x;
1732
1733         char *newm = (new == 1) ? "message" : "messages";
1734         char *oldm = (old == 1) ? "message" : "messages";
1735         if (!adsi_available(chan))
1736                 return;
1737         if (new) {
1738                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
1739                 if (old) {
1740                         strcat(buf1, " and");
1741                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
1742                 } else {
1743                         snprintf(buf2, sizeof(buf2), "%s.", newm);
1744                 }
1745         } else if (old) {
1746                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
1747                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
1748         } else {
1749                 strcpy(buf1, "You have no messages.");
1750                 strcpy(buf2, " ");
1751         }
1752         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1753         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1754         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1755
1756         for (x=0;x<6;x++)
1757                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1758         keys[6] = 0;
1759         keys[7] = 0;
1760
1761         /* Don't let them listen if there are none */
1762         if (lastmsg < 0)
1763                 keys[0] = 1;
1764         bytes += adsi_set_keys(buf + bytes, keys);
1765
1766         bytes += adsi_voice_mode(buf + bytes, 0);
1767
1768         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1769 }
1770
1771 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
1772 {
1773         char buf[256], buf1[256], buf2[256];
1774         int bytes=0;
1775         unsigned char keys[8];
1776         int x;
1777
1778         char *mess = (messages == 1) ? "message" : "messages";
1779
1780         if (!adsi_available(chan))
1781                 return;
1782
1783         /* Original command keys */
1784         for (x=0;x<6;x++)
1785                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
1786
1787         keys[6] = 0;
1788         keys[7] = 0;
1789
1790         if (messages < 1)
1791                 keys[0] = 0;
1792
1793         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
1794                         strcasecmp(folder, "INBOX") ? " folder" : "");
1795
1796         if (messages)
1797                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
1798         else
1799                 strcpy(buf2, "no messages.");
1800         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
1801         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
1802         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
1803         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1804         bytes += adsi_set_keys(buf + bytes, keys);
1805
1806         bytes += adsi_voice_mode(buf + bytes, 0);
1807
1808         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1809         
1810 }
1811
1812 static void adsi_clear(struct ast_channel *chan)
1813 {
1814         char buf[256];
1815         int bytes=0;
1816         if (!adsi_available(chan))
1817                 return;
1818         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1819         bytes += adsi_voice_mode(buf + bytes, 0);
1820
1821         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1822 }
1823
1824 static void adsi_goodbye(struct ast_channel *chan)
1825 {
1826         char buf[256];
1827         int bytes=0;
1828
1829         if (!adsi_available(chan))
1830                 return;
1831         bytes += adsi_logo(buf + bytes);
1832         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
1833         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
1834         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1835         bytes += adsi_voice_mode(buf + bytes, 0);
1836
1837         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1838 }
1839
1840 static int get_folder(struct ast_channel *chan, int start)
1841 {
1842         int x;
1843         int d;
1844         char fn[256];
1845         d = play_and_wait(chan, "vm-press");
1846         if (d)
1847                 return d;
1848         for (x = start; x< 5; x++) {
1849                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language)))
1850                         return d;
1851                 d = play_and_wait(chan, "vm-for");
1852                 if (d)
1853                         return d;
1854                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
1855                 d = play_and_wait(chan, fn);
1856                 if (d)
1857                         return d;
1858                 d = play_and_wait(chan, "vm-messages");
1859                 if (d)
1860                         return d;
1861                 d = ast_waitfordigit(chan, 500);
1862                 if (d)
1863                         return d;
1864         }
1865         d = play_and_wait(chan, "vm-tocancel");
1866         if (d)
1867                 return d;
1868         d = ast_waitfordigit(chan, 4000);
1869         return d;
1870 }
1871
1872 static int get_folder2(struct ast_channel *chan, char *fn, int start)
1873 {
1874         int res = 0;
1875         res = play_and_wait(chan, fn);
1876         while (((res < '0') || (res > '9')) &&
1877                         (res != '#') && (res >= 0)) {
1878                 res = get_folder(chan, 0);
1879         }
1880         return res;
1881 }
1882
1883 static int
1884 forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
1885 {
1886         char username[70];
1887         char sys[256];
1888         char todir[256];
1889         int todircount=0;
1890         long duration;
1891         struct ast_config *mif;
1892         char miffile[256];
1893         char fn[256];
1894         char callerid[512];
1895         int res = 0;
1896         struct ast_vm_user *receiver, srec;
1897         char tmp[256];
1898         char *stringp, *s;
1899         
1900         while(!res) {
1901                 res = ast_streamfile(chan, "vm-extension", chan->language);
1902                 if (res)
1903                         break;
1904                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
1905                         break;
1906                 if ((receiver = find_user(&srec, context, username))) {
1907                         /* if (play_and_wait(chan, "vm-savedto"))
1908                                 break;
1909                         */
1910
1911                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, receiver->context, username);
1912                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
1913                         ast_log(LOG_DEBUG, sys);
1914                         system(sys);
1915
1916                         todircount = count_messages(todir);
1917                         strncpy(tmp, fmt, sizeof(tmp));
1918                         stringp = tmp;
1919                         while((s = strsep(&stringp, "|"))) {
1920                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
1921                                 ast_log(LOG_DEBUG, sys);
1922                                 system(sys);
1923                         }
1924                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
1925                         ast_log(LOG_DEBUG, sys);
1926                         system(sys);
1927                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
1928
1929                         /* load the information on the source message so we can send an e-mail like a new message */
1930                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
1931                         if ((mif=ast_load(miffile))) {
1932
1933               /* set callerid and duration variables */
1934               snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
1935               duration = atol(ast_variable_retrieve(mif, NULL, "duration"));
1936                         
1937               if (strlen(receiver->email)) {
1938                                 int attach_user_voicemail = attach_voicemail;
1939                                 char *myserveremail = serveremail;
1940                                 if (receiver->attach > -1)
1941                                         attach_user_voicemail = receiver->attach;
1942                                 if (strlen(receiver->serveremail))
1943                                         myserveremail = receiver->serveremail;
1944                       sendmail(myserveremail, receiver, todircount, username, callerid, fn, tmp, atol(ast_variable_retrieve(mif, NULL, "duration")), attach_user_voicemail);
1945               }
1946              
1947                         if (strlen(receiver->pager)) {
1948                                 char *myserveremail = serveremail;
1949                                 if (strlen(receiver->serveremail))
1950                                         myserveremail = receiver->serveremail;
1951                                 sendpage(myserveremail, receiver->pager, todircount, username, callerid, duration, receiver);
1952                         }
1953                           
1954                           ast_destroy(mif); /* or here */
1955                         }
1956                         /* Leave voicemail for someone */
1957                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", username, ast_app_has_voicemail(username));
1958
1959                         /* give confirmatopm that the message was saved */
1960                         res = play_and_wait(chan, "vm-message");
1961                         if (!res)
1962                                 res = play_and_wait(chan, "vm-saved");
1963                         free_user(receiver);
1964                         break;
1965                 } else {
1966                         res = play_and_wait(chan, "pbx-invalid");
1967                 }
1968         }
1969         return res;
1970 }
1971
1972 struct vm_state {
1973         char curbox[80];
1974         char username[80];
1975         char curdir[256];
1976         char vmbox[256];
1977         char fn[256];
1978         char fn2[256];
1979         int deleted[MAXMSG];
1980         int heard[MAXMSG];
1981         int curmsg;
1982         int lastmsg;
1983         int newmessages;
1984         int oldmessages;
1985         int starting;
1986         int repeats;
1987 };
1988
1989
1990 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
1991 {
1992         int res;
1993         if ((res = ast_streamfile(chan, file, chan->language))) 
1994                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
1995         if (!res)
1996                 res = ast_waitstream(chan, AST_DIGIT_ANY);
1997         return res;
1998 }
1999
2000 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2001 {
2002         int res;
2003         if ((res = ast_streamfile(chan, file, chan->language)))
2004                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2005         if (!res)
2006                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2007         return res;
2008 }
2009
2010 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
2011 {
2012         int res = 0;
2013         char filename[256], *origtime;
2014         struct vm_zone *the_zone = NULL;
2015         struct ast_config *msg_cfg;
2016         time_t t;
2017         long tin;
2018
2019         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2020         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2021         msg_cfg = ast_load(filename);
2022         if (!msg_cfg) {
2023                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2024                 return 0;
2025         }
2026
2027         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2028                 return 0;
2029         if (sscanf(origtime,"%ld",&tin) < 1) {
2030                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2031                 return 0;
2032         }
2033         t = tin;
2034         ast_destroy(msg_cfg);
2035
2036         /* Does this user have a timezone specified? */
2037         if (strlen(vmu->zonetag)) {
2038                 /* Find the zone in the list */
2039                 struct vm_zone *z;
2040                 z = zones;
2041                 while (z) {
2042                         if (!strcmp(z->name, vmu->zonetag)) {
2043                                 the_zone = z;
2044                                 break;
2045                         }
2046                         z = z->next;
2047                 }
2048         }
2049
2050 /* No internal variable parsing for now, so we'll comment it out for the time being */
2051 #if 0
2052         /* Set the DIFF_* variables */
2053         localtime_r(&t, &time_now);
2054         gettimeofday(&tv_now,NULL);
2055         tnow = tv_now.tv_sec;
2056         localtime_r(&tnow,&time_then);
2057
2058         /* Day difference */
2059         if (time_now.tm_year == time_then.tm_year)
2060                 sprintf(temp,"%d",time_now.tm_yday);
2061         else
2062                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2063         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2064
2065         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2066 #endif
2067         if (the_zone)
2068                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2069         else
2070                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2071 #if 0
2072         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2073 #endif
2074         return res;
2075 }
2076
2077 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2078 {
2079         int res = 0;
2080         vms->starting = 0; 
2081         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2082         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2083         if (!msg)
2084                 res = wait_file2(chan, vms, "vm-first");
2085         else if (msg == vms->lastmsg)
2086                 res = wait_file2(chan, vms, "vm-last");
2087         if (!res) {
2088                 res = wait_file2(chan, vms, "vm-message");
2089                 if (msg && (msg != vms->lastmsg)) {
2090                         if (!res)
2091                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2092                 }
2093         }
2094
2095         if (!res)
2096                 res = play_message_datetime(chan,vmu,vms);
2097
2098         if (!res) {
2099                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2100                 vms->heard[msg] = 1;
2101                 res = wait_file(chan, vms, vms->fn);
2102         }
2103         return res;
2104 }
2105
2106 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2107 {
2108         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2109         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2110         vms->lastmsg = count_messages(vms->curdir) - 1;
2111         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2112 }
2113
2114 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2115 {
2116         int x;
2117         char ntxt[256] = "";
2118         char txt[256] = "";
2119         if (vms->lastmsg > -1) { 
2120                 /* Get the deleted messages fixed */ 
2121                 vms->curmsg = -1; 
2122                 for (x=0;x < MAXMSG;x++) { 
2123                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2124                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2125                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2126                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2127                                         break;
2128                                 vms->curmsg++; 
2129                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2130                                 if (strcmp(vms->fn, vms->fn2)) { 
2131                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2132                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2133                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2134                                         rename(txt, ntxt); 
2135                                 } 
2136                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2137                                 /* Move to old folder before deleting */ 
2138                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2139                         } 
2140                 } 
2141                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2142                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2143                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2144                                 break;
2145                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2146                         ast_filedelete(vms->fn, NULL); 
2147                         unlink(txt); 
2148                 } 
2149         } 
2150         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2151         memset(vms->heard, 0, sizeof(vms->heard)); 
2152 }
2153
2154 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2155 {
2156         /* Introduce messages they have */
2157         int res;
2158         res = play_and_wait(chan, "vm-youhave");
2159         if (!res) {
2160                 if (vms->newmessages) {
2161                         res = say_and_wait(chan, vms->newmessages);
2162                         if (!res)
2163                                 res = play_and_wait(chan, "vm-INBOX");
2164                         if (vms->oldmessages && !res)
2165                                 res = play_and_wait(chan, "vm-and");
2166                         else if (!res) {
2167                                 if ((vms->newmessages == 1))
2168                                         res = play_and_wait(chan, "vm-message");
2169                                 else
2170                                         res = play_and_wait(chan, "vm-messages");
2171                         }
2172                                 
2173                 }
2174                 if (!res && vms->oldmessages) {
2175                         res = say_and_wait(chan, vms->oldmessages);
2176                         if (!res)
2177                                 res = play_and_wait(chan, "vm-Old");
2178                         if (!res) {
2179                                 if (vms->oldmessages == 1)
2180                                         res = play_and_wait(chan, "vm-message");
2181                                 else
2182                                         res = play_and_wait(chan, "vm-messages");
2183                         }
2184                 }
2185                 if (!res) {
2186                         if (!vms->oldmessages && !vms->newmessages) {
2187                                 res = play_and_wait(chan, "vm-no");
2188                                 if (!res)
2189                                         res = play_and_wait(chan, "vm-messages");
2190                         }
2191                 }
2192         }
2193         return res;
2194 }
2195
2196 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2197 {
2198         int res = 0;
2199         /* Play instructions and wait for new command */
2200         while(!res) {
2201                 if (vms->starting) {
2202                         if (vms->lastmsg > -1) {
2203                                 res = play_and_wait(chan, "vm-onefor");
2204                                 if (!res)
2205                                         res = play_and_wait(chan, vms->vmbox);
2206                                 if (!res)
2207                                         res = play_and_wait(chan, "vm-messages");
2208                         }
2209                         if (!res)
2210                                 res = play_and_wait(chan, "vm-opts");
2211                 } else {
2212                         if (vms->curmsg)
2213                                 res = play_and_wait(chan, "vm-prev");
2214                         if (!res)
2215                                 res = play_and_wait(chan, "vm-repeat");
2216                         if (!res && (vms->curmsg != vms->lastmsg))
2217                                 res = play_and_wait(chan, "vm-next");
2218                         if (!res) {
2219                                 if (!vms->deleted[vms->curmsg])
2220                                         res = play_and_wait(chan, "vm-delete");
2221                                 else
2222                                         res = play_and_wait(chan, "vm-undelete");
2223                                 if (!res)
2224                                         res = play_and_wait(chan, "vm-toforward");
2225                                 if (!res)
2226                                         res = play_and_wait(chan, "vm-savemessage");
2227                         }
2228                 }
2229                 if (!res)
2230                         res = play_and_wait(chan, "vm-helpexit");
2231                 if (!res)
2232                         res = ast_waitfordigit(chan, 6000);
2233                 if (!res) {
2234                         vms->repeats++;
2235                         if (vms->repeats > 2) {
2236                                 res = play_and_wait(chan, "vm-goodbye");
2237                                 if (!res)
2238                                         res = 't';
2239                         }
2240                 }
2241         }
2242         return res;
2243 }
2244
2245 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2246 {
2247         int cmd = 0;
2248         int retries = 0;
2249         char newpassword[80] = "";
2250         char newpassword2[80] = "";
2251         char prefile[256]="";
2252         char buf[256];
2253         int bytes=0;
2254
2255         if (adsi_available(chan))
2256         {
2257                 bytes += adsi_logo(buf + bytes);
2258                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2259                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2260                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2261                 bytes += adsi_voice_mode(buf + bytes, 0);
2262                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2263         }
2264         while((cmd >= 0) && (cmd != 't')) {
2265                 if (cmd)
2266                         retries = 0;
2267                 switch (cmd) {
2268                 case '1':
2269                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2270                         cmd = play_and_record(chan,"vm-rec-unv",prefile, maxgreet, fmtc);
2271                         break;
2272                 case '2': 
2273                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2274                         cmd = play_and_record(chan,"vm-rec-busy",prefile, maxgreet, fmtc);
2275                         break;
2276                 case '3': 
2277                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2278                         cmd = play_and_record(chan,"vm-rec-name",prefile, maxgreet, fmtc);
2279                         break;
2280                 case '4':
2281                         newpassword[1] = '\0';
2282                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2283                         if (cmd < 0)
2284                                 break;
2285                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2286                                 break;
2287             }
2288                         newpassword2[1] = '\0';
2289                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2290                         if (cmd < 0)
2291                                 break;
2292
2293                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2294                                 break;
2295             }
2296                         if (strcmp(newpassword, newpassword2)) {
2297                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2298                                 cmd = play_and_wait(chan, "vm-mismatch");
2299                                 break;
2300                         }
2301                         vm_change_password(vmu,newpassword);
2302                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2303                         cmd = play_and_wait(chan,"vm-passchanged");
2304                         break;
2305                 case '*': 
2306                         cmd = 't';
2307                         break;
2308                 default: 
2309                         cmd = play_and_wait(chan,"vm-options");
2310                         if (!cmd)
2311                                 cmd = ast_waitfordigit(chan,6000);
2312                         if (!cmd)
2313                                 retries++;
2314                         if (retries > 3)
2315                                 cmd = 't';
2316                  }
2317         }
2318         if (cmd == 't')
2319                 cmd = 0;
2320         return cmd;
2321 }
2322
2323 static int vm_execmain(struct ast_channel *chan, void *data)
2324 {
2325         /* XXX This is, admittedly, some pretty horrendus code.  For some
2326            reason it just seemed a lot easier to do with GOTO's.  I feel
2327            like I'm back in my GWBASIC days. XXX */
2328         int res=-1;
2329         int valid = 0;
2330         int prefix = 0;
2331         int cmd=0;
2332         struct localuser *u;
2333         char prefixstr[80] ="";
2334         char empty[80] = "";
2335         int box;
2336         int useadsi = 0;
2337         int skipuser = 0;
2338         char tmp[256], *ext;
2339         char fmtc[256] = "";
2340         char password[80];
2341         struct vm_state vms;
2342         int logretries = 0;
2343         struct ast_vm_user *vmu = NULL, vmus;
2344         char *context=NULL;
2345
2346         LOCAL_USER_ADD(u);
2347         memset(&vms, 0, sizeof(vms));
2348         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2349         if (chan->_state != AST_STATE_UP)
2350                 ast_answer(chan);
2351
2352         if (data && strlen(data)) {
2353                 strncpy(tmp, data, sizeof(tmp) - 1);
2354                 ext = tmp;
2355
2356                 switch (*ext) {
2357                         case 's':
2358                  /* We should skip the user's password */
2359                                 valid++;
2360                                 ext++;
2361                                 break;
2362                         case 'p':
2363                  /* We should prefix the mailbox with the supplied data */
2364                                 prefix++;
2365                                 ext++;
2366                                 break;
2367                 }
2368
2369                 context = strchr(ext, '@');
2370                 if (context) {
2371                         *context = '\0';
2372                         context++;
2373                 }
2374
2375                 if (prefix)
2376                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2377                 else
2378                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2379                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2380                         skipuser++;
2381                 else
2382                         valid = 0;
2383
2384         }
2385
2386         /* If ADSI is supported, setup login screen */
2387         adsi_begin(chan, &useadsi);
2388         if (!skipuser && useadsi)
2389                 adsi_login(chan);
2390         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2391                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2392                 goto out;
2393         }
2394         
2395         /* Authenticate them and get their mailbox/password */
2396         
2397         while (!valid && (logretries < maxlogins)) {
2398                 /* Prompt for, and read in the username */
2399                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2400                         ast_log(LOG_WARNING, "Couldn't read username\n");
2401                         goto out;
2402                 }
2403                 if (!strlen(vms.username)) {
2404                         if (option_verbose > 2)
2405                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2406                         res = 0;
2407                         goto out;
2408                 }
2409                 if (useadsi)
2410                         adsi_password(chan);
2411                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2412                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2413                         goto out;
2414                 }
2415                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
2416                         ast_log(LOG_WARNING, "Unable to read password\n");
2417                         goto out;
2418                 }
2419                 if (prefix) {
2420                         char fullusername[80] = "";
2421                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
2422                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
2423                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
2424                 }
2425                 if (!skipuser) 
2426                         vmu = find_user(&vmus, context, vms.username);
2427                 if (vmu && !strcmp(vmu->password, password)) 
2428                         valid++;
2429                 else {
2430                         if (option_verbose > 2)
2431                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
2432                         if (prefix)
2433                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
2434                 }
2435                 if (!valid) {
2436                         if (useadsi)
2437                                 adsi_login(chan);
2438                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
2439                                 break;
2440                 }
2441                 logretries++;
2442         }
2443         if (!valid && (logretries >= maxlogins)) {
2444                 ast_stopstream(chan);
2445                 res = play_and_wait(chan, "vm-goodbye");
2446                 if (res > 0)
2447                         res = 0;
2448         }
2449
2450         if (valid) {
2451                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
2452                 mkdir(vms.curdir, 0700);
2453                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
2454                 mkdir(vms.curdir, 0700);
2455                 /* Retrieve old and new message counts */
2456                 open_mailbox(&vms, vmu, 1);
2457                 vms.oldmessages = vms.lastmsg + 1;
2458                 /* Start in INBOX */
2459                 open_mailbox(&vms, vmu, 0);
2460                 vms.newmessages = vms.lastmsg + 1;
2461                 
2462
2463                 /* Select proper mailbox FIRST!! */
2464                 if (!vms.newmessages && vms.oldmessages) {
2465                         /* If we only have old messages start here */
2466                         open_mailbox(&vms, vmu, 1);
2467                 }
2468
2469                 if (useadsi)
2470                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2471                 res = 0;
2472                 cmd = vm_intro(chan, &vms);
2473                 vms.repeats = 0;
2474                 vms.starting = 1;
2475                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
2476                         /* Run main menu */
2477                         switch(cmd) {
2478                         case '1':
2479                                 vms.curmsg = 0;
2480                                 /* Fall through */
2481                         case '5':
2482                                 if (vms.lastmsg > -1) {
2483                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2484                                 } else {
2485                                         cmd = play_and_wait(chan, "vm-youhave");
2486                                         if (!cmd) 
2487                                                 cmd = play_and_wait(chan, "vm-no");
2488                                         if (!cmd) {
2489                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
2490                                                 cmd = play_and_wait(chan, vms.fn);
2491                                         }
2492                                         if (!cmd)
2493                                                 cmd = play_and_wait(chan, "vm-messages");
2494                                 }
2495                                 break;
2496                         case '2': /* Change folders */
2497                                 if (useadsi)
2498                                         adsi_folders(chan, 0, "Change to folder...");
2499                                 cmd = get_folder2(chan, "vm-changeto", 0);
2500                                 if (cmd == '#') {
2501                                         cmd = 0;
2502                                 } else if (cmd > 0) {
2503                                         cmd = cmd - '0';
2504                                         close_mailbox(&vms, vmu);
2505                                         open_mailbox(&vms, vmu, cmd);
2506                                         cmd = 0;
2507                                 }
2508                                 if (useadsi)
2509                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
2510                                 if (!cmd)
2511                                         cmd = play_and_wait(chan, vms.vmbox);
2512                                 if (!cmd)
2513                                         cmd = play_and_wait(chan, "vm-messages");
2514                                 vms.starting = 1;
2515                                 break;
2516                         case '4':
2517                                 if (vms.curmsg) {
2518                                         vms.curmsg--;
2519                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2520                                 } else {
2521                                         cmd = play_and_wait(chan, "vm-nomore");
2522                                 }
2523                                 break;
2524                         case '6':
2525                                 if (vms.curmsg < vms.lastmsg) {
2526                                         vms.curmsg++;
2527                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
2528                                 } else {
2529                                         cmd = play_and_wait(chan, "vm-nomore");
2530                                 }
2531                                 break;
2532                         case '7':
2533                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
2534                                 if (useadsi)
2535                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
2536                                 if (vms.deleted[vms.curmsg]) 
2537                                         cmd = play_and_wait(chan, "vm-deleted");
2538                                 else
2539                                         cmd = play_and_wait(chan, "vm-undeleted");
2540                                 break;
2541                         case '8':
2542                                 if(vms.lastmsg > -1)
2543                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
2544                                 else
2545                                         cmd = play_and_wait(chan, "vm-nomore");
2546                                 break;
2547                         case '9':
2548                                 if (useadsi)
2549                                         adsi_folders(chan, 1, "Save to folder...");
2550                                 cmd = get_folder2(chan, "vm-savefolder", 1);
2551                                 box = 0;        /* Shut up compiler */
2552                                 if (cmd == '#') {
2553                                         cmd = 0;
2554                                         break;
2555                                 } else if (cmd > 0) {
2556                                         box = cmd = cmd - '0';
2557                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
2558                                         vms.deleted[vms.curmsg]=1;
2559                                 }
2560                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
2561                                 if (useadsi)
2562                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
2563                                 if (!cmd)
2564                                         cmd = play_and_wait(chan, "vm-message");
2565                                 if (!cmd)
2566                                         cmd = say_and_wait(chan, vms.curmsg + 1);
2567                                 if (!cmd)
2568                                         cmd = play_and_wait(chan, "vm-savedto");
2569                                 if (!cmd) {
2570                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
2571                                         cmd = play_and_wait(chan, vms.fn);
2572                                 }
2573                                 if (!cmd)
2574                                         cmd = play_and_wait(chan, "vm-messages");
2575                                 break;
2576                         case '*':
2577                                 if (!vms.starting) {
2578                                         cmd = play_and_wait(chan, "vm-onefor");
2579                                         if (!cmd)
2580                                                 cmd = play_and_wait(chan, vms.vmbox);
2581                                         if (!cmd)
2582                                                 cmd = play_and_wait(chan, "vm-messages");
2583                                         if (!cmd)
2584                                                 cmd = play_and_wait(chan, "vm-opts");
2585                                 } else
2586                                         cmd = 0;
2587                                 break;
2588                         case '0':
2589                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
2590                                 if (useadsi)
2591                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
2592                                 break;
2593                         default:        /* Nothing */
2594                                 cmd = vm_instructions(chan, &vms);
2595                                 break;
2596                         }
2597                 }
2598                 if ((cmd == 't') || (cmd == '#')) {
2599                         /* Timeout */
2600                         res = 0;
2601                 } else {
2602                         /* Hangup */
2603                         res = -1;
2604                 }
2605         }
2606 out:
2607         if (res > -1) {
2608                 ast_stopstream(chan);
2609                 adsi_goodbye(chan);
2610                 if(valid) {
2611                         res = play_and_wait(chan, "vm-goodbye");
2612                         if (res > 0)
2613                                 res = 0;
2614                 }
2615                 if (useadsi)
2616                         adsi_unload_session(chan);
2617         }
2618         if (vmu)
2619                 close_mailbox(&vms, vmu);
2620         if (vmu)
2621                 free_user(vmu);
2622         if (valid) {
2623                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vms.username, ast_app_has_voicemail(vms.username));
2624         }
2625         LOCAL_USER_REMOVE(u);
2626         return res;
2627
2628 }
2629
2630 static int vm_exec(struct ast_channel *chan, void *data)
2631 {
2632         int res=0, silent=0, busy=0, unavail=0;
2633         struct localuser *u;
2634         char tmp[256], *ext;
2635         
2636         LOCAL_USER_ADD(u);
2637         if (chan->_state != AST_STATE_UP)
2638                 ast_answer(chan);
2639         if (data)
2640                 strncpy(tmp, data, sizeof(tmp) - 1);
2641         else {
2642                 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
2643                 if (res < 0)
2644                         return res;
2645                 if (!strlen(tmp))
2646                         return 0;
2647         }
2648         ext = tmp;
2649         while(*ext) {
2650                 if (*ext == 's') {
2651                         silent = 2;
2652                         ext++;
2653                 } else if (*ext == 'b') {
2654                         busy=1;
2655                         ext++;
2656                 } else if (*ext == 'u') {
2657                         unavail=1;
2658                         ext++;
2659                 } else 
2660                         break;
2661         }
2662         res = leave_voicemail(chan, ext, silent, busy, unavail);
2663         LOCAL_USER_REMOVE(u);
2664         return res;
2665 }
2666
2667 static int append_mailbox(char *context, char *mbox, char *data)
2668 {
2669         /* Assumes lock is already held */
2670         char tmp[256] = "";
2671         char *stringp;
2672         char *s;
2673         struct ast_vm_user *vmu;
2674         strncpy(tmp, data, sizeof(tmp));
2675         vmu = malloc(sizeof(struct ast_vm_user));
2676         if (vmu) {
2677                 memset(vmu, 0, sizeof(struct ast_vm_user));
2678                 strncpy(vmu->context, context, sizeof(vmu->context));
2679                 strncpy(vmu->mailbox, mbox, sizeof(vmu->mailbox));
2680                 vmu->attach = -1;
2681                 stringp = tmp;
2682                 if ((s = strsep(&stringp, ","))) 
2683                         strncpy(vmu->password, s, sizeof(vmu->password));
2684                 if (stringp && (s = strsep(&stringp, ","))) 
2685                         strncpy(vmu->fullname, s, sizeof(vmu->fullname));
2686                 if (stringp && (s = strsep(&stringp, ","))) 
2687                         strncpy(vmu->email, s, sizeof(vmu->email));
2688                 if (stringp && (s = strsep(&stringp, ","))) 
2689                         strncpy(vmu->pager, s, sizeof(vmu->pager));
2690                 if (stringp && (s = strsep(&stringp, ","))) 
2691                         apply_options(vmu, s);
2692                 vmu->next = NULL;
2693                 if (usersl)
2694                         usersl->next = vmu;
2695                 else
2696                         users = vmu;
2697                 usersl = vmu;
2698         }
2699         return 0;
2700 }
2701
2702 static int load_config(void)
2703 {
2704         struct ast_vm_user *cur, *l;
2705         struct vm_zone *zcur, *zl;
2706         struct ast_config *cfg;
2707         char *cat;
2708         struct ast_variable *var;
2709         char *astattach;
2710         char *silencestr;
2711         char *thresholdstr;
2712         char *fmt;
2713         char *astemail;
2714         char *s;
2715         int x;
2716
2717         cfg = ast_load(VOICEMAIL_CONFIG);
2718         ast_mutex_lock(&vmlock);
2719         cur = users;
2720         while(cur) {
2721                 l = cur;
2722                 cur = cur->next;
2723                 free_user(l);
2724         }
2725         zcur = zones;
2726         while(zcur) {
2727                 zl = zcur;
2728                 zcur = zcur->next;
2729                 free_zone(zl);
2730         }
2731         zones = NULL;
2732         zonesl = NULL;
2733         users = NULL;
2734         usersl = NULL;
2735         if (cfg) {
2736                 /* General settings */
2737                 attach_voicemail = 1;
2738                 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
2739                         astattach = "yes";
2740                 attach_voicemail = ast_true(astattach);
2741                 maxsilence = 0;
2742                 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
2743                         maxsilence = atoi(silencestr);
2744                         if (maxsilence > 0)
2745                                 maxsilence *= 1000;
2746                 }
2747                 
2748                 silencethreshold = 256;
2749                 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
2750                         silencethreshold = atoi(thresholdstr);
2751                 
2752                 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
2753                         astemail = ASTERISK_USERNAME;
2754                 strncpy(serveremail, astemail, sizeof(serveremail) - 1);
2755                 
2756                 vmmaxmessage = 0;
2757                 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
2758                         if (sscanf(s, "%d", &x) == 1) {
2759                                 vmmaxmessage = x;
2760                         } else {
2761                                 ast_log(LOG_WARNING, "Invalid max message time length\n");
2762                         }
2763                 }
2764                 fmt = ast_variable_retrieve(cfg, "general", "format");
2765                 if (!fmt)
2766                         fmt = "wav";    
2767                 strncpy(vmfmts, fmt, sizeof(vmfmts) - 1);
2768
2769                 skipms = 3000;
2770                 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
2771                         if (sscanf(s, "%d", &x) == 1) {
2772                                 maxgreet = x;
2773                         } else {
2774                                 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
2775                         }
2776                 }
2777
2778                 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
2779                         if (sscanf(s, "%d", &x) == 1) {
2780                                 skipms = x;
2781                         } else {
2782                                 ast_log(LOG_WARNING, "Invalid skipms value\n");
2783                         }
2784                 }
2785
2786                 maxlogins = 3;
2787                 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
2788                         if (sscanf(s, "%d", &x) == 1) {
2789                                 maxlogins = x;
2790                         } else {
2791                                 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
2792                         }
2793                 }
2794
2795 #ifdef USEMYSQLVM
2796                 if (!(s=ast_variable_retrieve(cfg, "general", "dbuser"))) {
2797                         strcpy(dbuser, "test");
2798                 } else {
2799                         strcpy(dbuser, s);
2800                 }
2801                 if (!(s=ast_variable_retrieve(cfg, "general", "dbpass"))) {
2802                         strcpy(dbpass, "test");
2803                 } else {
2804                         strcpy(dbpass, s);
2805                 }
2806                 if (!(s=ast_variable_retrieve(cfg, "general", "dbhost"))) {
2807                         strcpy(dbhost, "");
2808                 } else {
2809                         strcpy(dbhost, s);
2810                 }
2811                 if (!(s=ast_variable_retrieve(cfg, "general", "dbname"))) {
2812                         strcpy(dbname, "vmdb");
2813                 } else {
2814                         strcpy(dbname, s);
2815                 }
2816 #endif
2817
2818 #ifdef USEPOSTGRESVM
2819                 if (!(s=ast_variable_retrieve(cfg, "general", "dboption"))) {
2820                         strcpy(dboption, "dboption not-specified in voicemail.conf");
2821                 } else {
2822                         strcpy(dboption, s);
2823                 }
2824 #endif
2825                 cat = ast_category_browse(cfg, NULL);
2826                 while(cat) {
2827                         if (strcasecmp(cat, "general")) {
2828                                 var = ast_variable_browse(cfg, cat);
2829                                 if (strcasecmp(cat, "zonemessages")) {
2830 #ifndef USESQLVM
2831                                         /* Process mailboxes in this context */
2832                                         while(var) {
2833                                                 append_mailbox(cat, var->name, var->value);
2834                                                 var = var->next;
2835                                         }
2836 #endif
2837                                 } else {
2838                                         /* Timezones in this context */
2839                                         while(var) {
2840                                                 struct vm_zone *z;
2841                                                 z = malloc(sizeof(struct vm_zone));
2842                                                 if (z != NULL) {
2843                                                         char *msg_format, *timezone;
2844                                                         msg_format = ast_strdupa(var->value);
2845                                                         if (msg_format != NULL) {
2846                                                                 timezone = strsep(&msg_format, "|");
2847                                                                 strncpy(z->name, var->name, sizeof(z->name) - 1);
2848                                                                 strncpy(z->timezone, timezone, sizeof(z->timezone) - 1);
2849                                                                 strncpy(z->msg_format, msg_format, sizeof(z->msg_format) - 1);
2850                                                                 z->next = NULL;
2851                                                                 if (zones) {
2852                                                                         zonesl->next = z;
2853                                                                         zonesl = z;
2854                                                                 } else {
2855                                                                         zones = z;
2856                                                                         zonesl = z;
2857                                                                 }
2858                                                         } else {
2859                                                                 ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2860                                                                 free(z);
2861                                                                 return -1;
2862                                                         }
2863                                                 } else {
2864                                                         ast_log(LOG_WARNING, "Out of memory while reading voicemail config\n");
2865                                                         return -1;
2866                                                 }
2867                                                 var = var->next;
2868                                         }
2869                                 }
2870                         }
2871                         cat = ast_category_browse(cfg, cat);
2872                 }
2873                 memset(fromstring,0,sizeof(fromstring));
2874                 memset(emailtitle,0,sizeof(emailtitle));
2875                 if (emailbody) {
2876                         free(emailbody);
2877                         emailbody = NULL;
2878                 }
2879                 if ((s=ast_variable_retrieve(cfg, "general", "pbxskip")))
2880                         pbxskip = ast_true(s);
2881                 if ((s=ast_variable_retrieve(cfg, "general", "fromstring")))
2882                         strncpy(fromstring,s,sizeof(fromstring)-1);
2883                 if ((s=ast_variable_retrieve(cfg, "general", "emailtitle")))
2884                         strncpy(emailtitle,s,sizeof(emailtitle)-1);
2885                 if ((s=ast_variable_retrieve(cfg, "general", "emailbody"))) {
2886                         char *tmpread, *tmpwrite;
2887                         emailbody = strdup(s);
2888
2889                         /* substitute strings \t and \n into the apropriate characters */
2890                         tmpread = tmpwrite = emailbody;
2891                         while ((tmpwrite = strchr(tmpread,'\\'))) {
2892                                 int len = strlen("\n");
2893                                 switch (tmpwrite[1]) {
2894                                         case 'n':
2895                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2896                                                 strncpy(tmpwrite,"\n",len);
2897                                                 break;
2898                                         case 't':
2899                                                 strncpy(tmpwrite+len,tmpwrite+2,strlen(tmpwrite+2)+1);
2900                                                 strncpy(tmpwrite,"\t",len);
2901                                                 break;
2902                                         default:
2903                                                 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n",tmpwrite[1]);
2904                                 }
2905                                 tmpread = tmpwrite+len;
2906                         }
2907                 }
2908                 ast_destroy(cfg);
2909                 ast_mutex_unlock(&vmlock);
2910                 return 0;
2911         } else {
2912                 ast_mutex_unlock(&vmlock);
2913                 ast_log(LOG_WARNING, "Error reading voicemail config\n");
2914                 return -1;
2915         }
2916 }
2917
2918 int reload(void)
2919 {
2920         return(load_config());
2921 }
2922
2923 int unload_module(void)
2924 {
2925         int res;
2926         STANDARD_HANGUP_LOCALUSERS;
2927         res = ast_unregister_application(app);
2928         res |= ast_unregister_application(app2);
2929         sql_close();
2930         return res;
2931 }
2932
2933 int load_module(void)
2934 {
2935         int res;
2936         res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
2937         res |= ast_register_application(capp, vm_exec, synopsis_vm, descrip_vm);
2938         res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
2939         res |= ast_register_application(capp2, vm_execmain, synopsis_vmain, descrip_vmain);
2940         if (res)
2941                 return(res);
2942
2943         if ((res=load_config())) {
2944                 return(res);
2945         }
2946
2947         if ((res = sql_init())) {
2948                 ast_log(LOG_WARNING, "SQL init\n");
2949                 return res;
2950         }
2951         return res;
2952 }
2953
2954 char *description(void)
2955 {
2956         return tdesc;
2957 }
2958
2959 int usecount(void)
2960 {
2961         int res;
2962         STANDARD_USECOUNT(res);
2963         return res;
2964 }
2965
2966 char *key()
2967 {
2968         return ASTERISK_GPL_KEY;
2969 }