Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Copyright (C) 2003 - 2005, Digium Inc.
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
* 12-16 - 2005 : Support for Greek added by InAccess Networks (work funded by HOL, www.hol.gr)
* George Konstantoulakis <gkon@inaccessnetworks.com>
* 05-10 - 2005 : Support for Swedish and Norwegian added by Daniel Nylander, http://www.danielnylander.se/
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <dirent.h>
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/module.h"
#include "asterisk/adsi.h"
#include "asterisk/app.h"
#include "asterisk/manager.h"
#include "asterisk/dsp.h"
#include "asterisk/localtime.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
Kevin P. Fleming
committed
#include "asterisk/res_odbc.h"
#define COMMAND_TIMEOUT 5000
#define VOICEMAIL_CONFIG "voicemail.conf"
#define ASTERISK_USERNAME "asterisk"
/* Default mail command to mail voicemail. Change it with the
mailcmd= command in voicemail.conf */
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAXMSG 100
#define BASEMAXINLINE 256
#define BASELINELEN 72
#define BASEMAXINLINE 256
#define eol "\r\n"
#define MAX_DATETIME_FORMAT 512
#define MAX_NUM_CID_CONTEXTS 10
#define VM_REVIEW (1 << 0)
#define VM_OPERATOR (1 << 1)
#define VM_SAYCID (1 << 2)
#define VM_SVMAIL (1 << 3)
#define VM_ENVELOPE (1 << 4)
#define VM_SAYDURATION (1 << 5)
#define VM_SKIPAFTERCMD (1 << 6)
#define VM_FORCENAME (1 << 7) /* Have new users record their name */
#define VM_FORCEGREET (1 << 8) /* Have new users record their greetings */
#define VM_PBXSKIP (1 << 9)
#define VM_DIRECFORWARD (1 << 10) /* directory_forward */
#define VM_ATTACH (1 << 11)
#define VM_DELETE (1 << 12)
#define VM_ALLOCED (1 << 13)
static int load_config(void);
/* Syntaxes supported, not really language codes.
en - English
de - German
es - Spanish
fr - French
nl - Dutch
pt - Portuguese
no - Norwegian
se - Swedish
German requires the following additional soundfile:
1F einE (feminine)
Spanish requires the following additional soundfile:
1M un (masculine)
Dutch, Portuguese & Spanish require the following additional soundfiles:
vm-INBOXs singular of 'new'
vm-Olds singular of 'old/heard/read'
NB these are plural:
vm-INBOX nieuwe (nl)
vm-Old oude (nl)
Swedish uses:
vm-nytt singular of 'new'
vm-nya plural of 'new'
vm-gammalt singular of 'old'
vm-gamla plural of 'old'
digits/ett 'one', not always same as 'digits/1'
Norwegian uses:
vm-ny singular of 'new'
vm-nye plural of 'new'
vm-gammel singular of 'old'
vm-gamle plural of 'old'
Dutch also uses:
nl-om 'at'?
Spanish also uses:
vm-youhaveno
Italian requires the following additional soundfile:
For vm_intro_it:
vm-nuovo new
vm-nuovi new plural
vm-vecchio old
vm-vecchi old plural
Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
spelled among others when you have to change folder. For the above reasons, vm-INBOX
and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
struct baseio {
int iocp;
int iolen;
int linelength;
int ateof;
unsigned char iobuf[BASEMAXINLINE];
};
/* Structure for linked list of users */
struct ast_vm_user {
char context[AST_MAX_CONTEXT]; /* Voicemail context */
char mailbox[AST_MAX_EXTENSION];/* Mailbox id, unique within vm context */
char password[80]; /* Secret pin code, numbers only */
char fullname[80]; /* Full name, for directory app */
char email[80]; /* E-mail address */
char pager[80]; /* E-mail address to pager (no attachment) */
char serveremail[80]; /* From: Mail address */
char mailcmd[160]; /* Configurable mail command */
char language[MAX_LANGUAGE]; /* Config: Language setting */
char zonetag[80]; /* Time zone */
char callback[80];
char dialout[80];
char uniqueid[20]; /* Unique integer identifier */
unsigned int flags; /* VM_ flags */
int saydurationm;
struct ast_vm_user *next;
};
struct vm_zone {
char name[80];
char timezone[80];
char msg_format[512];
struct vm_zone *next;
};
Martin Pycko
committed
struct vm_state {
char curbox[80];
char username[80];
char curdir[256];
char vmbox[256];
char fn[256];
char fn2[256];
int deleted[MAXMSG];
int heard[MAXMSG];
int curmsg;
int lastmsg;
int newmessages;
int oldmessages;
int starting;
int repeats;
};
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option);
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir);
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc);
static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
static void apply_options(struct ast_vm_user *vmu, const char *options);
#ifdef USE_ODBC_STORAGE
static char odbc_database[80];
#define RETRIEVE(a,b) retrieve_file(a,b)
#define DISPOSE(a,b) remove_file(a,b)
#define STORE(a,b) store_file(a,b)
#define EXISTS(a,b,c,d) (message_exists(a,b))
#define RENAME(a,b,c,d,e,f) (rename_file(a,b,c,d))
#define COPY(a,b,c,d,e,f) (copy_file(a,b,c,d))
#define DELETE(a,b,c) (delete_file(a,b))
#else
#define RETRIEVE(a,b)
#define DISPOSE(a,b)
#define STORE(a,b)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f) (rename_file(e,f));
#define COPY(a,b,c,d,e,f) (copy_file(e,f));
#define DELETE(a,b,c) (vm_delete(c))
#endif
static char VM_SPOOL_DIR[AST_CONFIG_MAX_PATH];
static char ext_pass_cmd[128];
Martin Pycko
committed
static char *tdesc = "Comedian Mail (Voicemail System)";
static char *synopsis_vm =
"Leave a voicemail message";
static char *descrip_vm =
Kevin P. Fleming
committed
" VoiceMail(extension[@context][&extension[@context]][...][|options]): Leaves"
"voicemail for a given extension (must be configured in voicemail.conf).\n"
Kevin P. Fleming
committed
" If the options contain: \n"
"* 's' then instructions for leaving the message will be skipped.\n"
"* 'u' then the \"unavailable\" message will be played.\n"
" (/var/lib/asterisk/sounds/vm/<exten>/unavail) if it exists.\n"
"* 'b' then the the busy message will be played (that is, busy instead of unavail).\n"
"If the caller presses '0' (zero) during the prompt, the call jumps to\n"
James Golovich
committed
"extension 'o' in the current context.\n"
James Golovich
committed
"If the caller presses '*' during the prompt, the call jumps to\n"
James Golovich
committed
"extension 'a' in the current context.\n"
"If the requested mailbox does not exist, and there exists a priority\n"
"n + 101, then that priority will be taken next.\n"
"When multiple mailboxes are specified, the unavailable or busy message\n"
"will be taken from the first mailbox specified.\n"
"Returns -1 on error or mailbox not found, or if the user hangs up.\n"
"Otherwise, it returns 0.\n";
static char *synopsis_vmain =
"Enter voicemail system";
static char *descrip_vmain =
Kevin P. Fleming
committed
" VoiceMailMain([mailbox][@context][|options]): Enters the main voicemail system\n"
"for the checking of voicemail. The mailbox can be passed in,\n"
"which will stop the voicemail system from prompting the user for the mailbox.\n"
Kevin P. Fleming
committed
"If the options contain 's' then the password check will be skipped. If\n"
"the options contain 'p' then the supplied mailbox is prepended to the\n"
"user's entry and the resulting string is used as the mailbox number. This is\n"
"useful for virtual hosting of voicemail boxes. If a context is specified,\n"
"logins are considered in that voicemail context only.\n"
"Returns -1 if the user hangs up or 0 otherwise.\n";
static char *synopsis_vm_box_exists =
"Check if vmbox exists";
static char *descrip_vm_box_exists =
" MailboxExists(mailbox[@context]): Conditionally branches to priority n+101\n"
"if the specified voice mailbox exists.\n";
static char *synopsis_vmauthenticate =
"Authenticate off voicemail passwords";
static char *descrip_vmauthenticate =
" VMAuthenticate([mailbox][@context][|options]): Behaves identically to\n"
"the Authenticate application, with the exception that the passwords are\n"
"taken from voicemail.conf.\n"
" If the mailbox is specified, only that mailbox's password will be considered\n"
"valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
"be set with the authenticated mailbox.\n"
"If the options contain 's' then no initial prompts will be played.\n";
/* Leave a message */
static char *app = "VoiceMail";
/* Check mail, control, etc */
static char *app2 = "VoiceMailMain";
static char *app3 = "MailboxExists";
static char *app4 = "VMAuthenticate";
struct ast_vm_user *users;
struct ast_vm_user *usersl;
struct vm_zone *zones = NULL;
struct vm_zone *zonesl = NULL;
static int maxsilence;
static int silencethreshold = 128;
static char serveremail[80];
static char mailcmd[160]; /* Configurable mail cmd */
static char externnotify[160];
static char vmfmts[80];
static int vmminmessage;
static int vmmaxmessage;
static int maxgreet;
static int skipms;
static int maxlogins;
static struct ast_flags globalflags = {0};
static int saydurationminfo;
static char dialcontext[AST_MAX_CONTEXT];
static char callcontext[AST_MAX_CONTEXT];
static char exitcontext[AST_MAX_CONTEXT];
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
static char *emailbody = NULL;
static char *emailsubject = NULL;
static char fromstring[100];
static char pagerfromstring[100];
static char emailtitle[100];
static char charset[32] = "ISO-8859-1";
static char adsifdn[4] = "\x00\x00\x00\x0F";
static char adsisec[4] = "\x9B\xDB\xF7\xAC";
static int adsiver = 1;
Kevin P. Fleming
committed
static char emaildateformat[32] = "%A, %B %d, %Y at %r";
static void populate_defaults(struct ast_vm_user *vmu)
ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
if (saydurationminfo>0)
vmu->saydurationm = saydurationminfo;
ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
ast_copy_string(vmu->language, value, sizeof(vmu->language));
ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
ast_set2_flag(vmu, ast_true(value), VM_DELETE);
ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
} else if (!strcasecmp(var, "sayduration")){
ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
} else if (!strcasecmp(var, "saydurationm")){
if (sscanf(value, "%d", &x) == 1) {
vmu->saydurationm = x;
} else {
ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
}
ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
} else if (!strcasecmp(var, "options")) {
apply_options(vmu, value);
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
int res;
if (!ast_strlen_zero(vmu->uniqueid)) {
res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
Russell Bryant
committed
if (res > 0) {
ast_copy_string(vmu->password, password, sizeof(vmu->password));
Russell Bryant
committed
res = 0;
} else if (!res) {
res = -1;
}
}
static void apply_options(struct ast_vm_user *vmu, const char *options)
{ /* Destructively Parse options and apply */
char *stringp;
char *s;
char *var, *value;
stringp = ast_strdupa(options);
while ((s = strsep(&stringp, "|"))) {
value = s;
if ((var = strsep(&value, "=")) && value) {
apply_option(vmu, var, value);
}
}
}
static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
struct ast_vm_user *retval;
if (ivm)
retval=ivm;
else
retval=malloc(sizeof(struct ast_vm_user));
if (retval) {
memset(retval, 0, sizeof(struct ast_vm_user));
if (mailbox)
ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
if (context)
ast_copy_string(retval->context, context, sizeof(retval->context));
populate_defaults(retval);
var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", retval->context, NULL);
if (var) {
tmp = var;
while(tmp) {
printf("%s => %s\n", tmp->name, tmp->value);
if (!strcasecmp(tmp->name, "password")) {
ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
} else if (!strcasecmp(tmp->name, "uniqueid")) {
ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
} else if (!strcasecmp(tmp->name, "pager")) {
ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
} else if (!strcasecmp(tmp->name, "email")) {
ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
} else if (!strcasecmp(tmp->name, "fullname")) {
ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
} else
apply_option(retval, tmp->name, tmp->value);
tmp = tmp->next;
}
} else {
if (!ivm)
free(retval);
}
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
{
/* This function could be made to generate one from a database, too */
struct ast_vm_user *vmu=NULL, *cur;
ast_mutex_lock(&vmlock);
cur = users;
if ((!context || !strcasecmp(context, cur->context)) &&
(!strcasecmp(mailbox, cur->mailbox)))
break;
cur=cur->next;
}
if (cur) {
if (ivm)
vmu = ivm;
else
/* Make a copy, so that on a reload, we have no race */
vmu = malloc(sizeof(struct ast_vm_user));
if (vmu) {
memcpy(vmu, cur, sizeof(struct ast_vm_user));
vmu->next = NULL;
}
} else
vmu = find_user_realtime(ivm, context, mailbox);
ast_mutex_unlock(&vmlock);
return vmu;
}
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
{
/* This function could be made to generate one from a database, too */
struct ast_vm_user *cur;
int res = -1;
ast_mutex_lock(&vmlock);
cur = users;
if ((!context || !strcasecmp(context, cur->context)) &&
(!strcasecmp(mailbox, cur->mailbox)))
break;
cur=cur->next;
}
if (cur) {
ast_copy_string(cur->password, newpass, sizeof(cur->password));
res = 0;
}
ast_mutex_unlock(&vmlock);
return res;
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
/* There's probably a better way of doing this. */
/* That's why I've put the password change in a separate function. */
/* This could also be done with a database function */
FILE *configin;
FILE *configout;
int linenum=0;
char inbuf[256];
char orig[256];
char currcontext[256] ="";
char tmpin[AST_CONFIG_MAX_PATH];
char tmpout[AST_CONFIG_MAX_PATH];
char *user, *pass, *rest, *trim, *tempcontext;
struct stat statbuf;
if (!change_password_realtime(vmu, newpassword))
return;
tempcontext = NULL;
snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
configin = fopen(tmpin,"r");
if (configin)
configout = fopen(tmpout,"w+");
else
configout = NULL;
if (!configin || !configout) {
if (configin)
else
ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
if (configout)
fclose(configout);
else
ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
return;
while (!feof(configin)) {
/* Read in the line */
fgets(inbuf, sizeof(inbuf), configin);
linenum++;
if (!feof(configin)) {
/* Make a backup of it */
memcpy(orig, inbuf, sizeof(orig));
/* Strip trailing \n and comment */
inbuf[strlen(inbuf) - 1] = '\0';
user = strchr(inbuf, ';');
if (user)
*user = '\0';
user=inbuf;
while (*user < 33)
user++;
/* check for '[' (opening of context name ) */
tempcontext = strchr(user, '[');
if (tempcontext) {
ast_copy_string(currcontext, tempcontext +1, sizeof(currcontext));
/* now check for ']' */
tempcontext = strchr(currcontext, ']');
if (tempcontext)
*tempcontext = '\0';
else
currcontext[0] = '\0';
}
pass = strchr(user, '=');
if (pass > user) {
trim = pass - 1;
while (*trim && *trim < 33) {
*trim = '\0';
trim--;
}
if (pass) {
*pass = '\0';
pass++;
if (*pass == '>')
pass++;
while (*pass && *pass < 33)
}
if (pass) {
rest = strchr(pass,',');
if (rest) {
*rest = '\0';
rest++;
} else
rest = NULL;
/* Compare user, pass AND context */
if (user && *user && !strcmp(user, vmu->mailbox) &&
pass && !strcmp(pass, vmu->password) &&
currcontext && *currcontext && !strcmp(currcontext, vmu->context)) {
/* This is the line */
if (rest) {
fprintf(configout, "%s => %s,%s\n", vmu->mailbox,newpassword,rest);
fprintf(configout, "%s => %s\n", vmu->mailbox,newpassword);
} else {
/* Put it back like it was */
fprintf(configout, orig);
}
}
fclose(configin);
fclose(configout);
stat((char *)tmpin, &statbuf);
chmod((char *)tmpout, statbuf.st_mode);
chown((char *)tmpout, statbuf.st_uid, statbuf.st_gid);
unlink((char *)tmpin);
rename((char *)tmpout,(char *)tmpin);
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
}
Mark Spencer
committed
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
{
char buf[255];
snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
ast_safe_system(buf);
}
static int make_dir(char *dest, int len, char *context, char *ext, char *mailbox)
{
return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, mailbox);
}
static int make_file(char *dest, int len, char *dir, int num)
{
return snprintf(dest, len, "%s/msg%04d", dir, num);
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
#ifdef USE_ODBC_STORAGE
static int retrieve_file(char *dir, int msgnum)
{
int x = 0;
int res;
int fd=-1;
size_t fdlen = 0;
void *fdm=NULL;
SQLLEN rowcount=0;
SQLSMALLINT colcount=0;
SQLHSTMT stmt;
char sql[256];
char fmt[80]="";
char *c;
char coltitle[256];
SQLSMALLINT collen;
SQLSMALLINT datatype;
SQLSMALLINT decimaldigits;
SQLSMALLINT nullable;
SQLULEN colsize;
FILE *f=NULL;
char rowdata[80];
char fn[256];
char full_fn[256];
char msgnums[80];
odbc_obj *obj;
obj = fetch_odbc_obj(odbc_database, 0);
ast_copy_string(fmt, vmfmts, sizeof(fmt));
c = strchr(fmt, '|');
if (c)
*c = '\0';
if (!strcasecmp(fmt, "wav49"))
snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
if (msgnum > -1)
make_file(fn, sizeof(fn), dir, msgnum);
else
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
f = fopen(full_fn, "w+");
snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
goto yuck;
}
snprintf(sql, sizeof(sql), "SELECT * FROM voicemessages WHERE dir=? AND msgnum=?");
res = SQLPrepare(stmt, sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
res = odbc_smart_execute(obj, stmt);
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLRowCount(stmt, &rowcount);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
if (rowcount) {
fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC);
if (fd < 0) {
ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLNumResultCols(stmt, &colcount);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
if (f)
fprintf(f, "[message]\n");
for (x=0;x<colcount;x++) {
rowdata[0] = '\0';
collen = sizeof(coltitle);
res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
&datatype, &colsize, &decimaldigits, &nullable);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
if (!strcmp(coltitle, "recording")) {
res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
fdlen = colsize;
fd = open(full_fn, O_RDWR | O_TRUNC | O_CREAT, 0770);
if (fd > -1) {
char tmp[1]="";
lseek(fd, fdlen - 1, SEEK_SET);
if (write(fd, tmp, 1) != 1) {
close(fd);
fd = -1;
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
}
if (fd > -1)
fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}
if (fdm) {
memset(fdm, 0, fdlen);
res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
}
} else {
res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
if (strcmp(coltitle, "msgnum") && strcmp(coltitle, "dir") && f)
fprintf(f, "%s=%s\n", coltitle, rowdata);
}
}
}
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
} else
ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
yuck:
if (f)
fclose(f);
if (fdm)
munmap(fdm, fdlen);
if (fd > -1)
close(fd);
return x - 1;
}
static int remove_file(char *dir, int msgnum)
{
char fn[256];
char full_fn[256];
char msgnums[80];
if (msgnum > -1) {
snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
make_file(fn, sizeof(fn), dir, msgnum);
} else
ast_filedelete(fn, NULL);
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
unlink(full_fn);
return 0;
}
static int last_message_index(char *dir)
{
int x = 0;
int res;
SQLLEN rowcount=0;
SQLHSTMT stmt;
char sql[256];
char rowdata[20];
odbc_obj *obj;
obj = fetch_odbc_obj(odbc_database, 0);
if (obj) {
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
goto yuck;
}
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=?");
res = SQLPrepare(stmt, sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
res = odbc_smart_execute(obj, stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLRowCount(stmt, &rowcount);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
Kevin P. Fleming
committed
if (sscanf(rowdata, "%d", &x) != 1)
ast_log(LOG_WARNING, "Failed to read message count!\n");
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
} else
ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
yuck:
return x - 1;
}
static int message_exists(char *dir, int msgnum)
{
int x = 0;
int res;
SQLLEN rowcount=0;
SQLHSTMT stmt;
char sql[256];
char rowdata[20];
char msgnums[20];
odbc_obj *obj;
obj = fetch_odbc_obj(odbc_database, 0);
if (obj) {
snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
goto yuck;
}
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM voicemessages WHERE dir=? AND msgnum=?");
res = SQLPrepare(stmt, sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
res = odbc_smart_execute(obj, stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLRowCount(stmt, &rowcount);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) || (rowcount < 1)) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
Kevin P. Fleming
committed
if (sscanf(rowdata, "%d", &x) != 1)
ast_log(LOG_WARNING, "Failed to read message count!\n");
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
} else
ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
yuck:
return x;
}
static int count_messages(char *dir)
{
return last_message_index(dir) + 1;
}
static void delete_file(char *sdir, int smsg)
{
int res;
SQLLEN rowcount=0;
SQLHSTMT stmt;
char sql[256];
char msgnums[20];
odbc_obj *obj;
obj = fetch_odbc_obj(odbc_database, 0);
if (obj) {
snprintf(msgnums, sizeof(msgnums), "%d", smsg);
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
goto yuck;
}
snprintf(sql, sizeof(sql), "DELETE FROM voicemessages WHERE dir=? AND msgnum=?");
res = SQLPrepare(stmt, sql, SQL_NTS);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
res = odbc_smart_execute(obj, stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
res = SQLRowCount(stmt, &rowcount);
if (((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO))) {
ast_log(LOG_WARNING, "SQL Row Count error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
goto yuck;
}
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
} else