Merge major BSD mutex and symbol conflict patches (bug #1816) (link patch still pending)
[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 "extension 'o' in the current context.\n"
176 "If the caller presses '*' during the prompt, the call jumps to\n"
177 "extension 'a' in the current context.\n"
178 "If the requested mailbox does not exist, and there exists a priority\n"
179 "n + 101, then that priority will be taken next.\n"
180 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
181 "Otherwise, it returns 0.\n";
182
183 static char *synopsis_vmain =
184 "Enter voicemail system";
185
186 static char *descrip_vmain =
187 "  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
188 "for the checking of voicemail.  The mailbox can be passed as the option,\n"
189 "which will stop the voicemail system from prompting the user for the mailbox.\n"
190 "If the mailbox is preceded by 's' then the password check will be skipped.  If\n"
191 "a context is specified, logins are considered in that voicemail context only.\n"
192 "Returns -1 if the user hangs up or 0 otherwise.\n";
193
194 static char *synopsis_vm_box_exists =
195 "Check if vmbox exists";
196
197 static char *descrip_vm_box_exists =
198 "  MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
199 "if the specified voice mailbox exists.\n";
200
201
202 /* Leave a message */
203 static char *capp = "VoiceMail2";
204 static char *app = "VoiceMail";
205
206 /* Check mail, control, etc */
207 static char *capp2 = "VoiceMailMain2";
208 static char *app2 = "VoiceMailMain";
209
210 static char *app3 = "MailboxExists";
211
212 AST_MUTEX_DEFINE_STATIC(vmlock);
213 struct ast_vm_user *users;
214 struct ast_vm_user *usersl;
215 struct vm_zone *zones = NULL;
216 struct vm_zone *zonesl = NULL;
217 static int attach_voicemail;
218 static int maxsilence;
219 static int silencethreshold = 128;
220 static char serveremail[80];
221 static char mailcmd[160];       /* Configurable mail cmd */
222 static char externnotify[160]; 
223
224 static char vmfmts[80];
225 static int vmminmessage;
226 static int vmmaxmessage;
227 static int maxgreet;
228 static int skipms;
229 static int maxlogins;
230
231 static int reviewvm;
232 static int calloper;
233 static int saycidinfo;
234 static int hearenv;
235 static char dialcontext[80];
236 static char callcontext[80];
237 static char exitcontext[80];
238
239 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
240
241
242 static char *emailbody = NULL;
243 static int pbxskip = 0;
244 static char *emailsubject = NULL;
245 static char fromstring[100];
246 static char emailtitle[100];
247 static char charset[32] = "ISO-8859-1";
248
249
250 STANDARD_LOCAL_USER;
251
252 LOCAL_USER_DECL;
253
254 static void populate_defaults(struct ast_vm_user *vmu)
255 {
256         vmu->attach = -1;
257         if (reviewvm)
258                 vmu->review = 1;
259         if (calloper)
260                 vmu->operator = 1;
261         if (saycidinfo)
262                 vmu->saycid = 1;
263         if (hearenv)
264                 vmu->envelope = 1;
265         if (callcontext)
266                 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
267         if (dialcontext)
268                 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
269         if (exitcontext)
270                 strncpy(vmu->exit, exitcontext, sizeof(vmu->exit) -1);
271 }
272
273 static void apply_options(struct ast_vm_user *vmu, char *options)
274 {
275         /* Destructively Parse options and apply */
276         char *stringp = ast_strdupa(options);
277         char *s;
278         char *var, *value;
279         
280         while((s = strsep(&stringp, "|"))) {
281                 value = s;
282                 if ((var = strsep(&value, "=")) && value) {
283                         if (!strcasecmp(var, "attach")) {
284                                 if (ast_true(value))
285                                         vmu->attach = 1;
286                                 else
287                                         vmu->attach = 0;
288                         } else if (!strcasecmp(var, "serveremail")) {
289                                 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
290                         } else if (!strcasecmp(var, "tz")) {
291                                 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
292                         } else if (!strcasecmp(var, "saycid")){
293                                 if(ast_true(value))
294                                         vmu->saycid = 1;
295                                 else
296                                         vmu->saycid = 0;
297                         } else if (!strcasecmp(var, "review")){
298                                 if(ast_true(value))
299                                         vmu->review = 1;
300                                 else
301                                         vmu->review = 0;
302                         } else if (!strcasecmp(var, "operator")){
303                                 if(ast_true(value))
304                                         vmu->operator = 1;
305                                 else
306                                         vmu->operator = 0;
307                         } else if (!strcasecmp(var, "envelope")){
308                                 if(ast_true(value))
309                                         vmu->envelope = 1;
310                                 else
311                                         vmu->envelope = 0;
312                         } else if (!strcasecmp(var, "callback")) {
313                                 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
314                         } else if (!strcasecmp(var, "dialout")) {
315                                 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
316                         } else if (!strcasecmp(var, "exitcontext")) {
317                                 strncpy(vmu->exit, value, sizeof(vmu->exit) -1);
318
319                         }
320                 }
321         }
322         
323 }
324
325 #ifdef USEMYSQLVM
326 #include "mysql-vm-routines.h"
327 #endif
328
329 #ifdef USEPOSTGRESVM
330
331 PGconn *dbhandler;
332 char    dboption[256];
333 AST_MUTEX_DEFINE_STATIC(postgreslock);
334
335 static int sql_init(void)
336 {
337         ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
338 /*      fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
339
340         dbhandler=PQconnectdb(dboption);
341         if (PQstatus(dbhandler) == CONNECTION_BAD) {
342                 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
343                 return(-1);
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[16] = "#";
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                         strcat(ecodes, "0");
1570                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "a", 1, chan->callerid))
1571                         strcat(ecodes, "*");
1572                 /* Play the beginning intro if desired */
1573                 if (strlen(prefile)) {
1574                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1575                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1576                                     res = ast_waitstream(chan, ecodes);
1577                         } else {
1578                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1579                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1580                         }
1581                         if (res < 0) {
1582                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1583                                 free_user(vmu);
1584                                 return -1;
1585                         }
1586                 }
1587                 if (res == '#') {
1588                         /* On a '#' we skip the instructions */
1589                         silent = 1;
1590                         res = 0;
1591                 }
1592                 if (!res && !silent) {
1593                         res = ast_streamfile(chan, INTRO, chan->language);
1594                         if (!res)
1595                                 res = ast_waitstream(chan, ecodes);
1596                         if (res == '#') {
1597                                 silent = 1;
1598                                 res = 0;
1599                         }
1600                 }
1601                 if (res > 0)
1602                         ast_stopstream(chan);
1603                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1604                 other than the operator -- an automated attendant or mailbox login for example */
1605                 if (res == '*') {
1606                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1607                         if (strlen(vmu->exit)) {
1608                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1609                         } else if (strlen(chan->macrocontext)) {
1610                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1611                         }
1612                         chan->priority = 0;
1613                         free_user(vmu);
1614                         return 0;
1615                 }
1616                 /* Check for a '0' here */
1617                 if (res == '0') {
1618                 transfer:
1619                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1620                         if (strlen(vmu->exit)) {
1621                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1622                         } else if (strlen(chan->macrocontext)) {
1623                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1624                         }
1625                         chan->priority = 0;
1626                         free_user(vmu);
1627                         return 0;
1628                 }
1629                 if (res >= 0) {
1630                         /* Unless we're *really* silent, try to send the beep */
1631                         res = ast_streamfile(chan, "beep", chan->language);
1632                         if (!res)
1633                                 res = ast_waitstream(chan, "");
1634                 }
1635                 if (res < 0) {
1636                         free_user(vmu);
1637                         return -1;
1638                 }
1639                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1640                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1641                 if (strlen(fmt)) {
1642                         msgnum = 0;
1643                         do {
1644                                 make_file(fn, sizeof(fn), dir, msgnum);
1645                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1646                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1647                                                                         vmu->fullname, ext, chan->name);
1648                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1649                                         break;
1650                                 msgnum++;
1651                         } while(msgnum < MAXMSG);
1652                         if (msgnum < MAXMSG) {
1653                                 /* Store information */
1654                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1655                                 txt = fopen(txtfile, "w+");
1656                                 if (txt) {
1657                                         get_date(date, sizeof(date));
1658                                         fprintf(txt, 
1659 ";\n"
1660 "; Message Information file\n"
1661 ";\n"
1662 "[message]\n"
1663 "origmailbox=%s\n"
1664 "context=%s\n"
1665 "macrocontext=%s\n"
1666 "exten=%s\n"
1667 "priority=%d\n"
1668 "callerchan=%s\n"
1669 "callerid=%s\n"
1670 "origdate=%s\n"
1671 "origtime=%ld\n",
1672         ext,
1673         chan->context,
1674         chan->macrocontext, 
1675         chan->exten,
1676         chan->priority,
1677         chan->name,
1678         chan->callerid ? chan->callerid : "Unknown",
1679         date, (long)time(NULL));
1680                                         fclose(txt);
1681                                 } else
1682                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1683                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1684                                 if (res == '0')
1685                                         goto transfer;
1686                                 if (res > 0)
1687                                         res = 0;
1688                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1689                                 if (fd > -1) {
1690                                         txt = fdopen(fd, "a");
1691                                         if (txt) {
1692                                                 fprintf(txt, "duration=%d\n", duration);
1693                                                 fclose(txt);
1694                                         } else
1695                                                 close(fd);
1696                                 }
1697                                 if (duration < vmminmessage) {
1698                                         vm_delete(fn);
1699                                         goto leave_vm_out;
1700                                 }
1701                                 stringp = fmt;
1702                                 strsep(&stringp, "|");
1703                                 /* Send e-mail if applicable */
1704                                 if (strlen(vmu->email)) {
1705                                         int attach_user_voicemail = attach_voicemail;
1706                                         char *myserveremail = serveremail;
1707                                         if (vmu->attach > -1)
1708                                                 attach_user_voicemail = vmu->attach;
1709                                         if (strlen(vmu->serveremail))
1710                                                 myserveremail = vmu->serveremail;
1711                                                 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1712                                 }
1713                                 if (strlen(vmu->pager)) {
1714                                         char *myserveremail = serveremail;
1715                                         if (strlen(vmu->serveremail))
1716                                                 myserveremail = vmu->serveremail;
1717                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1718                                 }
1719                         } else {
1720                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1721                                 if (!res)
1722                                         res = ast_waitstream(chan, "");
1723                                 ast_log(LOG_WARNING, "No more messages possible\n");
1724                         }
1725                 } else
1726                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1727 leave_vm_out:
1728                 free_user(vmu);
1729         } else {
1730                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1731                         /*Send the call to n+101 priority, where n is the current priority*/
1732                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1733                                 chan->priority+=100;
1734         }
1735         /* Leave voicemail for someone */
1736         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
1737
1738         /* If an external program is specified to be run after leaving a voicemail */
1739         run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
1740
1741         return res;
1742 }
1743
1744 static char *mbox(int id)
1745 {
1746         switch(id) {
1747         case 0:
1748                 return "INBOX";
1749         case 1:
1750                 return "Old";
1751         case 2:
1752                 return "Work";
1753         case 3:
1754                 return "Family";
1755         case 4:
1756                 return "Friends";
1757         case 5:
1758                 return "Cust1";
1759         case 6:
1760                 return "Cust2";
1761         case 7:
1762                 return "Cust3";
1763         case 8:
1764                 return "Cust4";
1765         case 9:
1766                 return "Cust5";
1767         default:
1768                 return "Unknown";
1769         }
1770 }
1771
1772 static int count_messages(char *dir)
1773 {
1774         int x;
1775         char fn[256];
1776         for (x=0;x<MAXMSG;x++) {
1777                 make_file(fn, sizeof(fn), dir, x);
1778                 if (ast_fileexists(fn, NULL, NULL) < 1)
1779                         break;
1780         }
1781         return x;
1782 }
1783
1784 static int say_and_wait(struct ast_channel *chan, int num)
1785 {
1786         int d;
1787         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1788         return d;
1789 }
1790
1791 static int copy(char *infile, char *outfile)
1792 {
1793         int ifd;
1794         int ofd;
1795         int res;
1796         int len;
1797         char buf[4096];
1798         if ((ifd = open(infile, O_RDONLY)) < 0) {
1799                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1800                 return -1;
1801         }
1802         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1803                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1804                 close(ifd);
1805                 return -1;
1806         }
1807         do {
1808                 len = read(ifd, buf, sizeof(buf));
1809                 if (len < 0) {
1810                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1811                         close(ifd);
1812                         close(ofd);
1813                         unlink(outfile);
1814                 }
1815                 if (len) {
1816                         res = write(ofd, buf, len);
1817                         if (res != len) {
1818                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1819                                 close(ifd);
1820                                 close(ofd);
1821                                 unlink(outfile);
1822                         }
1823                 }
1824         } while(len);
1825         close(ifd);
1826         close(ofd);
1827         return 0;
1828 }
1829
1830 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1831 {
1832         char sfn[256];
1833         char dfn[256];
1834         char ddir[256];
1835         char txt[256];
1836         char ntxt[256];
1837         char *dbox = mbox(box);
1838         int x;
1839         make_file(sfn, sizeof(sfn), dir, msg);
1840         make_dir(ddir, sizeof(ddir), context, username, dbox);
1841         mkdir(ddir, 0700);
1842         for (x=0;x<MAXMSG;x++) {
1843                 make_file(dfn, sizeof(dfn), ddir, x);
1844                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1845                         break;
1846         }
1847         if (x >= MAXMSG)
1848                 return -1;
1849         ast_filecopy(sfn, dfn, NULL);
1850         if (strcmp(sfn, dfn)) {
1851                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1852                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1853                 copy(txt, ntxt);
1854         }
1855         return 0;
1856 }
1857
1858 static int adsi_logo(unsigned char *buf)
1859 {
1860         int bytes = 0;
1861         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1862         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1863         return bytes;
1864 }
1865
1866 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1867 {
1868         char buf[256];
1869         int bytes=0;
1870         int x;
1871         char num[5];
1872
1873         *useadsi = 0;
1874         bytes += adsi_data_mode(buf + bytes);
1875         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1876
1877         bytes = 0;
1878         bytes += adsi_logo(buf);
1879         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1880 #ifdef DISPLAY
1881         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1882 #endif
1883         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1884         bytes += adsi_data_mode(buf + bytes);
1885         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1886
1887         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1888                 bytes = 0;
1889                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1890                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1891                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1892                 bytes += adsi_voice_mode(buf + bytes, 0);
1893                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1894                 return 0;
1895         }
1896
1897 #ifdef DISPLAY
1898         /* Add a dot */
1899         bytes = 0;
1900         bytes += adsi_logo(buf);
1901         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1902         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1903         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1904         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1905 #endif
1906         bytes = 0;
1907         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1908         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1909         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1910         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1911         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1912         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1913         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1914
1915 #ifdef DISPLAY
1916         /* Add another dot */
1917         bytes = 0;
1918         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1919       bytes += adsi_voice_mode(buf + bytes, 0);
1920
1921         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1922         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1923 #endif
1924
1925         bytes = 0;
1926         /* These buttons we load but don't use yet */
1927         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1928         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1929         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1930         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1931         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1932         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1933         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1934
1935 #ifdef DISPLAY
1936         /* Add another dot */
1937         bytes = 0;
1938         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1939         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1940         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1941 #endif
1942
1943         bytes = 0;
1944         for (x=0;x<5;x++) {
1945                 snprintf(num, sizeof(num), "%d", x);
1946                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1947         }
1948         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1949         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1950
1951 #ifdef DISPLAY
1952         /* Add another dot */
1953         bytes = 0;
1954         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1955         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1956         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1957 #endif
1958
1959         if (adsi_end_download(chan)) {
1960                 bytes = 0;
1961                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1962                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1963                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1964                 bytes += adsi_voice_mode(buf + bytes, 0);
1965                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1966                 return 0;
1967         }
1968         bytes = 0;
1969         bytes += adsi_download_disconnect(buf + bytes);
1970         bytes += adsi_voice_mode(buf + bytes, 0);
1971         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1972
1973         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1974
1975 #ifdef DISPLAY
1976         /* Add last dot */
1977         bytes = 0;
1978         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1979         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1980 #endif
1981         ast_log(LOG_DEBUG, "Restarting session...\n");
1982
1983         bytes = 0;
1984         /* Load the session now */
1985         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1986                 *useadsi = 1;
1987                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1988         } else
1989                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1990
1991         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1992         return 0;
1993 }
1994
1995 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1996 {
1997         int x;
1998         if (!adsi_available(chan))
1999           return;
2000         x = adsi_load_session(chan, adapp, adver, 1);
2001         if (x < 0)
2002                 return;
2003         if (!x) {
2004                 if (adsi_load_vmail(chan, useadsi)) {
2005                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2006                         return;
2007                 }
2008         } else
2009                 *useadsi = 1;
2010 }
2011
2012 static void adsi_login(struct ast_channel *chan)
2013 {
2014         char buf[256];
2015         int bytes=0;
2016         unsigned char keys[8];
2017         int x;
2018         if (!adsi_available(chan))
2019                 return;
2020
2021         for (x=0;x<8;x++)
2022                 keys[x] = 0;
2023         /* Set one key for next */
2024         keys[3] = ADSI_KEY_APPS + 3;
2025
2026         bytes += adsi_logo(buf + bytes);
2027         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2028         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2029         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2030         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2031         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2032         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2033         bytes += adsi_set_keys(buf + bytes, keys);
2034         bytes += adsi_voice_mode(buf + bytes, 0);
2035         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2036 }
2037
2038 static void adsi_password(struct ast_channel *chan)
2039 {
2040         char buf[256];
2041         int bytes=0;
2042         unsigned char keys[8];
2043         int x;
2044         if (!adsi_available(chan))
2045                 return;
2046
2047         for (x=0;x<8;x++)
2048                 keys[x] = 0;
2049         /* Set one key for next */
2050         keys[3] = ADSI_KEY_APPS + 3;
2051
2052         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2053         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2054         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2055         bytes += adsi_set_keys(buf + bytes, keys);
2056         bytes += adsi_voice_mode(buf + bytes, 0);
2057         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2058 }
2059
2060 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2061 {
2062         char buf[256];
2063         int bytes=0;
2064         unsigned char keys[8];
2065         int x,y;
2066
2067         if (!adsi_available(chan))
2068                 return;
2069
2070         for (x=0;x<5;x++) {
2071                 y = ADSI_KEY_APPS + 12 + start + x;
2072                 if (y > ADSI_KEY_APPS + 12 + 4)
2073                         y = 0;
2074                 keys[x] = ADSI_KEY_SKT | y;
2075         }
2076         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2077         keys[6] = 0;
2078         keys[7] = 0;
2079
2080         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2081         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2082         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2083         bytes += adsi_set_keys(buf + bytes, keys);
2084         bytes += adsi_voice_mode(buf + bytes, 0);
2085
2086         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2087 }
2088
2089 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2090 {
2091         int bytes=0;
2092         char buf[256], buf1[256], buf2[256];
2093         char fn2[256];
2094
2095         char cid[256]="";
2096         char *val;
2097         char *name, *num;
2098         char datetime[21]="";
2099         FILE *f;
2100
2101         unsigned char keys[8];
2102
2103         int x;
2104
2105         if (!adsi_available(chan))
2106                 return;
2107
2108         /* Retrieve important info */
2109         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2110         f = fopen(fn2, "r");
2111         if (f) {
2112                 while(!feof(f)) {       
2113                         fgets(buf, sizeof(buf), f);
2114                         if (!feof(f)) {
2115                                 char *stringp=NULL;
2116                                 stringp=buf;
2117                                 strsep(&stringp, "=");
2118                                 val = strsep(&stringp, "=");
2119                                 if (val && strlen(val)) {
2120                                         if (!strcmp(buf, "callerid"))
2121                                                 strncpy(cid, val, sizeof(cid) - 1);
2122                                         if (!strcmp(buf, "origdate"))
2123                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2124                                 }
2125                         }
2126                 }
2127                 fclose(f);
2128         }
2129         /* New meaning for keys */
2130         for (x=0;x<5;x++)
2131                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2132         keys[6] = 0x0;
2133         keys[7] = 0x0;
2134
2135         if (!msg) {
2136                 /* No prev key, provide "Folder" instead */
2137                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2138         }
2139         if (msg >= last) {
2140                 /* If last message ... */
2141                 if (msg) {
2142                         /* but not only message, provide "Folder" instead */
2143                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2144       bytes += adsi_voice_mode(buf + bytes, 0);
2145
2146                 } else {
2147                         /* Otherwise if only message, leave blank */
2148                         keys[3] = 1;
2149                 }
2150         }
2151
2152         if (strlen(cid)) {
2153                 ast_callerid_parse(cid, &name, &num);
2154                 if (!name)
2155                         name = num;
2156         } else
2157                 name = "Unknown Caller";
2158
2159         /* If deleted, show "undeleted" */
2160
2161         if (deleted)
2162                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2163
2164         /* Except "Exit" */
2165         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2166         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2167                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2168         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2169
2170         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2171         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2172         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2173         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2174         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2175         bytes += adsi_set_keys(buf + bytes, keys);
2176         bytes += adsi_voice_mode(buf + bytes, 0);
2177
2178         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2179 }
2180
2181 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2182 {
2183         int bytes=0;
2184         char buf[256];
2185         unsigned char keys[8];
2186
2187         int x;
2188
2189         if (!adsi_available(chan))
2190                 return;
2191
2192         /* New meaning for keys */
2193         for (x=0;x<5;x++)
2194                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2195
2196         keys[6] = 0x0;
2197         keys[7] = 0x0;
2198
2199         if (!msg) {
2200                 /* No prev key, provide "Folder" instead */
2201                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2202         }
2203         if (msg >= last) {
2204                 /* If last message ... */
2205                 if (msg) {
2206                         /* but not only message, provide "Folder" instead */
2207                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2208                 } else {
2209                         /* Otherwise if only message, leave blank */
2210                         keys[3] = 1;
2211                 }
2212         }
2213
2214         /* If deleted, show "undeleted" */
2215         if (deleted) 
2216                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2217
2218         /* Except "Exit" */
2219         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2220         bytes += adsi_set_keys(buf + bytes, keys);
2221         bytes += adsi_voice_mode(buf + bytes, 0);
2222
2223         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2224 }
2225
2226 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2227 {
2228         char buf[256], buf1[256], buf2[256];
2229         int bytes=0;
2230         unsigned char keys[8];
2231         int x;
2232
2233         char *newm = (new == 1) ? "message" : "messages";
2234         char *oldm = (old == 1) ? "message" : "messages";
2235         if (!adsi_available(chan))
2236                 return;
2237         if (new) {
2238                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2239                 if (old) {
2240                         strcat(buf1, " and");
2241                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2242                 } else {
2243                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2244                 }
2245         } else if (old) {
2246                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2247                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2248         } else {
2249                 strcpy(buf1, "You have no messages.");
2250                 strcpy(buf2, " ");
2251         }
2252         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2253         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2254         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2255
2256         for (x=0;x<6;x++)
2257                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2258         keys[6] = 0;
2259         keys[7] = 0;
2260
2261         /* Don't let them listen if there are none */
2262         if (lastmsg < 0)
2263                 keys[0] = 1;
2264         bytes += adsi_set_keys(buf + bytes, keys);
2265
2266         bytes += adsi_voice_mode(buf + bytes, 0);
2267
2268         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2269 }
2270
2271 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2272 {
2273         char buf[256], buf1[256], buf2[256];
2274         int bytes=0;
2275         unsigned char keys[8];
2276         int x;
2277
2278         char *mess = (messages == 1) ? "message" : "messages";
2279
2280         if (!adsi_available(chan))
2281                 return;
2282
2283         /* Original command keys */
2284         for (x=0;x<6;x++)
2285                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2286
2287         keys[6] = 0;
2288         keys[7] = 0;
2289
2290         if (messages < 1)
2291                 keys[0] = 0;
2292
2293         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2294                         strcasecmp(folder, "INBOX") ? " folder" : "");
2295
2296         if (messages)
2297                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2298         else
2299                 strcpy(buf2, "no messages.");
2300         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2301         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2302         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2303         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2304         bytes += adsi_set_keys(buf + bytes, keys);
2305
2306         bytes += adsi_voice_mode(buf + bytes, 0);
2307
2308         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2309         
2310 }
2311
2312 /*
2313 static void adsi_clear(struct ast_channel *chan)
2314 {
2315         char buf[256];
2316         int bytes=0;
2317         if (!adsi_available(chan))
2318                 return;
2319         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2320         bytes += adsi_voice_mode(buf + bytes, 0);
2321
2322         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2323 }
2324 */
2325
2326 static void adsi_goodbye(struct ast_channel *chan)
2327 {
2328         char buf[256];
2329         int bytes=0;
2330
2331         if (!adsi_available(chan))
2332                 return;
2333         bytes += adsi_logo(buf + bytes);
2334         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2335         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2336         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2337         bytes += adsi_voice_mode(buf + bytes, 0);
2338
2339         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2340 }
2341
2342 static int get_folder(struct ast_channel *chan, int start)
2343 {
2344         int x;
2345         int d;
2346         char fn[256];
2347         d = play_and_wait(chan, "vm-press");
2348         if (d)
2349                 return d;
2350         for (x = start; x< 5; x++) {
2351                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2352                         return d;
2353                 d = play_and_wait(chan, "vm-for");
2354                 if (d)
2355                         return d;
2356                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2357                 d = play_and_wait(chan, fn);
2358                 if (d)
2359                         return d;
2360                 d = play_and_wait(chan, "vm-messages");
2361                 if (d)
2362                         return d;
2363                 d = ast_waitfordigit(chan, 500);
2364                 if (d)
2365                         return d;
2366         }
2367         d = play_and_wait(chan, "vm-tocancel");
2368         if (d)
2369                 return d;
2370         d = ast_waitfordigit(chan, 4000);
2371         return d;
2372 }
2373
2374 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2375 {
2376         int res = 0;
2377         res = play_and_wait(chan, fn);
2378         while (((res < '0') || (res > '9')) &&
2379                         (res != '#') && (res >= 0)) {
2380                 res = get_folder(chan, 0);
2381         }
2382         return res;
2383 }
2384
2385 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2386 {
2387         int cmd = 0;
2388         int retries = 0;
2389         int duration = 0;
2390
2391         while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2392                 if (cmd)
2393                         retries = 0;
2394                 switch (cmd) {
2395                 case '1': 
2396                         /* prepend a message to the current message and return */
2397                 {
2398                         char file[200];
2399                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2400                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2401                         break;
2402                 }
2403                 case '2': 
2404                         cmd = 't';
2405                         break;
2406                 case '*':
2407                         cmd = '*';
2408                         break;
2409                 default: 
2410                         cmd = play_and_wait(chan,"vm-forwardoptions");
2411                         if (!cmd)
2412                                 cmd = play_and_wait(chan,"vm-starmain");
2413                         if (!cmd)
2414                                 cmd = ast_waitfordigit(chan,6000);
2415                         if (!cmd)
2416                                 retries++;
2417                         if (retries > 3)
2418                                 cmd = 't';
2419                  }
2420         }
2421         if (cmd == 't')
2422                 cmd = 0;
2423         return cmd;
2424 }
2425
2426 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2427 {
2428         char username[70];
2429         char sys[256];
2430         char todir[256];
2431         int todircount=0;
2432         int duration;
2433         struct ast_config *mif;
2434         char miffile[256];
2435         char fn[256];
2436         char callerid[512];
2437         char ext_context[256]="";
2438         int res = 0, cmd = 0;
2439         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2440         char tmp[256];
2441         char *stringp, *s;
2442         int saved_messages = 0, found = 0;
2443         int valid_extensions = 0;
2444         while (!res && !valid_extensions) {
2445                 res = ast_streamfile(chan, "vm-extension", chan->language);
2446                 if (res)
2447                         break;
2448                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2449                         break;
2450                 /* start all over if no username */
2451                 if (!strlen(username))
2452                         continue;
2453                 stringp = username;
2454                 s = strsep(&stringp, "*");
2455                 /* start optimistic */
2456                 valid_extensions = 1;
2457                 while (s) {
2458                         /* find_user is going to malloc since we have a NULL as first argument */
2459                         if ((receiver = find_user(NULL, context, s))) {
2460                                 if (!extensions)
2461                                         vmtmp = extensions = receiver;
2462                                 else {
2463                                         vmtmp->next = receiver;
2464                                         vmtmp = receiver;
2465                                 }
2466                                 found++;
2467                         } else {
2468                                 valid_extensions = 0;
2469                                 break;
2470                         }
2471                         s = strsep(&stringp, "*");
2472                 }
2473                 /* break from the loop of reading the extensions */
2474                 if (valid_extensions)
2475                         break;
2476                 /* invalid extension, try again */
2477                 res = play_and_wait(chan, "pbx-invalid");
2478         }
2479         /* check if we're clear to proceed */
2480         if (!extensions || !valid_extensions)
2481                 return res;
2482         vmtmp = extensions;
2483         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2484         if (!cmd) {
2485                 while(!res && vmtmp) {
2486                         /* if (play_and_wait(chan, "vm-savedto"))
2487                                 break;
2488                         */
2489                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2490                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2491                         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2492                         ast_log(LOG_DEBUG, sys);
2493                         ast_safe_system(sys);
2494         
2495                         todircount = count_messages(todir);
2496                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2497                         stringp = tmp;
2498                         while((s = strsep(&stringp, "|"))) {
2499                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2500                                 if (!strcasecmp(s, "wav49"))
2501                                         s = "WAV";
2502                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2503                                 ast_log(LOG_DEBUG, sys);
2504                                 ast_safe_system(sys);
2505                         }
2506                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2507                         ast_log(LOG_DEBUG, sys);
2508                         ast_safe_system(sys);
2509                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2510         
2511                         /* load the information on the source message so we can send an e-mail like a new message */
2512                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2513                         if ((mif=ast_load(miffile))) {
2514         
2515                                 /* set callerid and duration variables */
2516                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2517                                 s = ast_variable_retrieve(mif, NULL, "duration");
2518                                 if (s)
2519                                         duration = atoi(s);
2520                                 else
2521                                         duration = 0;
2522                                 if (strlen(vmtmp->email)) {
2523                                         int attach_user_voicemail = attach_voicemail;
2524                                         char *myserveremail = serveremail;
2525                                         if (vmtmp->attach > -1)
2526                                                 attach_user_voicemail = vmtmp->attach;
2527                                         if (strlen(vmtmp->serveremail))
2528                                                 myserveremail = vmtmp->serveremail;
2529                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2530                                 }
2531                              
2532                                 if (strlen(vmtmp->pager)) {
2533                                         char *myserveremail = serveremail;
2534                                         if (strlen(vmtmp->serveremail))
2535                                                 myserveremail = vmtmp->serveremail;
2536                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2537                                 }
2538                                   
2539                                 ast_destroy(mif); /* or here */
2540                         }
2541                         /* Leave voicemail for someone */
2542                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2543                         run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2544         
2545                         saved_messages++;
2546                         vmfree = vmtmp;
2547                         vmtmp = vmtmp->next;
2548                         free_user(vmfree);
2549                 }
2550                 if (saved_messages > 0) {
2551                         /* give confirmatopm that the message was saved */
2552                         /* commented out since we can't forward batches yet
2553                         if (saved_messages == 1)
2554                                 res = play_and_wait(chan, "vm-message");
2555                         else
2556                                 res = play_and_wait(chan, "vm-messages");
2557                         if (!res)
2558                                 res = play_and_wait(chan, "vm-saved"); */
2559                         if (!res)
2560                                 res = play_and_wait(chan, "vm-msgsaved");
2561                 }
2562         }
2563         return res ? res : cmd;
2564 }
2565
2566
2567 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2568 {
2569         int res;
2570         if ((res = ast_streamfile(chan, file, chan->language))) 
2571                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2572         if (!res)
2573                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2574         return res;
2575 }
2576
2577 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2578 {
2579         return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2580 }
2581
2582 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2583 {
2584         int res = 0;
2585         struct vm_zone *the_zone = NULL;
2586         time_t t;
2587         long tin;
2588
2589         if (sscanf(origtime,"%ld",&tin) < 1) {
2590                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2591                 return 0;
2592         }
2593         t = tin;
2594
2595         /* Does this user have a timezone specified? */
2596         if (strlen(vmu->zonetag)) {
2597                 /* Find the zone in the list */
2598                 struct vm_zone *z;
2599                 z = zones;
2600                 while (z) {
2601                         if (!strcmp(z->name, vmu->zonetag)) {
2602                                 the_zone = z;
2603                                 break;
2604                         }
2605                         z = z->next;
2606                 }
2607         }
2608
2609 /* No internal variable parsing for now, so we'll comment it out for the time being */
2610 #if 0
2611         /* Set the DIFF_* variables */
2612         localtime_r(&t, &time_now);
2613         gettimeofday(&tv_now,NULL);
2614         tnow = tv_now.tv_sec;
2615         localtime_r(&tnow,&time_then);
2616
2617         /* Day difference */
2618         if (time_now.tm_year == time_then.tm_year)
2619                 sprintf(temp,"%d",time_now.tm_yday);
2620         else
2621                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2622         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2623
2624         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2625 #endif
2626         if (the_zone)
2627                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2628         else
2629                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2630 #if 0
2631         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2632 #endif
2633         return res;
2634 }
2635
2636
2637
2638 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2639 {
2640         int res = 0;
2641         int i;
2642         char *callerid, *name;
2643         char prefile[256]="";
2644         
2645
2646         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2647         /* 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 */
2648         if((cid == NULL)||(context == NULL))
2649                 return res;
2650
2651         /* Strip off caller ID number from name */
2652         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2653         ast_callerid_parse(cid, &name, &callerid);
2654         if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2655                 /* Check for internal contexts and only */
2656                 /* say extension when the call didn't come from an internal context in the list */
2657                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2658                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2659                         if((strcmp(cidinternalcontexts[i], context) == 0))
2660                                 break;
2661                 }
2662                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2663                         if(!res) {
2664                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2665                                 if (strlen(prefile)) {
2666                                 /* See if we can find a recorded name for this person instead of their extension number */
2667                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2668                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2669                                                 if (!callback)
2670                                                         res = wait_file2(chan, vms, "vm-from");
2671                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2672                                                 res = ast_waitstream(chan, "");
2673                                         } else {
2674                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2675                                                 /* BB: Say "from extension" as one saying to sound smoother */
2676                                                 if (!callback)
2677                                                         res = wait_file2(chan, vms, "vm-from-extension");
2678                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2679                                         }
2680                                 }
2681                         }
2682                 }
2683
2684                 else if (!res){
2685                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2686                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2687                         if (!callback)
2688                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2689                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2690                 }
2691         }
2692         else{
2693                 /* Number unknown */
2694                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2695                 if(!res)
2696                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2697                         res = wait_file2(chan, vms, "vm-unknown-caller");
2698         }
2699         return res;                                                               
2700 }
2701
2702 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2703 {
2704         int res = 0;
2705         char filename[256],*origtime, *cid, *context;
2706         struct ast_config *msg_cfg;
2707
2708         vms->starting = 0; 
2709         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2710         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2711         if (!msg)
2712                 res = wait_file2(chan, vms, "vm-first");
2713         else if (msg == vms->lastmsg)
2714                 res = wait_file2(chan, vms, "vm-last");
2715         if (!res) {
2716                 res = wait_file2(chan, vms, "vm-message");
2717                 if (msg && (msg != vms->lastmsg)) {
2718                         if (!res)
2719                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2720                 }
2721         }
2722
2723         /* Retrieve info from VM attribute file */
2724
2725         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2726         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2727         msg_cfg = ast_load(filename);
2728         if (!msg_cfg) {
2729                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2730                 return 0;
2731         }
2732                                                                                                                                  
2733         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2734                 return 0;
2735                                                                                                                                  
2736         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2737
2738         context = ast_variable_retrieve(msg_cfg, "message", "context");
2739         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2740                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2741
2742         if ((!res)&&(vmu->envelope))
2743                 res = play_message_datetime(chan, vmu, origtime, filename);
2744         if ((!res)&&(vmu->saycid))
2745                 res = play_message_callerid(chan, vms, cid, context, 0);
2746         /* Allow pressing '1' to skip envelope / callerid */
2747         if (res == '1')
2748                 res = 0;
2749         ast_destroy(msg_cfg);
2750
2751         if (!res) {
2752                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2753                 vms->heard[msg] = 1;
2754                 res = wait_file(chan, vms, vms->fn);
2755         }
2756         return res;
2757 }
2758
2759 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2760 {
2761         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2762         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2763         vms->lastmsg = count_messages(vms->curdir) - 1;
2764         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2765 }
2766
2767 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2768 {
2769         int x;
2770         char ntxt[256] = "";
2771         char txt[256] = "";
2772         if (vms->lastmsg > -1) { 
2773                 /* Get the deleted messages fixed */ 
2774                 vms->curmsg = -1; 
2775                 for (x=0;x < MAXMSG;x++) { 
2776                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2777                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2778                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2779                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2780                                         break;
2781                                 vms->curmsg++; 
2782                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2783                                 if (strcmp(vms->fn, vms->fn2)) { 
2784                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2785                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2786                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2787                                         rename(txt, ntxt); 
2788                                 } 
2789                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2790                                 /* Move to old folder before deleting */ 
2791                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2792                         } 
2793                 } 
2794                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2795                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2796                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2797                                 break;
2798                         vm_delete(vms->fn);
2799                 } 
2800         } 
2801         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2802         memset(vms->heard, 0, sizeof(vms->heard)); 
2803 }
2804
2805 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2806 {
2807         /* Introduce messages they have */
2808         int res;
2809         res = play_and_wait(chan, "vm-youhave");
2810         if (!res) {
2811                 if (vms->newmessages) {
2812                         res = say_and_wait(chan, vms->newmessages);
2813                         if (!res)
2814                                 res = play_and_wait(chan, "vm-INBOX");
2815                         if (vms->oldmessages && !res)
2816                                 res = play_and_wait(chan, "vm-and");
2817                         else if (!res) {
2818                                 if ((vms->newmessages == 1))
2819                                         res = play_and_wait(chan, "vm-message");
2820                                 else
2821                                         res = play_and_wait(chan, "vm-messages");
2822                         }
2823                                 
2824                 }
2825                 if (!res && vms->oldmessages) {
2826                         res = say_and_wait(chan, vms->oldmessages);
2827                         if (!res)
2828                                 res = play_and_wait(chan, "vm-Old");
2829                         if (!res) {
2830                                 if (vms->oldmessages == 1)
2831                                         res = play_and_wait(chan, "vm-message");
2832                                 else
2833                                         res = play_and_wait(chan, "vm-messages");
2834                         }
2835                 }
2836                 if (!res) {
2837                         if (!vms->oldmessages && !vms->newmessages) {
2838                                 res = play_and_wait(chan, "vm-no");
2839                                 if (!res)
2840                                         res = play_and_wait(chan, "vm-messages");
2841                         }
2842                 }
2843         }
2844         return res;
2845 }
2846
2847 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2848 {
2849         int res = 0;
2850         /* Play instructions and wait for new command */
2851         while(!res) {
2852                 if (vms->starting) {
2853                         if (vms->lastmsg > -1) {
2854                                 res = play_and_wait(chan, "vm-onefor");
2855                                 if (!res)
2856                                         res = play_and_wait(chan, vms->vmbox);
2857                                 if (!res)
2858                                         res = play_and_wait(chan, "vm-messages");
2859                         }
2860                         if (!res)
2861                                 res = play_and_wait(chan, "vm-opts");
2862                 } else {
2863                         if (vms->curmsg)
2864                                 res = play_and_wait(chan, "vm-prev");
2865                         if (!res)
2866                                 res = play_and_wait(chan, "vm-advopts");
2867                         if (!res)
2868                                 res = play_and_wait(chan, "vm-repeat");
2869                         if (!res && (vms->curmsg != vms->lastmsg))
2870                                 res = play_and_wait(chan, "vm-next");
2871                         if (!res) {
2872                                 if (!vms->deleted[vms->curmsg])
2873                                         res = play_and_wait(chan, "vm-delete");
2874                                 else
2875                                         res = play_and_wait(chan, "vm-undelete");
2876                                 if (!res)
2877                                         res = play_and_wait(chan, "vm-toforward");
2878                                 if (!res)
2879                                         res = play_and_wait(chan, "vm-savemessage");
2880                         }
2881                 }
2882                 if (!res)
2883                         res = play_and_wait(chan, "vm-helpexit");
2884                 if (!res)
2885                         res = ast_waitfordigit(chan, 6000);
2886                 if (!res) {
2887                         vms->repeats++;
2888                         if (vms->repeats > 2) {
2889                                 res = 't';
2890                         }
2891                 }
2892         }
2893         return res;
2894 }
2895
2896 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2897 {
2898         int cmd = 0;
2899         int retries = 0;
2900         int duration = 0;
2901         char newpassword[80] = "";
2902         char newpassword2[80] = "";
2903         char prefile[256]="";
2904         char buf[256];
2905         int bytes=0;
2906
2907         if (adsi_available(chan))
2908         {
2909                 bytes += adsi_logo(buf + bytes);
2910                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2911                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2912                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2913                 bytes += adsi_voice_mode(buf + bytes, 0);
2914                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2915         }
2916         while((cmd >= 0) && (cmd != 't')) {
2917                 if (cmd)
2918                         retries = 0;
2919                 switch (cmd) {
2920                 case '1':
2921                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2922                         cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2923                         break;
2924                 case '2': 
2925                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2926                         cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2927                         break;
2928                 case '3': 
2929                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2930                         cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2931                         break;
2932                 case '4':
2933                         newpassword[1] = '\0';
2934                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2935                         if (cmd < 0)
2936                                 break;
2937                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2938                                 break;
2939             }
2940                         newpassword2[1] = '\0';
2941                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2942                         if (cmd < 0)
2943                                 break;
2944
2945                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2946                                 break;
2947             }
2948                         if (strcmp(newpassword, newpassword2)) {
2949                     &nbs