changeset 410:1f0e7229ee58 HEAD

Split IOBuffer into mmaped IBuffer, file IBuffer, memory data IBuffer and file OBuffer.
author Timo Sirainen <tss@iki.fi>
date Mon, 14 Oct 2002 02:49:11 +0300
parents 849f3846212a
children c2fdc076ae7f
files src/auth/login-connection.c src/auth/master.c src/auth/userinfo-passwd-file.c src/imap/client.c src/imap/client.h src/imap/cmd-append.c src/imap/main.c src/lib-imap/imap-bodystructure.c src/lib-imap/imap-bodystructure.h src/lib-imap/imap-envelope.c src/lib-imap/imap-message-cache.c src/lib-imap/imap-message-cache.h src/lib-imap/imap-parser.c src/lib-imap/imap-parser.h src/lib-index/mail-index-update-cache.c src/lib-index/mail-index-update.c src/lib-index/mail-index-util.c src/lib-index/mail-index.h src/lib-index/maildir/maildir-index.h src/lib-index/maildir/maildir-open.c src/lib-index/maildir/maildir-update.c src/lib-index/mbox/mbox-append.c src/lib-index/mbox/mbox-fsck.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-index.h src/lib-index/mbox/mbox-open.c src/lib-index/mbox/mbox-rebuild.c src/lib-index/mbox/mbox-rewrite.c src/lib-index/mbox/mbox-sync.c src/lib-mail/message-parser.c src/lib-mail/message-parser.h src/lib-mail/message-send.c src/lib-mail/message-send.h src/lib-mail/message-size.c src/lib-mail/message-size.h src/lib-storage/index/index-copy.c src/lib-storage/index/index-fetch-section.c src/lib-storage/index/index-fetch.c src/lib-storage/index/index-fetch.h src/lib-storage/index/index-msgcache.c src/lib-storage/index/index-save.c src/lib-storage/index/index-search.c src/lib-storage/index/index-storage.h src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/mbox/mbox-expunge.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-storage.h src/lib-storage/mail-storage.h src/lib/Makefile.am src/lib/compat.c src/lib/compat.h src/lib/data-stack.c src/lib/ibuffer-data.c src/lib/ibuffer-file.c src/lib/ibuffer-internal.h src/lib/ibuffer-mmap.c src/lib/ibuffer.c src/lib/ibuffer.h src/lib/iobuffer-internal.h src/lib/iobuffer.c src/lib/iobuffer.h src/lib/lib.h src/lib/network.c src/lib/network.h src/lib/obuffer-file.c src/lib/obuffer-internal.h src/lib/obuffer.c src/lib/obuffer.h src/login/auth-connection.c src/login/client-authenticate.c src/login/client.c src/login/client.h src/master/auth-process.c src/master/login-process.c src/master/settings.c
diffstat 76 files changed, 2619 insertions(+), 1889 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/login-connection.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/auth/login-connection.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,8 @@
 
 #include "common.h"
 #include "network.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "login-connection.h"
 
 #include <stdlib.h>
@@ -18,7 +19,8 @@
 
 	int fd;
 	IO io;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
         AuthRequestType type;
 };
 
@@ -32,10 +34,10 @@
 
 	i_assert(reply->data_size <= AUTH_MAX_REPLY_DATA_SIZE);
 
-	if (io_buffer_send(conn->outbuf, reply, sizeof(AuthReplyData)) < 0)
+	if (o_buffer_send(conn->outbuf, reply, sizeof(AuthReplyData)) < 0)
 		login_connection_destroy(conn);
 	else if (reply->data_size > 0) {
-		if (io_buffer_send(conn->outbuf, data, reply->data_size) < 0)
+		if (o_buffer_send(conn->outbuf, data, reply->data_size) < 0)
 			login_connection_destroy(conn);
 	}
 }
@@ -44,10 +46,10 @@
 			IO io __attr_unused__)
 {
 	LoginConnection *conn  = context;
-        unsigned char *data;
+        const unsigned char *data;
 	size_t size;
 
-	switch (io_buffer_read(conn->inbuf)) {
+	switch (i_buffer_read(conn->inbuf)) {
 	case 0:
 		return;
 	case -1:
@@ -62,7 +64,7 @@
 		return;
 	}
 
-	data = io_buffer_get_data(conn->inbuf, &size);
+	data = i_buffer_get_data(conn->inbuf, &size);
 	if (size < sizeof(AuthRequestType))
 		return;
 
@@ -80,7 +82,7 @@
 			return;
 
 		memcpy(&request, data, sizeof(request));
-		io_buffer_skip(conn->inbuf, sizeof(request));
+		i_buffer_skip(conn->inbuf, sizeof(request));
 
 		/* we have a full init request */
 		auth_init_request(&request, request_callback, conn);
@@ -95,8 +97,7 @@
 		if (size < sizeof(request) + request.data_size)
 			return;
 
-		io_buffer_skip(conn->inbuf,
-			       sizeof(request) + request.data_size);
+		i_buffer_skip(conn->inbuf, sizeof(request) + request.data_size);
 
 		/* we have a full continued request */
 		auth_continue_request(&request, data + sizeof(request),
@@ -117,18 +118,18 @@
 	conn = i_new(LoginConnection, 1);
 
 	conn->fd = fd;
-	conn->inbuf = io_buffer_create(fd, default_pool,
-				       IO_PRIORITY_DEFAULT, MAX_INBUF_SIZE);
-	conn->outbuf = io_buffer_create(fd, default_pool,
-					IO_PRIORITY_DEFAULT, MAX_OUTBUF_SIZE);
+	conn->inbuf = i_buffer_create_file(fd, default_pool, MAX_INBUF_SIZE,
+					   FALSE);
+	conn->outbuf = o_buffer_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
+					    IO_PRIORITY_DEFAULT, FALSE);
 	conn->io = io_add(fd, IO_READ, login_input, conn);
 	conn->type = AUTH_REQUEST_NONE;
 
 	conn->next = connections;
 	connections = conn;
 
-	if (io_buffer_send(conn->outbuf, &auth_init_data,
-			   sizeof(auth_init_data)) < 0) {
+	if (o_buffer_send(conn->outbuf, &auth_init_data,
+			  sizeof(auth_init_data)) < 0) {
 		login_connection_destroy(conn);
 		conn = NULL;
 	}
@@ -147,8 +148,8 @@
 		}
 	}
 
-	io_buffer_close(conn->inbuf);
-	io_buffer_close(conn->outbuf);
+	i_buffer_unref(conn->inbuf);
+	o_buffer_unref(conn->outbuf);
 
 	io_remove(conn->io);
 	net_disconnect(conn->fd);
--- a/src/auth/master.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/auth/master.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
-#include "iobuffer.h"
+#include "obuffer.h"
 #include "network.h"
 #include "cookie.h"
 
@@ -9,7 +9,7 @@
 
 static AuthCookieReplyData failure_reply;
 
-static IOBuffer *outbuf;
+static OBuffer *outbuf;
 static IO io_master;
 
 static unsigned int master_pos;
@@ -33,7 +33,7 @@
 	}
 
 	reply->id = request->id;
-	switch (io_buffer_send(outbuf, reply, sizeof(AuthCookieReplyData))) {
+	switch (o_buffer_send(outbuf, reply, sizeof(AuthCookieReplyData))) {
 	case -2:
 		i_fatal("Master transmit buffer full, aborting");
 	case -1:
@@ -70,13 +70,14 @@
 	memset(&failure_reply, 0, sizeof(failure_reply));
 
 	master_pos = 0;
-	outbuf = io_buffer_create(MASTER_SOCKET_FD, default_pool,
-				  IO_PRIORITY_DEFAULT, MAX_OUTBUF_SIZE);
+	outbuf = o_buffer_create_file(MASTER_SOCKET_FD, default_pool,
+				      MAX_OUTBUF_SIZE, IO_PRIORITY_DEFAULT,
+				      FALSE);
 	io_master = io_add(MASTER_SOCKET_FD, IO_READ, master_input, NULL);
 }
 
 void master_deinit(void)
 {
-	io_buffer_unref(outbuf);
+	o_buffer_unref(outbuf);
 	io_remove(io_master);
 }
--- a/src/auth/userinfo-passwd-file.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/auth/userinfo-passwd-file.c	Mon Oct 14 02:49:11 2002 +0300
@@ -10,7 +10,7 @@
 
 #ifdef USERINFO_PASSWD_FILE
 
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "hash.h"
 #include "hex-binary.h"
 #include "md5.h"
@@ -294,15 +294,15 @@
 
 static void passwd_file_parse_file(PasswdFile *pw)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	char *const *args;
 	char *line;
 
-	inbuf = io_buffer_create_file(pw->fd, default_pool, 2048, FALSE);
+	inbuf = i_buffer_create_file(pw->fd, default_pool, 2048, FALSE);
 	for (;;) {
-		line = io_buffer_next_line(inbuf);
+		line = i_buffer_next_line(inbuf);
 		if (line == NULL) {
-			if (io_buffer_read(inbuf) <= 0)
+			if (i_buffer_read(inbuf) <= 0)
 				break;
                         continue;
 		}
@@ -318,7 +318,7 @@
 		}
 		t_pop();
 	}
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 }
 
 static PasswdFile *passwd_file_parse(const char *path)
--- a/src/imap/client.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/imap/client.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,8 @@
 
 #include "common.h"
 #include "network.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "commands.h"
 
 #include <stdlib.h>
@@ -33,8 +34,8 @@
 {
 	Client *client = context;
 
-	io_buffer_close(client->inbuf);
-	io_buffer_close(client->outbuf);
+	i_buffer_close(client->inbuf);
+	o_buffer_close(client->outbuf);
 }
 
 static void client_input_timeout(void *context,
@@ -44,37 +45,35 @@
 
 	client_send_line(my_client, "* BYE Disconnected for inactivity "
 			 "while waiting for command data.");
-	io_buffer_close(client->outbuf);
+	o_buffer_close(client->outbuf);
 }
 
