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