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