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