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