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