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