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