-Client *client_create(int hin, int hout, int socket, MailStorage *storage)
+Client *client_create(int hin, int hout, MailStorage *storage)
 {
 	Client *client;
 
 	client = i_new(Client, 1);
-	client->socket = socket;
-	client->inbuf = io_buffer_create(hin, default_pool, 0,
-					 MAX_INBUF_SIZE);
-	client->outbuf = io_buffer_create(hout, default_pool, 0, 0);
+	client->inbuf = i_buffer_create_file(hin, default_pool,
+					     MAX_INBUF_SIZE, FALSE);
+	client->outbuf = o_buffer_create_file(hout, default_pool, 4096,
+					      IO_PRIORITY_DEFAULT, FALSE);
 
 	/* always use nonblocking I/O */
 	net_set_nonblock(hin, TRUE);
 	net_set_nonblock(hout, TRUE);
 
-	/* set timeout for sending data */
-	io_buffer_set_blocking(client->outbuf, 4096, CLIENT_OUTPUT_TIMEOUT,
-			       client_output_timeout, client);
-
 	/* set timeout for reading expected data (eg. APPEND). This is
 	   different from the actual idle time. */
-	io_buffer_set_blocking(client->inbuf, 0, CLIENT_CMDINPUT_TIMEOUT,
-			       client_input_timeout, client);
+	i_buffer_set_blocking(client->inbuf, CLIENT_CMDINPUT_TIMEOUT,
+			      client_input_timeout, client);
 
-	client->inbuf->file = !socket;
-	client->outbuf->file = !socket;
+	/* set timeout for sending data */
+	o_buffer_set_blocking(client->outbuf, CLIENT_OUTPUT_TIMEOUT,
+			      client_output_timeout, client);
 
-	client->io = io_add(socket, IO_READ, (IOFunc) client_input, client);
-	client->parser = imap_parser_create(client->inbuf, client->outbuf);
+	client->io = io_add(hin, IO_READ, (IOFunc) client_input, client);
+	client->parser = imap_parser_create(client->inbuf, client->outbuf,
+					    MAX_INBUF_SIZE);
         client->last_input = ioloop_time;
 
 	client->storage = storage;
@@ -86,7 +85,7 @@
 
 void client_destroy(Client *client)
 {
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
 	if (client->mailbox != NULL)
 		client->mailbox->close(client->mailbox);
@@ -95,8 +94,8 @@
 	imap_parser_destroy(client->parser);
 	io_remove(client->io);
 
-	io_buffer_unref(client->inbuf);
-	io_buffer_unref(client->outbuf);
+	i_buffer_unref(client->inbuf);
+	o_buffer_unref(client->outbuf);
 
 	i_free(client);
 
@@ -107,43 +106,24 @@
 
 void client_disconnect(Client *client)
 {
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
-	io_buffer_close(client->inbuf);
-	io_buffer_close(client->outbuf);
+	i_buffer_close(client->inbuf);
+	o_buffer_close(client->outbuf);
 }
 
 void client_send_line(Client *client, const char *data)
 {
-	unsigned char *buf;
-	size_t len;
-
 	if (client->outbuf->closed)
 		return;
 
-	len = strlen(data);
-
-	buf = io_buffer_get_space(client->outbuf, len+2);
-	if (buf != NULL) {
-		memcpy(buf, data, len);
-		buf[len++] = '\r'; buf[len++] = '\n';
-
-		/* Returns error only if we disconnected -
-		   we don't need to do anything about it. */
-		(void)io_buffer_send_buffer(client->outbuf, len);
-	} else {
-		/* not enough space in output buffer, send this directly.
-		   will block. */
-		io_buffer_send(client->outbuf, data, len);
-		io_buffer_send(client->outbuf, "\r\n", 2);
-	}
+	(void)o_buffer_send(client->outbuf, data, strlen(data));
+	(void)o_buffer_send(client->outbuf, "\r\n", 2);
 }
 
 void client_send_tagline(Client *client, const char *data)
 {
 	const char *tag = client->cmd_tag;
-	unsigned char *buf;
-	size_t taglen, len;
 
 	if (client->outbuf->closed)
 		return;
@@ -151,24 +131,10 @@
 	if (tag == NULL || *tag == '\0')
 		tag = "*";
 
-	taglen = strlen(tag);
-	len = strlen(data);
-
-	buf = io_buffer_get_space(client->outbuf, taglen+1+len+2);
-	if (buf != NULL) {
-		memcpy(buf, tag, taglen); buf[taglen] = ' ';
-		buf += taglen+1;
-
-		memcpy(buf, data, len); buf += len;
-		buf[0] = '\r'; buf[1] = '\n';
-
-		(void)io_buffer_send_buffer(client->outbuf, taglen+1+len+2);
-	} else {
-		const char *str;
-
-		str = t_strconcat(tag, " ", data, "\r\n", NULL);
-		(void)io_buffer_send(client->outbuf, str, strlen(str));
-	}
+	(void)o_buffer_send(client->outbuf, tag, strlen(tag));
+	(void)o_buffer_send(client->outbuf, " ", 1);
+	(void)o_buffer_send(client->outbuf, data, strlen(data));
+	(void)o_buffer_send(client->outbuf, "\r\n", 2);
 }
 
 void client_send_command_error(Client *client, const char *msg)
@@ -260,16 +226,16 @@
    returns TRUE if newline was found. */
 static int client_skip_line(Client *client)
 {
-	unsigned char *data;
+	const unsigned char *data;
 	size_t i, data_size;
 
 	/* get the beginning of data in input buffer */
-	data = io_buffer_get_data(client->inbuf, &data_size);
+	data = i_buffer_get_data(client->inbuf, &data_size);
 
 	for (i = 0; i < data_size; i++) {
 		if (data[i] == '\n') {
 			client->inbuf_skip_line = FALSE;
-			io_buffer_skip(client->inbuf, i+1);
+			i_buffer_skip(client->inbuf, i+1);
 			break;
 		}
 	}
@@ -338,7 +304,7 @@
 {
 	client->last_input = ioloop_time;
 
-	switch (io_buffer_read(client->inbuf)) {
+	switch (i_buffer_read(client->inbuf)) {
 	case -1:
 		/* disconnected */
 		client_destroy(client);
@@ -354,10 +320,10 @@
 		break;
 	}
 
-	io_buffer_cork(client->outbuf);
+	o_buffer_cork(client->outbuf);
 	while (client_handle_input(client))
 		;
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
 	if (client->outbuf->closed)
 		client_destroy(client);
--- a/src/imap/client.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/imap/client.h	Mon Oct 14 02:49:11 2002 +0300
@@ -11,7 +11,8 @@
 struct _Client {
 	int socket;
 	IO io;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 
 	MailStorage *storage;
 	Mailbox *mailbox;
@@ -32,7 +33,7 @@
 
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
-Client *client_create(int hin, int hout, int socket, MailStorage *storage);
+Client *client_create(int hin, int hout, MailStorage *storage);
 void client_destroy(Client *client);
 
 /* Disconnect client connection */
--- a/src/imap/cmd-append.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/imap/cmd-append.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,7 @@
 
 #include "common.h"
 #include "ioloop.h"
-#include "iobuffer.h"
+#include "obuffer.h"
 #include "commands.h"
 #include "imap-parser.h"
 #include "imap-date.h"
@@ -120,8 +120,8 @@
 		}
 	}
 
-	io_buffer_send(client->outbuf, "+ OK\r\n", 6);
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_send(client->outbuf, "+ OK\r\n", 6);
+	o_buffer_flush(client->outbuf);
 
 	/* save the mail */
 	failed = !box->save(box, flags, custom_flags, internal_date,
--- a/src/imap/main.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/imap/main.c	Mon Oct 14 02:49:11 2002 +0300
@@ -70,7 +70,7 @@
 				"autodetection failed (home %s)", home);
 		}
 	} else {
-		client = client_create(0, 1, FALSE, storage);
+		client = client_create(0, 1, storage);
 
 		tag = getenv("LOGIN_TAG");
 		if (tag == NULL || *tag == '\0')
--- a/src/lib-imap/imap-bodystructure.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-bodystructure.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "temp-string.h"
 #include "rfc822-tokenize.h"
 #include "message-parser.h"
@@ -213,15 +213,15 @@
 	t_pop();
 }
 
-static void part_parse_headers(MessagePart *part, IOBuffer *inbuf,
+static void part_parse_headers(MessagePart *part, IBuffer *inbuf,
 			       uoff_t start_offset, Pool pool)
 {
 	while (part != NULL) {
 		/* note that we want to parse the header of all
 		   the message parts, multiparts too. */
-		i_assert(part->physical_pos >= inbuf->offset - start_offset);
-		io_buffer_skip(inbuf, part->physical_pos -
-			       (inbuf->offset - start_offset));
+		i_assert(part->physical_pos >= inbuf->v_offset - start_offset);
+		i_buffer_skip(inbuf, part->physical_pos -
+			      (inbuf->v_offset - start_offset));
 
 		message_parse_header(part, inbuf, NULL, parse_header, pool);
 
@@ -239,6 +239,8 @@
 {
 	MessagePartBodyData *data = part->context;
 
+	i_assert(data != NULL);
+
 	if (part->children != NULL)
 		part_write_bodystructure(part->children, str, extended);
 	else {
@@ -405,14 +407,14 @@
 }
 
 const char *imap_part_get_bodystructure(Pool pool, MessagePart **part,
-					IOBuffer *inbuf, int extended)
+					IBuffer *inbuf, int extended)
 {
 	uoff_t start_offset;
 
 	if (*part == NULL)
 		*part = message_parse(pool, inbuf, parse_header, pool);
 	else {
-		start_offset = inbuf->offset;
+		start_offset = inbuf->v_offset;
 		part_parse_headers(*part, inbuf, start_offset, pool);
 	}
 
--- a/src/lib-imap/imap-bodystructure.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-bodystructure.h	Mon Oct 14 02:49:11 2002 +0300
@@ -4,6 +4,6 @@
 /* If *part is non-NULL, it's used as base for building the body structure.
    Otherwise it's set to the root MessagePart and parsed. */
 const char *imap_part_get_bodystructure(Pool pool, MessagePart **part,
-					IOBuffer *inbuf, int extended);
+					IBuffer *inbuf, int extended);
 
 #endif
--- a/src/lib-imap/imap-envelope.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-envelope.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "temp-string.h"
 #include "rfc822-address.h"
 #include "imap-parser.h"
@@ -231,17 +231,18 @@
 
 const char *imap_envelope_parse(const char *envelope, ImapEnvelopeField field)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	ImapParser *parser;
 	ImapArg *args;
 	const char *value;
+	size_t len;
 	int ret;
 
 	i_assert(field < IMAP_ENVELOPE_FIELDS);
 
-	inbuf = io_buffer_create_from_data(envelope, strlen(envelope),
-					   data_stack_pool);
-	parser = imap_parser_create(inbuf, NULL);
+	len = strlen(envelope);
+	inbuf = i_buffer_create_from_data(data_stack_pool, envelope, len);
+	parser = imap_parser_create(inbuf, NULL, len);
 
 	ret = imap_parser_read_args(parser, field, 0, &args);
 	if (ret < 0)
@@ -251,6 +252,6 @@
 		imap_envelope_parse_arg(&args[field], field, envelope);
 
 	imap_parser_destroy(parser);
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	return value;
 }
--- a/src/lib-imap/imap-message-cache.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-message-cache.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "temp-string.h"
 #include "mmap-util.h"
 #include "message-parser.h"
@@ -47,7 +47,7 @@
 	int messages_count;
 
 	CachedMessage *open_msg;
-	IOBuffer *open_inbuf;
+	IBuffer *open_inbuf;
 
 	void *context;
 };
@@ -160,7 +160,7 @@
 {
 	if (cache->open_inbuf == NULL)
 		cache->open_inbuf = cache->iface->open_mail(cache->context);
-	else if (offset < cache->open_inbuf->offset) {
+	else if (offset < cache->open_inbuf->v_offset) {
 		/* need to rewind */
 		cache->open_inbuf =
 			cache->iface->inbuf_rewind(cache->open_inbuf,
@@ -170,9 +170,9 @@
 	if (cache->open_inbuf == NULL)
 		return FALSE;
 
-	i_assert(offset >= cache->open_inbuf->offset);
+	i_assert(offset >= cache->open_inbuf->v_offset);
 
-	io_buffer_skip(cache->open_inbuf, offset - cache->open_inbuf->offset);
+	i_buffer_skip(cache->open_inbuf, offset - cache->open_inbuf->v_offset);
 	return TRUE;
 }
 
@@ -342,7 +342,7 @@
 void imap_msgcache_close(ImapMessageCache *cache)
 {
 	if (cache->open_inbuf != NULL) {
-		io_buffer_unref(cache->open_inbuf);
+		i_buffer_unref(cache->open_inbuf);
 		cache->open_inbuf = NULL;
 	}
 
@@ -386,7 +386,7 @@
 	return cache->open_msg->part;
 }
 
-int imap_msgcache_get_rfc822(ImapMessageCache *cache, IOBuffer **inbuf,
+int imap_msgcache_get_rfc822(ImapMessageCache *cache, IBuffer **inbuf,
 			     MessageSize *hdr_size, MessageSize *body_size)
 {
 	CachedMessage *msg;
@@ -422,11 +422,11 @@
 	return TRUE;
 }
 
-static void get_partial_size(IOBuffer *inbuf,
+static void get_partial_size(IBuffer *inbuf,
 			     uoff_t virtual_skip, uoff_t max_virtual_size,
 			     MessageSize *partial, MessageSize *dest)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t size;
 	int cr_skipped;
 
@@ -434,7 +434,7 @@
 	if (partial->virtual_size > virtual_skip)
 		memset(partial, 0, sizeof(MessageSize));
 	else {
-		io_buffer_skip(inbuf, partial->physical_size);
+		i_buffer_skip(inbuf, partial->physical_size);
 		virtual_skip -= partial->virtual_size;
 	}
 
@@ -442,7 +442,7 @@
 
 	if (!cr_skipped) {
 		/* see if we need to add virtual CR */
-		if (io_buffer_read_data_blocking(inbuf, &msg, &size, 0) > 0) {
+		if (i_buffer_read_data(inbuf, &msg, &size, 0) > 0) {
 			if (msg[0] == '\n')
 				dest->virtual_size++;
 		}
@@ -455,7 +455,7 @@
 				     uoff_t virtual_skip,
 				     uoff_t max_virtual_size,
 				     int get_header, MessageSize *size,
-                                     IOBuffer **inbuf)
+                                     IBuffer **inbuf)
 {
 	CachedMessage *msg;
 	uoff_t physical_skip;
@@ -513,7 +513,7 @@
 	return TRUE;
 }
 
-int imap_msgcache_get_data(ImapMessageCache *cache, IOBuffer **inbuf)
+int imap_msgcache_get_data(ImapMessageCache *cache, IBuffer **inbuf)
 {
 	i_assert(cache->open_msg != NULL);
 
--- a/src/lib-imap/imap-message-cache.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-message-cache.h	Mon Oct 14 02:49:11 2002 +0300
@@ -24,10 +24,10 @@
 
 typedef struct {
 	/* Open mail for reading. */
-	IOBuffer *(*open_mail)(void *context);
+	IBuffer *(*open_mail)(void *context);
 	/* Rewind input buffer to beginning, possibly closing the old buffer
 	   if it can't directly be rewinded. */
-	IOBuffer *(*inbuf_rewind)(IOBuffer *inbuf, void *context);
+	IBuffer *(*inbuf_rewind)(IBuffer *inbuf, void *context);
 
 	/* Returns field if it's already cached, or NULL. */
 	const char *(*get_cached_field)(ImapCacheField field, void *context);
@@ -59,7 +59,7 @@
 
 /* Returns TRUE if successful. If inbuf is not NULL, it's set to point to
    beginning of message, or to beginning of message body if hdr_size is NULL. */
-int imap_msgcache_get_rfc822(ImapMessageCache *cache, IOBuffer **inbuf,
+int imap_msgcache_get_rfc822(ImapMessageCache *cache, IBuffer **inbuf,
 			     MessageSize *hdr_size, MessageSize *body_size);
 
 /* Returns TRUE if successful. *inbuf is set to point to the first non-skipped
@@ -68,10 +68,10 @@
 				     uoff_t virtual_skip,
 				     uoff_t max_virtual_size,
 				     int get_header, MessageSize *size,
-				     IOBuffer **inbuf);
+				     IBuffer **inbuf);
 
 /* Returns TRUE if successful. *inbuf is set to point to beginning of
    message. */
-int imap_msgcache_get_data(ImapMessageCache *cache, IOBuffer **inbuf);
+int imap_msgcache_get_data(ImapMessageCache *cache, IBuffer **inbuf);
 
 #endif
--- a/src/lib-imap/imap-parser.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-parser.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,8 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "imap-parser.h"
 
 #define is_linebreak(c) \
@@ -17,7 +18,9 @@
 
 struct _ImapParser {
 	Pool pool;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
+	size_t max_literal_size;
 
 	unsigned int pos;
 	ImapArg *args;
@@ -39,14 +42,16 @@
 	unsigned int error:1;
 };
 
-ImapParser *imap_parser_create(IOBuffer *inbuf, IOBuffer *outbuf)
+ImapParser *imap_parser_create(IBuffer *inbuf, OBuffer *outbuf,
+			       size_t max_literal_size)
 {
 	ImapParser *parser;
 
 	parser = i_new(ImapParser, 1);
-	parser->pool = pool_create("IMAP parser", 8192, FALSE);
+        parser->pool = pool_create("IMAP parser", 8192, FALSE);
 	parser->inbuf = inbuf;
 	parser->outbuf = outbuf;
+	parser->max_literal_size = max_literal_size;
 	return parser;
 }
 
@@ -84,7 +89,7 @@
 			break;
 	}
 
-        io_buffer_skip(parser->inbuf, i);
+        i_buffer_skip(parser->inbuf, i);
 	parser->cur_pos = 0;
 
 	*data += i;
@@ -279,15 +284,15 @@
 static int imap_parser_literal_end(ImapParser *parser)
 {
 	if ((parser->flags & IMAP_PARSE_FLAG_LITERAL_SIZE) == 0) {
-		if (parser->literal_size > parser->inbuf->max_buffer_size) {
+		if (parser->literal_size > parser->max_literal_size) {
 			/* too long string, abort. */
 			parser->error = TRUE;
 			return FALSE;
 		}
 
 		if (parser->outbuf != NULL) {
-			io_buffer_send(parser->outbuf, "+ OK\r\n", 6);
-			io_buffer_send_flush(parser->outbuf);
+			o_buffer_send(parser->outbuf, "+ OK\r\n", 6);
+			o_buffer_flush(parser->outbuf);
 		}
 	}
 
@@ -306,7 +311,7 @@
 	/* expecting digits + "}" */
 	for (i = parser->cur_pos; i < data_size; i++) {
 		if (data[i] == '}') {
-			io_buffer_skip(parser->inbuf, i+1);
+			i_buffer_skip(parser->inbuf, i+1);
 			if (!imap_parser_literal_end(parser))
 				return FALSE;
 			break;
@@ -338,14 +343,14 @@
 				return TRUE;
 
 			data++; data_size--;
-			io_buffer_skip(parser->inbuf, 1);
+			i_buffer_skip(parser->inbuf, 1);
 		}
 
 		if (*data != '\n')
 			return FALSE;
 
 		data++; data_size--;
-		io_buffer_skip(parser->inbuf, 1);
+		i_buffer_skip(parser->inbuf, 1);
 		parser->literal_skip_crlf = FALSE;
 
 		i_assert(parser->cur_pos == 0);
@@ -373,7 +378,7 @@
 	char *data;
 	size_t data_size;
 
-	data = (char *) io_buffer_get_data(parser->inbuf, &data_size);
+	data = (char *) i_buffer_get_data(parser->inbuf, &data_size);
 	if (data_size == 0)
 		return FALSE;
 
@@ -441,7 +446,7 @@
 
 		/* pass through to parsing data. since inbuf->skip was
 		   modified, we need to get the data start position again. */
-		data = (char *) io_buffer_get_data(parser->inbuf, &data_size);
+		data = (char *) i_buffer_get_data(parser->inbuf, &data_size);
 	case ARG_PARSE_LITERAL_DATA:
 		imap_parser_read_literal_data(parser, data, data_size);
 		break;
@@ -502,7 +507,7 @@
 	size_t i, data_size;
 
 	/* get the beginning of data in input buffer */
-	data = (char *) io_buffer_get_data(parser->inbuf, &data_size);
+	data = (char *) i_buffer_get_data(parser->inbuf, &data_size);
 
 	for (i = 0; i < data_size; i++) {
 		if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n')
@@ -510,7 +515,7 @@
 	}
 
 	if (i < data_size) {
-		io_buffer_skip(parser->inbuf, i + (data[i] == ' ' ? 1 : 0));
+		i_buffer_skip(parser->inbuf, i + (data[i] == ' ' ? 1 : 0));
 		return p_strndup(parser->pool, data, i);
 	} else {
 		return NULL;
@@ -523,7 +528,7 @@
 	size_t i, data_size;
 
 	/* get the beginning of data in input buffer */
-	data = (char *) io_buffer_get_data(parser->inbuf, &data_size);
+	data = (char *) i_buffer_get_data(parser->inbuf, &data_size);
 
 	for (i = 0; i < data_size; i++) {
 		if (data[i] == '\r' || data[i] == '\n')
@@ -531,7 +536,7 @@
 	}
 
 	if (i < data_size) {
-		io_buffer_skip(parser->inbuf, i);
+		i_buffer_skip(parser->inbuf, i);
 		return p_strndup(parser->pool, data, i);
 	} else {
 		return NULL;
--- a/src/lib-imap/imap-parser.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-imap/imap-parser.h	Mon Oct 14 02:49:11 2002 +0300
@@ -38,9 +38,11 @@
 };
 
 /* Create new IMAP argument parser. The max. size of inbuf limits the
-   maximum size of each argument. outbuf is used for sending command
-   continuation requests for string literals. */
-ImapParser *imap_parser_create(IOBuffer *inbuf, IOBuffer *outbuf);
+   maximum size of each argument. max_literal_size also limits sizes of
+   literals which we even try to handle if FLAG_LITERAL_SIZE is not set.
+   outbuf is used for sending command continuation requests for literals. */
+ImapParser *imap_parser_create(IBuffer *inbuf, OBuffer *outbuf,
+			       size_t max_literal_size);
 void imap_parser_destroy(ImapParser *parser);
 
 /* Reset the parser to initial state. */
--- a/src/lib-index/mail-index-update-cache.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mail-index-update-cache.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "mail-index.h"
 
 #include <unistd.h>
@@ -10,7 +10,7 @@
 			MailField cache_fields)
 {
 	MailIndexUpdate *update;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	int failed;
 
 	inbuf = index->open_mail(index, rec);
@@ -23,7 +23,7 @@
 	mail_index_update_headers(update, inbuf, cache_fields, NULL, NULL);
 	failed = !index->update_end(update);
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	return !failed;
 }
 
--- a/src/lib-index/mail-index-update.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mail-index-update.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "ioloop.h"
 #include "rfc822-date.h"
 #include "rfc822-tokenize.h"
@@ -319,7 +319,7 @@
 	}
 }
 
-void mail_index_update_headers(MailIndexUpdate *update, IOBuffer *inbuf,
+void mail_index_update_headers(MailIndexUpdate *update, IBuffer *inbuf,
                                MailField cache_fields,
 			       MessageHeaderFunc header_func, void *context)
 {
@@ -361,7 +361,7 @@
 			}
 		}
 
-		start_offset = inbuf->offset;
+		start_offset = inbuf->v_offset;
 
 		if (part == NULL) {
 			part = message_parse(pool, inbuf,
@@ -369,7 +369,7 @@
 		} else {
 			/* cached, construct the bodystructure using it.
 			   also we need to parse the header.. */
-			io_buffer_seek(inbuf, start_offset);
+			i_buffer_seek(inbuf, start_offset);
 			message_parse_header(NULL, inbuf, NULL,
 					     update_header_func, &ctx);
 		}
@@ -380,7 +380,7 @@
 
 		if (cache_fields & FIELD_TYPE_BODY) {
 			t_push();
-			io_buffer_seek(inbuf, start_offset);
+			i_buffer_seek(inbuf, start_offset);
 			value = imap_part_get_bodystructure(pool, &part,
 							    inbuf, FALSE);
 			update->index->update_field(update, FIELD_TYPE_BODY,
@@ -390,7 +390,7 @@
 
 		if (cache_fields & FIELD_TYPE_BODYSTRUCTURE) {
 			t_push();
-			io_buffer_seek(inbuf, start_offset);
+			i_buffer_seek(inbuf, start_offset);
 			value = imap_part_get_bodystructure(pool, &part,
 							    inbuf, TRUE);
 			update->index->update_field(update,
@@ -414,7 +414,7 @@
 				     update_header_func, &ctx);
 
 		update->rec->header_size = hdr_size.physical_size;
-		update->rec->body_size = inbuf->size - inbuf->offset;
+		update->rec->body_size = inbuf->v_size - inbuf->v_offset;
 	}
 
 	if (ctx.envelope != NULL) {
--- a/src/lib-index/mail-index-util.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mail-index-util.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "hostpid.h"
 #include "message-size.h"
 #include "message-part-serialize.h"
@@ -111,7 +111,7 @@
 				int fastscan, uoff_t *virtual_size)
 {
 	MessageSize hdr_size, body_size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const void *part_data;
 	size_t size;
 
@@ -155,6 +155,6 @@
 	message_get_body_size(inbuf, &body_size, (uoff_t)-1);
 	*virtual_size = body_size.virtual_size;
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	return TRUE;
 }
--- a/src/lib-index/mail-index.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mail-index.h	Mon Oct 14 02:49:11 2002 +0300
@@ -229,9 +229,9 @@
 	const void *(*lookup_field_raw)(MailIndex *index, MailIndexRecord *rec,
 					MailField field, size_t *size);
 
-	/* Open mail file and return it as mmap()ed IOBuffer, or
+	/* Open mail file and return it as mmap()ed IBuffer, or
 	   NULL if failed. */
-	IOBuffer *(*open_mail)(MailIndex *index, MailIndexRecord *rec);
+	IBuffer *(*open_mail)(MailIndex *index, MailIndexRecord *rec);
 
 	/* Expunge a mail from index. Tree and modifylog is also updated. The
 	   index must be exclusively locked before calling this function.
@@ -391,7 +391,7 @@
 int mail_index_verify_hole_range(MailIndex *index);
 void mail_index_mark_flag_changes(MailIndex *index, MailIndexRecord *rec,
 				  MailFlags old_flags, MailFlags new_flags);
-void mail_index_update_headers(MailIndexUpdate *update, IOBuffer *inbuf,
+void mail_index_update_headers(MailIndexUpdate *update, IBuffer *inbuf,
                                MailField cache_fields,
 			       MessageHeaderFunc header_func, void *context);
 int mail_index_update_cache(MailIndex *index);
--- a/src/lib-index/maildir/maildir-index.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/maildir/maildir-index.h	Mon Oct 14 02:49:11 2002 +0300
@@ -20,7 +20,7 @@
 int maildir_index_build_dir(MailIndex *index, const char *source_dir,
 			    const char *dest_dir);
 
-IOBuffer *maildir_open_mail(MailIndex *index, MailIndexRecord *rec);
+IBuffer *maildir_open_mail(MailIndex *index, MailIndexRecord *rec);
 
 int maildir_record_update(MailIndexUpdate *update, int fd, off_t file_size);
 
--- a/src/lib-index/maildir/maildir-open.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/maildir/maildir-open.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "maildir-index.h"
 #include "mail-index-data.h"
 #include "mail-index-util.h"
@@ -9,7 +9,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-IOBuffer *maildir_open_mail(MailIndex *index, MailIndexRecord *rec)
+IBuffer *maildir_open_mail(MailIndex *index, MailIndexRecord *rec)
 {
 	const char *fname, *path;
 	int fd;
@@ -34,6 +34,6 @@
 		return NULL;
 	}
 
-	return io_buffer_create_mmap(fd, default_pool, MAIL_MMAP_BLOCK_SIZE, 0,
-				     0, IOBUFFER_FLAG_AUTOCLOSE);
+	return i_buffer_create_mmap(fd, default_pool, MAIL_MMAP_BLOCK_SIZE,
+				    0, 0, TRUE);
 }
--- a/src/lib-index/maildir/maildir-update.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/maildir/maildir-update.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,18 +1,18 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "maildir-index.h"
 
 int maildir_record_update(MailIndexUpdate *update, int fd, off_t file_size)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 
 	t_push();
-	inbuf = io_buffer_create_mmap(fd, data_stack_pool,
-				      MAIL_MMAP_BLOCK_SIZE, 0, file_size, 0);
+	inbuf = i_buffer_create_mmap(fd, data_stack_pool, MAIL_MMAP_BLOCK_SIZE,
+				     0, file_size, FALSE);
 	mail_index_update_headers(update, inbuf, 0, NULL, NULL);
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	t_pop();
 	return TRUE;
 }
--- a/src/lib-index/mbox/mbox-append.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-append.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,7 @@
 
 #include "lib.h"
 #include "ioloop.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "hex-binary.h"
 #include "md5.h"
 #include "mbox-index.h"
@@ -23,20 +23,21 @@
 	return rec;
 }
 
-static int mbox_index_append_next(MailIndex *index, IOBuffer *inbuf)
+static int mbox_index_append_next(MailIndex *index, IBuffer *inbuf)
 {
 	MailIndexRecord *rec;
 	MailIndexUpdate *update;
         MboxHeaderContext ctx;
 	time_t internal_date;
 	uoff_t abs_start_offset, eoh_offset;
-	unsigned char *data, md5_digest[16];
+	const unsigned char *data;
+	unsigned char md5_digest[16];
 	size_t size, pos;
 	int failed;
 
 	/* get the From-line */
 	pos = 0;
-	while (io_buffer_read_data_blocking(inbuf, &data, &size, pos) > 0) {
+	while (i_buffer_read_data(inbuf, &data, &size, pos) > 0) {
 		for (; pos < size; pos++) {
 			if (data[pos] == '\n')
 				break;
@@ -62,13 +63,13 @@
 	if (internal_date <= 0)
 		internal_date = ioloop_time;
 
-	io_buffer_skip(inbuf, pos+1);
-	abs_start_offset = inbuf->start_offset + inbuf->offset;
+	i_buffer_skip(inbuf, pos+1);
+	abs_start_offset = inbuf->start_offset + inbuf->v_offset;
 
 	/* now, find the end of header. also stops at "\nFrom " if it's
 	   found (broken messages) */
 	mbox_skip_header(inbuf);
-	eoh_offset = inbuf->offset;
+	eoh_offset = inbuf->v_offset;
 
 	/* add message to index */
 	rec = mail_index_record_append_begin(index, internal_date);
@@ -87,13 +88,13 @@
 	mbox_header_init_context(&ctx, index, inbuf);
         ctx.set_read_limit = TRUE;
 
-	io_buffer_seek(inbuf, abs_start_offset - inbuf->start_offset);
+	i_buffer_seek(inbuf, abs_start_offset - inbuf->start_offset);
 
-	io_buffer_set_read_limit(inbuf, eoh_offset);
+	i_buffer_set_read_limit(inbuf, eoh_offset);
 	mail_index_update_headers(update, inbuf, 0, mbox_header_func, &ctx);
 
-	io_buffer_seek(inbuf, inbuf->limit);
-	io_buffer_set_read_limit(inbuf, 0);
+	i_buffer_seek(inbuf, inbuf->v_limit);
+	i_buffer_set_read_limit(inbuf, 0);
 
 	/* save MD5 */
 	md5_final(&ctx.md5, md5_digest);
@@ -117,9 +118,9 @@
 	return !failed;
 }
 
-int mbox_index_append(MailIndex *index, IOBuffer *inbuf)
+int mbox_index_append(MailIndex *index, IBuffer *inbuf)
 {
-	if (inbuf->offset == inbuf->size) {
+	if (inbuf->v_offset == inbuf->v_size) {
 		/* no new data */
 		return TRUE;
 	}
@@ -128,7 +129,7 @@
 		return FALSE;
 
 	for (;;) {
-		if (inbuf->start_offset + inbuf->offset != 0) {
+		if (inbuf->start_offset + inbuf->v_offset != 0) {
 			/* we're at the [\r]\n before the From-line,
 			   skip it */
 			if (!mbox_skip_crlf(inbuf)) {
@@ -142,7 +143,7 @@
 			}
 		}
 
-		if (inbuf->offset == inbuf->size)
+		if (inbuf->v_offset == inbuf->v_size)
 			break;
 
 		if (!mbox_index_append_next(index, inbuf))
--- a/src/lib-index/mbox/mbox-fsck.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-fsck.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "hex-binary.h"
 #include "message-parser.h"
 #include "message-part-serialize.h"
@@ -12,20 +12,20 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-static void skip_line(IOBuffer *inbuf)
+static void skip_line(IBuffer *inbuf)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size;
 
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, 0) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, 0) > 0) {
 		for (i = 0; i < size; i++) {
 			if (msg[i] == '\n') {
-				io_buffer_skip(inbuf, i+1);
+				i_buffer_skip(inbuf, i+1);
 				return;
 			}
 		}
 
-		io_buffer_skip(inbuf, i);
+		i_buffer_skip(inbuf, i);
 	}
 }
 
@@ -83,7 +83,7 @@
 }
 
 static int match_next_record(MailIndex *index, MailIndexRecord *rec,
-			     unsigned int seq, IOBuffer *inbuf,
+			     unsigned int seq, IBuffer *inbuf,
 			     MailIndexRecord **next_rec, int *dirty)
 {
         MailIndexUpdate *update;
@@ -97,14 +97,14 @@
 	/* skip the From-line */
 	skip_line(inbuf);
 
-	header_offset = inbuf->offset;
+	header_offset = inbuf->v_offset;
 
 	if (rec->body_size == 0) {
 		/* possibly broken message, find the From-line to make sure
 		   header parser won't pass it. */
 		mbox_skip_header(inbuf);
-		io_buffer_set_read_limit(inbuf, inbuf->offset);
-		io_buffer_seek(inbuf, header_offset);
+		i_buffer_set_read_limit(inbuf, inbuf->v_offset);
+		i_buffer_seek(inbuf, header_offset);
 	}
 
 	/* get the MD5 sum of fixed headers and the current message flags
@@ -114,9 +114,9 @@
 	md5_final(&ctx.md5, current_digest);
 
 	mbox_header_free_context(&ctx);
-	io_buffer_set_read_limit(inbuf, 0);
+	i_buffer_set_read_limit(inbuf, 0);
 
-	body_offset = inbuf->offset;
+	body_offset = inbuf->v_offset;
 	do {
 		if (verify_header_md5sum(index, rec, current_digest) &&
 		    mbox_verify_end_of_body(inbuf,
@@ -169,11 +169,11 @@
 	return TRUE;
 }
 
-static int mbox_index_fsck_buf(MailIndex *index, IOBuffer *inbuf)
+static int mbox_index_fsck_buf(MailIndex *index, IBuffer *inbuf)
 {
 	MailIndexRecord *rec;
 	uoff_t from_offset;
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 	unsigned int seq;
 	int dirty;
@@ -185,8 +185,8 @@
 
 	/* first make sure we start with a "From " line. If file is too
 	   small, we'll just treat it as empty mbox file. */
-	if (io_buffer_read_data_blocking(inbuf, &data, &size, 5) > 0 &&
-	    strncmp((char *) data, "From ", 5) != 0) {
+	if (i_buffer_read_data(inbuf, &data, &size, 5) > 0 &&
+	    strncmp((const char *) data, "From ", 5) != 0) {
 		index_set_error(index, "File isn't in mbox format: %s",
 				index->mbox_path);
 		return FALSE;
@@ -205,8 +205,8 @@
 
 	dirty = FALSE;
 	while (rec != NULL) {
-		from_offset = inbuf->offset;
-		if (inbuf->offset != 0) {
+		from_offset = inbuf->v_offset;
+		if (inbuf->v_offset != 0) {
 			/* we're at the [\r]\n before the From-line,
 			   skip it */
 			if (!mbox_skip_crlf(inbuf)) {
@@ -216,7 +216,7 @@
 			}
 		}
 
-		if (inbuf->offset == inbuf->size)
+		if (inbuf->v_offset == inbuf->v_size)
 			break;
 
 		if (!match_next_record(index, rec, seq, inbuf, &rec, &dirty))
@@ -224,7 +224,7 @@
 
 		if (rec == NULL) {
 			/* Get back to line before From */
-			io_buffer_seek(inbuf, from_offset);
+			i_buffer_seek(inbuf, from_offset);
 			break;
 		}
 
@@ -244,7 +244,7 @@
 		index->header->flags &= ~MAIL_INDEX_FLAG_DIRTY_MESSAGES;
 	}
 
-	if (inbuf->offset == inbuf->size)
+	if (inbuf->v_offset == inbuf->v_size)
 		return TRUE;
 	else
 		return mbox_index_append(index, inbuf);
@@ -252,7 +252,7 @@
 
 int mbox_index_fsck(MailIndex *index)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	int failed;
 
 	inbuf = mbox_file_open(index, 0, TRUE);
@@ -265,7 +265,7 @@
 		failed = !mbox_index_fsck_buf(index, inbuf);
 		(void)mbox_unlock(index);
 	}
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 
 	if (failed)
 		return FALSE;
--- a/src/lib-index/mbox/mbox-index.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-index.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "rfc822-tokenize.h"
 #include "mbox-index.h"
 #include "mail-index-util.h"
@@ -22,7 +22,7 @@
 	return FALSE;
 }
 
-IOBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen)
+IBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen)
 {
 	i_assert(offset < OFF_T_MAX);
 
@@ -37,9 +37,9 @@
 		}
 	}
 
-	return io_buffer_create_mmap(index->mbox_fd, default_pool,
-				     MAIL_MMAP_BLOCK_SIZE,
-				     (uoff_t)offset, 0, 0);
+	return i_buffer_create_mmap(index->mbox_fd, default_pool,
+				    MAIL_MMAP_BLOCK_SIZE,
+				    (uoff_t)offset, 0, FALSE);
 }
 
 void mbox_file_close(MailIndex *index)
@@ -51,7 +51,7 @@
 }
 
 void mbox_header_init_context(MboxHeaderContext *ctx, MailIndex *index,
-			      IOBuffer *inbuf)
+			      IBuffer *inbuf)
 {
 	memset(ctx, 0, sizeof(MboxHeaderContext));
 	md5_init(&ctx->md5);
@@ -198,19 +198,19 @@
 			break;
 
 		/* a) use Content-Length, b) search for "From "-line */
-		start_offset = ctx->inbuf->offset;
-		io_buffer_set_read_limit(ctx->inbuf, 0);
+		start_offset = ctx->inbuf->v_offset;
+		i_buffer_set_read_limit(ctx->inbuf, 0);
 
 		end_offset = start_offset + ctx->content_length;
 		if (ctx->content_length == 0 ||
 		    !mbox_verify_end_of_body(ctx->inbuf, end_offset)) {
 			mbox_skip_message(ctx->inbuf);
-			end_offset = ctx->inbuf->offset;
+			end_offset = ctx->inbuf->v_offset;
 			ctx->content_length = end_offset - start_offset;
 		}
 
-		io_buffer_seek(ctx->inbuf, start_offset);
-		io_buffer_set_read_limit(ctx->inbuf, end_offset);
+		i_buffer_seek(ctx->inbuf, start_offset);
+		i_buffer_set_read_limit(ctx->inbuf, end_offset);
 		break;
 
 	case 'R':
@@ -346,16 +346,16 @@
 	}
 }
 
-int mbox_skip_crlf(IOBuffer *inbuf)
+int mbox_skip_crlf(IBuffer *inbuf)
 {
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size, pos;
 
 	pos = 0;
-	while (io_buffer_read_data_blocking(inbuf, &data, &size, pos) > 0) {
+	while (i_buffer_read_data(inbuf, &data, &size, pos) > 0) {
 		if (pos == 0) {
 			if (data[0] == '\n') {
-				io_buffer_skip(inbuf, 1);
+				i_buffer_skip(inbuf, 1);
 				return TRUE;
 			}
 			if (data[0] != '\r')
@@ -368,7 +368,7 @@
 			if (data[1] != '\n')
 				return FALSE;
 
-			io_buffer_skip(inbuf, 2);
+			i_buffer_skip(inbuf, 2);
 			return TRUE;
 		}
 	}
@@ -377,25 +377,25 @@
 	return TRUE;
 }
 
-void mbox_skip_empty_lines(IOBuffer *inbuf)
+void mbox_skip_empty_lines(IBuffer *inbuf)
 {
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 
 	/* skip empty lines at beginning */
-	while (io_buffer_read_data_blocking(inbuf, &data, &size, 0) > 0 &&
+	while (i_buffer_read_data(inbuf, &data, &size, 0) > 0 &&
 	       (data[0] == '\r' || data[0] == '\n')) {
-		io_buffer_skip(inbuf, 1);
+		i_buffer_skip(inbuf, 1);
 	}
 }
 
-static int mbox_is_valid_from(IOBuffer *inbuf, size_t startpos)
+static int mbox_is_valid_from(IBuffer *inbuf, size_t startpos)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size;
 
 	i = startpos;
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, i) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, i) > 0) {
 		for (; i < size; i++) {
 			if (msg[i] == '\n') {
 				msg += startpos;
@@ -409,15 +409,15 @@
 	return FALSE;
 }
 
-static void mbox_skip_forward(IOBuffer *inbuf, int header)
+static void mbox_skip_forward(IBuffer *inbuf, int header)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos;
 	int lastmsg;
 
 	/* read until "[\r]\nFrom " is found */
 	startpos = i = 0; lastmsg = TRUE;
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size; i++) {
 			if (msg[i] == '\n' && header && i >= 1) {
 				/* \n[\r]\n - end of header? */
@@ -458,12 +458,12 @@
 		startpos = i < 7 ? i : 7;
 		i -= startpos;
 
-		io_buffer_skip(inbuf, i);
+		i_buffer_skip(inbuf, i);
 	}
 
 	if (lastmsg && startpos > 0) {
 		/* end of file, remove the last [\r]\n */
-		msg = io_buffer_get_data(inbuf, &size);
+		msg = i_buffer_get_data(inbuf, &size);
 		if (size == startpos) {
 			if (msg[startpos-1] == '\n')
 				startpos--;
@@ -472,36 +472,36 @@
 		}
 	}
 
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 }
 
-void mbox_skip_header(IOBuffer *inbuf)
+void mbox_skip_header(IBuffer *inbuf)
 {
 	mbox_skip_forward(inbuf, TRUE);
 }
 
-void mbox_skip_message(IOBuffer *inbuf)
+void mbox_skip_message(IBuffer *inbuf)
 {
 	mbox_skip_forward(inbuf, FALSE);
 }
 
-int mbox_verify_end_of_body(IOBuffer *inbuf, uoff_t end_offset)
+int mbox_verify_end_of_body(IBuffer *inbuf, uoff_t end_offset)
 {
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 
 	/* don't bother parsing the whole body, just make
 	   sure it ends properly */
-	io_buffer_seek(inbuf, end_offset);
+	i_buffer_seek(inbuf, end_offset);
 
-	if (inbuf->offset == inbuf->size) {
+	if (inbuf->v_offset == inbuf->v_size) {
 		/* end of file. a bit unexpected though,
 		   since \n is missing. */
 		return TRUE;
 	}
 
 	/* read forward a bit */
-	if (io_buffer_read_data_blocking(inbuf, &data, &size, 6) < 0)
+	if (i_buffer_read_data(inbuf, &data, &size, 6) < 0)
 		return FALSE;
 
 	/* either there should be the next From-line,
--- a/src/lib-index/mbox/mbox-index.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-index.h	Mon Oct 14 02:49:11 2002 +0300
@@ -11,7 +11,7 @@
 	MD5Context md5;
 	int received;
 
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	uoff_t content_length;
 	int set_read_limit;
 } MboxHeaderContext;
@@ -21,11 +21,11 @@
 /* Make sure the mbox is opened. If reopen is TRUE, the file is closed first,
    which is useful when you want to be sure you're not accessing a deleted
    mbox file. */
-IOBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen);
+IBuffer *mbox_file_open(MailIndex *index, uoff_t offset, int reopen);
 void mbox_file_close(MailIndex *index);
 
 void mbox_header_init_context(MboxHeaderContext *ctx, MailIndex *index,
-			      IOBuffer *inbuf);
+			      IBuffer *inbuf);
 void mbox_header_free_context(MboxHeaderContext *ctx);
 void mbox_header_func(MessagePart *part __attr_unused__,
 		      const char *name, size_t name_len,
@@ -35,11 +35,11 @@
 			 const char *custom_flags[MAIL_CUSTOM_FLAGS_COUNT],
 			 void (*func)(const char *, size_t, int, void *),
 			 void *context);
-int mbox_skip_crlf(IOBuffer *inbuf);
-void mbox_skip_empty_lines(IOBuffer *inbuf);
-void mbox_skip_header(IOBuffer *inbuf);
-void mbox_skip_message(IOBuffer *inbuf);
-int mbox_verify_end_of_body(IOBuffer *inbuf, uoff_t end_offset);
+int mbox_skip_crlf(IBuffer *inbuf);
+void mbox_skip_empty_lines(IBuffer *inbuf);
+void mbox_skip_header(IBuffer *inbuf);
+void mbox_skip_message(IBuffer *inbuf);
+int mbox_verify_end_of_body(IBuffer *inbuf, uoff_t end_offset);
 int mbox_mail_get_start_offset(MailIndex *index, MailIndexRecord *rec,
 			       uoff_t *offset);
 
@@ -47,9 +47,9 @@
 int mbox_index_rebuild(MailIndex *index);
 int mbox_index_sync(MailIndex *index);
 int mbox_index_fsck(MailIndex *index);
-IOBuffer *mbox_open_mail(MailIndex *index, MailIndexRecord *rec);
+IBuffer *mbox_open_mail(MailIndex *index, MailIndexRecord *rec);
 
-int mbox_index_append(MailIndex *index, IOBuffer *inbuf);
+int mbox_index_append(MailIndex *index, IBuffer *inbuf);
 
 time_t mbox_from_parse_date(const char *msg, size_t size);
 const char *mbox_from_create(const char *sender, time_t time);
--- a/src/lib-index/mbox/mbox-open.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-open.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "mbox-index.h"
 #include "mail-index-util.h"
 
@@ -9,11 +9,11 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-IOBuffer *mbox_open_mail(MailIndex *index, MailIndexRecord *rec)
+IBuffer *mbox_open_mail(MailIndex *index, MailIndexRecord *rec)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	uoff_t offset, stop_offset;
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 	int failed;
 
@@ -34,13 +34,14 @@
 
 	/* make sure message size is valid - it must end with
 	   either EOF or "\nFrom "*/
-	if (!io_buffer_seek(inbuf, stop_offset - offset)) {
-		mbox_set_syscall_error(index, "io_buffer_seek()");
-		io_buffer_unref(inbuf);
+	if (!i_buffer_seek(inbuf, stop_offset - offset)) {
+		errno = inbuf->buf_errno;
+		mbox_set_syscall_error(index, "i_buffer_seek()");
+		i_buffer_unref(inbuf);
 		return NULL;
 	}
 
-	(void)io_buffer_read_data_blocking(inbuf, &data, &size, 6);
+	(void)i_buffer_read_data(inbuf, &data, &size, 6);
 	if (size >= 6) {
 		/* "[\r]\nFrom " expected */
 		if (data[0] == '\r') {
@@ -48,7 +49,8 @@
 			size--;
 		}
 
-		failed = size < 6 || strncmp((char *) data, "\nFrom ", 6) != 0;
+		failed = size < 6 ||
+			strncmp((const char *) data, "\nFrom ", 6) != 0;
 	} else {
 		if (size > 0 && data[0] == '\r') {
 			data++;
@@ -61,8 +63,9 @@
 		failed = size != 0;
 	}
 
-	if (!io_buffer_seek(inbuf, 0)) {
-		mbox_set_syscall_error(index, "io_buffer_seek()");
+	if (!i_buffer_seek(inbuf, 0)) {
+		errno = inbuf->buf_errno;
+		mbox_set_syscall_error(index, "i_buffer_seek()");
 		failed = TRUE;
 	}
 
@@ -72,10 +75,10 @@
 
 		index_set_error(index, "mbox file %s was modified "
 				"unexpectedly, fscking", index->mbox_path);
-		io_buffer_unref(inbuf);
+		i_buffer_unref(inbuf);
 		return NULL;
 	}
 
-	io_buffer_set_read_limit(inbuf, stop_offset - offset);
+	i_buffer_set_read_limit(inbuf, stop_offset - offset);
 	return inbuf;
 }
--- a/src/lib-index/mbox/mbox-rebuild.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-rebuild.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "mbox-index.h"
 #include "mbox-lock.h"
 #include "mail-index-data.h"
@@ -14,7 +14,7 @@
 
 int mbox_index_rebuild(MailIndex *index)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	struct stat st;
 	int failed;
 
@@ -48,7 +48,7 @@
 
 	/* lock the mailbox so we can be sure no-one interrupts us. */
 	if (!mbox_lock_read(index)) {
-		io_buffer_unref(inbuf);
+		i_buffer_unref(inbuf);
 		return FALSE;
 	}
 
@@ -56,7 +56,7 @@
 	failed = !mbox_index_append(index, inbuf);
 	(void)mbox_unlock(index);
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 
 	if (failed)
 		return FALSE;
--- a/src/lib-index/mbox/mbox-rewrite.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-rewrite.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,8 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "temp-string.h"
 #include "write-full.h"
 #include "mbox-index.h"
@@ -15,7 +16,7 @@
 #include <fcntl.h>
 
 typedef struct {
-	IOBuffer *outbuf;
+	OBuffer *outbuf;
 	int failed;
 
 	uoff_t content_length;
@@ -48,23 +49,28 @@
 				  MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS);
 }
 
-static int mbox_write(MailIndex *index, IOBuffer *inbuf, IOBuffer *outbuf,
+static int mbox_write(MailIndex *index, IBuffer *inbuf, OBuffer *outbuf,
 		      uoff_t end_offset)
 {
-	i_assert(inbuf->offset <= end_offset);
+	uoff_t old_limit;
+
+	i_assert(inbuf->v_offset <= end_offset);
 
-	if (io_buffer_send_iobuffer(outbuf, inbuf,
-				    end_offset - inbuf->offset) < 0)
-		return FALSE;
-
-	if (inbuf->offset < end_offset) {
+	old_limit = inbuf->v_limit;
+	i_buffer_set_read_limit(inbuf, end_offset - inbuf->v_offset);
+	if (o_buffer_send_ibuffer(outbuf, inbuf) < 0) {
+		index_set_error(index, "Error rewriting mbox file %s: %s",
+				index->mbox_path, strerror(outbuf->buf_errno));
+	} else if (inbuf->v_offset < end_offset) {
 		/* fsck should have noticed it.. */
 		index_set_error(index, "Error rewriting mbox file %s: "
 				"Unexpected end of file", index->mbox_path);
-		return FALSE;
+	} else {
+		return TRUE;
 	}
 
-	return TRUE;
+	i_buffer_set_read_limit(inbuf, old_limit);
+	return FALSE;
 }
 
 static int mbox_write_ximapbase(MboxRewriteContext *ctx)
@@ -74,21 +80,21 @@
 
 	str = t_strdup_printf("X-IMAPbase: %u %u",
 			      ctx->uid_validity, ctx->uid_last);
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
 		if (ctx->custom_flags[i] != NULL) {
-			if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
+			if (o_buffer_send(ctx->outbuf, " ", 1) < 0)
 				return FALSE;
 
-			if (io_buffer_send(ctx->outbuf, ctx->custom_flags[i],
-					   strlen(ctx->custom_flags[i])) < 0)
+			if (o_buffer_send(ctx->outbuf, ctx->custom_flags[i],
+					  strlen(ctx->custom_flags[i])) < 0)
 				return FALSE;
 		}
 	}
 
-	if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
+	if (o_buffer_send(ctx->outbuf, "\n", 1) < 0)
 		return FALSE;
 
 	return TRUE;
@@ -103,32 +109,32 @@
 	    x_keywords == NULL)
 		return TRUE;
 
-	if (io_buffer_send(ctx->outbuf, "X-Keywords:", 11) < 0)
+	if (o_buffer_send(ctx->outbuf, "X-Keywords:", 11) < 0)
 		return FALSE;
 
 	field = 1 << MAIL_CUSTOM_FLAG_1_BIT;
 	for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++, field <<= 1) {
 		if ((ctx->msg_flags & field) && ctx->custom_flags[i] != NULL) {
-			if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
+			if (o_buffer_send(ctx->outbuf, " ", 1) < 0)
 				return FALSE;
 
-			if (io_buffer_send(ctx->outbuf, ctx->custom_flags[i],
-					   strlen(ctx->custom_flags[i])) < 0)
+			if (o_buffer_send(ctx->outbuf, ctx->custom_flags[i],
+					  strlen(ctx->custom_flags[i])) < 0)
 				return FALSE;
 		}
 	}
 
 	if (x_keywords != NULL) {
 		/* X-Keywords that aren't custom flags */
-		if (io_buffer_send(ctx->outbuf, " ", 1) < 0)
+		if (o_buffer_send(ctx->outbuf, " ", 1) < 0)
 			return FALSE;
 
-		if (io_buffer_send(ctx->outbuf, x_keywords,
-				   strlen(x_keywords)) < 0)
+		if (o_buffer_send(ctx->outbuf, x_keywords,
+				  strlen(x_keywords)) < 0)
 			return FALSE;
 	}
 
-	if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
+	if (o_buffer_send(ctx->outbuf, "\n", 1) < 0)
 		return FALSE;
 
 	return TRUE;
@@ -142,9 +148,9 @@
 	if (status != NULL)
 		str = t_strconcat(str, status, NULL);
 
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
-	if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
+	if (o_buffer_send(ctx->outbuf, "\n", 1) < 0)
 		return FALSE;
 
 	return TRUE;
@@ -166,9 +172,9 @@
 			  (ctx->msg_flags & MAIL_DELETED) ? "T" : "",
 			  x_status, NULL);
 
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
-	if (io_buffer_send(ctx->outbuf, "\n", 1) < 0)
+	if (o_buffer_send(ctx->outbuf, "\n", 1) < 0)
 		return FALSE;
 
 	return TRUE;
@@ -181,7 +187,7 @@
 	i_snprintf(str, sizeof(str), "Content-Length: %"PRIuUOFF_T"\n",
 		   ctx->content_length);
 
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 	return TRUE;
 }
@@ -274,10 +280,10 @@
 		(void)mbox_write_content_length(ctx);
 	} else if (name_len > 0) {
 		/* save this header */
-		(void)io_buffer_send(ctx->outbuf, name, name_len);
-		(void)io_buffer_send(ctx->outbuf, ": ", 2);
-		(void)io_buffer_send(ctx->outbuf, value, value_len);
-		(void)io_buffer_send(ctx->outbuf, "\n", 1);
+		(void)o_buffer_send(ctx->outbuf, name, name_len);
+		(void)o_buffer_send(ctx->outbuf, ": ", 2);
+		(void)o_buffer_send(ctx->outbuf, value, value_len);
+		(void)o_buffer_send(ctx->outbuf, "\n", 1);
 	}
 
 	if (ctx->outbuf->closed)
@@ -286,7 +292,7 @@
 
 static int mbox_write_header(MailIndex *index,
 			     MailIndexRecord *rec, unsigned int seq,
-			     IOBuffer *inbuf, IOBuffer *outbuf,
+			     IBuffer *inbuf, OBuffer *outbuf,
 			     uoff_t end_offset)
 {
 	/* We need to update fields that define message flags. Standard fields
@@ -303,7 +309,7 @@
 	MboxRewriteContext ctx;
 	MessageSize hdr_size;
 
-	if (inbuf->offset >= end_offset) {
+	if (inbuf->v_offset >= end_offset) {
 		/* fsck should have noticed it.. */
 		index_set_error(index, "Error rewriting mbox file %s: "
 				"Unexpected end of file", index->mbox_path);
@@ -321,9 +327,9 @@
 	ctx.uid_validity = index->header->uid_validity-1;
 	ctx.custom_flags = mail_custom_flags_list_get(index->custom_flags);
 
-	io_buffer_set_read_limit(inbuf, inbuf->offset + rec->header_size);
+	i_buffer_set_read_limit(inbuf, inbuf->v_offset + rec->header_size);
 	message_parse_header(NULL, inbuf, &hdr_size, header_func, &ctx);
-	io_buffer_set_read_limit(inbuf, 0);
+	i_buffer_set_read_limit(inbuf, 0);
 
 	i_assert(hdr_size.physical_size == rec->header_size);
 
@@ -347,14 +353,15 @@
 	mail_custom_flags_list_unref(index->custom_flags);
 
 	/* empty line ends headers */
-	(void)io_buffer_send(outbuf, "\n", 1);
+	(void)o_buffer_send(outbuf, "\n", 1);
 
 	return TRUE;
 }
 
 static int fd_copy(int in_fd, int out_fd, uoff_t out_offset)
 {
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 	int ret;
 
 	i_assert(out_offset <= OFF_T_MAX);
@@ -364,21 +371,22 @@
 
 	t_push();
 
-	inbuf = io_buffer_create_mmap(in_fd, data_stack_pool,
-				      1024*256, 0, 0, 0);
-	outbuf = io_buffer_create_file(out_fd, data_stack_pool, 1024, FALSE);
+	inbuf = i_buffer_create_mmap(in_fd, data_stack_pool,
+				     1024*256, 0, 0, FALSE);
+	outbuf = o_buffer_create_file(out_fd, data_stack_pool, 1024,
+				      IO_PRIORITY_DEFAULT, FALSE);
 
-	ret = io_buffer_send_iobuffer(outbuf, inbuf, inbuf->size);
+	ret = o_buffer_send_ibuffer(outbuf, inbuf);
 	if (ret < 0)
 		errno = outbuf->buf_errno;
 	else {
 		/* we may have shrinked the file */
-		i_assert(out_offset + inbuf->size <= OFF_T_MAX);
-		ret = ftruncate(out_fd, (off_t) (out_offset + inbuf->size));
+		i_assert(out_offset + inbuf->v_size <= OFF_T_MAX);
+		ret = ftruncate(out_fd, (off_t) (out_offset + inbuf->v_size));
 	}
 
-	io_buffer_unref(outbuf);
-	io_buffer_unref(inbuf);
+	o_buffer_unref(outbuf);
+	i_buffer_unref(inbuf);
 	t_pop();
 
 	return ret;
@@ -391,7 +399,8 @@
 	   interrupted (see below). This rewriting relies quite a lot on
 	   valid header/body sizes which fsck() should have ensured. */
 	MailIndexRecord *rec;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 	uoff_t offset, dirty_offset;
 	const char *path;
 	unsigned int seq;
@@ -439,7 +448,7 @@
 		if (locked)
 			(void)mbox_unlock(index);
 		if (inbuf != NULL)
-			io_buffer_unref(inbuf);
+			i_buffer_unref(inbuf);
 		return !failed;
 	}
 
@@ -452,7 +461,8 @@
 	dirty_offset = 0;
 
 	t_push();
-	outbuf = io_buffer_create_file(tmp_fd, data_stack_pool, 8192, FALSE);
+	outbuf = o_buffer_create_file(tmp_fd, data_stack_pool, 8192,
+				      IO_PRIORITY_DEFAULT, FALSE);
 
 	failed = FALSE; seq = 1;
 	rec = index->lookup(index, 1);
@@ -465,7 +475,8 @@
 				break;
 			}
 
-			if (offset + rec->header_size + rec->body_size > inbuf->size) {
+			if (offset + rec->header_size +
+			    rec->body_size > inbuf->v_size) {
 				index_set_corrupted(index,
 						    "Invalid message size");
 				failed = TRUE;
@@ -481,7 +492,7 @@
 			dirty_found = TRUE;
 			dirty_offset = offset;
 
-			io_buffer_seek(inbuf, dirty_offset);
+			i_buffer_seek(inbuf, dirty_offset);
 		}
 
 		if (dirty_found) {
@@ -518,15 +529,15 @@
 	}
 
 	/* always end with a \n */
-	(void)io_buffer_send(outbuf, "\n", 1);
+	(void)o_buffer_send(outbuf, "\n", 1);
 	if (outbuf->closed) {
 		errno = outbuf->buf_errno;
 		mbox_set_syscall_error(index, "write()");
 		failed = TRUE;
 	}
 
-	io_buffer_unref(inbuf);
-	io_buffer_unref(outbuf);
+	i_buffer_unref(inbuf);
+	o_buffer_unref(outbuf);
 	t_pop();
 
 	if (!failed) {
--- a/src/lib-index/mbox/mbox-sync.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-index/mbox/mbox-sync.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,6 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
 #include "mbox-index.h"
 #include "mail-index-util.h"
 
--- a/src/lib-mail/message-parser.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-parser.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "rfc822-tokenize.h"
 #include "message-content-parser.h"
 #include "message-parser.h"
@@ -27,12 +27,12 @@
 	void *context;
 } MessageParseContext;
 
-static MessagePart *message_parse_part(IOBuffer *inbuf,
+static MessagePart *message_parse_part(IBuffer *inbuf,
 				       MessageParseContext *parse_ctx);
-static MessagePart *message_parse_body(IOBuffer *inbuf,
+static MessagePart *message_parse_body(IBuffer *inbuf,
 				       MessageBoundary *boundaries,
 				       MessageSize *body_size);
-static MessagePart *message_skip_boundary(IOBuffer *inbuf,
+static MessagePart *message_skip_boundary(IBuffer *inbuf,
 					  MessageBoundary *boundaries,
 					  MessageSize *boundary_size);
 
@@ -136,7 +136,7 @@
 	}
 }
 
-static MessagePart *message_parse_multipart(IOBuffer *inbuf,
+static MessagePart *message_parse_multipart(IBuffer *inbuf,
 					    MessageParseContext *parse_ctx)
 {
 	MessagePart *parent_part, *next_part, *part;
@@ -185,7 +185,7 @@
 	return next_part;
 }
 
-static MessagePart *message_parse_part(IOBuffer *inbuf,
+static MessagePart *message_parse_part(IBuffer *inbuf,
 				       MessageParseContext *parse_ctx)
 {
 	MessagePart *next_part, *part;
@@ -241,7 +241,7 @@
 	return next_part;
 }
 
-MessagePart *message_parse(Pool pool, IOBuffer *inbuf,
+MessagePart *message_parse(Pool pool, IBuffer *inbuf,
 			   MessageHeaderFunc func, void *context)
 {
 	MessagePart *part;
@@ -260,14 +260,14 @@
 }
 
 /* skip over to next line increasing message size */
-static void message_skip_line(IOBuffer *inbuf, MessageSize *msg_size)
+static void message_skip_line(IBuffer *inbuf, MessageSize *msg_size)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos;
 
 	startpos = 0;
 
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size; i++) {
 			if (msg[i] == '\n') {
 				if (msg_size != NULL) {
@@ -285,7 +285,7 @@
 		}
 
 		/* leave the last character, it may be \r */
-		io_buffer_skip(inbuf, i - 1);
+		i_buffer_skip(inbuf, i - 1);
 		startpos = 1;
 
 		if (msg_size != NULL) {
@@ -294,7 +294,7 @@
 		}
 	}
 
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 
 	if (msg_size != NULL) {
 		msg_size->physical_size += startpos;
@@ -302,11 +302,11 @@
 	}
 }
 
-void message_parse_header(MessagePart *part, IOBuffer *inbuf,
+void message_parse_header(MessagePart *part, IBuffer *inbuf,
 			  MessageSize *hdr_size,
 			  MessageHeaderFunc func, void *context)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos, missing_cr_count;
 	size_t line_start, colon_pos, end_pos, name_len, value_len;
 	int ret;
@@ -317,8 +317,7 @@
 	missing_cr_count = startpos = line_start = 0;
 	colon_pos = UINT_MAX;
 	for (;;) {
-		ret = io_buffer_read_data_blocking(inbuf, &msg, &size,
-						   startpos+1);
+		ret = i_buffer_read_data(inbuf, &msg, &size, startpos+1);
 		if (ret == -2) {
 			/* overflow, line is too long. just skip it. */
 			i_assert(size > 2);
@@ -411,13 +410,13 @@
 			colon_pos -= line_start;
 		if (hdr_size != NULL)
 			hdr_size->physical_size += line_start;
-		io_buffer_skip(inbuf, line_start);
+		i_buffer_skip(inbuf, line_start);
 
 		startpos = i-line_start;
 		line_start = 0;
 	}
 
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 
 	if (hdr_size != NULL) {
 		hdr_size->physical_size += startpos;
@@ -450,17 +449,17 @@
    [\r]\n before the boundary, otherwise leave it right after the known
    boundary so the ending "--" can be checked. */
 static MessageBoundary *
-message_find_boundary(IOBuffer *inbuf, MessageBoundary *boundaries,
+message_find_boundary(IBuffer *inbuf, MessageBoundary *boundaries,
 		      MessageSize *msg_size, int skip_over)
 {
 	MessageBoundary *boundary;
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos, line_start, missing_cr_count;
 
 	boundary = NULL;
 	missing_cr_count = startpos = line_start = 0;
 
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size; i++) {
 			if (msg[i] != '\n')
 				continue;
@@ -510,7 +509,7 @@
 			line_start -= i;
 		}
 
-		io_buffer_skip(inbuf, i);
+		i_buffer_skip(inbuf, i);
 		msg_size->physical_size += i;
 		msg_size->virtual_size += i;
 
@@ -534,7 +533,7 @@
 		startpos = line_start;
 	}
 
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 	msg_size->physical_size += startpos;
 	msg_size->virtual_size += startpos + missing_cr_count;
 
@@ -543,7 +542,7 @@
 	return boundary;
 }
 
-static MessagePart *message_parse_body(IOBuffer *inbuf,
+static MessagePart *message_parse_body(IBuffer *inbuf,
 				       MessageBoundary *boundaries,
 				       MessageSize *body_size)
 {
@@ -561,12 +560,12 @@
 
 /* skip data until next boundary is found. if it's end boundary,
    skip the footer as well. */
-static MessagePart *message_skip_boundary(IOBuffer *inbuf,
+static MessagePart *message_skip_boundary(IBuffer *inbuf,
 					  MessageBoundary *boundaries,
 					  MessageSize *boundary_size)
 {
 	MessageBoundary *boundary;
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t size;
 	int end_boundary;
 
@@ -577,7 +576,7 @@
 
 	/* now, see if it's end boundary */
 	end_boundary = FALSE;
-	if (io_buffer_read_data_blocking(inbuf, &msg, &size, 1) > 0)
+	if (i_buffer_read_data(inbuf, &msg, &size, 1) > 0)
 		end_boundary = msg[0] == '-' && msg[1] == '-';
 
 	/* skip the rest of the line */
--- a/src/lib-mail/message-parser.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-parser.h	Mon Oct 14 02:49:11 2002 +0300
@@ -44,7 +44,7 @@
 				  void *context);
 
 /* func is called for each field in message header. */
-MessagePart *message_parse(Pool pool, IOBuffer *inbuf,
+MessagePart *message_parse(Pool pool, IBuffer *inbuf,
 			   MessageHeaderFunc func, void *context);
 
 /* Call func for each field in message header. Fills the hdr_size.
@@ -52,7 +52,7 @@
    This function doesn't use data stack so your header function may save
    values to it. When finished, inbuf will point to beginning of message
    body. */
-void message_parse_header(MessagePart *part, IOBuffer *inbuf,
+void message_parse_header(MessagePart *part, IBuffer *inbuf,
 			  MessageSize *hdr_size,
 			  MessageHeaderFunc func, void *context);
 
--- a/src/lib-mail/message-send.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-send.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,16 +1,18 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "message-send.h"
 #include "message-size.h"
 
-int message_send(IOBuffer *outbuf, IOBuffer *inbuf, MessageSize *msg_size,
+int message_send(OBuffer *outbuf, IBuffer *inbuf, MessageSize *msg_size,
 		 uoff_t virtual_skip, uoff_t max_virtual_size)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
+	uoff_t old_limit;
 	size_t i, size;
-	int cr_skipped, add_cr;
+	int cr_skipped, add_cr, ret;
 
 	if (msg_size->physical_size == 0 ||
 	    virtual_skip >= msg_size->virtual_size)
@@ -21,15 +23,21 @@
 
 	if (msg_size->physical_size == msg_size->virtual_size) {
 		/* no need to kludge with CRs, we can use sendfile() */
-		io_buffer_skip(inbuf, virtual_skip);
-		return io_buffer_send_iobuffer(outbuf, inbuf,
-					       max_virtual_size) > 0;
+		i_buffer_skip(inbuf, virtual_skip);
+
+		old_limit = inbuf->v_limit;
+		i_buffer_set_read_limit(inbuf,
+					I_MIN(max_virtual_size, old_limit));
+		ret = o_buffer_send_ibuffer(outbuf, inbuf) > 0;
+		i_buffer_set_read_limit(inbuf, old_limit);
+
+		return ret;
 	}
 
 	message_skip_virtual(inbuf, virtual_skip, NULL, &cr_skipped);
 
 	/* go through the message data and insert CRs where needed.  */
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, 0) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, 0) > 0) {
 		add_cr = FALSE;
 		for (i = 0; i < size; i++) {
 			if (msg[i] == '\n') {
@@ -52,11 +60,11 @@
 			}
 		}
 
-		if (io_buffer_send(outbuf, msg, i) <= 0)
+		if (o_buffer_send(outbuf, msg, i) <= 0)
 			return FALSE;
 
 		if (add_cr) {
-			if (io_buffer_send(outbuf, "\r", 1) <= 0)
+			if (o_buffer_send(outbuf, "\r", 1) <= 0)
 				return FALSE;
 			cr_skipped = TRUE;
 		} else {
@@ -67,7 +75,7 @@
 		if (max_virtual_size == 0)
 			break;
 
-		io_buffer_skip(inbuf, i);
+		i_buffer_skip(inbuf, i);
 	}
 
 	return TRUE;
--- a/src/lib-mail/message-send.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-send.h	Mon Oct 14 02:49:11 2002 +0300
@@ -6,7 +6,7 @@
 /* Send message to client inserting CRs if needed. Only max_virtual_size
    bytes if sent (relative to virtual_skip), if you want it unlimited,
    use (uoff_t)-1. Returns TRUE if successful. */
-int message_send(IOBuffer *outbuf, IOBuffer *inbuf, MessageSize *msg_size,
+int message_send(OBuffer *outbuf, IBuffer *inbuf, MessageSize *msg_size,
 		 uoff_t virtual_skip, uoff_t max_virtual_size);
 
 #endif
--- a/src/lib-mail/message-size.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-size.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,19 +1,19 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "message-parser.h"
 #include "message-size.h"
 
-void message_get_header_size(IOBuffer *inbuf, MessageSize *hdr)
+void message_get_header_size(IBuffer *inbuf, MessageSize *hdr)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos, missing_cr_count;
 
 	memset(hdr, 0, sizeof(MessageSize));
 
 	missing_cr_count = 0; startpos = 0;
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size; i++) {
 			if (msg[i] != '\n')
 				continue;
@@ -44,28 +44,28 @@
 
 		/* leave the last two characters, they may be \r\n */
 		startpos = size == 1 ? 1 : 2;
-		io_buffer_skip(inbuf, i - startpos);
+		i_buffer_skip(inbuf, i - startpos);
 
 		hdr->physical_size += i - startpos;
 	}
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 	hdr->physical_size += startpos;
 
 	hdr->virtual_size = hdr->physical_size + missing_cr_count;
 	i_assert(hdr->virtual_size >= hdr->physical_size);
 }
 
-void message_get_body_size(IOBuffer *inbuf, MessageSize *body,
+void message_get_body_size(IBuffer *inbuf, MessageSize *body,
 			   uoff_t max_virtual_size)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos, missing_cr_count;
 
 	memset(body, 0, sizeof(MessageSize));
 
 	missing_cr_count = 0; startpos = 0;
 	while (max_virtual_size != 0 &&
-	       io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	       i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size && max_virtual_size != 0; i++) {
 			if (max_virtual_size > 0)
 				max_virtual_size--;
@@ -91,22 +91,22 @@
 		}
 
 		/* leave the last character, it may be \r */
-		io_buffer_skip(inbuf, i - 1);
+		i_buffer_skip(inbuf, i - 1);
 		startpos = 1;
 
 		body->physical_size += i - 1;
 	}
-	io_buffer_skip(inbuf, startpos);
+	i_buffer_skip(inbuf, startpos);
 	body->physical_size += startpos;
 
 	body->virtual_size = body->physical_size + missing_cr_count;
 	i_assert(body->virtual_size >= body->physical_size);
 }
 
-void message_skip_virtual(IOBuffer *inbuf, uoff_t virtual_skip,
+void message_skip_virtual(IBuffer *inbuf, uoff_t virtual_skip,
 			  MessageSize *msg_size, int *cr_skipped)
 {
-	unsigned char *msg;
+	const unsigned char *msg;
 	size_t i, size, startpos;
 
 	*cr_skipped = FALSE;
@@ -114,7 +114,7 @@
 		return;
 
 	startpos = 0;
-	while (io_buffer_read_data_blocking(inbuf, &msg, &size, startpos) > 0) {
+	while (i_buffer_read_data(inbuf, &msg, &size, startpos) > 0) {
 		for (i = startpos; i < size && virtual_skip > 0; i++) {
 			virtual_skip--;
 
@@ -151,12 +151,12 @@
 		}
 
 		if (i < size) {
-			io_buffer_skip(inbuf, i);
+			i_buffer_skip(inbuf, i);
 			break;
 		}
 
 		/* leave the last character, it may be \r */
-		io_buffer_skip(inbuf, i - 1);
+		i_buffer_skip(inbuf, i - 1);
 		startpos = 1;
 	}
 }
--- a/src/lib-mail/message-size.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-mail/message-size.h	Mon Oct 14 02:49:11 2002 +0300
@@ -5,16 +5,16 @@
 
 /* Calculate size of message header. Leave the inbuf point to first
    character in body. */
-void message_get_header_size(IOBuffer *inbuf, MessageSize *hdr);
+void message_get_header_size(IBuffer *inbuf, MessageSize *hdr);
 /* Calculate size of message body. Read only max_virtual_size virtual bytes,
    if you want it unlimited, use (uoff_t)-1. */
-void message_get_body_size(IOBuffer *inbuf, MessageSize *body,
+void message_get_body_size(IBuffer *inbuf, MessageSize *body,
 			   uoff_t max_virtual_size);
 
 /* Skip number of virtual bytes from buffer. If first character is \n, and
    cr_skipped is FALSE, \r must be sent before it. msg_size is updated if
    it's not NULL. */
-void message_skip_virtual(IOBuffer *inbuf, uoff_t virtual_skip,
+void message_skip_virtual(IBuffer *inbuf, uoff_t virtual_skip,
 			  MessageSize *msg_size, int *cr_skipped);
 
 /* Sum contents of src into dest. */
--- a/src/lib-storage/index/index-copy.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-copy.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "mail-custom-flags.h"
 #include "index-storage.h"
 #include "index-messageset.h"
@@ -18,7 +18,7 @@
 		     unsigned int idx_seq __attr_unused__, void *context)
 {
 	CopyContext *ctx = context;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	int failed;
 
 	inbuf = index->open_mail(index, rec);
@@ -28,9 +28,9 @@
 	/* save it in destination mailbox */
 	failed = !ctx->dest->save(ctx->dest, rec->msg_flags,
 				  ctx->custom_flags, rec->internal_date,
-				  inbuf, inbuf->size);
+				  inbuf, inbuf->v_size);
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	return !failed;
 }
 
--- a/src/lib-storage/index/index-fetch-section.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-fetch-section.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,8 @@
 
 #include "lib.h"
 #include "temp-string.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "rfc822-tokenize.h"
 #include "message-send.h"
 #include "index-storage.h"
@@ -42,7 +43,7 @@
 		      FetchContext *ctx, const char *prefix, int fetch_header)
 {
 	MessageSize size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *str;
 
 	if (!imap_msgcache_get_rfc822_partial(ctx->cache, sect->skip,
@@ -55,7 +56,7 @@
 
 	str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n",
 			      prefix, size.virtual_size);
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	return message_send(ctx->outbuf, inbuf, &size, 0, sect->max_size);
@@ -133,7 +134,7 @@
 
 typedef struct {
 	TempString *dest;
-	IOBuffer *outbuf;
+	OBuffer *outbuf;
 	uoff_t dest_size;
 
 	uoff_t skip, max_size;
@@ -165,7 +166,7 @@
 	ctx->dest_size += size;
 
 	if (ctx->outbuf != NULL) {
-		if (io_buffer_send(ctx->outbuf, str, size) < 0)
+		if (o_buffer_send(ctx->outbuf, str, size) < 0)
 			return FALSE;
 	}
 	return ctx->dest_size < ctx->max_size;
@@ -214,7 +215,7 @@
 	(void)fetch_header_append(ctx, "\r\n", 2);
 }
 
-static int fetch_header_fields(IOBuffer *inbuf, const char *section,
+static int fetch_header_fields(IBuffer *inbuf, const char *section,
 			       FetchHeaderFieldContext *ctx)
 {
 	if (strncasecmp(section, "HEADER.FIELDS ", 14) == 0) {
@@ -240,7 +241,7 @@
 }
 
 /* fetch wanted headers from given data */
-static int fetch_header_from(IOBuffer *inbuf, IOBuffer *outbuf, 
+static int fetch_header_from(IBuffer *inbuf, OBuffer *outbuf,
 			     const char *prefix, MessageSize *size,
 			     const char *section, MailFetchBodyData *sect)
 {
@@ -255,7 +256,7 @@
 		/* all headers */
 		str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n",
 				      prefix, size->virtual_size);
-		if (io_buffer_send(outbuf, str, strlen(str)) < 0)
+		if (o_buffer_send(outbuf, str, strlen(str)) < 0)
 			return FALSE;
 		return message_send(outbuf, inbuf, size,
 				    sect->skip, sect->max_size);
@@ -270,7 +271,7 @@
 	ctx.max_size = sect->max_size;
 
 	failed = FALSE;
-	start_offset = inbuf->offset;
+	start_offset = inbuf->v_offset;
 
 	t_push();
 
@@ -291,7 +292,7 @@
 	if (!failed) {
 		str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n",
 				      prefix, ctx.dest_size);
-		if (io_buffer_send(outbuf, str, strlen(str)) < 0)
+		if (o_buffer_send(outbuf, str, strlen(str)) < 0)
 			failed = TRUE;
 	}
 
@@ -301,7 +302,7 @@
 			uoff_t first_size = ctx.dest_size;
 
 			ctx.outbuf = outbuf;
-			if (!io_buffer_seek(inbuf, start_offset))
+			if (!i_buffer_seek(inbuf, start_offset))
 				failed = TRUE;
 
 			if (!failed &&
@@ -310,8 +311,8 @@
 
 			i_assert(first_size == ctx.dest_size);
 		} else {
-			if (io_buffer_send(outbuf, ctx.dest->str,
-					   ctx.dest->len) < 0)
+			if (o_buffer_send(outbuf, ctx.dest->str,
+					  ctx.dest->len) < 0)
 				failed = TRUE;
 		}
 	}
@@ -325,7 +326,7 @@
 			const char *prefix)
 {
 	MessageSize hdr_size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 
 	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, &hdr_size, NULL))
 		return FALSE;
@@ -378,7 +379,7 @@
 static int fetch_part_body(MessagePart *part, MailFetchBodyData *sect,
 			   FetchContext *ctx, const char *prefix)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *str;
 	uoff_t skip_pos;
 
@@ -387,11 +388,11 @@
 
 	/* jump to beginning of wanted data */
 	skip_pos = part->physical_pos + part->header_size.physical_size;
-	io_buffer_skip(inbuf, skip_pos);
+	i_buffer_skip(inbuf, skip_pos);
 
 	str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n",
 			      prefix, part->body_size.virtual_size);
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	/* FIXME: potential performance problem with big messages:
@@ -405,12 +406,12 @@
 			     MailFetchBodyData *sect, FetchContext *ctx,
 			     const char *prefix)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 
 	if (!imap_msgcache_get_data(ctx->cache, &inbuf))
 		return FALSE;
 
-	io_buffer_skip(inbuf, part->physical_pos);
+	i_buffer_skip(inbuf, part->physical_pos);
 	return fetch_header_from(inbuf, ctx->outbuf, prefix, &part->header_size,
 				 section, sect);
 }
--- a/src/lib-storage/index/index-fetch.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-fetch.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "obuffer.h"
 #include "temp-string.h"
 #include "mail-custom-flags.h"
 #include "index-storage.h"
@@ -108,7 +108,7 @@
 static int index_fetch_send_rfc822(MailIndexRecord *rec, FetchContext *ctx)
 {
 	MessageSize hdr_size, body_size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *str;
 
 	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf,
@@ -124,7 +124,7 @@
 	if (ctx->first) {
 		str++; ctx->first = FALSE;
 	}
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	body_size.physical_size += hdr_size.physical_size;
@@ -136,7 +136,7 @@
 					  FetchContext *ctx)
 {
 	MessageSize hdr_size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *str;
 
 	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, &hdr_size, NULL)) {
@@ -151,7 +151,7 @@
 	if (ctx->first) {
 		str++; ctx->first = FALSE;
 	}
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	return message_send(ctx->outbuf, inbuf, &hdr_size, 0, (uoff_t)-1);
@@ -160,7 +160,7 @@
 static int index_fetch_send_rfc822_text(MailIndexRecord *rec, FetchContext *ctx)
 {
 	MessageSize body_size;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *str;
 
 	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, NULL, &body_size)) {
@@ -175,7 +175,7 @@
 	if (ctx->first) {
 		str++; ctx->first = FALSE;
 	}
-	if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
+	if (o_buffer_send(ctx->outbuf, str, strlen(str)) < 0)
 		return FALSE;
 
 	return message_send(ctx->outbuf, inbuf, &body_size, 0, (uoff_t)-1);
@@ -299,8 +299,8 @@
 			if (!ctx->first)
 				ctx->str->len--;
 
-			if (io_buffer_send(ctx->outbuf, ctx->str->str,
-					   ctx->str->len) < 0)
+			if (o_buffer_send(ctx->outbuf, ctx->str->str,
+					  ctx->str->len) < 0)
 				break;
 		}
 
@@ -327,7 +327,7 @@
 	} while (0);
 
 	if (data_written) {
-		if (io_buffer_send(ctx->outbuf, ")\r\n", 3) < 0)
+		if (o_buffer_send(ctx->outbuf, ")\r\n", 3) < 0)
 			failed = TRUE;
 	}
 
@@ -336,7 +336,7 @@
 }
 
 int index_storage_fetch(Mailbox *box, MailFetchData *fetch_data,
-			IOBuffer *outbuf, int *all_found)
+			OBuffer *outbuf, int *all_found)
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
 	FetchContext ctx;
--- a/src/lib-storage/index/index-fetch.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-fetch.h	Mon Oct 14 02:49:11 2002 +0300
@@ -9,7 +9,7 @@
 	const char **custom_flags;
 
 	MailFetchData *fetch_data;
-	IOBuffer *outbuf;
+	OBuffer *outbuf;
 	TempString *str;
 	int update_seen, found_unseen;
 	int first;
--- a/src/lib-storage/index/index-msgcache.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-msgcache.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "imap-message-cache.h"
 #include "message-part-serialize.h"
 #include "mail-index.h"
@@ -24,20 +24,20 @@
 	return ctx;
 }
 
-static IOBuffer *index_msgcache_open_mail(void *context)
+static IBuffer *index_msgcache_open_mail(void *context)
 {
         IndexMsgcacheContext *ctx = context;
 
 	return ctx->index->open_mail(ctx->index, ctx->rec);
 }
 
-static IOBuffer *index_msgcache_inbuf_rewind(IOBuffer *inbuf,
-					     void *context __attr_unused__)
+static IBuffer *index_msgcache_inbuf_rewind(IBuffer *inbuf,
+					    void *context __attr_unused__)
 {
-	if (!io_buffer_seek(inbuf, 0)) {
+	if (!i_buffer_seek(inbuf, 0)) {
 		i_error("index_msgcache_inbuf_rewind: lseek() failed: %m");
 
-		io_buffer_unref(inbuf);
+		i_buffer_unref(inbuf);
 		return NULL;
 	}
 
--- a/src/lib-storage/index/index-save.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-save.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "write-full.h"
 #include "index-storage.h"
 
@@ -38,9 +38,9 @@
 }
 
 int index_storage_save_into_fd(MailStorage *storage, int fd, const char *path,
-			       IOBuffer *buf, uoff_t data_size)
+			       IBuffer *buf, uoff_t data_size)
 {
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 	ssize_t ret;
 	int last_cr, failed;
@@ -49,14 +49,14 @@
 
 	failed = FALSE;
 	while (data_size > 0) {
-		ret = io_buffer_read_blocking(buf);
+		ret = i_buffer_read(buf);
 		if (ret < 0) {
 			mail_storage_set_critical(storage,
 						  "Error reading mail: %m");
 			return FALSE;
 		}
 
-		data = io_buffer_get_data(buf, &size);
+		data = i_buffer_get_data(buf, &size);
 		if (size > data_size)
 			size = (size_t)data_size;
 		data_size -= size;
@@ -73,7 +73,7 @@
 			failed = TRUE;
 		}
 
-		io_buffer_skip(buf, size);
+		i_buffer_skip(buf, size);
 	}
 
 	return !failed;
--- a/src/lib-storage/index/index-search.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-search.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,8 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "mmap-util.h"
 #include "rfc822-tokenize.h"
 #include "rfc822-date.h"
@@ -530,12 +531,12 @@
 		search_text(arg, ctx);
 }
 
-static void search_arg_match_data(IOBuffer *inbuf, uoff_t max_size,
+static void search_arg_match_data(IBuffer *inbuf, uoff_t max_size,
 				  MailSearchArg *args,
 				  MailSearchForeachFunc search_func)
 {
 	SearchTextContext ctx;
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size, max_searchword_len;
 
 	memset(&ctx, 0, sizeof(ctx));
@@ -545,33 +546,33 @@
 	mail_search_args_foreach(args, search_func, &ctx);
         max_searchword_len = ctx.max_searchword_len;
 
-	io_buffer_set_read_limit(inbuf, inbuf->offset + max_size);
+	i_buffer_set_read_limit(inbuf, inbuf->v_offset + max_size);
 
 	/* do this in blocks: read data, compare it for all search words, skip
 	   for block size - (strlen(largest_searchword)-1) and continue. */
-	while (io_buffer_read_data_blocking(inbuf, &data, &size,
-					    max_searchword_len-1) > 0) {
+	while (i_buffer_read_data(inbuf, &data, &size,
+				  max_searchword_len-1) > 0) {
 		ctx.msg = (char *) data;
 		ctx.size = size;
 		mail_search_args_foreach(args, search_func, &ctx);
-		io_buffer_skip(inbuf, size - (max_searchword_len-1));
+		i_buffer_skip(inbuf, size - (max_searchword_len-1));
 	}
 
 	if (size > 0) {
 		/* last block */
-		ctx.msg = (char *) data;
+		ctx.msg = (const char *) data;
 		ctx.size = size;
 		mail_search_args_foreach(args, search_func, &ctx);
-		io_buffer_skip(inbuf, size);
+		i_buffer_skip(inbuf, size);
 	}
 
-	io_buffer_set_read_limit(inbuf, 0);
+	i_buffer_set_read_limit(inbuf, 0);
 }
 
 static int search_arg_match_text(IndexMailbox *ibox, MailIndexRecord *rec,
 				 MailSearchArg *args)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	MessageSize hdr_size;
 	int have_headers, have_body, have_text;
 
@@ -604,10 +605,11 @@
 	}
 
 	if (have_text) {
-		if (inbuf->offset != 0) {
+		if (inbuf->v_offset != 0) {
 			/* need to rewind back to beginning of headers */
-			if (!io_buffer_seek(inbuf, 0)) {
-				i_error("io_buffer_seek() failed: %m");
+			if (!i_buffer_seek(inbuf, 0)) {
+				errno = inbuf->buf_errno;
+				i_error("i_buffer_seek() failed: %m");
 				return FALSE;
 			}
 		}
@@ -617,16 +619,16 @@
 	}
 
 	if (have_text || have_body) {
-		if (inbuf->offset == 0) {
+		if (inbuf->v_offset == 0) {
 			/* skip over headers */
-			io_buffer_skip(inbuf, hdr_size.physical_size);
+			i_buffer_skip(inbuf, hdr_size.physical_size);
 		}
 
 		search_arg_match_data(inbuf, rec->body_size, args,
 				      search_text_body);
 	}
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	return TRUE;
 }
 
@@ -739,7 +741,7 @@
 }
 
 static int search_messages(IndexMailbox *ibox, MailSearchArg *args,
-			   IOBuffer *outbuf, int uid_result)
+			   OBuffer *outbuf, int uid_result)
 {
 	SearchIndexContext ctx;
 	MailIndexRecord *rec;
@@ -795,7 +797,7 @@
 			if (found) {
 				i_snprintf(num, sizeof(num), " %u",
 					   uid_result ? rec->uid : client_seq);
-				io_buffer_send(outbuf, num, strlen(num));
+				o_buffer_send(outbuf, num, strlen(num));
 			}
 		}
 
@@ -806,7 +808,7 @@
 }
 
 int index_storage_search(Mailbox *box, MailSearchArg *args,
-			 IOBuffer *outbuf, int uid_result)
+			 OBuffer *outbuf, int uid_result)
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
 	int failed;
@@ -817,9 +819,9 @@
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
 		return mail_storage_set_index_error(ibox);
 
-	io_buffer_send(outbuf, "* SEARCH", 8);
+	o_buffer_send(outbuf, "* SEARCH", 8);
 	failed = !search_messages(ibox, args, outbuf, uid_result);
-	io_buffer_send(outbuf, "\r\n", 2);
+	o_buffer_send(outbuf, "\r\n", 2);
 
 	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
 		return mail_storage_set_index_error(ibox);
--- a/src/lib-storage/index/index-storage.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/index-storage.h	Mon Oct 14 02:49:11 2002 +0300
@@ -40,7 +40,7 @@
 			     MailIndexRecord **rec);
 
 int index_storage_save_into_fd(MailStorage *storage, int fd, const char *path,
-			       IOBuffer *buf, uoff_t data_size);
+			       IBuffer *buf, uoff_t data_size);
 
 void *index_msgcache_get_context(MailIndex *index, MailIndexRecord *rec);
 
@@ -61,8 +61,8 @@
 			       MailFlagUpdateFunc func, void *context,
 			       int *all_found);
 int index_storage_fetch(Mailbox *box, MailFetchData *fetch_data,
-			IOBuffer *outbuf, int *all_found);
+			OBuffer *outbuf, int *all_found);
 int index_storage_search(Mailbox *box, MailSearchArg *args,
-			 IOBuffer *outbuf, int uid_result);
+			 OBuffer *outbuf, int uid_result);
 
 #endif
--- a/src/lib-storage/index/maildir/maildir-save.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,8 +1,8 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "ioloop.h"
 #include "hostpid.h"
-#include "iobuffer.h"
 #include "maildir-index.h"
 #include "maildir-storage.h"
 
@@ -41,7 +41,7 @@
 }
 
 static const char *maildir_read_into_tmp(MailStorage *storage, const char *dir,
-					 IOBuffer *buf, uoff_t data_size)
+					 IBuffer *buf, uoff_t data_size)
 {
 	const char *fname, *path;
 	int fd;
@@ -63,7 +63,7 @@
 
 int maildir_storage_save(Mailbox *box, MailFlags flags,
 			 const char *custom_flags[], time_t internal_date,
-			 IOBuffer *data, uoff_t data_size)
+			 IBuffer *data, uoff_t data_size)
 {
         IndexMailbox *ibox = (IndexMailbox *) box;
         struct utimbuf buf;
--- a/src/lib-storage/index/maildir/maildir-storage.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Mon Oct 14 02:49:11 2002 +0300
@@ -7,7 +7,7 @@
 			 const char *messageset, int uidset);
 int maildir_storage_save(Mailbox *box, MailFlags flags,
 			 const char *custom_flags[], time_t internal_date,
-			 IOBuffer *data, uoff_t data_size);
+			 IBuffer *data, uoff_t data_size);
 
 int maildir_find_mailboxes(MailStorage *storage, const char *mask,
 			   MailboxFunc func, void *context);
--- a/src/lib-storage/index/mbox/mbox-expunge.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-expunge.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,8 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "mbox-index.h"
 #include "mbox-storage.h"
 #include "mbox-lock.h"
@@ -10,14 +11,14 @@
 #include <unistd.h>
 
 static int expunge_real(IndexMailbox *ibox, MailIndexRecord *rec,
-			unsigned int seq, IOBuffer *inbuf, IOBuffer *outbuf,
+			unsigned int seq, IBuffer *inbuf, OBuffer *outbuf,
 			MailExpungeFunc expunge_func, void *context)
 {
-	uoff_t offset, end_offset, from_offset, copy_size;
+	uoff_t offset, end_offset, from_offset, copy_size, old_limit;
 	unsigned int uid;
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
-	int expunges;
+	int expunges, failed;
 
 	if (seq == 1)
 		end_offset = 0;
@@ -34,6 +35,8 @@
 		rec = ibox->index->next(ibox->index, rec);
 	}
 
+	old_limit = inbuf->v_limit;
+
 	expunges = FALSE;
 	while (rec != NULL) {
 		if (!mbox_mail_get_start_offset(ibox->index, rec, &offset))
@@ -57,31 +60,34 @@
 			if (!expunges) {
 				/* first expunged record, seek to position
 				   where we want to begin writing */
-				if (!io_buffer_seek(outbuf, from_offset))
+				if (!o_buffer_seek(outbuf, from_offset))
 					return FALSE;
 				expunges = TRUE;
 			}
 		} else if (expunges) {
 			/* seek to wanted input position, and copy
 			   this messages */
-			i_assert(inbuf->offset <= from_offset);
-			io_buffer_skip(inbuf, from_offset - inbuf->offset);
+			i_assert(inbuf->v_offset <= from_offset);
+			i_buffer_skip(inbuf, from_offset - inbuf->v_offset);
 
-			if (outbuf->offset == 0) {
+			if (outbuf->v_offset == 0) {
 				/* we're writing to beginning of mbox, so we
 				   don't want the [\r]\n there */
-				(void)io_buffer_read_data_blocking(inbuf, &data,
-								   &size, 1);
+				(void)i_buffer_read_data(inbuf, &data,
+							 &size, 1);
 				if (size > 0 && data[0] == '\n')
-					io_buffer_skip(inbuf, 1);
+					i_buffer_skip(inbuf, 1);
 				else if (size > 1 && data[0] == '\r' &&
 					 data[1] == '\n')
-					io_buffer_skip(inbuf, 2);
+					i_buffer_skip(inbuf, 2);
 			}
 
-                        copy_size = end_offset - inbuf->offset;
-			if (io_buffer_send_iobuffer(outbuf, inbuf,
-						    copy_size) < 0)
+			i_buffer_set_read_limit(inbuf,
+						end_offset - inbuf->v_offset);
+			failed = o_buffer_send_ibuffer(outbuf, inbuf) < 0;
+			i_buffer_set_read_limit(inbuf, old_limit);
+
+			if (failed || inbuf->v_offset != end_offset)
 				return FALSE;
 		}
 
@@ -89,23 +95,28 @@
 		seq++;
 	}
 
-	io_buffer_skip(inbuf, end_offset - inbuf->offset);
+	i_buffer_skip(inbuf, end_offset - inbuf->v_offset);
 
 	/* copy the rest as well, should be only \n but someone might
 	   as well just appended more data.. but if we've deleted all mail,
 	   don't write the only \n there. */
-	copy_size = inbuf->size - inbuf->offset;
-	if (outbuf->offset == 0 && copy_size == 1)
+	copy_size = inbuf->v_size - inbuf->v_offset;
+	if (outbuf->v_offset == 0 && copy_size == 1)
 		return TRUE;
-	else
-		return io_buffer_send_iobuffer(outbuf, inbuf, copy_size) > 0;
+
+	i_buffer_set_read_limit(inbuf, copy_size);
+	failed = o_buffer_send_ibuffer(outbuf, inbuf) < 0;
+	i_buffer_set_read_limit(inbuf, old_limit);
+
+	return !failed && inbuf->v_offset == end_offset;
 }
 
 int mbox_expunge_locked(IndexMailbox *ibox,
 			MailExpungeFunc expunge_func, void *context)
 {
 	MailIndexRecord *rec;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 	unsigned int seq;
 	int failed;
 
@@ -122,25 +133,24 @@
 		return FALSE;
 
 	if (!mbox_lock_write(ibox->index)) {
-		io_buffer_unref(inbuf);
+		i_buffer_unref(inbuf);
 		return FALSE;
 	}
 
 	t_push();
-	outbuf = io_buffer_create_file(inbuf->fd, data_stack_pool, 4096, FALSE);
+	outbuf = o_buffer_create_file(ibox->index->mbox_fd, data_stack_pool,
+				      4096, IO_PRIORITY_DEFAULT, FALSE);
 
 	failed = !expunge_real(ibox, rec, seq, inbuf, outbuf,
 			       expunge_func, context);
 
-	if (failed && outbuf->offset > 0) {
+	if (failed && outbuf->v_offset > 0) {
 		/* we moved some of the data. move the rest as well so there
 		   won't be invalid holes in mbox file */
-		i_assert(inbuf->offset <= inbuf->size);
-		(void)io_buffer_send_iobuffer(outbuf, inbuf,
-					      inbuf->size - inbuf->offset);
+		(void)o_buffer_send_ibuffer(outbuf, inbuf);
 	}
 
-	if (ftruncate(outbuf->fd, outbuf->offset) < 0) {
+	if (ftruncate(ibox->index->mbox_fd, outbuf->v_offset) < 0) {
 		mail_storage_set_error(ibox->box.storage, "ftruncate() failed "
 				       "for mbox file %s: %m",
 				       ibox->index->mbox_path);
@@ -148,7 +158,7 @@
 	}
 
 	(void)mbox_unlock(ibox->index);
-	io_buffer_unref(outbuf);
+	o_buffer_unref(outbuf);
 	t_pop();
 
 	return !failed;
--- a/src/lib-storage/index/mbox/mbox-save.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-save.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,7 @@
 
 #include "lib.h"
 #include "hostpid.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "write-full.h"
 #include "mbox-index.h"
 #include "mbox-lock.h"
@@ -145,12 +145,12 @@
 }
 
 int mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
-		      time_t internal_date, IOBuffer *data, uoff_t data_size)
+		      time_t internal_date, IBuffer *data, uoff_t data_size)
 {
 	IndexMailbox *ibox = (IndexMailbox *) box;
 	MailFlags real_flags;
 	const char *mbox_path;
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	int fd, failed;
 	off_t pos;
 
@@ -165,12 +165,12 @@
 	if (!index_mailbox_fix_custom_flags(ibox, &real_flags, custom_flags))
 		return FALSE;
 
-	/* just make sure the mbox is opened, we don't need the iobuffer */
+	/* just make sure the mbox is opened, we don't need the ibuffer */
 	inbuf = mbox_file_open(ibox->index, 0, TRUE);
 	if (inbuf == NULL)
 		return FALSE;
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 	fd = ibox->index->mbox_fd;
 
 	if (!mbox_lock_write(ibox->index)) {
--- a/src/lib-storage/index/mbox/mbox-storage.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Mon Oct 14 02:49:11 2002 +0300
@@ -6,7 +6,7 @@
 int mbox_storage_copy(Mailbox *box, Mailbox *destbox,
 		      const char *messageset, int uidset);
 int mbox_storage_save(Mailbox *box, MailFlags flags, const char *custom_flags[],
-		      time_t internal_date, IOBuffer *data, uoff_t data_size);
+		      time_t internal_date, IBuffer *data, uoff_t data_size);
 
 int mbox_find_mailboxes(MailStorage *storage, const char *mask,
 			MailboxFunc func, void *context);
--- a/src/lib-storage/mail-storage.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib-storage/mail-storage.h	Mon Oct 14 02:49:11 2002 +0300
@@ -159,16 +159,16 @@
 	/* Fetch wanted mail data. The results are written into outbuf
 	   in RFC2060 FETCH format. */
 	int (*fetch)(Mailbox *box, MailFetchData *fetch_data,
-		     IOBuffer *outbuf, int *all_found);
+		     OBuffer *outbuf, int *all_found);
 
 	/* Search wanted mail data. args contains the search criteria.
 	   results are written into outbuf in RFC2060 SEARCH format. */
 	int (*search)(Mailbox *box, MailSearchArg *args,
-		      IOBuffer *outbuf, int uid_result);
+		      OBuffer *outbuf, int uid_result);
 
 	/* Save a new mail into mailbox. */
 	int (*save)(Mailbox *box, MailFlags flags, const char *custom_flags[],
-		    time_t internal_date, IOBuffer *data, uoff_t data_size);
+		    time_t internal_date, IBuffer *data, uoff_t data_size);
 
 	/* Returns TRUE if mailbox is now in inconsistent state, meaning that
 	   the message IDs etc. may have changed - only way to recover this
--- a/src/lib/Makefile.am	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/Makefile.am	Mon Oct 14 02:49:11 2002 +0300
@@ -24,6 +24,10 @@
 	hostpid.c \
 	imem.c \
 	iobuffer.c \
+	ibuffer.c \
+	ibuffer-data.c \
+	ibuffer-file.c \
+	ibuffer-mmap.c \
 	ioloop.c \
 	$(ioloop_source) \
 	lib.c \
@@ -36,6 +40,8 @@
 	mmap-anon.c \
 	mmap-util.c \
 	network.c \
+	obuffer.c \
+	obuffer-file.c \
 	primes.c \
 	randgen.c \
 	restrict-access.c \
@@ -59,7 +65,9 @@
 	hex-binary.h \
 	hostpid.h \
 	imem.h \
-	iobuffer.h \
+	iobuffer-internal.h \
+	ibuffer.h \
+	ibuffer-internal.h \
 	ioloop.h \
 	ioloop-internal.h \
 	lib.h \
@@ -69,6 +77,8 @@
 	mempool.h \
 	mmap-util.h \
 	network.h \
+	obuffer.h \
+	obuffer-internal.h \
 	primes.h \
 	randgen.h \
 	restrict-access.h \
--- a/src/lib/compat.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/compat.c	Mon Oct 14 02:49:11 2002 +0300
@@ -134,3 +134,29 @@
 #endif
 }
 #endif
+
+#ifndef HAVE_WRITEV
+ssize_t my_writev(int fd, const struct iovec *iov, size_t iov_len)
+{
+	size_t i, written;
+	ssize_t ret;
+
+	written = 0;
+	for (i = 0; i < iov_len; i++, iov++) {
+		ret = write(fd, iov->iov_base, iov->iov_len);
+		if (ret < 0)
+			return -1;
+
+		written += ret;
+		if ((size_t)ret != iov->iov_len)
+			break;
+	}
+
+	if (written > SSIZE_T_MAX) {
+		errno = ERANGE;
+		return -1;
+	}
+
+	return (ssize_t)written;
+}
+#endif
--- a/src/lib/compat.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/compat.h	Mon Oct 14 02:49:11 2002 +0300
@@ -62,6 +62,19 @@
 int my_getpagesize(void);
 #endif
 
+#ifndef HAVE_STRUCT_IOVEC
+struct iovec {
+	void *iov_base;
+	size_t iov_len;
+};
+#endif
+
+#ifndef HAVE_WRITEV
+#  define writev my_writev
+struct iovec;
+ssize_t my_writev(int fd, const struct iovec *iov, size_t iov_len);
+#endif
+
 /* ctype.h isn't safe with signed chars,
    use our own instead if really needed */
 #define i_toupper(x) ((char) toupper((int) (unsigned char) (x)))
