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