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