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