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