/*
$Id: dbmsgbuf.c 2180 2006-06-18 06:39:48Z aaron $
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/**
* \file dbmsgbuf.c
*
* implement msgbuf functions prototyped in dbmsgbuf.h
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "dbmsgbuf.h"
#include "db.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MSGBUF_WINDOWSIZE (128ull*1024ull)
static unsigned _msgrow_idx = 0;
static int _msg_fetch_inited = 0;
/* for issuing queries to the backend */
char query[DEF_QUERYSIZE];
/**
* CONDITIONS FOR MSGBUF
*/
static u64_t rowlength = 0; /**< length of current row*/
static u64_t rowpos = 0; /**< current position in row */
static db_pos_t zeropos; /**< absolute position (block/offset) of
msgbuf_buf[0]*/
static unsigned nblocks = 0; /**< number of block */
static const char * tmprow; /**< temporary row number */
int db_init_fetch_messageblks(u64_t msg_idnr, char *query_template);
int db_init_fetch_messageblks(u64_t msg_idnr, char *query_template)
{
if (_msg_fetch_inited != 0) {
return 0;
}
msgbuf_buf = (char *) dm_malloc(sizeof(char) *
(size_t) MSGBUF_WINDOWSIZE);
if (!msgbuf_buf) {
return -1;
}
memset(msgbuf_buf, '\0', (size_t) MSGBUF_WINDOWSIZE);
snprintf(query, DEF_QUERYSIZE, query_template, msg_idnr);
if (db_query(query) == -1) {
trace(TRACE_ERROR, "%s,%s: could not get message",
__FILE__, __func__);
dm_free(msgbuf_buf);
return (-1);
}
nblocks = db_num_rows();
if (nblocks == 0) {
trace(TRACE_ERROR, "%s,%s: message has no blocks",
__FILE__, __func__);
db_free_result();
dm_free(msgbuf_buf);
return -1; /* msg should have 1 block at least */
}
_msg_fetch_inited = 1;
msgbuf_idx = 0;
/* start at row (tuple) 0 */
_msgrow_idx = 0;
/* FIXME: this will explode is db_get_result returns NULL. */
tmprow = db_get_result(_msgrow_idx, 0);
rowlength = (u64_t)strlen(tmprow);
if (! strncpy(msgbuf_buf,tmprow, MSGBUF_WINDOWSIZE - 1)) {
db_free_result();
dm_free(msgbuf_buf);
return -1;
}
zeropos.block = 0;
zeropos.pos = 0;
if (rowlength >= MSGBUF_WINDOWSIZE - 1) {
msgbuf_buflen = MSGBUF_WINDOWSIZE - 1;
rowpos = MSGBUF_WINDOWSIZE; /* remember store pos */
msgbuf_buf[msgbuf_buflen] = '\0'; /* terminate buff */
return 1; /* msgbuf_buf full */
}
msgbuf_buflen = rowlength; /* NOTE \0 has been copied from the result set */
rowpos = rowlength; /* no more to read from this row */
_msgrow_idx++;
if (_msgrow_idx >= db_num_rows()) {
rowlength = rowpos = 0;
return 1;
}
/* FIXME: this will explode is db_get_result returns NULL. */
rowpos = 0;
tmprow = db_get_result(_msgrow_idx, 0);
rowlength = (u64_t)strlen(tmprow);
strncpy(&msgbuf_buf[msgbuf_buflen], tmprow, MSGBUF_WINDOWSIZE - msgbuf_buflen - 1);
if (rowlength <= MSGBUF_WINDOWSIZE - msgbuf_buflen - 1) {
/* 2nd block fits entirely */
rowpos = rowlength;
msgbuf_buflen += rowlength;
} else {
rowpos = MSGBUF_WINDOWSIZE - (msgbuf_buflen + 1);
msgbuf_buflen = MSGBUF_WINDOWSIZE - 1;
}
msgbuf_buf[msgbuf_buflen] = '\0'; /* add NULL */
/* store the current result set in db.c as msgbuf_result for
* later use */
db_store_msgbuf_result();
return 1;
}
int db_init_fetch_headers(u64_t msg_idnr)
{
char *query_template = "SELECT block.messageblk "
"FROM dbmail_messageblks block, dbmail_messages msg "
"WHERE block.physmessage_id = msg.physmessage_id "
"AND dbmail_messageblks.is_header = 1"
"AND msg.message_idnr = '%llu' "
"ORDER BY block.messageblk_idnr";
return db_init_fetch_messageblks(msg_idnr, query_template);
}
int db_init_fetch_message(u64_t msg_idnr)
{
char *query_template = "SELECT block.messageblk "
"FROM dbmail_messageblks block, dbmail_messages msg "
"WHERE block.physmessage_id = msg.physmessage_id "
"AND msg.message_idnr = '%llu' "
"ORDER BY block.messageblk_idnr";
return db_init_fetch_messageblks(msg_idnr, query_template);
}
int db_update_msgbuf(int minlen)
{
/* use the former msgbuf_result */
db_use_msgbuf_result();
if (_msgrow_idx >= db_num_rows()) {
db_store_msgbuf_result();
return 0; /* no more */
}
if (msgbuf_idx > msgbuf_buflen) {
db_store_msgbuf_result();
return -1; /* error, msgbuf_idx should be within buf */
}
if (minlen > 0 && ((int) (msgbuf_buflen - msgbuf_idx)) > minlen) {
db_store_msgbuf_result();
return 1; /* ok, need no update */
}
if (msgbuf_idx == 0) {
db_store_msgbuf_result();
return 1; /* update no use, buffer would not change */
}
trace(TRACE_DEBUG,
"%s,%s: update msgbuf_buf updating %llu, %llu, %llu, %llu",
__FILE__, __func__, MSGBUF_WINDOWSIZE,
msgbuf_buflen, rowlength, rowpos);
/* move buf to make msgbuf_idx 0 */
memmove(msgbuf_buf, &msgbuf_buf[msgbuf_idx],
(msgbuf_buflen - msgbuf_idx));
if (msgbuf_idx > (msgbuf_buflen - rowpos)) {
zeropos.block++;
zeropos.pos = (msgbuf_idx - ((msgbuf_buflen) - rowpos));
} else {
zeropos.pos += msgbuf_idx;
}
msgbuf_buflen -= msgbuf_idx;
msgbuf_idx = 0;
if ((rowlength - rowpos) >= (MSGBUF_WINDOWSIZE - msgbuf_buflen)) {
trace(TRACE_DEBUG, "%s,%s update msgbuf non-entire fit",
__FILE__, __func__);
/* rest of row does not fit entirely in buf */
/* FIXME: this will explode is db_get_result returns NULL. */
strncpy(&msgbuf_buf[msgbuf_buflen],
&((db_get_result(_msgrow_idx, 0))[rowpos]),
MSGBUF_WINDOWSIZE - msgbuf_buflen);
rowpos += (MSGBUF_WINDOWSIZE - msgbuf_buflen - 1);
msgbuf_buflen = MSGBUF_WINDOWSIZE - 1;
msgbuf_buf[msgbuf_buflen] = '\0';
db_store_msgbuf_result();
return 1;
}
trace(TRACE_DEBUG, "%s,%s: update msgbuf: entire fit",
__FILE__, __func__);
/* FIXME: this will explode is db_get_result returns NULL. */
strncpy(&msgbuf_buf[msgbuf_buflen],
&((db_get_result(_msgrow_idx, 0))[rowpos]),
(rowlength - rowpos));
msgbuf_buflen += (rowlength - rowpos);
msgbuf_buf[msgbuf_buflen] = '\0';
rowpos = rowlength;
/* try to fetch a new row */
_msgrow_idx++;
if (_msgrow_idx >= db_num_rows()) {
trace(TRACE_DEBUG, "%s,%s update msgbuf succes NOMORE",
__FILE__, __func__);
db_store_msgbuf_result();
return 0;
}
rowlength = db_get_length(_msgrow_idx, 0);
rowpos = 0;
trace(TRACE_DEBUG, "%s,%s: update msgbuf, got new block, "
"trying to place data", __FILE__, __func__);
/* FIXME: this will explode is db_get_result returns NULL. */
strncpy(&msgbuf_buf[msgbuf_buflen], db_get_result(_msgrow_idx, 0),
MSGBUF_WINDOWSIZE - msgbuf_buflen - 1);
if (rowlength <= MSGBUF_WINDOWSIZE - msgbuf_buflen - 1) {
/* 2nd block fits entirely */
trace(TRACE_DEBUG,
"update msgbuf: new block fits entirely\n");
rowpos = rowlength;
msgbuf_buflen += rowlength;
} else {
rowpos = MSGBUF_WINDOWSIZE - (msgbuf_buflen + 1);
msgbuf_buflen = MSGBUF_WINDOWSIZE - 1;
}
msgbuf_buf[msgbuf_buflen] = '\0'; /* add NULL */
trace(TRACE_DEBUG, "%s,%s: update msgbuf succes", __FILE__,
__func__);
db_store_msgbuf_result();
return 1;
}
void db_close_msgfetch()
{
if (!_msg_fetch_inited)
return; /* nothing to be done */
dm_free(msgbuf_buf);
msgbuf_buf = NULL;
nblocks = 0;
/* make sure the right result set is freed and restore the
* old one after that.*/
db_use_msgbuf_result();
db_free_result();
db_store_msgbuf_result();
_msg_fetch_inited = 0;
}
void db_give_msgpos(db_pos_t * pos)
{
if (msgbuf_idx >= ((msgbuf_buflen) - rowpos)) {
pos->block = zeropos.block + 1;
pos->pos = msgbuf_idx - ((msgbuf_buflen) - rowpos);
} else {
pos->block = zeropos.block;
pos->pos = zeropos.pos + msgbuf_idx;
}
}
u64_t db_give_range_size(db_pos_t * start, db_pos_t * end)
{
unsigned i;
u64_t size;
if (start->block > end->block)
return 0; /* bad range */
if (start->block >= nblocks || end->block >= nblocks)
return 0; /* bad range */
if (start->block == end->block)
return (start->pos >
end->pos) ? 0 : (end->pos - start->pos + 1);
/* use the former msgbuf result */
db_use_msgbuf_result();
if (start->pos > db_get_length(start->block, 0) ||
end->pos > db_get_length(end->block, 0)) {
db_store_msgbuf_result();
return 0; /* bad range */
}
size = db_get_length(start->block, 0) - start->pos;
for (i = start->block + 1; i < end->block; i++)
size += db_get_length(i, 0);
size += end->pos;
size++;
/* store the result again.. */
db_store_msgbuf_result();
return size;
}
u64_t db_dump_range(MEM * outmem, db_pos_t start,
db_pos_t end, u64_t msg_idnr)
{
u64_t i, startpos, endpos, j, bufcnt;
u64_t outcnt;
u64_t distance;
char buf[DUMP_BUF_SIZE];
const char *field;
trace(TRACE_DEBUG,
"%s,%s: Dumping range: (%llu,%llu) - (%llu,%llu)",
__FILE__, __func__,
start.block, start.pos, end.block, end.pos);
if (start.block > end.block) {
trace(TRACE_ERROR, "%s,%s: bad range specified",
__FILE__, __func__);
return 0;
}
if (start.block == end.block && start.pos > end.pos) {
trace(TRACE_ERROR, "%s,%s: bad range specified",
__FILE__, __func__);
return 0;
}
snprintf(query, DEF_QUERYSIZE,
"SELECT block.messageblk "
"FROM dbmail_messageblks block, dbmail_messages msg "
"WHERE block.physmessage_id = msg.physmessage_id "
"AND msg.message_idnr = '%llu' "
"ORDER BY block.messageblk_idnr",
msg_idnr);
if (db_query(query) == -1) {
trace(TRACE_ERROR, "%s,%s: could not get message",
__FILE__, __func__);
return 0;
}
if (start.block >= db_num_rows()) {
trace(TRACE_ERROR,
"db_dump_range(): bad range specified\n");
db_free_result();
return 0;
}
outcnt = 0;
/* just one block? */
if (start.block == end.block) {
/* dump everything */
bufcnt = 0;
field = db_get_result(start.block, 0);
for (i = start.pos; i <= end.pos; i++) {
if (bufcnt >= DUMP_BUF_SIZE - 1) {
outcnt += mwrite(buf, bufcnt, outmem);
bufcnt = 0;
}
/* FIXME: field may be NULL from db_get_result! */
if (field[i] == '\n' && (i > 0) && (field[i - 1] != '\r')) {
trace(TRACE_DEBUG, "%s,%s: adding '\r' to buf",
__FILE__, __func__);
buf[bufcnt++] = '\r';
buf[bufcnt++] = '\n';
} else
buf[bufcnt++] = field[i];
}
outcnt += mwrite(buf, bufcnt, outmem);
bufcnt = 0;
db_free_result();
return outcnt;
}
/*
* multiple block range specified
*/
for (i = start.block, outcnt = 0; i <= end.block; i++) {
if (i >= db_num_rows()) {
trace(TRACE_ERROR,
"db_dump_range(): bad range specified\n");
db_free_result();
return 0;
}
startpos = (i == start.block) ? start.pos : 0;
endpos = (i == end.block) ? end.pos + 1 : db_get_length(i, 0);
distance = endpos - startpos;
/* output */
bufcnt = 0;
field = db_get_result(i, 0);
for (j = 0; j < distance; j++) {
if (bufcnt >= DUMP_BUF_SIZE - 1) {
outcnt += mwrite(buf, bufcnt, outmem);
bufcnt = 0;
}
/* FIXME: field may be NULL from db_get_result! */
if (field[startpos + j] == '\n' && (j > 0) && (field[startpos + j - 1] != '\r')) {
trace(TRACE_DEBUG, "%s,%s: adding '\r' to buf",
__FILE__, __func__);
buf[bufcnt++] = '\r';
buf[bufcnt++] = '\n';
} else if (field[startpos + j])
buf[bufcnt++] = field[startpos + j];
}
outcnt += mwrite(buf, bufcnt, outmem);
bufcnt = 0;
}
db_free_result();
return outcnt;
}
syntax highlighted by Code2HTML, v. 0.9.1