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