--- a/src/lib/data-stack.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/data-stack.c	Mon Oct 14 02:49:11 2002 +0300
@@ -365,6 +365,8 @@
 	StackFrame *frame;
 
 	frame = malloc(sizeof(StackFrame));
+	if (frame == NULL)
+		i_panic("t_push(): Out of memory");
 	frame->allocs = NULL;
 
 	frame->next = current_frame;
@@ -397,6 +399,8 @@
 	FrameAlloc *alloc;
 
 	alloc = malloc(sizeof(FrameAlloc));
+	if (alloc == NULL)
+		i_panic("add_alloc(): Out of memory");
 	alloc->mem = mem;
 	alloc->next = current_frame->allocs;
 	current_frame->allocs = alloc;
@@ -412,6 +416,8 @@
 	void *mem;
 
 	mem = malloc(size);
+	if (mem == NULL)
+		i_panic("t_malloc(): Out of memory");
 	add_alloc(mem);
 	return mem;
 }
@@ -421,6 +427,8 @@
 	void *mem;
 
 	mem = calloc(size, 1);
+	if (mem == NULL)
+		i_panic("t_malloc0(): Out of memory");
 	add_alloc(mem);
 	return mem;
 }
@@ -440,6 +448,8 @@
 void *t_buffer_get(size_t size)
 {
 	buffer_mem = realloc(buffer_mem, size);
+	if (buffer_mem == NULL)
+		i_panic("t_buffer_get(): Out of memory");
 	return buffer_mem;
 }
 
