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