Further minor fixup
[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 static int vm_delete(char *file);
148
149
150
151 static char *tdesc = "Comedian Mail (Voicemail System)";
152
153 static char *adapp = "CoMa";
154
155 static char *adsec = "_AST";
156
157 static char *addesc = "Comedian Mail";
158
159 static int adver = 1;
160
161 static char *synopsis_vm =
162 "Leave a voicemail message";
163
164 static char *descrip_vm =
165 "  VoiceMail([s|u|b]extension[@context]):  Leaves voicemail for a given\n"
166 "extension (must be configured in voicemail.conf).\n"
167 " If the extension is preceded by \n"
168 "* 's' then instructions for leaving the message will be skipped.\n"
169 "* 'u' then the \"unavailable\" message will be played.\n"
170 "  (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
171 "* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
172 "If the caller presses '0' (zero) during the prompt, the call jumps to\n"
173 "priority 'o' in the current context.\n"
174 "If the requested mailbox does not exist, and there exists a priority\n"
175 "n + 101, then that priority will be taken next.\n"
176 "Returns -1 on error or mailbox not found, or if the user hangs up.\n"
177 "Otherwise, it returns 0.\n";
178
179 static char *synopsis_vmain =
180 "Enter voicemail system";
181
182 static char *descrip_vmain =
183 "  VoiceMailMain([[s]mailbox][@context]): Enters the main voicemail system\n"
184 "for the checking of voicemail.  The mailbox can be passed as the option,\n"
185 "which will stop the voicemail system from prompting the user for the mailbox.\n"
186 "If the mailbox is preceded by 's' then the password check will be skipped.  If\n"
187 "a context is specified, logins are considered in that voicemail context only.\n"
188 "Returns -1 if the user hangs up or 0 otherwise.\n";
189
190 static char *synopsis_vm_box_exists =
191 "Check if vmbox exists";
192
193 static char *descrip_vm_box_exists =
194 "  MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
195 "if the specified voice mailbox exists.\n";
196
197
198 /* Leave a message */
199 static char *capp = "VoiceMail2";
200 static char *app = "VoiceMail";
201
202 /* Check mail, control, etc */
203 static char *capp2 = "VoiceMailMain2";
204 static char *app2 = "VoiceMailMain";
205
206 static char *app3 = "MailboxExists";
207
208 static ast_mutex_t vmlock = AST_MUTEX_INITIALIZER;
209 struct ast_vm_user *users;
210 struct ast_vm_user *usersl;
211 struct vm_zone *zones = NULL;
212 struct vm_zone *zonesl = NULL;
213 static int attach_voicemail;
214 static int maxsilence;
215 static int silencethreshold = 128;
216 static char serveremail[80];
217 static char mailcmd[160];       /* Configurable mail cmd */
218 static char externnotify[160]; 
219
220 static char vmfmts[80];
221 static int vmminmessage;
222 static int vmmaxmessage;
223 static int maxgreet;
224 static int skipms;
225 static int maxlogins;
226
227 static int reviewvm;
228 static int calloper;
229 static int saycidinfo;
230 static char dialcontext[80];
231 static char callcontext[80];
232
233 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
234
235
236 static char *emailbody = NULL;
237 static int pbxskip = 0;
238 static char fromstring[100];
239 static char emailtitle[100];
240
241
242 STANDARD_LOCAL_USER;
243
244 LOCAL_USER_DECL;
245
246 static void populate_defaults(struct ast_vm_user *vmu)
247 {
248         vmu->attach = -1;
249         if (reviewvm)
250                 vmu->review = 1;
251         if (calloper)
252                 vmu->operator = 1;
253         if (saycidinfo)
254                 vmu->saycid = 1;
255         if (callcontext)
256                 strncpy(vmu->callback, callcontext, sizeof(vmu->callback) -1);
257         if (dialcontext)
258                 strncpy(vmu->dialout, dialcontext, sizeof(vmu->dialout) -1);
259 }
260
261 static void apply_options(struct ast_vm_user *vmu, char *options)
262 {
263         /* Destructively Parse options and apply */
264         char *stringp = ast_strdupa(options);
265         char *s;
266         char *var, *value;
267         
268         while((s = strsep(&stringp, "|"))) {
269                 value = s;
270                 if ((var = strsep(&value, "=")) && value) {
271                         if (!strcasecmp(var, "attach")) {
272                                 if (ast_true(value))
273                                         vmu->attach = 1;
274                                 else
275                                         vmu->attach = 0;
276                         } else if (!strcasecmp(var, "serveremail")) {
277                                 strncpy(vmu->serveremail, value, sizeof(vmu->serveremail) - 1);
278                         } else if (!strcasecmp(var, "tz")) {
279                                 strncpy(vmu->zonetag, value, sizeof(vmu->zonetag) - 1);
280                         } else if (!strcasecmp(var, "saycid")){
281                                 if(ast_true(value))
282                                         vmu->saycid = 1;
283                                 else
284                                         vmu->saycid = 0;
285                         } else if (!strcasecmp(var, "review")){
286                                 if(ast_true(value))
287                                         vmu->review = 1;
288                                 else
289                                         vmu->review = 0;
290                         } else if (!strcasecmp(var, "operator")){
291                                 if(ast_true(value))
292                                         vmu->operator = 1;
293                                 else
294                                         vmu->operator = 0;
295                         } else if (!strcasecmp(var, "callback")) {
296                                 strncpy(vmu->callback, value, sizeof(vmu->callback) -1);
297                         } else if (!strcasecmp(var, "dialout")) {
298                                 strncpy(vmu->dialout, value, sizeof(vmu->dialout) -1);
299
300                         }
301                 }
302         }
303         
304 }
305
306 #ifdef USEMYSQLVM
307 #include "mysql-vm-routines.h"
308 #endif
309
310 #ifdef USEPOSTGRESVM
311
312 PGconn *dbhandler;
313 char    dboption[256];
314 ast_mutex_t postgreslock;
315
316 static int sql_init(void)
317 {
318         ast_verbose( VERBOSE_PREFIX_3 "Logging into postgres database: %s\n", dboption);
319 /*      fprintf(stderr,"Logging into postgres database: %s\n", dboption); */
320
321         dbhandler=PQconnectdb(dboption);
322         if (PQstatus(dbhandler) == CONNECTION_BAD) {
323                 ast_log(LOG_WARNING, "Error Logging into database %s: %s\n",dboption,PQerrorMessage(dbhandler));
324                 return(-1);
325         }
326         ast_mutex_init(&postgreslock);
327
328 /*      fprintf(stderr,"postgres login OK\n"); */
329         return(0);
330 }
331
332 static void sql_close(void)
333 {
334         PQfinish(dbhandler);
335 }
336
337
338 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, char *context, char *mailbox)
339 {
340         PGresult *PGSQLres;
341
342
343         int numFields, i;
344         char *fname;
345         char query[240];
346         char options[160] = "";
347         struct ast_vm_user *retval;
348
349         retval=malloc(sizeof(struct ast_vm_user));
350
351 /*      fprintf(stderr,"postgres find_user:\n"); */
352
353         if (retval) {
354                 memset(retval, 0, sizeof(struct ast_vm_user));
355                 retval->alloced=1;
356                 if (mailbox) {
357                         strcpy(retval->mailbox, mailbox);
358                 }
359                 if (context) {
360                         strcpy(retval->context, context);
361                 }
362                 else
363                 {
364                         strcpy(retval->context, "default");
365                 }
366                 populate_defaults(retval);
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, int 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), "%d:%02d", 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 + 1, 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, int 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), "%d:%02d", 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 *duration, 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         /* barf if no pointer passed to store duration in */
1036         if (duration == NULL) {
1037                 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1038                 return -1;
1039         }
1040
1041         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1042         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1043
1044         if (playfile || beep) { 
1045                 if (!beep)
1046                         d = play_and_wait(chan, playfile);
1047                 if (d > -1)
1048                         d = ast_streamfile(chan, "beep",chan->language);
1049                 if (!d)
1050                         d = ast_waitstream(chan,"");
1051                 if (d < 0)
1052                         return -1;
1053         }
1054         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
1055         strcat(prependfile, "-prepend");
1056                         
1057         fmts = ast_strdupa(fmt);
1058         
1059         stringp=fmts;
1060         strsep(&stringp, "|");
1061         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
1062         sfmt[0] = ast_strdupa(fmts);
1063         
1064         while((fmt = strsep(&stringp, "|"))) {
1065                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1066                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1067                         break;
1068                 }
1069                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1070         }
1071
1072         time(&start);
1073         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1074         for (x=0;x<fmtcnt;x++) {
1075                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1076                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1077                 if (!others[x]) {
1078                         break;
1079                 }
1080         }
1081         
1082         sildet = ast_dsp_new(); /* Create the silence detector */
1083         if (!sildet) {
1084                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1085                 return -1;
1086         }
1087         ast_dsp_set_threshold(sildet, silencethreshold);
1088
1089         if (maxsilence > 0) {
1090                 rfmt = chan->readformat;
1091                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1092                 if (res < 0) {
1093                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1094                         return -1;
1095                 }
1096         }
1097                                                 
1098         if (x == fmtcnt) {
1099         /* Loop forever, writing the packets we read to the writer(s), until
1100            we read a # or get a hangup */
1101                 f = NULL;
1102                 for(;;) {
1103                         res = ast_waitfor(chan, 2000);
1104                         if (!res) {
1105                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1106                                 /* Try one more time in case of masq */
1107                                 res = ast_waitfor(chan, 2000);
1108                                 if (!res) {
1109                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1110                                         res = -1;
1111                                 }
1112                         }
1113                         
1114                         if (res < 0) {
1115                                 f = NULL;
1116                                 break;
1117                         }
1118                         f = ast_read(chan);
1119                         if (!f)
1120                                 break;
1121                         if (f->frametype == AST_FRAME_VOICE) {
1122                                 /* write each format */
1123                                 for (x=0;x<fmtcnt;x++) {
1124                                         if (!others[x])
1125                                                 break;
1126                                         res = ast_writestream(others[x], f);
1127                                 }
1128                                 
1129                                 /* Silence Detection */
1130                                 if (maxsilence > 0) {
1131                                         dspsilence = 0;
1132                                         ast_dsp_silence(sildet, f, &dspsilence);
1133                                         if (dspsilence)
1134                                                 totalsilence = dspsilence;
1135                                         else
1136                                                 totalsilence = 0;
1137                                         
1138                                         if (totalsilence > maxsilence) {
1139                                         /* Ended happily with silence */
1140                                         ast_frfree(f);
1141                                         gotsilence = 1;
1142                                         outmsg=2;
1143                                         break;
1144                                         }
1145                                 }
1146                                 /* Exit on any error */
1147                                 if (res) {
1148                                         ast_log(LOG_WARNING, "Error writing frame\n");
1149                                         ast_frfree(f);
1150                                         break;
1151                                 }
1152                         } else if (f->frametype == AST_FRAME_VIDEO) {
1153                                 /* Write only once */
1154                                 ast_writestream(others[0], f);
1155                         } else if (f->frametype == AST_FRAME_DTMF) {
1156                                 /* stop recording with any digit */
1157                                 if (option_verbose > 2) 
1158                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1159                                 res = 't';
1160                                 outmsg = 2;
1161                                 ast_frfree(f);
1162                                 break;
1163                         }
1164                         if (maxtime) {
1165                                 time(&end);
1166                                 if (maxtime < (end - start)) {
1167                                         if (option_verbose > 2)
1168                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1169                                         res = 't';
1170                                         ast_frfree(f);
1171                                         break;
1172                                 }
1173                         }
1174                         ast_frfree(f);
1175                 }
1176                 if (end == start) time(&end);
1177                 if (!f) {
1178                         if (option_verbose > 2) 
1179                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1180                         res = -1;
1181                         outmsg=1;
1182 #if 0
1183                         /* delete all the prepend files */
1184                         for (x=0;x<fmtcnt;x++) {
1185                                 if (!others[x])
1186                                         break;
1187                                 ast_closestream(others[x]);
1188                                 ast_filedelete(prependfile, sfmt[x]);
1189                         }
1190 #endif
1191                 }
1192         } else {
1193                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
1194         }
1195         *duration = end - start;
1196 #if 0
1197         if (outmsg > 1) {
1198 #else
1199         if (outmsg) {
1200 #endif
1201                 struct ast_frame *fr;
1202                 for (x=0;x<fmtcnt;x++) {
1203                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1204                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1205                         if (!others[x] || !realfiles[x])
1206                                 break;
1207                         if (totalsilence)
1208                                 ast_stream_rewind(others[x], totalsilence-200);
1209                         else
1210                                 ast_stream_rewind(others[x], 200);
1211                         ast_truncstream(others[x]);
1212                         /* add the original file too */
1213                         while ((fr = ast_readframe(realfiles[x]))) {
1214                                 ast_writestream(others[x],fr);
1215                         }
1216                         ast_closestream(others[x]);
1217                         ast_closestream(realfiles[x]);
1218                         ast_filerename(prependfile, recordfile, sfmt[x]);
1219 #if 0
1220                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1221 #endif
1222                         ast_filedelete(prependfile, sfmt[x]);
1223                 }
1224         }
1225         if (rfmt) {
1226                 if (ast_set_read_format(chan, rfmt)) {
1227                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1228                 }
1229         }
1230         if (outmsg) {
1231                 if (outmsg > 1) {
1232                         /* Let them know it worked */
1233                         ast_streamfile(chan, "auth-thankyou", chan->language);
1234                         ast_waitstream(chan, "");
1235                 }
1236         }       
1237         return res;
1238 }
1239
1240 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1241 {
1242         char d, *fmts;
1243         char comment[256];
1244         int x, fmtcnt=1, res=-1,outmsg=0;
1245         struct ast_frame *f;
1246         struct ast_filestream *others[MAX_OTHER_FORMATS];
1247         char *sfmt[MAX_OTHER_FORMATS];
1248         char *stringp=NULL;
1249         time_t start, end;
1250         struct ast_dsp *sildet;         /* silence detector dsp */
1251         int totalsilence = 0;
1252         int dspsilence = 0;
1253         int gotsilence = 0;             /* did we timeout for silence? */
1254         int rfmt=0;
1255
1256         /* barf if no pointer passed to store duration in */
1257         if (duration == NULL) {
1258                 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1259                 return -1;
1260         }
1261
1262         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1263         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1264
1265         if (playfile) {
1266                 d = play_and_wait(chan, playfile);
1267                 if (d > -1)
1268                         d = ast_streamfile(chan, "beep",chan->language);
1269                 if (!d)
1270                         d = ast_waitstream(chan,"");
1271                 if (d < 0)
1272                         return -1;
1273         }
1274
1275         fmts = ast_strdupa(fmt);
1276
1277         stringp=fmts;
1278         strsep(&stringp, "|");
1279         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1280         sfmt[0] = ast_strdupa(fmts);
1281
1282         while((fmt = strsep(&stringp, "|"))) {
1283                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1284                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1285                         break;
1286                 }
1287                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1288         }
1289
1290         time(&start);
1291         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1292         for (x=0;x<fmtcnt;x++) {
1293                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1294                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1295
1296                 if (!others[x]) {
1297                         break;
1298                 }
1299         }
1300
1301         sildet = ast_dsp_new(); /* Create the silence detector */
1302         if (!sildet) {
1303                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1304                 return -1;
1305         }
1306         ast_dsp_set_threshold(sildet, silencethreshold);
1307         
1308         if (maxsilence > 0) {
1309                 rfmt = chan->readformat;
1310                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1311                 if (res < 0) {
1312                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1313                         return -1;
1314                 }
1315         }
1316
1317         if (x == fmtcnt) {
1318         /* Loop forever, writing the packets we read to the writer(s), until
1319            we read a # or get a hangup */
1320                 f = NULL;
1321                 for(;;) {
1322                         res = ast_waitfor(chan, 2000);
1323                         if (!res) {
1324                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1325                                 /* Try one more time in case of masq */
1326                                 res = ast_waitfor(chan, 2000);
1327                                 if (!res) {
1328                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1329                                         res = -1;
1330                                 }
1331                         }
1332
1333                         if (res < 0) {
1334                                 f = NULL;
1335                                 break;
1336                         }
1337                         f = ast_read(chan);
1338                         if (!f)
1339                                 break;
1340                         if (f->frametype == AST_FRAME_VOICE) {
1341                                 /* write each format */
1342                                 for (x=0;x<fmtcnt;x++) {
1343                                         res = ast_writestream(others[x], f);
1344                                 }
1345
1346                                 /* Silence Detection */
1347                                 if (maxsilence > 0) {
1348                                         dspsilence = 0;
1349                                         ast_dsp_silence(sildet, f, &dspsilence);
1350                                         if (dspsilence)
1351                                                 totalsilence = dspsilence;
1352                                         else
1353                                                 totalsilence = 0;
1354
1355                                         if (totalsilence > maxsilence) {
1356                                         /* Ended happily with silence */
1357                                         ast_frfree(f);
1358                                         gotsilence = 1;
1359                                         outmsg=2;
1360                                         break;
1361                                         }
1362                                 }
1363                                 /* Exit on any error */
1364                                 if (res) {
1365                                         ast_log(LOG_WARNING, "Error writing frame\n");
1366                                         ast_frfree(f);
1367                                         break;
1368                                 }
1369                         } else if (f->frametype == AST_FRAME_VIDEO) {
1370                                 /* Write only once */
1371                                 ast_writestream(others[0], f);
1372                         } else if (f->frametype == AST_FRAME_DTMF) {
1373                                 if (f->subclass == '#') {
1374                                         if (option_verbose > 2)
1375                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1376                                         res = '#';
1377                                         outmsg = 2;
1378                                         ast_frfree(f);
1379                                         break;
1380                                 }
1381                         }
1382                                 if (f->subclass == '0') {
1383                                 /* Check for a '0' during message recording also, in case caller wants operator */
1384                                         if (option_verbose > 2)
1385                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1386                                         res = '0';
1387                                         outmsg = 0;
1388                                         ast_frfree(f);
1389                                         break;
1390                                 }
1391                         if (maxtime) {
1392                                 time(&end);
1393                                 if (maxtime < (end - start)) {
1394                                         if (option_verbose > 2)
1395                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1396                                         res = 't';
1397                                         ast_frfree(f);
1398                                         break;
1399                                 }
1400                         }
1401                         ast_frfree(f);
1402                 }
1403                 if (end == start) time(&end);
1404                 if (!f) {
1405                         if (option_verbose > 2)
1406                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1407                         res = -1;
1408                         outmsg=1;
1409                 }
1410         } else {
1411                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1412         }
1413
1414         *duration = end - start;
1415
1416         for (x=0;x<fmtcnt;x++) {
1417                 if (!others[x])
1418                         break;
1419                 if (totalsilence)
1420                         ast_stream_rewind(others[x], totalsilence-200);
1421                 else
1422                         ast_stream_rewind(others[x], 200);
1423                 ast_truncstream(others[x]);
1424                 ast_closestream(others[x]);
1425         }
1426         if (rfmt) {
1427                 if (ast_set_read_format(chan, rfmt)) {
1428                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1429                 }
1430         }
1431         if (outmsg) {
1432                 if (outmsg > 1) {
1433                 /* Let them know recording is stopped */
1434                         ast_streamfile(chan, "auth-thankyou", chan->language);
1435                         ast_waitstream(chan, "");
1436                 }
1437         }
1438
1439         return res;
1440 }
1441
1442 static void free_user(struct ast_vm_user *vmu)
1443 {
1444         if (vmu->alloced)
1445                 free(vmu);
1446 }
1447
1448 static void free_zone(struct vm_zone *z)
1449 {
1450         free(z);
1451 }
1452
1453 static void run_externnotify(char *context, char *extension, int numvoicemails)
1454 {
1455         char arguments[255];
1456
1457         if(externnotify[0]) {
1458                 strncpy(arguments, externnotify, sizeof(arguments));
1459                 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1460                 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1461                 ast_safe_system(arguments);
1462         }
1463 }
1464
1465
1466 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1467 {
1468         char comment[256];
1469         char txtfile[256];
1470         FILE *txt;
1471         int res = 0;
1472         int msgnum;
1473         int fd;
1474         int duration = 0;
1475         char date[256];
1476         char dir[256];
1477         char fn[256];
1478         char prefile[256]="";
1479         char fmt[80];
1480         char *context;
1481         char *ecodes = "#";
1482         char *stringp;
1483         char tmp[256] = "";
1484         struct ast_vm_user *vmu;
1485         struct ast_vm_user svm;
1486
1487
1488         strncpy(tmp, ext, sizeof(tmp) - 1);
1489         ext = tmp;
1490         context = strchr(tmp, '@');
1491         if (context) {
1492                 *context = '\0';
1493                 context++;
1494         }
1495
1496         if ((vmu = find_user(&svm, context, ext))) {
1497                 /* Setup pre-file if appropriate */
1498                 if (busy)
1499                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1500                 else if (unavail)
1501                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1502                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1503                 /* It's easier just to try to make it than to check for its existence */
1504                 if (mkdir(dir, 0700) && (errno != EEXIST))
1505                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1506                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1507                 /* It's easier just to try to make it than to check for its existence */
1508                 if (mkdir(dir, 0700) && (errno != EEXIST))
1509                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1510                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1511                 if (mkdir(dir, 0700) && (errno != EEXIST))
1512                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1513                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1514                         ecodes = "#0*";
1515                 /* Play the beginning intro if desired */
1516                 if (strlen(prefile)) {
1517                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1518                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1519                                     res = ast_waitstream(chan, "#0*");
1520                         } else {
1521                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1522                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1523                         }
1524                         if (res < 0) {
1525                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1526                                 free_user(vmu);
1527                                 return -1;
1528                         }
1529                 }
1530                 if (res == '#') {
1531                         /* On a '#' we skip the instructions */
1532                         silent = 1;
1533                         res = 0;
1534                 }
1535                 if (!res && !silent) {
1536                         res = ast_streamfile(chan, INTRO, chan->language);
1537                         if (!res)
1538                                 res = ast_waitstream(chan, ecodes);
1539                         if (res == '#') {
1540                                 silent = 1;
1541                                 res = 0;
1542                         }
1543                 }
1544                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1545                 other than the operator -- an automated attendant or mailbox login for example */
1546                 if (res == '*') {
1547                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1548                         if (strlen(chan->macrocontext))
1549                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1550                         chan->priority = 0;
1551                         free_user(vmu);
1552                         return 0;
1553                 }
1554                 /* Check for a '0' here */
1555                 if (res == '0') {
1556                 transfer:
1557                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1558                         if (strlen(chan->macrocontext))
1559                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1560                         chan->priority = 0;
1561                         free_user(vmu);
1562                         return 0;
1563                 }
1564                 if (res >= 0) {
1565                         /* Unless we're *really* silent, try to send the beep */
1566                         res = ast_streamfile(chan, "beep", chan->language);
1567                         if (!res)
1568                                 res = ast_waitstream(chan, "");
1569                 }
1570                 if (res < 0) {
1571                         free_user(vmu);
1572                         return -1;
1573                 }
1574                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1575                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1576                 if (strlen(fmt)) {
1577                         msgnum = 0;
1578                         do {
1579                                 make_file(fn, sizeof(fn), dir, msgnum);
1580                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1581                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1582                                                                         vmu->fullname, ext, chan->name);
1583                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1584                                         break;
1585                                 msgnum++;
1586                         } while(msgnum < MAXMSG);
1587                         if (msgnum < MAXMSG) {
1588                                 /* Store information */
1589                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1590                                 txt = fopen(txtfile, "w+");
1591                                 if (txt) {
1592                                         get_date(date, sizeof(date));
1593                                         fprintf(txt, 
1594 ";\n"
1595 "; Message Information file\n"
1596 ";\n"
1597 "[message]\n"
1598 "origmailbox=%s\n"
1599 "context=%s\n"
1600 "macrocontext=%s\n"
1601 "exten=%s\n"
1602 "priority=%d\n"
1603 "callerchan=%s\n"
1604 "callerid=%s\n"
1605 "origdate=%s\n"
1606 "origtime=%ld\n",
1607         ext,
1608         chan->context,
1609         chan->macrocontext, 
1610         chan->exten,
1611         chan->priority,
1612         chan->name,
1613         chan->callerid ? chan->callerid : "Unknown",
1614         date, (long)time(NULL));
1615                                         fclose(txt);
1616                                 } else
1617                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1618                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1619                                 if (res == '0')
1620                                         goto transfer;
1621                                 if (res > 0)
1622                                         res = 0;
1623                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1624                                 if (fd > -1) {
1625                                         txt = fdopen(fd, "a");
1626                                         if (txt) {
1627                                                 fprintf(txt, "duration=%d\n", duration);
1628                                                 fclose(txt);
1629                                         } else
1630                                                 close(fd);
1631                                 }
1632                                 if (duration < vmminmessage) {
1633                                         vm_delete(fn);
1634                                         goto leave_vm_out;
1635                                 }
1636                                 stringp = fmt;
1637                                 strsep(&stringp, "|");
1638                                 /* Send e-mail if applicable */
1639                                 if (strlen(vmu->email)) {
1640                                         int attach_user_voicemail = attach_voicemail;
1641                                         char *myserveremail = serveremail;
1642                                         if (vmu->attach > -1)
1643                                                 attach_user_voicemail = vmu->attach;
1644                                         if (strlen(vmu->serveremail))
1645                                                 myserveremail = vmu->serveremail;
1646                                                 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1647                                 }
1648                                 if (strlen(vmu->pager)) {
1649                                         char *myserveremail = serveremail;
1650                                         if (strlen(vmu->serveremail))
1651                                                 myserveremail = vmu->serveremail;
1652                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1653                                 }
1654                         } else {
1655                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1656                                 if (!res)
1657                                         res = ast_waitstream(chan, "");
1658                                 ast_log(LOG_WARNING, "No more messages possible\n");
1659                         }
1660                 } else
1661                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1662 leave_vm_out:
1663                 free_user(vmu);
1664         } else {
1665                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1666                         /*Send the call to n+101 priority, where n is the current priority*/
1667                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1668                                 chan->priority+=100;
1669         }
1670         /* Leave voicemail for someone */
1671         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1672
1673         /* If an external program is specified to be run after leaving a voicemail */
1674         run_externnotify(chan->context, ext, ast_app_has_voicemail(ext));
1675
1676         return res;
1677 }
1678
1679 static char *mbox(int id)
1680 {
1681         switch(id) {
1682         case 0:
1683                 return "INBOX";
1684         case 1:
1685                 return "Old";
1686         case 2:
1687                 return "Work";
1688         case 3:
1689                 return "Family";
1690         case 4:
1691                 return "Friends";
1692         case 5:
1693                 return "Cust1";
1694         case 6:
1695                 return "Cust2";
1696         case 7:
1697                 return "Cust3";
1698         case 8:
1699                 return "Cust4";
1700         case 9:
1701                 return "Cust5";
1702         default:
1703                 return "Unknown";
1704         }
1705 }
1706
1707 static int count_messages(char *dir)
1708 {
1709         int x;
1710         char fn[256];
1711         for (x=0;x<MAXMSG;x++) {
1712                 make_file(fn, sizeof(fn), dir, x);
1713                 if (ast_fileexists(fn, NULL, NULL) < 1)
1714                         break;
1715         }
1716         return x;
1717 }
1718
1719 static int say_and_wait(struct ast_channel *chan, int num)
1720 {
1721         int d;
1722         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1723         return d;
1724 }
1725
1726 static int copy(char *infile, char *outfile)
1727 {
1728         int ifd;
1729         int ofd;
1730         int res;
1731         int len;
1732         char buf[4096];
1733         if ((ifd = open(infile, O_RDONLY)) < 0) {
1734                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1735                 return -1;
1736         }
1737         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1738                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1739                 close(ifd);
1740                 return -1;
1741         }
1742         do {
1743                 len = read(ifd, buf, sizeof(buf));
1744                 if (len < 0) {
1745                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1746                         close(ifd);
1747                         close(ofd);
1748                         unlink(outfile);
1749                 }
1750                 if (len) {
1751                         res = write(ofd, buf, len);
1752                         if (res != len) {
1753                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1754                                 close(ifd);
1755                                 close(ofd);
1756                                 unlink(outfile);
1757                         }
1758                 }
1759         } while(len);
1760         close(ifd);
1761         close(ofd);
1762         return 0;
1763 }
1764
1765 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1766 {
1767         char sfn[256];
1768         char dfn[256];
1769         char ddir[256];
1770         char txt[256];
1771         char ntxt[256];
1772         char *dbox = mbox(box);
1773         int x;
1774         make_file(sfn, sizeof(sfn), dir, msg);
1775         make_dir(ddir, sizeof(ddir), context, username, dbox);
1776         mkdir(ddir, 0700);
1777         for (x=0;x<MAXMSG;x++) {
1778                 make_file(dfn, sizeof(dfn), ddir, x);
1779                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1780                         break;
1781         }
1782         if (x >= MAXMSG)
1783                 return -1;
1784         ast_filecopy(sfn, dfn, NULL);
1785         if (strcmp(sfn, dfn)) {
1786                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1787                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1788                 copy(txt, ntxt);
1789         }
1790         return 0;
1791 }
1792
1793 static int adsi_logo(unsigned char *buf)
1794 {
1795         int bytes = 0;
1796         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1797         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1798         return bytes;
1799 }
1800
1801 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1802 {
1803         char buf[256];
1804         int bytes=0;
1805         int x;
1806         char num[5];
1807
1808         *useadsi = 0;
1809         bytes += adsi_data_mode(buf + bytes);
1810         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1811
1812         bytes = 0;
1813         bytes += adsi_logo(buf);
1814         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1815 #ifdef DISPLAY
1816         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1817 #endif
1818         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1819         bytes += adsi_data_mode(buf + bytes);
1820         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1821
1822         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1823                 bytes = 0;
1824                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1825                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1826                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1827                 bytes += adsi_voice_mode(buf + bytes, 0);
1828                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1829                 return 0;
1830         }
1831
1832 #ifdef DISPLAY
1833         /* Add a dot */
1834         bytes = 0;
1835         bytes += adsi_logo(buf);
1836         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1837         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1838         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1839         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1840 #endif
1841         bytes = 0;
1842         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1843         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1844         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1845         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1846         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1847         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1848         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1849
1850 #ifdef DISPLAY
1851         /* Add another dot */
1852         bytes = 0;
1853         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1854       bytes += adsi_voice_mode(buf + bytes, 0);
1855
1856         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1857         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1858 #endif
1859
1860         bytes = 0;
1861         /* These buttons we load but don't use yet */
1862         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1863         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1864         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1865         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1866         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1867         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1868         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1869
1870 #ifdef DISPLAY
1871         /* Add another dot */
1872         bytes = 0;
1873         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1874         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1875         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1876 #endif
1877
1878         bytes = 0;
1879         for (x=0;x<5;x++) {
1880                 snprintf(num, sizeof(num), "%d", x);
1881                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1882         }
1883         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1884         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1885
1886 #ifdef DISPLAY
1887         /* Add another dot */
1888         bytes = 0;
1889         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1890         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1891         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1892 #endif
1893
1894         if (adsi_end_download(chan)) {
1895                 bytes = 0;
1896                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1897                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1898                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1899                 bytes += adsi_voice_mode(buf + bytes, 0);
1900                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1901                 return 0;
1902         }
1903         bytes = 0;
1904         bytes += adsi_download_disconnect(buf + bytes);
1905         bytes += adsi_voice_mode(buf + bytes, 0);
1906         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1907
1908         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1909
1910 #ifdef DISPLAY
1911         /* Add last dot */
1912         bytes = 0;
1913         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1914         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1915 #endif
1916         ast_log(LOG_DEBUG, "Restarting session...\n");
1917
1918         bytes = 0;
1919         /* Load the session now */
1920         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1921                 *useadsi = 1;
1922                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1923         } else
1924                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1925
1926         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1927         return 0;
1928 }
1929
1930 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1931 {
1932         int x;
1933         if (!adsi_available(chan))
1934           return;
1935         x = adsi_load_session(chan, adapp, adver, 1);
1936         if (x < 0)
1937                 return;
1938         if (!x) {
1939                 if (adsi_load_vmail(chan, useadsi)) {
1940                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1941                         return;
1942                 }
1943         } else
1944                 *useadsi = 1;
1945 }
1946
1947 static void adsi_login(struct ast_channel *chan)
1948 {
1949         char buf[256];
1950         int bytes=0;
1951         unsigned char keys[8];
1952         int x;
1953         if (!adsi_available(chan))
1954                 return;
1955
1956         for (x=0;x<8;x++)
1957                 keys[x] = 0;
1958         /* Set one key for next */
1959         keys[3] = ADSI_KEY_APPS + 3;
1960
1961         bytes += adsi_logo(buf + bytes);
1962         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1963         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1964         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1965         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1966         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1967         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1968         bytes += adsi_set_keys(buf + bytes, keys);
1969         bytes += adsi_voice_mode(buf + bytes, 0);
1970         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1971 }
1972
1973 static void adsi_password(struct ast_channel *chan)
1974 {
1975         char buf[256];
1976         int bytes=0;
1977         unsigned char keys[8];
1978         int x;
1979         if (!adsi_available(chan))
1980                 return;
1981
1982         for (x=0;x<8;x++)
1983                 keys[x] = 0;
1984         /* Set one key for next */
1985         keys[3] = ADSI_KEY_APPS + 3;
1986
1987         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1988         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1989         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1990         bytes += adsi_set_keys(buf + bytes, keys);
1991         bytes += adsi_voice_mode(buf + bytes, 0);
1992         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1993 }
1994
1995 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1996 {
1997         char buf[256];
1998         int bytes=0;
1999         unsigned char keys[8];
2000         int x,y;
2001
2002         if (!adsi_available(chan))
2003                 return;
2004
2005         for (x=0;x<5;x++) {
2006                 y = ADSI_KEY_APPS + 12 + start + x;
2007                 if (y > ADSI_KEY_APPS + 12 + 4)
2008                         y = 0;
2009                 keys[x] = ADSI_KEY_SKT | y;
2010         }
2011         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2012         keys[6] = 0;
2013         keys[7] = 0;
2014
2015         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2016         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2017         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2018         bytes += adsi_set_keys(buf + bytes, keys);
2019         bytes += adsi_voice_mode(buf + bytes, 0);
2020
2021         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2022 }
2023
2024 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2025 {
2026         int bytes=0;
2027         char buf[256], buf1[256], buf2[256];
2028         char fn2[256];
2029
2030         char cid[256]="";
2031         char *val;
2032         char *name, *num;
2033         char datetime[21]="";
2034         FILE *f;
2035
2036         unsigned char keys[8];
2037
2038         int x;
2039
2040         if (!adsi_available(chan))
2041                 return;
2042
2043         /* Retrieve important info */
2044         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2045         f = fopen(fn2, "r");
2046         if (f) {
2047                 while(!feof(f)) {       
2048                         fgets(buf, sizeof(buf), f);
2049                         if (!feof(f)) {
2050                                 char *stringp=NULL;
2051                                 stringp=buf;
2052                                 strsep(&stringp, "=");
2053                                 val = strsep(&stringp, "=");
2054                                 if (val && strlen(val)) {
2055                                         if (!strcmp(buf, "callerid"))
2056                                                 strncpy(cid, val, sizeof(cid) - 1);
2057                                         if (!strcmp(buf, "origdate"))
2058                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2059                                 }
2060                         }
2061                 }
2062                 fclose(f);
2063         }
2064         /* New meaning for keys */
2065         for (x=0;x<5;x++)
2066                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2067         keys[6] = 0x0;
2068         keys[7] = 0x0;
2069
2070         if (!msg) {
2071                 /* No prev key, provide "Folder" instead */
2072                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2073         }
2074         if (msg >= last) {
2075                 /* If last message ... */
2076                 if (msg) {
2077                         /* but not only message, provide "Folder" instead */
2078                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2079       bytes += adsi_voice_mode(buf + bytes, 0);
2080
2081                 } else {
2082                         /* Otherwise if only message, leave blank */
2083                         keys[3] = 1;
2084                 }
2085         }
2086
2087         if (strlen(cid)) {
2088                 ast_callerid_parse(cid, &name, &num);
2089                 if (!name)
2090                         name = num;
2091         } else
2092                 name = "Unknown Caller";
2093
2094         /* If deleted, show "undeleted" */
2095
2096         if (deleted)
2097                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2098
2099         /* Except "Exit" */
2100         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2101         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2102                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2103         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2104
2105         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2106         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2107         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2108         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2109         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2110         bytes += adsi_set_keys(buf + bytes, keys);
2111         bytes += adsi_voice_mode(buf + bytes, 0);
2112
2113         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2114 }
2115
2116 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2117 {
2118         int bytes=0;
2119         char buf[256];
2120         unsigned char keys[8];
2121
2122         int x;
2123
2124         if (!adsi_available(chan))
2125                 return;
2126
2127         /* New meaning for keys */
2128         for (x=0;x<5;x++)
2129                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2130
2131         keys[6] = 0x0;
2132         keys[7] = 0x0;
2133
2134         if (!msg) {
2135                 /* No prev key, provide "Folder" instead */
2136                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2137         }
2138         if (msg >= last) {
2139                 /* If last message ... */
2140                 if (msg) {
2141                         /* but not only message, provide "Folder" instead */
2142                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2143                 } else {
2144                         /* Otherwise if only message, leave blank */
2145                         keys[3] = 1;
2146                 }
2147         }
2148
2149         /* If deleted, show "undeleted" */
2150         if (deleted) 
2151                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2152
2153         /* Except "Exit" */
2154         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2155         bytes += adsi_set_keys(buf + bytes, keys);
2156         bytes += adsi_voice_mode(buf + bytes, 0);
2157
2158         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2159 }
2160
2161 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2162 {
2163         char buf[256], buf1[256], buf2[256];
2164         int bytes=0;
2165         unsigned char keys[8];
2166         int x;
2167
2168         char *newm = (new == 1) ? "message" : "messages";
2169         char *oldm = (old == 1) ? "message" : "messages";
2170         if (!adsi_available(chan))
2171                 return;
2172         if (new) {
2173                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2174                 if (old) {
2175                         strcat(buf1, " and");
2176                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2177                 } else {
2178                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2179                 }
2180         } else if (old) {
2181                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2182                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2183         } else {
2184                 strcpy(buf1, "You have no messages.");
2185                 strcpy(buf2, " ");
2186         }
2187         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2188         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2189         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2190
2191         for (x=0;x<6;x++)
2192                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2193         keys[6] = 0;
2194         keys[7] = 0;
2195
2196         /* Don't let them listen if there are none */
2197         if (lastmsg < 0)
2198                 keys[0] = 1;
2199         bytes += adsi_set_keys(buf + bytes, keys);
2200
2201         bytes += adsi_voice_mode(buf + bytes, 0);
2202
2203         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2204 }
2205
2206 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2207 {
2208         char buf[256], buf1[256], buf2[256];
2209         int bytes=0;
2210         unsigned char keys[8];
2211         int x;
2212
2213         char *mess = (messages == 1) ? "message" : "messages";
2214
2215         if (!adsi_available(chan))
2216                 return;
2217
2218         /* Original command keys */
2219         for (x=0;x<6;x++)
2220                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2221
2222         keys[6] = 0;
2223         keys[7] = 0;
2224
2225         if (messages < 1)
2226                 keys[0] = 0;
2227
2228         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2229                         strcasecmp(folder, "INBOX") ? " folder" : "");
2230
2231         if (messages)
2232                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2233         else
2234                 strcpy(buf2, "no messages.");
2235         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2236         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2237         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2238         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2239         bytes += adsi_set_keys(buf + bytes, keys);
2240
2241         bytes += adsi_voice_mode(buf + bytes, 0);
2242
2243         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2244         
2245 }
2246
2247 /*
2248 static void adsi_clear(struct ast_channel *chan)
2249 {
2250         char buf[256];
2251         int bytes=0;
2252         if (!adsi_available(chan))
2253                 return;
2254         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2255         bytes += adsi_voice_mode(buf + bytes, 0);
2256
2257         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2258 }
2259 */
2260
2261 static void adsi_goodbye(struct ast_channel *chan)
2262 {
2263         char buf[256];
2264         int bytes=0;
2265
2266         if (!adsi_available(chan))
2267                 return;
2268         bytes += adsi_logo(buf + bytes);
2269         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2270         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2271         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2272         bytes += adsi_voice_mode(buf + bytes, 0);
2273
2274         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2275 }
2276
2277 static int get_folder(struct ast_channel *chan, int start)
2278 {
2279         int x;
2280         int d;
2281         char fn[256];
2282         d = play_and_wait(chan, "vm-press");
2283         if (d)
2284                 return d;
2285         for (x = start; x< 5; x++) {
2286                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2287                         return d;
2288                 d = play_and_wait(chan, "vm-for");
2289                 if (d)
2290                         return d;
2291                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2292                 d = play_and_wait(chan, fn);
2293                 if (d)
2294                         return d;
2295                 d = play_and_wait(chan, "vm-messages");
2296                 if (d)
2297                         return d;
2298                 d = ast_waitfordigit(chan, 500);
2299                 if (d)
2300                         return d;
2301         }
2302         d = play_and_wait(chan, "vm-tocancel");
2303         if (d)
2304                 return d;
2305         d = ast_waitfordigit(chan, 4000);
2306         return d;
2307 }
2308
2309 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2310 {
2311         int res = 0;
2312         res = play_and_wait(chan, fn);
2313         while (((res < '0') || (res > '9')) &&
2314                         (res != '#') && (res >= 0)) {
2315                 res = get_folder(chan, 0);
2316         }
2317         return res;
2318 }
2319
2320 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2321 {
2322         int cmd = 0;
2323         int retries = 0;
2324         int duration = 0;
2325
2326         while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2327                 if (cmd)
2328                         retries = 0;
2329                 switch (cmd) {
2330                 case '1': 
2331                         /* prepend a message to the current message and return */
2332                 {
2333                         char file[200];
2334                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2335                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2336                         break;
2337                 }
2338                 case '2': 
2339                         cmd = 't';
2340                         break;
2341                 case '*':
2342                         cmd = '*';
2343                         break;
2344                 default: 
2345                         cmd = play_and_wait(chan,"vm-forwardoptions");
2346                         if (!cmd)
2347                                 cmd = play_and_wait(chan,"vm-starmain");
2348                         if (!cmd)
2349                                 cmd = ast_waitfordigit(chan,6000);
2350                         if (!cmd)
2351                                 retries++;
2352                         if (retries > 3)
2353                                 cmd = 't';
2354                  }
2355         }
2356         if (cmd == 't')
2357                 cmd = 0;
2358         return cmd;
2359 }
2360
2361 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2362 {
2363         char username[70];
2364         char sys[256];
2365         char todir[256];
2366         int todircount=0;
2367         int duration;
2368         struct ast_config *mif;
2369         char miffile[256];
2370         char fn[256];
2371         char callerid[512];
2372         int res = 0, cmd = 0;
2373         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2374         char tmp[256];
2375         char *stringp, *s;
2376         int saved_messages = 0, found = 0;
2377         int valid_extensions = 0;
2378         while (!res && !valid_extensions) {
2379                 res = ast_streamfile(chan, "vm-extension", chan->language);
2380                 if (res)
2381                         break;
2382                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2383                         break;
2384                 /* start all over if no username */
2385                 if (!strlen(username))
2386                         continue;
2387                 stringp = username;
2388                 s = strsep(&stringp, "*");
2389                 /* start optimistic */
2390                 valid_extensions = 1;
2391                 while (s) {
2392                         /* find_user is going to malloc since we have a NULL as first argument */
2393                         if ((receiver = find_user(NULL, context, s))) {
2394                                 if (!extensions)
2395                                         vmtmp = extensions = receiver;
2396                                 else {
2397                                         vmtmp->next = receiver;
2398                                         vmtmp = receiver;
2399                                 }
2400                                 found++;
2401                         } else {
2402                                 valid_extensions = 0;
2403                                 break;
2404                         }
2405                         s = strsep(&stringp, "*");
2406                 }
2407                 /* break from the loop of reading the extensions */
2408                 if (valid_extensions)
2409                         break;
2410                 /* invalid extension, try again */
2411                 res = play_and_wait(chan, "pbx-invalid");
2412         }
2413         /* check if we're clear to proceed */
2414         if (!extensions || !valid_extensions)
2415                 return res;
2416         vmtmp = extensions;
2417         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2418         if (!cmd) {
2419                 while(!res && vmtmp) {
2420                         /* if (play_and_wait(chan, "vm-savedto"))
2421                                 break;
2422                         */
2423                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2424                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2425                         ast_log(LOG_DEBUG, sys);
2426                         ast_safe_system(sys);
2427         
2428                         todircount = count_messages(todir);
2429                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2430                         stringp = tmp;
2431                         while((s = strsep(&stringp, "|"))) {
2432                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2433                                 if (!strcasecmp(s, "wav49"))
2434                                         s = "WAV";
2435                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2436                                 ast_log(LOG_DEBUG, sys);
2437                                 ast_safe_system(sys);
2438                         }
2439                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2440                         ast_log(LOG_DEBUG, sys);
2441                         ast_safe_system(sys);
2442                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2443         
2444                         /* load the information on the source message so we can send an e-mail like a new message */
2445                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2446                         if ((mif=ast_load(miffile))) {
2447         
2448                                 /* set callerid and duration variables */
2449                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2450                                 s = ast_variable_retrieve(mif, NULL, "duration");
2451                                 if (s)
2452                                         duration = atoi(s);
2453                                 else
2454                                         duration = 0;
2455                                 if (strlen(vmtmp->email)) {
2456                                         int attach_user_voicemail = attach_voicemail;
2457                                         char *myserveremail = serveremail;
2458                                         if (vmtmp->attach > -1)
2459                                                 attach_user_voicemail = vmtmp->attach;
2460                                         if (strlen(vmtmp->serveremail))
2461                                                 myserveremail = vmtmp->serveremail;
2462                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2463                                 }
2464                              
2465                                 if (strlen(vmtmp->pager)) {
2466                                         char *myserveremail = serveremail;
2467                                         if (strlen(vmtmp->serveremail))
2468                                                 myserveremail = vmtmp->serveremail;
2469                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2470                                 }
2471                                   
2472                                 ast_destroy(mif); /* or here */
2473                         }
2474                         /* Leave voicemail for someone */
2475                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2476                         run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2477         
2478                         saved_messages++;
2479                         vmfree = vmtmp;
2480                         vmtmp = vmtmp->next;
2481                         free_user(vmfree);
2482                 }
2483                 if (saved_messages > 0) {
2484                         /* give confirmatopm that the message was saved */
2485                         /* commented out since we can't forward batches yet
2486                         if (saved_messages == 1)
2487                                 res = play_and_wait(chan, "vm-message");
2488                         else
2489                                 res = play_and_wait(chan, "vm-messages");
2490                         if (!res)
2491                                 res = play_and_wait(chan, "vm-saved"); */
2492                         if (!res)
2493                                 res = play_and_wait(chan, "vm-msgsaved");
2494                 }
2495         }
2496         return res ? res : cmd;
2497 }
2498
2499
2500 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2501 {
2502         int res;
2503         if ((res = ast_streamfile(chan, file, chan->language))) 
2504                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2505         if (!res)
2506                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2507         return res;
2508 }
2509
2510 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2511 {
2512         int res;
2513         if ((res = ast_streamfile(chan, file, chan->language)))
2514                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2515         if (!res)
2516                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2517         return res;
2518 }
2519
2520 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2521 {
2522         int res = 0;
2523         struct vm_zone *the_zone = NULL;
2524         time_t t;
2525         long tin;
2526
2527         if (sscanf(origtime,"%ld",&tin) < 1) {
2528                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2529                 return 0;
2530         }
2531         t = tin;
2532
2533         /* Does this user have a timezone specified? */
2534         if (strlen(vmu->zonetag)) {
2535                 /* Find the zone in the list */
2536                 struct vm_zone *z;
2537                 z = zones;
2538                 while (z) {
2539                         if (!strcmp(z->name, vmu->zonetag)) {
2540                                 the_zone = z;
2541                                 break;
2542                         }
2543                         z = z->next;
2544                 }
2545         }
2546
2547 /* No internal variable parsing for now, so we'll comment it out for the time being */
2548 #if 0
2549         /* Set the DIFF_* variables */
2550         localtime_r(&t, &time_now);
2551         gettimeofday(&tv_now,NULL);
2552         tnow = tv_now.tv_sec;
2553         localtime_r(&tnow,&time_then);
2554
2555         /* Day difference */
2556         if (time_now.tm_year == time_then.tm_year)
2557                 sprintf(temp,"%d",time_now.tm_yday);
2558         else
2559                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2560         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2561
2562         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2563 #endif
2564         if (the_zone)
2565                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2566         else
2567                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2568 #if 0
2569         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2570 #endif
2571         return res;
2572 }
2573
2574
2575
2576 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2577 {
2578         int res = 0;
2579         int i;
2580         char *callerid, *name;
2581         char prefile[256]="";
2582         
2583
2584         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2585         /* 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 */
2586         if((cid == NULL)||(context == NULL))
2587                 return res;
2588
2589         /* Strip off caller ID number from name */
2590         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2591         ast_callerid_parse(cid, &name, &callerid);
2592         if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2593                 /* Check for internal contexts and only */
2594                 /* say extension when the call didn't come from an internal context in the list */
2595                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2596                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2597                         if((strcmp(cidinternalcontexts[i], context) == 0))
2598                                 break;
2599                 }
2600                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2601                         if(!res) {
2602                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2603                                 if (strlen(prefile)) {
2604                                 /* See if we can find a recorded name for this person instead of their extension number */
2605                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2606                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2607                                                 if (!callback)
2608                                                         res = wait_file2(chan, vms, "vm-from");
2609                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2610                                                 res = ast_waitstream(chan, "");
2611                                         } else {
2612                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2613                                                 /* BB: Say "from extension" as one saying to sound smoother */
2614                                                 if (!callback)
2615                                                         res = wait_file2(chan, vms, "vm-from-extension");
2616                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2617                                         }
2618                                 }
2619                         }
2620                 }
2621
2622                 else if (!res){
2623                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2624                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2625                         if (!callback)
2626                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2627                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2628                 }
2629         }
2630         else{
2631                 /* Number unknown */
2632                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2633                 if(!res)
2634                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2635                         res = wait_file2(chan, vms, "vm-unknown-caller");
2636         }
2637         return res;                                                               
2638 }
2639
2640 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2641 {
2642         int res = 0;
2643         char filename[256],*origtime, *cid, *context;
2644         struct ast_config *msg_cfg;
2645
2646         vms->starting = 0; 
2647         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2648         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2649         if (!msg)
2650                 res = wait_file2(chan, vms, "vm-first");
2651         else if (msg == vms->lastmsg)
2652                 res = wait_file2(chan, vms, "vm-last");
2653         if (!res) {
2654                 res = wait_file2(chan, vms, "vm-message");
2655                 if (msg && (msg != vms->lastmsg)) {
2656                         if (!res)
2657                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2658                 }
2659         }
2660
2661         /* Retrieve info from VM attribute file */
2662
2663         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2664         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2665         msg_cfg = ast_load(filename);
2666         if (!msg_cfg) {
2667                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2668                 return 0;
2669         }
2670                                                                                                                                  
2671         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2672                 return 0;
2673                                                                                                                                  
2674         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2675
2676         context = ast_variable_retrieve(msg_cfg, "message", "context");
2677         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2678                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2679
2680         if (!res)
2681                 res = play_message_datetime(chan, vmu, origtime, filename);
2682
2683         if ((!res)&&(vmu->saycid))
2684                 res = play_message_callerid(chan, vms, cid, context, 0);
2685
2686         ast_destroy(msg_cfg);
2687
2688         if (!res) {
2689                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2690                 vms->heard[msg] = 1;
2691                 res = wait_file(chan, vms, vms->fn);
2692         }
2693         return res;
2694 }
2695
2696 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2697 {
2698         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2699         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2700         vms->lastmsg = count_messages(vms->curdir) - 1;
2701         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2702 }
2703
2704 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2705 {
2706         int x;
2707         char ntxt[256] = "";
2708         char txt[256] = "";
2709         if (vms->lastmsg > -1) { 
2710                 /* Get the deleted messages fixed */ 
2711                 vms->curmsg = -1; 
2712                 for (x=0;x < MAXMSG;x++) { 
2713                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2714                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2715                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2716                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2717                                         break;
2718                                 vms->curmsg++; 
2719                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2720                                 if (strcmp(vms->fn, vms->fn2)) { 
2721                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2722                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2723                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2724                                         rename(txt, ntxt); 
2725                                 } 
2726                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2727                                 /* Move to old folder before deleting */ 
2728                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2729                         } 
2730                 } 
2731                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2732                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2733                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2734                                 break;
2735                         vm_delete(vms->fn);
2736                 } 
2737         } 
2738         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2739         memset(vms->heard, 0, sizeof(vms->heard)); 
2740 }
2741
2742 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2743 {
2744         /* Introduce messages they have */
2745         int res;
2746         res = play_and_wait(chan, "vm-youhave");
2747         if (!res) {
2748                 if (vms->newmessages) {
2749                         res = say_and_wait(chan, vms->newmessages);
2750                         if (!res)
2751                                 res = play_and_wait(chan, "vm-INBOX");
2752                         if (vms->oldmessages && !res)
2753                                 res = play_and_wait(chan, "vm-and");
2754                         else if (!res) {
2755                                 if ((vms->newmessages == 1))
2756                                         res = play_and_wait(chan, "vm-message");
2757                                 else
2758                                         res = play_and_wait(chan, "vm-messages");
2759                         }
2760                                 
2761                 }
2762                 if (!res && vms->oldmessages) {
2763                         res = say_and_wait(chan, vms->oldmessages);
2764                         if (!res)
2765                                 res = play_and_wait(chan, "vm-Old");
2766                         if (!res) {
2767                                 if (vms->oldmessages == 1)
2768                                         res = play_and_wait(chan, "vm-message");
2769                                 else
2770                                         res = play_and_wait(chan, "vm-messages");
2771                         }
2772                 }
2773                 if (!res) {
2774                         if (!vms->oldmessages && !vms->newmessages) {
2775                                 res = play_and_wait(chan, "vm-no");
2776                                 if (!res)
2777                                         res = play_and_wait(chan, "vm-messages");
2778                         }
2779                 }
2780         }
2781         return res;
2782 }
2783
2784 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2785 {
2786         int res = 0;
2787         /* Play instructions and wait for new command */
2788         while(!res) {
2789                 if (vms->starting) {
2790                         if (vms->lastmsg > -1) {
2791                                 res = play_and_wait(chan, "vm-onefor");
2792                                 if (!res)
2793                                         res = play_and_wait(chan, vms->vmbox);
2794                                 if (!res)
2795                                         res = play_and_wait(chan, "vm-messages");
2796                         }
2797                         if (!res)
2798                                 res = play_and_wait(chan, "vm-opts");
2799                 } else {
2800                         if (vms->curmsg)
2801                                 res = play_and_wait(chan, "vm-prev");
2802                         if (!res)
2803                                 res = play_and_wait(chan, "vm-advopts");
2804                         if (!res)
2805                                 res = play_and_wait(chan, "vm-repeat");
2806                         if (!res && (vms->curmsg != vms->lastmsg))
2807                                 res = play_and_wait(chan, "vm-next");
2808                         if (!res) {
2809                                 if (!vms->deleted[vms->curmsg])
2810                                         res = play_and_wait(chan, "vm-delete");
2811                                 else
2812                                         res = play_and_wait(chan, "vm-undelete");
2813                                 if (!res)
2814                                         res = play_and_wait(chan, "vm-toforward");
2815                                 if (!res)
2816                                         res = play_and_wait(chan, "vm-savemessage");
2817                         }
2818                 }
2819                 if (!res)
2820                         res = play_and_wait(chan, "vm-helpexit");
2821                 if (!res)
2822                         res = ast_waitfordigit(chan, 6000);
2823                 if (!res) {
2824                         vms->repeats++;
2825                         if (vms->repeats > 2) {
2826                                 res = 't';
2827                         }
2828                 }
2829         }
2830         return res;
2831 }
2832
2833 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2834 {
2835         int cmd = 0;
2836         int retries = 0;
2837         int duration = 0;
2838         char newpassword[80] = "";
2839         char newpassword2[80] = "";
2840         char prefile[256]="";
2841         char buf[256];
2842         int bytes=0;
2843
2844         if (adsi_available(chan))
2845         {
2846                 bytes += adsi_logo(buf + bytes);
2847                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2848                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2849                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2850                 bytes += adsi_voice_mode(buf + bytes, 0);
2851                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2852         }
2853         while((cmd >= 0) && (cmd != 't')) {
2854                 if (cmd)
2855                         retries = 0;
2856                 switch (cmd) {
2857                 case '1':
2858                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2859                         cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2860                         break;
2861                 case '2': 
2862                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2863                         cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2864                         break;
2865                 case '3': 
2866                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2867                         cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2868                         break;
2869                 case '4':
2870                         newpassword[1] = '\0';
2871                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2872                         if (cmd < 0)
2873                                 break;
2874                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2875                                 break;
2876             }
2877                         newpassword2[1] = '\0';
2878                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2879                         if (cmd < 0)
2880                                 break;
2881
2882                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2883                                 break;
2884             }
2885                         if (strcmp(newpassword, newpassword2)) {
2886                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2887                                 cmd = play_and_wait(chan, "vm-mismatch");
2888                                 break;
2889                         }
2890                         vm_change_password(vmu,newpassword);
2891                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2892                         cmd = play_and_wait(chan,"vm-passchanged");
2893                         break;
2894                 case '*': 
2895                         cmd = 't';
2896                         break;
2897                 default: 
2898                         cmd = play_and_wait(chan,"vm-options");
2899                         if (!cmd)
2900                                 cmd = ast_waitfordigit(chan,6000);
2901                         if (!cmd)
2902                                 retries++;
2903                         if (retries > 3)
2904                                 cmd = 't';
2905                  }
2906         }
2907         if (cmd == 't')
2908                 cmd = 0;
2909         return cmd;
2910 }
2911
2912 static int vm_execmain(struct ast_channel *chan, void *data)
2913 {
2914         /* XXX This is, admittedly, some pretty horrendus code.  For some
2915            reason it just seemed a lot easier to do with GOTO's.  I feel
2916            like I'm back in my GWBASIC days. XXX */
2917         int res=-1;
2918         int valid = 0;
2919         int prefix = 0;
2920         int cmd=0;
2921         struct localuser *u;
2922         char prefixstr[80] ="";
2923         char empty[80] = "";
2924         int box;
2925         int useadsi = 0;
2926         int skipuser = 0;
2927         char tmp[256], *ext;
2928         char fmtc[256] = "";
2929         char password[80];
2930         struct vm_state vms;
2931         int logretries = 0;
2932         struct ast_vm_user *vmu = NULL, vmus;
2933         char *context=NULL;
2934         int silentexit = 0;
2935
2936         LOCAL_USER_ADD(u);
2937         memset(&vms, 0, sizeof(vms));
2938         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2939         if (chan->_state != AST_STATE_UP)
2940                 ast_answer(chan);
2941
2942         if (data && strlen(data)) {
2943                 strncpy(tmp, data, sizeof(tmp) - 1);
2944                 ext = tmp;
2945
2946                 switch (*ext) {
2947                         case 's':
2948                  /* We should skip the user's password */
2949                                 valid++;
2950                                 ext++;
2951                                 break;
2952                         case 'p':
2953                  /* We should prefix the mailbox with the supplied data */
2954                                 prefix++;
2955                                 ext++;
2956                                 break;
2957                 }
2958
2959                 context = strchr(ext, '@');
2960                 if (context) {