Make playback of envelope a configurable option (bug #1683)
[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                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1581                 other than the operator -- an automated attendant or mailbox login for example */
1582                 if (res == '*') {
1583                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1584                         if (strlen(chan->macrocontext))
1585                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1586                         chan->priority = 0;
1587                         free_user(vmu);
1588                         return 0;
1589                 }
1590                 /* Check for a '0' here */
1591                 if (res == '0') {
1592                 transfer:
1593                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1594                         if (strlen(chan->macrocontext))
1595                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1596                         chan->priority = 0;
1597                         free_user(vmu);
1598                         return 0;
1599                 }
1600                 if (res >= 0) {
1601                         /* Unless we're *really* silent, try to send the beep */
1602                         res = ast_streamfile(chan, "beep", chan->language);
1603                         if (!res)
1604                                 res = ast_waitstream(chan, "");
1605                 }
1606                 if (res < 0) {
1607                         free_user(vmu);
1608                         return -1;
1609                 }
1610                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1611                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1612                 if (strlen(fmt)) {
1613                         msgnum = 0;
1614                         do {
1615                                 make_file(fn, sizeof(fn), dir, msgnum);
1616                                 snprintf(comment, sizeof(comment), "Voicemail from %s to %s (%s) on %s\n",
1617                                                                         (chan->callerid ? chan->callerid : "Unknown"), 
1618                                                                         vmu->fullname, ext, chan->name);
1619                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1620                                         break;
1621                                 msgnum++;
1622                         } while(msgnum < MAXMSG);
1623                         if (msgnum < MAXMSG) {
1624                                 /* Store information */
1625                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1626                                 txt = fopen(txtfile, "w+");
1627                                 if (txt) {
1628                                         get_date(date, sizeof(date));
1629                                         fprintf(txt, 
1630 ";\n"
1631 "; Message Information file\n"
1632 ";\n"
1633 "[message]\n"
1634 "origmailbox=%s\n"
1635 "context=%s\n"
1636 "macrocontext=%s\n"
1637 "exten=%s\n"
1638 "priority=%d\n"
1639 "callerchan=%s\n"
1640 "callerid=%s\n"
1641 "origdate=%s\n"
1642 "origtime=%ld\n",
1643         ext,
1644         chan->context,
1645         chan->macrocontext, 
1646         chan->exten,
1647         chan->priority,
1648         chan->name,
1649         chan->callerid ? chan->callerid : "Unknown",
1650         date, (long)time(NULL));
1651                                         fclose(txt);
1652                                 } else
1653                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1654                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1655                                 if (res == '0')
1656                                         goto transfer;
1657                                 if (res > 0)
1658                                         res = 0;
1659                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1660                                 if (fd > -1) {
1661                                         txt = fdopen(fd, "a");
1662                                         if (txt) {
1663                                                 fprintf(txt, "duration=%d\n", duration);
1664                                                 fclose(txt);
1665                                         } else
1666                                                 close(fd);
1667                                 }
1668                                 if (duration < vmminmessage) {
1669                                         vm_delete(fn);
1670                                         goto leave_vm_out;
1671                                 }
1672                                 stringp = fmt;
1673                                 strsep(&stringp, "|");
1674                                 /* Send e-mail if applicable */
1675                                 if (strlen(vmu->email)) {
1676                                         int attach_user_voicemail = attach_voicemail;
1677                                         char *myserveremail = serveremail;
1678                                         if (vmu->attach > -1)
1679                                                 attach_user_voicemail = vmu->attach;
1680                                         if (strlen(vmu->serveremail))
1681                                                 myserveremail = vmu->serveremail;
1682                                                 sendmail(myserveremail, vmu, msgnum, ext, chan->callerid, fn, fmt, duration, attach_user_voicemail);
1683                                 }
1684                                 if (strlen(vmu->pager)) {
1685                                         char *myserveremail = serveremail;
1686                                         if (strlen(vmu->serveremail))
1687                                                 myserveremail = vmu->serveremail;
1688                                         sendpage(myserveremail, vmu->pager, msgnum, ext, chan->callerid, duration, vmu);
1689                                 }
1690                         } else {
1691                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1692                                 if (!res)
1693                                         res = ast_waitstream(chan, "");
1694                                 ast_log(LOG_WARNING, "No more messages possible\n");
1695                         }
1696                 } else
1697                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");                                      
1698 leave_vm_out:
1699                 free_user(vmu);
1700         } else {
1701                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1702                         /*Send the call to n+101 priority, where n is the current priority*/
1703                         if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1704                                 chan->priority+=100;
1705         }
1706         /* Leave voicemail for someone */
1707         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
1708
1709         /* If an external program is specified to be run after leaving a voicemail */
1710         run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
1711
1712         return res;
1713 }
1714
1715 static char *mbox(int id)
1716 {
1717         switch(id) {
1718         case 0:
1719                 return "INBOX";
1720         case 1:
1721                 return "Old";
1722         case 2:
1723                 return "Work";
1724         case 3:
1725                 return "Family";
1726         case 4:
1727                 return "Friends";
1728         case 5:
1729                 return "Cust1";
1730         case 6:
1731                 return "Cust2";
1732         case 7:
1733                 return "Cust3";
1734         case 8:
1735                 return "Cust4";
1736         case 9:
1737                 return "Cust5";
1738         default:
1739                 return "Unknown";
1740         }
1741 }
1742
1743 static int count_messages(char *dir)
1744 {
1745         int x;
1746         char fn[256];
1747         for (x=0;x<MAXMSG;x++) {
1748                 make_file(fn, sizeof(fn), dir, x);
1749                 if (ast_fileexists(fn, NULL, NULL) < 1)
1750                         break;
1751         }
1752         return x;
1753 }
1754
1755 static int say_and_wait(struct ast_channel *chan, int num)
1756 {
1757         int d;
1758         d = ast_say_number(chan, num, AST_DIGIT_ANY, chan->language, (char *) NULL);
1759         return d;
1760 }
1761
1762 static int copy(char *infile, char *outfile)
1763 {
1764         int ifd;
1765         int ofd;
1766         int res;
1767         int len;
1768         char buf[4096];
1769         if ((ifd = open(infile, O_RDONLY)) < 0) {
1770                 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1771                 return -1;
1772         }
1773         if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1774                 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1775                 close(ifd);
1776                 return -1;
1777         }
1778         do {
1779                 len = read(ifd, buf, sizeof(buf));
1780                 if (len < 0) {
1781                         ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1782                         close(ifd);
1783                         close(ofd);
1784                         unlink(outfile);
1785                 }
1786                 if (len) {
1787                         res = write(ofd, buf, len);
1788                         if (res != len) {
1789                                 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1790                                 close(ifd);
1791                                 close(ofd);
1792                                 unlink(outfile);
1793                         }
1794                 }
1795         } while(len);
1796         close(ifd);
1797         close(ofd);
1798         return 0;
1799 }
1800
1801 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1802 {
1803         char sfn[256];
1804         char dfn[256];
1805         char ddir[256];
1806         char txt[256];
1807         char ntxt[256];
1808         char *dbox = mbox(box);
1809         int x;
1810         make_file(sfn, sizeof(sfn), dir, msg);
1811         make_dir(ddir, sizeof(ddir), context, username, dbox);
1812         mkdir(ddir, 0700);
1813         for (x=0;x<MAXMSG;x++) {
1814                 make_file(dfn, sizeof(dfn), ddir, x);
1815                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1816                         break;
1817         }
1818         if (x >= MAXMSG)
1819                 return -1;
1820         ast_filecopy(sfn, dfn, NULL);
1821         if (strcmp(sfn, dfn)) {
1822                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1823                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1824                 copy(txt, ntxt);
1825         }
1826         return 0;
1827 }
1828
1829 static int adsi_logo(unsigned char *buf)
1830 {
1831         int bytes = 0;
1832         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1833         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1834         return bytes;
1835 }
1836
1837 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1838 {
1839         char buf[256];
1840         int bytes=0;
1841         int x;
1842         char num[5];
1843
1844         *useadsi = 0;
1845         bytes += adsi_data_mode(buf + bytes);
1846         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1847
1848         bytes = 0;
1849         bytes += adsi_logo(buf);
1850         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1851 #ifdef DISPLAY
1852         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1853 #endif
1854         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1855         bytes += adsi_data_mode(buf + bytes);
1856         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1857
1858         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1859                 bytes = 0;
1860                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1861                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1862                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1863                 bytes += adsi_voice_mode(buf + bytes, 0);
1864                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1865                 return 0;
1866         }
1867
1868 #ifdef DISPLAY
1869         /* Add a dot */
1870         bytes = 0;
1871         bytes += adsi_logo(buf);
1872         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1873         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1874         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1875         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1876 #endif
1877         bytes = 0;
1878         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1879         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1880         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1881         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1882         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1883         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1884         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1885
1886 #ifdef DISPLAY
1887         /* Add another dot */
1888         bytes = 0;
1889         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
1890       bytes += adsi_voice_mode(buf + bytes, 0);
1891
1892         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1893         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1894 #endif
1895
1896         bytes = 0;
1897         /* These buttons we load but don't use yet */
1898         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
1899         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
1900         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
1901         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
1902         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
1903         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
1904         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1905
1906 #ifdef DISPLAY
1907         /* Add another dot */
1908         bytes = 0;
1909         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
1910         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1911         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1912 #endif
1913
1914         bytes = 0;
1915         for (x=0;x<5;x++) {
1916                 snprintf(num, sizeof(num), "%d", x);
1917                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
1918         }
1919         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
1920         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1921
1922 #ifdef DISPLAY
1923         /* Add another dot */
1924         bytes = 0;
1925         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
1926         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1927         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1928 #endif
1929
1930         if (adsi_end_download(chan)) {
1931                 bytes = 0;
1932                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
1933                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1934                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1935                 bytes += adsi_voice_mode(buf + bytes, 0);
1936                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1937                 return 0;
1938         }
1939         bytes = 0;
1940         bytes += adsi_download_disconnect(buf + bytes);
1941         bytes += adsi_voice_mode(buf + bytes, 0);
1942         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
1943
1944         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
1945
1946 #ifdef DISPLAY
1947         /* Add last dot */
1948         bytes = 0;
1949         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
1950         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1951 #endif
1952         ast_log(LOG_DEBUG, "Restarting session...\n");
1953
1954         bytes = 0;
1955         /* Load the session now */
1956         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
1957                 *useadsi = 1;
1958                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
1959         } else
1960                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
1961
1962         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1963         return 0;
1964 }
1965
1966 static void adsi_begin(struct ast_channel *chan, int *useadsi)
1967 {
1968         int x;
1969         if (!adsi_available(chan))
1970           return;
1971         x = adsi_load_session(chan, adapp, adver, 1);
1972         if (x < 0)
1973                 return;
1974         if (!x) {
1975                 if (adsi_load_vmail(chan, useadsi)) {
1976                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
1977                         return;
1978                 }
1979         } else
1980                 *useadsi = 1;
1981 }
1982
1983 static void adsi_login(struct ast_channel *chan)
1984 {
1985         char buf[256];
1986         int bytes=0;
1987         unsigned char keys[8];
1988         int x;
1989         if (!adsi_available(chan))
1990                 return;
1991
1992         for (x=0;x<8;x++)
1993                 keys[x] = 0;
1994         /* Set one key for next */
1995         keys[3] = ADSI_KEY_APPS + 3;
1996
1997         bytes += adsi_logo(buf + bytes);
1998         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
1999         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2000         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2001         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2002         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2003         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2004         bytes += adsi_set_keys(buf + bytes, keys);
2005         bytes += adsi_voice_mode(buf + bytes, 0);
2006         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2007 }
2008
2009 static void adsi_password(struct ast_channel *chan)
2010 {
2011         char buf[256];
2012         int bytes=0;
2013         unsigned char keys[8];
2014         int x;
2015         if (!adsi_available(chan))
2016                 return;
2017
2018         for (x=0;x<8;x++)
2019                 keys[x] = 0;
2020         /* Set one key for next */
2021         keys[3] = ADSI_KEY_APPS + 3;
2022
2023         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2024         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2025         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2026         bytes += adsi_set_keys(buf + bytes, keys);
2027         bytes += adsi_voice_mode(buf + bytes, 0);
2028         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2029 }
2030
2031 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2032 {
2033         char buf[256];
2034         int bytes=0;
2035         unsigned char keys[8];
2036         int x,y;
2037
2038         if (!adsi_available(chan))
2039                 return;
2040
2041         for (x=0;x<5;x++) {
2042                 y = ADSI_KEY_APPS + 12 + start + x;
2043                 if (y > ADSI_KEY_APPS + 12 + 4)
2044                         y = 0;
2045                 keys[x] = ADSI_KEY_SKT | y;
2046         }
2047         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2048         keys[6] = 0;
2049         keys[7] = 0;
2050
2051         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2052         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2053         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2054         bytes += adsi_set_keys(buf + bytes, keys);
2055         bytes += adsi_voice_mode(buf + bytes, 0);
2056
2057         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2058 }
2059
2060 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2061 {
2062         int bytes=0;
2063         char buf[256], buf1[256], buf2[256];
2064         char fn2[256];
2065
2066         char cid[256]="";
2067         char *val;
2068         char *name, *num;
2069         char datetime[21]="";
2070         FILE *f;
2071
2072         unsigned char keys[8];
2073
2074         int x;
2075
2076         if (!adsi_available(chan))
2077                 return;
2078
2079         /* Retrieve important info */
2080         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2081         f = fopen(fn2, "r");
2082         if (f) {
2083                 while(!feof(f)) {       
2084                         fgets(buf, sizeof(buf), f);
2085                         if (!feof(f)) {
2086                                 char *stringp=NULL;
2087                                 stringp=buf;
2088                                 strsep(&stringp, "=");
2089                                 val = strsep(&stringp, "=");
2090                                 if (val && strlen(val)) {
2091                                         if (!strcmp(buf, "callerid"))
2092                                                 strncpy(cid, val, sizeof(cid) - 1);
2093                                         if (!strcmp(buf, "origdate"))
2094                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2095                                 }
2096                         }
2097                 }
2098                 fclose(f);
2099         }
2100         /* New meaning for keys */
2101         for (x=0;x<5;x++)
2102                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2103         keys[6] = 0x0;
2104         keys[7] = 0x0;
2105
2106         if (!msg) {
2107                 /* No prev key, provide "Folder" instead */
2108                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2109         }
2110         if (msg >= last) {
2111                 /* If last message ... */
2112                 if (msg) {
2113                         /* but not only message, provide "Folder" instead */
2114                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2115       bytes += adsi_voice_mode(buf + bytes, 0);
2116
2117                 } else {
2118                         /* Otherwise if only message, leave blank */
2119                         keys[3] = 1;
2120                 }
2121         }
2122
2123         if (strlen(cid)) {
2124                 ast_callerid_parse(cid, &name, &num);
2125                 if (!name)
2126                         name = num;
2127         } else
2128                 name = "Unknown Caller";
2129
2130         /* If deleted, show "undeleted" */
2131
2132         if (deleted)
2133                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2134
2135         /* Except "Exit" */
2136         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2137         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2138                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2139         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2140
2141         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2142         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2143         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2144         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2145         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2146         bytes += adsi_set_keys(buf + bytes, keys);
2147         bytes += adsi_voice_mode(buf + bytes, 0);
2148
2149         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2150 }
2151
2152 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2153 {
2154         int bytes=0;
2155         char buf[256];
2156         unsigned char keys[8];
2157
2158         int x;
2159
2160         if (!adsi_available(chan))
2161                 return;
2162
2163         /* New meaning for keys */
2164         for (x=0;x<5;x++)
2165                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2166
2167         keys[6] = 0x0;
2168         keys[7] = 0x0;
2169
2170         if (!msg) {
2171                 /* No prev key, provide "Folder" instead */
2172                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2173         }
2174         if (msg >= last) {
2175                 /* If last message ... */
2176                 if (msg) {
2177                         /* but not only message, provide "Folder" instead */
2178                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2179                 } else {
2180                         /* Otherwise if only message, leave blank */
2181                         keys[3] = 1;
2182                 }
2183         }
2184
2185         /* If deleted, show "undeleted" */
2186         if (deleted) 
2187                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2188
2189         /* Except "Exit" */
2190         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2191         bytes += adsi_set_keys(buf + bytes, keys);
2192         bytes += adsi_voice_mode(buf + bytes, 0);
2193
2194         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2195 }
2196
2197 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2198 {
2199         char buf[256], buf1[256], buf2[256];
2200         int bytes=0;
2201         unsigned char keys[8];
2202         int x;
2203
2204         char *newm = (new == 1) ? "message" : "messages";
2205         char *oldm = (old == 1) ? "message" : "messages";
2206         if (!adsi_available(chan))
2207                 return;
2208         if (new) {
2209                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2210                 if (old) {
2211                         strcat(buf1, " and");
2212                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2213                 } else {
2214                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2215                 }
2216         } else if (old) {
2217                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2218                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2219         } else {
2220                 strcpy(buf1, "You have no messages.");
2221                 strcpy(buf2, " ");
2222         }
2223         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2224         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2225         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2226
2227         for (x=0;x<6;x++)
2228                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2229         keys[6] = 0;
2230         keys[7] = 0;
2231
2232         /* Don't let them listen if there are none */
2233         if (lastmsg < 0)
2234                 keys[0] = 1;
2235         bytes += adsi_set_keys(buf + bytes, keys);
2236
2237         bytes += adsi_voice_mode(buf + bytes, 0);
2238
2239         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2240 }
2241
2242 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2243 {
2244         char buf[256], buf1[256], buf2[256];
2245         int bytes=0;
2246         unsigned char keys[8];
2247         int x;
2248
2249         char *mess = (messages == 1) ? "message" : "messages";
2250
2251         if (!adsi_available(chan))
2252                 return;
2253
2254         /* Original command keys */
2255         for (x=0;x<6;x++)
2256                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2257
2258         keys[6] = 0;
2259         keys[7] = 0;
2260
2261         if (messages < 1)
2262                 keys[0] = 0;
2263
2264         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2265                         strcasecmp(folder, "INBOX") ? " folder" : "");
2266
2267         if (messages)
2268                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2269         else
2270                 strcpy(buf2, "no messages.");
2271         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2272         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2273         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2274         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2275         bytes += adsi_set_keys(buf + bytes, keys);
2276
2277         bytes += adsi_voice_mode(buf + bytes, 0);
2278
2279         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2280         
2281 }
2282
2283 /*
2284 static void adsi_clear(struct ast_channel *chan)
2285 {
2286         char buf[256];
2287         int bytes=0;
2288         if (!adsi_available(chan))
2289                 return;
2290         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2291         bytes += adsi_voice_mode(buf + bytes, 0);
2292
2293         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2294 }
2295 */
2296
2297 static void adsi_goodbye(struct ast_channel *chan)
2298 {
2299         char buf[256];
2300         int bytes=0;
2301
2302         if (!adsi_available(chan))
2303                 return;
2304         bytes += adsi_logo(buf + bytes);
2305         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2306         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2307         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2308         bytes += adsi_voice_mode(buf + bytes, 0);
2309
2310         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2311 }
2312
2313 static int get_folder(struct ast_channel *chan, int start)
2314 {
2315         int x;
2316         int d;
2317         char fn[256];
2318         d = play_and_wait(chan, "vm-press");
2319         if (d)
2320                 return d;
2321         for (x = start; x< 5; x++) {
2322                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2323                         return d;
2324                 d = play_and_wait(chan, "vm-for");
2325                 if (d)
2326                         return d;
2327                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));
2328                 d = play_and_wait(chan, fn);
2329                 if (d)
2330                         return d;
2331                 d = play_and_wait(chan, "vm-messages");
2332                 if (d)
2333                         return d;
2334                 d = ast_waitfordigit(chan, 500);
2335                 if (d)
2336                         return d;
2337         }
2338         d = play_and_wait(chan, "vm-tocancel");
2339         if (d)
2340                 return d;
2341         d = ast_waitfordigit(chan, 4000);
2342         return d;
2343 }
2344
2345 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2346 {
2347         int res = 0;
2348         res = play_and_wait(chan, fn);
2349         while (((res < '0') || (res > '9')) &&
2350                         (res != '#') && (res >= 0)) {
2351                 res = get_folder(chan, 0);
2352         }
2353         return res;
2354 }
2355
2356 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2357 {
2358         int cmd = 0;
2359         int retries = 0;
2360         int duration = 0;
2361
2362         while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2363                 if (cmd)
2364                         retries = 0;
2365                 switch (cmd) {
2366                 case '1': 
2367                         /* prepend a message to the current message and return */
2368                 {
2369                         char file[200];
2370                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2371                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2372                         break;
2373                 }
2374                 case '2': 
2375                         cmd = 't';
2376                         break;
2377                 case '*':
2378                         cmd = '*';
2379                         break;
2380                 default: 
2381                         cmd = play_and_wait(chan,"vm-forwardoptions");
2382                         if (!cmd)
2383                                 cmd = play_and_wait(chan,"vm-starmain");
2384                         if (!cmd)
2385                                 cmd = ast_waitfordigit(chan,6000);
2386                         if (!cmd)
2387                                 retries++;
2388                         if (retries > 3)
2389                                 cmd = 't';
2390                  }
2391         }
2392         if (cmd == 't')
2393                 cmd = 0;
2394         return cmd;
2395 }
2396
2397 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2398 {
2399         char username[70];
2400         char sys[256];
2401         char todir[256];
2402         int todircount=0;
2403         int duration;
2404         struct ast_config *mif;
2405         char miffile[256];
2406         char fn[256];
2407         char callerid[512];
2408         char ext_context[256]="";
2409         int res = 0, cmd = 0;
2410         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2411         char tmp[256];
2412         char *stringp, *s;
2413         int saved_messages = 0, found = 0;
2414         int valid_extensions = 0;
2415         while (!res && !valid_extensions) {
2416                 res = ast_streamfile(chan, "vm-extension", chan->language);
2417                 if (res)
2418                         break;
2419                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2420                         break;
2421                 /* start all over if no username */
2422                 if (!strlen(username))
2423                         continue;
2424                 stringp = username;
2425                 s = strsep(&stringp, "*");
2426                 /* start optimistic */
2427                 valid_extensions = 1;
2428                 while (s) {
2429                         /* find_user is going to malloc since we have a NULL as first argument */
2430                         if ((receiver = find_user(NULL, context, s))) {
2431                                 if (!extensions)
2432                                         vmtmp = extensions = receiver;
2433                                 else {
2434                                         vmtmp->next = receiver;
2435                                         vmtmp = receiver;
2436                                 }
2437                                 found++;
2438                         } else {
2439                                 valid_extensions = 0;
2440                                 break;
2441                         }
2442                         s = strsep(&stringp, "*");
2443                 }
2444                 /* break from the loop of reading the extensions */
2445                 if (valid_extensions)
2446                         break;
2447                 /* invalid extension, try again */
2448                 res = play_and_wait(chan, "pbx-invalid");
2449         }
2450         /* check if we're clear to proceed */
2451         if (!extensions || !valid_extensions)
2452                 return res;
2453         vmtmp = extensions;
2454         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2455         if (!cmd) {
2456                 while(!res && vmtmp) {
2457                         /* if (play_and_wait(chan, "vm-savedto"))
2458                                 break;
2459                         */
2460                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2461                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2462                         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2463                         ast_log(LOG_DEBUG, sys);
2464                         ast_safe_system(sys);
2465         
2466                         todircount = count_messages(todir);
2467                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2468                         stringp = tmp;
2469                         while((s = strsep(&stringp, "|"))) {
2470                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2471                                 if (!strcasecmp(s, "wav49"))
2472                                         s = "WAV";
2473                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2474                                 ast_log(LOG_DEBUG, sys);
2475                                 ast_safe_system(sys);
2476                         }
2477                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2478                         ast_log(LOG_DEBUG, sys);
2479                         ast_safe_system(sys);
2480                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2481         
2482                         /* load the information on the source message so we can send an e-mail like a new message */
2483                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2484                         if ((mif=ast_load(miffile))) {
2485         
2486                                 /* set callerid and duration variables */
2487                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2488                                 s = ast_variable_retrieve(mif, NULL, "duration");
2489                                 if (s)
2490                                         duration = atoi(s);
2491                                 else
2492                                         duration = 0;
2493                                 if (strlen(vmtmp->email)) {
2494                                         int attach_user_voicemail = attach_voicemail;
2495                                         char *myserveremail = serveremail;
2496                                         if (vmtmp->attach > -1)
2497                                                 attach_user_voicemail = vmtmp->attach;
2498                                         if (strlen(vmtmp->serveremail))
2499                                                 myserveremail = vmtmp->serveremail;
2500                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2501                                 }
2502                              
2503                                 if (strlen(vmtmp->pager)) {
2504                                         char *myserveremail = serveremail;
2505                                         if (strlen(vmtmp->serveremail))
2506                                                 myserveremail = vmtmp->serveremail;
2507                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2508                                 }
2509                                   
2510                                 ast_destroy(mif); /* or here */
2511                         }
2512                         /* Leave voicemail for someone */
2513                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2514                         run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2515         
2516                         saved_messages++;
2517                         vmfree = vmtmp;
2518                         vmtmp = vmtmp->next;
2519                         free_user(vmfree);
2520                 }
2521                 if (saved_messages > 0) {
2522                         /* give confirmatopm that the message was saved */
2523                         /* commented out since we can't forward batches yet
2524                         if (saved_messages == 1)
2525                                 res = play_and_wait(chan, "vm-message");
2526                         else
2527                                 res = play_and_wait(chan, "vm-messages");
2528                         if (!res)
2529                                 res = play_and_wait(chan, "vm-saved"); */
2530                         if (!res)
2531                                 res = play_and_wait(chan, "vm-msgsaved");
2532                 }
2533         }
2534         return res ? res : cmd;
2535 }
2536
2537
2538 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2539 {
2540         int res;
2541         if ((res = ast_streamfile(chan, file, chan->language))) 
2542                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2543         if (!res)
2544                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2545         return res;
2546 }
2547
2548 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2549 {
2550         int res;
2551         if ((res = ast_streamfile(chan, file, chan->language)))
2552                 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
2553         if (!res)
2554                 res = ast_waitstream_fr(chan, AST_DIGIT_ANY, "#", "*",skipms);
2555         return res;
2556 }
2557
2558 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2559 {
2560         int res = 0;
2561         struct vm_zone *the_zone = NULL;
2562         time_t t;
2563         long tin;
2564
2565         if (sscanf(origtime,"%ld",&tin) < 1) {
2566                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2567                 return 0;
2568         }
2569         t = tin;
2570
2571         /* Does this user have a timezone specified? */
2572         if (strlen(vmu->zonetag)) {
2573                 /* Find the zone in the list */
2574                 struct vm_zone *z;
2575                 z = zones;
2576                 while (z) {
2577                         if (!strcmp(z->name, vmu->zonetag)) {
2578                                 the_zone = z;
2579                                 break;
2580                         }
2581                         z = z->next;
2582                 }
2583         }
2584
2585 /* No internal variable parsing for now, so we'll comment it out for the time being */
2586 #if 0
2587         /* Set the DIFF_* variables */
2588         localtime_r(&t, &time_now);
2589         gettimeofday(&tv_now,NULL);
2590         tnow = tv_now.tv_sec;
2591         localtime_r(&tnow,&time_then);
2592
2593         /* Day difference */
2594         if (time_now.tm_year == time_then.tm_year)
2595                 sprintf(temp,"%d",time_now.tm_yday);
2596         else
2597                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2598         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2599
2600         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2601 #endif
2602         if (the_zone)
2603                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2604         else
2605                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2606 #if 0
2607         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2608 #endif
2609         return res;
2610 }
2611
2612
2613
2614 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2615 {
2616         int res = 0;
2617         int i;
2618         char *callerid, *name;
2619         char prefile[256]="";
2620         
2621
2622         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2623         /* 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 */
2624         if((cid == NULL)||(context == NULL))
2625                 return res;
2626
2627         /* Strip off caller ID number from name */
2628         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2629         ast_callerid_parse(cid, &name, &callerid);
2630         if((callerid != NULL)&&(!res)&&(strlen(callerid))){
2631                 /* Check for internal contexts and only */
2632                 /* say extension when the call didn't come from an internal context in the list */
2633                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2634                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2635                         if((strcmp(cidinternalcontexts[i], context) == 0))
2636                                 break;
2637                 }
2638                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2639                         if(!res) {
2640                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2641                                 if (strlen(prefile)) {
2642                                 /* See if we can find a recorded name for this person instead of their extension number */
2643                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2644                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2645                                                 if (!callback)
2646                                                         res = wait_file2(chan, vms, "vm-from");
2647                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2648                                                 res = ast_waitstream(chan, "");
2649                                         } else {
2650                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2651                                                 /* BB: Say "from extension" as one saying to sound smoother */
2652                                                 if (!callback)
2653                                                         res = wait_file2(chan, vms, "vm-from-extension");
2654                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2655                                         }
2656                                 }
2657                         }
2658                 }
2659
2660                 else if (!res){
2661                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2662                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2663                         if (!callback)
2664                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2665                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2666                 }
2667         }
2668         else{
2669                 /* Number unknown */
2670                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2671                 if(!res)
2672                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2673                         res = wait_file2(chan, vms, "vm-unknown-caller");
2674         }
2675         return res;                                                               
2676 }
2677
2678 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2679 {
2680         int res = 0;
2681         char filename[256],*origtime, *cid, *context;
2682         struct ast_config *msg_cfg;
2683
2684         vms->starting = 0; 
2685         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2686         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2687         if (!msg)
2688                 res = wait_file2(chan, vms, "vm-first");
2689         else if (msg == vms->lastmsg)
2690                 res = wait_file2(chan, vms, "vm-last");
2691         if (!res) {
2692                 res = wait_file2(chan, vms, "vm-message");
2693                 if (msg && (msg != vms->lastmsg)) {
2694                         if (!res)
2695                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2696                 }
2697         }
2698
2699         /* Retrieve info from VM attribute file */
2700
2701         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2702         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2703         msg_cfg = ast_load(filename);
2704         if (!msg_cfg) {
2705                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2706                 return 0;
2707         }
2708                                                                                                                                  
2709         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2710                 return 0;
2711                                                                                                                                  
2712         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2713
2714         context = ast_variable_retrieve(msg_cfg, "message", "context");
2715         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2716                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2717
2718         if ((!res)&&(vmu->envelope))
2719                 res = play_message_datetime(chan, vmu, origtime, filename);
2720
2721         if ((!res)&&(vmu->saycid))
2722                 res = play_message_callerid(chan, vms, cid, context, 0);
2723
2724         ast_destroy(msg_cfg);
2725
2726         if (!res) {
2727                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2728                 vms->heard[msg] = 1;
2729                 res = wait_file(chan, vms, vms->fn);
2730         }
2731         return res;
2732 }
2733
2734 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2735 {
2736         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2737         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2738         vms->lastmsg = count_messages(vms->curdir) - 1;
2739         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2740 }
2741
2742 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2743 {
2744         int x;
2745         char ntxt[256] = "";
2746         char txt[256] = "";
2747         if (vms->lastmsg > -1) { 
2748                 /* Get the deleted messages fixed */ 
2749                 vms->curmsg = -1; 
2750                 for (x=0;x < MAXMSG;x++) { 
2751                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2752                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2753                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2754                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2755                                         break;
2756                                 vms->curmsg++; 
2757                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2758                                 if (strcmp(vms->fn, vms->fn2)) { 
2759                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2760                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2761                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2762                                         rename(txt, ntxt); 
2763                                 } 
2764                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2765                                 /* Move to old folder before deleting */ 
2766                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2767                         } 
2768                 } 
2769                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2770                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2771                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2772                                 break;
2773                         vm_delete(vms->fn);
2774                 } 
2775         } 
2776         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2777         memset(vms->heard, 0, sizeof(vms->heard)); 
2778 }
2779
2780 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2781 {
2782         /* Introduce messages they have */
2783         int res;
2784         res = play_and_wait(chan, "vm-youhave");
2785         if (!res) {
2786                 if (vms->newmessages) {
2787                         res = say_and_wait(chan, vms->newmessages);
2788                         if (!res)
2789                                 res = play_and_wait(chan, "vm-INBOX");
2790                         if (vms->oldmessages && !res)
2791                                 res = play_and_wait(chan, "vm-and");
2792                         else if (!res) {
2793                                 if ((vms->newmessages == 1))
2794                                         res = play_and_wait(chan, "vm-message");
2795                                 else
2796                                         res = play_and_wait(chan, "vm-messages");
2797                         }
2798                                 
2799                 }
2800                 if (!res && vms->oldmessages) {
2801                         res = say_and_wait(chan, vms->oldmessages);
2802                         if (!res)
2803                                 res = play_and_wait(chan, "vm-Old");
2804                         if (!res) {
2805                                 if (vms->oldmessages == 1)
2806                                         res = play_and_wait(chan, "vm-message");
2807                                 else
2808                                         res = play_and_wait(chan, "vm-messages");
2809                         }
2810                 }
2811                 if (!res) {
2812                         if (!vms->oldmessages && !vms->newmessages) {
2813                                 res = play_and_wait(chan, "vm-no");
2814                                 if (!res)
2815                                         res = play_and_wait(chan, "vm-messages");
2816                         }
2817                 }
2818         }
2819         return res;
2820 }
2821
2822 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms)
2823 {
2824         int res = 0;
2825         /* Play instructions and wait for new command */
2826         while(!res) {
2827                 if (vms->starting) {
2828                         if (vms->lastmsg > -1) {
2829                                 res = play_and_wait(chan, "vm-onefor");
2830                                 if (!res)
2831                                         res = play_and_wait(chan, vms->vmbox);
2832                                 if (!res)
2833                                         res = play_and_wait(chan, "vm-messages");
2834                         }
2835                         if (!res)
2836                                 res = play_and_wait(chan, "vm-opts");
2837                 } else {
2838                         if (vms->curmsg)
2839                                 res = play_and_wait(chan, "vm-prev");
2840                         if (!res)
2841                                 res = play_and_wait(chan, "vm-advopts");
2842                         if (!res)
2843                                 res = play_and_wait(chan, "vm-repeat");
2844                         if (!res && (vms->curmsg != vms->lastmsg))
2845                                 res = play_and_wait(chan, "vm-next");
2846                         if (!res) {
2847                                 if (!vms->deleted[vms->curmsg])
2848                                         res = play_and_wait(chan, "vm-delete");
2849                                 else
2850                                         res = play_and_wait(chan, "vm-undelete");
2851                                 if (!res)
2852                                         res = play_and_wait(chan, "vm-toforward");
2853                                 if (!res)
2854                                         res = play_and_wait(chan, "vm-savemessage");
2855                         }
2856                 }
2857                 if (!res)
2858                         res = play_and_wait(chan, "vm-helpexit");
2859                 if (!res)
2860                         res = ast_waitfordigit(chan, 6000);
2861                 if (!res) {
2862                         vms->repeats++;
2863                         if (vms->repeats > 2) {
2864                                 res = 't';
2865                         }
2866                 }
2867         }
2868         return res;
2869 }
2870
2871 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc)
2872 {
2873         int cmd = 0;
2874         int retries = 0;
2875         int duration = 0;
2876         char newpassword[80] = "";
2877         char newpassword2[80] = "";
2878         char prefile[256]="";
2879         char buf[256];
2880         int bytes=0;
2881
2882         if (adsi_available(chan))
2883         {
2884                 bytes += adsi_logo(buf + bytes);
2885                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
2886                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
2887                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2888                 bytes += adsi_voice_mode(buf + bytes, 0);
2889                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2890         }
2891         while((cmd >= 0) && (cmd != 't')) {
2892                 if (cmd)
2893                         retries = 0;
2894                 switch (cmd) {
2895                 case '1':
2896                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/unavail",vmu->context, vms->username);
2897                         cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration);
2898                         break;
2899                 case '2': 
2900                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/busy",vmu->context, vms->username);
2901                         cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration);
2902                         break;
2903                 case '3': 
2904                         snprintf(prefile,sizeof(prefile),"voicemail/%s/%s/greet",vmu->context, vms->username);
2905                         cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration);
2906                         break;
2907                 case '4':
2908                         newpassword[1] = '\0';
2909                         newpassword[0] = cmd = play_and_wait(chan,"vm-newpassword");
2910                         if (cmd < 0)
2911                                 break;
2912                         if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
2913                                 break;
2914             }
2915                         newpassword2[1] = '\0';
2916                         newpassword2[0] = cmd = play_and_wait(chan,"vm-reenterpassword");
2917                         if (cmd < 0)
2918                                 break;
2919
2920                         if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
2921                                 break;
2922             }
2923                         if (strcmp(newpassword, newpassword2)) {
2924                                 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
2925                                 cmd = play_and_wait(chan, "vm-mismatch");
2926                                 break;
2927                         }
2928                         vm_change_password(vmu,newpassword);
2929                         ast_log(LOG_DEBUG,"User %s set password to %s of length %i\n",vms->username,newpassword,strlen(newpassword));
2930                         cmd = play_and_wait(chan,"vm-passchanged");
2931                         break;
2932                 case '*': 
2933                         cmd = 't';
2934                         break;
2935                 default: 
2936                         cmd = play_and_wait(chan,"vm-options");
2937                         if (!cmd)
2938                                 cmd = ast_waitfordigit(chan,6000);
2939                         if (!cmd)
2940                                 retries++;
2941                         if (retries > 3)
2942                                 cmd = 't';
2943                  }
2944         }
2945         if (cmd == 't')
2946                 cmd = 0;
2947         return cmd;
2948 }
2949
2950 static int vm_execmain(struct ast_channel *chan, void *data)
2951 {
2952         /* XXX This is, admittedly, some pretty horrendus code.  For some
2953            reason it just seemed a lot easier to do with GOTO's.  I feel
2954            like I'm back in my GWBASIC days. XXX */
2955         int res=-1;