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