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