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