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