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