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