@@ -448,6 +458,8 @@
 	i_assert(buffer == buffer_mem);
 
 	buffer_mem = realloc(buffer_mem, size);
+	if (buffer_mem == NULL)
+		i_panic("t_buffer_reget(): Out of memory");
 	return buffer_mem;
 }
 
@@ -458,6 +470,8 @@
 	i_assert(buffer_mem != NULL);
 
 	mem = realloc(buffer_mem, size);
+	if (mem == NULL)
+		i_panic("t_buffer_alloc(): Out of memory");
 	buffer_mem = NULL;
 
 	add_alloc(mem);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer-data.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,84 @@
+/*
+   ibuffer-data.c : Input buffer interface for reading from data buffer
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "ibuffer-internal.h"
+
+static void _close(_IOBuffer *buf __attr_unused__)
+{
+}
+
+static void _destroy(_IOBuffer *buf __attr_unused__)
+{
+}
+
+static void _set_max_size(_IOBuffer *buf __attr_unused__,
+			  size_t max_size __attr_unused__)
+{
+}
+
+static void _set_blocking(_IOBuffer *buf __attr_unused__,
+			  int timeout_msecs __attr_unused__,
+			  TimeoutFunc timeout_func __attr_unused__,
+			  void *context __attr_unused__)
+{
+}
+
+static ssize_t _read(_IBuffer *buf)
+{
+	return buf->pos - buf->skip;
+}
+
+static int _seek(_IBuffer *buf, uoff_t v_offset)
+{
+	buf->skip = v_offset;
+	return 1;
+}
+
+static int _skip(_IBuffer *buf, uoff_t count)
+{
+	buf->skip += count;
+	return 1;
+}
+
+IBuffer *i_buffer_create_from_data(Pool pool, const unsigned char *data,
+				   size_t size)
+{
+	_IBuffer *buf;
+
+	buf = p_new(pool, _IBuffer, 1);
+	buf->buffer = data;
+
+	buf->iobuf.close = _close;
+	buf->iobuf.destroy = _destroy;
+	buf->iobuf.set_max_size = _set_max_size;
+	buf->iobuf.set_blocking = _set_blocking;
+
+	buf->read = _read;
+	buf->skip_count = _skip;
+	buf->seek = _seek;
+
+	return _i_buffer_create(buf, pool, -1, 0, size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer-file.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,305 @@
+/*
+   ibuffer-file.c : Input buffer handling for files
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "ibuffer-internal.h"
+
+#include <unistd.h>
+
+#define I_BUFFER_MIN_SIZE 1024
+
+typedef struct {
+	_IBuffer ibuf;
+
+	size_t max_buffer_size;
+
+	int timeout_msecs;
+	TimeoutFunc timeout_func;
+	void *timeout_context;
+
+	unsigned int autoclose_fd:1;
+} FileIBuffer;
+
+typedef struct {
+	IOLoop ioloop;
+	IBuffer *buf;
+	int timeout;
+} IOLoopReadContext;
+
+static void _close(_IOBuffer *buf)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+	_IBuffer *_buf = (_IBuffer *) buf;
+
+	if (fbuf->autoclose_fd && _buf->fd != -1) {
+		if (close(_buf->fd) < 0)
+			i_error("FileIBuffer.close() failed: %m");
+		_buf->fd = -1;
+	}
+}
+
+static void _destroy(_IOBuffer *buf)
+{
+	_IBuffer *_buf = (_IBuffer *) buf;
+
+	p_free(_buf->iobuf.pool, _buf->w_buffer);
+}
+
+static void _set_max_size(_IOBuffer *buf, size_t max_size)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+
+	fbuf->max_buffer_size = max_size;
+}
+
+static void _set_blocking(_IOBuffer *buf, int timeout_msecs,
+			  TimeoutFunc timeout_func, void *context)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+
+	fbuf->timeout_msecs = timeout_msecs;
+	fbuf->timeout_func = timeout_func;
+	fbuf->timeout_context = context;
+}
+
+static void i_buffer_grow(_IBuffer *buf, size_t bytes)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+
+	buf->buffer_size = buf->pos + bytes;
+	buf->buffer_size =
+		buf->buffer_size <= I_BUFFER_MIN_SIZE ? I_BUFFER_MIN_SIZE :
+		nearest_power(buf->buffer_size);
+
+	if (fbuf->max_buffer_size > 0 &&
+	    buf->buffer_size > fbuf->max_buffer_size)
+		buf->buffer_size = fbuf->max_buffer_size;
+
+	buf->buffer = buf->w_buffer =
+		p_realloc(buf->iobuf.pool, buf->w_buffer, buf->buffer_size);
+}
+
+static void i_buffer_compress(_IBuffer *buf)
+{
+	memmove(buf->w_buffer, buf->w_buffer + buf->skip,
+		buf->pos - buf->skip);
+	buf->pos -= buf->skip;
+
+	if (buf->skip > buf->cr_lookup_pos)
+		buf->cr_lookup_pos = 0;
+	else
+		buf->cr_lookup_pos -= buf->skip;
+
+	buf->skip = 0;
+}
+
+static void ioloop_read(void *context, int fd __attr_unused__,
+			IO io __attr_unused__)
+{
+	IOLoopReadContext *ctx = context;
+
+	if (i_buffer_read(ctx->buf) != 0) {
+		/* got data / error */
+		io_loop_stop(ctx->ioloop);
+	}
+}
+
+static void ioloop_timeout(void *context, Timeout timeout __attr_unused__)
+{
+	IOLoopReadContext *ctx = context;
+
+	ctx->timeout = TRUE;
+	io_loop_stop(ctx->ioloop);
+}
+
+static ssize_t i_buffer_read_blocking(_IBuffer *buf)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+        IOLoopReadContext ctx;
+	Timeout to;
+	IO io;
+
+	t_push();
+
+	/* create a new I/O loop */
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.ioloop = io_loop_create(data_stack_pool);
+	ctx.buf = &buf->ibuffer;
+
+	io = io_add(buf->fd, IO_READ, ioloop_read, &ctx);
+	to = fbuf->timeout_msecs <= 0 ? NULL :
+		timeout_add(fbuf->timeout_msecs, ioloop_timeout, &ctx);
+
+	io_loop_run(ctx.ioloop);
+
+	io_remove(io);
+	if (to != NULL) {
+		if (ctx.timeout && fbuf->timeout_func != NULL) {
+			/* call user-given timeout function */
+			fbuf->timeout_func(fbuf->timeout_context, to);
+		}
+		timeout_remove(to);
+	}
+
+	io_loop_destroy(ctx.ioloop);
+	t_pop();
+
+	return buf->pos > buf->skip ? (ssize_t) (buf->pos-buf->skip) : -1;
+}
+
+static ssize_t _read(_IBuffer *buf)
+{
+	FileIBuffer *fbuf = (FileIBuffer *) buf;
+	size_t size;
+	ssize_t ret;
+
+	if (buf->ibuffer.closed)
+		return -1;
+
+	buf->ibuffer.buf_errno = 0;
+
+	if (buf->pos == buf->buffer_size) {
+		if (buf->skip > 0) {
+			/* remove the unused bytes from beginning of buffer */
+                        i_buffer_compress(buf);
+		} else if (fbuf->max_buffer_size == 0 ||
+			   buf->buffer_size < fbuf->max_buffer_size) {
+			/* buffer is full - grow it */
+			i_buffer_grow(buf, I_BUFFER_MIN_SIZE);
+		}
+
+		if (buf->pos == buf->buffer_size)
+                        return -2; /* buffer full */
+	}
+
+	size = buf->buffer_size - buf->pos;
+	if (buf->ibuffer.v_limit > 0) {
+		i_assert(buf->ibuffer.v_limit >= buf->ibuffer.v_offset);
+		if (size >= buf->ibuffer.v_limit - buf->ibuffer.v_offset) {
+			size = buf->ibuffer.v_limit - buf->ibuffer.v_offset;
+			if (size == 0) {
+				/* virtual limit reached == EOF */
+				return -1;
+			}
+		}
+	}
+
+	ret = read(buf->fd, buf->w_buffer + buf->pos, size);
+	if (ret == 0) {
+		/* EOF */
+		return -1;
+	}
+
+	if (ret < 0) {
+		if (errno == EINTR || errno == EAGAIN)
+			ret = 0;
+		else {
+			buf->ibuffer.buf_errno = errno;
+			return -1;
+		}
+	}
+
+	if (ret > 0 || fbuf->timeout_msecs == 0) {
+		buf->pos += ret;
+		return ret;
+	} else {
+		/* blocking read */
+		return i_buffer_read_blocking(buf);
+	}
+}
+
+static int _skip(_IBuffer *buf, uoff_t count)
+{
+	uoff_t old_limit;
+	ssize_t ret;
+	off_t skipped;
+
+	if (buf->buffer_size == 0)
+		i_buffer_grow(buf, I_BUFFER_MIN_SIZE);
+
+	skipped = 0;
+	old_limit = buf->ibuffer.v_limit;
+	i_buffer_set_read_limit(&buf->ibuffer, buf->ibuffer.v_offset + count);
+
+	while (count > 0 && (ret = i_buffer_read(&buf->ibuffer)) > 0) {
+		if ((size_t)ret > count)
+			ret = count;
+
+		count -= ret;
+		buf->skip += ret;
+		buf->ibuffer.v_offset += ret;
+	}
+
+	i_buffer_set_read_limit(&buf->ibuffer, old_limit);
+	return count > 0 ? -1 : 1;
+}
+
+static int _seek(_IBuffer *buf, uoff_t v_offset)
+{
+	uoff_t real_offset;
+	off_t ret;
+
+	real_offset = buf->ibuffer.start_offset + v_offset;
+	if (real_offset > OFF_T_MAX) {
+		buf->ibuffer.buf_errno = EINVAL;
+		return -1;
+	}
+
+	ret = lseek(buf->fd, (off_t)real_offset, SEEK_SET);
+	if (ret < 0) {
+		buf->ibuffer.buf_errno = errno;
+		return -1;
+	}
+
+	if (ret != (off_t)real_offset) {
+		buf->ibuffer.buf_errno = EINVAL;
+		return -1;
+	}
+
+	buf->ibuffer.buf_errno = 0;
+	buf->ibuffer.v_offset = v_offset;
+	return 1;
+}
+
+IBuffer *i_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
+			      int autoclose_fd)
+{
+	FileIBuffer *mbuf;
+
+	mbuf = p_new(pool, FileIBuffer, 1);
+	mbuf->max_buffer_size = max_buffer_size;
+	mbuf->autoclose_fd = autoclose_fd;
+
+	mbuf->ibuf.iobuf.close = _close;
+	mbuf->ibuf.iobuf.destroy = _destroy;
+	mbuf->ibuf.iobuf.set_max_size = _set_max_size;
+	mbuf->ibuf.iobuf.set_blocking = _set_blocking;
+
+	mbuf->ibuf.read = _read;
+	mbuf->ibuf.skip_count = _skip;
+	mbuf->ibuf.seek = _seek;
+
+	return _i_buffer_create(&mbuf->ibuf, pool, fd, 0, 0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer-internal.h	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,33 @@
+#ifndef __IBUFFER_INTERNAL_H
+#define __IBUFFER_INTERNAL_H
+
+#include "ibuffer.h"
+#include "iobuffer-internal.h"
+
+typedef struct __IBuffer _IBuffer;
+
+struct __IBuffer {
+/* inheritance: */
+	_IOBuffer iobuf;
+
+/* methods: */
+	ssize_t (*read)(_IBuffer *buf);
+	int (*skip_count)(_IBuffer *buf, uoff_t count);
+	int (*seek)(_IBuffer *buf, uoff_t v_offset);
+
+/* data: */
+	IBuffer ibuffer;
+
+	int fd;
+	const unsigned char *buffer;
+	unsigned char *w_buffer; /* may be NULL */
+	size_t buffer_size;
+
+	size_t skip, pos, cr_lookup_pos;
+	int last_cr;
+};
+
+IBuffer *_i_buffer_create(_IBuffer *_buf, Pool pool, int fd,
+			  uoff_t start_offset, uoff_t v_size);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer-mmap.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,235 @@
+/*
+   ibuffer-mmap.c : Input buffer handling for mmap()ed files
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "mmap-util.h"
+#include "ibuffer-internal.h"
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+typedef struct {
+	_IBuffer ibuf;
+
+	int fd;
+	void *mmap_base;
+	uoff_t mmap_offset;
+	size_t mmap_block_size;
+
+	unsigned int autoclose_fd:1;
+} MmapIBuffer;
+
+static size_t mmap_pagesize = 0;
+static size_t mmap_pagemask = 0;
+
+static void _close(_IOBuffer *buf)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+
+	if (mbuf->autoclose_fd && mbuf->fd != -1) {
+		if (close(mbuf->fd) < 0)
+			i_error("MmapIBuffer.close() failed: %m");
+		mbuf->fd = -1;
+	}
+}
+
+static void i_buffer_munmap(MmapIBuffer *mbuf)
+{
+	_IBuffer *_buf = &mbuf->ibuf;
+
+	if (_buf->buffer != NULL) {
+		if (munmap(mbuf->mmap_base, _buf->buffer_size) < 0)
+			i_error("MmapIBuffer.munmap() failed: %m");
+		mbuf->mmap_base = NULL;
+		_buf->buffer = NULL;
+		_buf->buffer_size = 0;
+		mbuf->mmap_offset = 0;
+	}
+}
+
+static void _destroy(_IOBuffer *buf)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+
+	i_buffer_munmap(mbuf);
+}
+
+static void _set_max_size(_IOBuffer *buf, size_t max_size)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+
+	mbuf->mmap_block_size = max_size;
+}
+
+static void _set_blocking(_IOBuffer *buf __attr_unused__,
+			  int timeout_msecs __attr_unused__,
+			  TimeoutFunc timeout_func __attr_unused__,
+			  void *context __attr_unused__)
+{
+	/* we never block */
+}
+
+static ssize_t io_buffer_set_mmaped_pos(_IBuffer *buf)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+
+	i_assert((uoff_t)mbuf->mmap_offset <=
+		 buf->ibuffer.start_offset + buf->ibuffer.v_limit);
+
+	buf->pos = buf->ibuffer.start_offset + buf->ibuffer.v_limit -
+		mbuf->mmap_offset;
+	if (buf->pos > buf->buffer_size)
+		buf->pos = buf->buffer_size;
+
+	return buf->pos - buf->skip;
+}
+
+static ssize_t _read(_IBuffer *buf)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+	size_t aligned_skip, limit_size;
+
+	if (buf->ibuffer.start_offset + buf->ibuffer.v_limit <=
+	    (uoff_t)mbuf->mmap_offset + buf->pos) {
+		/* end of file */
+		return -1;
+	}
+
+	if (buf->pos < buf->buffer_size) {
+		/* more bytes available without needing to mmap() */
+		return io_buffer_set_mmaped_pos(buf);
+	}
+
+	aligned_skip = buf->skip & ~mmap_pagemask;
+	if (aligned_skip == 0 && mbuf->mmap_base != NULL) {
+		/* didn't skip enough bytes */
+		return -2;
+	}
+
+	buf->skip -= aligned_skip;
+	mbuf->mmap_offset += aligned_skip;
+
+	if (mbuf->mmap_base != NULL) {
+		if (munmap(mbuf->mmap_base, buf->buffer_size) < 0)
+			i_error("io_buffer_read_mmaped(): munmap() failed: %m");
+	}
+
+	buf->buffer_size = buf->ibuffer.start_offset + buf->ibuffer.v_size -
+		mbuf->mmap_offset;
+	if (buf->buffer_size > mbuf->mmap_block_size)
+		buf->buffer_size = mbuf->mmap_block_size;
+
+	i_assert((uoff_t)mbuf->mmap_offset + buf->buffer_size <=
+		 buf->ibuffer.start_offset + buf->ibuffer.v_size);
+
+	mbuf->mmap_base = mmap(NULL, buf->buffer_size, PROT_READ, MAP_PRIVATE,
+			       mbuf->fd, mbuf->mmap_offset);
+	buf->buffer = mbuf->mmap_base;
+	if (mbuf->mmap_base == MAP_FAILED) {
+		buf->ibuffer.buf_errno = errno;
+		mbuf->mmap_base = NULL;
+		buf->buffer = NULL;
+		buf->buffer_size = 0;
+		buf->skip = buf->pos = 0;
+		i_error("MmapIBuffer.mmap() failed: %m");
+		return -1;
+	}
+
+	/* madvise() only if non-limited mmap()ed buffer area larger than
+	   page size */
+	limit_size = buf->ibuffer.start_offset + buf->ibuffer.v_limit -
+		mbuf->mmap_offset;
+	if (limit_size > mmap_pagesize) {
+		if (limit_size > buf->buffer_size)
+			limit_size = buf->buffer_size;
+
+		(void)madvise(mbuf->mmap_base, limit_size, MADV_SEQUENTIAL);
+	}
+
+	return io_buffer_set_mmaped_pos(buf);
+}
+
+static int _seek(_IBuffer *buf, uoff_t v_offset)
+{
+	MmapIBuffer *mbuf = (MmapIBuffer *) buf;
+	uoff_t abs_offset;
+
+	abs_offset = buf->ibuffer.start_offset + v_offset;
+	if (mbuf->mmap_offset >= abs_offset &&
+	    mbuf->mmap_offset + buf->pos < abs_offset) {
+		/* already mmaped */
+		buf->skip = mbuf->mmap_offset - abs_offset;
+		buf->pos = buf->buffer_size;
+	} else {
+		/* force reading next time */
+		i_buffer_munmap(mbuf);
+		buf->skip = buf->pos = 0;
+	}
+
+	return 1;
+}
+
+static int _skip(_IBuffer *buf, uoff_t count)
+{
+	return _seek(buf, buf->ibuffer.v_offset + count);
+}
+
+IBuffer *i_buffer_create_mmap(int fd, Pool pool, size_t block_size,
+			      uoff_t start_offset, uoff_t v_size,
+			      int autoclose_fd)
+{
+	MmapIBuffer *mbuf;
+	struct stat st;
+
+	if (mmap_pagesize == 0) {
+		mmap_pagesize = getpagesize();
+		mmap_pagemask = mmap_pagesize-1;
+	}
+
+	if (v_size == 0) {
+		if (fstat(fd, &st) < 0) {
+			i_error("i_buffer_create_mmap(): fstat() failed: %m");
+			v_size = 0;
+		} else {
+			v_size = st.st_size;
+		}
+	}
+
+	mbuf = p_new(pool, MmapIBuffer, 1);
+	mbuf->fd = fd;
+	mbuf->mmap_block_size = block_size;
+	mbuf->autoclose_fd = autoclose_fd;
+
+	mbuf->ibuf.iobuf.close = _close;
+	mbuf->ibuf.iobuf.destroy = _destroy;
+	mbuf->ibuf.iobuf.set_max_size = _set_max_size;
+	mbuf->ibuf.iobuf.set_blocking = _set_blocking;
+
+	mbuf->ibuf.read = _read;
+	mbuf->ibuf.skip_count = _skip;
+	mbuf->ibuf.seek = _seek;
+
+	return _i_buffer_create(&mbuf->ibuf, pool, fd, start_offset, v_size);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,250 @@
+/*
+   ibuffer.c : Input buffer handling
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "ibuffer-internal.h"
+
+void i_buffer_ref(IBuffer *buf)
+{
+	_io_buffer_ref(buf->real_buffer);
+}
+
+void i_buffer_unref(IBuffer *buf)
+{
+	_io_buffer_unref(buf->real_buffer);
+}
+
+int i_buffer_get_fd(IBuffer *buf)
+{
+	_IBuffer *_buf = buf->real_buffer;
+
+	return _buf->fd;
+}
+
+void i_buffer_close(IBuffer *buf)
+{
+	_io_buffer_close(buf->real_buffer);
+	buf->closed = TRUE;
+}
+
+void i_buffer_set_max_size(IBuffer *buf, size_t max_size)
+{
+	_io_buffer_set_max_size(buf->real_buffer, max_size);
+}
+
+void i_buffer_set_blocking(IBuffer *buf, int timeout_msecs,
+			   TimeoutFunc timeout_func, void *context)
+{
+	_io_buffer_set_blocking(buf->real_buffer, timeout_msecs,
+				timeout_func, context);
+}
+
+void i_buffer_set_start_offset(IBuffer *buf, uoff_t offset)
+{
+	_IBuffer *_buf = buf->real_buffer;
+	off_t diff;
+
+	i_assert(buf->v_size == 0 || offset <= buf->start_offset + buf->v_size);
+
+	if (offset == buf->start_offset)
+		return;
+
+	diff = (off_t)buf->start_offset - (off_t)offset;
+	buf->start_offset = offset;
+	buf->v_offset += diff;
+	if (buf->v_size != 0) {
+		buf->v_size += diff;
+		buf->v_limit += diff;
+	}
+
+	/* reset buffer data */
+	_buf->skip = _buf->pos = _buf->cr_lookup_pos = 0;
+	_buf->last_cr = FALSE;
+}
+
+void i_buffer_set_read_limit(IBuffer *buf, uoff_t v_offset)
+{
+	_IBuffer *_buf = buf->real_buffer;
+
+	i_assert(v_offset <= buf->v_size);
+
+	if (v_offset == 0)
+		buf->v_limit = buf->v_size;
+	else {
+		i_assert(v_offset >= buf->v_offset);
+
+		buf->v_limit = v_offset;
+		if (_buf->pos > v_offset - buf->v_offset + _buf->skip)
+			_buf->pos = v_offset - buf->v_offset + _buf->skip;
+	}
+}
+
+ssize_t i_buffer_read(IBuffer *buf)
+{
+	_IBuffer *_buf = buf->real_buffer;
+
+	if (buf->closed)
+		return -1;
+
+	return _buf->read(_buf);
+}
+
+int i_buffer_skip(IBuffer *buf, uoff_t count)
+{
+	_IBuffer *_buf = buf->real_buffer;
+	size_t data_size;
+
+	i_assert(buf->v_size == 0 || buf->v_offset + count <= buf->v_size);
+
+	if (count <= _buf->pos - _buf->skip) {
+		buf->v_offset += count;
+		_buf->skip += count;
+		return 1;
+	}
+
+	if (buf->closed)
+		return -1;
+
+	data_size = _buf->pos - _buf->skip;
+	_buf->skip = _buf->pos = 0;
+
+	count -= data_size;
+	buf->v_offset += data_size;
+
+	return _buf->skip_count(_buf, count);
+}
+
+int i_buffer_seek(IBuffer *buf, uoff_t v_offset)
+{
+	_IBuffer *_buf = buf->real_buffer;
+
+	i_assert(v_offset <= buf->v_size);
+
+	if (buf->closed)
+		return -1;
+
+	return _buf->seek(_buf, v_offset);
+}
+
+/* skip the first LF, if it exists */
+static void i_buffer_skip_lf(_IBuffer *_buf)
+{
+	if (!_buf->last_cr || _buf->skip >= _buf->pos)
+		return;
+
+	if (_buf->buffer[_buf->skip] == 10) {
+		if (_buf->skip == _buf->cr_lookup_pos)
+			_buf->cr_lookup_pos++;
+		_buf->skip++;
+		_buf->ibuffer.v_offset++;
+	}
+	_buf->last_cr = FALSE;
+}
+
+char *i_buffer_next_line(IBuffer *buf)
+{
+	_IBuffer *_buf = buf->real_buffer;
+	char *ret_buf;
+        size_t i;
+
+        i_assert(buf != NULL);
+
+	i_buffer_skip_lf(_buf);
+	if (_buf->skip >= _buf->pos)
+		return NULL;
+
+	if (_buf->w_buffer == NULL) {
+		i_error("i_buffer_next_line() called for unmodifyable buffer");
+		return NULL;
+	}
+
+	ret_buf = NULL;
+	for (i = _buf->cr_lookup_pos; i < _buf->pos; i++) {
+		if (_buf->buffer[i] == 13 || _buf->buffer[i] == 10) {
+			/* got it */
+			_buf->last_cr = _buf->buffer[i] == 13;
+
+			ret_buf = (char *) _buf->buffer + _buf->skip;
+			_buf->w_buffer[i] = '\0';
+
+			i++;
+			buf->v_offset += i - _buf->skip;
+			_buf->skip = i;
+                        break;
+		}
+	}
+
+	_buf->cr_lookup_pos = i;
+        return ret_buf;
+}
+
+const unsigned char *i_buffer_get_data(IBuffer *buf, size_t *size)
+{
+	_IBuffer *_buf = buf->real_buffer;
+
+	i_buffer_skip_lf(_buf);
+
+	if (_buf->skip >= _buf->pos) {
+		*size = 0;
+		return NULL;
+	}
+
+        *size = _buf->pos - _buf->skip;
+        return _buf->buffer + _buf->skip;
+}
+
+int i_buffer_read_data(IBuffer *buf, const unsigned char **data,
+		       size_t *size, size_t threshold)
+{
+	_IBuffer *_buf = buf->real_buffer;
+	ssize_t ret;
+
+	while (_buf->pos - _buf->skip <= threshold) {
+		/* we need more data */
+		ret = _buf->read(_buf);
+		if (ret < 0) {
+			if (ret == -2)
+				return -2;
+			else
+				break;
+		}
+	}
+
+	*data = i_buffer_get_data(buf, size);
+	return *size > threshold ? 1 : *size > 0 ? 0 : -1;
+}
+
+IBuffer *_i_buffer_create(_IBuffer *_buf, Pool pool, int fd,
+			  uoff_t start_offset, uoff_t v_size)
+{
+	_buf->fd = fd;
+	_buf->ibuffer.start_offset = start_offset;
+	_buf->ibuffer.v_size = v_size;
+	_buf->ibuffer.v_limit = v_size;
+	_buf->ibuffer.real_buffer = _buf;
+
+	_io_buffer_init(pool, &_buf->iobuf);
+	return &_buf->ibuffer;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/ibuffer.h	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,73 @@
+#ifndef __IBUFFER_H
+#define __IBUFFER_H
+
+#include "ioloop.h" /* TimeoutFunc */
+
+struct _IBuffer {
+	uoff_t start_offset;
+	uoff_t v_offset, v_size, v_limit; /* relative to start_offset */
+
+	int buf_errno;
+	unsigned int closed:1;
+
+	void *real_buffer;
+};
+
+IBuffer *i_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
+			      int autoclose_fd);
+IBuffer *i_buffer_create_mmap(int fd, Pool pool, size_t block_size,
+			      uoff_t start_offset, uoff_t v_size,
+			      int autoclose_fd);
+IBuffer *i_buffer_create_from_data(Pool pool, const unsigned char *data,
+				   size_t size);
+
+/* Reference counting. References start from 1, so calling i_buffer_unref()
+   destroys the buffer if i_buffer_ref() is never used. */
+void i_buffer_ref(IBuffer *buf);
+void i_buffer_unref(IBuffer *buf);
+
+/* Return file descriptor for buffer, or -1 if none is available. */
+int i_buffer_get_fd(IBuffer *buf);
+
+/* Mark the buffer closed. Any reads after this will return -1. The data
+   already in buffer can still be used. */
+void i_buffer_close(IBuffer *buf);
+
+/* Change the maximum size for buffer to grow. */
+void i_buffer_set_max_size(IBuffer *buf, size_t max_size);
+/* Change the start_offset and drop all data in buffers. Doesn't do anything
+   if offset is the same as existing start_offset. */
+void i_buffer_set_start_offset(IBuffer *buf, uoff_t offset);
+/* Input buffer won't be read past specified offset. Giving 0 as offset
+   removes the limit. The offset is  */
+void i_buffer_set_read_limit(IBuffer *buf, uoff_t v_offset);
+/* Makes reads blocking until at least one byte is read. timeout_func is
+   called if nothing is read in specified time. The blocking state in file
+   descriptor isn't changed, but for timeout to work it must be in
+   non-blocking state. Setting timeout_msecs to 0 makes it non-blocking. */
+void i_buffer_set_blocking(IBuffer *buf, int timeout_msecs,
+			   TimeoutFunc timeout_func, void *context);
+
+/* Returns number of bytes read if read was ok, -1 if EOF or error, -2 if the
+   buffer is full. */
+ssize_t i_buffer_read(IBuffer *buf);
+/* Skip forward a number of bytes. Returns 1 if all bytes were actually
+   skipped, or -1 if EOF or error. */
+int i_buffer_skip(IBuffer *buf, uoff_t count);
+/* Seek to specified position from beginning of file. This works only for
+   files. Returns 1 if successful, -1 if error. */
+int i_buffer_seek(IBuffer *buf, uoff_t v_offset);
+/* Returns the next line from input buffer, or NULL if more data is needed
+   to make a full line. NOTE: modifies the data in the buffer for the \0, so
+   it works only with ibuffers that allow it (currently only file). */
+char *i_buffer_next_line(IBuffer *buf);
+/* Returns pointer to beginning of data in buffer, or NULL if there's
+   no data. */
+const unsigned char *i_buffer_get_data(IBuffer *buf, size_t *size);
+/* Like i_buffer_get_data(), but read it when needed. Returns 1 if more
+   than threshold bytes were stored into buffer, 0 if less, -1 if error or
+   EOF with no bytes in buffer or -2 if buffer is full. */
+int i_buffer_read_data(IBuffer *buf, const unsigned char **data,
+		       size_t *size, size_t threshold);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/iobuffer-internal.h	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,29 @@
+#ifndef __IOBUFFER_INTERNAL_H
+#define __IOBUFFER_INTERNAL_H
+
+#include "ioloop.h" /* TimeoutFunc */
+
+/* This file is private to IBuffer and OBuffer implementation */
+
+typedef struct _IOBuffer _IOBuffer;
+
+struct _IOBuffer {
+	Pool pool;
+	int refcount;
+
+	void (*close)(_IOBuffer *buf);
+	void (*destroy)(_IOBuffer *buf);
+	void (*set_max_size)(_IOBuffer *buf, size_t max_size);
+	void (*set_blocking)(_IOBuffer *buf, int timeout_msecs,
+			     TimeoutFunc timeout_func, void *context);
+};
+
+void _io_buffer_init(Pool pool, _IOBuffer *buf);
+void _io_buffer_ref(_IOBuffer *buf);
+void _io_buffer_unref(_IOBuffer *buf);
+void _io_buffer_close(_IOBuffer *buf);
+void _io_buffer_set_max_size(_IOBuffer *buf, size_t max_size);
+void _io_buffer_set_blocking(_IOBuffer *buf, int timeout_msecs,
+			     TimeoutFunc timeout_func, void *context);
+
+#endif
--- a/src/lib/iobuffer.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/iobuffer.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,5 +1,5 @@
 /*
-   iobuffer.c : Input/output transmit buffer handling
+   iobuffer.c : Input/output buffer common handling
 
     Copyright (c) 2002 Timo Sirainen
 
@@ -24,1158 +24,47 @@
 */
 
 #include "lib.h"
