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