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