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