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