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