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