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