e3a884a339504da0c0e7493957a93e1597103216
[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);
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         struct ast_config *cfg=NULL;
1137         odbc_obj *obj;
1138
1139         delete_file(dir, msgnum);
1140         obj = fetch_odbc_obj(odbc_database);
1141         if (obj) {
1142                 if (dir[0] != '/') {
1143                         snprintf(rdir, sizeof(rdir), "%s/%s", ast_config_AST_SPOOL_DIR, dir);
1144                         dir = rdir;
1145                 }
1146                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
1147                 c = strchr(fmt, '|');
1148                 if (c)
1149                         *c = '\0';
1150                 if (!strcasecmp(fmt, "wav49"))
1151                         strncpy(fmt, "WAV", sizeof(fmt));
1152                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1153                 if (msgnum > -1)
1154                         make_file(fn, sizeof(fn), dir, msgnum);
1155                 else
1156                         strncpy(fn, dir, sizeof(fn) - 1);
1157                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1158                 cfg = ast_load(full_fn);
1159                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1160                 fd = open(full_fn, O_RDWR);
1161                 if (fd < 0) {
1162                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1163                         goto yuck;
1164                 }
1165                 if (cfg) {
1166                         context = ast_variable_retrieve(cfg, "message", "context");
1167                         if (!context) context = "";
1168                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1169                         if (!macrocontext) macrocontext = "";
1170                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1171                         if (!callerid) callerid = "";
1172                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1173                         if (!origtime) origtime = "";
1174                         duration = ast_variable_retrieve(cfg, "message", "duration");
1175                         if (!duration) duration = "";
1176                 }
1177                 fdlen = lseek(fd, 0, SEEK_END);
1178                 lseek(fd, 0, SEEK_SET);
1179                 printf("Length is %d\n", fdlen);
1180                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1181                 if (!fdm) {
1182                         ast_log(LOG_WARNING, "Memory map failed!\n");
1183                         goto yuck;
1184                 } 
1185                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1186                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1187                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1188                         goto yuck;
1189                 }
1190                 snprintf(sql, sizeof(sql), "INSERT INTO voicemessages (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration) VALUES (?,?,?,?,?,?,?,?)");
1191                 res = SQLPrepare(stmt, sql, SQL_NTS);
1192                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1193                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1194                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1195                         goto yuck;
1196                 }
1197                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1198                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1199                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1200                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1201                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1202                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1203                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1204                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1205                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1206                 res = SQLExecute(stmt);
1207                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1208                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1209                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1210                         goto yuck;
1211                 }
1212                 res = SQLRowCount(stmt, &rowcount);
1213                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1214                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1215                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1216                         goto yuck;
1217                 }
1218                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1219         } else
1220                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1221 yuck:   
1222         if (cfg)
1223                 ast_destroy(cfg);
1224         if (fdm)
1225                 munmap(fdm, fdlen);
1226         if (fd > -1)
1227                 close(fd);
1228         return x;
1229 }
1230
1231 static void rename_file(char *sdir, int smsg, char *ddir, int dmsg)
1232 {
1233         int res;
1234         SQLLEN rowcount=0;
1235         SQLHSTMT stmt;
1236         char sql[256];
1237         char msgnums[20];
1238         char msgnumd[20];
1239         char rsdir[256];
1240         char rddir[256];
1241         odbc_obj *obj;
1242
1243         delete_file(ddir, dmsg);
1244         obj = fetch_odbc_obj(odbc_database);
1245         if (obj) {
1246                 if (ddir[0] != '/') {
1247                         snprintf(rddir, sizeof(rddir), "%s/%s", ast_config_AST_SPOOL_DIR, ddir);
1248                         ddir = rddir;
1249                 }
1250                 if (sdir[0] != '/') {
1251                         snprintf(rsdir, sizeof(rsdir), "%s/%s", ast_config_AST_SPOOL_DIR, sdir);
1252                         sdir = rsdir;
1253                 }
1254                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1255                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1256                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1257                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1258                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1259                         goto yuck;
1260                 }
1261                 snprintf(sql, sizeof(sql), "UPDATE voicemessages SET dir=?, msgnum=? WHERE dir=? AND msgnum=?");
1262                 res = SQLPrepare(stmt, sql, SQL_NTS);
1263                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1264                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1265                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1266                         goto yuck;
1267                 }
1268                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1269                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1270                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1271                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1272                 res = SQLExecute(stmt);
1273                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1274                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1275                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1276                         goto yuck;
1277                 }
1278                 res = SQLRowCount(stmt, &rowcount);
1279                 if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
1280                         ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
1281                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1282                         goto yuck;
1283                 }
1284                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1285         } else
1286                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1287 yuck:
1288         return; 
1289 }
1290
1291 #else
1292 static int count_messages(char *dir)
1293 {
1294         // Find all .txt files - even if they are not in sequence from 0000
1295
1296         int vmcount = 0;
1297         DIR *vmdir = NULL;
1298         struct dirent *vment = NULL;
1299
1300         if ((vmdir = opendir(dir))) {
1301                 while ((vment = readdir(vmdir)))
1302                 {
1303                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7,".txt",4))
1304                         {
1305                                 vmcount++;
1306                         }
1307                 }
1308                 closedir(vmdir);
1309         }
1310
1311         return vmcount;
1312 }
1313
1314 static void rename_file(char *sfn, char *dfn)
1315 {
1316         char stxt[256];
1317         char dtxt[256];
1318         ast_filerename(sfn,dfn,NULL);
1319         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1320         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1321         rename(stxt, dtxt);
1322 }
1323
1324 static int copy(char *infile, char *outfile)
1325 {
1326         int ifd;
1327         int ofd;
1328         int res;
1329         int len;
1330         char buf[4096];
1331
1332 #ifdef HARDLINK_WHEN_POSSIBLE
1333         /* Hard link if possible; saves disk space & is faster */
1334         if (link(infile, outfile)) {
1335 #endif
1336                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1337                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1338                         return -1;
1339                 }
1340                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) {
1341                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1342                         close(ifd);
1343                         return -1;
1344                 }
1345                 do {
1346                         len = read(ifd, buf, sizeof(buf));
1347                         if (len < 0) {
1348                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1349                                 close(ifd);
1350                                 close(ofd);
1351                                 unlink(outfile);
1352                         }
1353                         if (len) {
1354                                 res = write(ofd, buf, len);
1355                                 if (res != len) {
1356                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1357                                         close(ifd);
1358                                         close(ofd);
1359                                         unlink(outfile);
1360                                 }
1361                         }
1362                 } while (len);
1363                 close(ifd);
1364                 close(ofd);
1365                 return 0;
1366 #ifdef HARDLINK_WHEN_POSSIBLE
1367         } else {
1368                 /* Hard link succeeded */
1369                 return 0;
1370         }
1371 #endif
1372 }
1373
1374 static void copy_file(char *frompath, char *topath)
1375 {
1376         char frompath2[256],topath2[256];
1377         ast_filecopy(frompath, topath, NULL);
1378         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1379         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1380         copy(frompath2, topath2);
1381 }
1382
1383 static int last_message_index(char *dir)
1384 {
1385         int x;
1386         char fn[256];
1387         for (x=0;x<MAXMSG;x++) {
1388                 make_file(fn, sizeof(fn), dir, x);
1389                 if (ast_fileexists(fn, NULL, NULL) < 1)
1390                         break;
1391         }
1392         return x-1;
1393 }
1394
1395 static int vm_delete(char *file)
1396 {
1397         char *txt;
1398         int txtsize = 0;
1399
1400         txtsize = (strlen(file) + 5)*sizeof(char);
1401         txt = (char *)alloca(txtsize);
1402         /* Sprintf here would safe because we alloca'd exactly the right length,
1403          * but trying to eliminate all sprintf's anyhow
1404          */
1405         snprintf(txt, txtsize, "%s.txt", file);
1406         unlink(txt);
1407         return ast_filedelete(file, NULL);
1408 }
1409
1410
1411 #endif
1412 static int
1413 inbuf(struct baseio *bio, FILE *fi)
1414 {
1415         int l;
1416
1417         if (bio->ateof)
1418                 return 0;
1419
1420         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1421                 if (ferror(fi))
1422                         return -1;
1423
1424                 bio->ateof = 1;
1425                 return 0;
1426         }
1427
1428         bio->iolen= l;
1429         bio->iocp= 0;
1430
1431         return 1;
1432 }
1433
1434 static int 
1435 inchar(struct baseio *bio, FILE *fi)
1436 {
1437         if (bio->iocp>=bio->iolen) {
1438                 if (!inbuf(bio, fi))
1439                         return EOF;
1440         }
1441
1442         return bio->iobuf[bio->iocp++];
1443 }
1444
1445 static int
1446 ochar(struct baseio *bio, int c, FILE *so)
1447 {
1448         if (bio->linelength>=BASELINELEN) {
1449                 if (fputs(eol,so)==EOF)
1450                         return -1;
1451
1452                 bio->linelength= 0;
1453         }
1454
1455         if (putc(((unsigned char)c),so)==EOF)
1456                 return -1;
1457
1458         bio->linelength++;
1459
1460         return 1;
1461 }
1462
1463 static int base_encode(char *filename, FILE *so)
1464 {
1465         unsigned char dtable[BASEMAXINLINE];
1466         int i,hiteof= 0;
1467         FILE *fi;
1468         struct baseio bio;
1469
1470         memset(&bio, 0, sizeof(bio));
1471         bio.iocp = BASEMAXINLINE;
1472
1473         if (!(fi = fopen(filename, "rb"))) {
1474                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1475                 return -1;
1476         }
1477
1478         for (i= 0;i<9;i++) {
1479                 dtable[i]= 'A'+i;
1480                 dtable[i+9]= 'J'+i;
1481                 dtable[26+i]= 'a'+i;
1482                 dtable[26+i+9]= 'j'+i;
1483         }
1484         for (i= 0;i<8;i++) {
1485                 dtable[i+18]= 'S'+i;
1486                 dtable[26+i+18]= 's'+i;
1487         }
1488         for (i= 0;i<10;i++) {
1489                 dtable[52+i]= '0'+i;
1490         }
1491         dtable[62]= '+';
1492         dtable[63]= '/';
1493
1494         while (!hiteof){
1495                 unsigned char igroup[3],ogroup[4];
1496                 int c,n;
1497
1498                 igroup[0]= igroup[1]= igroup[2]= 0;
1499
1500                 for (n= 0;n<3;n++) {
1501                         if ((c = inchar(&bio, fi)) == EOF) {
1502                                 hiteof= 1;
1503                                 break;
1504                         }
1505
1506                         igroup[n]= (unsigned char)c;
1507                 }
1508
1509                 if (n> 0) {
1510                         ogroup[0]= dtable[igroup[0]>>2];
1511                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1512                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1513                         ogroup[3]= dtable[igroup[2]&0x3F];
1514
1515                         if (n<3) {
1516                                 ogroup[3]= '=';
1517
1518                                 if (n<2)
1519                                         ogroup[2]= '=';
1520                         }
1521
1522                         for (i= 0;i<4;i++)
1523                                 ochar(&bio, ogroup[i], so);
1524                 }
1525         }
1526
1527         if (fputs(eol,so)==EOF)
1528                 return 0;
1529
1530         fclose(fi);
1531
1532         return 1;
1533 }
1534
1535 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)
1536 {
1537         char callerid[256];
1538         /* Prepare variables for substition in email body and subject */
1539         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1540         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1541         snprintf(passdata, passdatasize, "%d", msgnum);
1542         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1543         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1544         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1545         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1546         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1547         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1548 }
1549
1550 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)
1551 {
1552         FILE *p=NULL;
1553         int pfd;
1554         char date[256];
1555         char host[256];
1556         char who[256];
1557         char bound[256];
1558         char fname[256];
1559         char dur[256];
1560         char tmp[80] = "/tmp/astmail-XXXXXX";
1561         char tmp2[256];
1562         time_t t;
1563         struct tm tm;
1564         struct vm_zone *the_zone = NULL;
1565         if (vmu && ast_strlen_zero(vmu->email)) {
1566                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1567                 return(0);
1568         }
1569         if (!strcmp(format, "wav49"))
1570                 format = "WAV";
1571         ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, attach_voicemail);
1572         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1573            command hangs */
1574         pfd = mkstemp(tmp);
1575         if (pfd > -1) {
1576                 p = fdopen(pfd, "w");
1577                 if (!p) {
1578                         close(pfd);
1579                         pfd = -1;
1580                 }
1581         }
1582         if (p) {
1583                 gethostname(host, sizeof(host));
1584                 if (strchr(srcemail, '@'))
1585                         strncpy(who, srcemail, sizeof(who)-1);
1586                 else {
1587                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1588                 }
1589                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1590                 time(&t);
1591
1592                 /* Does this user have a timezone specified? */
1593                 if (!ast_strlen_zero(vmu->zonetag)) {
1594                         /* Find the zone in the list */
1595                         struct vm_zone *z;
1596                         z = zones;
1597                         while (z) {
1598                                 if (!strcmp(z->name, vmu->zonetag)) {
1599                                         the_zone = z;
1600                                         break;
1601                                 }
1602                                 z = z->next;
1603                         }
1604                 }
1605
1606                 if (the_zone)
1607                         ast_localtime(&t,&tm,the_zone->timezone);
1608                 else
1609                         ast_localtime(&t,&tm,NULL);
1610                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1611                 fprintf(p, "Date: %s\n", date);
1612
1613                 if (*fromstring) {
1614                         struct ast_channel *ast = ast_channel_alloc(0);
1615                         if (ast) {
1616                                 char *passdata;
1617                                 int vmlen = strlen(fromstring)*3 + 200;
1618                                 if ((passdata = alloca(vmlen))) {
1619                                         memset(passdata, 0, vmlen);
1620                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1621                                         pbx_substitute_variables_helper(ast,fromstring,passdata,vmlen);
1622                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1623                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1624                                 ast_channel_free(ast);
1625                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1626                 } else
1627                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1628                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1629
1630                 if (emailsubject) {
1631                         struct ast_channel *ast = ast_channel_alloc(0);
1632                         if (ast) {
1633                                 char *passdata;
1634                                 int vmlen = strlen(emailsubject)*3 + 200;
1635                                 if ((passdata = alloca(vmlen))) {
1636                                         memset(passdata, 0, vmlen);
1637                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1638                                         pbx_substitute_variables_helper(ast,emailsubject,passdata,vmlen);
1639                                         fprintf(p, "Subject: %s\n",passdata);
1640                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1641                                 ast_channel_free(ast);
1642                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1643                 } else
1644                 if (*emailtitle) {
1645                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1646                         fprintf(p,"\n") ;
1647                 } else if (pbxskip)
1648                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1649                 else
1650                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1651                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)rand(), mailbox, getpid(), host);
1652                 fprintf(p, "MIME-Version: 1.0\n");
1653                 if (attach_user_voicemail) {
1654                         /* Something unique. */
1655                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)rand());
1656
1657                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1658
1659                         fprintf(p, "--%s\n", bound);
1660                 }
1661                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1662                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1663                 if (emailbody) {
1664                         struct ast_channel *ast = ast_channel_alloc(0);
1665                         if (ast) {
1666                                 char *passdata;
1667                                 int vmlen = strlen(emailbody)*3 + 200;
1668                                 if ((passdata = alloca(vmlen))) {
1669                                         memset(passdata, 0, vmlen);
1670                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1671                                         pbx_substitute_variables_helper(ast,emailbody,passdata,vmlen);
1672                                         fprintf(p, "%s\n",passdata);
1673                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1674                                 ast_channel_free(ast);
1675                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1676                 } else {
1677                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1678
1679                         "in mailbox %s from %s, on %s so you might\n"
1680                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1681                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1682                 }
1683                 if (attach_user_voicemail) {
1684                         fprintf(p, "--%s\n", bound);
1685                         fprintf(p, "Content-Type: audio/x-%s; name=\"msg%04d.%s\"\n", format, msgnum, format);
1686                         fprintf(p, "Content-Transfer-Encoding: base64\n");
1687                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1688                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1689
1690                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1691                         base_encode(fname, p);
1692                         fprintf(p, "\n\n--%s--\n.\n", bound);
1693                 }
1694                 fclose(p);
1695                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1696                 ast_safe_system(tmp2);
1697                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
1698         } else {
1699                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1700                 return -1;
1701         }
1702         return 0;
1703 }
1704
1705 static int sendpage(char *srcemail, char *pager, int msgnum, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu)
1706 {
1707         FILE *p=NULL;
1708         int pfd;
1709         char date[256];
1710         char host[256];
1711         char who[256];
1712         char dur[256];
1713         char tmp[80] = "/tmp/astmail-XXXXXX";
1714         char tmp2[256];
1715         time_t t;
1716         struct tm tm;
1717         struct vm_zone *the_zone = NULL;
1718         pfd = mkstemp(tmp);
1719
1720         if (pfd > -1) {
1721                 p = fdopen(pfd, "w");
1722                 if (!p) {
1723                         close(pfd);
1724                         pfd = -1;
1725                 }
1726         }
1727
1728         if (p) {
1729                 gethostname(host, sizeof(host));
1730                 if (strchr(srcemail, '@'))
1731                         strncpy(who, srcemail, sizeof(who)-1);
1732                 else {
1733                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1734                 }
1735                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1736                 time(&t);
1737
1738                 /* Does this user have a timezone specified? */
1739                 if (!ast_strlen_zero(vmu->zonetag)) {
1740                         /* Find the zone in the list */
1741                         struct vm_zone *z;
1742                         z = zones;
1743                         while (z) {
1744                                 if (!strcmp(z->name, vmu->zonetag)) {
1745                                         the_zone = z;
1746                                         break;
1747                                 }
1748                                 z = z->next;
1749                         }
1750                 }
1751
1752                 if (the_zone)
1753                         ast_localtime(&t,&tm,the_zone->timezone);
1754                 else
1755                         ast_localtime(&t,&tm,NULL);
1756
1757                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1758                 fprintf(p, "Date: %s\n", date);
1759
1760                 if (*pagerfromstring) {
1761                         struct ast_channel *ast = ast_channel_alloc(0);
1762                         if (ast) {
1763                                 char *passdata;
1764                                 int vmlen = strlen(fromstring)*3 + 200;
1765                                 if ((passdata = alloca(vmlen))) {
1766                                         memset(passdata, 0, vmlen);
1767                                         prep_email_sub_vars(ast,vmu,msgnum + 1,mailbox,cidnum, cidname,dur,date,passdata, vmlen);
1768                                         pbx_substitute_variables_helper(ast,pagerfromstring,passdata,vmlen);
1769                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1770                                 } else 
1771                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1772                                 ast_channel_free(ast);
1773                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1774                 } else
1775                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1776                 fprintf(p, "To: %s\n", pager);
1777                 fprintf(p, "Subject: New VM\n\n");
1778                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
1779                 fprintf(p, "New %s long msg in box %s\n"
1780                            "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
1781                 fclose(p);
1782                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
1783                 ast_safe_system(tmp2);
1784                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
1785         } else {
1786                 ast_log(LOG_WARNING, "Unable to launch '%s'\n", mailcmd);
1787                 return -1;
1788         }
1789         return 0;
1790 }
1791
1792 static int get_date(char *s, int len)
1793 {
1794         struct tm tm;
1795         time_t t;
1796         t = time(0);
1797         localtime_r(&t,&tm);
1798         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
1799 }
1800
1801 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
1802 {
1803         int res;
1804         char fn[256];
1805         snprintf(fn, sizeof(fn), "voicemail/%s/%s/greet", context, ext);
1806         RETRIEVE(fn, -1);
1807         if (ast_fileexists(fn, NULL, NULL) > 0) {
1808                 res = ast_streamfile(chan, fn, chan->language);
1809                 if (res) {
1810                         DISPOSE(fn, -1);
1811                         return -1;
1812                 }
1813                 res = ast_waitstream(chan, ecodes);
1814                 if (res) {
1815                         DISPOSE(fn, -1);
1816                         return res;
1817                 }
1818         } else {
1819                 /* Dispose just in case */
1820                 DISPOSE(fn, -1);
1821                 res = ast_streamfile(chan, "vm-theperson", chan->language);
1822                 if (res)
1823                         return -1;
1824                 res = ast_waitstream(chan, ecodes);
1825                 if (res)
1826                         return res;
1827                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
1828                 if (res)
1829                         return res;
1830         }
1831         if (busy)
1832                 res = ast_streamfile(chan, "vm-isonphone", chan->language);
1833         else
1834                 res = ast_streamfile(chan, "vm-isunavail", chan->language);
1835         if (res)
1836                 return -1;
1837         res = ast_waitstream(chan, ecodes);
1838         return res;
1839 }
1840
1841 static void free_user(struct ast_vm_user *vmu)
1842 {
1843         if (vmu->alloced)
1844                 free(vmu);
1845 }
1846
1847 static void free_zone(struct vm_zone *z)
1848 {
1849         free(z);
1850 }
1851
1852 static char *mbox(int id)
1853 {
1854         switch(id) {
1855         case 0:
1856                 return "INBOX";
1857         case 1:
1858                 return "Old";
1859         case 2:
1860                 return "Work";
1861         case 3:
1862                 return "Family";
1863         case 4:
1864                 return "Friends";
1865         case 5:
1866                 return "Cust1";
1867         case 6:
1868                 return "Cust2";
1869         case 7:
1870                 return "Cust3";
1871         case 8:
1872                 return "Cust4";
1873         case 9:
1874                 return "Cust5";
1875         default:
1876                 return "Unknown";
1877         }
1878 }
1879
1880 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
1881
1882 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)
1883 {
1884         char fromdir[256], todir[256], frompath[256], topath[256];
1885         char *frombox = mbox(imbox);
1886         int recipmsgnum;
1887
1888         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
1889
1890         make_dir(todir, sizeof(todir), recip->context, "", "");
1891         /* It's easier just to try to make it than to check for its existence */
1892         if (mkdir(todir, 0700) && (errno != EEXIST))
1893                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1894         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "");
1895         /* It's easier just to try to make it than to check for its existence */
1896         if (mkdir(todir, 0700) && (errno != EEXIST))
1897                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1898         make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
1899         if (mkdir(todir, 0700) && (errno != EEXIST))
1900                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", todir, strerror(errno));
1901
1902         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
1903         make_file(frompath, sizeof(frompath), fromdir, msgnum);
1904         recipmsgnum = 0;
1905         do {
1906                 make_file(topath, sizeof(topath), todir, recipmsgnum);
1907                 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
1908                         break;
1909                 recipmsgnum++;
1910         } while (recipmsgnum < MAXMSG);
1911         if (recipmsgnum < MAXMSG) {
1912                 COPY(fromdir, msgnum, todir, recipmsgnum, frompath, topath);
1913         } else {
1914                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
1915         }
1916
1917         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
1918 }
1919
1920 static void run_externnotify(char *context, char *extension)
1921 {
1922         char arguments[255];
1923         int newvoicemails = 0, oldvoicemails = 0;
1924
1925         if (!ast_strlen_zero(externnotify)) {
1926                 if (ast_app_messagecount(extension, &newvoicemails, &oldvoicemails)) {
1927                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
1928                 } else {
1929                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
1930                         ast_log(LOG_DEBUG, "Executing %s\n", arguments);
1931                         ast_safe_system(arguments);
1932                 }
1933         }
1934 }
1935
1936
1937 static int leave_voicemail(struct ast_channel *chan, char *ext, int silent, int busy, int unavail)
1938 {
1939         char txtfile[256];
1940         char callerid[256];
1941         FILE *txt;
1942         int res = 0;
1943         int msgnum;
1944         int fd;
1945         int duration = 0;
1946         int ausemacro = 0;
1947         int ousemacro = 0;
1948         char date[256];
1949         char dir[256];
1950         char fn[256];
1951         char prefile[256]="";
1952         char tempfile[256]="";
1953         char ext_context[256] = "";
1954         char fmt[80];
1955         char *context;
1956         char ecodes[16] = "#";
1957         char tmp[256] = "", *tmpptr;
1958         struct ast_vm_user *vmu;
1959         struct ast_vm_user svm;
1960
1961         strncpy(tmp, ext, sizeof(tmp) - 1);
1962         ext = tmp;
1963         context = strchr(tmp, '@');
1964         if (context) {
1965                 *context = '\0';
1966                 context++;
1967                 tmpptr = strchr(context, '&');
1968         } else {
1969                 tmpptr = strchr(ext, '&');
1970         }
1971
1972         if (tmpptr) {
1973                 *tmpptr = '\0';
1974                 tmpptr++;
1975         }
1976
1977         if ((vmu = find_user(&svm, context, ext))) {
1978                 /* Setup pre-file if appropriate */
1979                 if (strcmp(vmu->context, "default"))
1980                         snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
1981                 else
1982                         strncpy(ext_context, vmu->context, sizeof(ext_context) - 1);            
1983                 if (busy)
1984                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/busy", vmu->context, ext);
1985                 else if (unavail)
1986                         snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/unavail", vmu->context, ext);
1987                 snprintf(tempfile, sizeof(tempfile), "voicemail/%s/%s/temp", vmu->context, ext);
1988                 RETRIEVE(tempfile, -1);
1989                 if (ast_fileexists(tempfile, NULL, NULL) > 0)
1990                         strncpy(prefile, tempfile, sizeof(prefile) - 1);
1991                 DISPOSE(tempfile, -1);
1992                 make_dir(dir, sizeof(dir), vmu->context, "", "");
1993                 /* It's easier just to try to make it than to check for its existence */
1994                 if (mkdir(dir, 0700) && (errno != EEXIST))
1995                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
1996                 make_dir(dir, sizeof(dir), vmu->context, ext, "");
1997                 /* It's easier just to try to make it than to check for its existence */
1998                 if (mkdir(dir, 0700) && (errno != EEXIST))
1999                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2000                 make_dir(dir, sizeof(dir), vmu->context, ext, "INBOX");
2001                 if (mkdir(dir, 0700) && (errno != EEXIST))
2002                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
2003
2004                 /* Check current or macro-calling context for special extensions */
2005                 if (!ast_strlen_zero(vmu->exit)) {
2006                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num))
2007                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2008                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num))
2009                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2010                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2011                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2012                         ousemacro = 1;
2013                 }
2014
2015                 if (!ast_strlen_zero(vmu->exit)) {
2016                         if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2017                                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2018                 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2019                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2020                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2021                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2022                         ausemacro = 1;
2023                 }
2024
2025
2026                 /* Play the beginning intro if desired */
2027                 if (!ast_strlen_zero(prefile)) {
2028                         RETRIEVE(prefile, -1);
2029                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
2030                                 if (ast_streamfile(chan, prefile, chan->language) > -1) 
2031                                         res = ast_waitstream(chan, ecodes);
2032                         } else {
2033                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2034                                 res = invent_message(chan, vmu->context, ext, busy, ecodes);
2035                         }
2036                         DISPOSE(prefile, -1);
2037                         if (res < 0) {
2038                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2039                                 free_user(vmu);
2040                                 return -1;
2041                         }
2042                 }
2043                 if (res == '#') {
2044                         /* On a '#' we skip the instructions */
2045                         silent = 1;
2046                         res = 0;
2047                 }
2048                 if (!res && !silent) {
2049                         res = ast_streamfile(chan, INTRO, chan->language);
2050                         if (!res)
2051                                 res = ast_waitstream(chan, ecodes);
2052                         if (res == '#') {
2053                                 silent = 1;
2054                                 res = 0;
2055                         }
2056                 }
2057                 if (res > 0)
2058                         ast_stopstream(chan);
2059                 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2060                 other than the operator -- an automated attendant or mailbox login for example */
2061                 if (res == '*') {
2062                         strncpy(chan->exten, "a", sizeof(chan->exten) - 1);
2063                         if (!ast_strlen_zero(vmu->exit)) {
2064                                 strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2065                         } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2066                                 strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2067                         }
2068                         chan->priority = 0;
2069                         free_user(vmu);
2070                         return 0;
2071                 }
2072                 /* Check for a '0' here */
2073                 if (res == '0') {
2074                         transfer:
2075                         if (vmu->operator) {
2076                                 strncpy(chan->exten, "o", sizeof(chan->exten) - 1);
2077                                 if (!ast_strlen_zero(vmu->exit)) {
2078                                         strncpy(chan->context, vmu->exit, sizeof(chan->context) - 1);
2079                                 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2080                                         strncpy(chan->context, chan->macrocontext, sizeof(chan->context) - 1);
2081                                 }
2082                                 ast_play_and_wait(chan, "transfer");
2083                                 chan->priority = 0;
2084                                 free_user(vmu);
2085                                 return 0;
2086                         } else {
2087                                 ast_play_and_wait(chan, "vm-sorry");
2088                                 return 0;
2089                         }
2090                 }
2091                 if (res < 0) {
2092                         free_user(vmu);
2093                         return -1;
2094                 }
2095                 /* The meat of recording the message...  All the announcements and beeps have been played*/
2096                 strncpy(fmt, vmfmts, sizeof(fmt) - 1);
2097                 if (!ast_strlen_zero(fmt)) {
2098                         msgnum = 0;
2099                         do {
2100                                 make_file(fn, sizeof(fn), dir, msgnum);
2101                                 if (!EXISTS(dir,msgnum,fn,chan->language))
2102                                         break;
2103                                 msgnum++;
2104                         } while (msgnum < MAXMSG);
2105                         if (res >= 0) {
2106                                 /* Unless we're *really* silent, try to send the beep */
2107                                 res = ast_streamfile(chan, "beep", chan->language);
2108                                 if (!res)
2109                                         res = ast_waitstream(chan, "");
2110                         }
2111                         if (msgnum < MAXMSG) {
2112                                 /* Store information */
2113                                 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
2114                                 txt = fopen(txtfile, "w+");
2115                                 if (txt) {
2116                                         get_date(date, sizeof(date));
2117                                         fprintf(txt, 
2118 ";\n"
2119 "; Message Information file\n"
2120 ";\n"
2121 "[message]\n"
2122 "origmailbox=%s\n"
2123 "context=%s\n"
2124 "macrocontext=%s\n"
2125 "exten=%s\n"
2126 "priority=%d\n"
2127 "callerchan=%s\n"
2128 "callerid=%s\n"
2129 "origdate=%s\n"
2130 "origtime=%ld\n",
2131         ext,
2132         chan->context,
2133         chan->macrocontext, 
2134         chan->exten,
2135         chan->priority,
2136         chan->name,
2137         ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2138         date, (long)time(NULL));
2139                                         fclose(txt);
2140                                 } else
2141                                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2142                                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration);
2143                                 if (res == '0')
2144                                         goto transfer;
2145                                 if (res > 0)
2146                                         res = 0;
2147                                 fd = open(txtfile, O_APPEND | O_WRONLY);
2148                                 if (fd > -1) {
2149                                         txt = fdopen(fd, "a");
2150                                         if (txt) {
2151                                                 fprintf(txt, "duration=%d\n", duration);
2152                                                 fclose(txt);
2153                                         } else
2154                                                 close(fd);
2155                                 }
2156                                 if (duration < vmminmessage) {
2157                                         if (option_verbose > 2) 
2158                                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2159                                         DELETE(dir,msgnum,fn);
2160                                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
2161                                         goto leave_vm_out;
2162                                 }
2163                                 /* Are there to be more recipients of this message? */
2164                                 while (tmpptr) {
2165                                         struct ast_vm_user recipu, *recip;
2166                                         char *exten, *context;
2167
2168                                         exten = strsep(&tmpptr, "&");
2169                                         context = strchr(exten, '@');
2170                                         if (context) {
2171                                                 *context = '\0';
2172                                                 context++;
2173                                         }
2174                                         if ((recip = find_user(&recipu, context, exten))) {
2175                                                 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
2176                                                 free_user(recip);
2177                                         }
2178                                 }
2179                                 if (ast_fileexists(fn, NULL, NULL)) {
2180                                         notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2181                                         STORE(dir, msgnum);
2182                                         DISPOSE(dir, msgnum);
2183                                 }
2184                         } else {
2185                                 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2186                                 if (!res)
2187                                         res = ast_waitstream(chan, "");
2188                                 ast_log(LOG_WARNING, "No more messages possible\n");
2189                         }
2190                 } else
2191                         ast_log(LOG_WARNING, "No format for saving voicemail?\n");
2192 leave_vm_out:
2193                 free_user(vmu);
2194         } else {
2195                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2196                 /*Send the call to n+101 priority, where n is the current priority*/
2197                 if (ast_exists_extension(chan, chan->context, chan->exten, chan->priority + 101, chan->cid.cid_num))
2198                         chan->priority+=100;
2199         }
2200
2201         return res;
2202 }
2203
2204 #ifdef USE_ODBC_STORAGE
2205 #endif
2206
2207 static void resequence_mailbox(char * dir)
2208 {
2209         // we know max messages, so stop process when number is hit
2210
2211         int x,dest;
2212         char sfn[256];
2213         char dfn[256];
2214
2215         for (x=0,dest=0;x<MAXMSG;x++) {
2216                 make_file(sfn, sizeof(sfn), dir, x);
2217                 if (EXISTS(dir, x, sfn, NULL)) {
2218
2219                         if(x != dest) {
2220                                 make_file(dfn, sizeof(dfn), dir, dest);
2221                                 RENAME(dir, x, dir, dest, sfn, dfn);
2222                         }
2223
2224                         dest++;
2225                 }
2226         }
2227 }
2228
2229
2230 static int say_and_wait(struct ast_channel *chan, int num, char *language)
2231 {
2232         int d;
2233         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
2234         return d;
2235 }
2236
2237 static int save_to_folder(char *dir, int msg, char *context, char *username, int box)
2238 {
2239         char sfn[256];
2240         char dfn[256];
2241         char ddir[256];
2242         char *dbox = mbox(box);
2243         int x;
2244         make_file(sfn, sizeof(sfn), dir, msg);
2245         make_dir(ddir, sizeof(ddir), context, username, dbox);
2246         mkdir(ddir, 0700);
2247         for (x=0;x<MAXMSG;x++) {
2248                 make_file(dfn, sizeof(dfn), ddir, x);
2249                 if (!EXISTS(ddir, x, dfn, NULL))
2250                         break;
2251         }
2252         if (x >= MAXMSG)
2253                 return -1;
2254         if (strcmp(sfn, dfn)) {
2255                 COPY(dir, msg, ddir, x, sfn, dfn);
2256         }
2257         return 0;
2258 }
2259
2260 static int adsi_logo(unsigned char *buf)
2261 {
2262         int bytes = 0;
2263         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
2264         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
2265         return bytes;
2266 }
2267
2268 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
2269 {
2270         char buf[256];
2271         int bytes=0;
2272         int x;
2273         char num[5];
2274
2275         *useadsi = 0;
2276         bytes += adsi_data_mode(buf + bytes);
2277         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2278
2279         bytes = 0;
2280         bytes += adsi_logo(buf);
2281         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2282 #ifdef DISPLAY
2283         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
2284 #endif
2285         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2286         bytes += adsi_data_mode(buf + bytes);
2287         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2288
2289         if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
2290                 bytes = 0;
2291                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
2292                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2293                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2294                 bytes += adsi_voice_mode(buf + bytes, 0);
2295                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2296                 return 0;
2297         }
2298
2299 #ifdef DISPLAY
2300         /* Add a dot */
2301         bytes = 0;
2302         bytes += adsi_logo(buf);
2303         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
2304         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
2305         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2306         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2307 #endif
2308         bytes = 0;
2309         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
2310         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
2311         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
2312         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
2313         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
2314         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
2315         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2316
2317 #ifdef DISPLAY
2318         /* Add another dot */
2319         bytes = 0;
2320         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
2321         bytes += adsi_voice_mode(buf + bytes, 0);
2322
2323         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2324         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2325 #endif
2326
2327         bytes = 0;
2328         /* These buttons we load but don't use yet */
2329         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
2330         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
2331         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
2332         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
2333         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
2334         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
2335         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2336
2337 #ifdef DISPLAY
2338         /* Add another dot */
2339         bytes = 0;
2340         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
2341         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2342         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2343 #endif
2344
2345         bytes = 0;
2346         for (x=0;x<5;x++) {
2347                 snprintf(num, sizeof(num), "%d", x);
2348                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
2349         }
2350         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
2351         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2352
2353 #ifdef DISPLAY
2354         /* Add another dot */
2355         bytes = 0;
2356         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
2357         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2358         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2359 #endif
2360
2361         if (adsi_end_download(chan)) {
2362                 bytes = 0;
2363                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
2364                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
2365                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2366                 bytes += adsi_voice_mode(buf + bytes, 0);
2367                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2368                 return 0;
2369         }
2370         bytes = 0;
2371         bytes += adsi_download_disconnect(buf + bytes);
2372         bytes += adsi_voice_mode(buf + bytes, 0);
2373         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
2374
2375         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
2376
2377 #ifdef DISPLAY
2378         /* Add last dot */
2379         bytes = 0;
2380         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
2381         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2382 #endif
2383         ast_log(LOG_DEBUG, "Restarting session...\n");
2384
2385         bytes = 0;
2386         /* Load the session now */
2387         if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
2388                 *useadsi = 1;
2389                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
2390         } else
2391                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
2392
2393         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2394         return 0;
2395 }
2396
2397 static void adsi_begin(struct ast_channel *chan, int *useadsi)
2398 {
2399         int x;
2400         if (!adsi_available(chan))
2401                 return;
2402         x = adsi_load_session(chan, adsifdn, adsiver, 1);
2403         if (x < 0)
2404                 return;
2405         if (!x) {
2406                 if (adsi_load_vmail(chan, useadsi)) {
2407                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
2408                         return;
2409                 }
2410         } else
2411                 *useadsi = 1;
2412 }
2413
2414 static void adsi_login(struct ast_channel *chan)
2415 {
2416         char buf[256];
2417         int bytes=0;
2418         unsigned char keys[8];
2419         int x;
2420         if (!adsi_available(chan))
2421                 return;
2422
2423         for (x=0;x<8;x++)
2424                 keys[x] = 0;
2425         /* Set one key for next */
2426         keys[3] = ADSI_KEY_APPS + 3;
2427
2428         bytes += adsi_logo(buf + bytes);
2429         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
2430         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
2431         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2432         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
2433         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
2434         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
2435         bytes += adsi_set_keys(buf + bytes, keys);
2436         bytes += adsi_voice_mode(buf + bytes, 0);
2437         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2438 }
2439
2440 static void adsi_password(struct ast_channel *chan)
2441 {
2442         char buf[256];
2443         int bytes=0;
2444         unsigned char keys[8];
2445         int x;
2446         if (!adsi_available(chan))
2447                 return;
2448
2449         for (x=0;x<8;x++)
2450                 keys[x] = 0;
2451         /* Set one key for next */
2452         keys[3] = ADSI_KEY_APPS + 3;
2453
2454         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2455         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
2456         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
2457         bytes += adsi_set_keys(buf + bytes, keys);
2458         bytes += adsi_voice_mode(buf + bytes, 0);
2459         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2460 }
2461
2462 static void adsi_folders(struct ast_channel *chan, int start, char *label)
2463 {
2464         char buf[256];
2465         int bytes=0;
2466         unsigned char keys[8];
2467         int x,y;
2468
2469         if (!adsi_available(chan))
2470                 return;
2471
2472         for (x=0;x<5;x++) {
2473                 y = ADSI_KEY_APPS + 12 + start + x;
2474                 if (y > ADSI_KEY_APPS + 12 + 4)
2475                         y = 0;
2476                 keys[x] = ADSI_KEY_SKT | y;
2477         }
2478         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
2479         keys[6] = 0;
2480         keys[7] = 0;
2481
2482         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
2483         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
2484         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2485         bytes += adsi_set_keys(buf + bytes, keys);
2486         bytes += adsi_voice_mode(buf + bytes, 0);
2487
2488         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2489 }
2490
2491 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
2492 {
2493         int bytes=0;
2494         char buf[256], buf1[256], buf2[256];
2495         char fn2[256];
2496
2497         char cid[256]="";
2498         char *val;
2499         char *name, *num;
2500         char datetime[21]="";
2501         FILE *f;
2502
2503         unsigned char keys[8];
2504
2505         int x;
2506
2507         if (!adsi_available(chan))
2508                 return;
2509
2510         /* Retrieve important info */
2511         snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
2512         f = fopen(fn2, "r");
2513         if (f) {
2514                 while (!feof(f)) {      
2515                         fgets(buf, sizeof(buf), f);
2516                         if (!feof(f)) {
2517                                 char *stringp=NULL;
2518                                 stringp=buf;
2519                                 strsep(&stringp, "=");
2520                                 val = strsep(&stringp, "=");
2521                                 if (val && !ast_strlen_zero(val)) {
2522                                         if (!strcmp(buf, "callerid"))
2523                                                 strncpy(cid, val, sizeof(cid) - 1);
2524                                         if (!strcmp(buf, "origdate"))
2525                                                 strncpy(datetime, val, sizeof(datetime) - 1);
2526                                 }
2527                         }
2528                 }
2529                 fclose(f);
2530         }
2531         /* New meaning for keys */
2532         for (x=0;x<5;x++)
2533                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2534         keys[6] = 0x0;
2535         keys[7] = 0x0;
2536
2537         if (!vms->curmsg) {
2538                 /* No prev key, provide "Folder" instead */
2539                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2540         }
2541         if (vms->curmsg >= vms->lastmsg) {
2542                 /* If last message ... */
2543                 if (vms->curmsg) {
2544                         /* but not only message, provide "Folder" instead */
2545                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2546                         bytes += adsi_voice_mode(buf + bytes, 0);
2547
2548                 } else {
2549                         /* Otherwise if only message, leave blank */
2550                         keys[3] = 1;
2551                 }
2552         }
2553
2554         if (!ast_strlen_zero(cid)) {
2555                 ast_callerid_parse(cid, &name, &num);
2556                 if (!name)
2557                         name = num;
2558         } else
2559                 name = "Unknown Caller";
2560
2561         /* If deleted, show "undeleted" */
2562
2563         if (vms->deleted[vms->curmsg])
2564                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2565
2566         /* Except "Exit" */
2567         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2568         snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
2569                 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
2570         snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
2571
2572         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2573         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2574         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
2575         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
2576         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2577         bytes += adsi_set_keys(buf + bytes, keys);
2578         bytes += adsi_voice_mode(buf + bytes, 0);
2579
2580         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2581 }
2582
2583 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
2584 {
2585         int bytes=0;
2586         char buf[256];
2587         unsigned char keys[8];
2588
2589         int x;
2590
2591         if (!adsi_available(chan))
2592                 return;
2593
2594         /* New meaning for keys */
2595         for (x=0;x<5;x++)
2596                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
2597
2598         keys[6] = 0x0;
2599         keys[7] = 0x0;
2600
2601         if (!vms->curmsg) {
2602                 /* No prev key, provide "Folder" instead */
2603                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2604         }
2605         if (vms->curmsg >= vms->lastmsg) {
2606                 /* If last message ... */
2607                 if (vms->curmsg) {
2608                         /* but not only message, provide "Folder" instead */
2609                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
2610                 } else {
2611                         /* Otherwise if only message, leave blank */
2612                         keys[3] = 1;
2613                 }
2614         }
2615
2616         /* If deleted, show "undeleted" */
2617         if (vms->deleted[vms->curmsg]) 
2618                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
2619
2620         /* Except "Exit" */
2621         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
2622         bytes += adsi_set_keys(buf + bytes, keys);
2623         bytes += adsi_voice_mode(buf + bytes, 0);
2624
2625         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2626 }
2627
2628 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
2629 {
2630         char buf[256] = "", buf1[256] = "", buf2[256] = "";
2631         int bytes=0;
2632         unsigned char keys[8];
2633         int x;
2634
2635         char *newm = (vms->newmessages == 1) ? "message" : "messages";
2636         char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
2637         if (!adsi_available(chan))
2638                 return;
2639         if (vms->newmessages) {
2640                 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
2641                 if (vms->oldmessages) {
2642                         strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
2643                         snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
2644                 } else {
2645                         snprintf(buf2, sizeof(buf2), "%s.", newm);
2646                 }
2647         } else if (vms->oldmessages) {
2648                 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
2649                 snprintf(buf2, sizeof(buf2), "%s.", oldm);
2650         } else {
2651                 strncpy(buf1, "You have no messages.", sizeof(buf1) - 1);
2652                 buf2[0] = ' ';
2653                 buf2[1] = '\0';
2654         }
2655         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2656         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2657         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2658
2659         for (x=0;x<6;x++)
2660                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2661         keys[6] = 0;
2662         keys[7] = 0;
2663
2664         /* Don't let them listen if there are none */
2665         if (vms->lastmsg < 0)
2666                 keys[0] = 1;
2667         bytes += adsi_set_keys(buf + bytes, keys);
2668
2669         bytes += adsi_voice_mode(buf + bytes, 0);
2670
2671         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2672 }
2673
2674 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
2675 {
2676         char buf[256] = "", buf1[256] = "", buf2[256] = "";
2677         int bytes=0;
2678         unsigned char keys[8];
2679         int x;
2680
2681         char *mess = (vms->lastmsg == 0) ? "message" : "messages";
2682
2683         if (!adsi_available(chan))
2684                 return;
2685
2686         /* Original command keys */
2687         for (x=0;x<6;x++)
2688                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
2689
2690         keys[6] = 0;
2691         keys[7] = 0;
2692
2693         if ((vms->lastmsg + 1) < 1)
2694                 keys[0] = 0;
2695
2696         snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
2697                 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
2698
2699         if (vms->lastmsg + 1)
2700                 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
2701         else
2702                 strncpy(buf2, "no messages.", sizeof(buf2) - 1);
2703         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
2704         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
2705         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
2706         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2707         bytes += adsi_set_keys(buf + bytes, keys);
2708
2709         bytes += adsi_voice_mode(buf + bytes, 0);
2710
2711         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2712         
2713 }
2714
2715 /*
2716 static void adsi_clear(struct ast_channel *chan)
2717 {
2718         char buf[256];
2719         int bytes=0;
2720         if (!adsi_available(chan))
2721                 return;
2722         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2723         bytes += adsi_voice_mode(buf + bytes, 0);
2724
2725         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2726 }
2727 */
2728
2729 static void adsi_goodbye(struct ast_channel *chan)
2730 {
2731         char buf[256];
2732         int bytes=0;
2733
2734         if (!adsi_available(chan))
2735                 return;
2736         bytes += adsi_logo(buf + bytes);
2737         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
2738         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
2739         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
2740         bytes += adsi_voice_mode(buf + bytes, 0);
2741
2742         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
2743 }
2744
2745 /*--- get_folder: Folder menu ---*/
2746 /* Plays "press 1 for INBOX messages" etc
2747    Should possibly be internationalized
2748  */
2749 static int get_folder(struct ast_channel *chan, int start)
2750 {
2751         int x;
2752         int d;
2753         char fn[256];
2754         d = ast_play_and_wait(chan, "vm-press");        /* "Press" */
2755         if (d)
2756                 return d;
2757         for (x = start; x< 5; x++) {    /* For all folders */
2758                 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
2759                         return d;
2760                 d = ast_play_and_wait(chan, "vm-for");  /* "for" */
2761                 if (d)
2762                         return d;
2763                 snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
2764                 d = vm_play_folder_name(chan, fn);
2765                 if (d)
2766                         return d;
2767                 d = ast_waitfordigit(chan, 500);
2768                 if (d)
2769                         return d;
2770         }
2771         d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
2772         if (d)
2773                 return d;
2774         d = ast_waitfordigit(chan, 4000);
2775         return d;
2776 }
2777
2778 static int get_folder2(struct ast_channel *chan, char *fn, int start)
2779 {
2780         int res = 0;
2781         res = ast_play_and_wait(chan, fn);      /* Folder name */
2782         while (((res < '0') || (res > '9')) &&
2783                         (res != '#') && (res >= 0)) {
2784                 res = get_folder(chan, 0);
2785         }
2786         return res;
2787 }
2788
2789 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfts, char *context)
2790 {
2791         int cmd = 0;
2792         int retries = 0;
2793         int duration = 0;
2794
2795         while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
2796                 if (cmd)
2797                         retries = 0;
2798                 switch (cmd) {
2799                 case '1': 
2800                         /* prepend a message to the current message and return */
2801                 {
2802                         char file[200];
2803                         snprintf(file, sizeof(file), "%s/msg%04d", curdir, curmsg);
2804                         cmd = ast_play_and_prepend(chan, NULL, file, 0, vmfmts, &duration, 1, silencethreshold, maxsilence);
2805                         break;
2806                 }
2807                 case '2': 
2808                         cmd = 't';
2809                         break;
2810                 case '*':
2811                         cmd = '*';
2812                         break;
2813                 default: 
2814                         cmd = ast_play_and_wait(chan,"vm-forwardoptions");
2815                                 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
2816                         if (!cmd)
2817                                 cmd = ast_play_and_wait(chan,"vm-starmain");
2818                                 /* "press star to return to the main menu" */
2819                         if (!cmd)
2820                                 cmd = ast_waitfordigit(chan,6000);
2821                         if (!cmd)
2822                                 retries++;
2823                         if (retries > 3)
2824                                 cmd = 't';
2825                  }
2826         }
2827         if (cmd == 't')
2828                 cmd = 0;
2829         return cmd;
2830 }
2831
2832 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
2833 {
2834         char todir[256], fn[256], ext_context[256], *stringp;
2835
2836         make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
2837         make_file(fn, sizeof(fn), todir, msgnum);
2838         snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
2839
2840         /* Attach only the first format */
2841         fmt = ast_strdupa(fmt);
2842         if (fmt) {
2843                 stringp = fmt;
2844                 strsep(&stringp, "|");
2845
2846                 if (!ast_strlen_zero(vmu->email)) {
2847                         int attach_user_voicemail = attach_voicemail;
2848                         char *myserveremail = serveremail;
2849                         if (vmu->attach > -1)
2850                                 attach_user_voicemail = vmu->attach;
2851                         if (!ast_strlen_zero(vmu->serveremail))
2852                                 myserveremail = vmu->serveremail;
2853                         sendmail(myserveremail, vmu, msgnum, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail);
2854                 }
2855
2856                 if (!ast_strlen_zero(vmu->pager)) {
2857                         char *myserveremail = serveremail;
2858                         if (!ast_strlen_zero(vmu->serveremail))
2859                                 myserveremail = vmu->serveremail;
2860                         sendpage(myserveremail, vmu->pager, msgnum, vmu->mailbox, cidnum, cidname, duration, vmu);
2861                 }
2862         } else {
2863                 ast_log(LOG_ERROR, "Out of memory\n");
2864         }
2865
2866         if (vmu->delete) {
2867                 DELETE(todir, msgnum, fn);
2868         }
2869
2870         /* Leave voicemail for someone */
2871         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));
2872         run_externnotify(chan->context, ext_context);
2873         return 0;
2874 }
2875
2876 static int forward_message(struct ast_channel *chan, char *context, char *dir, int curmsg, struct ast_vm_user *sender, char *fmt,int flag)
2877 {
2878         char username[70]="";
2879         char sys[256];
2880         char todir[256];
2881         int todircount=0;
2882         int duration;
2883         struct ast_config *mif;
2884         char miffile[256];
2885         char fn[256];
2886         char callerid[512];
2887         char ext_context[256]="";
2888         int res = 0, cmd = 0;
2889         struct ast_vm_user *receiver, *extensions = NULL, *vmtmp = NULL, *vmfree;
2890         char tmp[256];
2891         char *stringp, *s;
2892         int saved_messages = 0, found = 0;
2893         int valid_extensions = 0;
2894         
2895         while (!res && !valid_extensions) {
2896                 
2897                 int use_directory = 0;
2898                 if( directory_forward ) {
2899                         int done = 0;
2900                         int retries = 0;
2901                         cmd=0;
2902                         while((cmd >= 0) && !done ){
2903                                 if (cmd)
2904                                         retries = 0;
2905                                 switch (cmd) {
2906                                 case '1': 
2907                                         use_directory = 0;
2908                                         done = 1;
2909                                         break;
2910                                 case '2': 
2911                                         use_directory = 1;
2912                                         done=1;
2913                                         break;
2914                                 case '*': 
2915                                         cmd = 't';
2916                                         done = 1;
2917                                         break;
2918                                 default: 
2919                                         /* Press 1 to enter an extension press 2 to use the directory */
2920                                         cmd = ast_play_and_wait(chan,"vm-forward");
2921                                         if (!cmd)
2922                                                 cmd = ast_waitfordigit(chan,3000);
2923                                         if (!cmd)
2924                                                 retries++;
2925                                         if (retries > 3)
2926                                         {
2927                                                 cmd = 't';
2928                                                 done = 1;
2929                                         }
2930                                         
2931                                  }
2932                         }
2933                         if( cmd<0 || cmd=='t' )
2934                                 break;
2935                 }
2936                 
2937                 if( use_directory ) {
2938                         /* use app_directory */
2939                         
2940                         char old_context[sizeof(chan->context)];
2941                         char old_exten[sizeof(chan->exten)];
2942                         int old_priority;
2943                         struct ast_app* app;
2944
2945                         
2946                         app = pbx_findapp("Directory");
2947                         if (app) {
2948                                 /* make mackup copies */
2949                                 memcpy(old_context, chan->context, sizeof(chan->context));
2950                                 memcpy(old_exten, chan->exten, sizeof(chan->exten));
2951                                 old_priority = chan->priority;
2952                                 
2953                                 /* call the the Directory, changes the channel */
2954                                 res = pbx_exec(chan, app, ((context)?context:chan->context), 1);
2955                                 
2956                                 strncpy(username, chan->exten, sizeof(username)-1);
2957                                 
2958                                 /* restore the old context, exten, and priority */
2959                                 memcpy(chan->context, old_context, sizeof(chan->context));
2960                                 memcpy(chan->exten, old_exten, sizeof(chan->exten));
2961                                 chan->priority = old_priority;
2962                                 
2963                         } else {
2964                                 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
2965                                 directory_forward = 0;
2966                         }
2967                 } else  {
2968                         /* Ask for an extension */
2969                         res = ast_streamfile(chan, "vm-extension", chan->language);     /* "extension" */
2970                         if (res)
2971                                 break;
2972                         if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
2973                                 break;
2974                 }
2975                 
2976                 /* start all over if no username */
2977                 if (ast_strlen_zero(username))
2978                         continue;
2979                 stringp = username;
2980                 s = strsep(&stringp, "*");
2981                 /* start optimistic */
2982                 valid_extensions = 1;
2983                 while (s) {
2984                         /* find_user is going to malloc since we have a NULL as first argument */
2985                         if ((receiver = find_user(NULL, context, s))) {
2986                                 if (!extensions)
2987                                         vmtmp = extensions = receiver;
2988                                 else {
2989                                         vmtmp->next = receiver;
2990                                         vmtmp = receiver;
2991                                 }
2992                                 found++;
2993                         } else {
2994                                 valid_extensions = 0;
2995                                 break;
2996                         }
2997                         s = strsep(&stringp, "*");
2998                 }
2999                 /* break from the loop of reading the extensions */
3000                 if (valid_extensions)
3001                         break;
3002                 /* "I am sorry, that's not a valid extension.  Please try again." */
3003                 res = ast_play_and_wait(chan, "pbx-invalid");
3004         }
3005         /* check if we're clear to proceed */
3006         if (!extensions || !valid_extensions)
3007                 return res;
3008         vmtmp = extensions;
3009         if (flag==1) {
3010                 /* Send VoiceMail */
3011                 cmd=leave_voicemail(chan,username,0,0,0);
3012         } else {
3013                 /* Forward VoiceMail */
3014                 cmd = vm_forwardoptions(chan, sender, dir, curmsg, vmfmts, context);
3015                 if (!cmd) {
3016                         while (!res && vmtmp) {
3017                                 /* if (ast_play_and_wait(chan, "vm-savedto"))
3018                                         break;
3019                                 */
3020                                 snprintf(todir, sizeof(todir), "%s/voicemail/%s/%s/INBOX",  (char *)ast_config_AST_SPOOL_DIR, vmtmp->context, vmtmp->mailbox);
3021                                 snprintf(sys, sizeof(sys), "mkdir -p %s\n", todir);
3022                                 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmtmp->mailbox, vmtmp->context);
3023                                 ast_log(LOG_DEBUG, sys);
3024                                 ast_safe_system(sys);
3025                 
3026                                 todircount = count_messages(todir);
3027                                 strncpy(tmp, fmt, sizeof(tmp) - 1);
3028                                 stringp = tmp;
3029                                 while ((s = strsep(&stringp, "|"))) {
3030                                         /* XXX This is a hack -- we should use build_filename or similar XXX */
3031                                         if (!strcasecmp(s, "wav49"))
3032                                                 s = "WAV";
3033                                         snprintf(sys, sizeof(sys), "cp %s/msg%04d.%s %s/msg%04d.%s\n", dir, curmsg, s, todir, todircount, s);
3034                                         ast_log(LOG_DEBUG, sys);
3035                                         ast_safe_system(sys);
3036                                 }
3037                                 snprintf(sys, sizeof(sys), "cp %s/msg%04d.txt %s/msg%04d.txt\n", dir, curmsg, todir, todircount);
3038                                 ast_log(LOG_DEBUG, sys);
3039                                 ast_safe_system(sys);
3040                                 snprintf(fn, sizeof(fn), "%s/msg%04d", todir,todircount);
3041         
3042                                 /* load the information on the source message so we can send an e-mail like a new message */
3043                                 snprintf(miffile, sizeof(miffile), "%s/msg%04d.txt", dir, curmsg);
3044                                 if ((mif=ast_load(miffile))) {
3045         
3046                                         /* set callerid and duration variables */
3047                                         snprintf(callerid, sizeof(callerid), "FWD from: %s from %s", sender->fullname, ast_variable_retrieve(mif, NULL, "callerid"));
3048                                         s = ast_variable_retrieve(mif, NULL, "duration");
3049                                         if (s)
3050                                                 duration = atoi(s);
3051                                         else
3052                                                 duration = 0;
3053                                         if (!ast_strlen_zero(vmtmp->email)) {
3054                                                 int attach_user_voicemail = attach_voicemail;
3055                                                 char *myserveremail = serveremail;
3056                                                 if (vmtmp->attach > -1)
3057                                                         attach_user_voicemail = vmtmp->attach;
3058                                                 if (!ast_strlen_zero(vmtmp->serveremail))
3059                                                         myserveremail = vmtmp->serveremail;
3060                                                 sendmail(myserveremail, vmtmp, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, fn, tmp, duration, attach_user_voicemail);
3061                                         }
3062
3063                                         if (!ast_strlen_zero(vmtmp->pager)) {
3064                                                 char *myserveremail = serveremail;
3065                                                 if (!ast_strlen_zero(vmtmp->serveremail))
3066                                                         myserveremail = vmtmp->serveremail;
3067                                                 sendpage(myserveremail, vmtmp->pager, todircount, vmtmp->mailbox, chan->cid.cid_num, chan->cid.cid_name, duration, vmtmp);
3068                                         }
3069                                   
3070                                         ast_destroy(mif); /* or here */
3071                                 }
3072                                 /* Leave voicemail for someone */
3073                                 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, ast_app_has_voicemail(ext_context, NULL));
3074                                 run_externnotify(chan->context, ext_context);
3075         
3076                                 saved_messages++;
3077                                 vmfree = vmtmp;
3078                                 vmtmp = vmtmp->next;
3079                                 free_user(vmfree);
3080                         }
3081                         if (saved_messages > 0) {
3082                                 /* give confirmation that the message was saved */
3083                                 /* commented out since we can't forward batches yet
3084                                 if (saved_messages == 1)
3085                                         res = ast_play_and_wait(chan, "vm-message");
3086                                 else
3087                                         res = ast_play_and_wait(chan, "vm-messages");
3088                                 if (!res)
3089                                         res = ast_play_and_wait(chan, "vm-saved"); */
3090                                 if (!res)
3091                                         res = ast_play_and_wait(chan, "vm-msgsaved");
3092                         }       
3093                 }
3094         }
3095         return res ? res : cmd;
3096 }
3097
3098 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
3099 {
3100         int res;
3101         if ((res = ast_streamfile(chan, file, chan->language))) 
3102                 ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
3103         if (!res)
3104                 res = ast_waitstream(chan, AST_DIGIT_ANY);
3105         return res;
3106 }
3107
3108 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
3109 {
3110         return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", skipms);
3111 }
3112
3113 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, char *origtime, char *filename)
3114 {
3115         int res = 0;
3116         struct vm_zone *the_zone = NULL;
3117         time_t t;
3118         long tin;
3119
3120         if (sscanf(origtime,"%ld",&tin) < 1) {
3121                 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
3122                 return 0;
3123         }
3124         t = tin;
3125
3126         /* Does this user have a timezone specified? */
3127         if (!ast_strlen_zero(vmu->zonetag)) {
3128                 /* Find the zone in the list */
3129                 struct vm_zone *z;
3130                 z = zones;
3131                 while (z) {
3132                         if (!strcmp(z->name, vmu->zonetag)) {
3133                                 the_zone = z;
3134                                 break;
3135                         }
3136                         z = z->next;
3137                 }
3138         }
3139
3140 /* No internal variable parsing for now, so we'll comment it out for the time being */
3141 #if 0
3142         /* Set the DIFF_* variables */
3143         localtime_r(&t, &time_now);
3144         gettimeofday(&tv_now,NULL);
3145         tnow = tv_now.tv_sec;
3146         localtime_r(&tnow,&time_then);
3147
3148         /* Day difference */
3149         if (time_now.tm_year == time_then.tm_year)
3150                 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
3151         else
3152                 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
3153         pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
3154
3155         /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
3156 #endif
3157         if (the_zone)
3158                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
3159         else if(!strcasecmp(chan->language,"de"))       /* GERMAN syntax */
3160                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
3161         else if (!strcasecmp(chan->language,"nl"))      /* DUTCH syntax */
3162                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
3163         else if (!strcasecmp(chan->language,"it"))      /* ITALIAN syntax */
3164                 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);
3165         else
3166                 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
3167 #if 0
3168         pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
3169 #endif
3170         return res;
3171 }
3172
3173
3174
3175 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, char *context, int callback)
3176 {
3177         int res = 0;
3178         int i;
3179         char *callerid, *name;
3180         char prefile[256]="";
3181         
3182
3183         /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
3184         /* 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 */
3185         if ((cid == NULL)||(context == NULL))
3186                 return res;
3187
3188         /* Strip off caller ID number from name */
3189         ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
3190         ast_callerid_parse(cid, &name, &callerid);
3191         if ((callerid != NULL)&&(!res)&&(!ast_strlen_zero(callerid))){
3192                 /* Check for internal contexts and only */
3193                 /* say extension when the call didn't come from an internal context in the list */
3194                 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
3195                         ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
3196                         if ((strcmp(cidinternalcontexts[i], context) == 0))
3197                                 break;
3198                 }
3199                 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
3200                         if (!res) {
3201                                 snprintf(prefile, sizeof(prefile), "voicemail/%s/%s/greet", context, callerid);
3202                                 if (!ast_strlen_zero(prefile)) {
3203                                 /* See if we can find a recorded name for this person instead of their extension number */
3204                                         if (ast_fileexists(prefile, NULL, NULL) > 0) {
3205                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
3206                                                 if (!callback)
3207                                                         res = wait_file2(chan, vms, "vm-from");
3208                                                 res = ast_streamfile(chan, prefile, chan->language) > -1;
3209                                                 res = ast_waitstream(chan, "");
3210                                         } else {
3211                                                 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
3212                                                 /* BB: Say "from extension" as one saying to sound smoother */
3213                                                 if (!callback)
3214                                                         res = wait_file2(chan, vms, "vm-from-extension");
3215                                                 res = ast_say_digit_str(chan, callerid, "", chan->language);
3216                                         }
3217                                 }
3218                         }
3219                 }
3220
3221                 else if (!res){
3222                         ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
3223                         /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
3224                         if (!callback)
3225                                 res = wait_file2(chan, vms, "vm-from-phonenumber");
3226                         res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
3227                 }
3228         } else {
3229                 /* Number unknown */
3230                 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
3231                 if (!res)
3232                         /* BB: Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
3233                         res = wait_file2(chan, vms, "vm-unknown-caller");
3234         }
3235         return res;
3236 }
3237
3238 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, char *duration, int minduration)
3239 {
3240         int res = 0;
3241         int durationm;
3242         int durations;
3243         /* Verify that we have a duration for the message */
3244         if((duration == NULL))
3245                 return res;
3246
3247         /* Convert from seconds to minutes */
3248         durations=atoi(duration);
3249         durationm=(durations / 60);
3250
3251         ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
3252
3253         if((!res)&&(durationm>=minduration)) {
3254                 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, (char *) NULL);
3255                 res = wait_file2(chan, vms, "vm-minutes");
3256         }
3257         return res;
3258 }
3259
3260 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
3261 {
3262         int res = 0;
3263         char filename[256],*origtime, *cid, *context, *duration;
3264         struct ast_config *msg_cfg;
3265
3266         vms->starting = 0; 
3267         make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3268         adsi_message(chan, vms);
3269         if (!vms->curmsg)
3270                 res = wait_file2(chan, vms, "vm-first");        /* "First" */
3271         else if (vms->curmsg == vms->lastmsg)
3272                 res = wait_file2(chan, vms, "vm-last");         /* "last" */
3273         if (!res) {
3274                 res = wait_file2(chan, vms, "vm-message");      /* "message" */
3275                 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
3276                         if (!res)
3277                                 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, (char *) NULL);
3278                 }
3279         }
3280
3281         /* Retrieve info from VM attribute file */
3282         make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
3283         snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
3284         RETRIEVE(vms->curdir, vms->curmsg);
3285         msg_cfg = ast_load(filename);
3286         if (!msg_cfg) {
3287                 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
3288                 return 0;
3289         }
3290                                                                                                                                                                                                         
3291         if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
3292                 ast_log(LOG_WARNING, "No origtime?!\n");
3293                 DISPOSE(vms->curdir, vms->curmsg);
3294                 return 0;
3295         }
3296
3297         cid = ast_variable_retrieve(msg_cfg, "message", "callerid");
3298         duration = ast_variable_retrieve(msg_cfg, "message", "duration");
3299
3300         context = ast_variable_retrieve(msg_cfg, "message", "context");
3301         if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
3302                 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
3303
3304         if ((!res)&&(vmu->envelope))
3305                 res = play_message_datetime(chan, vmu, origtime, filename);
3306         if ((!res)&&(vmu->saycid))
3307                 res = play_message_callerid(chan, vms, cid, context, 0);
3308         if ((!res)&&(vmu->sayduration))
3309                 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
3310         /* Allow pressing '1' to skip envelope / callerid */
3311         if (res == '1')
3312                 res = 0;
3313         ast_destroy(msg_cfg);
3314
3315         if (!res) {
3316                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
3317                 vms->heard[vms->curmsg] = 1;
3318                 printf("yay!\n");
3319                 res = wait_file(chan, vms, vms->fn);
3320         }
3321         DISPOSE(vms->curdir, vms->curmsg);
3322         return res;
3323 }
3324
3325 static void open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
3326 {
3327         strncpy(vms->curbox, mbox(box), sizeof(vms->curbox) - 1);
3328         make_dir(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3329         vms->lastmsg = count_messages(vms->curdir) - 1;
3330
3331         /*
3332         The following test is needed in case sequencing gets messed up.
3333         There appears to be more than one way to mess up sequence, so
3334         we will not try to find all of the root causes--just fix it when
3335         detected.
3336         */
3337
3338         if(vms->lastmsg != last_message_index(vms->curdir))
3339         {
3340                 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
3341                 resequence_mailbox(vms->curdir);
3342         }
3343
3344         snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
3345 }
3346
3347 static void close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
3348 {
3349         int x;
3350         if (vms->lastmsg > -1) { 
3351                 /* Get the deleted messages fixed */ 
3352                 vms->curmsg = -1; 
3353                 for (x=0;x < MAXMSG;x++) { 
3354                         if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) { 
3355                                 /* Save this message.  It's not in INBOX or hasn't been heard */ 
3356                                 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
3357                                 if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
3358                                         break;
3359                                 vms->curmsg++; 
3360                                 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
3361                                 if (strcmp(vms->fn, vms->fn2)) { 
3362                                         RENAME(vms->curdir, x, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
3363                                 } 
3364                         } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) { 
3365                                 /* Move to old folder before deleting */ 
3366                                 save_to_folder(vms->curdir, x, vmu->context, vms->username, 1); 
3367                         } 
3368                 } 
3369                 for (x = vms->curmsg + 1; x <= MAXMSG; x++) { 
3370                         make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
3371                         if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
3372                                 break;
3373                         DELETE(vms->curdir, x, vms->fn);
3374                 } 
3375         } 
3376         memset(vms->deleted, 0, sizeof(vms->deleted)); 
3377    &nb