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