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