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