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