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