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