-#include "ioloop.h"
-#include "iobuffer.h"
-#include "mmap-util.h"
-#include "sendfile-util.h"
-#include "network.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-
-#define IO_BUFFER_MIN_SIZE 4096
-
-#define MAX_SSIZE_T(size) ((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
-
-typedef struct {
-	IOLoop ioloop;
-	IOBuffer *outbuf;
-
-	const char *data;
-	uoff_t size;
-	IOBuffer *inbuf;
+#include "iobuffer-internal.h"
 
-	int timeout;
-	int last_block;
-} IOBufferBlockContext;
-
-static size_t mmap_pagesize = 0;
-static size_t mmap_pagemask = 0;
-
-IOBuffer *io_buffer_create(int fd, Pool pool, int priority,
-			   size_t max_buffer_size)
+void _io_buffer_init(Pool pool, _IOBuffer *buf)
 {
-	IOBuffer *buf;
-
-        i_assert(fd >= 0);
-        i_assert(pool != NULL);
-
-	pool_ref(pool);
-	buf = p_new(pool, IOBuffer, 1);
+	buf->pool = pool;
 	buf->refcount = 1;
-	buf->fd = fd;
-	buf->pool = pool;
-	buf->priority = priority;
-	buf->max_buffer_size = max_buffer_size;
-	return buf;
 }
 
