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