d1183ff7901cd440eebc8208ba18ee7599ef1424
[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
837         if (!strcmp(format, "wav49"))
838                 format = "WAV";
839         ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
840         /* Make a temporary file instead of piping directly to sendmail, in case the mail
841            command hangs */
842         pfd = mkstemp(tmp);
843         if (pfd > -1) {
844                 p = fdopen(pfd, "w");
845                 if (!p) {
846                         close(pfd);
847                         pfd = -1;
848                 }
849         }
850         if (p) {
851                 gethostname(host, sizeof(host));
852                 if (strchr(srcemail, '@'))
853                         strncpy(who, srcemail, sizeof(who)-1);
854                 else {
855                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
856                 }
857                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
858                 time(&t);
859
860                 /* Does this user have a timezone specified? */
861                 if (!ast_strlen_zero(vmu->zonetag)) {
862                         /* Find the zone in the list */
863                         struct vm_zone *z;
864                         z = zones;
865                         while (z) {
866                                 if (!strcmp(z->name, vmu->zonetag)) {
867                                         the_zone = z;
868                                         break;
869                                 }
870                                 z = z->next;
871                         }
872                 }
873
874                 if (the_zone)
875                         ast_localtime(&t,&tm,the_zone->timezone);
876                 else
877                         ast_localtime(&t,&tm,NULL);
878                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
879                 fprintf(p, "Date: %s\n", date);
880
881                 if (*fromstring) {
882                         struct ast_channel *ast = ast_channel_alloc(0);
883                         if (ast) {
884                                 char *passdata;
885                                 int vmlen = strlen(fromstring)*3 + 200;
886                                 if ((passdata = alloca(vmlen))) {
887                                         memset(passdata, 0, vmlen);
888                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
889                                         pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
890                                         fprintf(p, "From: %s <%s>\n",passdata,who);
891                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
892                                 ast_channel_free(ast);
893                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
894                 } else
895                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
896                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
897
898                 if (emailsubject) {
899                         struct ast_channel *ast = ast_channel_alloc(0);
900                         if (ast) {
901                                 char *passdata;
902                                 int vmlen = strlen(emailsubject)*3 + 200;
903                                 if ((passdata = alloca(vmlen))) {
904                                         memset(passdata, 0, vmlen);
905                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
906                                         pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
907                                         fprintf(p, "Subject: %s\n",passdata);
908                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
909                                 ast_channel_free(ast);
910                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
911                 } else
912                 if( *emailtitle)
913                 {
914                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
915                         fprintf(p,"\n") ;
916                 }
917                 else
918                 if (pbxskip)
919                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
920                 else
921                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
922                 fprintf(p, "Message-ID: <Asterisk-%d-%s-%d@%s>\n", msgnum, mailbox, getpid(), host);
923                 fprintf(p, "MIME-Version: 1.0\n");
924                 if (attach_user_voicemail) {
925                         /* Something unique. */
926                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d", msgnum, mailbox, getpid());
927
928                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
929
930                         fprintf(p, "--%s\n", bound);
931                 }
932                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
933                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
934                 if (emailbody) {
935                         struct ast_channel *ast = ast_channel_alloc(0);
936                         if (ast) {
937                                 char *passdata;
938                                 int vmlen = strlen(emailbody)*3 + 200;
939                                 if ((passdata = alloca(vmlen))) {
940                                         memset(passdata, 0, vmlen);
941                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,callerid,dur,date,passdata);
942                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
943                                         fprintf(p, "%s\n",passdata);
944                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
945                                 ast_channel_free(ast);
946                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
947                 } else {
948                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
949
950                         "in mailbox %s from %s, on %s so you might\n"
951                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
952                         dur, msgnum + 1, mailbox, (callerid ? callerid : "an unknown caller"), date);
953                 }
954                 if (attach_user_voicemail) {
955                         fprintf(p, "--%s\n", bound);
956                         fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
957                         fprintf(p, "Content-Transfer-Encoding: base64\n");
958                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
959                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
960
961                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
962                         base_encode(fname, p);
963                         fprintf(p, "\n\n--%s--\n.\n", bound);
964                 }
965                 fclose(p);
966                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
967                 ast_safe_system(tmp2);
968                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
969         } else {
970                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
971                 return -1;
972         }
973         return 0;
974 }
975
976 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *callerid, int duration, struct ast_vm_user *vmu)
977 {
978         FILE *p=NULL;
979         int pfd;
980         char date[256];
981         char host[256];
982         char who[256];
983         char dur[256];
984         char tmp[80] = "/tmp/astmail-XXXXXX";
985         char tmp2[256];
986         time_t t;
987         struct tm tm;
988         struct vm_zone *the_zone = NULL;
989         pfd = mkstemp(tmp);
990
991         if (pfd > -1) {
992                 p = fdopen(pfd, "w");
993                 if (!p) {
994                         close(pfd);
995                         pfd = -1;
996                 }
997         }
998
999         if (p) {
1000                 gethostname(host, sizeof(host));
1001                 if (strchr(srcemail, '@'))
1002                         strncpy(who, srcemail, sizeof(who)-1);
1003                 else {
1004                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1005                 }
1006                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1007                 time(&t);
1008
1009                 /* Does this user have a timezone specified? */
1010                 if (!ast_strlen_zero(vmu->zonetag)) {
1011                         /* Find the zone in the list */
1012                         struct vm_zone *z;
1013                         z = zones;
1014                         while (z) {
1015                                 if (!strcmp(z->name, vmu->zonetag)) {
1016                                         the_zone = z;
1017                                         break;
1018                                 }
1019                                 z = z->next;
1020                         }
1021                 }
1022
1023                 if (the_zone)
1024                         ast_localtime(&t,&tm,the_zone->timezone);
1025                 else
1026                         ast_localtime(&t,&tm,NULL);
1027
1028                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1029                 fprintf(p, "Date: %s\n", date);
1030                 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1031                 fprintf(p, "To: %s\n", pager);
1032                 fprintf(p, "Subject: New VM\n\n");
1033                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1034                 fprintf(p, "New %s long msg in box %s\n"
1035                            "from %s, on %s", dur, mailbox, (callerid ? callerid : "unknown"), date);
1036                 fclose(p);
1037                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1038                 ast_safe_system(tmp2);
1039                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", who, mailcmd);
1040         } else {
1041                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1042                 return -1;
1043         }
1044         return 0;
1045 }
1046
1047 static int get_date(char *s, int len)
1048 {
1049         struct tm tm;
1050         time_t t;
1051         t = time(0);
1052         localtime_r(&t,&tm);
1053         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1054 }
1055
1056 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1057 {
1058         int res;
1059         char fn[256];
1060         snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1061         if (ast_fileexists(fn, NULL, NULL) > 0) {
1062                 res = ast_streamfile(chan, fn, chan->language);
1063                 if (res)
1064                         return -1;
1065                 res = ast_waitstream(chan, ecodes);
1066                 if (res)
1067                         return res;
1068         } else {
1069                 res = ast_streamfile(chan, "vm-theperson", chan->language);
1070                 if (res)
1071                         return -1;
1072                 res = ast_waitstream(chan, ecodes);
1073                 if (res)
1074                         return res;
1075                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1076                 if (res)
1077                         return res;
1078         }
1079         if (busy)
1080                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1081         else
1082                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1083         if (res)
1084                 return -1;
1085         res = ast_waitstream(chan, ecodes);
1086         return res;
1087 }
1088
1089 static int play_and_wait(struct ast_channel *chan, char *fn)
1090 {
1091         int d;
1092         d = ast_streamfile(chan, fn, chan->language);
1093         if (d)
1094                 return d;
1095         d = ast_waitstream(chan, AST_DIGIT_ANY);
1096         ast_stopstream(chan);
1097         return d;
1098 }
1099
1100 static int play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration, int beep)
1101 {
1102         char d = 0, *fmts;
1103         char comment[256];
1104         int x, fmtcnt=1, res=-1,outmsg=0;
1105         struct ast_frame *f;
1106         struct ast_filestream *others[MAX_OTHER_FORMATS];
1107         struct ast_filestream *realfiles[MAX_OTHER_FORMATS];
1108         char *sfmt[MAX_OTHER_FORMATS];
1109         char *stringp=NULL;
1110         time_t start, end;
1111         struct ast_dsp *sildet;         /* silence detector dsp */
1112         int totalsilence = 0;
1113         int dspsilence = 0;
1114         int gotsilence = 0;             /* did we timeout for silence? */
1115         int rfmt=0;     
1116         char prependfile[80];
1117         
1118         /* barf if no pointer passed to store duration in */
1119         if (duration == NULL) {
1120                 ast_log(LOG_WARNING, "Error play_and_prepend called without duration pointer\n");
1121                 return -1;
1122         }
1123
1124         ast_log(LOG_DEBUG,"play_and_prepend: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1125         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1126
1127         if (playfile || beep) { 
1128                 if (!beep)
1129                         d = play_and_wait(chan, playfile);
1130                 if (d > -1)
1131                         d = ast_streamfile(chan, "beep",chan->language);
1132                 if (!d)
1133                         d = ast_waitstream(chan,"");
1134                 if (d < 0)
1135                         return -1;
1136         }
1137         strncpy(prependfile, recordfile, sizeof(prependfile) -1);       
1138         strcat(prependfile, "-prepend");
1139                         
1140         fmts = ast_strdupa(fmt);
1141         
1142         stringp=fmts;
1143         strsep(&stringp, "|");
1144         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);       
1145         sfmt[0] = ast_strdupa(fmts);
1146         
1147         while((fmt = strsep(&stringp, "|"))) {
1148                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1149                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1150                         break;
1151                 }
1152                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1153         }
1154
1155         time(&start);
1156         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1157         for (x=0;x<fmtcnt;x++) {
1158                 others[x] = ast_writefile(prependfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1159                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, prependfile, sfmt[x], others[x]);
1160                 if (!others[x]) {
1161                         break;
1162                 }
1163         }
1164         
1165         sildet = ast_dsp_new(); /* Create the silence detector */
1166         if (!sildet) {
1167                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1168                 return -1;
1169         }
1170         ast_dsp_set_threshold(sildet, silencethreshold);
1171
1172         if (maxsilence > 0) {
1173                 rfmt = chan->readformat;
1174                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1175                 if (res < 0) {
1176                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1177                         return -1;
1178                 }
1179         }
1180                                                 
1181         if (x == fmtcnt) {
1182         /* Loop forever, writing the packets we read to the writer(s), until
1183            we read a # or get a hangup */
1184                 f = NULL;
1185                 for(;;) {
1186                         res = ast_waitfor(chan, 2000);
1187                         if (!res) {
1188                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1189                                 /* Try one more time in case of masq */
1190                                 res = ast_waitfor(chan, 2000);
1191                                 if (!res) {
1192                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1193                                         res = -1;
1194                                 }
1195                         }
1196                         
1197                         if (res < 0) {
1198                                 f = NULL;
1199                                 break;
1200                         }
1201                         f = ast_read(chan);
1202                         if (!f)
1203                                 break;
1204                         if (f->frametype == AST_FRAME_VOICE) {
1205                                 /* write each format */
1206                                 for (x=0;x<fmtcnt;x++) {
1207                                         if (!others[x])
1208                                                 break;
1209                                         res = ast_writestream(others[x], f);
1210                                 }
1211                                 
1212                                 /* Silence Detection */
1213                                 if (maxsilence > 0) {
1214                                         dspsilence = 0;
1215                                         ast_dsp_silence(sildet, f, &dspsilence);
1216                                         if (dspsilence)
1217                                                 totalsilence = dspsilence;
1218                                         else
1219                                                 totalsilence = 0;
1220                                         
1221                                         if (totalsilence > maxsilence) {
1222                                         /* Ended happily with silence */
1223                                         ast_frfree(f);
1224                                         gotsilence = 1;
1225                                         outmsg=2;
1226                                         break;
1227                                         }
1228                                 }
1229                                 /* Exit on any error */
1230                                 if (res) {
1231                                         ast_log(LOG_WARNING, "Error writing frame\n");
1232                                         ast_frfree(f);
1233                                         break;
1234                                 }
1235                         } else if (f->frametype == AST_FRAME_VIDEO) {
1236                                 /* Write only once */
1237                                 ast_writestream(others[0], f);
1238                         } else if (f->frametype == AST_FRAME_DTMF) {
1239                                 /* stop recording with any digit */
1240                                 if (option_verbose > 2) 
1241                                         ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1242                                 res = 't';
1243                                 outmsg = 2;
1244                                 ast_frfree(f);
1245                                 break;
1246                         }
1247                         if (maxtime) {
1248                                 time(&end);
1249                                 if (maxtime < (end - start)) {
1250                                         if (option_verbose > 2)
1251                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1252                                         res = 't';
1253                                         outmsg=2;
1254                                         ast_frfree(f);
1255                                         break;
1256                                 }
1257                         }
1258                         ast_frfree(f);
1259                 }
1260                 if (end == start) time(&end);
1261                 if (!f) {
1262                         if (option_verbose > 2) 
1263                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1264                         res = -1;
1265                         outmsg=1;
1266 #if 0
1267                         /* delete all the prepend files */
1268                         for (x=0;x<fmtcnt;x++) {
1269                                 if (!others[x])
1270                                         break;
1271                                 ast_closestream(others[x]);
1272                                 ast_filedelete(prependfile, sfmt[x]);
1273                         }
1274 #endif
1275                 }
1276         } else {
1277                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", prependfile, sfmt[x]); 
1278         }
1279         *duration = end - start;
1280 #if 0
1281         if (outmsg > 1) {
1282 #else
1283         if (outmsg) {
1284 #endif
1285                 struct ast_frame *fr;
1286                 for (x=0;x<fmtcnt;x++) {
1287                         snprintf(comment, sizeof(comment), "Opening the real file %s.%s\n", recordfile, sfmt[x]);
1288                         realfiles[x] = ast_readfile(recordfile, sfmt[x], comment, O_RDONLY, 0, 0);
1289                         if (!others[x] || !realfiles[x])
1290                                 break;
1291                         if (totalsilence)
1292                                 ast_stream_rewind(others[x], totalsilence-200);
1293                         else
1294                                 ast_stream_rewind(others[x], 200);
1295                         ast_truncstream(others[x]);
1296                         /* add the original file too */
1297                         while ((fr = ast_readframe(realfiles[x]))) {
1298                                 ast_writestream(others[x],fr);
1299                         }
1300                         ast_closestream(others[x]);
1301                         ast_closestream(realfiles[x]);
1302                         ast_filerename(prependfile, recordfile, sfmt[x]);
1303 #if 0
1304                         ast_verbose("Recording Format: sfmts=%s, prependfile %s, recordfile %s\n", sfmt[x],prependfile,recordfile);
1305 #endif
1306                         ast_filedelete(prependfile, sfmt[x]);
1307                 }
1308         }
1309         if (rfmt) {
1310                 if (ast_set_read_format(chan, rfmt)) {
1311                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1312                 }
1313         }
1314         if (outmsg) {
1315                 if (outmsg > 1) {
1316                         /* Let them know it worked */
1317                         ast_streamfile(chan, "auth-thankyou", chan->language);
1318                         ast_waitstream(chan, "");
1319                 }
1320         }       
1321         return res;
1322 }
1323
1324 static int play_and_record(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int *duration)
1325 {
1326         char d, *fmts;
1327         char comment[256];
1328         int x, fmtcnt=1, res=-1,outmsg=0;
1329         struct ast_frame *f;
1330         struct ast_filestream *others[MAX_OTHER_FORMATS];
1331         char *sfmt[MAX_OTHER_FORMATS];
1332         char *stringp=NULL;
1333         time_t start, end;
1334         struct ast_dsp *sildet;         /* silence detector dsp */
1335         int totalsilence = 0;
1336         int dspsilence = 0;
1337         int gotsilence = 0;             /* did we timeout for silence? */
1338         int rfmt=0;
1339
1340         /* barf if no pointer passed to store duration in */
1341         if (duration == NULL) {
1342                 ast_log(LOG_WARNING, "Error play_and_record called without duration pointer\n");
1343                 return -1;
1344         }
1345
1346         ast_log(LOG_DEBUG,"play_and_record: %s, %s, '%s'\n", playfile ? playfile : "<None>", recordfile, fmt);
1347         snprintf(comment,sizeof(comment),"Playing %s, Recording to: %s on %s\n", playfile ? playfile : "<None>", recordfile, chan->name);
1348
1349         if (playfile) {
1350                 d = play_and_wait(chan, playfile);
1351                 if (d > -1)
1352                         d = ast_streamfile(chan, "beep",chan->language);
1353                 if (!d)
1354                         d = ast_waitstream(chan,"");
1355                 if (d < 0)
1356                         return -1;
1357         }
1358
1359         fmts = ast_strdupa(fmt);
1360
1361         stringp=fmts;
1362         strsep(&stringp, "|");
1363         ast_log(LOG_DEBUG,"Recording Formats: sfmts=%s\n", fmts);
1364         sfmt[0] = ast_strdupa(fmts);
1365
1366         while((fmt = strsep(&stringp, "|"))) {
1367                 if (fmtcnt > MAX_OTHER_FORMATS - 1) {
1368                         ast_log(LOG_WARNING, "Please increase MAX_OTHER_FORMATS in app_voicemail.c\n");
1369                         break;
1370                 }
1371                 sfmt[fmtcnt++] = ast_strdupa(fmt);
1372         }
1373
1374         time(&start);
1375         end=start;  /* pre-initialize end to be same as start in case we never get into loop */
1376         for (x=0;x<fmtcnt;x++) {
1377                 others[x] = ast_writefile(recordfile, sfmt[x], comment, O_TRUNC, 0, 0700);
1378                 ast_verbose( VERBOSE_PREFIX_3 "x=%i, open writing:  %s format: %s, %p\n", x, recordfile, sfmt[x], others[x]);
1379
1380                 if (!others[x]) {
1381                         break;
1382                 }
1383         }
1384
1385         sildet = ast_dsp_new(); /* Create the silence detector */
1386         if (!sildet) {
1387                 ast_log(LOG_WARNING, "Unable to create silence detector :(\n");
1388                 return -1;
1389         }
1390         ast_dsp_set_threshold(sildet, silencethreshold);
1391         
1392         if (maxsilence > 0) {
1393                 rfmt = chan->readformat;
1394                 res = ast_set_read_format(chan, AST_FORMAT_SLINEAR);
1395                 if (res < 0) {
1396                         ast_log(LOG_WARNING, "Unable to set to linear mode, giving up\n");
1397                         return -1;
1398                 }
1399         }
1400
1401         if (x == fmtcnt) {
1402         /* Loop forever, writing the packets we read to the writer(s), until
1403            we read a # or get a hangup */
1404                 f = NULL;
1405                 for(;;) {
1406                         res = ast_waitfor(chan, 2000);
1407                         if (!res) {
1408                                 ast_log(LOG_DEBUG, "One waitfor failed, trying another\n");
1409                                 /* Try one more time in case of masq */
1410                                 res = ast_waitfor(chan, 2000);
1411                                 if (!res) {
1412                                         ast_log(LOG_WARNING, "No audio available on %s??\n", chan->name);
1413                                         res = -1;
1414                                 }
1415                         }
1416
1417                         if (res < 0) {
1418                                 f = NULL;
1419                                 break;
1420                         }
1421                         f = ast_read(chan);
1422                         if (!f)
1423                                 break;
1424                         if (f->frametype == AST_FRAME_VOICE) {
1425                                 /* write each format */
1426                                 for (x=0;x<fmtcnt;x++) {
1427                                         res = ast_writestream(others[x], f);
1428                                 }
1429
1430                                 /* Silence Detection */
1431                                 if (maxsilence > 0) {
1432                                         dspsilence = 0;
1433                                         ast_dsp_silence(sildet, f, &dspsilence);
1434                                         if (dspsilence)
1435                                                 totalsilence = dspsilence;
1436                                         else
1437                                                 totalsilence = 0;
1438
1439                                         if (totalsilence > maxsilence) {
1440                                         /* Ended happily with silence */
1441                                         ast_frfree(f);
1442                                         gotsilence = 1;
1443                                         outmsg=2;
1444                                         break;
1445                                         }
1446                                 }
1447                                 /* Exit on any error */
1448                                 if (res) {
1449                                         ast_log(LOG_WARNING, "Error writing frame\n");
1450                                         ast_frfree(f);
1451                                         break;
1452                                 }
1453                         } else if (f->frametype == AST_FRAME_VIDEO) {
1454                                 /* Write only once */
1455                                 ast_writestream(others[0], f);
1456                         } else if (f->frametype == AST_FRAME_DTMF) {
1457                                 if (f->subclass == '#') {
1458                                         if (option_verbose > 2)
1459                                                 ast_verbose( VERBOSE_PREFIX_3 "User ended message by pressing %c\n", f->subclass);
1460                                         res = '#';
1461                                         outmsg = 2;
1462                                         ast_frfree(f);
1463                                         break;
1464                                 }
1465                         }
1466                                 if (f->subclass == '0') {
1467                                 /* Check for a '0' during message recording also, in case caller wants operator */
1468                                         if (option_verbose > 2)
1469                                                 ast_verbose(VERBOSE_PREFIX_3 "User cancelled by pressing %c\n", f->subclass);
1470                                         res = '0';
1471                                         outmsg = 0;
1472                                         ast_frfree(f);
1473                                         break;
1474                                 }
1475                         if (maxtime) {
1476                                 time(&end);
1477                                 if (maxtime < (end - start)) {
1478                                         if (option_verbose > 2)
1479                                                 ast_verbose( VERBOSE_PREFIX_3 "Took too long, cutting it short...\n");
1480                                         outmsg = 2;
1481                                         res = 't';
1482                                         ast_frfree(f);
1483                                         break;
1484                                 }
1485                         }
1486                         ast_frfree(f);
1487                 }
1488                 if (end == start) time(&end);
1489                 if (!f) {
1490                         if (option_verbose > 2)
1491                                 ast_verbose( VERBOSE_PREFIX_3 "User hung up\n");
1492                         res = -1;
1493                         outmsg=1;
1494                 }
1495         } else {
1496                 ast_log(LOG_WARNING, "Error creating writestream '%s', format '%s'\n", recordfile, sfmt[x]);
1497         }
1498
1499         *duration = end - start;
1500
1501         for (x=0;x<fmtcnt;x++) {
1502                 if (!others[x])
1503                         break;
1504                 if (totalsilence)
1505                         ast_stream_rewind(others[x], totalsilence-200);
1506                 else
1507                         ast_stream_rewind(others[x], 200);
1508                 ast_truncstream(others[x]);
1509                 ast_closestream(others[x]);
1510         }
1511         if (rfmt) {
1512                 if (ast_set_read_format(chan, rfmt)) {
1513                         ast_log(LOG_WARNING, "Unable to restore format %s to channel '%s'\n", ast_getformatname(rfmt), chan->name);
1514                 }
1515         }
1516         if (outmsg) {
1517                 if (outmsg > 1) {
1518                 /* Let them know recording is stopped */
1519                         ast_streamfile(chan, "auth-thankyou", chan->language);
1520                         ast_waitstream(chan, "");
1521                 }
1522         }
1523
1524         return res;
1525 }
1526
1527 static void free_user(struct ast_vm_user *vmu)
1528 {
1529         if (vmu->alloced)
1530                 free(vmu);
1531 }
1532
1533 static void free_zone(struct vm_zone *z)
1534 {
1535         free(z);
1536 }
1537
1538 static char *mbox(int id)
1539 {
1540         switch(id) {
1541         case 0:
1542                 return "INBOX";
1543         case 1:
1544                 return "Old";
1545         case 2:
1546                 return "Work";
1547         case 3:
1548                 return "Family";
1549         case 4:
1550                 return "Friends";
1551         case 5:
1552                 return "Cust1";
1553         case 6:
1554                 return "Cust2";
1555         case 7:
1556                 return "Cust3";
1557         case 8:
1558                 return "Cust4";
1559         case 9:
1560                 return "Cust5";
1561         default:
1562                 return "Unknown";
1563         }
1564 }
1565
1566 static int copy(char *infile, char *outfile)
1567 {
1568         int ifd;
1569         int ofd;
1570         int res;
1571         int len;
1572         char buf[4096];
1573
1574 #ifdef HARDLINK_WHEN_POSSIBLE
1575         /* Hard link if possible; saves disk space & is faster */
1576         if (link(infile, outfile)) {
1577 #endif
1578                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1579                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1580                         return -1;
1581                 }
1582                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1583                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1584                         close(ifd);
1585                         return -1;
1586                 }
1587                 do {
1588                         len = read(ifd, buf, sizeof(buf));
1589                         if (len < 0) {
1590                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1591                                 close(ifd);
1592                                 close(ofd);
1593                                 unlink(outfile);
1594                         }
1595                         if (len) {
1596                                 res = write(ofd, buf, len);
1597                                 if (res != len) {
1598                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1599                                         close(ifd);
1600                                         close(ofd);
1601                                         unlink(outfile);
1602                                 }
1603                         }
1604                 } while(len);
1605                 close(ifd);
1606                 close(ofd);
1607                 return 0;
1608 #ifdef HARDLINK_WHEN_POSSIBLE
1609         } else {
1610                 /* Hard link succeeded */
1611                 return 0;
1612         }
1613 #endif
1614 }
1615
1616 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid);
1617
1618 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)
1619 {
1620         char fromdir[256], todir[256], frompath[256], topath[256];
1621         char *frombox = mbox(imbox);
1622         int recipmsgnum;
1623
1624         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1625
1626         make_dir(todir, sizeof(todir), recip->context, "", "");
1627         /* It's easier just to try to make it than to check for its existence */
1628         if (mkdir(todir, 0700) && (errno != EEXIST))
1629                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1630         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1631         /* It's easier just to try to make it than to check for its existence */
1632         if (mkdir(todir, 0700) && (errno != EEXIST))
1633                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1634         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1635         if (mkdir(todir, 0700) && (errno != EEXIST))
1636                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1637
1638         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1639         make_file(frompath, sizeof(frompath), fromdir, msgnum);
1640         recipmsgnum = 0;
1641         do {
1642                 make_file(topath, sizeof(topath), todir, recipmsgnum);
1643                 if (ast_fileexists(topath, NULL, chan->language) <= 0) 
1644                         break;
1645                 recipmsgnum++;
1646         } while(recipmsgnum < MAXMSG);
1647         if (recipmsgnum < MAXMSG) {
1648                 char frompath2[256],topath2[256];
1649                 ast_filecopy(frompath, topath, NULL);
1650                 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1651                 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1652                 copy(frompath2, topath2);
1653         } else {
1654                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1655         }
1656
1657         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->callerid);
1658 }
1659
1660 static void run_externnotify(char *context, char *extension, int numvoicemails)
1661 {
1662         char arguments[255];
1663
1664         if(externnotify[0]) {
1665                 strncpy(arguments, externnotify, sizeof(arguments));
1666                 snprintf(arguments, sizeof(arguments)-1, "%s %s %s %d&", externnotify, context, extension, numvoicemails);
1667                 ast_log(LOG_DEBUG,"Executing %s\n", arguments);
1668                 ast_safe_system(arguments);
1669         }
1670 }
1671
1672
1673 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1674 {
1675         char txtfile[256];
1676         FILE *txt;
1677         int res = 0;
1678         int msgnum;
1679         int fd;
1680         int duration = 0;
1681         char date[256];
1682         char dir[256];
1683         char fn[256];
1684         char prefile[256]="";
1685         char ext_context[256] = "";
1686         char fmt[80];
1687         char *context;
1688         char ecodes[16] = "#";
1689         char tmp[256] = "", *tmpptr;
1690         struct ast_vm_user *vmu;
1691         struct ast_vm_user svm;
1692
1693         strncpy(tmp, ext, sizeof(tmp) - 1);
1694         ext = tmp;
1695         context = strchr(tmp, '@');
1696         if (context) {
1697                 *context = '\0';
1698                 context++;
1699                 tmpptr = strchr(context, '&');
1700         } else {
1701                 tmpptr = strchr(ext, '&');
1702         }
1703
1704         if (tmpptr) {
1705                 *tmpptr = '\0';
1706                 tmpptr++;
1707         }
1708
1709         if ((vmu = find_user(&svm, context, ext))) {
1710                 /* Setup pre-file if appropriate */
1711                 if (strcmp(vmu->context, "default"))
1712                         snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1713                 else
1714                         strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);
1715                 if (busy)
1716                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1717                 else if (unavail)
1718                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1719                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1720                 /* It's easier just to try to make it than to check for its existence */
1721                 if (mkdir(dir, 0700) && (errno != EEXIST))
1722                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1723                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1724                 /* It's easier just to try to make it than to check for its existence */
1725                 if (mkdir(dir, 0700) && (errno != EEXIST))
1726                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1727                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
1728                 if (mkdir(dir, 0700) && (errno != EEXIST))
1729                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1730                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "o", 1, chan->callerid))
1731                         strcat(ecodes, "0");
1732                 if (ast_exists_extension(chan, strlen(chan->macrocontext) ? chan->macrocontext : chan->context, "a", 1, chan->callerid))
1733                         strcat(ecodes, "*");
1734                 /* Play the beginning intro if desired */
1735                 if (!ast_strlen_zero(prefile)) {
1736                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
1737                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
1738                                     res = ast_waitstream(chan, ecodes);
1739                         } else {
1740                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
1741                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
1742                         }
1743                         if (res < 0) {
1744                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
1745                                 free_user(vmu);
1746                                 return -1;
1747                         }
1748                 }
1749                 if (res == '#') {
1750                         /* On a '#' we skip the instructions */
1751                         silent = 1;
1752                         res = 0;
1753                 }
1754                 if (!res && !silent) {
1755                         res = ast_streamfile(chan, INTRO, chan->language);
1756                         if (!res)
1757                                 res = ast_waitstream(chan, ecodes);
1758                         if (res == '#') {
1759                                 silent = 1;
1760                                 res = 0;
1761                         }
1762                 }
1763                 if (res > 0)
1764                         ast_stopstream(chan);
1765                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
1766                 other than the operator -- an automated attendant or mailbox login for example */
1767                 if (res == '*') {
1768                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
1769                         if (!ast_strlen_zero(vmu->exit)) {
1770                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1771                         } else if (!ast_strlen_zero(chan->macrocontext)) {
1772                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1773                         }
1774                         chan->priority = 0;
1775                         free_user(vmu);
1776                         return 0;
1777                 }
1778                 /* Check for a '0' here */
1779                 if (res == '0') {
1780                 transfer:
1781                         strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
1782                         if (!ast_strlen_zero(vmu->exit)) {
1783                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
1784                         } else if (!ast_strlen_zero(chan->macrocontext)) {
1785                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
1786                         }
1787                         chan->priority = 0;
1788                         free_user(vmu);
1789                         return 0;
1790                 }
1791                 if (res >= 0) {
1792                         /* Unless we're *really* silent, try to send the beep */
1793                         res = ast_streamfile(chan, "beep", chan->language);
1794                         if (!res)
1795                                 res = ast_waitstream(chan, "");
1796                 }
1797                 if (res < 0) {
1798                         free_user(vmu);
1799                         return -1;
1800                 }
1801                 /* The meat of recording the message...  All the announcements and beeps have been played*/
1802                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1803                 if (!ast_strlen_zero(fmt)) {
1804                         msgnum = 0;
1805                         do {
1806                                 make_file(fn, sizeof(fn), dir, msgnum);
1807                                 if (ast_fileexists(fn, NULL, chan->language) <= 0) 
1808                                         break;
1809                                 msgnum++;
1810                         } while(msgnum < MAXMSG);
1811                         if (msgnum < MAXMSG) {
1812                                 /* Store information */
1813                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
1814                                 txt = fopen(txtfile, "w+");
1815                                 if (txt) {
1816                                         get_date(date, sizeof(date));
1817                                         fprintf(txt, 
1818 ";\n"
1819 "; Message Information file\n"
1820 ";\n"
1821 "[message]\n"
1822 "origmailbox=%s\n"
1823 "context=%s\n"
1824 "macrocontext=%s\n"
1825 "exten=%s\n"
1826 "priority=%d\n"
1827 "callerchan=%s\n"
1828 "callerid=%s\n"
1829 "origdate=%s\n"
1830 "origtime=%ld\n",
1831         ext,
1832         chan->context,
1833         chan->macrocontext, 
1834         chan->exten,
1835         chan->priority,
1836         chan->name,
1837         chan->callerid ? chan->callerid : "Unknown",
1838         date, (long)time(NULL));
1839                                         fclose(txt);
1840                                 } else
1841                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
1842                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
1843                                 if (res == '0')
1844                                         goto transfer;
1845                                 if (res > 0)
1846                                         res = 0;
1847                                 fd = open(txtfile, O_APPEND | O_WRONLY);
1848                                 if (fd > -1) {
1849                                         txt = fdopen(fd, "a");
1850                                         if (txt) {
1851                                                 fprintf(txt, "duration=%d\n", duration);
1852                                                 fclose(txt);
1853                                         } else
1854                                                 close(fd);
1855                                 }
1856                                 if (duration < vmminmessage) {
1857                                         vm_delete(fn);
1858                                         goto leave_vm_out;
1859                                 }
1860                                 /* Are there to be more recipients of this message? */
1861                                 while (tmpptr) {
1862                                         struct ast_vm_user recipu, *recip;
1863                                         char *exten, *context;
1864
1865                                         exten = strsep(&tmpptr, "&");
1866                                         context = strchr(exten, '@');
1867                                         if (context) {
1868                                                 *context = '\0';
1869                                                 context++;
1870                                         }
1871                                         if ((recip = find_user(&recipu, context, exten))) {
1872                                                 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
1873                                                 free_user(recip);
1874                                         }
1875                                 }
1876                                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->callerid);
1877                         } else {
1878                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
1879                                 if (!res)
1880                                         res = ast_waitstream(chan, "");
1881                                 ast_log(LOG_WARNING, "No more messages possible\n");
1882                         }
1883                 } else
1884                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");
1885 leave_vm_out:
1886                 free_user(vmu);
1887         } else {
1888                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
1889                 /*Send the call to n+101 priority, where n is the current priority*/
1890                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->callerid))
1891                         chan->priority+=100;
1892         }
1893
1894         return res;
1895 }
1896
1897 static int count_messages(char *dir)
1898 {
1899         int x;
1900         char fn[256];
1901         for (x=0;x<MAXMSG;x++) {
1902                 make_file(fn, sizeof(fn), dir, x);
1903                 if (ast_fileexists(fn, NULL, NULL) < 1)
1904                         break;
1905         }
1906         return x;
1907 }
1908
1909 static int say_and_wait(struct ast_channel *chan, int num, char *language)
1910 {
1911         int d;
1912         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
1913         return d;
1914 }
1915
1916 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
1917 {
1918         char sfn[256];
1919         char dfn[256];
1920         char ddir[256];
1921         char txt[256];
1922         char ntxt[256];
1923         char *dbox = mbox(box);
1924         int x;
1925         make_file(sfn, sizeof(sfn), dir, msg);
1926         make_dir(ddir, sizeof(ddir), context, username, dbox);
1927         mkdir(ddir, 0700);
1928         for (x=0;x<MAXMSG;x++) {
1929                 make_file(dfn, sizeof(dfn), ddir, x);
1930                 if (ast_fileexists(dfn, NULL, NULL) < 0)
1931                         break;
1932         }
1933         if (x >= MAXMSG)
1934                 return -1;
1935         ast_filecopy(sfn, dfn, NULL);
1936         if (strcmp(sfn, dfn)) {
1937                 snprintf(txt, sizeof(txt), "%s.txt", sfn);
1938                 snprintf(ntxt, sizeof(ntxt), "%s.txt", dfn);
1939                 copy(txt, ntxt);
1940         }
1941         return 0;
1942 }
1943
1944 static int adsi_logo(unsigned char *buf)
1945 {
1946         int bytes = 0;
1947         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
1948         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
1949         return bytes;
1950 }
1951
1952 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
1953 {
1954         char buf[256];
1955         int bytes=0;
1956         int x;
1957         char num[5];
1958
1959         *useadsi = 0;
1960         bytes += adsi_data_mode(buf + bytes);
1961         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1962
1963         bytes = 0;
1964         bytes += adsi_logo(buf);
1965         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1966 #ifdef DISPLAY
1967         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
1968 #endif
1969         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1970         bytes += adsi_data_mode(buf + bytes);
1971         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1972
1973         if (adsi_begin_download(chan, addesc, adapp, adsec, adver)) {
1974                 bytes = 0;
1975                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
1976                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
1977                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1978                 bytes += adsi_voice_mode(buf + bytes, 0);
1979                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1980                 return 0;
1981         }
1982
1983 #ifdef DISPLAY
1984         /* Add a dot */
1985         bytes = 0;
1986         bytes += adsi_logo(buf);
1987         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
1988         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
1989         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
1990         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
1991 #endif
1992         bytes = 0;
1993         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
1994         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
1995         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
1996         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
1997         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
1998         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
1999         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2000
2001 #ifdef DISPLAY
2002         /* Add another dot */
2003         bytes = 0;
2004         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
2005       bytes += adsi_voice_mode(buf + bytes, 0);
2006
2007         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2008         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2009 #endif
2010
2011         bytes = 0;
2012         /* These buttons we load but don't use yet */
2013         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2014         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2015         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2016         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2017         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2018         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2019         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2020
2021 #ifdef DISPLAY
2022         /* Add another dot */
2023         bytes = 0;
2024         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
2025         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2026         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2027 #endif
2028
2029         bytes = 0;
2030         for (x=0;x<5;x++) {
2031                 snprintf(num, sizeof(num), "%d", x);
2032                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2033         }
2034         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2035         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2036
2037 #ifdef DISPLAY
2038         /* Add another dot */
2039         bytes = 0;
2040         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
2041         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2042         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2043 #endif
2044
2045         if (adsi_end_download(chan)) {
2046                 bytes = 0;
2047                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2048                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2049                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2050                 bytes += adsi_voice_mode(buf + bytes, 0);
2051                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2052                 return 0;
2053         }
2054         bytes = 0;
2055         bytes += adsi_download_disconnect(buf + bytes);
2056         bytes += adsi_voice_mode(buf + bytes, 0);
2057         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2058
2059         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2060
2061 #ifdef DISPLAY
2062         /* Add last dot */
2063         bytes = 0;
2064         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
2065         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2066 #endif
2067         ast_log(LOG_DEBUG, "Restarting session...\n");
2068
2069         bytes = 0;
2070         /* Load the session now */
2071         if (adsi_load_session(chan, adapp, adver, 1) == 1) {
2072                 *useadsi = 1;
2073                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2074         } else
2075                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2076
2077         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2078         return 0;
2079 }
2080
2081 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2082 {
2083         int x;
2084         if (!adsi_available(chan))
2085           return;
2086         x = adsi_load_session(chan, adapp, adver, 1);
2087         if (x < 0)
2088                 return;
2089         if (!x) {
2090                 if (adsi_load_vmail(chan, useadsi)) {
2091                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2092                         return;
2093                 }
2094         } else
2095                 *useadsi = 1;
2096 }
2097
2098 static void adsi_login(struct ast_channel *chan)
2099 {
2100         char buf[256];
2101         int bytes=0;
2102         unsigned char keys[8];
2103         int x;
2104         if (!adsi_available(chan))
2105                 return;
2106
2107         for (x=0;x<8;x++)
2108                 keys[x] = 0;
2109         /* Set one key for next */
2110         keys[3] = ADSI_KEY_APPS + 3;
2111
2112         bytes += adsi_logo(buf + bytes);
2113         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2114         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2115         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2116         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2117         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2118         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2119         bytes += adsi_set_keys(buf + bytes, keys);
2120         bytes += adsi_voice_mode(buf + bytes, 0);
2121         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2122 }
2123
2124 static void adsi_password(struct ast_channel *chan)
2125 {
2126         char buf[256];
2127         int bytes=0;
2128         unsigned char keys[8];
2129         int x;
2130         if (!adsi_available(chan))
2131                 return;
2132
2133         for (x=0;x<8;x++)
2134                 keys[x] = 0;
2135         /* Set one key for next */
2136         keys[3] = ADSI_KEY_APPS + 3;
2137
2138         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2139         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2140         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2141         bytes += adsi_set_keys(buf + bytes, keys);
2142         bytes += adsi_voice_mode(buf + bytes, 0);
2143         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2144 }
2145
2146 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2147 {
2148         char buf[256];
2149         int bytes=0;
2150         unsigned char keys[8];
2151         int x,y;
2152
2153         if (!adsi_available(chan))
2154                 return;
2155
2156         for (x=0;x<5;x++) {
2157                 y = ADSI_KEY_APPS + 12 + start + x;
2158                 if (y > ADSI_KEY_APPS + 12 + 4)
2159                         y = 0;
2160                 keys[x] = ADSI_KEY_SKT | y;
2161         }
2162         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2163         keys[6] = 0;
2164         keys[7] = 0;
2165
2166         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2167         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2168         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2169         bytes += adsi_set_keys(buf + bytes, keys);
2170         bytes += adsi_voice_mode(buf + bytes, 0);
2171
2172         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2173 }
2174
2175 static void adsi_message(struct ast_channel *chan, char *folder, int msg, int last, int deleted, char *fn)
2176 {
2177         int bytes=0;
2178         char buf[256], buf1[256], buf2[256];
2179         char fn2[256];
2180
2181         char cid[256]="";
2182         char *val;
2183         char *name, *num;
2184         char datetime[21]="";
2185         FILE *f;
2186
2187         unsigned char keys[8];
2188
2189         int x;
2190
2191         if (!adsi_available(chan))
2192                 return;
2193
2194         /* Retrieve important info */
2195         snprintf(fn2, sizeof(fn2), "%s.txt", fn);
2196         f = fopen(fn2, "r");
2197         if (f) {
2198                 while(!feof(f)) {       
2199                         fgets(buf, sizeof(buf), f);
2200                         if (!feof(f)) {
2201                                 char *stringp=NULL;
2202                                 stringp=buf;
2203                                 strsep(&stringp, "=");
2204                                 val = strsep(&stringp, "=");
2205                                 if (val && !ast_strlen_zero(val)) {
2206                                         if (!strcmp(buf, "callerid"))
2207                                                 strncpy(cid, val, sizeof(cid) - 1);
2208                                         if (!strcmp(buf, "origdate"))
2209                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2210                                 }
2211                         }
2212                 }
2213                 fclose(f);
2214         }
2215         /* New meaning for keys */
2216         for (x=0;x<5;x++)
2217                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2218         keys[6] = 0x0;
2219         keys[7] = 0x0;
2220
2221         if (!msg) {
2222                 /* No prev key, provide "Folder" instead */
2223                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2224         }
2225         if (msg >= last) {
2226                 /* If last message ... */
2227                 if (msg) {
2228                         /* but not only message, provide "Folder" instead */
2229                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2230       bytes += adsi_voice_mode(buf + bytes, 0);
2231
2232                 } else {
2233                         /* Otherwise if only message, leave blank */
2234                         keys[3] = 1;
2235                 }
2236         }
2237
2238         if (!ast_strlen_zero(cid)) {
2239                 ast_callerid_parse(cid, &name, &num);
2240                 if (!name)
2241                         name = num;
2242         } else
2243                 name = "Unknown Caller";
2244
2245         /* If deleted, show "undeleted" */
2246
2247         if (deleted)
2248                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2249
2250         /* Except "Exit" */
2251         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2252         snprintf(buf1, sizeof(buf1), "%s%s", folder,
2253                  strcasecmp(folder, "INBOX") ? " Messages" : "");
2254         snprintf(buf2, sizeof(buf2), "Message %d of %d", msg + 1, last + 1);
2255
2256         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2257         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2258         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2259         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2260         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2261         bytes += adsi_set_keys(buf + bytes, keys);
2262         bytes += adsi_voice_mode(buf + bytes, 0);
2263
2264         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2265 }
2266
2267 static void adsi_delete(struct ast_channel *chan, int msg, int last, int deleted)
2268 {
2269         int bytes=0;
2270         char buf[256];
2271         unsigned char keys[8];
2272
2273         int x;
2274
2275         if (!adsi_available(chan))
2276                 return;
2277
2278         /* New meaning for keys */
2279         for (x=0;x<5;x++)
2280                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2281
2282         keys[6] = 0x0;
2283         keys[7] = 0x0;
2284
2285         if (!msg) {
2286                 /* No prev key, provide "Folder" instead */
2287                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2288         }
2289         if (msg >= last) {
2290                 /* If last message ... */
2291                 if (msg) {
2292                         /* but not only message, provide "Folder" instead */
2293                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2294                 } else {
2295                         /* Otherwise if only message, leave blank */
2296                         keys[3] = 1;
2297                 }
2298         }
2299
2300         /* If deleted, show "undeleted" */
2301         if (deleted) 
2302                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2303
2304         /* Except "Exit" */
2305         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2306         bytes += adsi_set_keys(buf + bytes, keys);
2307         bytes += adsi_voice_mode(buf + bytes, 0);
2308
2309         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2310 }
2311
2312 static void adsi_status(struct ast_channel *chan, int new, int old, int lastmsg)
2313 {
2314         char buf[256], buf1[256], buf2[256];
2315         int bytes=0;
2316         unsigned char keys[8];
2317         int x;
2318
2319         char *newm = (new == 1) ? "message" : "messages";
2320         char *oldm = (old == 1) ? "message" : "messages";
2321         if (!adsi_available(chan))
2322                 return;
2323         if (new) {
2324                 snprintf(buf1, sizeof(buf1), "You have %d new", new);
2325                 if (old) {
2326                         strcat(buf1, " and");
2327                         snprintf(buf2, sizeof(buf2), "%d old %s.", old, oldm);
2328                 } else {
2329                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2330                 }
2331         } else if (old) {
2332                 snprintf(buf1, sizeof(buf1), "You have %d old", old);
2333                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2334         } else {
2335                 strcpy(buf1, "You have no messages.");
2336                 strcpy(buf2, " ");
2337         }
2338         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2339         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2340         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2341
2342         for (x=0;x<6;x++)
2343                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2344         keys[6] = 0;
2345         keys[7] = 0;
2346
2347         /* Don't let them listen if there are none */
2348         if (lastmsg < 0)
2349                 keys[0] = 1;
2350         bytes += adsi_set_keys(buf + bytes, keys);
2351
2352         bytes += adsi_voice_mode(buf + bytes, 0);
2353
2354         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2355 }
2356
2357 static void adsi_status2(struct ast_channel *chan, char *folder, int messages)
2358 {
2359         char buf[256], buf1[256], buf2[256];
2360         int bytes=0;
2361         unsigned char keys[8];
2362         int x;
2363
2364         char *mess = (messages == 1) ? "message" : "messages";
2365
2366         if (!adsi_available(chan))
2367                 return;
2368
2369         /* Original command keys */
2370         for (x=0;x<6;x++)
2371                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2372
2373         keys[6] = 0;
2374         keys[7] = 0;
2375
2376         if (messages < 1)
2377                 keys[0] = 0;
2378
2379         snprintf(buf1, sizeof(buf1), "%s%s has", folder,
2380                         strcasecmp(folder, "INBOX") ? " folder" : "");
2381
2382         if (messages)
2383                 snprintf(buf2, sizeof(buf2), "%d %s.", messages, mess);
2384         else
2385                 strcpy(buf2, "no messages.");
2386         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2387         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2388         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2389         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2390         bytes += adsi_set_keys(buf + bytes, keys);
2391
2392         bytes += adsi_voice_mode(buf + bytes, 0);
2393
2394         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2395         
2396 }
2397
2398 /*
2399 static void adsi_clear(struct ast_channel *chan)
2400 {
2401         char buf[256];
2402         int bytes=0;
2403         if (!adsi_available(chan))
2404                 return;
2405         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2406         bytes += adsi_voice_mode(buf + bytes, 0);
2407
2408         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2409 }
2410 */
2411
2412 static void adsi_goodbye(struct ast_channel *chan)
2413 {
2414         char buf[256];
2415         int bytes=0;
2416
2417         if (!adsi_available(chan))
2418                 return;
2419         bytes += adsi_logo(buf + bytes);
2420         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2421         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
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 /*--- get_folder: Folder menu ---*/
2429 /* Plays "press 1 for INBOX messages" etc
2430    Should possibly be internationalized
2431  */
2432 static int get_folder(struct ast_channel *chan, int start)
2433 {
2434         int x;
2435         int d;
2436         char fn[256];
2437         d = play_and_wait(chan, "vm-press");    /* "Press" */
2438         if (d)
2439                 return d;
2440         for (x = start; x< 5; x++) {    /* For all folders */
2441                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2442                         return d;
2443                 d = play_and_wait(chan, "vm-for");      /* "for" */
2444                 if (d)
2445                         return d;
2446                 if (!strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "fr") || !strcasecmp(chan->language, "pt")) { /* Spanish, French or Portuguese syntax */
2447                         d = play_and_wait(chan, "vm-messages"); /* "messages */
2448                         if (d)
2449                                 return d;
2450                         snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
2451                         d = play_and_wait(chan, fn);
2452                         if (d)
2453                                 return d;
2454                 } else {  /* Default English */
2455                         snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
2456                         d = play_and_wait(chan, fn);
2457                         if (d)
2458                                 return d;
2459                         d = play_and_wait(chan, "vm-messages"); /* "messages */
2460                         if (d)
2461                                 return d;
2462                 }
2463                 d = ast_waitfordigit(chan, 500);
2464                 if (d)
2465                         return d;
2466         }
2467         d = play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2468         if (d)
2469                 return d;
2470         d = ast_waitfordigit(chan, 4000);
2471         return d;
2472 }
2473
2474 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2475 {
2476         int res = 0;
2477         res = play_and_wait(chan, fn);  /* Folder name */
2478         while (((res < '0') || (res > '9')) &&
2479                         (res != '#') && (res >= 0)) {
2480                 res = get_folder(chan, 0);
2481         }
2482         return res;
2483 }
2484
2485 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2486 {
2487         int cmd = 0;
2488         int retries = 0;
2489         int duration = 0;
2490
2491         while((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2492                 if (cmd)
2493                         retries = 0;
2494                 switch (cmd) {
2495                 case '1': 
2496                         /* prepend a message to the current message and return */
2497                 {
2498                         char file[200];
2499                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2500                         cmd = play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1);
2501                         break;
2502                 }
2503                 case '2': 
2504                         cmd = 't';
2505                         break;
2506                 case '*':
2507                         cmd = '*';
2508                         break;
2509                 default: 
2510                         cmd = play_and_wait(chan,"vm-forwardoptions");
2511                                 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2512                         if (!cmd)
2513                                 cmd = play_and_wait(chan,"vm-starmain");
2514                                 /* "press star to return to the main menu" */
2515                         if (!cmd)
2516                                 cmd = ast_waitfordigit(chan,6000);
2517                         if (!cmd)
2518                                 retries++;
2519                         if (retries > 3)
2520                                 cmd = 't';
2521                  }
2522         }
2523         if (cmd == 't')
2524                 cmd = 0;
2525         return cmd;
2526 }
2527
2528 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *callerid)
2529 {
2530         char todir[256], fn[256], *stringp;
2531
2532         make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2533         make_file(fn, sizeof(fn), todir, msgnum);
2534
2535         /* Attach only the first format */
2536         fmt = ast_strdupa(fmt);
2537         if (fmt) {
2538                 stringp = fmt;
2539                 strsep(&stringp, "|");
2540
2541                 if (!ast_strlen_zero(vmu->email)) {
2542                         int attach_user_voicemail = attach_voicemail;
2543                         char *myserveremail = serveremail;
2544                         if (vmu->attach > -1)
2545                                 attach_user_voicemail = vmu->attach;
2546                         if (!ast_strlen_zero(vmu->serveremail))
2547                                 myserveremail = vmu->serveremail;
2548                         sendmail(myserveremail, vmu, msgnum, vmu->mailbox, callerid, fn, fmt, duration, attach_user_voicemail);
2549                 }
2550
2551                 if (!ast_strlen_zero(vmu->pager)) {
2552                         char *myserveremail = serveremail;
2553                         if (!ast_strlen_zero(vmu->serveremail))
2554                                 myserveremail = vmu->serveremail;
2555                         sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, callerid, duration, vmu);
2556                 }
2557         } else {
2558                 ast_log(LOG_ERROR, "Out of memory\n");
2559         }
2560
2561         if (vmu->delete) {
2562                 vm_delete(fn);
2563         }
2564
2565         /* Leave voicemail for someone */
2566         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", vmu->mailbox, ast_app_has_voicemail(vmu->mailbox));
2567         run_externnotify(chan->context, vmu->mailbox, ast_app_has_voicemail(vmu->mailbox));
2568         return 0;
2569 }
2570
2571 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt)
2572 {
2573         char username[70];
2574         char sys[256];
2575         char todir[256];
2576         int todircount=0;
2577         int duration;
2578         struct ast_config *mif;
2579         char miffile[256];
2580         char fn[256];
2581         char callerid[512];
2582         char ext_context[256]="";
2583         int res = 0, cmd = 0;
2584         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2585         char tmp[256];
2586         char *stringp, *s;
2587         int saved_messages = 0, found = 0;
2588         int valid_extensions = 0;
2589         while (!res && !valid_extensions) {
2590                 res = ast_streamfile(chan, "vm-extension", chan->language);     /* "extension" */
2591                 if (res)
2592                         break;
2593                 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2594                         break;
2595                 /* start all over if no username */
2596                 if (ast_strlen_zero(username))
2597                         continue;
2598                 stringp = username;
2599                 s = strsep(&stringp, "*");
2600                 /* start optimistic */
2601                 valid_extensions = 1;
2602                 while (s) {
2603                         /* find_user is going to malloc since we have a NULL as first argument */
2604                         if ((receiver = find_user(NULL, context, s))) {
2605                                 if (!extensions)
2606                                         vmtmp = extensions = receiver;
2607                                 else {
2608                                         vmtmp->next = receiver;
2609                                         vmtmp = receiver;
2610                                 }
2611                                 found++;
2612                         } else {
2613                                 valid_extensions = 0;
2614                                 break;
2615                         }
2616                         s = strsep(&stringp, "*");
2617                 }
2618                 /* break from the loop of reading the extensions */
2619                 if (valid_extensions)
2620                         break;
2621                 /* "I am sorry, that's not a valid extension.  Please try again." */
2622                 res = play_and_wait(chan, "pbx-invalid");
2623         }
2624         /* check if we're clear to proceed */
2625         if (!extensions || !valid_extensions)
2626                 return res;
2627         vmtmp = extensions;
2628         cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
2629         if (!cmd) {
2630                 while(!res && vmtmp) {
2631                         /* if (play_and_wait(chan, "vm-savedto"))
2632                                 break;
2633                         */
2634                         snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
2635                         snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
2636                         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
2637                         ast_log(LOG_DEBUG, sys);
2638                         ast_safe_system(sys);
2639         
2640                         todircount = count_messages(todir);
2641                         strncpy(tmp, fmt, sizeof(tmp) - 1);
2642                         stringp = tmp;
2643                         while((s = strsep(&stringp, "|"))) {
2644                                 /* XXX This is a hack -- we should use build_filename or similar XXX */
2645                                 if (!strcasecmp(s, "wav49"))
2646                                         s = "WAV";
2647                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
2648                                 ast_log(LOG_DEBUG, sys);
2649                                 ast_safe_system(sys);
2650                         }
2651                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
2652                         ast_log(LOG_DEBUG, sys);
2653                         ast_safe_system(sys);
2654                         snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
2655         
2656                         /* load the information on the source message so we can send an e-mail like a new message */
2657                         snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
2658                         if ((mif=ast_load(miffile))) {
2659         
2660                                 /* set callerid and duration variables */
2661                                 snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
2662                                 s = ast_variable_retrieve(mif, NULL, "duration");
2663                                 if (s)
2664                                         duration = atoi(s);
2665                                 else
2666                                         duration = 0;
2667                                 if (!ast_strlen_zero(vmtmp->email)) {
2668                                         int attach_user_voicemail = attach_voicemail;
2669                                         char *myserveremail = serveremail;
2670                                         if (vmtmp->attach > -1)
2671                                                 attach_user_voicemail = vmtmp->attach;
2672                                         if (!ast_strlen_zero(vmtmp->serveremail))
2673                                                 myserveremail = vmtmp->serveremail;
2674                                         sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, callerid, fn, tmp, duration, attach_user_voicemail);
2675                                 }
2676                              
2677                                 if (!ast_strlen_zero(vmtmp->pager)) {
2678                                         char *myserveremail = serveremail;
2679                                         if (!ast_strlen_zero(vmtmp->serveremail))
2680                                                 myserveremail = vmtmp->serveremail;
2681                                         sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, callerid, duration, vmtmp);
2682                                 }
2683                                   
2684                                 ast_destroy(mif); /* or here */
2685                         }
2686                         /* Leave voicemail for someone */
2687                         manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context));
2688                         run_externnotify(chan->context, ext_context, ast_app_has_voicemail(ext_context));
2689         
2690                         saved_messages++;
2691                         vmfree = vmtmp;
2692                         vmtmp = vmtmp->next;
2693                         free_user(vmfree);
2694                 }
2695                 if (saved_messages > 0) {
2696                         /* give confirmation that the message was saved */
2697                         /* commented out since we can't forward batches yet
2698                         if (saved_messages == 1)
2699                                 res = play_and_wait(chan, "vm-message");
2700                         else
2701                                 res = play_and_wait(chan, "vm-messages");
2702                         if (!res)
2703                                 res = play_and_wait(chan, "vm-saved"); */
2704                         if (!res)
2705                                 res = play_and_wait(chan, "vm-msgsaved");
2706                 }
2707         }
2708         return res ? res : cmd;
2709 }
2710
2711
2712 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
2713 {
2714         int res;
2715         if ((res = ast_streamfile(chan, file, chan->language))) 
2716                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
2717         if (!res)
2718                 res = ast_waitstream(chan, AST_DIGIT_ANY);
2719         return res;
2720 }
2721
2722 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
2723 {
2724         return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
2725 }
2726
2727 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
2728 {
2729         int res = 0;
2730         struct vm_zone *the_zone = NULL;
2731         time_t t;
2732         long tin;
2733
2734         if (sscanf(origtime,"%ld",&tin) < 1) {
2735                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
2736                 return 0;
2737         }
2738         t = tin;
2739
2740         /* Does this user have a timezone specified? */
2741         if (!ast_strlen_zero(vmu->zonetag)) {
2742                 /* Find the zone in the list */
2743                 struct vm_zone *z;
2744                 z = zones;
2745                 while (z) {
2746                         if (!strcmp(z->name, vmu->zonetag)) {
2747                                 the_zone = z;
2748                                 break;
2749                         }
2750                         z = z->next;
2751                 }
2752         }
2753
2754 /* No internal variable parsing for now, so we'll comment it out for the time being */
2755 #if 0
2756         /* Set the DIFF_* variables */
2757         localtime_r(&t, &time_now);
2758         gettimeofday(&tv_now,NULL);
2759         tnow = tv_now.tv_sec;
2760         localtime_r(&tnow,&time_then);
2761
2762         /* Day difference */
2763         if (time_now.tm_year == time_then.tm_year)
2764                 sprintf(temp,"%d",time_now.tm_yday);
2765         else
2766                 sprintf(temp,"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
2767         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
2768
2769         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
2770 #endif
2771         if (the_zone)
2772                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
2773         else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
2774                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
2775         else
2776                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
2777 #if 0
2778         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
2779 #endif
2780         return res;
2781 }
2782
2783
2784
2785 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
2786 {
2787         int res = 0;
2788         int i;
2789         char *callerid, *name;
2790         char prefile[256]="";
2791         
2792
2793         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
2794         /* 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 */
2795         if((cid == NULL)||(context == NULL))
2796                 return res;
2797
2798         /* Strip off caller ID number from name */
2799         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
2800         ast_callerid_parse(cid, &name, &callerid);
2801         if((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
2802                 /* Check for internal contexts and only */
2803                 /* say extension when the call didn't come from an internal context in the list */
2804                 for(i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
2805                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
2806                         if((strcmp(cidinternalcontexts[i], context) == 0))
2807                                 break;
2808                 }
2809                 if(i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
2810                         if(!res) {
2811                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
2812                                 if (!ast_strlen_zero(prefile)) {
2813                                 /* See if we can find a recorded name for this person instead of their extension number */
2814                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2815                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
2816                                                 if (!callback)
2817                                                         res = wait_file2(chan, vms, "vm-from");
2818                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
2819                                                 res = ast_waitstream(chan, "");
2820                                         } else {
2821                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
2822                                                 /* BB: Say "from extension" as one saying to sound smoother */
2823                                                 if (!callback)
2824                                                         res = wait_file2(chan, vms, "vm-from-extension");
2825                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
2826                                         }
2827                                 }
2828                         }
2829                 }
2830
2831                 else if (!res){
2832                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
2833                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
2834                         if (!callback)
2835                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
2836                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
2837                 }
2838         }
2839         else{
2840                 /* Number unknown */
2841                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number");
2842                 if(!res)
2843                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
2844                         res = wait_file2(chan, vms, "vm-unknown-caller");
2845         }
2846         return res;                                                               
2847 }
2848
2849 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg)
2850 {
2851         int res = 0;
2852         char filename[256],*origtime, *cid, *context;
2853         struct ast_config *msg_cfg;
2854
2855         vms->starting = 0; 
2856         make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2857         adsi_message(chan, vms->curbox, msg, vms->lastmsg, vms->deleted[msg], vms->fn);
2858         if (!msg)
2859                 res = wait_file2(chan, vms, "vm-first");        /* "First" */
2860         else if (msg == vms->lastmsg)
2861                 res = wait_file2(chan, vms, "vm-last");         /* "last" */
2862         if (!res) {
2863                 res = wait_file2(chan, vms, "vm-message");      /* "message" */
2864                 if (msg && (msg != vms->lastmsg)) {
2865                         if (!res)
2866                                 res = ast_say_number(chan, msg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
2867                 }
2868         }
2869
2870         /* Retrieve info from VM attribute file */
2871         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
2872         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
2873         msg_cfg = ast_load(filename);
2874         if (!msg_cfg) {
2875                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
2876                 return 0;
2877         }
2878                                                                                                                                  
2879         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")))
2880                 return 0;
2881                                                                                                                                  
2882         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
2883
2884         context = ast_variable_retrieve(msg_cfg, "message", "context");
2885         if(!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
2886                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
2887
2888         if ((!res)&&(vmu->envelope))
2889                 res = play_message_datetime(chan, vmu, origtime, filename);
2890         if ((!res)&&(vmu->saycid))
2891                 res = play_message_callerid(chan, vms, cid, context, 0);
2892         /* Allow pressing '1' to skip envelope / callerid */
2893         if (res == '1')
2894                 res = 0;
2895         ast_destroy(msg_cfg);
2896
2897         if (!res) {
2898                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
2899                 vms->heard[msg] = 1;
2900                 res = wait_file(chan, vms, vms->fn);
2901         }
2902         return res;
2903 }
2904
2905 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
2906 {
2907         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
2908         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2909         vms->lastmsg = count_messages(vms->curdir) - 1;
2910         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
2911 }
2912
2913 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
2914 {
2915         int x;
2916         char ntxt[256] = "";
2917         char txt[256] = "";
2918         if (vms->lastmsg > -1) { 
2919                 /* Get the deleted messages fixed */ 
2920                 vms->curmsg = -1; 
2921                 for (x=0;x < MAXMSG;x++) { 
2922                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
2923                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
2924                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2925                                 if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2926                                         break;
2927                                 vms->curmsg++; 
2928                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
2929                                 if (strcmp(vms->fn, vms->fn2)) { 
2930                                         snprintf(txt, sizeof(txt), "%s.txt", vms->fn); 
2931                                         snprintf(ntxt, sizeof(ntxt), "%s.txt", vms->fn2); 
2932                                         ast_filerename(vms->fn, vms->fn2, NULL); 
2933                                         rename(txt, ntxt); 
2934                                 } 
2935                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
2936                                 /* Move to old folder before deleting */ 
2937                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
2938                         } 
2939                 } 
2940                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
2941                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
2942                         if (ast_fileexists(vms->fn, NULL, NULL) < 1) 
2943                                 break;
2944                         vm_delete(vms->fn);
2945                 } 
2946         } 
2947         memset(vms->deleted, 0, sizeof(vms->deleted)); 
2948         memset(vms->heard, 0, sizeof(vms->heard)); 
2949 }
2950
2951 /* Default English syntax */
2952 static int vm_intro(struct ast_channel *chan,struct vm_state *vms)
2953 {
2954         /* Introduce messages they have */
2955         int res;
2956         res = play_and_wait(chan, "vm-youhave");
2957         if (!res) {
2958                 if (vms->newmessages) {
2959                         res = say_and_wait(chan, vms->newmessages, chan->language);
2960                         if (!res)
2961                                 res = play_and_wait(chan, "vm-INBOX");
2962                         if (vms->oldmessages && !res)
2963                                 res = play_and_wait(chan, "vm-and");
2964                         else if (!res) {
2965                                 if ((vms->newmessages == 1))
2966