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