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