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