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