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