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