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