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