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