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