-IOBuffer *io_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
-				int flags)
-{
-	IOBuffer *buf;
-
-	buf = io_buffer_create(fd, pool, IO_PRIORITY_DEFAULT, max_buffer_size);
-	buf->file = TRUE;
-	buf->close_file = (flags & IOBUFFER_FLAG_AUTOCLOSE) != 0;
-        return buf;
-}
-
-IOBuffer *io_buffer_create_mmap(int fd, Pool pool, size_t block_size,
-				uoff_t start_offset, uoff_t size, int flags)
-{
-	IOBuffer *buf;
-	struct stat st;
-	uoff_t stop_offset;
-
-	i_assert(start_offset < OFF_T_MAX);
-
-	/* block size must be page aligned, and at least two pages long */
-	if (mmap_pagesize == 0) {
-		mmap_pagesize = getpagesize();
-		mmap_pagemask = mmap_pagesize-1;
-	}
-
-	if (block_size < mmap_pagesize*2)
-		block_size = mmap_pagesize*2;
-	else if ((block_size & mmap_pagemask) != 0) {
-		block_size &= ~mmap_pagemask;
-		block_size += mmap_pagesize;
-	}
-
-	buf = io_buffer_create_file(fd, pool, block_size, flags);
-	buf->mmaped = TRUE;
-	buf->receive = TRUE;
-
-	if (fstat(fd, &st) == 0)
-		stop_offset = (uoff_t)st.st_size;
-	else {
-		i_error("io_buffer_create_mmap(): lseek() failed: %m");
-		stop_offset = 0;
-	}
-
-	if (start_offset > stop_offset)
-		start_offset = stop_offset;
-
-	if (size > stop_offset-start_offset) {
-		i_warning("Trying to create IOBuffer with size "
-			  "%"PRIuUOFF_T" but we have only %"PRIuUOFF_T
-			  " bytes available in file", size,
-			  stop_offset-start_offset);
-		size = stop_offset-start_offset;
-	}
-
-	if (size == 0)
-		size = stop_offset - start_offset;
-
-	buf->start_offset = start_offset;
-	buf->limit = buf->size = size;
-
-	buf->skip = buf->pos = buf->start_offset;
-	return buf;
-}
-
-void io_buffer_ref(IOBuffer *buf)
+void _io_buffer_ref(_IOBuffer *buf)
 {
 	buf->refcount++;
 }
 
-void io_buffer_unref(IOBuffer *buf)
+void _io_buffer_unref(_IOBuffer *buf)
 {
-	if (buf == NULL)
-		return;
+	Pool pool;
 
 	i_assert(buf->refcount > 0);
-	if (--buf->refcount > 0)
-		return;
-
-        if (buf->io != NULL)
-		io_remove(buf->io);
-	if (buf->buffer != NULL) {
-		if (!buf->mmaped)
-			p_free(buf->pool, buf->buffer);
-		else {
-			if (munmap(buf->buffer, buf->buffer_size) < 0) {
-				i_error("io_buffer_destroy(): "
-					"munmap() failed: %m");
-			}
-		}
-	}
-	io_buffer_close(buf);
-        p_free(buf->pool, buf);
-	pool_unref(buf->pool);
-}
-
-void io_buffer_close(IOBuffer *buf)
-{
-	if (buf == NULL)
-		return;
-
-	buf->closed = TRUE;
-	if (buf->close_file && buf->fd != -1) {
-		if (close(buf->fd) < 0)
-			i_error("io_buffer_close(): close() failed: %m");
-		buf->fd = -1;
-	}
-}
-
-void io_buffer_reset(IOBuffer *buf)
-{
-	buf->pos = buf->skip = buf->cr_lookup_pos = 0;
-	buf->last_cr = FALSE;
-
-	if (buf->mmaped && buf->buffer != NULL) {
-		if (munmap(buf->buffer, buf->buffer_size) < 0)
-			i_error("io_buffer_reset(): munmap() failed: %m");
-		buf->buffer = NULL;
-		buf->buffer_size = 0;
-	}
-
-	buf->mmap_offset = buf->offset = 0;
-}
-
-IOBuffer *io_buffer_set_pool(IOBuffer *buf, Pool pool)
-{
-	IOBuffer *newbuf;
-
-        i_assert(buf != NULL);
-        i_assert(pool != NULL);
-
-	newbuf = p_new(pool, IOBuffer, 1);
-	memcpy(newbuf, buf, sizeof(IOBuffer));
-
-	newbuf->pool = pool;
-
-	if (!newbuf->mmaped) {
-		newbuf->buffer = p_malloc(pool, buf->buffer_size);
-		memcpy(newbuf->buffer, buf->buffer + buf->skip,
-		       buf->buffer_size - buf->skip);
-
-		newbuf->cr_lookup_pos -= newbuf->skip;
-		newbuf->pos -= newbuf->skip;
-		newbuf->skip = 0;
-
-		p_free(buf->pool, buf->buffer);
-	}
-
-        p_free(buf->pool, buf);
-        return newbuf;
-}
-
-void io_buffer_set_max_size(IOBuffer *buf, size_t max_size)
-{
-	i_assert(!buf->mmaped);
-
-	buf->max_buffer_size = max_size;
-}
-
-void io_buffer_set_blocking(IOBuffer *buf, size_t max_size,
-			    int timeout_msecs, TimeoutFunc timeout_func,
-			    void *context)
-{
-	buf->timeout_msecs = timeout_msecs;
-	buf->timeout_func = timeout_func;
-	buf->timeout_context = context;
-	buf->blocking = max_size > 0;
-
-	if (max_size != 0)
-		buf->max_buffer_size = max_size;
-}
-
-static ssize_t my_write(int fd, const void *buf, size_t size)
-{
-	ssize_t ret;
-
-	ret = write(fd, buf, size);
-	if (ret < 0 && (errno == EINTR || errno == EAGAIN))
-		ret = 0;
-
-	return ret;
-}
-
-static ssize_t io_buffer_write(IOBuffer *buf, const void *data, size_t size)
-{
-	ssize_t ret;
-
-	if (size > SSIZE_T_MAX)
-		size = SSIZE_T_MAX;
-	if (size == 0)
-		return 0;
-
-	ret = buf->file ? my_write(buf->fd, data, size) :
-		net_transmit(buf->fd, data, size);
-	if (ret < 0) {
-		/* disconnected */
-		buf->buf_errno = errno;
-		io_buffer_close(buf);
-		return -1;
-	}
-
-	buf->offset += ret;
-	return ret;
-}
-
-static void buf_send_real(IOBuffer *buf)
-{
-	ssize_t ret;
-
-	ret = io_buffer_write(buf, buf->buffer + buf->skip,
-			      buf->pos - buf->skip);
-	if (ret < 0)
+	if (--buf->refcount != 0)
 		return;
 
-	buf->skip += ret;
-	if (buf->skip == buf->pos) {
-		/* everything sent */
-		buf->skip = buf->pos = 0;
-
-		/* call flush function */
-		if (buf->flush_func != NULL) {
-			buf->flush_func(buf->flush_context, buf);
-			buf->flush_func = NULL;
-
-			if (buf->corked) {
-				/* remove cork */
-				if (!buf->file)
-					net_set_cork(buf->fd, FALSE);
-				buf->corked = FALSE;
-			}
-		}
-	}
-}
-
-static int buf_send(IOBuffer *buf)
-{
-	buf_send_real(buf);
-
-	if (buf->closed || buf->pos == 0) {
-		io_remove(buf->io);
-                buf->io = NULL;
-		return FALSE;
-	}
-
-        return TRUE;
-}
-
-#define IOBUFFER_IS_FULL(buf) \
-	((buf)->pos == (buf)->buffer_size)
-
-/* write only as much as needed, put the rest into buffer.
-   write() only full buffers. */
-static void block_loop_send(IOBufferBlockContext *ctx)
-{
-	size_t size, buffer_space_left;
-	ssize_t ret;
-
-	size = MAX_SSIZE_T(ctx->size);
-
-	buffer_space_left = ctx->outbuf->buffer_size - ctx->outbuf->pos;
-	if (ctx->outbuf->pos != 0 || ctx->size < buffer_space_left) {
-		if (buffer_space_left > 0) {
-			/* we have space in the buffer, fill it before
-			   writing */
-			if (size > buffer_space_left)
-				size = buffer_space_left;
-
-			memcpy(ctx->outbuf->buffer + ctx->outbuf->pos,
-			       ctx->data, size);
-			ctx->outbuf->pos += size;
-
-			ctx->data += size;
-			ctx->size -= size;
-		}
-
-		if (IOBUFFER_IS_FULL(ctx->outbuf))
-			buf_send_real(ctx->outbuf);
-	} else {
-		ret = io_buffer_write(ctx->outbuf, ctx->data, size);
-		if (ret > 0) {
-			ctx->data += ret;
-			ctx->size -= ret;
-		}
-	}
-
-	if (ctx->outbuf->closed || (ctx->size == 0 && ctx->last_block &&
-				    !IOBUFFER_IS_FULL(ctx->outbuf)))
-		io_loop_stop(ctx->ioloop);
-}
-
-/* write out all data from buffer */
-static void block_loop_flush(IOBufferBlockContext *ctx)
-{
-	if (ctx->outbuf->skip != ctx->outbuf->pos)
-		buf_send_real(ctx->outbuf);
-
-	if (ctx->outbuf->closed || ctx->outbuf->skip == ctx->outbuf->pos)
-		io_loop_stop(ctx->ioloop);
-}
-
-/* this can be called with both io_buffer_ioloop() or
-   io_buffer_read_blocking() */
-static void block_loop_timeout(void *context, Timeout timeout __attr_unused__)
-{
-	IOBufferBlockContext *ctx = context;
-
-	ctx->timeout = TRUE;
-	io_loop_stop(ctx->ioloop);
-}
-
-static int io_buffer_ioloop(IOBuffer *buf, IOBufferBlockContext *ctx,
-			    void (*send_func)(IOBufferBlockContext *ctx))
-{
-	Timeout to;
-	int save_errno;
-
-	/* close old IO */
-	if (buf->io != NULL)
-		io_remove(buf->io);
-
-	t_push();
-
-	/* create a new I/O loop */
-	ctx->ioloop = io_loop_create(data_stack_pool);
-	ctx->outbuf = buf;
-
-	buf->io = io_add(buf->fd, IO_WRITE, (IOFunc) send_func, ctx);
-	to = buf->timeout_msecs <= 0 ? NULL :
-		timeout_add(buf->timeout_msecs, block_loop_timeout, ctx);
-
-	io_loop_run(ctx->ioloop);
-	save_errno = errno;
-
-	if (buf->io != NULL) {
-		io_remove(buf->io);
-		buf->io = NULL;
-	}
-
-	if (to != NULL) {
-		if (ctx->timeout && buf->timeout_func != NULL) {
-			/* call user-given timeout function */
-			buf->timeout_func(buf->timeout_context, to);
-		}
-		timeout_remove(to);
-	}
-
-	io_loop_destroy(ctx->ioloop);
-
-	t_pop();
-
-	errno = save_errno;
-	return ctx->size > 0 ? -1 : 1;
-}
-
-static int io_buffer_send_blocking(IOBuffer *buf, const void *data,
-				   size_t size)
-{
-        IOBufferBlockContext ctx;
-
-	memset(&ctx, 0, sizeof(ctx));
-
-	ctx.data = data;
-	ctx.size = size;
-	ctx.last_block = TRUE;
-
-        return io_buffer_ioloop(buf, &ctx, block_loop_send);
-}
-
-static int io_buffer_flush(IOBuffer *buf)
-{
-        IOBufferBlockContext ctx;
-	ssize_t ret;
-
-	if (buf->skip == buf->pos)
-		return 1;
-
-	ret = io_buffer_write(buf, buf->buffer + buf->skip,
-			      buf->pos - buf->skip);
-	if (ret < 0)
-		return -1;
-
-	buf->skip += ret;
-	if (buf->skip == buf->pos)
-		return 1;
-
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.last_block = TRUE;
+	buf->close(buf);
+	buf->destroy(buf);
 
-        return io_buffer_ioloop(buf, &ctx, block_loop_flush);
-}
-
-void io_buffer_cork(IOBuffer *buf)
-{
-	i_assert(!buf->receive);
-
-	if (!buf->corked) {
-		if (!buf->file)
-			net_set_cork(buf->fd, TRUE);
-		buf->corked = TRUE;
-	}
-}
-
-static void buffer_alloc_more(IOBuffer *buf, size_t size)
-{
-	i_assert(!buf->mmaped);
-
-	buf->buffer_size = buf->pos+size;
-	buf->buffer_size =
-		buf->buffer_size <= IO_BUFFER_MIN_SIZE ? IO_BUFFER_MIN_SIZE :
-		nearest_power(buf->buffer_size);
-
-	if (buf->max_buffer_size > 0 && buf->buffer_size > buf->max_buffer_size)
-		buf->buffer_size = buf->max_buffer_size;
-
-	buf->buffer = p_realloc(buf->pool, buf->buffer, buf->buffer_size);
-	if (buf->buffer == NULL) {
-		/* pool limit exceeded */
-		buf->pos = buf->buffer_size = 0;
-	}
-}
-
-static void io_buffer_compress(IOBuffer *buf)
-{
-	memmove(buf->buffer, buf->buffer + buf->skip,
-		buf->pos - buf->skip);
-	buf->pos -= buf->skip;
-
-	if (buf->skip > buf->cr_lookup_pos)
-		buf->cr_lookup_pos = 0;
-	else
-		buf->cr_lookup_pos -= buf->skip;
-
-	buf->skip = 0;
-}
-
-int io_buffer_send(IOBuffer *buf, const void *data, size_t size)
-{
-	ssize_t ret;
-	int i, corked;
-
-	i_assert(!buf->receive);
-        i_assert(data != NULL);
-	i_assert(size <= SSIZE_T_MAX);
-	buf->transmit = TRUE;
-
-	if (buf->closed)
-                return -1;
-
-	/* if we're corked, first try adding it to buffer. if it's larger
-	   than the buffer, send it immediately. */
-	corked = buf->corked;
-	for (i = 0; i < 2; i++) {
-		if (buf->pos == 0 && !corked) {
-			/* buffer is empty, try to send the data immediately */
-			ret = io_buffer_write(buf, data, size);
-			if (ret < 0)
-				return -1;
-
-			data = (const char *) data + ret;
-			size -= ret;
-		}
-
-		if (size == 0) {
-			/* all sent */
-			return 1;
-		}
-
-		if (io_buffer_get_space(buf, size) != NULL)
-			break;
-
-		if (corked)
-			corked = FALSE;
-		else {
-			if (buf->blocking) {
-				/* if we don't have space, we block */
-				return io_buffer_send_blocking(buf, data, size);
-			}
-			return -2;
-		}
-	}
-
-	i_assert(buf->pos + size <= buf->buffer_size);
-
-	/* add to buffer */
-	memcpy(buf->buffer + buf->pos, data, size);
-	buf->pos += size;
-
-	if (buf->io == NULL && !buf->corked) {
-		buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
-					  (IOFunc) buf_send, buf);
-	}
-        return 1;
-}
-
-static void block_loop_sendfile(IOBufferBlockContext *ctx)
-{
-	uoff_t offset;
-	ssize_t ret;
-
-	i_assert(ctx->inbuf->offset < OFF_T_MAX);
-
-	offset = ctx->inbuf->offset;
-	ret = safe_sendfile(ctx->outbuf->fd, ctx->inbuf->fd, &offset,
-			    MAX_SSIZE_T(ctx->size));
-	if (ret < 0) {
-		if (errno != EINTR && errno != EAGAIN) {
-			ctx->outbuf->buf_errno = errno;
-                        io_buffer_close(ctx->outbuf);
-		}
-		ret = 0;
-	}
-
-	io_buffer_skip(ctx->inbuf, (size_t)ret);
-	ctx->outbuf->offset += ret;
-
-	ctx->size -= ret;
-	if (ctx->outbuf->closed || ctx->size == 0)
-		io_loop_stop(ctx->ioloop);
-}
-
-static int io_buffer_sendfile(IOBuffer *outbuf, IOBuffer *inbuf,
-			      uoff_t long_size)
-{
-        IOBufferBlockContext ctx;
-	uoff_t offset;
-	ssize_t ret;
-
-	i_assert(inbuf->offset < OFF_T_MAX);
-
-	/* flush out any data in buffer */
-	if (io_buffer_flush(outbuf) < 0)
-		return -1;
-
-	/* first try if we can do it with a single sendfile() call */
-	offset = inbuf->offset;
-	ret = safe_sendfile(outbuf->fd, inbuf->fd, &offset,
-			    MAX_SSIZE_T(long_size));
-	if (ret < 0) {
-		if (errno != EINTR && errno != EAGAIN) {
-			outbuf->buf_errno = errno;
-			return -1;
-		}
-		ret = 0;
-	}
-
-	io_buffer_skip(inbuf, (size_t)ret);
-	outbuf->offset += ret;
-
-	if ((uoff_t) ret == long_size) {
-		/* yes, all sent */
-		return 1;
-	}
-
-	memset(&ctx, 0, sizeof(ctx));
-
-	ctx.inbuf = inbuf;
-	ctx.size = long_size - ret;
-
-	ret = io_buffer_ioloop(outbuf, &ctx, block_loop_sendfile);
-	if (ret < 0 && outbuf->buf_errno == EINVAL) {
-		/* this shouldn't happen, must be a bug. It would also
-		   mess up later if we let this pass. */
-		i_panic("io_buffer_sendfile() failed: %m");
-	}
-	return ret;
+	pool = buf->pool;
+        p_free(pool, buf);
+	pool_unref(pool);
 }
 
-static void block_loop_copy(IOBufferBlockContext *ctx)
-{
-	unsigned char *in_data;
-	size_t size, full_size, sent_size, data_size;
-
-	if (io_buffer_read_data_blocking(ctx->inbuf, &in_data, &size, 0) < 0) {
-		io_loop_stop(ctx->ioloop);
-		return;
-	}
-
-	full_size = ctx->size;
-	data_size = size < full_size ? size : full_size;
-
-	/* send the data */
-	ctx->size = data_size;
-	ctx->data = (const char *) in_data;
-	ctx->last_block = data_size == full_size;
-	block_loop_send(ctx);
-
-	/* ctx->size now contains number of bytes unsent */
-	sent_size = data_size - ctx->size;
-	ctx->size = full_size - sent_size;
-
-	io_buffer_skip(ctx->inbuf, sent_size);
-}
-
-int io_buffer_send_iobuffer(IOBuffer *outbuf, IOBuffer *inbuf, uoff_t size)
-{
-	IOBufferBlockContext ctx;
-	int ret;
-
-	i_assert(size < OFF_T_MAX);
-	i_assert(inbuf->limit > 0 || size <= inbuf->limit - inbuf->offset);
-
-	if (inbuf->closed || outbuf->closed)
-		return -1;
-
-	ret = io_buffer_sendfile(outbuf, inbuf, size);
-	if (ret > 0 || outbuf->buf_errno != EINVAL)
-		return ret < 0 ? -1 : 1;
-
-	/* sendfile() not supported (with this fd), fallback to
-	   regular sending */
-
-	/* create blocking send loop */
-	memset(&ctx, 0, sizeof(ctx));
-
-	ctx.inbuf = inbuf;
-	ctx.size = size;
-
-	return io_buffer_ioloop(outbuf, &ctx, block_loop_copy);
-}
-
-void io_buffer_send_flush(IOBuffer *buf)
-{
-	i_assert(!buf->receive);
-
-	if (buf->closed)
-                return;
-
-	io_buffer_flush(buf);
-
-	if (buf->corked) {
-		/* remove cork */
-		if (!buf->file)
-			net_set_cork(buf->fd, FALSE);
-		buf->corked = FALSE;
-	}
-}
-
-void io_buffer_send_flush_callback(IOBuffer *buf, IOBufferFlushFunc func,
-				   void *context)
-{
-	i_assert(!buf->receive);
-
-	if (buf->skip == buf->pos) {
-		func(context, buf);
-		return;
-	}
-
-	buf->flush_func = func;
-	buf->flush_context = context;
-
-	/* if we're corked, the io wasn't set */
-	if (buf->io == NULL) {
-		buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
-					  (IOFunc) buf_send, buf);
-	}
-}
-
-static ssize_t io_buffer_set_mmaped_pos(IOBuffer *buf)
-{
-	i_assert((uoff_t)buf->mmap_offset <= buf->start_offset + buf->limit);
-
-	buf->pos = buf->start_offset + buf->limit - buf->mmap_offset;
-	if (buf->pos > buf->buffer_size)
-		buf->pos = buf->buffer_size;
-
-	return buf->pos - buf->skip;
-}
-
-static ssize_t io_buffer_read_mmaped(IOBuffer *buf)
+void _io_buffer_close(_IOBuffer *buf)
 {
-	size_t aligned_skip;
-
-	if (buf->start_offset + buf->limit <=
-	    (uoff_t)buf->mmap_offset + buf->pos) {
-		/* end of file */
-		return -1;
-	}
-
-	if (buf->pos < buf->buffer_size) {
-		/* more bytes available without needing to mmap() */
-		return io_buffer_set_mmaped_pos(buf);
-	}
-
-	aligned_skip = buf->skip & ~mmap_pagemask;
-	if (aligned_skip == 0 && buf->buffer != NULL) {
-		/* didn't skip enough bytes */
-		return -2;
-	}
-
-	buf->skip -= aligned_skip;
-	buf->mmap_offset += aligned_skip;
-
-	if (buf->buffer != NULL) {
-		if (munmap(buf->buffer, buf->buffer_size) < 0)
-			i_error("io_buffer_read_mmaped(): munmap() failed: %m");
-	}
-
-	buf->buffer_size = buf->start_offset + buf->size - buf->mmap_offset;
-	if (buf->buffer_size > buf->max_buffer_size)
-		buf->buffer_size = buf->max_buffer_size;
-
-	i_assert((uoff_t)buf->mmap_offset + buf->buffer_size <=
-		 buf->start_offset + buf->size);
-
-	buf->buffer = mmap(NULL, buf->buffer_size, PROT_READ, MAP_PRIVATE,
-			   buf->fd, buf->mmap_offset);
-	if (buf->buffer == MAP_FAILED) {
-		buf->buf_errno = errno;
-		buf->buffer = NULL;
-		buf->buffer_size = 0;
-		buf->skip = buf->pos;
-		i_error("io_buffer_read_mmaped(): mmap() failed: %m");
-		return -1;
-	}
-
-	/* madvise() only if the mmap()ed area was larger than page size */
-	if (buf->buffer_size > mmap_pagesize) {
-		(void)madvise((void *) buf->buffer, buf->buffer_size,
-			      MADV_SEQUENTIAL);
-	}
-
-	return io_buffer_set_mmaped_pos(buf);
-}
-
-void io_buffer_set_start_offset(IOBuffer *buf, uoff_t offset)
-{
-	off_t diff;
-
-	i_assert(offset <= buf->size);
-
-	if (offset == buf->start_offset)
-		return;
-
-	diff = (off_t)buf->start_offset - (off_t)offset;
-	buf->start_offset = offset;
-	buf->size += diff;
-	buf->limit += diff;
-
-	io_buffer_reset(buf);
-
-	buf->skip = buf->pos = buf->start_offset;
-}
-
-void io_buffer_set_read_limit(IOBuffer *buf, uoff_t offset)
-{
-	i_assert(offset <= buf->size);
-
-	if (offset == 0)
-		buf->limit = buf->size;
-	else {
-		i_assert(offset >= buf->offset);
-
-		buf->limit = offset;
-		if (buf->offset + (buf->pos - buf->skip) > offset)
-			buf->pos = offset - buf->offset + buf->skip;
-	}
-}
-
-ssize_t io_buffer_read(IOBuffer *buf)
-{
-	size_t size;
-	ssize_t ret;
-
-	i_assert(!buf->transmit);
-	buf->receive = TRUE;
-
-	if (buf->closed)
-		return -1;
-
-	if (buf->mmaped)
-		return io_buffer_read_mmaped(buf);
-
-	if (buf->pos == buf->buffer_size) {
-		if (buf->skip > 0) {
-			/* remove the unused bytes from beginning of buffer */
-                        io_buffer_compress(buf);
-		} else if (buf->max_buffer_size == 0 ||
-			   buf->buffer_size < buf->max_buffer_size) {
-			/* buffer is full - grow it */
-			buffer_alloc_more(buf, IO_BUFFER_MIN_SIZE);
-		}
-
-		if (buf->pos == buf->buffer_size)
-                        return -2; /* buffer full */
-	}
-
-	size = buf->buffer_size - buf->pos;
-	if (buf->limit > 0) {
-		i_assert(buf->limit >= buf->offset);
-		if (size >= buf->limit - buf->offset) {
-			size = buf->limit - buf->offset;
-			if (size == 0)
-				return -1;
-		}
-	}
-
-        /* fill the buffer */
-	if (!buf->file) {
-		ret = net_receive(buf->fd, buf->buffer + buf->pos, size);
-	} else {
-		ret = read(buf->fd, buf->buffer + buf->pos, size);
-		if (ret == 0)
-			ret = -1; /* EOF */
-		else if (ret < 0 && (errno == EINTR || errno == EAGAIN))
-                        ret = 0;
-	}
-
-	if (ret < 0) {
-		/* disconnected */
-		buf->buf_errno = errno;
-                return -1;
-	}
-
-	buf->pos += ret;
-        return ret;
-}
-
-static void io_read_data(void *context, int fd __attr_unused__,
-			 IO io __attr_unused__)
-{
-	IOBufferBlockContext *ctx = context;
-
-	if (io_buffer_read(ctx->inbuf) != 0) {
-		/* got data / error */
-		io_loop_stop(ctx->ioloop);
-	}
+	buf->close(buf);
 }
 
-ssize_t io_buffer_read_blocking(IOBuffer *buf)
-{
-        IOBufferBlockContext ctx;
-	Pool pool;
-	Timeout to;
-	ssize_t ret;
-
-	/* first check if we can get some data */
-	ret = io_buffer_read(buf);
-	if (ret != 0)
-		return ret;
-
-	/* blocking now */
-
-	/* create a new I/O loop */
-	memset(&ctx, 0, sizeof(ctx));
-	pool = pool_create("io_buffer_read_blocking", 1024, FALSE);
-	ctx.ioloop = io_loop_create(pool);
-	ctx.inbuf = buf;
-
-	buf->io = io_add(buf->fd, IO_READ, io_read_data, &ctx);
-	to = buf->timeout_msecs <= 0 ? NULL :
-		timeout_add(buf->timeout_msecs, block_loop_timeout, &ctx);
-
-	io_loop_run(ctx.ioloop);
-
-	if (buf->io != NULL) {
-		io_remove(buf->io);
-		buf->io = NULL;
-	}
-
-	if (to != NULL) {
-		if (ctx.timeout && buf->timeout_func != NULL) {
-			/* call user-given timeout function */
-			buf->timeout_func(buf->timeout_context, to);
-		}
-		timeout_remove(to);
-	}
-
-	io_loop_destroy(ctx.ioloop);
-	pool_unref(pool);
-
-	return buf->pos > buf->skip ?
-		(ssize_t) (buf->pos-buf->skip) : -1;
-}
-
-void io_buffer_skip(IOBuffer *buf, uoff_t count)
+void _io_buffer_set_max_size(_IOBuffer *buf, size_t max_size)
 {
-	uoff_t old_limit;
-	ssize_t ret;
-
-	buf->offset += count;
-
-	if (count <= buf->pos - buf->skip) {
-		buf->skip += count;
-		return;
-	}
-
-	if (buf->mmaped) {
-		/* these point outside mmap now, next io_buffer_read_mmaped()
-		   will fix them */
-		buf->skip += count;
-		buf->pos = buf->skip;
-	} else {
-		if (buf->buffer_size == 0)
-			buffer_alloc_more(buf, IO_BUFFER_MIN_SIZE);
-
-		count -= buf->skip;
-
-		old_limit = buf->limit;
-		io_buffer_set_read_limit(buf, buf->offset + count);
-
-		while ((ret = io_buffer_read_blocking(buf)) > 0)
-			io_buffer_skip(buf, ret);
-
-		io_buffer_set_read_limit(buf, old_limit);
-	}
-}
-
-int io_buffer_seek(IOBuffer *buf, uoff_t offset)
-{
-	uoff_t real_offset;
-	off_t ret;
-
-	if (buf->closed) {
-		errno = EBADF;
-		return FALSE;
-	}
-
-	real_offset = buf->start_offset + offset;
-	if (real_offset > OFF_T_MAX) {
-		errno = EINVAL;
-		return FALSE;
-	}
-
-	if (buf->mmaped) {
-		/* first reset everything */
-		io_buffer_reset(buf);
-
-		/* then set the wanted position, next read will
-		   pick up from there */
-		buf->pos = buf->skip = real_offset;
-	} else {
-		ret = lseek(buf->fd, (off_t)real_offset, SEEK_SET);
-		if (ret < 0)
-			return FALSE;
-
-		if (ret != (off_t)real_offset) {
-			errno = EINVAL;
-			return FALSE;
-		}
-	}
-
-	buf->offset = offset;
-	return TRUE;
-}
-
-/* skip the first LF, if it exists */
-static void io_buffer_skip_lf(IOBuffer *buf)
-{
-	if (!buf->last_cr || buf->skip >= buf->pos)
-		return;
-
-	if (buf->buffer[buf->skip] == 10) {
-		if (buf->skip == buf->cr_lookup_pos)
-			buf->cr_lookup_pos++;
-		buf->skip++;
-		buf->offset++;
-	}
-	buf->last_cr = FALSE;
+	buf->set_max_size(buf, max_size);
 }
 
