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