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