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