Stay in admin menu until user wants out (bug #977)
[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 = 't';
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, "auth-thankyou", 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);
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)))
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 = play_and_wait(chan,"vm-starmain");
2333                         if (!cmd)
2334                                 cmd = ast_waitfordigit(chan,6000);
2335                         if (!cmd)
2336                                 retries++;
2337                         if (retries > 3)
2338                                 cmd = 't';
2339                  }
2340         }
2341         if (cmd == 't')
2342                 cmd = 0;
2343         return cmd;
2344 }
2345
2346 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2347 {
2348         char username[70];
2349         char sys[256];
2350         char todir[256];
2351         int todircount=0;
2352         long duration;
2353         struct ast_config *mif;
2354         char miffile[256];
2355         char fn[256];
2356         char callerid[512];
2357         int res = 0, cmd = 0;
2358         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2359         char tmp[256];
2360         char *stringp, *s;
2361         int saved_messages = 0, found = 0;
2362         int valid_extensions = 0;
2363         while (!res && !valid_extensions) {
2364                 res = ast_streamfile(chan, "vm-extension", chan->language);
2365                 if (res)
2366                         break;
2367                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2368                         break;
2369                 /* start all over if no username */
2370                 if (!strlen(username))
2371                         continue;
2372                 stringp = username;
2373                 s = strsep(&stringp, "*");
2374                 /* start optimistic */
2375                 valid_extensions = 1;
2376                 while (s) {
2377                         /* find_user is going to malloc since we have a NULL as first argument */
2378                         if ((receiver = find_user(NULL, context, s))) {
2379                                 if (!extensions)
2380                                         vmtmp = extensions = receiver;
2381                                 else {
2382                                         vmtmp->next = receiver;
2383                                         vmtmp = receiver;
2384                                 }
2385                                 found++;
2386                         } else {
2387                                 valid_extensions = 0;
2388                                 break;
2389                         }
2390                         s = strsep(&stringp, "*");
2391                 }
2392                 /* break from the loop of reading the extensions */
2393                 if (valid_extensions)
2394                         break;
2395                 /* invalid extension, try again */
2396                 res = play_and_wait(chan, "pbx-invalid");
2397         }
2398         /* check if we're clear to proceed */
2399         if (!extensions || !valid_extensions)
2400                 return res;
2401         vmtmp = extensions;
2402         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2403         if (!cmd) {
2404                 while(!res && vmtmp) {
2405                         /* if (play_and_wait(chan, "vm-savedto"))
2406                                 break;
2407                         */
2408                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2409                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2410                         ast_log(LOG_DEBUG, sys);
2411                         ast_safe_system(sys);
2412         
2413                         todircount = count_messages(todir);
2414                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2415                         stringp = tmp;
2416                         while((s = strsep(&stringp, "|"))) {
2417                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2418                                 if (!strcasecmp(s, "wav49"))
2419                                         s = "WAV";
2420                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2421                                 ast_log(LOG_DEBUG, sys);
2422                                 ast_safe_system(sys);
2423                         }
2424                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2425                         ast_log(LOG_DEBUG, sys);
2426                         ast_safe_system(sys);
2427                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2428         
2429                         /* load the information on the source message so we can send an e-mail like a new message */
2430                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2431                         if ((mif=ast_load(miffile))) {
2432         
2433                                 /* set callerid and duration variables */
2434                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2435                                 s = ast_variable_retrieve(mif, NULL, "duration");
2436                                 if (s)
2437                                         duration = atol(s);
2438                                 else
2439                                         duration = 0;
2440                                 if (strlen(vmtmp->email)) {
2441                                         int attach_user_voicemail = attach_voicemail;
2442                                         char *myserveremail = serveremail;
2443                                         if (vmtmp->attach > -1)
2444                                                 attach_user_voicemail = vmtmp->attach;
2445                                         if (strlen(vmtmp->serveremail))
2446                                                 myserveremail = vmtmp->serveremail;
2447                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2448                                 }
2449                              
2450                                 if (strlen(vmtmp->pager)) {
2451                                         char *myserveremail = serveremail;
2452                                         if (strlen(vmtmp->serveremail))
2453                                                 myserveremail = vmtmp->serveremail;
2454                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2455                                 }
2456                                   
2457                                 ast_destroy(mif); /* or here */
2458                         }
2459                         /* Leave voicemail for someone */
2460                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2461                         run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2462         
2463                         saved_messages++;
2464                         vmfree = vmtmp;
2465                         vmtmp = vmtmp->next;
2466                         free_user(vmfree);
2467                 }
2468                 if (saved_messages > 0) {
2469                         /* give confirmatopm that the message was saved */
2470                         /* commented out since we can't forward batches yet
2471                         if (saved_messages == 1)
2472                                 res = play_and_wait(chan, "vm-message");
2473                         else
2474                                 res = play_and_wait(chan, "vm-messages");
2475                         if (!res)
2476                                 res = play_and_wait(chan, "vm-saved"); */
2477                         if (!res)
2478                                 res = play_and_wait(chan, "vm-msgsaved");
2479                 }
2480         }
2481         return res ? res : cmd;
2482 }
2483
2484
2485 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2486 {
2487         int res;
2488         if ((res = ast_streamfile(chan, file, chan->language))) 
2489                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2490         if (!res)
2491                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2492         return res;
2493 }
2494
2495 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2496 {
2497         int res;
2498         if ((res = ast_streamfile(chan, file, chan->language)))
2499                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2500         if (!res)
2501                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2502         return res;
2503 }
2504
2505 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2506 {
2507         int res = 0;
2508         struct vm_zone *the_zone = NULL;
2509         time_t t;
2510         long tin;
2511
2512         if (sscanf(origtime,"%ld",&tin) < 1) {
2513                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2514                 return 0;
2515         }
2516         t = tin;
2517
2518         /* Does this user have a timezone specified? */
2519         if (strlen(vmu->zonetag)) {
2520                 /* Find the zone in the list */
2521                 struct vm_zone *z;
2522                 z = zones;
2523                 while (z) {
2524                         if (!strcmp(z->name, vmu->zonetag)) {
2525                                 the_zone = z;
2526                                 break;
2527                         }
2528                         z = z->next;
2529                 }
2530         }
2531
2532 /* No internal variable parsing for now, so we'll comment it out for the time being */
2533 #if 0
2534         /* Set the DIFF_* variables */
2535         localtime_r(&t, &time_now);
2536         gettimeofday(&tv_now,NULL);
2537         tnow = tv_now.tv_sec;
2538         localtime_r(&tnow,&time_then);
2539
2540         /* Day difference */
2541         if (time_now.tm_year == time_then.tm_year)
2542                 sprintf(temp,"%d",time_now.tm_yday);
2543         else
2544                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2545         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2546
2547         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2548 #endif
2549         if (the_zone)
2550                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2551         else
2552                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2553 #if 0
2554         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2555 #endif
2556         return res;
2557 }
2558
2559
2560
2561 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2562 {
2563         int res = 0;
2564         int i;
2565         char *callerid, *name;
2566         char prefile[256]="";
2567         
2568
2569         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2570         /* 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 */
2571         if((cid == NULL)||(context == NULL))
2572                 return res;
2573
2574         /* Strip off caller ID number from name */
2575         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2576         ast_callerid_parse(cid, &name, &callerid);
2577         if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2578                 /* Check for internal contexts and only */
2579                 /* say extension when the call didn't come from an internal context in the list */
2580                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2581                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2582                         if((strcmp(cidinternalcontexts[i], context) == 0))
2583                                 break;
2584                 }
2585                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2586                         if(!res) {
2587                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2588                                 if (strlen(prefile)) {
2589                                 /* See if we can find a recorded name for this person instead of their extension number */
2590                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2591                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2592                                                 if (!callback)
2593                                                         res = wait_file2(chan, vms, "vm-from");
2594                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2595                                                 res = ast_waitstream(chan, "");
2596                                         } else {
2597                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2598                                                 /* BB: Say "from extension" as one saying to sound smoother */
2599                                                 if (!callback)
2600                                                         res = wait_file2(chan, vms, "vm-from-extension");
2601                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2602                                         }
2603                                 }
2604                         }
2605                 }
2606
2607                 else if (!res){
2608                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2609                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2610                         if (!callback)
2611                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2612                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2613                 }
2614         }
2615         else{
2616                 /* Number unknown */
2617                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2618                 if(!res)
2619                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2620                         res = wait_file2(chan, vms, "vm-unknown-caller");
2621         }
2622         return res;                                                               
2623 }
2624
2625 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2626 {
2627         int res = 0;
2628         char filename[256],*origtime, *cid, *context;
2629         struct ast_config *msg_cfg;
2630
2631         vms->starting = 0; 
2632         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2633         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2634         if (!msg)
2635                 res = wait_file2(chan, vms, "vm-first");
2636         else if (msg == vms->lastmsg)
2637                 res = wait_file2(chan, vms, "vm-last");
2638         if (!res) {
2639                 res = wait_file2(chan, vms, "vm-message");
2640                 if (msg && (msg != vms->lastmsg)) {
2641                         if (!res)
2642                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language);
2643                 }
2644         }
2645
2646         /* Retrieve info from VM attribute file */
2647
2648         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2649         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2650         msg_cfg = ast_load(filename);
2651         if (!msg_cfg) {
2652                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2653                 return 0;
2654         }
2655                                                                                                                                  
2656         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2657                 return 0;
2658                                                                                                                                  
2659         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2660
2661         context = ast_variable_retrieve(msg_cfg, "message", "context");
2662         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2663                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2664
2665         if (!res)
2666                 res = play_message_datetime(chan, vmu, origtime, filename);
2667
2668         if ((!res)&&(vmu->saycid))
2669                 res = play_message_callerid(chan, vms, cid, context, 0);
2670
2671         ast_destroy(msg_cfg);
2672
2673         if (!res) {
2674                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2675                 vms->heard[msg] = 1;
2676                 res = wait_file(chan, vms, vms->fn);
2677         }
2678         return res;
2679 }
2680
2681 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2682 {
2683         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2684         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2685         vms->lastmsg = count_messages(vms->curdir) - 1;
2686         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2687 }
2688
2689 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2690 {
2691         int x;
2692         char ntxt[256] = "";
2693         char txt[256] = "";
2694         if (vms->lastmsg > -1) { 
2695                 /* Get the deleted messages fixed */ 
2696                 vms->curmsg = -1; 
2697                 for (x=0;x < MAXMSG;x++) { 
2698                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2699                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2700                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2701                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2702                                         break;
2703                                 vms->curmsg++; 
2704                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2705                                 if (strcmp(vms->fn, vms->fn2)) { 
2706                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2707                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2708                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2709                                         rename(txt, ntxt); 
2710                                 } 
2711                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2712                                 /* Move to old folder before deleting */ 
2713                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2714                         } 
2715                 } 
2716                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2717                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2718                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2719                                 break;
2720                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2721                         ast_filedelete(vms->fn, NULL); 
2722                         unlink(txt); 
2723                 } 
2724         } 
2725         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2726         memset(vms->heard, 0, sizeof(vms->heard)); 
2727 }
2728
2729 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2730 {
2731         /* Introduce messages they have */
2732         int res;
2733         res = play_and_wait(chan, "vm-youhave");
2734         if (!res) {
2735                 if (vms->newmessages) {
2736                         res = say_and_wait(chan, vms->newmessages);
2737                         if (!res)
2738                                 res = play_and_wait(chan, "vm-INBOX");
2739                         if (vms->oldmessages && !res)
2740                                 res = play_and_wait(chan, "vm-and");
2741                         else if (!res) {
2742                                 if ((vms->newmessages == 1))
2743                                         res = play_and_wait(chan, "vm-message");
2744                                 else
2745                                         res = play_and_wait(chan, "vm-messages");
2746                         }
2747                                 
2748                 }
2749                 if (!res && vms->oldmessages) {
2750                         res = say_and_wait(chan, vms->oldmessages);
2751                         if (!res)
2752                                 res = play_and_wait(chan, "vm-Old");
2753                         if (!res) {
2754                                 if (vms->oldmessages == 1)
2755                                         res = play_and_wait(chan, "vm-message");
2756                                 else
2757                                         res = play_and_wait(chan, "vm-messages");
2758                         }
2759                 }
2760                 if (!res) {
2761                         if (!vms->oldmessages && !vms->newmessages) {
2762                                 res = play_and_wait(chan, "vm-no");
2763                                 if (!res)
2764                                         res = play_and_wait(chan, "vm-messages");
2765                         }
2766                 }
2767         }
2768         return res;
2769 }
2770
2771 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2772 {
2773         int res = 0;
2774         /* Play instructions and wait for new command */
2775         while(!res) {
2776                 if (vms->starting) {
2777                         if (vms->lastmsg > -1) {
2778                                 res = play_and_wait(chan, "vm-onefor");
2779                                 if (!res)
2780                                         res = play_and_wait(chan, vms->vmbox);
2781                                 if (!res)
2782                                         res = play_and_wait(chan, "vm-messages");
2783                         }
2784                         if (!res)
2785                                 res = play_and_wait(chan, "vm-opts");
2786                 } else {
2787                         if (vms->curmsg)
2788                                 res = play_and_wait(chan, "vm-prev");
2789                         if (!res)
2790                                 res = play_and_wait(chan, "vm-advopts");
2791                         if (!res)
2792                                 res = play_and_wait(chan, "vm-repeat");
2793                         if (!res && (vms->curmsg != vms->lastmsg))
2794                                 res = play_and_wait(chan, "vm-next");
2795                         if (!res) {
2796                                 if (!vms->deleted[vms->curmsg])
2797                                         res = play_and_wait(chan, "vm-delete");
2798                                 else
2799                                         res = play_and_wait(chan, "vm-undelete");
2800                                 if (!res)
2801                                         res = play_and_wait(chan, "vm-toforward");
2802                                 if (!res)
2803                                         res = play_and_wait(chan, "vm-savemessage");
2804                         }
2805                 }
2806                 if (!res)
2807                         res = play_and_wait(chan, "vm-helpexit");
2808                 if (!res)
2809                         res = ast_waitfordigit(chan, 6000);
2810                 if (!res) {
2811                         vms->repeats++;
2812                         if (vms->repeats > 2) {
2813                                 res = 't';
2814                         }
2815                 }
2816         }
2817         return res;
2818 }
2819
2820 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2821 {
2822         int cmd = 0;
2823         int retries = 0;
2824         char newpassword[80] = "";
2825         char newpassword2[80] = "";
2826         char prefile[256]="";
2827         char buf[256];
2828         int bytes=0;
2829
2830         if (adsi_available(chan))
2831         {
2832                 bytes += adsi_logo(buf + bytes);
2833                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2834                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2835                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2836                 bytes += adsi_voice_mode(buf + bytes, 0);
2837                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2838         }
2839         while((cmd >= 0) && (cmd != 't')) {
2840                 if (cmd)
2841                         retries = 0;
2842                 switch (cmd) {
2843                 case '1':
2844                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2845                         cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, 0);
2846                         break;
2847                 case '2': 
2848                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2849                         cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, 0);
2850                         break;
2851                 case '3': 
2852                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2853                         cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, 0);
2854                         break;
2855                 case '4':
2856                         newpassword[1] = '\0';
2857                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2858                         if (cmd < 0)
2859                                 break;
2860                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2861                                 break;
2862             }
2863                         newpassword2[1] = '\0';
2864                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2865                         if (cmd < 0)
2866                                 break;
2867
2868                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2869                                 break;
2870             }
2871                         if (strcmp(newpassword, newpassword2)) {
2872                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2873                                 cmd = play_and_wait(chan, "vm-mismatch");
2874                                 break;
2875                         }
2876                         vm_change_password(vmu,newpassword);
2877                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2878                         cmd = play_and_wait(chan,"vm-passchanged");
2879                         break;
2880                 case '*': 
2881                         cmd = 't';
2882                         break;
2883                 default: 
2884                         cmd = play_and_wait(chan,"vm-options");
2885                         if (!cmd)
2886                                 cmd = ast_waitfordigit(chan,6000);
2887                         if (!cmd)
2888                                 retries++;
2889                         if (retries > 3)
2890                                 cmd = 't';
2891                  }
2892         }
2893         if (cmd == 't')
2894                 cmd = 0;
2895         return cmd;
2896 }
2897
2898 static int vm_execmain(struct ast_channel *chan, void *data)
2899 {
2900         /* XXX This is, admittedly, some pretty horrendus code.  For some
2901            reason it just seemed a lot easier to do with GOTO's.  I feel
2902            like I'm back in my GWBASIC days. XXX */
2903         int res=-1;
2904         int valid = 0;
2905         int prefix = 0;
2906         int cmd=0;
2907         struct localuser *u;
2908         char prefixstr[80] ="";
2909         char empty[80] = "";
2910         int box;
2911         int useadsi = 0;
2912         int skipuser = 0;
2913         char tmp[256], *ext;
2914         char fmtc[256] = "";
2915         char password[80];
2916         struct vm_state vms;
2917         int logretries = 0;
2918         struct ast_vm_user *vmu = NULL, vmus;
2919         char *context=NULL;
2920         int silentexit = 0;
2921
2922         LOCAL_USER_ADD(u);
2923         memset(&vms, 0, sizeof(vms));
2924         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2925         if (chan->_state != AST_STATE_UP)
2926                 ast_answer(chan);
2927
2928         if (data && strlen(data)) {
2929                 strncpy(tmp, data, sizeof(tmp) - 1);
2930                 ext = tmp;
2931
2932                 switch (*ext) {
2933                         case 's':
2934                  /* We should skip the user's password */
2935                                 valid++;
2936                                 ext++;
2937                                 break;
2938                         case 'p':
2939                  /* We should prefix the mailbox with the supplied data */
2940                                 prefix++;
2941                                 ext++;
2942                                 break;
2943                 }
2944
2945                 context = strchr(ext, '@');
2946                 if (context) {
2947                         *context = '\0';
2948                         context++;
2949                 }
2950
2951                 if (prefix)
2952                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2953                 else
2954                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2955                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2956                         skipuser++;
2957                 else
2958                &