-char *io_buffer_next_line(IOBuffer *buf)
-{
-	/* FIXME: buf->offset isn't updated right.. (skip_lf thing?) */
-	char *ret_buf;
-        size_t i;
-
-        i_assert(buf != NULL);
-
-	io_buffer_skip_lf(buf);
-	if (buf->skip >= buf->pos)
-		return NULL;
-
-	ret_buf = NULL;
-	for (i = buf->cr_lookup_pos; i < buf->pos; i++) {
-		if (buf->buffer[i] == 13 || buf->buffer[i] == 10) {
-			/* got it */
-                        buf->last_cr = buf->buffer[i] == 13;
-			buf->buffer[i] = '\0';
-			ret_buf = (char *) buf->buffer + buf->skip;
-
-			i++;
-			buf->offset += i - buf->skip;
-			buf->skip = i;
-                        break;
-		}
-	}
-
-	buf->cr_lookup_pos = i;
-        return ret_buf;
-}
-
-unsigned char *io_buffer_get_data(IOBuffer *buf, size_t *size)
-{
-	io_buffer_skip_lf(buf);
-
-	if (buf->skip >= buf->pos) {
-		*size = 0;
-		return NULL;
-	}
-
-        *size = buf->pos - buf->skip;
-        return buf->buffer + buf->skip;
-}
-
-int io_buffer_read_data_blocking(IOBuffer *buf, unsigned char **data,
-				 size_t *size, size_t threshold)
-{
-	ssize_t ret;
-
-	while (buf->pos - buf->skip <= threshold) {
-		/* we need more data */
-		ret = io_buffer_read_blocking(buf);
-		if (ret < 0) {
-			if (ret == -2)
-				return -2;
-			else
-				break;
-		}
-	}
-
-	*data = io_buffer_get_data(buf, size);
-	return *size > threshold ? 1 : *size > 0 ? 0 : -1;
-}
-
-unsigned char *io_buffer_get_space(IOBuffer *buf, size_t size)
+void _io_buffer_set_blocking(_IOBuffer *buf, int timeout_msecs,
+			     TimeoutFunc timeout_func, void *context)
 {
-	i_assert(size > 0);
-	i_assert(size <= SSIZE_T_MAX);
-	i_assert(!buf->receive);
-	buf->transmit = TRUE;
-
-	/* make sure we have enough space in buffer */
-	if (buf->buffer_size - buf->pos < size && buf->skip > 0) {
-		/* remove the unused bytes from beginning of buffer */
-		io_buffer_compress(buf);
-	}
-
-	if (buf->buffer_size - buf->pos < size &&
-	    (buf->max_buffer_size == 0 ||
-	     size <= buf->max_buffer_size - buf->pos)) {
-		/* allocate more space */
-                buffer_alloc_more(buf, size);
-	}
-
-	if (buf->buffer_size - buf->pos < size)
-                return NULL;
-
-        return buf->buffer + buf->pos;
+	buf->set_blocking(buf, timeout_msecs, timeout_func, context);
 }
-
-int io_buffer_send_buffer(IOBuffer *buf, size_t size)
-{
-	ssize_t ret;
-
-	i_assert(size <= SSIZE_T_MAX);
-	i_assert(!buf->receive);
-
-	if (buf->closed)
-		return -1;
-
-	if (buf->pos == 0 && !buf->corked) {
-		/* buffer is empty, try to send the data immediately */
-		ret = io_buffer_write(buf, buf->buffer, size);
-		if (ret < 0)
-			return -1;
-
-		if ((size_t)ret == size) {
-                        /* all sent */
-			return 1;
-		}
-
-		buf->skip += ret;
-	}
-
-	buf->pos += size;
-
-	if (buf->io == NULL && !buf->corked) {
-		buf->io = io_add_priority(buf->fd, buf->priority, IO_WRITE,
-					  (IOFunc) buf_send, buf);
-	}
-
-        return 1;
-}
-
-int io_buffer_set_data(IOBuffer *buf, const void *data, size_t size)
-{
-	i_assert(!buf->mmaped);
-
-	io_buffer_reset(buf);
-
-	if (buf->buffer_size < size) {
-		buffer_alloc_more(buf, size);
-		if (buf->buffer_size < size)
-                        return -2;
-	}
-
-        buf->offset += size;
-        buf->offset -= buf->pos - buf->skip;
-
-	memcpy(buf->buffer, data, size);
-	buf->pos = size;
-	buf->skip = 0;
-	return 1;
-}
-
-int io_buffer_is_empty(IOBuffer *buf)
-{
-        return buf->skip >= buf->pos;
-}
--- a/src/lib/iobuffer.h	Sun Oct 13 18:52:19 2002 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,166 +0,0 @@
-#ifndef __IOBUFFER_H
-#define __IOBUFFER_H
-
-#include "ioloop.h"
-
-typedef void (*IOBufferFlushFunc) (void *context, IOBuffer *buf);
-
-enum {
-        IOBUFFER_FLAG_AUTOCLOSE		= 0x01
-};
-
-struct _IOBuffer {
-	int fd;
-
-	uoff_t start_offset;
-	uoff_t offset, size, limit; /* virtual offsets, 0 = start_offset */
-	int buf_errno; /* set when write() or read() failed. */
-
-/* private: */
-	Pool pool;
-	IO io;
-	int refcount;
-	int priority;
-
-	int timeout_msecs;
-	TimeoutFunc timeout_func;
-	void *timeout_context;
-
-	IOBufferFlushFunc flush_func;
-	void *flush_context;
-
-	unsigned char *buffer;
-        size_t cr_lookup_pos; /* used only when reading a line */
-
-	off_t mmap_offset;
-	size_t pos, skip;
-	size_t buffer_size, max_buffer_size;
-
-	unsigned int file:1; /* reading/writing a file */
-	unsigned int close_file:1; /* io_buffer_close() actually close()s fd */
-	unsigned int mmaped:1; /* reading a file with mmap() */
-	unsigned int closed:1; /* all further read/writes will return 0 */
-	unsigned int transmit:1; /* this is a transmit buffer */
-	unsigned int receive:1; /* this is a receive buffer */
-	unsigned int last_cr:1; /* we're expecting a LF to be skipped in
-		next call to io_buffer_next_line() */
-	unsigned int blocking:1; /* writes block if buffer is full */
-	unsigned int corked:1; /* TCP_CORK set */
-};
-
-/* Create an I/O buffer. It can be used for either sending or receiving data,
-   NEVER BOTH AT SAME TIME. */
-IOBuffer *io_buffer_create(int fd, Pool pool, int priority,
-			   size_t max_buffer_size);
-/* Same as io_buffer_create(), but specify that we're reading/writing file. */
-IOBuffer *io_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
-				int flags);
-/* Read the file by mmap()ing it in blocks. stop_offset specifies where to
-   stop reading, or 0 to end of file. */
-IOBuffer *io_buffer_create_mmap(int fd, Pool pool, size_t block_size,
-				uoff_t start_offset, uoff_t size,
-				int flags);
-
-/* Reference counting. References start from 1, so calling io_buffer_unref()
-   destroys the buffer if io_buffer_ref() is never used. */
-void io_buffer_ref(IOBuffer *buf);
-void io_buffer_unref(IOBuffer *buf);
-
-/* Mark the buffer closed. Any sends/reads after this will return -1.
-   The data already in buffer can be used, and the remaining output buffer
-   will be sent. */
-void io_buffer_close(IOBuffer *buf);
-/* Reset all pointers so that the buffer looks empty, the actual data is
-   not touched and can be used. */
-void io_buffer_reset(IOBuffer *buf);
-
-/* Change the memory pool used by the buffer. Data already in
-   buffer will be transferred to new buffer. */
-IOBuffer *io_buffer_set_pool(IOBuffer *buf, Pool pool);
-/* Change the maximum size for buffer to grow. */
-void io_buffer_set_max_size(IOBuffer *buf, size_t max_size);
-/* Change buffer's blocking state. The blocking state in fd itself isn't
-   changed, and it's not needed to be blocking. This affects two things:
-
-   When buffer reaches max_size, it will block until all the data has been
-   sent or timeout has been reached. Setting max_size to 0 disables this
-   (default). Setting timeout_msecs to 0 may block infinitely, or until
-   socket is closed.
-
-   Sets the timeout for io_buffer_read_blocking(). If max_size is non-zero,
-   it acts the same as io_buffer_set_max_size().
-
-   timeout_func is called with both cases when timeout occurs.
-*/
-void io_buffer_set_blocking(IOBuffer *buf, size_t max_size,
-			    int timeout_msecs, TimeoutFunc timeout_func,
-			    void *context);
-
-/* Set TCP_CORK on if supported, ie. don't send out partial frames.
-   io_buffer_send_flush() removes the cork. */
-void io_buffer_cork(IOBuffer *buf);
-
-/* Returns 1 if all was ok, -1 if disconnected, -2 if buffer is full */
-int io_buffer_send(IOBuffer *buf, const void *data, size_t size);
-/* Send data from input buffer to output buffer using the fastest
-   possible method. Returns 1 if all sent or -1 if disconnected.
-   Note that this function may block. */
-int io_buffer_send_iobuffer(IOBuffer *outbuf, IOBuffer *inbuf,
-			    uoff_t long_size);
-/* Flush the output buffer, blocks until all is sent. If
-   io_buffer_set_send_blocking() is called, it's timeout settings are used. */
-void io_buffer_send_flush(IOBuffer *buf);
-/* Call specified function when the whole transmit buffer has been sent.
-   If the buffer is empty already, the function will be called immediately.
-   The function will be called only once. */
-void io_buffer_send_flush_callback(IOBuffer *buf, IOBufferFlushFunc func,
-				   void *context);
-
-/* Change the start_offset and call io_buffer_reset(). Doesn't do anything
-   if offset is the same as existing start_offset. */
-void io_buffer_set_start_offset(IOBuffer *buf, uoff_t offset);
-
-/* IO buffer won't be read past specified offset. Giving 0 as offset removes
-   the limit. */
-void io_buffer_set_read_limit(IOBuffer *buf, uoff_t offset);
-
-/* Returns number of bytes read if read was ok,
-   -1 if disconnected / EOF, -2 if the buffer is full */
-ssize_t io_buffer_read(IOBuffer *buf);
-/* Blocking read, doesn't return until at least one byte is read, or until
-   socket is disconnected or timeout has occured. Note that the fd must be
-   nonblocking, or the timeout doesn't work. Returns number of bytes read
-   (never 0), -1 if error or -2 if buffer is full. */
-ssize_t io_buffer_read_blocking(IOBuffer *buf);
-/* Skip forward a number of bytes */
-void io_buffer_skip(IOBuffer *buf, uoff_t count);
-/* Seek to specified position from beginning of file. This works only for
-   files. Returns TRUE if successful. */
-int io_buffer_seek(IOBuffer *buf, uoff_t offset);
-/* Returns the next line from input buffer, or NULL if more data is needed
-   to make a full line. NOTE: call to io_buffer_read() invalidates the
-   returned data. */
-char *io_buffer_next_line(IOBuffer *buf);
-/* Returns pointer to beginning of data in buffer,
-   or NULL if there's no data. */
-unsigned char *io_buffer_get_data(IOBuffer *buf, size_t *size);
-/* Like io_buffer_get_data(), but read it when needed. Returns 1 if more
-   than threshold bytes were stored into buffer, 0 if less, -1 if error or
-   EOF with no bytes in buffer or -2 if buffer is full. */
-int io_buffer_read_data_blocking(IOBuffer *buf, unsigned char **data,
-				 size_t *size, size_t threshold);
-
-/* Returns a pointer to buffer wanted amount of space,
-   or NULL if size is too big. */
-unsigned char *io_buffer_get_space(IOBuffer *buf, size_t size);
-/* Send data saved to buffer from io_buffer_get_space().
-   Returns -1 if disconnected. */
-int io_buffer_send_buffer(IOBuffer *buf, size_t size);
-
-/* Put data to buffer as if it was received.
-   Returns 1 if successful, -2 if buffer isn't big enough. */
-int io_buffer_set_data(IOBuffer *buf, const void *data, size_t size);
-/* Returns TRUE if there's nothing in buffer. */
-int io_buffer_is_empty(IOBuffer *buf);
-
-#endif
--- a/src/lib/lib.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/lib.h	Mon Oct 14 02:49:11 2002 +0300
@@ -18,7 +18,8 @@
 typedef struct _Timeout *Timeout;
 
 typedef struct _IPADDR IPADDR;
-typedef struct _IOBuffer IOBuffer;
+typedef struct _IBuffer IBuffer;
+typedef struct _OBuffer OBuffer;
 typedef struct _TempString TempString;
 
 #include "compat.h"
--- a/src/lib/network.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/network.c	Mon Oct 14 02:49:11 2002 +0300
@@ -225,11 +225,13 @@
 #endif
 }
 
-void net_set_cork(int fd __attr_unused__, int cork __attr_unused__)
+int net_set_cork(int fd __attr_unused__, int cork __attr_unused__)
 {
 #ifdef TCP_CORK
-	if (setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork)) < 0)
-		i_error("setsockopt(TCP_CORK) failed: %m");
+	return setsockopt(fd, SOL_TCP, TCP_CORK, &cork, sizeof(cork));
+#else
+	errno = ENOPROTOOPT;
+	return -1;
 #endif
 }
 
--- a/src/lib/network.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/lib/network.h	Mon Oct 14 02:49:11 2002 +0300
@@ -54,8 +54,9 @@
 
 /* Set socket blocking/nonblocking */
 void net_set_nonblock(int fd, int nonblock);
-/* Set TCP_CORK if supported, ie. don't send out partial frames. */
-void net_set_cork(int fd, int cork);
+/* Set TCP_CORK if supported, ie. don't send out partial frames.
+   Returns 0 if ok, -1 if failed. */
+int net_set_cork(int fd, int cork);
 
 /* Listen for connections on a socket */
 int net_listen(IPADDR *my_ip, unsigned int *port);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/obuffer-file.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,687 @@
+/*
+   obuffer-file.c : Output buffer handling for files
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "network.h"
+#include "sendfile-util.h"
+#include "ibuffer.h"
+#include "obuffer-internal.h"
+
+#include <unistd.h>
+#ifdef HAVE_SYS_UIO_H
+#  include <sys/uio.h>
+#endif
+
+#define O_BUFFER_MIN_SIZE 4096
+
+#define IS_BUFFER_EMPTY(fbuf) \
+	(!(fbuf)->full && (fbuf)->head == (fbuf)->tail)
+#define BUFFER_IS_BLOCKING(fbuf) \
+	((fbuf)->timeout_msecs != 0)
+
+#define MAX_SSIZE_T(size) \
+	((size) < SSIZE_T_MAX ? (size_t)(size) : SSIZE_T_MAX)
+
+typedef struct {
+	_OBuffer obuf;
+
+	int fd;
+	int priority;
+	IO io;
+
+	unsigned char *buffer; /* ring-buffer */
+	size_t buffer_size, max_buffer_size;
+	size_t head, tail; /* first unsent/unused byte */
+
+	int timeout_msecs;
+	TimeoutFunc timeout_func;
+	void *timeout_context;
+
+	unsigned int full:1; /* if head == tail, is buffer empty or full? */
+	unsigned int corked:1;
+	unsigned int no_socket_cork:1;
+	unsigned int autoclose_fd:1;
+} FileOBuffer;
+
+typedef struct {
+	IOLoop ioloop;
+	FileOBuffer *fbuf;
+	uoff_t sent;
+	int timeout;
+
+	IBuffer *inbuf;
+	struct iovec iov[3];
+	unsigned int iov_len;
+} IOLoopWriteContext;
+
+static void _close(_IOBuffer *buf)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+
+	if (fbuf->autoclose_fd && fbuf->fd != -1) {
+		if (close(fbuf->fd) < 0)
+			i_error("FileOBuffer.close() failed: %m");
+		fbuf->fd = -1;
+	}
+
+	if (fbuf->io != NULL) {
+		io_remove(fbuf->io);
+		fbuf->io = NULL;
+	}
+}
+
+static void _destroy(_IOBuffer *buf)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+
+	p_free(fbuf->obuf.iobuf.pool, fbuf->buffer);
+}
+
+static void _set_max_size(_IOBuffer *buf, size_t max_size)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+
+	fbuf->max_buffer_size = max_size;
+}
+
+static void _set_blocking(_IOBuffer *buf, int timeout_msecs,
+			  TimeoutFunc timeout_func, void *context)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+
+	fbuf->timeout_msecs = timeout_msecs;
+	fbuf->timeout_func = timeout_func;
+	fbuf->timeout_context = context;
+}
+
+static void _cork(_OBuffer *buf)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+
+	if (!fbuf->corked) {
+		if (!fbuf->no_socket_cork) {
+			if (net_set_cork(fbuf->fd, TRUE) < 0)
+				fbuf->no_socket_cork = TRUE;
+		}
+		fbuf->corked = TRUE;
+	}
+}
+
+static void update_iovec(struct iovec *iov, unsigned int iov_size, size_t size)
+{
+	while (size > 0) {
+		i_assert(iov_size > 0);
+
+		if (iov->iov_len <= size) {
+			size -= iov->iov_len;
+			iov->iov_base = NULL;
+			iov->iov_len = 0;
+		} else {
+			iov->iov_base = (char *) iov->iov_base + size;
+			iov->iov_len -= size;
+			size = 0;
+		}
+		iov++; iov_size--;
+	}
+}
+
+static void update_buffer(FileOBuffer *fbuf, size_t size)
+{
+	size_t used;
+
+	if (IS_BUFFER_EMPTY(fbuf))
+		return;
+
+	if (fbuf->head < fbuf->tail) {
+		/* ...HXXXT... */
+		used = fbuf->tail - fbuf->head;
+		fbuf->head += I_MIN(used, size);
+	} else {
+		/* XXXT...HXXX */
+		used = fbuf->buffer_size - fbuf->head;
+		if (size > used) {
+			size -= used;
+			if (size < fbuf->tail)
+				fbuf->head = size;
+			else {
+				/* whole buffer is sent */
+				fbuf->head = fbuf->tail = 0;
+			}
+		} else {
+			fbuf->head += I_MIN(used, size);
+		}
+
+		fbuf->full = FALSE;
+	}
+
+	if (fbuf->head == fbuf->tail)
+		fbuf->head = fbuf->tail = 0;
+
+	if (fbuf->head == fbuf->buffer_size)
+		fbuf->head = 0;
+}
+
+/* NOTE: modifies iov */
+static ssize_t
+o_buffer_writev(FileOBuffer *fbuf, struct iovec *iov, unsigned int iov_size)
+{
+	ssize_t ret;
+
+	while (iov->iov_len == 0 && iov_size > 0) {
+		iov++;
+		iov_size--;
+	}
+
+	i_assert(iov_size > 0);
+
+	ret = writev(fbuf->fd, iov, iov_size);
+	if (ret < 0)
+		return -1;
+
+	update_iovec(iov, iov_size, ret);
+	update_buffer(fbuf, ret);
+	fbuf->obuf.obuffer.v_offset += ret;
+
+	return ret;
+}
+
+static void ioloop_send(IOLoopWriteContext *ctx)
+{
+	if (o_buffer_writev(ctx->fbuf, ctx->iov, ctx->iov_len) < 0 ||
+	    ctx->iov[ctx->iov_len-1].iov_len == 0) {
+		/* error / all sent */
+		io_loop_stop(ctx->ioloop);
+	}
+}
+
+static void ioloop_timeout(void *context, Timeout timeout __attr_unused__)
+{
+	IOLoopWriteContext *ctx = context;
+
+	ctx->timeout = TRUE;
+	io_loop_stop(ctx->ioloop);
+}
+
+static int o_buffer_ioloop(FileOBuffer *fbuf, IOLoopWriteContext *ctx,
+			   void (*send_func)(IOLoopWriteContext *ctx))
+{
+	Timeout to;
+	IO io;
+
+	/* close old IO */
+	if (fbuf->io != NULL)
+		io_remove(fbuf->io);
+
+	t_push();
+
+	/* create a new I/O loop */
+	ctx->ioloop = io_loop_create(data_stack_pool);
+	ctx->fbuf = fbuf;
+
+	io = io_add(fbuf->fd, IO_WRITE, (IOFunc) send_func, ctx);
+	to = fbuf->timeout_msecs <= 0 ? NULL :
+		timeout_add(fbuf->timeout_msecs, ioloop_timeout, ctx);
+
+	io_loop_run(ctx->ioloop);
+
+	io_remove(io);
+
+	if (to != NULL) {
+		if (ctx->timeout && fbuf->timeout_func != NULL) {
+			/* call user-given timeout function */
+			fbuf->timeout_func(fbuf->timeout_context, to);
+		}
+		timeout_remove(to);
+	}
+
+	io_loop_destroy(ctx->ioloop);
+	t_pop();
+
+	return fbuf->obuf.obuffer.closed ? -1 : 1;
+}
+
+/* returns how much of vector was used */
+static int o_buffer_fill_iovec(FileOBuffer *fbuf, struct iovec iov[2])
+{
+	if (IS_BUFFER_EMPTY(fbuf))
+		return 0;
+
+	if (fbuf->head < fbuf->tail) {
+		iov[0].iov_base = fbuf->buffer + fbuf->head;
+		iov[0].iov_len = fbuf->tail - fbuf->head;
+		return 1;
+	} else {
+		iov[0].iov_base = fbuf->buffer + fbuf->head;
+		iov[0].iov_len = fbuf->buffer_size - fbuf->head;
+		if (fbuf->tail == 0)
+			return 1;
+		else {
+			iov[1].iov_base = fbuf->buffer;
+			iov[1].iov_len = fbuf->tail;
+			return 2;
+		}
+	}
+}
+
+static int o_buffer_send_blocking(FileOBuffer *fbuf, const void *data,
+				  size_t size)
+{
+	IOLoopWriteContext ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	ctx.iov_len = o_buffer_fill_iovec(fbuf, ctx.iov);
+	if (size > 0) {
+		ctx.iov[ctx.iov_len].iov_base = (void *) data;
+		ctx.iov[ctx.iov_len].iov_len = size;
+		ctx.iov_len++;
+	}
+
+        return o_buffer_ioloop(fbuf, &ctx, ioloop_send);
+}
+
+static int buffer_flush(FileOBuffer *fbuf)
+{
+	struct iovec iov[2];
+	int iov_len;
+
+	if (!IS_BUFFER_EMPTY(fbuf)) {
+		iov_len = o_buffer_fill_iovec(fbuf, iov);
+		if (o_buffer_writev(fbuf, iov, iov_len) < 0)
+			return -1;
+
+		if (!IS_BUFFER_EMPTY(fbuf)) {
+			if (o_buffer_send_blocking(fbuf, NULL, 0) < 0)
+				return -1;
+		}
+	}
+
+	return 1;
+}
+
+int _flush(_OBuffer *buf)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+	int ret;
+
+	ret = buffer_flush(fbuf);
+
+	if (fbuf->corked) {
+		/* remove cork */
+		if (!fbuf->no_socket_cork) {
+			if (net_set_cork(fbuf->fd, FALSE) < 0)
+				i_error("net_set_cork() failed: %m");
+		}
+		fbuf->corked = FALSE;
+	}
+
+	return ret;
+}
+
+static size_t get_unused_space(FileOBuffer *fbuf)
+{
+	if (fbuf->head > fbuf->tail) {
+		/* XXXT...HXXX */
+		return fbuf->head - fbuf->tail;
+	} else if (fbuf->head < fbuf->tail) {
+		/* ...HXXXT... */
+		return (fbuf->buffer_size - fbuf->tail) + fbuf->head;
+	} else {
+		/* either fully unused or fully used */
+		return fbuf->full ? 0 : fbuf->buffer_size;
+	}
+}
+
+static int _have_space(_OBuffer *buf, size_t size)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+	size_t unused;
+
+	if (fbuf->max_buffer_size == 0)
+		return 1;
+
+	unused = get_unused_space(fbuf);
+	if (size <= unused)
+		return 1;
+
+	unused += (fbuf->max_buffer_size - fbuf->buffer_size);
+	return size <= unused ? 1 : 0;
+}
+
+static int _seek(_OBuffer *buf, uoff_t offset)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+	off_t ret;
+
+	if (offset > OFF_T_MAX) {
+		buf->obuffer.buf_errno = EINVAL;
+		return -1;
+	}
+
+	ret = lseek(fbuf->fd, (off_t)offset, SEEK_SET);
+	if (ret < 0) {
+		buf->obuffer.buf_errno = errno;
+		return -1;
+	}
+
+	if (ret != (off_t)offset) {
+		buf->obuffer.buf_errno = EINVAL;
+		return -1;
+	}
+
+	buf->obuffer.buf_errno = 0;
+	buf->obuffer.v_offset = 0;
+	return 1;
+}
+
+static void o_buffer_grow(FileOBuffer *fbuf, size_t bytes)
+{
+	size_t size, head_size;
+
+	size = nearest_power(fbuf->buffer_size + bytes);
+	if (fbuf->max_buffer_size > 0 && size > fbuf->max_buffer_size)
+		size = fbuf->max_buffer_size;
+
+	if (size == fbuf->buffer_size)
+		return;
+
+	fbuf->buffer = p_realloc(fbuf->obuf.iobuf.pool, fbuf->buffer, size);
+
+	if (fbuf->tail <= fbuf->head && !IS_BUFFER_EMPTY(fbuf)) {
+		head_size = I_MIN(fbuf->head, size - fbuf->buffer_size);
+		memcpy(fbuf->buffer + fbuf->buffer_size, fbuf->buffer,
+		       head_size);
+
+		if (head_size == fbuf->head)
+			fbuf->tail = fbuf->buffer_size + head_size;
+		else {
+			memmove(fbuf->buffer, fbuf->buffer + head_size,
+				fbuf->head - head_size);
+			fbuf->tail = fbuf->head - head_size;
+		}
+	}
+
+	fbuf->full = FALSE;
+	fbuf->buffer_size = size;
+}
+
+static void buffer_send_io(void *context, int fd __attr_unused__,
+			   IO io __attr_unused__)
+{
+	FileOBuffer *fbuf = context;
+	struct iovec iov[2];
+	int iov_len;
+
+	iov_len = o_buffer_fill_iovec(fbuf, iov);
+
+	if (iov_len == 0 || o_buffer_writev(fbuf, iov, iov_len) < 0 ||
+	    iov[iov_len-1].iov_len == 0) {
+		/* error / all sent */
+		io_remove(fbuf->io);
+                fbuf->io = NULL;
+	}
+}
+
+static size_t o_buffer_add(FileOBuffer *fbuf, const void *data, size_t size)
+{
+	size_t unused, sent;
+	int i;
+
+	unused = get_unused_space(fbuf);
+	if (unused < size)
+		o_buffer_grow(fbuf, size-unused);
+
+	sent = 0;
+	for (i = 0; i < 2 && sent < size && !fbuf->full; i++) {
+		unused = fbuf->tail >= fbuf->head ?
+			fbuf->buffer_size - fbuf->tail :
+			fbuf->head - fbuf->tail;
+
+		if (unused > size-sent)
+			unused = size-sent;
+		memcpy(fbuf->buffer + fbuf->tail, data, unused);
+		sent += unused;
+
+		fbuf->tail += unused;
+		if (fbuf->tail == fbuf->buffer_size)
+			fbuf->tail = 0;
+
+		if (fbuf->head == fbuf->tail)
+			fbuf->full = TRUE;
+	}
+
+	if (sent != 0 && fbuf->io == NULL && !fbuf->corked) {
+		fbuf->io = io_add_priority(fbuf->fd, fbuf->priority, IO_WRITE,
+					   buffer_send_io, fbuf);
+	}
+
+	i_assert(!BUFFER_IS_BLOCKING(fbuf) || sent == size);
+	return sent;
+}
+
+static ssize_t _send(_OBuffer *buf, const void *data, size_t size)
+{
+	FileOBuffer *fbuf = (FileOBuffer *) buf;
+	struct iovec iov;
+	ssize_t ret;
+
+	i_assert(size <= SSIZE_T_MAX);
+
+	if (buf->obuffer.closed)
+		return -1;
+
+	buf->obuffer.buf_errno = 0;
+
+	if (IS_BUFFER_EMPTY(fbuf) &&
+	    (!fbuf->corked || !_have_space(buf, size))) {
+		iov.iov_base = (void *) data;
+		iov.iov_len = size;
+		ret = o_buffer_writev(fbuf, &iov, 1);
+		if (ret < 0 || (size_t)ret == size)
+			return ret;
+
+		data = (const char *) data + ret;
+		size -= ret;
+	}
+
+	if (!_have_space(buf, size) && BUFFER_IS_BLOCKING(fbuf)) {
+		/* send it blocking */
+		if (o_buffer_send_blocking(fbuf, data, size) < 0)
+			return -1;
+		return (ssize_t)size;
+	} else {
+		/* buffer it, at least partly */
+		return (ssize_t)o_buffer_add(fbuf, data, size);
+	}
+}
+
+static void ioloop_sendfile(IOLoopWriteContext *ctx)
+{
+	OBuffer *outbuf;
+	uoff_t offset, send_size;
+	ssize_t ret;
+	int in_fd;
+
+	outbuf = &ctx->fbuf->obuf.obuffer;
+	in_fd = i_buffer_get_fd(ctx->inbuf);
+	i_assert(in_fd != -1);
+
+	offset = ctx->inbuf->v_offset;
+	send_size = ctx->inbuf->v_limit - offset;
+
+	ret = safe_sendfile(ctx->fbuf->fd, in_fd, &offset,
+			    MAX_SSIZE_T(send_size));
+	if (ret < 0) {
+		if (errno != EINTR && errno != EAGAIN) {
+			outbuf->buf_errno = errno;
+                        o_buffer_close(outbuf);
+		}
+		ret = 0;
+	}
+
+	i_buffer_skip(ctx->inbuf, (size_t)ret);
+	outbuf->v_offset += ret;
+
+	if (outbuf->closed || (size_t)ret == send_size)
+		io_loop_stop(ctx->ioloop);
+}
+
+static void ioloop_copy(IOLoopWriteContext *ctx)
+{
+	const unsigned char *data;
+	size_t size;
+	int pos;
+
+	i_assert(ctx->iov_len <= 2);
+
+	(void)i_buffer_read_data(ctx->inbuf, &data, &size, O_BUFFER_MIN_SIZE-1);
+
+	pos = ctx->iov_len++;
+	ctx->iov[pos].iov_base = (void *) data;
+	ctx->iov[pos].iov_len = size;
+
+	if (o_buffer_writev(ctx->fbuf, ctx->iov, ctx->iov_len) < 0 ||
+	    ctx->iov[ctx->iov_len-1].iov_len == 0) {
+		/* error / all sent */
+		io_loop_stop(ctx->ioloop);
+	}
+
+	i_buffer_skip(ctx->inbuf, size - ctx->iov[pos].iov_len);
+
+	do {
+		ctx->iov_len--;
+	} while (ctx->iov_len > 0 && ctx->iov[ctx->iov_len-1].iov_len == 0);
+}
+
+static off_t o_buffer_sendfile(_OBuffer *outbuf, IBuffer *inbuf)
+{
+	FileOBuffer *foutbuf = (FileOBuffer *) outbuf;
+        IOLoopWriteContext ctx;
+	uoff_t offset, send_size;
+	ssize_t s_ret;
+	int in_fd;
+
+	in_fd = i_buffer_get_fd(inbuf);
+	if (in_fd == -1) {
+		outbuf->obuffer.buf_errno = EINVAL;
+		return -1;
+	}
+
+	/* flush out any data in buffer */
+	if (buffer_flush(foutbuf) < 0)
+		return -1;
+
+	/* first try if we can do it with a single sendfile() call */
+	offset = inbuf->v_offset;
+	send_size = inbuf->v_limit - offset;
+
+	s_ret = safe_sendfile(foutbuf->fd, in_fd, &offset,
+			      MAX_SSIZE_T(send_size));
+	if (s_ret < 0) {
+		if (errno != EINTR && errno != EAGAIN) {
+			outbuf->obuffer.buf_errno = errno;
+			return -1;
+		}
+		s_ret = 0;
+	}
+
+	i_buffer_skip(inbuf, (size_t)s_ret);
+	outbuf->obuffer.v_offset += s_ret;
+
+	if ((uoff_t)s_ret == send_size) {
+		/* yes, all sent */
+		return (off_t)s_ret;
+	}
+
+	memset(&ctx, 0, sizeof(ctx));
+
+	ctx.fbuf = foutbuf;
+	ctx.inbuf = inbuf;
+
+	if (o_buffer_ioloop(foutbuf, &ctx, ioloop_sendfile) < 0) {
+		if (outbuf->obuffer.buf_errno == EINVAL) {
+			/* this shouldn't happen, must be a bug. It would also
+			   mess up later if we let this pass. */
+			i_panic("o_buffer_sendfile() failed: %m");
+		}
+		return -1;
+	} else {
+		return (off_t)ctx.sent;
+	}
+}
+
+static off_t _send_ibuffer(_OBuffer *outbuf, IBuffer *inbuf)
+{
+        IOLoopWriteContext ctx;
+	off_t ret;
+
+	i_assert(inbuf->v_limit <= OFF_T_MAX);
+	i_assert(inbuf->v_offset <= inbuf->v_limit);
+
+	ret = o_buffer_sendfile(outbuf, inbuf);
+	if (ret >= 0 || outbuf->obuffer.buf_errno != EINVAL)
+		return ret;
+
+	/* sendfile() not supported (with this fd), fallback to
+	   regular sending */
+
+	/* create blocking send loop */
+	memset(&ctx, 0, sizeof(ctx));
+
+	ctx.fbuf = (FileOBuffer *) outbuf;
+	ctx.iov_len = o_buffer_fill_iovec(ctx.fbuf, ctx.iov);
+	ctx.inbuf = inbuf;
+
+	if (o_buffer_ioloop(ctx.fbuf, &ctx, ioloop_copy) < 0)
+		return -1;
+	else
+		return (off_t)ctx.sent;
+}
+
+OBuffer *o_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
+			      int priority, int autoclose_fd)
+{
+	FileOBuffer *fbuf;
+
+	fbuf = p_new(pool, FileOBuffer, 1);
+	fbuf->fd = fd;
+	fbuf->priority = priority;
+	fbuf->max_buffer_size = max_buffer_size;
+	fbuf->autoclose_fd = autoclose_fd;
+
+	fbuf->obuf.iobuf.close = _close;
+	fbuf->obuf.iobuf.destroy = _destroy;
+	fbuf->obuf.iobuf.set_max_size = _set_max_size;
+	fbuf->obuf.iobuf.set_blocking = _set_blocking;
+
+	fbuf->obuf.cork = _cork;
+	fbuf->obuf.flush = _flush;
+	fbuf->obuf.have_space = _have_space;
+	fbuf->obuf.seek = _seek;
+	fbuf->obuf.send = _send;
+	fbuf->obuf.send_ibuffer = _send_ibuffer;
+
+	return _o_buffer_create(&fbuf->obuf, pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/obuffer-internal.h	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,27 @@
+#ifndef __OBUFFER_INTERNAL_H
+#define __OBUFFER_INTERNAL_H
+
+#include "obuffer.h"
+#include "iobuffer-internal.h"
+
+typedef struct __OBuffer _OBuffer;
+
+struct __OBuffer {
+/* inheritance: */
+	_IOBuffer iobuf;
+
+/* methods: */
+	void (*cork)(_OBuffer *buf);
+	int (*flush)(_OBuffer *buf);
+	int (*have_space)(_OBuffer *buf, size_t size);
+	int (*seek)(_OBuffer *buf, uoff_t offset);
+	ssize_t (*send)(_OBuffer *buf, const void *data, size_t size);
+	off_t (*send_ibuffer)(_OBuffer *outbuf, IBuffer *inbuf);
+
+/* data: */
+	OBuffer obuffer;
+};
+
+OBuffer *_o_buffer_create(_OBuffer *_buf, Pool pool);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/obuffer.c	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,121 @@
+/*
+   obuffer.c : Output buffer handling
+
+    Copyright (c) 2002 Timo Sirainen
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "lib.h"
+#include "ibuffer.h"
+#include "obuffer-internal.h"
+
+void o_buffer_ref(OBuffer *buf)
+{
+	_io_buffer_ref(buf->real_buffer);
+}
+
+void o_buffer_unref(OBuffer *buf)
+{
+	_io_buffer_unref(buf->real_buffer);
+}
+
+void o_buffer_close(OBuffer *buf)
+{
+	_io_buffer_close(buf->real_buffer);
+	buf->closed = TRUE;
+}
+
+void o_buffer_set_max_size(OBuffer *buf, size_t max_size)
+{
+	_io_buffer_set_max_size(buf->real_buffer, max_size);
+}
+
+void o_buffer_set_blocking(OBuffer *buf, int timeout_msecs,
+			   TimeoutFunc timeout_func, void *context)
+{
+	_io_buffer_set_blocking(buf->real_buffer, timeout_msecs,
+				timeout_func, context);
+}
+
+void o_buffer_cork(OBuffer *buf)
+{
+	_OBuffer *_buf = buf->real_buffer;
+
+	if (buf->closed)
+		return;
+
+	_buf->cork(_buf);
+}
+
+int o_buffer_flush(OBuffer *buf)
+{
+	_OBuffer *_buf = buf->real_buffer;
+
+	if (buf->closed)
+		return -1;
+
+	return _buf->flush(_buf);
+}
+
+int o_buffer_have_space(OBuffer *buf, size_t size)
+{
+	_OBuffer *_buf = buf->real_buffer;
+
+	return _buf->have_space(_buf, size);
+}
+
+ssize_t o_buffer_seek(OBuffer *buf, uoff_t offset)
+{
+	_OBuffer *_buf = buf->real_buffer;
+
+	if (buf->closed)
+		return -1;
+
+	return _buf->seek(_buf, offset);
+}
+
+ssize_t o_buffer_send(OBuffer *buf, const void *data, size_t size)
+{
+	_OBuffer *_buf = buf->real_buffer;
+
+	if (buf->closed)
+		return -1;
+
+	return _buf->send(_buf, data, size);
+}
+
+off_t o_buffer_send_ibuffer(OBuffer *outbuf, IBuffer *inbuf)
+{
+	_OBuffer *_outbuf = outbuf->real_buffer;
+
+	if (outbuf->closed || inbuf->closed)
+		return -1;
+
+	return _outbuf->send_ibuffer(_outbuf, inbuf);
+}
+
+OBuffer *_o_buffer_create(_OBuffer *_buf, Pool pool)
+{
+	_buf->obuffer.real_buffer = _buf;
+
+	_io_buffer_init(pool, &_buf->iobuf);
+	return &_buf->obuffer;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/obuffer.h	Mon Oct 14 02:49:11 2002 +0300
@@ -0,0 +1,56 @@
+#ifndef __OBUFFER_H
+#define __OBUFFER_H
+
+#include "ioloop.h" /* TimeoutFunc */
+
+struct _OBuffer {
+	uoff_t v_offset; /* relative to start offset */
+
+	int buf_errno;
+	unsigned int closed:1;
+
+	void *real_buffer;
+};
+
+OBuffer *o_buffer_create_file(int fd, Pool pool, size_t max_buffer_size,
+			      int priority, int autoclose_fd);
+
+/* Reference counting. References start from 1, so calling o_buffer_unref()
+   destroys the buffer if o_buffer_ref() is never used. */
+void o_buffer_ref(OBuffer *buf);
+void o_buffer_unref(OBuffer *buf);
+
+/* Mark the buffer closed. Nothing will be sent after this call. */
+void o_buffer_close(OBuffer *buf);
+
+/* Change the maximum size for buffer to grow. */
+void o_buffer_set_max_size(OBuffer *buf, size_t max_size);
+/* Buffer is made to be flushed out whenever it gets full (assumes max_size
+   is already set), ie. writes will never be partial. Also makes any blocking
+   writes to fail after specified timeout, also calling timeout_func if it's
+   set. The blocking state in file descriptor isn't changed, but for timeout
+   to work it must be in non-blocking state. */
+void o_buffer_set_blocking(OBuffer *buf, int timeout_msecs,
+			   TimeoutFunc timeout_func, void *context);
+
+/* Delays sending as far as possible, writing only full buffers. Also sets
+   TCP_CORK on if supported. o_buffer_flush() removes the cork. */
+void o_buffer_cork(OBuffer *buf);
+/* Flush the output buffer, blocks until everything is sent.
+   Returns 1 if ok, -1 if error. */
+int o_buffer_flush(OBuffer *buf);
+/* Returns 1 if specified amount of data fits into buffer before reaching
+   max_size, 0 if not. */
+int o_buffer_have_space(OBuffer *buf, size_t size);
+
+/* Seek to specified position from beginning of file and v_offset is set to 0.
+   This works only for files. Returns 1 if successful, -1 if error. */
+int o_buffer_seek(OBuffer *buf, uoff_t offset);
+/* Returns number of bytes sent or buffered, or -1 if disconnected */
+ssize_t o_buffer_send(OBuffer *buf, const void *data, size_t size);
+/* Send data from input buffer to output buffer using the fastest
+   possible method. Returns number of bytes sent, or -1 if error.
+   Note that this function may block. */
+off_t o_buffer_send_ibuffer(OBuffer *outbuf, IBuffer *inbuf);
+
+#endif
--- a/src/login/auth-connection.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/login/auth-connection.c	Mon Oct 14 02:49:11 2002 +0300
@@ -3,7 +3,8 @@
 #include "common.h"
 #include "hash.h"
 #include "network.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "auth-connection.h"
 
 #include <unistd.h>
@@ -20,7 +21,8 @@
 	char *path;
 	int fd;
 	IO io;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 
 	int auth_process;
 	AuthMethod available_auth_methods;
@@ -70,10 +72,10 @@
 	conn->path = i_strdup(path);
 	conn->fd = fd;
 	conn->io = io_add(fd, IO_READ, auth_input, conn);
-	conn->inbuf = io_buffer_create(fd, default_pool, IO_PRIORITY_HIGH,
-				       MAX_INBUF_SIZE);
-	conn->outbuf = io_buffer_create(fd, default_pool, IO_PRIORITY_DEFAULT,
-					MAX_OUTBUF_SIZE);
+	conn->inbuf = i_buffer_create_file(fd, default_pool, MAX_INBUF_SIZE,
+					   FALSE);
+	conn->outbuf = o_buffer_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
+					    IO_PRIORITY_DEFAULT, FALSE);
 	conn->requests = hash_create(default_pool, 100, NULL, NULL);
 
 	conn->next = auth_connections;
@@ -118,8 +120,8 @@
 
 	(void)close(conn->fd);
 	io_remove(conn->io);
-	io_buffer_unref(conn->inbuf);
-	io_buffer_unref(conn->outbuf);
+	i_buffer_unref(conn->inbuf);
+	o_buffer_unref(conn->outbuf);
 	i_free(conn->path);
 	i_free(conn);
 }
