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