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