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