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