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