6d14042fde4e31c4b996ad7971c74e9b5b8ad8b3
[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
1039         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1040         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1041
1042         if (playfile || beep) { 
1043                 if (!beep)
1044                         d = play_and_wait(chan, playfile);
1045                 if (d > -1)
1046                         d = ast_streamfile(chan, "beep",chan->language);
1047                 if (!d)
1048                         d = ast_waitstream(chan,"");
1049                 if (d < 0)
1050                         return -1;
1051         }
1052         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
1053         strcat(prependfile, "-prepend");
1054                         
1055         fmts = ast_strdupa(fmt);
1056         
1057         stringp=fmts;
1058         strsep(&stringp, "|");
1059         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
1060         sfmt[0] = ast_strdupa(fmts);
1061         
1062         while((fmt = strsep(&stringp, "|"))) {
1063                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1064                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1065                         break;
1066                 }
1067                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1068         }
1069
1070         time(&start);
1071         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1072         for (x=0;x<fmtcnt;x++) {
1073                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1074                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1075                 if (!others[x]) {
1076                         break;
1077                 }
1078         }
1079         
1080         sildet = ast_dsp_new(); /* Create the silence detector */
1081         if (!sildet) {
1082                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1083                 return -1;
1084         }
1085         ast_dsp_set_threshold(sildet, silencethreshold);
1086
1087         if (maxsilence > 0) {
1088                 rfmt = chan->readformat;
1089                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1090                 if (res < 0) {
1091                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1092                         return -1;
1093                 }
1094         }
1095                                                 
1096         if (x == fmtcnt) {
1097         /* Loop forever, writing the packets we read to the writer(s), until
1098            we read a # or get a hangup */
1099                 f = NULL;
1100                 for(;;) {
1101                         res = ast_waitfor(chan, 2000);
1102                         if (!res) {
1103                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1104                                 /* Try one more time in case of masq */
1105                                 res = ast_waitfor(chan, 2000);
1106                                 if (!res) {
1107                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1108                                         res = -1;
1109                                 }
1110                         }
1111                         
1112                         if (res < 0) {
1113                                 f = NULL;
1114                                 break;
1115                         }
1116                         f = ast_read(chan);
1117                         if (!f)
1118                                 break;
1119                         if (f->frametype == AST_FRAME_VOICE) {
1120                                 /* write each format */
1121                                 for (x=0;x<fmtcnt;x++) {
1122                                         if (!others[x])
1123                                                 break;
1124                                         res = ast_writestream(others[x], f);
1125                                 }
1126                                 
1127                                 /* Silence Detection */
1128                                 if (maxsilence > 0) {
1129                                         dspsilence = 0;
1130                                         ast_dsp_silence(sildet, f, &dspsilence);
1131                                         if (dspsilence)
1132                                                 totalsilence = dspsilence;
1133                                         else
1134                                                 totalsilence = 0;
1135                                         
1136                                         if (totalsilence > maxsilence) {
1137                                         /* Ended happily with silence */
1138                                         ast_frfree(f);
1139                                         gotsilence = 1;
1140                                         outmsg=2;
1141                                         break;
1142                                         }
1143                                 }
1144                                 /* Exit on any error */
1145                                 if (res) {
1146                                         ast_log(LOG_WARNING, "Error writing frame\n");
1147                                         ast_frfree(f);
1148                                         break;
1149                                 }
1150                         } else if (f->frametype == AST_FRAME_VIDEO) {
1151                                 /* Write only once */
1152                                 ast_writestream(others[0], f);
1153                         } else if (f->frametype == AST_FRAME_DTMF) {
1154                                 /* stop recording with any digit */
1155                                 if (option_verbose > 2) 
1156                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1157                                 res = 't';
1158                                 outmsg = 2;
1159                                 ast_frfree(f);
1160                                 break;
1161                         }
1162                         if (maxtime) {
1163                                 time(&end);
1164                                 if (maxtime < (end - start)) {
1165                                         if (option_verbose > 2)
1166                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1167                                         res = 't';
1168                                         ast_frfree(f);
1169                                         break;
1170                                 }
1171                         }
1172                         ast_frfree(f);
1173                 }
1174                 if (end == start) time(&end);
1175                 if (!f) {
1176                         if (option_verbose > 2) 
1177                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1178                         res = -1;
1179                         outmsg=1;
1180 #if 0
1181                         /* delete all the prepend files */
1182                         for (x=0;x<fmtcnt;x++) {
1183                                 if (!others[x])
1184                                         break;
1185                                 ast_closestream(others[x]);
1186                                 ast_filedelete(prependfile, sfmt[x]);
1187                         }
1188 #endif
1189                 }
1190         } else {
1191                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
1192         }
1193         *duration = end - start;
1194 #if 0
1195         if (outmsg > 1) {
1196 #else
1197         if (outmsg) {
1198 #endif
1199                 struct ast_frame *fr;
1200                 for (x=0;x<fmtcnt;x++) {
1201                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1202                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1203                         if (!others[x] || !realfiles[x])
1204                                 break;
1205                         if (totalsilence)
1206                                 ast_stream_rewind(others[x], totalsilence-200);
1207                         else
1208                                 ast_stream_rewind(others[x], 200);
1209                         ast_truncstream(others[x]);
1210                         /* add the original file too */
1211                         while ((fr = ast_readframe(realfiles[x]))) {
1212                                 ast_writestream(others[x],fr);
1213                         }
1214                         ast_closestream(others[x]);
1215                         ast_closestream(realfiles[x]);
1216                         ast_filerename(prependfile, recordfile, sfmt[x]);
1217 #if 0
1218                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1219 #endif
1220                         ast_filedelete(prependfile, sfmt[x]);
1221                 }
1222         }
1223         if (rfmt) {
1224                 if (ast_set_read_format(chan, rfmt)) {
1225                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1226                 }
1227         }
1228         if (outmsg) {
1229                 if (outmsg > 1) {
1230                         /* Let them know it worked */
1231                         ast_streamfile(chan, "auth-thankyou", chan->language);
1232                         ast_waitstream(chan, "");
1233                 }
1234         }       
1235         return res;
1236 }
1237
1238 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1239 {
1240         char d, *fmts;
1241         char comment[256];
1242         int x, fmtcnt=1, res=-1,outmsg=0;
1243         struct ast_frame *f;
1244         struct ast_filestream *others[MAX_OTHER_FORMATS];
1245         char *sfmt[MAX_OTHER_FORMATS];
1246         char *stringp=NULL;
1247         time_t start, end;
1248         struct ast_dsp *sildet;         /* silence detector dsp */
1249         int totalsilence = 0;
1250         int dspsilence = 0;
1251         int gotsilence = 0;             /* did we timeout for silence? */
1252         int rfmt=0;
1253
1254         /* barf if no pointer passed to store duration in */
1255         if (duration == NULL)
1256                 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1257
1258         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1259         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1260
1261         if (playfile) {
1262                 d = play_and_wait(chan, playfile);
1263                 if (d > -1)
1264                         d = ast_streamfile(chan, "beep",chan->language);
1265                 if (!d)
1266                         d = ast_waitstream(chan,"");
1267                 if (d < 0)
1268                         return -1;
1269         }
1270
1271         fmts = ast_strdupa(fmt);
1272
1273         stringp=fmts;
1274         strsep(&stringp, "|");
1275         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1276         sfmt[0] = ast_strdupa(fmts);
1277
1278         while((fmt = strsep(&stringp, "|"))) {
1279                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1280                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1281                         break;
1282                 }
1283                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1284         }
1285
1286         time(&start);
1287         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1288         for (x=0;x<fmtcnt;x++) {
1289                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1290                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1291
1292                 if (!others[x]) {
1293                         break;
1294                 }
1295         }
1296
1297         sildet = ast_dsp_new(); /* Create the silence detector */
1298         if (!sildet) {
1299                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1300                 return -1;
1301         }
1302         ast_dsp_set_threshold(sildet, silencethreshold);
1303         
1304         if (maxsilence > 0) {
1305                 rfmt = chan->readformat;
1306                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1307                 if (res < 0) {
1308                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1309                         return -1;
1310                 }
1311         }
1312
1313         if (x == fmtcnt) {
1314         /* Loop forever, writing the packets we read to the writer(s), until
1315            we read a # or get a hangup */
1316                 f = NULL;
1317                 for(;;) {
1318                         res = ast_waitfor(chan, 2000);
1319                         if (!res) {
1320                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1321                                 /* Try one more time in case of masq */
1322                                 res = ast_waitfor(chan, 2000);
1323                                 if (!res) {
1324                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1325                                         res = -1;
1326                                 }
1327                         }
1328
1329                         if (res < 0) {
1330                                 f = NULL;
1331                                 break;
1332                         }
1333                         f = ast_read(chan);
1334                         if (!f)
1335                                 break;
1336                         if (f->frametype == AST_FRAME_VOICE) {
1337                                 /* write each format */
1338                                 for (x=0;x<fmtcnt;x++) {
1339                                         res = ast_writestream(others[x], f);
1340                                 }
1341
1342                                 /* Silence Detection */
1343                                 if (maxsilence > 0) {
1344                                         dspsilence = 0;
1345                                         ast_dsp_silence(sildet, f, &dspsilence);
1346                                         if (dspsilence)
1347                                                 totalsilence = dspsilence;
1348                                         else
1349                                                 totalsilence = 0;
1350
1351                                         if (totalsilence > maxsilence) {
1352                                         /* Ended happily with silence */
1353                                         ast_frfree(f);
1354                                         gotsilence = 1;
1355                                         outmsg=2;
1356                                         break;
1357                                         }
1358                                 }
1359                                 /* Exit on any error */
1360                                 if (res) {
1361                                         ast_log(LOG_WARNING, "Error writing frame\n");
1362                                         ast_frfree(f);
1363                                         break;
1364                                 }
1365                         } else if (f->frametype == AST_FRAME_VIDEO) {
1366                                 /* Write only once */
1367                                 ast_writestream(others[0], f);
1368                         } else if (f->frametype == AST_FRAME_DTMF) {
1369                                 if (f->subclass == '#') {
1370                                         if (option_verbose > 2)
1371                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1372                                         res = '#';
1373                                         outmsg = 2;
1374                                         ast_frfree(f);
1375                                         break;
1376                                 }
1377                         }
1378                                 if (f->subclass == '0') {
1379                                 /* Check for a '0' during message recording also, in case caller wants operator */
1380                                         if (option_verbose > 2)
1381                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1382                                         res = '0';
1383                                         outmsg = 0;
1384                                         ast_frfree(f);
1385                                         break;
1386                                 }
1387                         if (maxtime) {
1388                                 time(&end);
1389                                 if (maxtime < (end - start)) {
1390                                         if (option_verbose > 2)
1391                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1392                                         res = 't';
1393                                         ast_frfree(f);
1394                                         break;
1395                                 }
1396                         }
1397                         ast_frfree(f);
1398                 }
1399                 if (end == start) time(&end);
1400                 if (!f) {
1401                         if (option_verbose > 2)
1402                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1403                         res = -1;
1404                         outmsg=1;
1405                 }
1406         } else {
1407                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1408         }
1409
1410         *duration = end - start;
1411
1412         for (x=0;x<fmtcnt;x++) {
1413                 if (!others[x])
1414                         break;
1415                 if (totalsilence)
1416                         ast_stream_rewind(others[x], totalsilence-200);
1417                 else
1418                         ast_stream_rewind(others[x], 200);
1419                 ast_truncstream(others[x]);
1420                 ast_closestream(others[x]);
1421         }
1422         if (rfmt) {
1423                 if (ast_set_read_format(chan, rfmt)) {
1424                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1425                 }
1426         }
1427         if (outmsg) {
1428                 if (outmsg > 1) {
1429                 /* Let them know recording is stopped */
1430                         ast_streamfile(chan, "auth-thankyou", chan->language);
1431                         ast_waitstream(chan, "");
1432                 }
1433         }
1434
1435         return res;
1436 }
1437
1438 static void free_user(struct ast_vm_user *vmu)
1439 {
1440         if (vmu->alloced)
1441                 free(vmu);
1442 }
1443
1444 static void free_zone(struct vm_zone *z)
1445 {
1446         free(z);
1447 }
1448
1449 static void run_externnotify(char *context, char *extension, int numvoicemails)
1450 {
1451         char arguments[255];
1452
1453         if(externnotify[0]) {
1454                 strncpy(arguments, externnotify, sizeof(arguments));
1455                 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1456                 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1457                 ast_safe_system(arguments);
1458         }
1459 }
1460
1461
1462 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1463 {
1464         char comment[256];
1465         char txtfile[256];
1466         FILE *txt;
1467         int res = 0;
1468         int msgnum;
1469         int fd;
1470         int duration = 0;
1471         char date[256];
1472         char dir[256];
1473         char fn[256];
1474         char prefile[256]="";
1475         char fmt[80];
1476         char *context;
1477         char *ecodes = "#";
1478         char *stringp;
1479         char tmp[256] = "";
1480         struct ast_vm_user *vmu;
1481         struct ast_vm_user svm;
1482
1483
1484         strncpy(tmp, ext, sizeof(tmp) - 1);
1485         ext = tmp;
1486         context = strchr(tmp, '@');
1487         if (context) {
1488                 *context = '\0';
1489                 context++;
1490         }
1491
1492         if ((vmu = find_user(&svm, context, ext))) {
1493                 /* Setup pre-file if appropriate */
1494                 if (busy)
1495                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1496                 else if (unavail)
1497                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1498                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1499                 /* It's easier just to try to make it than to check for its existence */
1500                 if (mkdir(dir, 0700) && (errno != EEXIST))
1501                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1502                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
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, "INBOX");
1507                 if (mkdir(dir, 0700) && (errno != EEXIST))
1508                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1509                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1510                         ecodes = "#0*";
1511                 /* Play the beginning intro if desired */
1512                 if (strlen(prefile)) {
1513                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1514                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1515                                     res = ast_waitstream(chan, "#0*");
1516                         } else {
1517                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1518                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1519                         }
1520                         if (res < 0) {
1521                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1522                                 free_user(vmu);
1523                                 return -1;
1524                         }
1525                 }
1526                 if (res == '#') {
1527                         /* On a '#' we skip the instructions */
1528                         silent = 1;
1529                         res = 0;
1530                 }
1531                 if (!res && !silent) {
1532                         res = ast_streamfile(chan, INTRO, chan->language);
1533                         if (!res)
1534                                 res = ast_waitstream(chan, ecodes);
1535                         if (res == '#') {
1536                                 silent = 1;
1537                                 res = 0;
1538                         }
1539                 }
1540                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1541                 other than the operator -- an automated attendant or mailbox login for example */
1542                 if (res == '*') {
1543                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1544                         if (strlen(chan->macrocontext))
1545                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1546                         chan->priority = 0;
1547                         free_user(vmu);
1548                         return 0;
1549                 }
1550                 /* Check for a '0' here */
1551                 if (res == '0') {
1552                 transfer:
1553                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1554                         if (strlen(chan->macrocontext))
1555                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1556                         chan->priority = 0;
1557                         free_user(vmu);
1558                         return 0;
1559                 }
1560                 if (res >= 0) {
1561                         /* Unless we're *really* silent, try to send the beep */
1562                         res = ast_streamfile(chan, "beep", chan->language);
1563                         if (!res)
1564                                 res = ast_waitstream(chan, "");
1565                 }
1566                 if (res < 0) {
1567                         free_user(vmu);
1568                         return -1;
1569                 }
1570                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1571                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1572                 if (strlen(fmt)) {
1573                         msgnum = 0;
1574                         do {
1575                                 make_file(fn, sizeof(fn), dir, msgnum);
1576                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1577                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1578                                                                         vmu->fullname, ext, chan->name);
1579                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1580                                         break;
1581                                 msgnum++;
1582                         } while(msgnum < MAXMSG);
1583                         if (msgnum < MAXMSG) {
1584                                 /* Store information */
1585                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1586                                 txt = fopen(txtfile, "w+");
1587                                 if (txt) {
1588                                         get_date(date, sizeof(date));
1589                                         fprintf(txt, 
1590 ";\n"
1591 "; Message Information file\n"
1592 ";\n"
1593 "[message]\n"
1594 "origmailbox=%s\n"
1595 "context=%s\n"
1596 "macrocontext=%s\n"
1597 "exten=%s\n"
1598 "priority=%d\n"
1599 "callerchan=%s\n"
1600 "callerid=%s\n"
1601 "origdate=%s\n"
1602 "origtime=%ld\n",
1603         ext,
1604         chan->context,
1605         chan->macrocontext, 
1606         chan->exten,
1607         chan->priority,
1608         chan->name,
1609         chan->callerid ? chan->callerid : "Unknown",
1610         date, (long)time(NULL));
1611                                         fclose(txt);
1612                                 } else
1613                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1614                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1615                                 if (res == '0')
1616                                         goto transfer;
1617                                 if (res > 0)
1618                                         res = 0;
1619                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1620                                 if (fd > -1) {
1621                                         txt = fdopen(fd, "a");
1622                                         if (txt) {
1623                                                 fprintf(txt, "duration=%d\n", duration);
1624                                                 fclose(txt);
1625                                         } else
1626                                                 close(fd);
1627                                 }
1628                                 if (duration < vmminmessage) {
1629                                         vm_delete(fn);
1630                                         goto leave_vm_out;
1631                                 }
1632                                 stringp = fmt;
1633                                 strsep(&stringp, "|");
1634                                 /* Send e-mail if applicable */
1635                                 if (strlen(vmu->email)) {
1636                                         int attach_user_voicemail = attach_voicemail;
1637                                         char *myserveremail = serveremail;
1638                                         if (vmu->attach > -1)
1639                                                 attach_user_voicemail = vmu->attach;
1640                                         if (strlen(vmu->serveremail))
1641                                                 myserveremail = vmu->serveremail;
1642                                                 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1643                                 }
1644                                 if (strlen(vmu->pager)) {
1645                                         char *myserveremail = serveremail;
1646                                         if (strlen(vmu->serveremail))
1647                                                 myserveremail = vmu->serveremail;
1648                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1649                                 }
1650                         } else {
1651                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1652                                 if (!res)
1653                                         res = ast_waitstream(chan, "");
1654                                 ast_log(LOG_WARNING, "No more messages possible\n");
1655                         }
1656                 } else
1657                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1658 leave_vm_out:
1659                 free_user(vmu);
1660         } else {
1661                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1662                         /*Send the call to n+101 priority, where n is the current priority*/
1663                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1664                                 chan->priority+=100;
1665         }
1666         /* Leave voicemail for someone */
1667         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext, ast_app_has_voicemail(ext));
1668
1669         /* If an external program is specified to be run after leaving a voicemail */
1670         run_externnotify(chan->context, ext, ast_app_has_voicemail(ext));
1671
1672         return res;
1673 }
1674
1675 static char *mbox(int id)
1676 {
1677         switch(id) {
1678         case 0:
1679                 return "INBOX";
1680         case 1:
1681                 return "Old";
1682         case 2:
1683                 return "Work";
1684         case 3:
1685                 return "Family";
1686         case 4:
1687                 return "Friends";
1688         case 5:
1689                 return "Cust1";
1690         case 6:
1691                 return "Cust2";
1692         case 7:
1693                 return "Cust3";
1694         case 8:
1695                 return "Cust4";
1696         case 9:
1697                 return "Cust5";
1698         default:
1699                 return "Unknown";
1700         }
1701 }
1702
1703 static int count_messages(char *dir)
1704 {
1705         int x;
1706         char fn[256];
1707         for (x=0;x<MAXMSG;x++) {
1708                 make_file(fn, sizeof(fn), dir, x);
1709                 if (ast_fileexists(fn, NULL, NULL) < 1)
1710                         break;
1711         }
1712         return x;
1713 }
1714
1715 static int say_and_wait(struct ast_channel *chan, int num)
1716 {
1717         int d;
1718         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1719         return d;
1720 }
1721
1722 static int copy(char *infile, char *outfile)
1723 {
1724         int ifd;
1725         int ofd;
1726         int res;
1727         int len;
1728         char buf[4096];
1729         if ((ifd = open(infile, O_RDONLY)) < 0) {
1730                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1731                 return -1;
1732         }
1733         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1734                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1735                 close(ifd);
1736                 return -1;
1737         }
1738         do {
1739                 len = read(ifd, buf, sizeof(buf));
1740                 if (len < 0) {
1741                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1742                         close(ifd);
1743                         close(ofd);
1744                         unlink(outfile);
1745                 }
1746                 if (len) {
1747                         res = write(ofd, buf, len);
1748                         if (res != len) {
1749                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1750                                 close(ifd);
1751                                 close(ofd);
1752                                 unlink(outfile);
1753                         }
1754                 }
1755         } while(len);
1756         close(ifd);
1757         close(ofd);
1758         return 0;
1759 }
1760
1761 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1762 {
1763         char sfn[256];
1764         char dfn[256];
1765         char ddir[256];
1766         char txt[256];
1767         char ntxt[256];
1768         char *dbox = mbox(box);
1769         int x;
1770         make_file(sfn, sizeof(sfn), dir, msg);
1771         make_dir(ddir, sizeof(ddir), context, username, dbox);
1772         mkdir(ddir, 0700);
1773         for (x=0;x<MAXMSG;x++) {
1774                 make_file(dfn, sizeof(dfn), ddir, x);
1775                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1776                         break;
1777         }
1778         if (x >= MAXMSG)
1779                 return -1;
1780         ast_filecopy(sfn, dfn, NULL);
1781         if (strcmp(sfn, dfn)) {
1782                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1783                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1784                 copy(txt, ntxt);
1785         }
1786         return 0;
1787 }
1788
1789 static int adsi_logo(unsigned char *buf)
1790 {
1791         int bytes = 0;
1792         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1793         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1794         return bytes;
1795 }
1796
1797 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1798 {
1799         char buf[256];
1800         int bytes=0;
1801         int x;
1802         char num[5];
1803
1804         *useadsi = 0;
1805         bytes += adsi_data_mode(buf + bytes);
1806         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1807
1808         bytes = 0;
1809         bytes += adsi_logo(buf);
1810         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1811 #ifdef DISPLAY
1812         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1813 #endif
1814         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1815         bytes += adsi_data_mode(buf + bytes);
1816         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1817
1818         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1819                 bytes = 0;
1820                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1821                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1822                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1823                 bytes += adsi_voice_mode(buf + bytes, 0);
1824                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1825                 return 0;
1826         }
1827
1828 #ifdef DISPLAY
1829         /* Add a dot */
1830         bytes = 0;
1831         bytes += adsi_logo(buf);
1832         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1833         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1834         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1835         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1836 #endif
1837         bytes = 0;
1838         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1839         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1840         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1841         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1842         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1843         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1844         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1845
1846 #ifdef DISPLAY
1847         /* Add another dot */
1848         bytes = 0;
1849         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1850       bytes += adsi_voice_mode(buf + bytes, 0);
1851
1852         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1853         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1854 #endif
1855
1856         bytes = 0;
1857         /* These buttons we load but don't use yet */
1858         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1859         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1860         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1861         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1862         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1863         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1864         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1865
1866 #ifdef DISPLAY
1867         /* Add another dot */
1868         bytes = 0;
1869         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1870         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1871         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1872 #endif
1873
1874         bytes = 0;
1875         for (x=0;x<5;x++) {
1876                 snprintf(num, sizeof(num), "%d", x);
1877                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1878         }
1879         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1880         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1881
1882 #ifdef DISPLAY
1883         /* Add another dot */
1884         bytes = 0;
1885         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1886         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1887         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1888 #endif
1889
1890         if (adsi_end_download(chan)) {
1891                 bytes = 0;
1892                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1893                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1894                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1895                 bytes += adsi_voice_mode(buf + bytes, 0);
1896                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1897                 return 0;
1898         }
1899         bytes = 0;
1900         bytes += adsi_download_disconnect(buf + bytes);
1901         bytes += adsi_voice_mode(buf + bytes, 0);
1902         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1903
1904         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1905
1906 #ifdef DISPLAY
1907         /* Add last dot */
1908         bytes = 0;
1909         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1910         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1911 #endif
1912         ast_log(LOG_DEBUG, "Restarting session...\n");
1913
1914         bytes = 0;
1915         /* Load the session now */
1916         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1917                 *useadsi = 1;
1918                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1919         } else
1920                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1921
1922         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1923         return 0;
1924 }
1925
1926 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1927 {
1928         int x;
1929         if (!adsi_available(chan))
1930           return;
1931         x = adsi_load_session(chan, adapp, adver, 1);
1932         if (x < 0)
1933                 return;
1934         if (!x) {
1935                 if (adsi_load_vmail(chan, useadsi)) {
1936                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1937                         return;
1938                 }
1939         } else
1940                 *useadsi = 1;
1941 }
1942
1943 static void adsi_login(struct ast_channel *chan)
1944 {
1945         char buf[256];
1946         int bytes=0;
1947         unsigned char keys[8];
1948         int x;
1949         if (!adsi_available(chan))
1950                 return;
1951
1952         for (x=0;x<8;x++)
1953                 keys[x] = 0;
1954         /* Set one key for next */
1955         keys[3] = ADSI_KEY_APPS + 3;
1956
1957         bytes += adsi_logo(buf + bytes);
1958         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1959         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
1960         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1961         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
1962         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
1963         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
1964         bytes += adsi_set_keys(buf + bytes, keys);
1965         bytes += adsi_voice_mode(buf + bytes, 0);
1966         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1967 }
1968
1969 static void adsi_password(struct ast_channel *chan)
1970 {
1971         char buf[256];
1972         int bytes=0;
1973         unsigned char keys[8];
1974         int x;
1975         if (!adsi_available(chan))
1976                 return;
1977
1978         for (x=0;x<8;x++)
1979                 keys[x] = 0;
1980         /* Set one key for next */
1981         keys[3] = ADSI_KEY_APPS + 3;
1982
1983         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1984         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
1985         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
1986         bytes += adsi_set_keys(buf + bytes, keys);
1987         bytes += adsi_voice_mode(buf + bytes, 0);
1988         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1989 }
1990
1991 static void adsi_folders(struct ast_channel *chan, int start, char *label)
1992 {
1993         char buf[256];
1994         int bytes=0;
1995         unsigned char keys[8];
1996         int x,y;
1997
1998         if (!adsi_available(chan))
1999                 return;
2000
2001         for (x=0;x<5;x++) {
2002                 y = ADSI_KEY_APPS + 12 + start + x;
2003                 if (y > ADSI_KEY_APPS + 12 + 4)
2004                         y = 0;
2005                 keys[x] = ADSI_KEY_SKT | y;
2006         }
2007         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2008         keys[6] = 0;
2009         keys[7] = 0;
2010
2011         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2012         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2013         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2014         bytes += adsi_set_keys(buf + bytes, keys);
2015         bytes += adsi_voice_mode(buf + bytes, 0);
2016
2017         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2018 }
2019
2020 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2021 {
2022         int bytes=0;
2023         char buf[256], buf1[256], buf2[256];
2024         char fn2[256];
2025
2026         char cid[256]="";
2027         char *val;
2028         char *name, *num;
2029         char datetime[21]="";
2030         FILE *f;
2031
2032         unsigned char keys[8];
2033
2034         int x;
2035
2036         if (!adsi_available(chan))
2037                 return;
2038
2039         /* Retrieve important info */
2040         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2041         f = fopen(fn2, "r");
2042         if (f) {
2043                 while(!feof(f)) {       
2044                         fgets(buf, sizeof(buf), f);
2045                         if (!feof(f)) {
2046                                 char *stringp=NULL;
2047                                 stringp=buf;
2048                                 strsep(&stringp, "=");
2049                                 val = strsep(&stringp, "=");
2050                                 if (val && strlen(val)) {
2051                                         if (!strcmp(buf, "callerid"))
2052                                                 strncpy(cid, val, sizeof(cid) - 1);
2053                                         if (!strcmp(buf, "origdate"))
2054                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2055                                 }
2056                         }
2057                 }
2058                 fclose(f);
2059         }
2060         /* New meaning for keys */
2061         for (x=0;x<5;x++)
2062                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2063         keys[6] = 0x0;
2064         keys[7] = 0x0;
2065
2066         if (!msg) {
2067                 /* No prev key, provide "Folder" instead */
2068                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2069         }
2070         if (msg >= last) {
2071                 /* If last message ... */
2072                 if (msg) {
2073                         /* but not only message, provide "Folder" instead */
2074                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2075       bytes += adsi_voice_mode(buf + bytes, 0);
2076
2077                 } else {
2078                         /* Otherwise if only message, leave blank */
2079                         keys[3] = 1;
2080                 }
2081         }
2082
2083         if (strlen(cid)) {
2084                 ast_callerid_parse(cid, &name, &num);
2085                 if (!name)
2086                         name = num;
2087         } else
2088                 name = "Unknown Caller";
2089
2090         /* If deleted, show "undeleted" */
2091
2092         if (deleted)
2093                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2094
2095         /* Except "Exit" */
2096         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2097         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2098                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2099         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2100
2101         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2102         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2103         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2104         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2105         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2106         bytes += adsi_set_keys(buf + bytes, keys);
2107         bytes += adsi_voice_mode(buf + bytes, 0);
2108
2109         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2110 }
2111
2112 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2113 {
2114         int bytes=0;
2115         char buf[256];
2116         unsigned char keys[8];
2117
2118         int x;
2119
2120         if (!adsi_available(chan))
2121                 return;
2122
2123         /* New meaning for keys */
2124         for (x=0;x<5;x++)
2125                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2126
2127         keys[6] = 0x0;
2128         keys[7] = 0x0;
2129
2130         if (!msg) {
2131                 /* No prev key, provide "Folder" instead */
2132                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2133         }
2134         if (msg >= last) {
2135                 /* If last message ... */
2136                 if (msg) {
2137                         /* but not only message, provide "Folder" instead */
2138                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2139                 } else {
2140                         /* Otherwise if only message, leave blank */
2141                         keys[3] = 1;
2142                 }
2143         }
2144
2145         /* If deleted, show "undeleted" */
2146         if (deleted) 
2147                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2148
2149         /* Except "Exit" */
2150         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2151         bytes += adsi_set_keys(buf + bytes, keys);
2152         bytes += adsi_voice_mode(buf + bytes, 0);
2153
2154         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2155 }
2156
2157 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2158 {
2159         char buf[256], buf1[256], buf2[256];
2160         int bytes=0;
2161         unsigned char keys[8];
2162         int x;
2163
2164         char *newm = (new == 1) ? "message" : "messages";
2165         char *oldm = (old == 1) ? "message" : "messages";
2166         if (!adsi_available(chan))
2167                 return;
2168         if (new) {
2169                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2170                 if (old) {
2171                         strcat(buf1, " and");
2172                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2173                 } else {
2174                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2175                 }
2176         } else if (old) {
2177                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2178                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2179         } else {
2180                 strcpy(buf1, "You have no messages.");
2181                 strcpy(buf2, " ");
2182         }
2183         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2184         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2185         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2186
2187         for (x=0;x<6;x++)
2188                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2189         keys[6] = 0;
2190         keys[7] = 0;
2191
2192         /* Don't let them listen if there are none */
2193         if (lastmsg < 0)
2194                 keys[0] = 1;
2195         bytes += adsi_set_keys(buf + bytes, keys);
2196
2197         bytes += adsi_voice_mode(buf + bytes, 0);
2198
2199         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2200 }
2201
2202 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2203 {
2204         char buf[256], buf1[256], buf2[256];
2205         int bytes=0;
2206         unsigned char keys[8];
2207         int x;
2208
2209         char *mess = (messages == 1) ? "message" : "messages";
2210
2211         if (!adsi_available(chan))
2212                 return;
2213
2214         /* Original command keys */
2215         for (x=0;x<6;x++)
2216                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2217
2218         keys[6] = 0;
2219         keys[7] = 0;
2220
2221         if (messages < 1)
2222                 keys[0] = 0;
2223
2224         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2225                         strcasecmp(folder, "INBOX") ? " folder" : "");
2226
2227         if (messages)
2228                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2229         else
2230                 strcpy(buf2, "no messages.");
2231         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2232         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2233         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2234         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2235         bytes += adsi_set_keys(buf + bytes, keys);
2236
2237         bytes += adsi_voice_mode(buf + bytes, 0);
2238
2239         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2240         
2241 }
2242
2243 /*
2244 static void adsi_clear(struct ast_channel *chan)
2245 {
2246         char buf[256];
2247         int bytes=0;
2248         if (!adsi_available(chan))
2249                 return;
2250         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2251         bytes += adsi_voice_mode(buf + bytes, 0);
2252
2253         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2254 }
2255 */
2256
2257 static void adsi_goodbye(struct ast_channel *chan)
2258 {
2259         char buf[256];
2260         int bytes=0;
2261
2262         if (!adsi_available(chan))
2263                 return;
2264         bytes += adsi_logo(buf + bytes);
2265         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2266         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2267         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2268         bytes += adsi_voice_mode(buf + bytes, 0);
2269
2270         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2271 }
2272
2273 static int get_folder(struct ast_channel *chan, int start)
2274 {
2275         int x;
2276         int d;
2277         char fn[256];
2278         d = play_and_wait(chan, "vm-press");
2279         if (d)
2280                 return d;
2281         for (x = start; x< 5; x++) {
2282                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2283                         return d;
2284                 d = play_and_wait(chan, "vm-for");
2285                 if (d)
2286                         return d;
2287                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2288                 d = play_and_wait(chan, fn);
2289                 if (d)
2290                         return d;
2291                 d = play_and_wait(chan, "vm-messages");
2292                 if (d)
2293                         return d;
2294                 d = ast_waitfordigit(chan, 500);
2295                 if (d)
2296                         return d;
2297         }
2298         d = play_and_wait(chan, "vm-tocancel");
2299         if (d)
2300                 return d;
2301         d = ast_waitfordigit(chan, 4000);
2302         return d;
2303 }
2304
2305 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2306 {
2307         int res = 0;
2308         res = play_and_wait(chan, fn);
2309         while (((res < '0') || (res > '9')) &&
2310                         (res != '#') && (res >= 0)) {
2311                 res = get_folder(chan, 0);
2312         }
2313         return res;
2314 }
2315
2316 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2317 {
2318         int cmd = 0;
2319         int retries = 0;
2320         int duration = 0;
2321
2322         while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2323                 if (cmd)
2324                         retries = 0;
2325                 switch (cmd) {
2326                 case '1': 
2327                         /* prepend a message to the current message and return */
2328                 {
2329                         char file[200];
2330                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2331                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2332                         break;
2333                 }
2334                 case '2': 
2335                         cmd = 't';
2336                         break;
2337                 case '*':
2338                         cmd = '*';
2339                         break;
2340                 default: 
2341                         cmd = play_and_wait(chan,"vm-forwardoptions");
2342                         if (!cmd)
2343                                 cmd = play_and_wait(chan,"vm-starmain");
2344                         if (!cmd)
2345                                 cmd = ast_waitfordigit(chan,6000);
2346                         if (!cmd)
2347                                 retries++;
2348                         if (retries > 3)
2349                                 cmd = 't';
2350                  }
2351         }
2352         if (cmd == 't')
2353                 cmd = 0;
2354         return cmd;
2355 }
2356
2357 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2358 {
2359         char username[70];
2360         char sys[256];
2361         char todir[256];
2362         int todircount=0;
2363         int duration;
2364         struct ast_config *mif;
2365         char miffile[256];
2366         char fn[256];
2367         char callerid[512];
2368         int res = 0, cmd = 0;
2369         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2370         char tmp[256];
2371         char *stringp, *s;
2372         int saved_messages = 0, found = 0;
2373         int valid_extensions = 0;
2374         while (!res && !valid_extensions) {
2375                 res = ast_streamfile(chan, "vm-extension", chan->language);
2376                 if (res)
2377                         break;
2378                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2379                         break;
2380                 /* start all over if no username */
2381                 if (!strlen(username))
2382                         continue;
2383                 stringp = username;
2384                 s = strsep(&stringp, "*");
2385                 /* start optimistic */
2386                 valid_extensions = 1;
2387                 while (s) {
2388                         /* find_user is going to malloc since we have a NULL as first argument */
2389                         if ((receiver = find_user(NULL, context, s))) {
2390                                 if (!extensions)
2391                                         vmtmp = extensions = receiver;
2392                                 else {
2393                                         vmtmp->next = receiver;
2394                                         vmtmp = receiver;
2395                                 }
2396                                 found++;
2397                         } else {
2398                                 valid_extensions = 0;
2399                                 break;
2400                         }
2401                         s = strsep(&stringp, "*");
2402                 }
2403                 /* break from the loop of reading the extensions */
2404                 if (valid_extensions)
2405                         break;
2406                 /* invalid extension, try again */
2407                 res = play_and_wait(chan, "pbx-invalid");
2408         }
2409         /* check if we're clear to proceed */
2410         if (!extensions || !valid_extensions)
2411                 return res;
2412         vmtmp = extensions;
2413         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2414         if (!cmd) {
2415                 while(!res && vmtmp) {
2416                         /* if (play_and_wait(chan, "vm-savedto"))
2417                                 break;
2418                         */
2419                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2420                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2421                         ast_log(LOG_DEBUG, sys);
2422                         ast_safe_system(sys);
2423         
2424                         todircount = count_messages(todir);
2425                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2426                         stringp = tmp;
2427                         while((s = strsep(&stringp, "|"))) {
2428                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2429                                 if (!strcasecmp(s, "wav49"))
2430                                         s = "WAV";
2431                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2432                                 ast_log(LOG_DEBUG, sys);
2433                                 ast_safe_system(sys);
2434                         }
2435                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2436                         ast_log(LOG_DEBUG, sys);
2437                         ast_safe_system(sys);
2438                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2439         
2440                         /* load the information on the source message so we can send an e-mail like a new message */
2441                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2442                         if ((mif=ast_load(miffile))) {
2443         
2444                                 /* set callerid and duration variables */
2445                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2446                                 s = ast_variable_retrieve(mif, NULL, "duration");
2447                                 if (s)
2448                                         duration = atol(s);
2449                                 else
2450                                         duration = 0;
2451                                 if (strlen(vmtmp->email)) {
2452                                         int attach_user_voicemail = attach_voicemail;
2453                                         char *myserveremail = serveremail;
2454                                         if (vmtmp->attach > -1)
2455                                                 attach_user_voicemail = vmtmp->attach;
2456                                         if (strlen(vmtmp->serveremail))
2457                                                 myserveremail = vmtmp->serveremail;
2458                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2459                                 }
2460                              
2461                                 if (strlen(vmtmp->pager)) {
2462                                         char *myserveremail = serveremail;
2463                                         if (strlen(vmtmp->serveremail))
2464                                                 myserveremail = vmtmp->serveremail;
2465                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2466                                 }
2467                                   
2468                                 ast_destroy(mif); /* or here */
2469                         }
2470                         /* Leave voicemail for someone */
2471                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2472                         run_externnotify(chan->context, vmtmp->mailbox, ast_app_has_voicemail(vmtmp->mailbox));
2473         
2474                         saved_messages++;
2475                         vmfree = vmtmp;
2476                         vmtmp = vmtmp->next;
2477                         free_user(vmfree);
2478                 }
2479                 if (saved_messages > 0) {
2480                         /* give confirmatopm that the message was saved */
2481                         /* commented out since we can't forward batches yet
2482                         if (saved_messages == 1)
2483                                 res = play_and_wait(chan, "vm-message");
2484                         else
2485                                 res = play_and_wait(chan, "vm-messages");
2486                         if (!res)
2487                                 res = play_and_wait(chan, "vm-saved"); */
2488                         if (!res)
2489                                 res = play_and_wait(chan, "vm-msgsaved");
2490                 }
2491         }
2492         return res ? res : cmd;
2493 }
2494
2495
2496 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2497 {
2498         int res;
2499         if ((res = ast_streamfile(chan, file, chan->language))) 
2500                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2501         if (!res)
2502                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2503         return res;
2504 }
2505
2506 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2507 {
2508         int res;
2509         if ((res = ast_streamfile(chan, file, chan->language)))
2510                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2511         if (!res)
2512                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2513         return res;
2514 }
2515
2516 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2517 {
2518         int res = 0;
2519         struct vm_zone *the_zone = NULL;
2520         time_t t;
2521         long tin;
2522
2523         if (sscanf(origtime,"%ld",&tin) < 1) {
2524                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2525                 return 0;
2526         }
2527         t = tin;
2528
2529         /* Does this user have a timezone specified? */
2530         if (strlen(vmu->zonetag)) {
2531                 /* Find the zone in the list */
2532                 struct vm_zone *z;
2533                 z = zones;
2534                 while (z) {
2535                         if (!strcmp(z->name, vmu->zonetag)) {
2536                                 the_zone = z;
2537                                 break;
2538                         }
2539                         z = z->next;
2540                 }
2541         }
2542
2543 /* No internal variable parsing for now, so we'll comment it out for the time being */
2544 #if 0
2545         /* Set the DIFF_* variables */
2546         localtime_r(&t, &time_now);
2547         gettimeofday(&tv_now,NULL);
2548         tnow = tv_now.tv_sec;
2549         localtime_r(&tnow,&time_then);
2550
2551         /* Day difference */
2552         if (time_now.tm_year == time_then.tm_year)
2553                 sprintf(temp,"%d",time_now.tm_yday);
2554         else
2555                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2556         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2557
2558         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2559 #endif
2560         if (the_zone)
2561                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2562         else
2563                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2564 #if 0
2565         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2566 #endif
2567         return res;
2568 }
2569
2570
2571
2572 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2573 {
2574         int res = 0;
2575         int i;
2576         char *callerid, *name;
2577         char prefile[256]="";
2578         
2579
2580         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2581         /* 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 */
2582         if((cid == NULL)||(context == NULL))
2583                 return res;
2584
2585         /* Strip off caller ID number from name */
2586         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2587         ast_callerid_parse(cid, &name, &callerid);
2588         if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2589                 /* Check for internal contexts and only */
2590                 /* say extension when the call didn't come from an internal context in the list */
2591                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2592                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2593                         if((strcmp(cidinternalcontexts[i], context) == 0))
2594                                 break;
2595                 }
2596                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2597                         if(!res) {
2598                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2599                                 if (strlen(prefile)) {
2600                                 /* See if we can find a recorded name for this person instead of their extension number */
2601                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2602                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2603                                                 if (!callback)
2604                                                         res = wait_file2(chan, vms, "vm-from");
2605                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2606                                                 res = ast_waitstream(chan, "");
2607                                         } else {
2608                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2609                                                 /* BB: Say "from extension" as one saying to sound smoother */
2610                                                 if (!callback)
2611                                                         res = wait_file2(chan, vms, "vm-from-extension");
2612                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2613                                         }
2614                                 }
2615                         }
2616                 }
2617
2618                 else if (!res){
2619                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2620                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2621                         if (!callback)
2622                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2623                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2624                 }
2625         }
2626         else{
2627                 /* Number unknown */
2628                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2629                 if(!res)
2630                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2631                         res = wait_file2(chan, vms, "vm-unknown-caller");
2632         }
2633         return res;                                                               
2634 }
2635
2636 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2637 {
2638         int res = 0;
2639         char filename[256],*origtime, *cid, *context;
2640         struct ast_config *msg_cfg;
2641
2642         vms->starting = 0; 
2643         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2644         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2645         if (!msg)
2646                 res = wait_file2(chan, vms, "vm-first");
2647         else if (msg == vms->lastmsg)
2648                 res = wait_file2(chan, vms, "vm-last");
2649         if (!res) {
2650                 res = wait_file2(chan, vms, "vm-message");
2651                 if (msg && (msg != vms->lastmsg)) {
2652                         if (!res)
2653                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2654                 }
2655         }
2656
2657         /* Retrieve info from VM attribute file */
2658
2659         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2660         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2661         msg_cfg = ast_load(filename);
2662         if (!msg_cfg) {
2663                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2664                 return 0;
2665         }
2666                                                                                                                                  
2667         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2668                 return 0;
2669                                                                                                                                  
2670         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2671
2672         context = ast_variable_retrieve(msg_cfg, "message", "context");
2673         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2674                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2675
2676         if (!res)
2677                 res = play_message_datetime(chan, vmu, origtime, filename);
2678
2679         if ((!res)&&(vmu->saycid))
2680                 res = play_message_callerid(chan, vms, cid, context, 0);
2681
2682         ast_destroy(msg_cfg);
2683
2684         if (!res) {
2685                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2686                 vms->heard[msg] = 1;
2687                 res = wait_file(chan, vms, vms->fn);
2688         }
2689         return res;
2690 }
2691
2692 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2693 {
2694         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2695         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2696         vms->lastmsg = count_messages(vms->curdir) - 1;
2697         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2698 }
2699
2700 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2701 {
2702         int x;
2703         char ntxt[256] = "";
2704         char txt[256] = "";
2705         if (vms->lastmsg > -1) { 
2706                 /* Get the deleted messages fixed */ 
2707                 vms->curmsg = -1; 
2708                 for (x=0;x < MAXMSG;x++) { 
2709                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2710                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2711                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2712                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2713                                         break;
2714                                 vms->curmsg++; 
2715                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2716                                 if (strcmp(vms->fn, vms->fn2)) { 
2717                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2718                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2719                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2720                                         rename(txt, ntxt); 
2721                                 } 
2722                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2723                                 /* Move to old folder before deleting */ 
2724                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2725                         } 
2726                 } 
2727                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2728                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2729                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2730                                 break;
2731                         vm_delete(vms->fn);
2732                 } 
2733         } 
2734         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2735         memset(vms->heard, 0, sizeof(vms->heard)); 
2736 }
2737
2738 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2739 {
2740         /* Introduce messages they have */
2741         int res;
2742         res = play_and_wait(chan, "vm-youhave");
2743         if (!res) {
2744                 if (vms->newmessages) {
2745                         res = say_and_wait(chan, vms->newmessages);
2746                         if (!res)
2747                                 res = play_and_wait(chan, "vm-INBOX");
2748                         if (vms->oldmessages && !res)
2749                                 res = play_and_wait(chan, "vm-and");
2750                         else if (!res) {
2751                                 if ((vms->newmessages == 1))
2752                                         res = play_and_wait(chan, "vm-message");
2753                                 else
2754                                         res = play_and_wait(chan, "vm-messages");
2755                         }
2756                                 
2757                 }
2758                 if (!res && vms->oldmessages) {
2759                         res = say_and_wait(chan, vms->oldmessages);
2760                         if (!res)
2761                                 res = play_and_wait(chan, "vm-Old");
2762                         if (!res) {
2763                                 if (vms->oldmessages == 1)
2764                                         res = play_and_wait(chan, "vm-message");
2765                                 else
2766                                         res = play_and_wait(chan, "vm-messages");
2767                         }
2768                 }
2769                 if (!res) {
2770                         if (!vms->oldmessages && !vms->newmessages) {
2771                                 res = play_and_wait(chan, "vm-no");
2772                                 if (!res)
2773                                         res = play_and_wait(chan, "vm-messages");
2774                         }
2775                 }
2776         }
2777         return res;
2778 }
2779
2780 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2781 {
2782         int res = 0;
2783         /* Play instructions and wait for new command */
2784         while(!res) {
2785                 if (vms->starting) {
2786                         if (vms->lastmsg > -1) {
2787                                 res = play_and_wait(chan, "vm-onefor");
2788                                 if (!res)
2789                                         res = play_and_wait(chan, vms->vmbox);
2790                                 if (!res)
2791                                         res = play_and_wait(chan, "vm-messages");
2792                         }
2793                         if (!res)
2794                                 res = play_and_wait(chan, "vm-opts");
2795                 } else {
2796                         if (vms->curmsg)
2797                                 res = play_and_wait(chan, "vm-prev");
2798                         if (!res)
2799                                 res = play_and_wait(chan, "vm-advopts");
2800                         if (!res)
2801                                 res = play_and_wait(chan, "vm-repeat");
2802                         if (!res && (vms->curmsg != vms->lastmsg))
2803                                 res = play_and_wait(chan, "vm-next");
2804                         if (!res) {
2805                                 if (!vms->deleted[vms->curmsg])
2806                                         res = play_and_wait(chan, "vm-delete");
2807                                 else
2808                                         res = play_and_wait(chan, "vm-undelete");
2809                                 if (!res)
2810                                         res = play_and_wait(chan, "vm-toforward");
2811                                 if (!res)
2812                                         res = play_and_wait(chan, "vm-savemessage");
2813                         }
2814                 }
2815                 if (!res)
2816                         res = play_and_wait(chan, "vm-helpexit");
2817                 if (!res)
2818                         res = ast_waitfordigit(chan, 6000);
2819                 if (!res) {
2820                         vms->repeats++;
2821                         if (vms->repeats > 2) {
2822                                 res = 't';
2823                         }
2824                 }
2825         }
2826         return res;
2827 }
2828
2829 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2830 {
2831         int cmd = 0;
2832         int retries = 0;
2833         int duration = 0;
2834         char newpassword[80] = "";
2835         char newpassword2[80] = "";
2836         char prefile[256]="";
2837         char buf[256];
2838         int bytes=0;
2839
2840         if (adsi_available(chan))
2841         {
2842                 bytes += adsi_logo(buf + bytes);
2843                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2844                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2845                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2846                 bytes += adsi_voice_mode(buf + bytes, 0);
2847                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2848         }
2849         while((cmd >= 0) && (cmd != 't')) {
2850                 if (cmd)
2851                         retries = 0;
2852                 switch (cmd) {
2853                 case '1':
2854                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2855                         cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2856                         break;
2857                 case '2': 
2858                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2859                         cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2860                         break;
2861                 case '3': 
2862                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2863                         cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2864                         break;
2865                 case '4':
2866                         newpassword[1] = '\0';
2867                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2868                         if (cmd < 0)
2869                                 break;
2870                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2871                                 break;
2872             }
2873                         newpassword2[1] = '\0';
2874                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2875                         if (cmd < 0)
2876                                 break;
2877
2878                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2879                                 break;
2880             }
2881                         if (strcmp(newpassword, newpassword2)) {
2882                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2883                                 cmd = play_and_wait(chan, "vm-mismatch");
2884                                 break;
2885                         }
2886                         vm_change_password(vmu,newpassword);
2887                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2888                         cmd = play_and_wait(chan,"vm-passchanged");
2889                         break;
2890                 case '*': 
2891                         cmd = 't';
2892                         break;
2893                 default: 
2894                         cmd = play_and_wait(chan,"vm-options");
2895                         if (!cmd)
2896                                 cmd = ast_waitfordigit(chan,6000);
2897                         if (!cmd)
2898                                 retries++;
2899                         if (retries > 3)
2900                                 cmd = 't';
2901                  }
2902         }
2903         if (cmd == 't')
2904                 cmd = 0;
2905         return cmd;
2906 }
2907
2908 static int vm_execmain(struct ast_channel *chan, void *data)
2909 {
2910         /* XXX This is, admittedly, some pretty horrendus code.  For some
2911            reason it just seemed a lot easier to do with GOTO's.  I feel
2912            like I'm back in my GWBASIC days. XXX */
2913         int res=-1;
2914         int valid = 0;
2915         int prefix = 0;
2916         int cmd=0;
2917         struct localuser *u;
2918         char prefixstr[80] ="";
2919         char empty[80] = "";
2920         int box;
2921         int useadsi = 0;
2922         int skipuser = 0;
2923         char tmp[256], *ext;
2924         char fmtc[256] = "";
2925         char password[80];
2926         struct vm_state vms;
2927         int logretries = 0;
2928         struct ast_vm_user *vmu = NULL, vmus;
2929         char *context=NULL;
2930         int silentexit = 0;
2931
2932         LOCAL_USER_ADD(u);
2933         memset(&vms, 0, sizeof(vms));
2934         strncpy(fmtc, vmfmts, sizeof(fmtc) - 1);
2935         if (chan->_state != AST_STATE_UP)
2936                 ast_answer(chan);
2937
2938         if (data && strlen(data)) {
2939                 strncpy(tmp, data, sizeof(tmp) - 1);
2940                 ext = tmp;
2941
2942                 switch (*ext) {
2943                         case 's':
2944                  /* We should skip the user's password */
2945                                 valid++;
2946                                 ext++;
2947                                 break;
2948                         case 'p':
2949                  /* We should prefix the mailbox with the supplied data */
2950                                 prefix++;
2951                                 ext++;
2952                                 break;
2953                 }
2954
2955                 context = strchr(ext, '@');
2956                 if (context) {
2957                         *context = '\0';
2958                         context++;
2959                 }
2960
2961                 if (prefix)
2962                         strncpy(prefixstr, ext, sizeof(prefixstr) - 1);
2963                 else
2964                         strncpy(vms.username, ext, sizeof(vms.username) - 1);
2965                 if (strlen(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
2966                         skipuser++;
2967                 else
2968                         valid = 0;
2969
2970         }
2971
2972         /* If ADSI is supported, setup login screen */
2973         adsi_begin(chan, &useadsi);
2974         if (!skipuser && useadsi)
2975                 adsi_login(chan);
2976         if (!skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
2977                 ast_log(LOG_WARNING, "Couldn't stream login file\n");
2978                 goto out;
2979         }
2980         
2981         /* Authenticate them and get their mailbox/password */
2982         
2983         while (!valid && (logretries < maxlogins)) {
2984                 /* Prompt for, and read in the username */
2985                 if (!skipuser && ast_readstring(chan, vms.username, sizeof(vms.username) - 1, 2000, 10000, "#") < 0) {
2986                         ast_log(LOG_WARNING, "Couldn't read username\n");
2987                         goto out;
2988                 }
2989                 if (!strlen(vms.username)) {
2990                         if (option_verbose > 2)
2991                                 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
2992                         res = 0;
2993                         goto out;
2994                 }
2995                 if (useadsi)
2996                         adsi_password(chan);
2997                 if (ast_streamfile(chan, "vm-password", chan->language)) {
2998                         ast_log(LOG_WARNING, "Unable to stream password file\n");
2999                         goto out;
3000                 }
3001                 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
3002                         ast_log(LOG_WARNING, "Unable to read password\n");
3003                         goto out;
3004                 }
3005                 if (prefix) {
3006                         char fullusername[80] = "";
3007                         strncpy(fullusername, prefixstr, sizeof(fullusername) - 1);
3008                         strncat(fullusername, vms.username, sizeof(fullusername) - 1);
3009                         strncpy(vms.username, fullusername, sizeof(vms.username) - 1);
3010                 }
3011                 if (!skipuser) 
3012                         vmu = find_user(&vmus, context, vms.username);
3013                 if (vmu && !strcmp(vmu->password, password)) 
3014                         valid++;
3015                 else {
3016                         if (option_verbose > 2)
3017                                 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, vms.username, context ? context : "<any>");
3018                         if (prefix)
3019                                 strncpy(vms.username, empty, sizeof(vms.username) -1);
3020                 }
3021                 if (!valid) {
3022                         if (useadsi)
3023                                 adsi_login(chan);
3024                         if (ast_streamfile(chan, "vm-incorrect", chan->language))
3025                                 break;
3026                 }
3027                 logretries++;
3028         }
3029         if (!valid && (logretries >= maxlogins)) {
3030                 ast_stopstream(chan);
3031                 res = play_and_wait(chan, "vm-goodbye");
3032                 if (res > 0)
3033                         res = 0;
3034         }
3035
3036         if (valid) {
3037                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context);
3038                 mkdir(vms.curdir, 0700);
3039                 snprintf(vms.curdir, sizeof(vms.curdir), "%s/voicemail/%s/%s", (char *)ast_config_AST_SPOOL_DIR, vmu->context, vms.username);
3040                 mkdir(vms.curdir, 0700);
3041                 /* Retrieve old and new message counts */
3042                 open_mailbox(&vms, vmu, 1);
3043                 vms.oldmessages = vms.lastmsg + 1;
3044                 /* Start in INBOX */
3045                 open_mailbox(&vms, vmu, 0);
3046                 vms.newmessages = vms.lastmsg + 1;
3047                 
3048
3049                 /* Select proper mailbox FIRST!! */
3050                 if (!vms.newmessages && vms.oldmessages) {
3051                         /* If we only have old messages start here */
3052                         open_mailbox(&vms, vmu, 1);
3053                 }
3054
3055                 if (useadsi)
3056                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
3057                 res = 0;
3058                 cmd = vm_intro(chan, &vms);
3059                 vms.repeats = 0;
3060                 vms.starting = 1;
3061                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
3062                         /* Run main menu */
3063                         switch(cmd) {
3064                         case '1':
3065                                 vms.curmsg = 0;
3066                                 /* Fall through */
3067                         case '5':
3068                                 if (vms.lastmsg > -1) {
3069                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
3070                                 } else {
3071                                         cmd = play_and_wait(chan, "vm-youhave");
3072                                         if (!cmd) 
3073                                                 cmd = play_and_wait(chan, "vm-no");
3074                                         if (!cmd) {
3075                                                 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", vms.curbox);
3076                                                 cmd = play_and_wait(chan, vms.fn);
3077                                         }
3078                                         if (!cmd)
3079                                                 cmd = play_and_wait(chan, "vm-messages");
3080                                 }
3081                                 break;
3082                         case '2': /* Change folders */
3083                                 if (useadsi)
3084                                         adsi_folders(chan, 0, "Change to folder...");
3085                                 cmd = get_folder2(chan, "vm-changeto", 0);
3086                                 if (cmd == '#') {
3087                                         cmd = 0;
3088                                 } else if (cmd > 0) {
3089                                         cmd = cmd - '0';
3090                                         close_mailbox(&vms, vmu);
3091                                         open_mailbox(&vms, vmu, cmd);
3092                                         cmd = 0;
3093                                 }
3094                                 if (useadsi)
3095                                         adsi_status2(chan, vms.curbox, vms.lastmsg + 1);
3096                                 if (!cmd)
3097                                         cmd = play_and_wait(chan, vms.vmbox);
3098                                 if (!cmd)
3099                                         cmd = play_and_wait(chan, "vm-messages");
3100                                 vms.starting = 1;
3101                                 break;
3102                         case '3': /* Advanced options */
3103                                 cmd = 0;
3104                                 vms.repeats = 0;
3105                                 while((cmd > -1) && (cmd != 't') && (cmd != '#')) {
3106                                         switch(cmd) {
3107                                         case '1': /* Reply */
3108                                                 if(vms.lastmsg > -1)
3109                                                         cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1);
3110                                                 else
3111                                                         cmd = play_and_wait(chan, "vm-sorry");
3112                                                 cmd = 't';
3113                                                 break;
3114                                         case '2': /* Callback */
3115                                                 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
3116                                                 if (strlen(vmu->callback) && vms.lastmsg > -1) {
3117                                                         cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2);
3118                                                         if (cmd == 9) {
3119                                                                 silentexit = 1;
3120                                                                 goto out;
3121                                                         }
3122                                                 }
3123                                                 else 
3124                                                         cmd = play_and_wait(chan, "vm-sorry");
3125                                                 cmd = 't';
3126                                                 break;
3127                                         case '3': /* Envelope */
3128                                                 if(vms.lastmsg > -1)
3129                                                         cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3);
3130                                                 else
3131                                                         cmd = play_and_wait(chan, "vm-sorry");
3132                                                 cmd = 't';
3133                                                 break;
3134                                         case '4': /* Dialout */
3135                                                 if (strlen(vmu->dialout)) {
3136                                                         cmd = dialout(chan, vmu, NULL, vmu->dialout);
3137                                                         if (cmd == 9) {
3138                                                                 silentexit = 1;
3139                                                                 goto out;
3140                                                         }
3141                                                 }
3142                                                 else 
3143                                                         cmd = play_and_wait(chan, "vm-sorry");
3144                                                 cmd = 't';
3145                                                 break;
3146
3147                                         case '*': /* Return to main menu */
3148                                                 cmd = 't';
3149                                                 break;
3150
3151                                         default:
3152                                                 cmd = 0;
3153                                                 if (!vms.starting) {
3154                                                         cmd = play_and_wait(chan, "vm-toreply");
3155                                                 }
3156                                                 if (strlen(vmu->callback) && !vms.starting && !cmd) {
3157                                                         cmd = play_and_wait(chan, "vm-tocallback");
3158                                                 }
3159
3160                                                 if (!cmd && !vms.starting) {
3161                                                         cmd = play_and_wait(chan, "vm-tohearenv");
3162                                                 }
3163                                                 if (strlen(vmu->dialout) && !cmd) {
3164                                                         cmd = play_and_wait(chan, "vm-tomakecall");
3165                                                 }
3166                                                 if (!cmd)
3167                                                         cmd = play_and_wait(chan, "vm-starmain");
3168                                                 if (!cmd)
3169                                                         cmd = ast_waitfordigit(chan,6000);
3170                                                 if (!cmd)
3171                                                         vms.repeats++;
3172                                                 if (vms.repeats > 3)
3173                                                         cmd = 't';
3174                                         }
3175                                 }
3176                                 if (cmd == 't') {
3177                                         cmd = 0;
3178                                         vms.repeats = 0;
3179                                 }
3180                                 break;
3181
3182
3183
3184
3185
3186                         case '4':
3187                                 if (vms.curmsg) {
3188                                         vms.curmsg--;
3189                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
3190                                 } else {
3191                                         cmd = play_and_wait(chan, "vm-nomore");
3192                                 }
3193                                 break;
3194                         case '6':
3195                                 if (vms.curmsg < vms.lastmsg) {
3196                                         vms.curmsg++;
3197                                         cmd = play_message(chan, vmu, &vms, vms.curmsg);
3198                                 } else {
3199                                         cmd = play_and_wait(chan, "vm-nomore");
3200                                 }
3201                                 break;
3202                         case '7':
3203                                 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
3204                                 if (useadsi)
3205                                         adsi_delete(chan, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg]);
3206                                 if (vms.deleted[vms.curmsg]) 
3207                                         cmd = play_and_wait(chan, "vm-deleted");
3208                                 else
3209                                         cmd = play_and_wait(chan, "vm-undeleted");
3210                                 break;
3211                         case '8':
3212                                 if(vms.lastmsg > -1)
3213                                         cmd = forward_message(chan, context, vms.curdir, vms.curmsg, vmu, vmfmts);
3214                                 else
3215                                         cmd = play_and_wait(chan, "vm-nomore");
3216                                 break;
3217                         case '9':
3218                                 if (useadsi)
3219                                         adsi_folders(chan, 1, "Save to folder...");
3220                                 cmd = get_folder2(chan, "vm-savefolder", 1);
3221                                 box = 0;        /* Shut up compiler */
3222                                 if (cmd == '#') {
3223                                         cmd = 0;
3224                                         break;
3225                                 } else if (cmd > 0) {
3226                                         box = cmd = cmd - '0';
3227                                         cmd = save_to_folder(vms.curdir, vms.curmsg, vmu->context, vms.username, cmd);
3228                                         vms.deleted[vms.curmsg]=1;
3229                                 }
3230                                 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
3231                                 if (useadsi)
3232                                         adsi_message(chan, vms.curbox, vms.curmsg, vms.lastmsg, vms.deleted[vms.curmsg], vms.fn);
3233                                 if (!cmd)
3234                                         cmd = play_and_wait(chan, "vm-message");
3235                                 if (!cmd)
3236                                         cmd = say_and_wait(chan, vms.curmsg + 1);
3237                                 if (!cmd)
3238                                         cmd = play_and_wait(chan, "vm-savedto");
3239                                 if (!cmd) {
3240                                         snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
3241                                         cmd = play_and_wait(chan, vms.fn);
3242                                 }
3243                                 if (!cmd)
3244                                         cmd = play_and_wait(chan, "vm-messages");
3245                                 break;
3246                         case '*':
3247                                 if (!vms.starting) {
3248                                         cmd = play_and_wait(chan, "vm-onefor");
3249                                         if (!cmd)
3250                                                 cmd = play_and_wait(chan, vms.vmbox);
3251                                         if (!cmd)
3252                                                 cmd = play_and_wait(chan, "vm-messages");
3253                                         if (!cmd)
3254                                                 cmd = play_and_wait(chan, "vm-opts");
3255                                 } else
3256                                         cmd = 0;
3257                                 break;
3258                         case '0':
3259                                 cmd = vm_options(chan, vmu, &vms, vmfmts);
3260                                 if (useadsi)
3261                                         adsi_status(chan, vms.newmessages, vms.oldmessages, vms.lastmsg);
3262                                 break;
3263                         default:        /* Nothing */
3264                                 cmd = vm_instructions(chan, &vms);
3265                                 break;
3266                         }
3267                 }
3268                 if ((cmd == 't') || (cmd == '#')) {
3269                         /* Timeout */
3270                         res = 0;
3271                 } else {
3272                         /* Hangup */
3273                         res = -1;
3274                 }
3275         }
3276 out:
3277         if (res > -1) {
3278                 ast_stopstream(chan);
3279                 adsi_goodbye(chan);
3280                 if(valid) {
3281                         if (silentexit)
3282                                 res = play_and_wait(chan, "vm-dialout");
3283                         else 
3284                                 res = play_and_wait(chan, "vm-goodbye");
3285                         if (res > 0)
3286                                 res = 0;
3287                 }
3288