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