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