@@ -133,7 +135,7 @@
 	found = FALSE;
 	for (conn = auth_connections; conn != NULL; conn = conn->next) {
 		if ((conn->available_auth_methods & method)) {
-			if (io_buffer_get_space(conn->outbuf, size) != NULL)
+			if (o_buffer_have_space(conn->outbuf, size) > 0)
 				return conn;
 
 			found = TRUE;
@@ -175,7 +177,7 @@
 }
 
 static void auth_handle_reply(AuthConnection *conn, AuthReplyData *reply_data,
-			      unsigned char *data)
+			      const unsigned char *data)
 {
 	AuthRequest *request;
 
@@ -204,10 +206,10 @@
 {
 	AuthConnection *conn = context;
         AuthInitData init_data;
-	unsigned char *data;
+	const unsigned char *data;
 	size_t size;
 
-	switch (io_buffer_read(conn->inbuf)) {
+	switch (i_buffer_read(conn->inbuf)) {
 	case 0:
 		return;
 	case -1:
@@ -223,12 +225,12 @@
 		return;
 	}
 
-	data = io_buffer_get_data(conn->inbuf, &size);
+	data = i_buffer_get_data(conn->inbuf, &size);
 
 	if (!conn->init_received) {
 		if (size == sizeof(AuthInitData)) {
 			memcpy(&init_data, data, sizeof(AuthInitData));
-			io_buffer_skip(conn->inbuf, sizeof(AuthInitData));
+			i_buffer_skip(conn->inbuf, sizeof(AuthInitData));
 
 			auth_handle_init(conn, &init_data);
 		} else if (size > sizeof(AuthInitData)) {
@@ -242,14 +244,14 @@
 	}
 
 	if (!conn->in_reply_received) {
-		data = io_buffer_get_data(conn->inbuf, &size);
+		data = i_buffer_get_data(conn->inbuf, &size);
 		if (size < sizeof(AuthReplyData))
 			return;
 
 		memcpy(&conn->in_reply, data, sizeof(AuthReplyData));
 		data += sizeof(AuthReplyData);
 		size -= sizeof(AuthReplyData);
-		io_buffer_skip(conn->inbuf, sizeof(AuthReplyData));
+		i_buffer_skip(conn->inbuf, sizeof(AuthReplyData));
 		conn->in_reply_received = TRUE;
 	}
 
@@ -259,7 +261,7 @@
 	/* we've got a full reply */
 	conn->in_reply_received = FALSE;
 	auth_handle_reply(conn, &conn->in_reply, data);
-	io_buffer_skip(conn->inbuf, conn->in_reply.data_size);
+	i_buffer_skip(conn->inbuf, conn->in_reply.data_size);
 }
 
 int auth_init_request(AuthMethod method, AuthCallback callback,
@@ -290,8 +292,8 @@
 	request_data.type = AUTH_REQUEST_INIT;
 	request_data.method = request->method;
 	request_data.id = request->id;
-	if (io_buffer_send(request->conn->outbuf, &request_data,
-			   sizeof(request_data)) < 0)
+	if (o_buffer_send(request->conn->outbuf, &request_data,
+			  sizeof(request_data)) < 0)
 		auth_connection_destroy(request->conn);
 	return TRUE;
 }
@@ -307,10 +309,10 @@
 	request_data.id = request->id;
 	request_data.data_size = data_size;
 
-	if (io_buffer_send(request->conn->outbuf, &request_data,
-			   sizeof(request_data)) < 0)
+	if (o_buffer_send(request->conn->outbuf, &request_data,
+			  sizeof(request_data)) < 0)
 		auth_connection_destroy(request->conn);
-	else if (io_buffer_send(request->conn->outbuf, data, data_size) < 0)
+	else if (o_buffer_send(request->conn->outbuf, data, data_size) < 0)
 		auth_connection_destroy(request->conn);
 }
 
--- a/src/login/client-authenticate.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/login/client-authenticate.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,8 @@
 
 #include "common.h"
 #include "base64.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "temp-string.h"
 #include "auth-connection.h"
 #include "client.h"
@@ -71,7 +72,7 @@
 
 	client_send_tagline(client, msg != NULL ? msg :
 			    "NO Authentication failed.");
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
 	/* get back to normal client input */
 	if (client->io != NULL)
@@ -109,11 +110,11 @@
 	t_push();
 
 	base64_data = base64_encode(data, size);
-	io_buffer_send(client->outbuf, "+ ", 2);
-	io_buffer_send(client->outbuf, base64_data, strlen(base64_data));
-	io_buffer_send(client->outbuf, "\r\n", 2);
+	o_buffer_send(client->outbuf, "+ ", 2);
+	o_buffer_send(client->outbuf, base64_data, strlen(base64_data));
+	o_buffer_send(client->outbuf, "\r\n", 2);
 
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
 	t_pop();
 }
@@ -239,7 +240,7 @@
 	if (!client_read(client))
 		return;
 
-	line = io_buffer_next_line(client->inbuf);
+	line = i_buffer_next_line(client->inbuf);
 	if (line == NULL)
 		return;
 
--- a/src/login/client.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/login/client.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,8 @@
 
 #include "common.h"
 #include "hash.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
+#include "obuffer.h"
 #include "client.h"
 #include "client-authenticate.h"
 #include "ssl-proxy.h"
@@ -49,7 +50,7 @@
 	}
 
 	client_send_tagline(client, "OK Begin TLS negotiation now.");
-	io_buffer_send_flush(client->outbuf);
+	o_buffer_flush(client->outbuf);
 
 	/* must be removed before ssl_proxy_new(), since it may
 	   io_add() the same fd. */
@@ -59,11 +60,15 @@
 	if (fd_ssl != -1) {
 		client->tls = TRUE;
 		client->fd = fd_ssl;
-		client->inbuf->fd = fd_ssl;
-		client->outbuf->fd = fd_ssl;
+
+		i_buffer_unref(client->inbuf);
+		o_buffer_unref(client->outbuf);
 
-		i_assert(client->inbuf->io == NULL);
-		i_assert(client->outbuf->io == NULL);
+		client->inbuf = i_buffer_create_file(fd_ssl, default_pool,
+						     8192, FALSE);
+		client->outbuf = o_buffer_create_file(fd_ssl, default_pool,
+						      1024, IO_PRIORITY_DEFAULT,
+						      FALSE);
 	} else {
 		client_send_line(client, " * BYE TLS handehake failed.");
 		client_destroy(client, "TLS handshake failed");
@@ -89,7 +94,7 @@
 
 int client_read(Client *client)
 {
-	switch (io_buffer_read(client->inbuf)) {
+	switch (i_buffer_read(client->inbuf)) {
 	case -2:
 		/* buffer full */
 		client_send_line(client, "* BYE Input buffer full, aborting");
@@ -181,9 +186,9 @@
 		return;
 
 	client_ref(client);
-	io_buffer_cork(client->outbuf);
+	o_buffer_cork(client->outbuf);
 
-	while ((line = io_buffer_next_line(client->inbuf)) != NULL) {
+	while ((line = i_buffer_next_line(client->inbuf)) != NULL) {
 		/* split the arguments, make sure we have at
 		   least tag + command */
 		i_free(client->tag);
@@ -198,7 +203,7 @@
 	}
 
 	if (client_unref(client))
-		io_buffer_send_flush(client->outbuf);
+		o_buffer_flush(client->outbuf);
 }
 
 static void client_hash_destroy_oldest(void *key, void *value __attr_unused__,
@@ -255,10 +260,9 @@
 	memcpy(&client->ip, ip, sizeof(IPADDR));
 	client->fd = fd;
 	client->io = io_add(fd, IO_READ, client_input, client);
-	client->inbuf = io_buffer_create(fd, default_pool,
-					 IO_PRIORITY_DEFAULT, 8192);
-	client->outbuf = io_buffer_create(fd, default_pool,
-					  IO_PRIORITY_DEFAULT, 1024);
+	client->inbuf = i_buffer_create_file(fd, default_pool, 8192, FALSE);
+	client->outbuf = o_buffer_create_file(fd, default_pool, 1024,
+					      IO_PRIORITY_DEFAULT, FALSE);
         client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
@@ -273,8 +277,8 @@
 
 	hash_remove(clients, client);
 
-	io_buffer_close(client->inbuf);
-	io_buffer_close(client->outbuf);
+	i_buffer_close(client->inbuf);
+	o_buffer_close(client->outbuf);
 
 	if (client->io != NULL) {
 		io_remove(client->io);
@@ -297,8 +301,8 @@
 	if (--client->refcount > 0)
 		return TRUE;
 
-	io_buffer_unref(client->inbuf);
-	io_buffer_unref(client->outbuf);
+	i_buffer_unref(client->inbuf);
+	o_buffer_unref(client->outbuf);
 
 	i_free(client->tag);
 	i_free(client->plain_login);
@@ -308,8 +312,8 @@
 
 void client_send_line(Client *client, const char *line)
 {
-	io_buffer_send(client->outbuf, line, strlen(line));
-	io_buffer_send(client->outbuf, "\r\n", 2);
+	o_buffer_send(client->outbuf, line, strlen(line));
+	o_buffer_send(client->outbuf, "\r\n", 2);
 }
 
 void client_send_tagline(Client *client, const char *line)
--- a/src/login/client.h	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/login/client.h	Mon Oct 14 02:49:11 2002 +0300
@@ -10,7 +10,8 @@
 
 	int fd;
 	IO io;
-	IOBuffer *inbuf, *outbuf;
+	IBuffer *inbuf;
+	OBuffer *outbuf;
 
 	time_t last_input;
 	char *tag;
--- a/src/master/auth-process.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/master/auth-process.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,7 @@
 
 #include "common.h"
 #include "network.h"
-#include "iobuffer.h"
+#include "obuffer.h"
 #include "restrict-access.h"
 #include "auth-process.h"
 
@@ -20,7 +20,7 @@
 	pid_t pid;
 	int fd;
 	IO io;
-	IOBuffer *outbuf;
+	OBuffer *outbuf;
 
 	unsigned int reply_pos;
 	char reply_buf[sizeof(AuthCookieReplyData)];
@@ -123,8 +123,9 @@
 	p->pid = pid;
 	p->fd = fd;
 	p->io = io_add(fd, IO_READ, auth_process_input, p);
-	p->outbuf = io_buffer_create(fd, default_pool, IO_PRIORITY_DEFAULT,
-				     sizeof(AuthCookieRequestData)*100);
+	p->outbuf = o_buffer_create_file(fd, default_pool,
+					 sizeof(AuthCookieRequestData)*100,
+					 IO_PRIORITY_DEFAULT, FALSE);
 
 	p->next_request = &p->requests;
 
@@ -154,7 +155,7 @@
 
 	(void)unlink(t_strconcat(set_login_dir, "/", p->name, NULL));
 
-	io_buffer_unref(p->outbuf);
+	o_buffer_unref(p->outbuf);
 	io_remove(p->io);
 	(void)close(p->fd);
 	i_free(p->name);
@@ -268,7 +269,7 @@
 	req.id = id;
 	memcpy(req.cookie, cookie, AUTH_COOKIE_SIZE);
 
-	if (io_buffer_send(process->outbuf, &req, sizeof(req)) < 0)
+	if (o_buffer_send(process->outbuf, &req, sizeof(req)) < 0)
 		auth_process_destroy(process);
 
 	push_request(process, id, callback, context);
--- a/src/master/login-process.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/master/login-process.c	Mon Oct 14 02:49:11 2002 +0300
@@ -2,7 +2,7 @@
 
 #include "common.h"
 #include "network.h"
-#include "iobuffer.h"
+#include "obuffer.h"
 #include "fdpass.h"
 #include "restrict-access.h"
 #include "login-process.h"
@@ -19,7 +19,7 @@
 	pid_t pid;
 	int fd;
 	IO io;
-	IOBuffer *outbuf;
+	OBuffer *outbuf;
 	unsigned int destroyed:1;
 } LoginProcess;
 
@@ -68,7 +68,7 @@
 	reply.id = request->login_id;
 
 	process = request->process;
-	if (io_buffer_send(process->outbuf, &reply, sizeof(reply)) < 0)
+	if (o_buffer_send(process->outbuf, &reply, sizeof(reply)) < 0)
 		login_process_destroy(process);
 
 	(void)close(request->fd);
@@ -139,8 +139,9 @@
 	p->pid = pid;
 	p->fd = fd;
 	p->io = io_add(fd, IO_READ, login_process_input, p);
-	p->outbuf = io_buffer_create(fd, default_pool, IO_PRIORITY_DEFAULT,
-				     sizeof(MasterReply)*10);
+	p->outbuf = o_buffer_create_file(fd, default_pool,
+					 sizeof(MasterReply)*10,
+					 IO_PRIORITY_DEFAULT, FALSE);
 
 	hash_insert(processes, POINTER_CAST(pid), p);
 	return p;
@@ -152,7 +153,7 @@
 		return;
 	p->destroyed = TRUE;
 
-	io_buffer_close(p->outbuf);
+	o_buffer_close(p->outbuf);
 	io_remove(p->io);
 	(void)close(p->fd);
 
@@ -165,7 +166,7 @@
 	if (--p->refcount > 0)
 		return;
 
-	io_buffer_unref(p->outbuf);
+	o_buffer_unref(p->outbuf);
 	i_free(p);
 }
 
--- a/src/master/settings.c	Sun Oct 13 18:52:19 2002 +0300
+++ b/src/master/settings.c	Mon Oct 14 02:49:11 2002 +0300
@@ -1,7 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
-#include "iobuffer.h"
+#include "ibuffer.h"
 #include "settings.h"
 
 #include <stdio.h>
@@ -124,6 +124,18 @@
 	}
 }
 
+static void auth_settings_verify(void)
+{
+	AuthConfig *auth;
+
+	for (auth = auth_processes_config; auth != NULL; auth = auth->next) {
+		if (access(set_imap_executable, X_OK) == -1) {
+			i_fatal("Can't use auth executable %s: %m",
+				auth->executable);
+		}
+	}
+}
+
 static void settings_verify(void)
 {
 	get_login_uid();
@@ -150,6 +162,8 @@
 		i_fatal("first_valid_uid can't be larger than last_valid_uid");
 	if (set_first_valid_gid < set_last_valid_gid)
 		i_fatal("first_valid_gid can't be larger than last_valid_gid");
+
+	auth_settings_verify();
 }
 
 static AuthConfig *auth_config_new(const char *name)
@@ -170,6 +184,9 @@
 {
 	AuthConfig *auth;
 
+	if (strchr(name, '/') != NULL)
+		return "Authentication process name must not contain '/'";
+
 	for (auth = auth_processes_config; auth != NULL; auth = auth->next) {
 		if (strcmp(auth->name, name) == 0) {
 			return "Authentication process already exists "
@@ -277,7 +294,7 @@
 
 void settings_read(const char *path)
 {
-	IOBuffer *inbuf;
+	IBuffer *inbuf;
 	const char *errormsg;
 	char *line, *key, *p;
 	int fd, linenum;
@@ -289,11 +306,11 @@
 		i_fatal("Can't open configuration file %s: %m", path);
 
 	linenum = 0;
-	inbuf = io_buffer_create_file(fd, default_pool, 2048, TRUE);
+	inbuf = i_buffer_create_file(fd, default_pool, 2048, TRUE);
 	for (;;) {
-		line = io_buffer_next_line(inbuf);
+		line = i_buffer_next_line(inbuf);
 		if (line == NULL) {
-			if (io_buffer_read(inbuf) <= 0)
+			if (i_buffer_read(inbuf) <= 0)
 				break;
                         continue;
 		}
@@ -338,7 +355,7 @@
 		}
 	};
 
-	io_buffer_unref(inbuf);
+	i_buffer_unref(inbuf);
 
         settings_verify();
 }