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