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