changeset 765:553f050c8313 HEAD

Added buffer API. Point is to hide all buffer writing behind this API which verifies that nothing overflows. Much better than doing the same checks all around the code, even if it is slightly slower. Buffer reading is still mostly done directly, that isn't such a big security risk and I can't think of a reasonable API for it anyway.
author Timo Sirainen <tss@iki.fi>
date Sun, 08 Dec 2002 07:23:07 +0200
parents f57c52738f90
children 03832c7f389b
files src/auth/auth-digest-md5.c src/auth/auth-plain.c src/auth/userinfo-passwd-file.c src/imap/cmd-list.c src/imap/cmd-sort.c src/imap/cmd-uid.c src/imap/main.c src/imap/rawlog.c src/lib-charset/charset-iconv.c src/lib-charset/charset-utf8.h src/lib-index/mail-custom-flags.c src/lib-index/mail-index-update.c src/lib-index/mail-modifylog.c src/lib-index/maildir/maildir-index.c src/lib-index/mbox/mbox-index.c src/lib-index/mbox/mbox-rewrite.c src/lib-mail/message-body-search.c src/lib-mail/message-header-search.c src/lib-mail/message-header-search.h src/lib-mail/message-parser.c src/lib-mail/message-part-serialize.c src/lib-mail/message-part-serialize.h src/lib-mail/quoted-printable.c src/lib-mail/quoted-printable.h src/lib-storage/index/index-search.c src/lib-storage/mail-sort.c src/lib-storage/mail-storage.c src/lib/Makefile.am src/lib/base64.c src/lib/base64.h src/lib/buffer.c src/lib/buffer.h src/lib/data-stack.c src/lib/hex-binary.c src/lib/hex-binary.h src/lib/lib.h src/login/client-authenticate.c src/login/client.c src/login/client.h
diffstat 39 files changed, 1095 insertions(+), 617 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-digest-md5.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/auth/auth-digest-md5.c	Sun Dec 08 07:23:07 2002 +0200
@@ -4,6 +4,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "buffer.h"
 #include "hex-binary.h"
 #include "md5.h"
 #include "randgen.h"
@@ -57,6 +58,7 @@
 static const char *get_digest_challenge(AuthData *auth)
 {
 	TempString *qoplist, *realms;
+	Buffer *buf;
 	char *const *tmp;
 	unsigned char nonce[16];
 	int i;
@@ -73,8 +75,15 @@
 
 	/* get 128bit of random data as nonce */
 	random_fill(nonce, sizeof(nonce));
-	auth->nonce = p_strdup(auth->pool,
-			       base64_encode(nonce, sizeof(nonce)));
+
+	t_push();
+	buf = buffer_create_static(data_stack_pool,
+				   MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1);
+
+	base64_encode(nonce, sizeof(nonce), buf);
+	buffer_append_c(buf, '\0');
+	auth->nonce = p_strdup(auth->pool, buffer_get_data(buf, NULL));
+	t_pop();
 
 	/* get list of allowed QoPs */
 	qoplist = t_string_new(32);
--- a/src/auth/auth-plain.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/auth/auth-plain.c	Sun Dec 08 07:23:07 2002 +0200
@@ -28,13 +28,14 @@
 	count = 0;
 	for (i = 0; i < request->data_size; i++) {
 		if (data[i] == '\0' && ++count == 2) {
-			if (i+1 == request->data_size)
+			i++;
+			if (i == request->data_size)
 				pass = "";
 			else {
-				len = request->data_size - (i+1);
+				len = request->data_size - i;
 				pass = t_malloc(len+1);
-				memcpy(pass, (const char *) data + (i+1), len);
-				pass[len] = '\0';
+                                memcpy(pass, (const char *) data + i, len);
+                                pass[len] = '\0';
 			}
 			break;
 		}
--- a/src/auth/userinfo-passwd-file.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/auth/userinfo-passwd-file.c	Sun Dec 08 07:23:07 2002 +0200
@@ -7,6 +7,7 @@
 
 #include "userinfo-passwd.h"
 
+#include "buffer.h"
 #include "istream.h"
 #include "hash.h"
 #include "hex-binary.h"
@@ -153,6 +154,7 @@
 {
 	const char *id;
 	PasswdUser *pu;
+	Buffer *buf;
 
 	passwd_file_sync();
 
@@ -167,7 +169,9 @@
 
 	/* found */
 	i_assert(strlen(pu->password) == 32);
-	if (!hex_to_binary(pu->password, digest))
+
+	buf = buffer_create_data(data_stack_pool, digest, sizeof(digest));
+	if (!hex_to_binary(pu->password, buf))
 		return FALSE;
 	
 	return get_reply_data(pu, reply);
--- a/src/imap/cmd-list.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/imap/cmd-list.c	Sun Dec 08 07:23:07 2002 +0200
@@ -40,11 +40,12 @@
 
 	parent = NULL;
 
-	t_push();
 	for (name = path;; path++) {
 		if (*path != separator && *path != '\0')
 			continue;
 
+		t_push();
+
 		/* escaping is done here to make sure we don't try to escape
 		   the separator char */
 		name = imap_escape(t_strdup_until(name, path));
@@ -64,6 +65,8 @@
 			(*node)->flags = MAILBOX_NOSELECT;
 		}
 
+		t_pop();
+
 		if (*path == '\0')
 			break;
 
@@ -71,7 +74,6 @@
 		parent = (*node)->name;
 		node = &(*node)->children;
 	}
-	t_pop();
 
 	return *node;
 }
@@ -93,7 +95,7 @@
 static void list_send(Client *client, ListNode *node, const char *cmd,
 		      const char *path, const char *sep, ImapMatchGlob *glob)
 {
-	const char *name;
+	const char *name, *str;
 
 	for (; node != NULL; node = node->next) {
 		t_push();
@@ -109,18 +111,14 @@
 		if (node->children != NULL)
 			list_send(client, node->children, cmd, name, sep, glob);
 
-		if ((node->flags & MAILBOX_NOSELECT) &&
-		    imap_match(glob, name) <= 0) {
-			/* doesn't match the mask */
-			t_pop();
-			continue;
+		if ((node->flags & MAILBOX_NOSELECT) == 0 ||
+		    imap_match(glob, name) > 0) {
+			/* node->name should already be escaped */
+			str = t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
+					      mailbox_flags2str(node->flags),
+					      sep, name);
+			client_send_line(client, str);
 		}
-
-		/* node->name should already be escaped */
-		client_send_line(client,
-				 t_strdup_printf("* %s (%s) \"%s\" \"%s\"", cmd,
-						 mailbox_flags2str(node->flags),
-						 sep, name));
 		t_pop();
 	}
 }
@@ -162,7 +160,7 @@
 			pattern = t_strconcat(ref, pattern, NULL);
 		}
 
-		ctx.pool = pool_create("ListCtx", 10240, FALSE);
+		ctx.pool = pool_create("ListContext", 10240, FALSE);
 		ctx.nodes = NULL;
 		ctx.storage = client->storage;
 
--- a/src/imap/cmd-sort.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/imap/cmd-sort.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "buffer.h"
 #include "commands.h"
 #include "mail-search.h"
 #include "mail-sort.h"
@@ -25,12 +26,12 @@
 
 static MailSortType *get_sort_program(Client *client, ImapArg *args)
 {
-	MailSortType *program, *temp_prog;
-	size_t program_alloc, program_size;
+	MailSortType type;
+	Buffer *buf;
 	int i;
 
-	program_alloc = 32; program_size = 0;
-	program = t_new(MailSortType, program_alloc+1);
+	buf = buffer_create_dynamic(data_stack_pool, 32 * sizeof(MailSortType),
+				    (size_t)-1);
 
 	while (args->type == IMAP_ARG_ATOM || args->type == IMAP_ARG_STRING) {
 		const char *arg = args->data.str;
@@ -46,20 +47,12 @@
 			return NULL;
 		}
 
-		if (program_size == program_alloc) {
-			program_alloc *= 2;
-			if (!t_try_realloc(program, program_alloc+1)) {
-				temp_prog = t_new(MailSortType, program_alloc);
-				memcpy(temp_prog, program,
-				       sizeof(MailSortType) * program_size);
-				program = temp_prog;
-			}
-		}
-		program[program_size++] = sort_names[i].type;
+		buffer_append(buf, &sort_names[i].type, sizeof(MailSortType));
 		args++;
 	}
 
-	program[program_size] = MAIL_SORT_END;
+	type = MAIL_SORT_END;
+	buffer_append(buf, &type, sizeof(type));
 
 	if (args->type != IMAP_ARG_EOL) {
 		client_send_command_error(client,
@@ -67,7 +60,7 @@
 		return NULL;
 	}
 
-	return program;
+	return buffer_free_without_data(buf);
 }
 
 int cmd_sort(Client *client)
--- a/src/imap/cmd-uid.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/imap/cmd-uid.c	Sun Dec 08 07:23:07 2002 +0200
@@ -30,6 +30,8 @@
 			client->cmd_func = cmd_store;
 		else if (strcasecmp(cmd, "SEARCH") == 0)
 			client->cmd_func = cmd_search;
+		else if (strcasecmp(cmd, "SORT") == 0)
+			client->cmd_func = cmd_sort;
 		break;
 	}
 
--- a/src/imap/main.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/imap/main.c	Sun Dec 08 07:23:07 2002 +0200
@@ -12,7 +12,7 @@
 #include <syslog.h>
 
 IOLoop ioloop;
-static char log_prefix[64];
+static char log_prefix[128];
 
 static void sig_quit(int signo __attr_unused__)
 {
--- a/src/imap/rawlog.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/imap/rawlog.c	Sun Dec 08 07:23:07 2002 +0200
@@ -113,14 +113,14 @@
 	if (strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S", tm) <= 0)
 		i_fatal("strftime() failed");
 
-	fname = t_strdup_printf("%s/%s-%d.in", path, timestamp, getpid());
+	fname = t_strdup_printf("%s/%s-%d.in", path, timestamp, (int)getpid());
 	log_in = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
 	if (log_in == -1) {
 		i_warning("rawlog_open: open() failed for %s: %m", fname);
 		return;
 	}
 
-	fname = t_strdup_printf("%s/%s-%d.out", path, timestamp, getpid());
+	fname = t_strdup_printf("%s/%s-%d.out", path, timestamp, (int)getpid());
 	log_out = open(fname, O_CREAT|O_EXCL|O_WRONLY, 0600);
 	if (log_out == -1) {
 		i_warning("rawlog_open: open() failed for %s: %m", fname);
--- a/src/lib-charset/charset-iconv.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-charset/charset-iconv.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "charset-utf8.h"
 
 #ifdef HAVE_ICONV_H
@@ -66,49 +67,87 @@
 		(void)iconv(t->cd, NULL, NULL, NULL, NULL);
 }
 
-int charset_to_ucase_utf8(CharsetTranslation *t,
-			  const unsigned char **inbuf, size_t *insize,
-			  unsigned char *outbuf, size_t *outsize)
+static void str_ucase_utf8(const unsigned char *src, size_t src_size,
+			   Buffer *dest, size_t destpos)
 {
-	ICONV_CONST char *ic_inbuf;
-	char *ic_outbuf;
-	size_t outleft, max_size, i;
+	char *destbuf;
+	size_t i;
+
+	destbuf = buffer_get_space(dest, destpos, src_size);
+	for (i = 0; i < src_size; i++)
+		destbuf[i] = i_toupper(src[i]); /* FIXME: utf8 */
+}
+
+CharsetResult
+charset_to_ucase_utf8(CharsetTranslation *t,
+		      const Buffer *src, size_t *src_pos, Buffer *dest)
+{
+	ICONV_CONST char *ic_srcbuf;
+	char *ic_destbuf;
+	size_t srcleft, destpos, destleft, size;
+        CharsetResult ret;
+
+	destpos = buffer_get_used_size(dest);
+	destleft = buffer_get_size(dest) - destpos;
 
 	if (t->cd == NULL) {
 		/* no translation needed - just copy it to outbuf uppercased */
-		max_size = I_MIN(*insize, *outsize);
-		for (i = 0; i < max_size; i++)
-			outbuf[i] = i_toupper((*inbuf)[i]); /* FIXME: utf8 */
-		*insize = 0;
-		*outsize = max_size;
-		return TRUE;
+		size = buffer_get_used_size(src);
+		if (size > destleft)
+			size = destleft;
+		str_ucase_utf8(buffer_get_data(src, NULL), size, dest, destpos);
+		if (src_pos != NULL)
+			*src_pos = size;
+		return CHARSET_RET_OK;
 	}
 
-	ic_inbuf = (ICONV_CONST char *) *inbuf;
-	ic_outbuf = (char *) outbuf;
-	outleft = *outsize;
+	size = destleft;
+	ic_srcbuf = (ICONV_CONST char *) buffer_get_data(src, &srcleft);
+	ic_destbuf = buffer_append_space(dest, destleft);
+
+	if (iconv(t->cd, &ic_srcbuf, &srcleft,
+		  &ic_destbuf, &destleft) != (size_t)-1)
+		ret = CHARSET_RET_OK;
+	else if (errno == E2BIG)
+		ret = CHARSET_RET_OUTPUT_FULL;
+	else if (errno == EINVAL)
+		ret = CHARSET_RET_INCOMPLETE_INPUT;
+	else {
+		/* should be EILSEQ */
+		return CHARSET_RET_INVALID_INPUT;
+	}
+	size -= destleft;
+
+	/* give back the memory we didn't use */
+	buffer_set_used_size(dest, buffer_get_used_size(dest) - destleft);
 
-	if (iconv(t->cd, &ic_inbuf, insize,
-		  &ic_outbuf, &outleft) == (size_t)-1) {
-		if (errno != E2BIG && errno != EINVAL) {
-			/* should be EILSEQ - invalid input */
-			return FALSE;
-		}
-	}
+	if (src_pos != NULL)
+		*src_pos = buffer_get_used_size(src) - srcleft;
+
+	str_ucase_utf8((unsigned char *) ic_destbuf - size, size,
+		       dest, destpos);
+	return ret;
+}
 
-	*inbuf = (const unsigned char *) ic_inbuf;
-	*outsize -= outleft;
+static const char *alloc_str_ucase_utf8(const Buffer *data, size_t *utf8_size)
+{
+	const char *buf;
+	size_t size;
+	Buffer *dest;
 
-	max_size = *outsize;
-	for (i = 0; i < max_size; i++)
-		outbuf[i] = i_toupper(outbuf[i]); /* FIXME: utf8 */
+	buf = buffer_get_data(data, &size);
 
-	return TRUE;
+	dest = buffer_create_dynamic(data_stack_pool, size, (size_t)-1);
+	str_ucase_utf8(buf, size, dest, 0);
+	if (utf8_size != NULL)
+		*utf8_size = buffer_get_used_size(dest);
+	buffer_append_c(dest, '\0');
+	return buffer_free_without_data(dest);
 }
 
 const char *
 charset_to_ucase_utf8_string(const char *charset, int *unknown_charset,
-			     const unsigned char *buf, size_t *size)
+			     const Buffer *data, size_t *utf8_size)
 {
 	iconv_t cd;
 	ICONV_CONST char *inbuf;
@@ -116,12 +155,10 @@
 	size_t inleft, outleft, outsize, pos;
 
 	if (charset == NULL || strcasecmp(charset, "us-ascii") == 0 ||
-	    strcasecmp(charset, "ascii") == 0) {
-		outbuf = t_malloc(*size + 1);
-		memcpy(outbuf, buf, *size);
-		outbuf[*size] = '\0';
-		return str_ucase(outbuf); /* FIXME: utf8 */
-	}
+	    strcasecmp(charset, "ascii") == 0 ||
+	    strcasecmp(charset, "UTF-8") == 0 ||
+	    strcasecmp(charset, "UTF8") == 0)
+	       return alloc_str_ucase_utf8(data, utf8_size);
 
 	cd = iconv_open("UTF-8", charset);
 	if (cd == (iconv_t)-1) {
@@ -133,10 +170,9 @@
 	if (unknown_charset != NULL)
 		*unknown_charset = FALSE;
 
-	inbuf = (ICONV_CONST char *) buf;
-	inleft = *size;
+	inbuf = (ICONV_CONST char *) buffer_get_data(data, &inleft);;
 
-	outsize = outleft = *size * 2;
+	outsize = outleft = inleft * 2;
 	outbuf = outpos = t_buffer_get(outsize + 1);
 
 	while (iconv(cd, &inbuf, &inleft, &outpos, &outleft) == (size_t)-1) {
@@ -155,9 +191,10 @@
 		outpos = outbuf + pos;
 	}
 
-	*size = (size_t) (outpos - outbuf);
+	if (utf8_size != NULL)
+		*utf8_size = (size_t) (outpos - outbuf);
 	*outpos++ = '\0';
-	t_buffer_alloc(*size + 1);
+	t_buffer_alloc((size_t) (outpos - outbuf));
 
 	str_ucase(outbuf); /* FIXME: utf8 */
 
--- a/src/lib-charset/charset-utf8.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-charset/charset-utf8.h	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,13 @@
 #ifndef __CHARSET_UTF8_H
 #define __CHARSET_UTF8_H
 
+typedef enum {
+	CHARSET_RET_OK = 1,
+	CHARSET_RET_OUTPUT_FULL = 0,
+	CHARSET_RET_INCOMPLETE_INPUT = -1,
+	CHARSET_RET_INVALID_INPUT = -2
+} CharsetResult;
+
 typedef struct _CharsetTranslation CharsetTranslation;
 
 /* Begin translation to UTF-8. */
@@ -11,18 +18,16 @@
 
 void charset_to_utf8_reset(CharsetTranslation *t);
 
-/* Convert inbuf to UTF-8. inbuf and inbuf_size is updated to specify beginning
-   of data that was not written to outbuf, either because of inbuf ended with
-   incomplete character sequence or because the outbuf got full. Returns TRUE
-   if no conversion errors were detected. */
-int charset_to_ucase_utf8(CharsetTranslation *t,
-			  const unsigned char **inbuf, size_t *insize,
-			  unsigned char *outbuf, size_t *outsize);
+/* Translate src to UTF-8. If src_pos is non-NULL, it's updated to first
+   non-translated character in src. */
+CharsetResult
+charset_to_ucase_utf8(CharsetTranslation *t,
+		      const Buffer *src, size_t *src_pos, Buffer *dest);
 
-/* Simple wrapper for above functions. size is updated to strlen() of
-   returned UTF-8 string. */
+/* Simple wrapper for above functions. If utf8_size is non-NULL, it's set
+   to same as strlen(returned data). */
 const char *
 charset_to_ucase_utf8_string(const char *charset, int *unknown_charset,
-			     const unsigned char *buf, size_t *size);
+			     const Buffer *data, size_t *utf8_size);
 
 #endif
--- a/src/lib-index/mail-custom-flags.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/mail-custom-flags.c	Sun Dec 08 07:23:07 2002 +0200
@@ -40,7 +40,7 @@
 static int lock_file(MailCustomFlags *mcf, int type);
 
 static int index_cf_set_syscall_error(MailCustomFlags *mcf,
-				       const char *function)
+				      const char *function)
 {
 	i_assert(function != NULL);
 
--- a/src/lib-index/mail-index-update.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/mail-index-update.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "istream.h"
 #include "ioloop.h"
 #include "rfc822-date.h"
@@ -130,30 +131,27 @@
         MailIndexDataRecordHeader *dest_hdr;
         MailIndexDataRecord *rec, *destrec;
 	MailDataField field;
-	void *mem;
+	Buffer *buf;
 	const void *src;
-	size_t pos, src_size;
+	size_t src_size;
+	size_t full_field_size;
 	int i;
 
 	i_assert(data_size <= UINT_MAX);
 
-	mem = p_malloc(update->pool, data_size);
+	buf = buffer_create_static_hard(update->pool, data_size);
 
 	/* set header */
-	dest_hdr = (MailIndexDataRecordHeader *) mem;
-	pos = sizeof(MailIndexDataRecordHeader);
-
+	dest_hdr = buffer_append_space(buf, sizeof(*dest_hdr));
 	memcpy(dest_hdr, &update->data_hdr, sizeof(*dest_hdr));
 	dest_hdr->data_size = data_size;
 
 	/* set fields */
 	rec = mail_index_data_lookup(update->index->data, update->rec, 0);
 	for (i = 0, field = 1; field != DATA_FIELD_LAST; i++, field <<= 1) {
-		destrec = (MailIndexDataRecord *) ((char *) mem + pos);
-
 		if (update->fields[i] != NULL) {
 			/* value was modified - use it */
-			destrec->full_field_size =
+			full_field_size =
 				get_max_align_size(update->field_sizes[i],
 						   update->field_extra_sizes[i],
 						   &extra_size);
@@ -161,34 +159,28 @@
 			src_size = update->field_sizes[i];
 		} else if (rec != NULL && rec->field == field) {
 			/* use the old value */
-			destrec->full_field_size = rec->full_field_size;
+			full_field_size = rec->full_field_size;
 			src = rec->data;
-			src_size = destrec->full_field_size;
+			src_size = rec->full_field_size;
 		} else {
 			/* the field doesn't exist, jump to next */
 			continue;
 		}
-		i_assert((destrec->full_field_size % MEM_ALIGN_SIZE) == 0);
+		i_assert((full_field_size % MEM_ALIGN_SIZE) == 0);
 
-		/* make sure we don't overflow our buffer */
-		if (src_size > data_size || data_size - src_size < pos) {
-			i_panic("data file for index %s unexpectedly modified",
-				update->index->filepath);
-		}
+		destrec = buffer_append_space(buf, SIZEOF_MAIL_INDEX_DATA +
+					      full_field_size);
+		destrec->field = field;
+		destrec->full_field_size = full_field_size;
 		memcpy(destrec->data, src, src_size);
 
-		destrec->field = field;
-		pos += DATA_RECORD_SIZE(destrec);
-
 		if (rec != NULL && rec->field == field) {
 			rec = mail_index_data_next(update->index->data,
 						   update->rec, rec);
 		}
 	}
 
-	i_assert(pos <= data_size);
-
-	return mem;
+	return buffer_free_without_data(buf);
 }
 
 /* Append all the data at the end of the data file and update 
@@ -418,6 +410,7 @@
 	MessagePart *part;
 	MessageSize hdr_size, body_size;
 	Pool pool;
+	Buffer *buf;
 	const char *value;
 	size_t size;
 	uoff_t start_offset;
@@ -497,7 +490,11 @@
 
 		if (cache_fields & DATA_FIELD_MESSAGEPART) {
 			t_push();
-			value = message_part_serialize(part, &size);
+			buf = buffer_create_dynamic(data_stack_pool, 2048,
+						    (size_t)-1);
+			message_part_serialize(part, buf);
+
+			value = buffer_get_data(buf, &size);
 			update->index->update_field_raw(update,
 							DATA_FIELD_MESSAGEPART,
 							value, size);
--- a/src/lib-index/mail-modifylog.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/mail-modifylog.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "file-lock.h"
 #include "file-set-size.h"
 #include "mmap-util.h"
@@ -1035,7 +1036,9 @@
 				unsigned int *expunges_before)
 {
 	ModifyLogRecord *rec;
-	ModifyLogExpunge *expunges, *arr;
+	ModifyLogExpunge *expunge;
+	Buffer *buf;
+	size_t count;
 	unsigned int before, max_records;
 
 	i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
@@ -1067,7 +1070,9 @@
 	if (max_records > last_seq - first_seq + 1)
 		max_records = last_seq - first_seq + 1;
 
-	expunges = arr = t_malloc((max_records+1) * sizeof(ModifyLogExpunge));
+	i_assert((max_records+1) < SSIZE_T_MAX/sizeof(ModifyLogExpunge));
+	buf = buffer_create_static_hard(data_stack_pool, (max_records+1) *
+					sizeof(ModifyLogExpunge));
 
 	before = 0;
 	for (; rec != NULL; rec = modifylog_next(log, rec)) {
@@ -1087,18 +1092,19 @@
 				return NULL;
 			}
 
+			expunge = buffer_append_space(buf, sizeof(*expunge));
+
 			if (rec->seq1 < first_seq) {
 				/* partial initial match, update
 				   before-counter */
 				before += first_seq - rec->seq1;
-				arr->seq_count = rec->seq2 - first_seq + 1;
+				expunge->seq_count = rec->seq2 - first_seq + 1;
 			} else {
-				arr->seq_count = rec->seq2 - rec->seq1 + 1;
+				expunge->seq_count = rec->seq2 - rec->seq1 + 1;
 			}
 
-			arr->uid1 = rec->uid1;
-			arr->uid2 = rec->uid2;
-			arr++;
+			expunge->uid1 = rec->uid1;
+			expunge->uid2 = rec->uid2;
 		}
 
 		if (rec->seq1 <= last_seq) {
@@ -1113,14 +1119,19 @@
 		}
 	}
 
-	arr->uid1 = arr->uid2 = arr->seq_count = 0;
+	/* terminate the array */
+	expunge = buffer_append_space(buf, sizeof(*expunge));
+	memset(expunge, 0, sizeof(*expunge));
+
+	/* extract the array from buffer */
+	count = buffer_get_used_size(buf)/sizeof(ModifyLogExpunge);
+	expunge = buffer_free_without_data(buf);
 
 	/* sort the UID array, not including the terminating 0 */
-	qsort(expunges, (unsigned int) (arr - expunges),
-	      sizeof(ModifyLogExpunge), compare_expunge);
+	qsort(expunge, count-1, sizeof(ModifyLogExpunge), compare_expunge);
 
 	*expunges_before = before;
-	return expunges;
+	return expunge;
 }
 
 const ModifyLogExpunge *
@@ -1132,7 +1143,9 @@
 	/* pretty much copy&pasted from sequence code above ..
 	   kind of annoying */
 	ModifyLogRecord *rec;
-	ModifyLogExpunge *expunges, *arr;
+	ModifyLogExpunge *expunge;
+	Buffer *buf;
+	size_t count;
 	unsigned int before, max_records;
 
 	i_assert(log->index->lock_type != MAIL_LOCK_UNLOCK);
@@ -1164,7 +1177,9 @@
 	if (max_records > last_uid - first_uid + 1)
 		max_records = last_uid - first_uid + 1;
 
-	expunges = arr = t_malloc((max_records+1) * sizeof(ModifyLogExpunge));
+	i_assert((max_records+1) < SSIZE_T_MAX/sizeof(ModifyLogExpunge));
+	buf = buffer_create_static_hard(data_stack_pool, (max_records+1) *
+					sizeof(ModifyLogExpunge));
 
 	before = 0;
 	for (; rec != NULL; rec = modifylog_next(log, rec)) {
@@ -1184,21 +1199,27 @@
 				return NULL;
 			}
 
-			arr->uid1 = rec->uid1;
-			arr->uid2 = rec->uid2;
-			arr->seq_count = rec->seq2 -rec->seq1 + 1;
-			arr++;
+			expunge = buffer_append_space(buf, sizeof(*expunge));
+
+			expunge->uid1 = rec->uid1;
+			expunge->uid2 = rec->uid2;
+			expunge->seq_count = rec->seq2 -rec->seq1 + 1;
 		}
 	}
 
-	arr->uid1 = arr->uid2 = arr->seq_count = 0;
+	/* terminate the array */
+	expunge = buffer_append_space(buf, sizeof(*expunge));
+	memset(expunge, 0, sizeof(*expunge));
+
+	/* extract the array from buffer */
+	count = buffer_get_used_size(buf)/sizeof(ModifyLogExpunge);
+	expunge = buffer_free_without_data(buf);
 
 	/* sort the UID array, not including the terminating 0 */
-	qsort(expunges, (unsigned int) (arr - expunges),
-	      sizeof(ModifyLogExpunge), compare_expunge);
+	qsort(expunge, count-1, sizeof(ModifyLogExpunge), compare_expunge);
 
 	*expunges_before = before;
-	return expunges;
+	return expunge;
 }
 
 static unsigned int modifylog_file_get_expunge_count(ModifyLogFile *file)
--- a/src/lib-index/maildir/maildir-index.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/maildir/maildir-index.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "temp-string.h"
 #include "maildir-index.h"
 #include "mail-index-data.h"
 #include "mail-index-util.h"
@@ -55,8 +56,8 @@
 
 const char *maildir_filename_set_flags(const char *fname, MailFlags flags)
 {
+	TempString *flags_str;
 	const char *info, *oldflags;
-	char *flags_buf, *p;
 	int i, nextflag;
 
 	/* remove the old :info from file name, and get the old flags */
@@ -73,9 +74,8 @@
 
 	/* insert the new flags between old flags. flags must be sorted by
 	   their ASCII code. unknown flags are kept. */
-	flags_buf = t_malloc(MAIL_FLAGS_COUNT+strlen(oldflags)+1);
-	p = flags_buf;
-
+	flags_str = t_string_new(MAIL_FLAGS_COUNT+strlen(oldflags)+4);
+	t_string_append(flags_str, ":2,");
 	for (;;) {
 		/* skip all known flags */
 		while (*oldflags == 'D' || *oldflags == 'F' ||
@@ -88,30 +88,30 @@
 			(unsigned char) *oldflags;
 
 		if ((flags & MAIL_DRAFT) && nextflag > 'D') {
-			*p++ = 'D';
+			t_string_append_c(flags_str, 'D');
 			flags &= ~MAIL_DRAFT;
 		}
 		if ((flags & MAIL_FLAGGED) && nextflag > 'F') {
-			*p++ = 'F';
+			t_string_append_c(flags_str, 'F');
 			flags &= ~MAIL_FLAGGED;
 		}
 		if ((flags & MAIL_ANSWERED) && nextflag > 'R') {
-			*p++ = 'R';
+			t_string_append_c(flags_str, 'R');
 			flags &= ~MAIL_ANSWERED;
 		}
 		if ((flags & MAIL_SEEN) && nextflag > 'S') {
-			*p++ = 'S';
+			t_string_append_c(flags_str, 'S');
 			flags &= ~MAIL_SEEN;
 		}
 		if ((flags & MAIL_DELETED) && nextflag > 'T') {
-			*p++ = 'T';
+			t_string_append_c(flags_str, 'T');
 			flags &= ~MAIL_DELETED;
 		}
 
 		if ((flags & MAIL_CUSTOM_FLAGS_MASK) && nextflag > 'a') {
 			for (i = 0; i < MAIL_CUSTOM_FLAGS_COUNT; i++) {
 				if (flags & (1 << (i + MAIL_CUSTOM_FLAG_1_BIT)))
-					*p++ = 'a' + i;
+					t_string_append_c(flags_str, 'a' + i);
 			}
 			flags &= ~MAIL_CUSTOM_FLAGS_MASK;
 		}
@@ -119,18 +119,16 @@
 		if (*oldflags == '\0' || *oldflags == ',')
 			break;
 
-		*p++ = *oldflags++;
+		t_string_append_c(flags_str, *oldflags);
 	}
 
 	if (*oldflags == ',') {
 		/* another flagset, we don't know about these, just keep them */
 		while (*oldflags != '\0')
-			*p++ = *oldflags++;
+			t_string_append_c(flags_str, *oldflags++);
 	}
 
-	*p = '\0';
-
-	return t_strconcat(fname, ":2,", flags_buf, NULL);
+	return flags_str->str;
 }
 
 MailIndex *maildir_index_alloc(const char *dir)
--- a/src/lib-index/mbox/mbox-index.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/mbox/mbox-index.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "istream.h"
 #include "rfc822-tokenize.h"
 #include "mbox-index.h"
@@ -13,6 +14,9 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 
+/* Don't try reading more custom flags than this. */
+#define MAX_CUSTOM_FLAGS 1024
+
 extern MailIndex mbox_index;
 
 int mbox_set_syscall_error(MailIndex *index, const char *function)
@@ -189,10 +193,12 @@
 static int mbox_parse_imapbase(const char *value, size_t len,
 			       MboxHeaderContext *ctx)
 {
-	const char **custom_flags, **old_flags;
+	const char **flag;
+	Buffer *buf;
 	size_t pos, start;
 	MailFlags flags;
-	int idx, ret, spaces, max;
+	unsigned int count;
+	int ret, spaces;
 
 	/* skip <uid validity> and <last uid> fields */
 	spaces = 0;
@@ -211,22 +217,16 @@
 	t_push();
 
 	/* we're at the 3rd field now, which begins the list of custom flags */
-	max = MAIL_CUSTOM_FLAGS_COUNT;
-	custom_flags = t_new(const char *, max);
-	for (idx = 0, start = pos; ; pos++) {
+	buf = buffer_create_dynamic(data_stack_pool,
+				    MAIL_CUSTOM_FLAGS_COUNT, MAX_CUSTOM_FLAGS);
+	for (start = pos; ; pos++) {
 		if (pos == len || value[pos] == ' ' || value[pos] == '\t') {
 			if (start != pos) {
-				if (idx == max) {
-					/* need more memory */
-					old_flags = custom_flags;
-					max *= 2;
-					custom_flags = t_new(const char *, max);
-					memcpy(custom_flags, old_flags,
-					       sizeof(const char *) * idx);
-				}
+				flag = buffer_append_space(buf, sizeof(*flag));
+				if (flag == NULL)
+					break;
 
-				custom_flags[idx++] =
-					t_strdup_until(value+start, value+pos);
+				*flag = t_strdup_until(value+start, value+pos);
 			}
 			start = pos+1;
 
@@ -236,8 +236,10 @@
 	}
 
 	flags = MAIL_CUSTOM_FLAGS_MASK;
+	count = buffer_get_used_size(buf) / sizeof(const char *);
+	flag = buffer_free_without_data(buf);
 	ret = mail_custom_flags_fix_list(ctx->index->custom_flags, &flags,
-					 custom_flags, idx);
+					 flag, count);
 
 	t_pop();
 
--- a/src/lib-index/mbox/mbox-rewrite.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-index/mbox/mbox-rewrite.c	Sun Dec 08 07:23:07 2002 +0200
@@ -201,7 +201,7 @@
 {
 	/* leave only unknown flags, very likely none */
 	char *ret, *p;
-	unsigned int i;
+	size_t i;
 
 	ret = p = t_buffer_get(value_len+1);
 	for (i = 0; i < value_len; i++) {
--- a/src/lib-mail/message-body-search.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-body-search.c	Sun Dec 08 07:23:07 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "base64.h"
+#include "buffer.h"
 #include "istream.h"
 #include "charset-utf8.h"
 #include "rfc822-tokenize.h"
@@ -30,14 +31,13 @@
 	HeaderSearchContext *hdr_search_ctx;
 	CharsetTranslation *translation;
 
-	unsigned char decode_buf[DECODE_BLOCK_SIZE];
-	size_t decode_buf_used;
+	Buffer *decode_buf;
 
 	size_t *matches;
 	ssize_t match_count;
 
-	const char *content_type;
-	const char *content_charset;
+	char *content_type;
+	char *content_charset;
 
 	unsigned int content_qp:1;
 	unsigned int content_base64:1;
@@ -53,7 +53,8 @@
 	PartSearchContext *ctx = context;
 
 	if (ctx->content_type != NULL && tokens[0].token == 'A') {
-		ctx->content_type = rfc822_tokens_get_value(tokens, count);
+		ctx->content_type =
+			i_strdup(rfc822_tokens_get_value(tokens, count));
 		ctx->content_type_text =
 			strncasecmp(ctx->content_type, "text/", 5) == 0 ||
 			strncasecmp(ctx->content_type, "message/", 8) == 0;
@@ -71,7 +72,7 @@
 
 	if (ctx->content_charset == NULL) {
 		ctx->content_charset =
-			rfc822_tokens_get_value(value, value_count);
+			i_strdup(rfc822_tokens_get_value(value, value_count));
 	}
 }
 
@@ -117,10 +118,12 @@
 		return;
 
 	if (!ctx->ignore_header) {
-		ctx->found = message_header_search(value, &value_len,
+		ctx->found = message_header_search(value, value_len,
 						   ctx->hdr_search_ctx);
 	}
 
+	t_push();
+
 	if (name_len == 12 && strncasecmp(name, "Content-Type", 12) == 0) {
 		(void)message_content_parse_header(t_strndup(value, value_len),
 						   parse_content_type,
@@ -132,6 +135,8 @@
 						   parse_content_encoding,
 						   NULL, ctx);
 	}
+
+	t_pop();
 }
 
 static int message_search_header(PartSearchContext *ctx, IStream *input)
@@ -148,26 +153,24 @@
 	return ctx->found;
 }
 
-static int message_search_decoded_block(PartSearchContext *ctx,
-					const unsigned char *data, size_t size)
+static int message_search_decoded_block(PartSearchContext *ctx, Buffer *block)
 {
 	const unsigned char *p, *end, *key;
-	size_t key_len;
+	size_t key_len, block_size;
 	ssize_t i;
-	int found;
 
 	key = (const unsigned char *) ctx->body_ctx->key;
 	key_len = ctx->body_ctx->key_len;
 
-	end = data + size; found = 0;
-	for (p = data; p != end; p++) {
+	p = buffer_get_data(block, &block_size);
+	end = p + block_size;
+	for (; p != end; p++) {
 		for (i = ctx->match_count-1; i >= 0; i--) {
 			if (key[ctx->matches[i]] == *p) {
 				if (++ctx->matches[i] == key_len) {
 					/* full match */
 					p++;
-					found = TRUE;
-					break;
+					return TRUE;
 				}
 			} else {
 				/* non-match */
@@ -180,73 +183,75 @@
 			}
 		}
 
-		if (found)
-			break;
-
 		if (*p == key[0]) {
 			if (key_len == 1) {
 				/* only one character in search key */
 				p++;
-				found = 1;
-				break;
+				return TRUE;
 			}
+
 			i_assert((size_t)ctx->match_count < key_len);
 			ctx->matches[ctx->match_count++] = 1;
 		}
 	}
 
-	return found;
+	return FALSE;
 }
 
-static int message_search_body_block(PartSearchContext *ctx,
-				     const unsigned char *data, size_t size)
+/* returns 1 = found, 0 = not found, -1 = error in input data */
+static int message_search_body_block(PartSearchContext *ctx, Buffer *block)
 {
-	const unsigned char *inbuf;
-	unsigned char outbuf[DECODE_BLOCK_SIZE];
-	size_t inbuf_size, outbuf_size, max_size;
+	Buffer *inbuf, *outbuf;
+        CharsetResult result;
+	size_t block_pos, inbuf_pos, inbuf_left, ret;
 
-	while (size > 0) {
-		if (ctx->decode_buf_used == 0) {
-			inbuf = data;
-			inbuf_size = I_MIN(size, sizeof(ctx->decode_buf));
-
-			data += inbuf_size;
-			size -= inbuf_size;
+	outbuf = buffer_create_static(data_stack_pool, DECODE_BLOCK_SIZE);
+	for (block_pos = 0; block_pos < buffer_get_used_size(block); ) {
+		if (buffer_get_used_size(ctx->decode_buf) == 0) {
+			/* we can use the buffer directly without copying */
+			inbuf = block;
+			inbuf_pos = block_pos;
+			block_pos += buffer_get_used_size(block);
 		} else {
 			/* some characters already in buffer, ie. last
 			   conversion contained partial data */
-			max_size = sizeof(ctx->decode_buf) -
-				ctx->decode_buf_used;
-			if (max_size > size)
-				max_size = size;
-
-			memcpy(ctx->decode_buf + ctx->decode_buf_used,
-			       data, max_size);
-			ctx->decode_buf_used += max_size;
+			block_pos += buffer_append_buf(ctx->decode_buf,
+						       block, block_pos,
+						       (size_t)-1);
 
 			inbuf = ctx->decode_buf;
-			inbuf_size = ctx->decode_buf_used;
-
-			data += max_size;
-			size -= max_size;
+			inbuf_pos = 0;
 		}
 
-		outbuf_size = sizeof(outbuf);
-		if (!charset_to_ucase_utf8(ctx->translation,
-					   &inbuf, &inbuf_size,
-					   outbuf, &outbuf_size)) {
-			/* something failed */
+		buffer_set_used_size(outbuf, 0);
+		result = charset_to_ucase_utf8(ctx->translation,
+					       inbuf, &inbuf_pos, outbuf);
+		inbuf_left = buffer_get_used_size(inbuf) - inbuf_pos;
+
+		switch (result) {
+		case CHARSET_RET_OUTPUT_FULL:
+			/* we should have copied the incomplete sequence.. */
+			i_assert(inbuf_left <= block_pos);
+			/* fall through */
+		case CHARSET_RET_OK:
+			buffer_set_used_size(ctx->decode_buf, 0);
+			block_pos -= inbuf_left;
+			break;
+		case CHARSET_RET_INCOMPLETE_INPUT:
+			/* save the partial sequence to buffer */
+			ret = buffer_copy(ctx->decode_buf, 0,
+					  inbuf, inbuf_pos, inbuf_left);
+			i_assert(ret == inbuf_left);
+
+			buffer_set_used_size(ctx->decode_buf, ret);
+			break;
+
+		case CHARSET_RET_INVALID_INPUT:
 			return -1;
 		}
 
-		if (message_search_decoded_block(ctx, outbuf, outbuf_size))
+		if (message_search_decoded_block(ctx, outbuf))
 			return 1;
-
-		if (inbuf_size > 0) {
-			/* partial input, save it */
-			memmove(ctx->decode_buf, inbuf, inbuf_size);
-			ctx->decode_buf_used = inbuf_size;
-		}
 	}
 
 	return 0;
@@ -255,9 +260,9 @@
 static int message_search_body(PartSearchContext *ctx, IStream *input,
 			       MessagePart *part)
 {
-	const unsigned char *data, *decoded;
-	unsigned char *decodebuf;
-	size_t data_size, decoded_size, pos;
+	const unsigned char *data;
+	Buffer *decodebuf;
+	size_t data_size, pos;
 	uoff_t old_limit;
 	ssize_t ret;
 	int found;
@@ -277,6 +282,8 @@
 	if (ctx->translation == NULL)
 		ctx->translation = charset_to_utf8_begin("ascii", NULL);
 
+	ctx->decode_buf = buffer_create_static(data_stack_pool, 256);
+
 	ctx->match_count = 0;
 	ctx->matches = t_malloc(sizeof(size_t) * ctx->body_ctx->key_len);
 
@@ -296,28 +303,35 @@
 
 		t_push();
 		if (ctx->content_qp) {
-			decoded = decodebuf = t_malloc(data_size);
-			decoded_size = quoted_printable_decode(data, &data_size,
-							       decodebuf);
+			decodebuf = buffer_create_static_hard(data_stack_pool,
+							      data_size);
+			quoted_printable_decode(data, data_size,
+						&data_size, decodebuf);
 		} else if (ctx->content_base64) {
-			decoded_size = MAX_BASE64_DECODED_SIZE(data_size);
-			decoded = decodebuf = t_malloc(decoded_size);
+			size_t size = MAX_BASE64_DECODED_SIZE(data_size);
+			decodebuf = buffer_create_static_hard(data_stack_pool,
+							      size);
 
-			ret = base64_decode(data, &data_size, decodebuf);
-			decoded_size = ret < 0 ? 0 : (size_t)decoded_size;
+			if (base64_decode(data, data_size,
+					  &data_size, decodebuf) < 0) {
+				/* corrupted base64 data, don't bother with
+				   the rest of it */
+				t_pop();
+				break;
+			}
 		} else {
-			decoded = data;
-			decoded_size = data_size;
+			decodebuf = buffer_create_const_data(data_stack_pool,
+							     data, data_size);
 		}
 
-		ret = message_search_body_block(ctx, decoded, decoded_size);
+		ret = message_search_body_block(ctx, decodebuf);
+		t_pop();
+
 		if (ret != 0) {
-			t_pop();
 			found = ret > 0;
 			break;
 		}
 
-		t_pop();
 		i_stream_skip(input, data_size);
 		pos -= data_size;
 	}
@@ -333,25 +347,26 @@
 				    const char *charset, int *unknown_charset,
 				    int search_header)
 {
-	size_t size;
+	Buffer *keybuf;
+	size_t key_len;
 
 	memset(ctx, 0, sizeof(BodySearchContext));
 
 	/* get the key uppercased */
-	size = strlen(key);
+        keybuf = buffer_create_const_data(data_stack_pool, key, strlen(key));
 	key = charset_to_ucase_utf8_string(charset, unknown_charset,
-					   (const unsigned char *) key, &size);
+					   keybuf, &key_len);
 	if (key == NULL)
 		return FALSE;
 
-	i_assert(size <= SSIZE_T_MAX/sizeof(size_t));
-
 	ctx->key = key;
-	ctx->key_len = size;
+	ctx->key_len = key_len;
 	ctx->charset = charset;
 	ctx->unknown_charset = charset == NULL;
 	ctx->search_header = search_header;
 
+	i_assert(ctx->key_len <= SSIZE_T_MAX/sizeof(size_t));
+
 	return TRUE;
 }
 
@@ -385,6 +400,9 @@
 				found = TRUE;
 		}
 
+		i_free(part_ctx.content_type);
+		i_free(part_ctx.content_charset);
+
 		t_pop();
 
 		part = part->next;
--- a/src/lib-mail/message-header-search.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-header-search.c	Sun Dec 08 07:23:07 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "base64.h"
+#include "buffer.h"
 #include "charset-utf8.h"
 #include "rfc822-tokenize.h"
 #include "quoted-printable.h"
@@ -19,9 +20,9 @@
 	size_t *matches; /* size of strlen(key) */
 	ssize_t match_count;
 
+	unsigned int found:1;
 	unsigned int last_newline:1;
 	unsigned int submatch:1;
-	unsigned int eoh:1;
 	unsigned int key_ascii:1;
 	unsigned int unknown_charset:1;
 };
@@ -31,35 +32,39 @@
 			   int *unknown_charset)
 {
 	HeaderSearchContext *ctx;
+	Buffer *keybuf;
+	size_t key_len;
 	const char *p;
-	size_t size;
 
 	ctx = p_new(pool, HeaderSearchContext, 1);
 	ctx->pool = pool;
 
 	/* get the key uppercased */
-	size = strlen(key);
+	keybuf = buffer_create_const_data(data_stack_pool, key, strlen(key));
 	key = charset_to_ucase_utf8_string(charset, unknown_charset,
-					   (const unsigned char *) key, &size);
-	if (key == NULL)
+					   keybuf, &key_len);
+
+	if (key == NULL) {
+		/* invalid key */
+		t_pop();
 		return NULL;
-
-	i_assert(size <= SSIZE_T_MAX/sizeof(size_t));
+	}
 
 	ctx->key = p_strdup(pool, key);
-	ctx->key_len = size;
+	ctx->key_len = key_len;
 	ctx->key_charset = p_strdup(pool, charset);
 	ctx->unknown_charset = charset == NULL;
 
 	ctx->key_ascii = TRUE;
-	for (p = key; *p != '\0'; p++) {
+	for (p = ctx->key; *p != '\0'; p++) {
 		if ((*p & 0x80) != 0) {
 			ctx->key_ascii = FALSE;
 			break;
 		}
 	}
 
-	ctx->matches = p_malloc(pool, sizeof(size_t) * ctx->key_len);
+	i_assert(ctx->key_len <= SSIZE_T_MAX/sizeof(size_t));
+	ctx->matches = p_new(pool, size_t, ctx->key_len);
 	return ctx;
 }
 
@@ -74,9 +79,11 @@
 	p_free(pool, ctx);
 }
 
-static int match_data(const unsigned char *data, size_t size,
-		      const char *charset, HeaderSearchContext *ctx)
+static int match_data(const Buffer *buffer, const char *charset,
+		      HeaderSearchContext *ctx)
 {
+	const char *utf8_data;
+	size_t size;
 	int ret;
 
 	if (ctx->unknown_charset) {
@@ -89,174 +96,162 @@
 		charset = ctx->key_charset;
 	}
 
-	data = (const unsigned char *)
-		charset_to_ucase_utf8_string(charset, NULL, data, &size);
-	if (data == NULL) {
+	utf8_data = charset_to_ucase_utf8_string(charset, NULL, buffer, &size);
+	if (utf8_data == NULL) {
 		/* unknown character set, or invalid data */
 		return FALSE;
 	}
 
 	ctx->submatch = TRUE;
-	ret = message_header_search(data, &size, ctx);
+	ret = message_header_search(utf8_data, size, ctx);
 	ctx->submatch = FALSE;
 
 	return ret;
 }
 
-static int match_encoded(const unsigned char **start, const unsigned char *end,
+static int split_encoded(Buffer *buffer, size_t *last_pos,
+			 const char **charset, const char **encoding)
+{
+	const char *p;
+	size_t size, pos, textpos;
+
+	p = buffer_get_data(buffer, &size);
+
+	/* get charset */
+	for (pos = 0; pos < size && p[pos] != '?'; pos++) ;
+	if (p[pos] != '?') return FALSE;
+	*charset = t_strndup(p, pos);
+
+	/* get encoding */
+	pos++;
+	if (pos+2 >= size || p[pos+1] != '?')
+		return FALSE;
+
+	if (p[pos] == 'Q' || p[pos] == 'q')
+		*encoding = "Q";
+	else if (p[pos] == 'B' || p[pos] == 'b')
+		*encoding = "B";
+	else
+		return FALSE;
+
+	/* get text */
+	pos += 2;
+	textpos = pos;
+	for (; pos < size && p[pos] != '?'; pos++) ;
+	if (p[pos] != '?' || pos+1 >= size || p[pos+1] != '=') return FALSE;
+
+	buffer_set_limit(buffer, pos);
+	buffer_set_start_pos(buffer, textpos + buffer_get_start_pos(buffer));
+	*last_pos = pos+1;
+
+	return TRUE;
+}
+
+static int match_encoded(Buffer *buffer, size_t *last_pos,
 			 HeaderSearchContext *ctx)
 {
-	const unsigned char *p, *encoding, *text, *new_end;
-	const char *charset;
-	unsigned char *buf;
-	ssize_t size;
-	size_t buf_size;
-	int ok, ret;
+	const char *charset, *encoding, *text;
+	Buffer *decodebuf;
+	size_t textsize;
+
+	/* first split the string charset?encoding?text?= */
+	if (!split_encoded(buffer, last_pos, &charset, &encoding)) {
+		ctx->match_count = 0;
+		return FALSE;
+	}
 
-	/* first split the string =?charset?encoding?text?= */
-	ok = FALSE;
-	charset = (const char *) *start; encoding = NULL; text = NULL;
-	for (p = *start; p != end; p++) {
-		if (*p == '?') {
-			if (encoding == NULL) {
-				charset = t_strdup_until(charset, p);
-				encoding = p+1;
-			} else if (text == NULL) {
-				if (p != encoding+1)
-					encoding = "?";
-				else if (*encoding == 'Q' || *encoding == 'q')
-					encoding = "Q";
-				else if (*encoding == 'B' || *encoding == 'b')
-					encoding = "B";
-				else
-					encoding = "?";
+	/* buffer is now limited to only the text portion */
+	text = buffer_get_data(buffer, &textsize);
+	decodebuf = buffer_create_static_hard(data_stack_pool, textsize);
 
-				text = p+1;
-			} else {
-				new_end = p;
-
-				p++;
-				if (p != end && *p == '=')
-					p++;
-
-				end = new_end;
-				*start = p-1;
-				ok = TRUE;
-				break;
-			}
+	if (*encoding == 'Q')
+		quoted_printable_decode(text, textsize, NULL, decodebuf);
+	else {
+		if (base64_decode(text, textsize, NULL, decodebuf) < 0) {
+			/* corrupted encoding */
+			ctx->match_count = 0;
+			return FALSE;
 		}
 	}
 
-	if (ok && *encoding != '?') {
-		t_push();
-
-		size = (ssize_t) (end - text);
-
-		buf_size = (size_t)size;
-		buf = t_malloc(buf_size);
-
-		if (*encoding == 'Q')
-			size = quoted_printable_decode(text, &buf_size, buf);
-		else
-			size = base64_decode(text, &buf_size, buf);
-
-		if (size >= 0) {
-			/* non-corrupted encoding */
-			ret = match_data(buf, (size_t)size, charset, ctx);
-			t_pop();
-			return ret;
-		}
-
-		t_pop();
-	}
-
-	/* non-supported encoding, we can't match it */
-	ctx->match_count = 0;
-	return FALSE;
+	return match_data(decodebuf, charset, ctx);
 }
 
-int message_header_search(const unsigned char *header_block,
-			  size_t *header_size, HeaderSearchContext *ctx)
+int message_header_search(const unsigned char *header_block, size_t size,
+			  HeaderSearchContext *ctx)
 {
-	const unsigned char *p, *end;
+	Buffer *buf;
+	ssize_t i;
+	size_t pos, subpos;
 	unsigned char chr;
-	ssize_t i;
-	int found;
+	int last_newline, ret;
 
-	if (ctx->eoh || *header_size == 0)
-		return FALSE;
+	if (ctx->found)
+		return TRUE;
+
+	t_push();
+	buf = buffer_create_const_data(data_stack_pool, header_block, size);
 
-	end = header_block + *header_size;
+	last_newline = ctx->last_newline;
+	for (pos = 0; pos < size; pos++) {
+		chr = header_block[pos];
 
-	found = FALSE;
-	for (p = header_block; p != end; p++) {
-		if (p[0] == '=' && p+1 != end && p[1] == '?' &&
-		    !ctx->submatch) {
+		if (chr == '=' && pos+1 < size &&
+		    header_block[pos+1] == '?' && !ctx->submatch) {
 			/* encoded string. read it. */
-			p += 2;
+                        buffer_set_start_pos(buf, pos+2);
+
 			t_push();
-			if (match_encoded(&p, end, ctx)) {
-				t_pop();
-				found = TRUE;
+			ret = match_encoded(buf, &subpos, ctx);
+			t_pop();
+
+			if (ret) {
+				ctx->found = TRUE;
 				break;
 			}
-			t_pop();
 
-			i_assert(p != end);
+			buffer_set_start_pos(buf, 0);
+			buffer_set_limit(buf, (size_t)-1);
+
+			pos += subpos - 1;
+			last_newline = FALSE;
 			continue;
 		}
 
-		if (ctx->submatch)
-			chr = *p;
-		else if ((*p & 0x80) == 0)
-			chr = i_toupper(*p);
-		else if (ctx->key_ascii || ctx->unknown_charset)
-			chr = *p;
-		else {
-			/* we have non-ascii in header. treat the rest of the
-			   header as encoded with the key's charset */
-			t_push();
-			found = match_data(p, (size_t) (end-p),
-					   ctx->key_charset, ctx);
-			t_pop();
-			break;
+		if (!ctx->submatch) {
+			if ((chr & 0x80) == 0)
+				chr = i_toupper(chr);
+			else if (!ctx->key_ascii && !ctx->unknown_charset) {
+				/* we have non-ascii in header and key contains
+				   non-ascii characters. treat the rest of the
+				   header as encoded with the key's charset */
+				t_push();
+				ctx->found = match_data(buf, ctx->key_charset,
+							ctx);
+				t_pop();
+				break;
+			}
 		}
 
-		chr = ctx->submatch || (*p & 0x80) != 0 ? *p : i_toupper(*p);
-
-		if (((p == header_block && ctx->last_newline) ||
-		     (p != header_block && p[-1] == '\n')) && !ctx->submatch) {
-			/* newline */
-			if (!IS_LWSP(*p)) {
+		if (last_newline && !ctx->submatch) {
+			if (!IS_LWSP(chr)) {
 				/* not a long header, reset matches */
 				ctx->match_count = 0;
-
-				/* and see if we're at end of header */
-				if (*p == '\n') {
-					p++;
-					ctx->eoh = TRUE;
-					break;
-				}
-
-				if (*p == '\r' && p[1] == '\n') {
-					p += 2;
-					ctx->eoh = TRUE;
-					break;
-				}
 			}
 			chr = ' ';
 		}
+		last_newline = chr == '\n';
 
-		if (*p == '\r' || *p == '\n')
+		if (chr == '\r' || chr == '\n')
 			continue;
 
 		for (i = ctx->match_count-1; i >= 0; i--) {
 			if (ctx->key[ctx->matches[i]] == chr) {
 				if (++ctx->matches[i] == ctx->key_len) {
 					/* full match */
-					p++;
-					found = TRUE;
-					break;
+					ctx->found = TRUE;
+					t_pop();
+					return TRUE;
 				}
 			} else {
 				/* non-match */
@@ -269,29 +264,24 @@
 			}
 		}
 
-		if (found)
-			break;
-
 		if (chr == ctx->key[0]) {
 			if (ctx->key_len == 1) {
 				/* only one character in search key */
-				p++;
-				found = TRUE;
+				ctx->found = TRUE;
 				break;
 			}
 			i_assert((size_t)ctx->match_count < ctx->key_len);
 			ctx->matches[ctx->match_count++] = 1;
 		}
 	}
-
-	*header_size = (size_t) (p - header_block);
+	t_pop();
 
-	ctx->last_newline = end[-1] == '\n';
-	return found;
+	ctx->last_newline = last_newline;
+	return ctx->found;
 }
 
 void message_header_search_reset(HeaderSearchContext *ctx)
 {
-	ctx->eoh = FALSE;
 	ctx->match_count = 0;
+	ctx->found = FALSE;
 }
--- a/src/lib-mail/message-header-search.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-header-search.h	Sun Dec 08 07:23:07 2002 +0200
@@ -14,11 +14,9 @@
 
 /* Returns TRUE if key is found from header. This function may be called
    multiple times with partial header blocks, but the blocks must contain only
-   full lines so RFC2047 parsing can be done. *header_size is updated to
-   contain the number of bytes we didn't access (either because we got a match,
-   or because end of headers). */
-int message_header_search(const unsigned char *header_block,
-			  size_t *header_size, HeaderSearchContext *ctx);
+   full lines so RFC2047 parsing can be done. */
+int message_header_search(const unsigned char *header_block, size_t size,
+			  HeaderSearchContext *ctx);
 
 /* Next call to message_header_search() will begin a new header. */
 void message_header_search_reset(HeaderSearchContext *ctx);
--- a/src/lib-mail/message-parser.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-parser.c	Sun Dec 08 07:23:07 2002 +0200
@@ -253,9 +253,7 @@
 	parse_ctx.context = context;
 	parse_ctx.part = part = p_new(pool, MessagePart, 1);
 
-	t_push();
 	message_parse_part(input, &parse_ctx);
-	t_pop();
 	return part;
 }
 
--- a/src/lib-mail/message-part-serialize.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-part-serialize.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "message-parser.h"
 #include "message-part-serialize.h"
 
@@ -32,25 +33,14 @@
 	unsigned int flags;
 } SerializedMessagePart;
 
-static void message_part_serialize_part(MessagePart *part,
-					unsigned int *children_count,
-					SerializedMessagePart **spart_base,
-					size_t *pos, size_t *size)
+void message_part_serialize(MessagePart *part, Buffer *dest)
 {
 	SerializedMessagePart *spart;
-	size_t buf_size;
 
 	while (part != NULL) {
-		/* make sure we have space */
-		if (*pos == *size) {
-			*size *= 2;
-			buf_size = sizeof(SerializedMessagePart) * (*size);
-
-			*spart_base = t_buffer_reget(*spart_base, buf_size);
-		}
-
 		/* create serialized part */
-		spart = (*spart_base) + (*pos);
+		spart = buffer_append_space(dest,
+					    sizeof(SerializedMessagePart));
 		memset(spart, 0, sizeof(SerializedMessagePart));
 
 		spart->physical_pos = part->physical_pos;
@@ -66,35 +56,12 @@
 		spart->children_count = 0;
 		spart->flags = part->flags;
 
-		if (children_count != NULL)
-			(*children_count)++;
-		(*pos)++;
-
-		if (part->children != NULL) {
-			message_part_serialize_part(part->children,
-						    &spart->children_count,
-						    spart_base, pos, size);
-		}
+		if (part->children != NULL)
+			message_part_serialize(part, dest);
 		part = part->next;
 	}
 }
 
-const void *message_part_serialize(MessagePart *part, size_t *size)
-{
-        SerializedMessagePart *spart_base;
-	size_t pos, buf_size;
-
-	buf_size = 32;
-	spart_base = t_buffer_get(sizeof(SerializedMessagePart) * buf_size);
-
-	pos = 0;
-	message_part_serialize_part(part, NULL, &spart_base, &pos, &buf_size);
-
-	*size = sizeof(SerializedMessagePart) * pos;
-	t_buffer_alloc(*size);
-	return spart_base;
-}
-
 static MessagePart *
 message_part_deserialize_part(Pool pool, MessagePart *parent,
 			      const SerializedMessagePart **spart_pos,
--- a/src/lib-mail/message-part-serialize.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/message-part-serialize.h	Sun Dec 08 07:23:07 2002 +0200
@@ -1,9 +1,8 @@
 #ifndef __MESSAGE_PART_SERIALIZE_H
 #define __MESSAGE_PART_SERIALIZE_H
 
-/* Serialize message part, allocating memory from data stack.
-   size is updated to contain the size of returned data. */
-const void *message_part_serialize(MessagePart *part, size_t *size);
+/* Serialize message part. */
+void message_part_serialize(MessagePart *part, Buffer *dest);
 
 /* Generate MessagePart from serialized data. */
 MessagePart *message_part_deserialize(Pool pool, const void *data, size_t size);
--- a/src/lib-mail/quoted-printable.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/quoted-printable.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,44 +1,46 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "hex-binary.h"
 #include "quoted-printable.h"
 
-size_t quoted_printable_decode(const unsigned char *src, size_t *size,
-			       unsigned char *dest)
+void quoted_printable_decode(const unsigned char *src, size_t src_size,
+			     size_t *src_pos_r, Buffer *dest)
 {
-	const unsigned char *end;
-	unsigned char *dest_start;
 	char hexbuf[3];
+	size_t src_pos, next;
 
 	hexbuf[2] = '\0';
 
-	dest_start = dest;
-	end = src + *size;
+	next = 0;
+	for (src_pos = 0; src_pos < src_size; src_pos++) {
+		if (src[src_pos] != '_' && src[src_pos] != '=')
+			continue;
+
+		buffer_append(dest, src, src_pos - next);
 
-	for (; src != end; src++) {
-		if (*src == '_') {
-			*dest++ = ' ';
-			continue;
-		}
-
-		if (*src == '=') {
-			if (src+2 >= end)
+		if (src[src_pos] == '_') {
+			buffer_append(dest, " ", 1);
+			next = src_pos+1;
+		} else {
+			/* =<hex> */
+			if (src_pos+2 >= src_size)
 				break;
 
-			hexbuf[0] = src[1];
-			hexbuf[1] = src[2];
+			hexbuf[0] = src[src_pos+1];
+			hexbuf[1] = src[src_pos+2];
 
 			if (hex_to_binary(hexbuf, dest) == 1) {
-				dest++;
-				src += 2;
-				continue;
+				src_pos += 2;
+				next = src_pos+1;
+			} else {
+				/* non-hex data */
+				next = src_pos;
 			}
 		}
-
-		*dest++ = *src;
 	}
 
-	*size -= (end-src);
-	return (size_t) (dest - dest_start);
+	if (src_pos_r != NULL)
+		*src_pos_r = src_pos;
 }
--- a/src/lib-mail/quoted-printable.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-mail/quoted-printable.h	Sun Dec 08 07:23:07 2002 +0200
@@ -2,13 +2,12 @@
 #define __QUOTED_PRINTABLE_H
 
 /* Translates quoted printable data into binary. dest must be at least the
-   size of src, and may be same as src. Returns size of the binary data.
-   Decoding errors are ignored.
+   size of src, and may be same as src. Decoding errors are ignored.
 
-   This function may be called multiple times for parsing same stream.
-   The *size is updated at return to contain the amount of data actually
-   parsed - the rest of the data should be passed again to this function. */
-size_t quoted_printable_decode(const unsigned char *src, size_t *size,
-			       unsigned char *dest);
+   This function may be called multiple times for parsing the same stream.
+   If src_pos is non-NULL, it's updated to first non-translated character in
+   src. */
+void quoted_printable_decode(const unsigned char *src, size_t src_size,
+			     size_t *src_pos_r, Buffer *dest);
 
 #endif
--- a/src/lib-storage/index/index-search.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-storage/index/index-search.c	Sun Dec 08 07:23:07 2002 +0200
@@ -402,7 +402,7 @@
 			}
 
 			size = strlen(field);
-			ret = message_header_search(field, &size,
+			ret = message_header_search(field, size,
 						    hdr_search_ctx) ? 1 : 0;
 		}
 	}
@@ -487,15 +487,18 @@
 		return;
 	}
 
+	t_push();
+
 	/* then check if the value matches */
 	hdr_search_ctx = search_header_context(ctx->index_context, arg);
 	if (hdr_search_ctx == NULL)
 		ret = 0;
 	else {
 		len = ctx->value_len;
-		ret = message_header_search(ctx->value, &len,
+		ret = message_header_search(ctx->value, len,
 					    hdr_search_ctx) ? 1 : 0;
 	}
+	t_pop();
 
         ARG_SET_RESULT(arg, ret);
 }
--- a/src/lib-storage/mail-sort.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-storage/mail-sort.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "ostream.h"
 #include "mail-sort.h"
 
@@ -13,8 +14,7 @@
 	MailSortFuncs funcs;
 	void *func_context;
 
-	size_t sort_buffer_size, sort_buffer_alloc;
-	unsigned int *sort_buffer;
+	Buffer *sort_buffer;
 
 	time_t last_arrival, last_date;
 	uoff_t last_size;
@@ -24,10 +24,9 @@
 static void mail_sort_flush(MailSortContext *ctx);
 
 static MailSortType
-mail_sort_normalize(const MailSortType *input,
-		    MailSortType output[MAX_SORT_PROGRAM_SIZE])
+mail_sort_normalize(const MailSortType *input, Buffer *output)
 {
-        MailSortType mask = 0;
+        MailSortType type, mask = 0;
 	int pos, reverse;
 
 	reverse = FALSE;
@@ -37,20 +36,21 @@
 		else {
 			if ((mask & *input) == 0) {
 				if (reverse) {
-					i_assert(pos < MAX_SORT_PROGRAM_SIZE);
-					output[pos++] = MAIL_SORT_REVERSE;
+					type = MAIL_SORT_REVERSE;
+					buffer_append(output,
+						      &type, sizeof(type));
 				}
 
-				i_assert(pos < MAX_SORT_PROGRAM_SIZE);
-				output[pos++] = *input;
+				buffer_append(output, input, sizeof(*input));
 				mask |= *input;
 			}
+
 			reverse = FALSE;
 		}
 	}
 
-	i_assert(pos < MAX_SORT_PROGRAM_SIZE);
-	output[pos] = MAIL_SORT_END;
+	type = MAIL_SORT_END;
+	buffer_append(output, &type, sizeof(type));
 
 	return mask;
 }
@@ -76,12 +76,20 @@
 	MailSortContext *ctx;
 	MailSortType norm_input[MAX_SORT_PROGRAM_SIZE];
 	MailSortType norm_output[MAX_SORT_PROGRAM_SIZE];
+	Buffer *buf;
 	int i;
 
 	ctx = i_new(MailSortContext, 1);
 
-	mail_sort_normalize(input, norm_input);
-	mail_sort_normalize(output, norm_output);
+	t_push();
+	buf = buffer_create_data(data_stack_pool,
+				 norm_input, sizeof(norm_input));
+	mail_sort_normalize(input, buf);
+
+	buf = buffer_create_data(data_stack_pool,
+				 norm_output, sizeof(norm_output));
+	mail_sort_normalize(output, buf);
+	t_pop();
 
 	/* remove the common part from output, we already know input is sorted
 	   that much so we don't have to worry about it. */
@@ -92,8 +100,9 @@
 		ctx->output[i] = output[i];
 	ctx->output[i] = MAIL_SORT_END;
 
-	ctx->sort_buffer_alloc = 128;
-	ctx->sort_buffer = i_new(unsigned int, ctx->sort_buffer_alloc);
+	ctx->sort_buffer = buffer_create_dynamic(system_pool,
+						 128 * sizeof(unsigned int),
+						 (size_t)-1);
 
 	ctx->funcs = funcs;
 	ctx->func_context = context;
@@ -103,13 +112,13 @@
 void mail_sort_deinit(MailSortContext *ctx)
 {
 	mail_sort_flush(ctx);
+	buffer_free(ctx->sort_buffer);
 
 	i_free(ctx->last_cc);
 	i_free(ctx->last_from);
 	i_free(ctx->last_subject);
 	i_free(ctx->last_to);
 
-	i_free(ctx->sort_buffer);
 	i_free(ctx);
 }
 
@@ -216,14 +225,7 @@
 	if (ctx->common_mask != 0)
 		mail_sort_check_flush(ctx, id);
 
-	if (ctx->sort_buffer_size == ctx->sort_buffer_alloc) {
-		ctx->sort_buffer_alloc *= 2;
-		ctx->sort_buffer = i_realloc(ctx->sort_buffer,
-					     ctx->sort_buffer_alloc *
-					     sizeof(unsigned int));
-	}
-
-	ctx->sort_buffer[ctx->sort_buffer_size++] = id;
+	buffer_append(ctx->sort_buffer, &id, sizeof(id));
 }
 
 static MailSortContext *mail_sort_qsort_context;
@@ -298,12 +300,15 @@
 
 static void mail_sort_flush(MailSortContext *ctx)
 {
+	unsigned int *arr;
+	size_t count;
+
 	mail_sort_qsort_context = ctx;
 
-	qsort(ctx->sort_buffer, ctx->sort_buffer_size, sizeof(unsigned int),
-	      mail_sort_qsort_func);
+	arr = buffer_get_modifyable_data(ctx->sort_buffer, NULL);
+	count = buffer_get_used_size(ctx->sort_buffer) / sizeof(unsigned int);
+	qsort(arr, count, sizeof(unsigned int), mail_sort_qsort_func);
 
-	ctx->funcs.output(ctx->sort_buffer, ctx->sort_buffer_size,
-			  ctx->func_context);
-	ctx->sort_buffer_size = 0;
+	ctx->funcs.output(arr, count, ctx->func_context);
+	buffer_set_used_size(ctx->sort_buffer, 0);
 }
--- a/src/lib-storage/mail-storage.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib-storage/mail-storage.c	Sun Dec 08 07:23:07 2002 +0200
@@ -147,12 +147,11 @@
 void mail_storage_set_internal_error(MailStorage *storage)
 {
 	struct tm *tm;
-	char *str;
+	char str[256];
 
 	tm = localtime(&ioloop_time);
-	str = t_buffer_get(256);
 
-	storage->error = strftime(str, 256, CRITICAL_MSG, tm) > 0 ?
+	storage->error = strftime(str, sizeof(str), CRITICAL_MSG, tm) > 0 ?
 		i_strdup(str) : i_strdup("Internal error");
 }
 
--- a/src/lib/Makefile.am	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/Makefile.am	Sun Dec 08 07:23:07 2002 +0200
@@ -3,6 +3,7 @@
 liblib_a_SOURCES = \
 	alarm-hup.c \
 	base64.c \
+	buffer.c \
 	compat.c \
 	data-stack.c \
 	env-util.c \
@@ -52,6 +53,7 @@
 noinst_HEADERS = \
 	alarm-hup.h \
 	base64.h \
+	buffer.h \
 	compat.h \
 	data-stack.h \
 	env-util.h \
--- a/src/lib/base64.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/base64.c	Sun Dec 08 07:23:07 2002 +0200
@@ -41,41 +41,48 @@
 
 #include "lib.h"
 #include "base64.h"
+#include "buffer.h"
 
 static const char basis_64[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
 
-const char *base64_encode(const unsigned char *data, size_t size)
+int base64_encode(const unsigned char *src, size_t src_size, Buffer *dest)
 {
-	char *buffer, *p;
+	size_t src_pos;
 	int c1, c2, c3;
 
-	/* + rounding errors + "==" + '\0' */
-	buffer = p = t_malloc(size/3*4 + 2+2+1);
-	while (size > 0) {
-		c1 = *data++; size--;
-		*p++ = basis_64[c1 >> 2];
+	for (src_pos = 0; src_pos < src_size; ) {
+		c1 = src[src_pos++];
+		if (buffer_append_c(dest, basis_64[c1 >> 2]) != 1)
+			return 0;
 
-		c2 = size == 0 ? 0 : *data++;
-		*p++ = basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
-		if (size-- == 0) {
-			*p++ = '=';
-			*p++ = '=';
+		c2 = src_pos == src_size ? 0 : src[src_pos++];
+		if (buffer_append_c(dest, basis_64[((c1 & 0x03) << 4) |
+						   ((c2 & 0xf0) >> 4)]) != 1)
+			return 0;
+
+		if (src_pos++ == src_size) {
+			if (buffer_append(dest, "==", 2) != 2)
+				return 0;
 			break;
 		}
 
-		c3 = size == 0 ? 0 : *data++;
-		*p++ = basis_64[((c2 & 0x0f) << 2) | ((c3 & 0xc0) >> 6)];
-		if (size-- == 0) {
-			*p++ = '=';
+		c3 = src_pos == src_size ? 0 : src[src_pos++];
+		if (buffer_append_c(dest, basis_64[((c2 & 0x0f) << 2) |
+						   ((c3 & 0xc0) >> 6)]) != 1)
+			return 0;
+
+		if (src_pos++ == src_size) {
+			if (buffer_append_c(dest, '=') != 1)
+				return 0;
 			break;
 		}
 
-		*p++ = basis_64[c3 & 0x3f];
+		if (buffer_append_c(dest, basis_64[c3 & 0x3f]) != 1)
+			return 0;
 	}
 
-	*p = '\0';
-	return buffer;
+	return 1;
 }
 
 #define XX 127
@@ -99,54 +106,73 @@
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
     XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX, XX,XX,XX,XX,
 };
-#define CHAR64(c)  (index_64[(int)(unsigned char)(c)])
-
-ssize_t base64_decode(const char *src, size_t *size, unsigned char *dest)
-{
-	unsigned char *p;
-	size_t left;
-	int c1, c2, c3, c4;
 
-	p = dest; left = *size;
-	while (left >= 4) {
-		c1 = *src++;
+int base64_decode(const unsigned char *src, size_t src_size,
+		  size_t *src_pos_r, Buffer *dest)
+{
+	size_t src_pos;
+	unsigned char buf[4];
+	int c1, c2, c3, c4;
+	size_t ret, size;
 
-		if (c1 == '\n' || c1 == '\r' || c1 == ' ' || c1 == '\t') {
-			left--;
+	for (src_pos = 0; src_pos+3 < src_size; ) {
+		c1 = src[src_pos++];
+
+		if (c1 == '\n' || c1 == '\r' || c1 == ' ' || c1 == '\t')
 			continue;
-		}
 
-		if (CHAR64(c1) == XX)
+		if (index_64[c1] == XX)
 			return -1;
 
-		c2 = *src++;
-		if (CHAR64(c2) == XX)
-			return -1;
-
-		c3 = *src++;
-		if (c3 != '=' && CHAR64(c3) == XX)
+		c2 = src[src_pos++];
+		if (index_64[c2] == XX)
 			return -1;
 
-		c4 = *src++;
-		if (c4 != '=' && CHAR64(c4) == XX)
+		c3 = src[src_pos++];
+		if (c3 != '=' && index_64[c3] == XX)
 			return -1;
 
-		left -= 4;
+		c4 = src[src_pos++];
+		if (c4 != '=' && index_64[c4] == XX)
+			return -1;
 
-		*p++ = ((CHAR64(c1) << 2) | ((CHAR64(c2) & 0x30) >> 4));
-
+		buf[0] = (index_64[c1] << 2) | ((index_64[c2] & 0x30) >> 4);
 		if (c3 == '=') {
 			if (c4 != '=')
 				return -1;
-			break;
+			size = 1;
+		} else {
+			buf[1] = ((index_64[c2] & 0xf) << 4) |
+				((index_64[c3] & 0x3c) >> 2);
+
+			if (c4 == '=')
+				size = 2;
+			else {
+				buf[2] = ((index_64[c3] & 0x3) << 6) |
+					index_64[c4];
+				size = 3;
+			}
 		}
 
-		*p++ = (((CHAR64(c2) & 0xf) << 4) | ((CHAR64(c3) & 0x3c) >> 2));
-		if (c4 == '=')
+		ret = buffer_append(dest, buf, size);
+		if (ret != size) {
+			/* buffer full */
+			if (src_pos_r != NULL) {
+				*src_pos_r = src_pos-4;
+                                ret = buffer_get_used_size(dest) - (size-ret);
+				buffer_set_used_size(dest, ret);
+			}
+			return 0;
+		}
+
+		if (size < 3) {
+			/* end of base64 data */
 			break;
-		*p++ = (((CHAR64(c3) & 0x3) << 6) | CHAR64(c4));
+		}
 	}
 
-	*size -= left;
-	return (ssize_t) (p-dest);
+	if (src_pos_r != NULL)
+		*src_pos_r = src_pos;
+
+	return 1;
 }
--- a/src/lib/base64.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/base64.h	Sun Dec 08 07:23:07 2002 +0200
@@ -1,21 +1,25 @@
 #ifndef __BASE64_H
 #define __BASE64_H
 
-/* Translates binary data into base64. Allocates memory from data stack. */
-const char *base64_encode(const unsigned char *data, size_t size);
+/* Translates binary data into base64. The src must not point to dest buffer.
+   Returns 1 if all ok, 0 if dest buffer got full. */
+int base64_encode(const unsigned char *src, size_t src_size, Buffer *dest);
+
+/* Translates base64 data into binary and appends it to dest buffer. dest may
+   point to same buffer as src. Returns 1 if all ok, 0 if dest buffer got full
+   or -1 if data is invalid. Any CR, LF characters are ignored, as well as
+   whitespace at beginning or end of line.
 
-/* Translates base64 data into binary. dest must be large enough, and may be
-   same as src. Returns size of the binary data, or -1 if error occured.
-   Any CR, LF characters are ignored, as well as whitespace at beginning or
-   end of line.
+   This function may be called multiple times for parsing the same stream.
+   If src_pos is non-NULL, it's updated to first non-translated character in
+   src. */
+int base64_decode(const unsigned char *src, size_t src_size,
+		  size_t *src_pos_r, Buffer *dest);
 
-   This function may be called multiple times for parsing same base64 stream.
-   The *size is updated at return to contain the amount of data actually
-   parsed - the rest of the data should be passed again to this function. */
-ssize_t base64_decode(const char *src, size_t *size, unsigned char *dest);
-
-/* max. buffer size required for base64_decode(), not including trailing \0 */
+/* max. buffer size required for base64_encode() */
+#define MAX_BASE64_ENCODED_SIZE(size) \
+	((size) / 3 * 4 + 2+2)
+/* max. buffer size required for base64_decode() */
 #define MAX_BASE64_DECODED_SIZE(size) \
 	((size) / 4 * 3 + 3)
-
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/buffer.c	Sun Dec 08 07:23:07 2002 +0200
@@ -0,0 +1,316 @@
+/*
+    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 "buffer.h"
+
+struct _Buffer {
+	Pool pool;
+
+	const unsigned char *r_buffer;
+	unsigned char *w_buffer;
+	size_t used, alloc, max_alloc, limit, start_pos;
+
+	unsigned int alloced:1;
+	unsigned int readonly:1;
+	unsigned int hard:1;
+};
+
+static void buffer_alloc(Buffer *buf, size_t min_size)
+{
+	if (min_size == 0)
+		return;
+
+	buf->alloc = min_size;
+	if (buf->w_buffer == NULL)
+		buf->w_buffer = p_malloc(buf->pool, buf->alloc);
+	else
+		buf->w_buffer = p_realloc(buf->pool, buf->w_buffer, buf->alloc);
+	buf->r_buffer = buf->w_buffer;
+	buf->alloced = TRUE;
+}
+
+static int buffer_check_read(const Buffer *buf, size_t *pos, size_t *data_size)
+{
+	size_t used_size, max_size;
+
+	used_size = I_MIN(buf->used, buf->limit);
+	if (*pos >= used_size - buf->start_pos)
+		return FALSE;
+
+	*pos += buf->start_pos;
+	max_size = used_size - *pos;
+	if (*data_size > max_size)
+		*data_size = max_size;
+	return TRUE;
+}
+
+static int buffer_check_write(Buffer *buf, size_t *pos, size_t *data_size,
+			      int accept_partial)
+{
+	size_t max_size, new_size;
+
+	if (buf->readonly)
+		return FALSE;
+
+	/* check that we don't overflow size_t */
+	max_size = (size_t)-1 - *pos;
+	if (buf->start_pos >= max_size)
+		return FALSE;
+	*pos += buf->start_pos;
+
+	if (*data_size <= max_size)
+		new_size = *pos + *data_size;
+	else {
+		new_size = *pos + max_size;
+		if (new_size <= *pos || !accept_partial)
+			return FALSE;
+		*data_size = max_size;
+	}
+
+	/* see if we need to grow the buffer */
+	if (new_size > buf->alloc) {
+		if (new_size > buf->limit) {
+			if (buf->hard) {
+				i_panic("Buffer full (%"PRIuSIZE_T" > "
+					"%"PRIuSIZE_T")", new_size, buf->limit);
+			}
+
+			if (!accept_partial)
+				return FALSE;
+
+			new_size = buf->limit;
+			if (new_size == buf->alloc || new_size <= *pos)
+				return FALSE;
+
+			*data_size = new_size - *pos;
+		}
+
+		buffer_alloc(buf, new_size);
+	}
+
+	if (new_size > buf->used)
+		buf->used = new_size;
+	return TRUE;
+}
+
+Buffer *buffer_create_static(Pool pool, size_t size)
+{
+	Buffer *buf;
+
+	buf = p_new(pool, Buffer, 1);
+	buf->pool = pool;
+	buf->max_alloc = buf->limit = size;
+	buffer_alloc(buf, size);
+	return buf;
+}
+
+Buffer *buffer_create_static_hard(Pool pool, size_t size)
+{
+	Buffer *buf;
+
+	buf = buffer_create_static(pool, size);
+	buf->hard = TRUE;
+	return buf;
+}
+
+Buffer *buffer_create_data(Pool pool, void *data, size_t size)
+{
+	Buffer *buf;
+
+	buf = p_new(pool, Buffer, 1);
+	buf->pool = pool;
+	buf->max_alloc = buf->limit = size;
+	buf->r_buffer = buf->w_buffer = data;
+	return buf;
+}
+
+Buffer *buffer_create_const_data(Pool pool, const void *data, size_t size)
+{
+	Buffer *buf;
+
+	buf = p_new(pool, Buffer, 1);
+	buf->pool = pool;
+	buf->used = buf->max_alloc = buf->limit = size;
+	buf->r_buffer = data;
+	buf->readonly = TRUE;
+	return buf;
+}
+
+Buffer *buffer_create_dynamic(Pool pool, size_t init_size, size_t max_size)
+{
+	Buffer *buf;
+
+	buf = p_new(pool, Buffer, 1);
+	buf->pool = pool;
+	buf->max_alloc = buf->limit = max_size;
+	buffer_alloc(buf, init_size);
+	return buf;
+}
+
+void buffer_free(Buffer *buf)
+{
+	if (buf->alloced)
+		p_free(buf->pool, buf->w_buffer);
+	p_free(buf->pool, buf);
+}
+
+void *buffer_free_without_data(Buffer *buf)
+{
+	void *data;
+
+	data = buf->w_buffer;
+	p_free(buf->pool, buf);
+	return data;
+}
+
+size_t buffer_write(Buffer *buf, size_t pos,
+		    const void *data, size_t data_size)
+{
+	if (!buffer_check_write(buf, &pos, &data_size, TRUE))
+		return 0;
+
+	memcpy(buf->w_buffer + pos, data, data_size);
+	return data_size;
+}
+
+size_t buffer_append(Buffer *buf, const void *data, size_t data_size)
+{
+	return buffer_write(buf, buf->used - buf->start_pos, data, data_size);
+}
+
+size_t buffer_append_c(Buffer *buf, char chr)
+{
+	size_t pos, data_size = 1;
+
+	pos = buf->used - buf->start_pos;
+	if (!buffer_check_write(buf, &pos, &data_size, TRUE))
+		return 0;
+
+	if (data_size == 1)
+		buf->w_buffer[pos] = chr;
+	return data_size;
+}
+
+size_t buffer_copy(Buffer *dest, size_t dest_pos,
+		   const Buffer *src, size_t src_pos, size_t copy_size)
+{
+	if (!buffer_check_read(src, &src_pos, &copy_size))
+		return 0;
+
+	if (!buffer_check_write(dest, &dest_pos, &copy_size, TRUE))
+		return 0;
+
+	if (src == dest) {
+		memmove(dest->w_buffer + dest_pos,
+			src->r_buffer + src_pos, copy_size);
+	} else {
+		memcpy(dest->w_buffer + dest_pos,
+		       src->r_buffer + src_pos, copy_size);
+	}
+	return copy_size;
+}
+
+size_t buffer_append_buf(Buffer *dest, const Buffer *src,
+			 size_t src_pos, size_t copy_size)
+{
+	return buffer_copy(dest, dest->used - dest->start_pos,
+			   src, src_pos, copy_size);
+}
+
+void *buffer_get_space(Buffer *buf, size_t pos, size_t size)
+{
+	if (!buffer_check_write(buf, &pos, &size, FALSE))
+		return 0;
+
+	return buf->w_buffer + pos;
+}
+
+void *buffer_append_space(Buffer *buf, size_t size)
+{
+	return buffer_get_space(buf, buf->used - buf->start_pos, size);
+}
+
+const void *buffer_get_data(const Buffer *buf, size_t *used_size)
+{
+	if (used_size != NULL)
+		*used_size = I_MIN(buf->used, buf->limit) - buf->start_pos;
+	return buf->r_buffer + buf->start_pos;
+}
+
+void *buffer_get_modifyable_data(const Buffer *buf, size_t *used_size)
+{
+	if (used_size != NULL)
+		*used_size = I_MIN(buf->used, buf->limit) - buf->start_pos;
+	return buf->w_buffer + buf->start_pos;
+}
+
+void buffer_set_used_size(Buffer *buf, size_t used_size)
+{
+	i_assert(used_size <= I_MIN(buf->alloc, buf->limit) - buf->start_pos);
+
+	buf->used = used_size + buf->start_pos;
+}
+
+size_t buffer_get_used_size(const Buffer *buf)
+{
+	return I_MIN(buf->used, buf->limit) - buf->start_pos;
+}
+
+size_t buffer_set_start_pos(Buffer *buf, size_t abs_pos)
+{
+	size_t old = buf->start_pos;
+
+	i_assert(abs_pos <= I_MIN(buf->used, buf->limit));
+
+	buf->start_pos = abs_pos;
+	return old;
+}
+
+size_t buffer_get_start_pos(const Buffer *buf)
+{
+	return buf->start_pos;
+}
+
+size_t buffer_set_limit(Buffer *buf, size_t limit)
+{
+	size_t old = buf->limit;
+
+	if (limit > (size_t)-1 - buf->start_pos)
+		limit = (size_t)-1;
+	else
+		limit += buf->start_pos;
+
+	buf->limit = I_MIN(limit, buf->max_alloc);
+	return old;
+}
+
+size_t buffer_get_limit(const Buffer *buf)
+{
+	return buf->limit - buf->start_pos;
+}
+
+size_t buffer_get_size(const Buffer *buf)
+{
+	return buf->alloc - buf->start_pos;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/buffer.h	Sun Dec 08 07:23:07 2002 +0200
@@ -0,0 +1,79 @@
+#ifndef __BUFFER_H
+#define __BUFFER_H
+
+/* Create a static sized buffer. Writes past this size will simply not
+   succeed. */
+Buffer *buffer_create_static(Pool pool, size_t size);
+/* Create a static sized buffer. Writes past this size will kill the program. */
+Buffer *buffer_create_static_hard(Pool pool, size_t size);
+/* Create a modifyable buffer from given data. */
+Buffer *buffer_create_data(Pool pool, void *data, size_t size);
+/* Create a non-modifyable buffer from given data. */
+Buffer *buffer_create_const_data(Pool pool, const void *data, size_t size);
+/* Creates a dynamically growing buffer. Whenever write would exceed the
+   current size it's grown. */
+Buffer *buffer_create_dynamic(Pool pool, size_t init_size, size_t max_size);
+/* Free the memory used by buffer. Not needed if the memory is free'd
+   directly from the memory pool. */
+void buffer_free(Buffer *buf);
+/* Free the memory used by buffer structure, but return the buffer data
+   unfree'd.*/
+void *buffer_free_without_data(Buffer *buf);
+
+/* Write data to buffer at specified position, returns number of bytes
+   written. */
+size_t buffer_write(Buffer *buf, size_t pos,
+		    const void *data, size_t data_size);
+/* Append data to buffer, returns number of bytes written. */
+size_t buffer_append(Buffer *buf, const void *data, size_t data_size);
+/* Append character to buffer, returns 1 if written, 0 if not. */
+size_t buffer_append_c(Buffer *buf, char chr);
+
+/* Copy data from buffer to another. The buffers may be same in which case
+   it's internal copying, possibly with overlapping positions (ie. memmove()
+   like functionality). copy_size may be set to (size_t)-1 to copy the rest of
+   the used data in buffer. Returns the number of bytes actually copied. */
+size_t buffer_copy(Buffer *dest, size_t dest_pos,
+		   const Buffer *src, size_t src_pos, size_t copy_size);
+/* Append data to buffer from another. copy_size may be set to (size_t)-1 to
+   copy the rest of the used data in buffer. */
+size_t buffer_append_buf(Buffer *dest, const Buffer *src,
+			 size_t src_pos, size_t copy_size);
+
+/* Returns pointer to specified position in buffer, or NULL if there's not
+   enough space. */
+void *buffer_get_space(Buffer *buf, size_t pos, size_t size);
+/* Increase the buffer usage by given size, and return a pointer to beginning
+   of it, or NULL if there's not enough space in buffer. */
+void *buffer_append_space(Buffer *buf, size_t size);
+
+/* Returns pointer to beginning of buffer data. Current used size of buffer is
+   stored in used_size if it's non-NULL. */
+const void *buffer_get_data(const Buffer *buf, size_t *used_size);
+/* Like buffer_get_data(), but don't return it as const. Returns NULL if the
+   buffer is non-modifyable. */
+void *buffer_get_modifyable_data(const Buffer *buf, size_t *used_size);
+
+/* Set the "used size" of buffer, ie. 0 would set the buffer empty.
+   Must not be used to grow buffer. */
+void buffer_set_used_size(Buffer *buf, size_t used_size);
+/* Returns the current used buffer size. */
+size_t buffer_get_used_size(const Buffer *buf);
+
+/* Change the buffer start position. The buffer acts as if data was removed or
+   inserted to beginning. Returns the old start position. */
+size_t buffer_set_start_pos(Buffer *buf, size_t abs_pos);
+/* Returns the current start position. */
+size_t buffer_get_start_pos(const Buffer *buf);
+
+/* Limit buffer size temporarily. All handling is treated as if this is the
+   current allocated memory size, except dynamic buffer won't be grown.
+   Setting the limit to (size_t)-1 removes it. Returns the old limit. */
+size_t buffer_set_limit(Buffer *buf, size_t limit);
+/* Returns the current buffer limit, or (size_t)-1 if there's none. */
+size_t buffer_get_limit(const Buffer *buf);
+
+/* Returns the current buffer size. */
+size_t buffer_get_size(const Buffer *buf);
+
+#endif
--- a/src/lib/data-stack.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/data-stack.c	Sun Dec 08 07:23:07 2002 +0200
@@ -41,7 +41,11 @@
 
 /* Initial stack size - this should be kept in a size that doesn't exceed
    in a normal use to avoid extra malloc()ing. */
-#define INITIAL_STACK_SIZE (1024*32)
+#ifdef DEBUG
+#  define INITIAL_STACK_SIZE (1024*8)
+#else
+#  define INITIAL_STACK_SIZE (1024*32)
+#endif
 
 typedef struct _StackBlock StackBlock;
 typedef struct _StackFrameBlock StackFrameBlock;
--- a/src/lib/hex-binary.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/hex-binary.c	Sun Dec 08 07:23:07 2002 +0200
@@ -24,6 +24,7 @@
 */
 
 #include "lib.h"
+#include "buffer.h"
 #include "hex-binary.h"
 
 const char *binary_to_hex(const unsigned char *data, size_t size)
@@ -45,12 +46,10 @@
 	return buf;
 }
 
-ssize_t hex_to_binary(const char *data, unsigned char *dest)
+int hex_to_binary(const char *data, Buffer *dest)
 {
-	size_t size;
 	int value;
 
-	size = 0;
 	while (*data != '\0') {
 		if (*data >= '0' && *data <= '9')
 			value = (*data - '0') << 4;
@@ -71,9 +70,10 @@
 		else
 			return -1;
 
-		dest[size++] = value;
+		if (buffer_append_c(dest, value) != 1)
+			return 0;
 		data++;
 	}
 
-	return (ssize_t)size;
+	return 1;
 }
--- a/src/lib/hex-binary.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/hex-binary.h	Sun Dec 08 07:23:07 2002 +0200
@@ -6,9 +6,8 @@
 const char *binary_to_hex(const unsigned char *data, size_t size);
 
 /* Convert hex to binary. data and dest may point to same value.
-   Returns TRUE if successful. Returns number of bytes written to dest,
-   or -1 if error occured. Make sure dest is at least half the size of
-   strlen(data). */
-ssize_t hex_to_binary(const char *data, unsigned char *dest);
+   Returns TRUE if successful. Returns 1 if all ok, 0 if dest buffer got full
+   or -1 if data is invalid. */
+int hex_to_binary(const char *data, Buffer *dest);
 
 #endif
--- a/src/lib/lib.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/lib/lib.h	Sun Dec 08 07:23:07 2002 +0200
@@ -24,6 +24,7 @@
 typedef struct _IPADDR IPADDR;
 typedef struct _IStream IStream;
 typedef struct _OStream OStream;
+typedef struct _Buffer Buffer;
 typedef struct _TempString TempString;
 
 #include "compat.h"
--- a/src/login/client-authenticate.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/login/client-authenticate.c	Sun Dec 08 07:23:07 2002 +0200
@@ -2,6 +2,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "buffer.h"
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
@@ -105,15 +106,17 @@
 static void client_send_auth_data(Client *client, const unsigned char *data,
 				  size_t size)
 {
-	const char *base64_data;
+	Buffer *buf;
 
 	t_push();
 
-	base64_data = base64_encode(data, size);
-	o_stream_send(client->output, "+ ", 2);
-	o_stream_send(client->output, base64_data, strlen(base64_data));
-	o_stream_send(client->output, "\r\n", 2);
+	buf = buffer_create_dynamic(data_stack_pool, size*2, (size_t)-1);
+	buffer_append(buf, "+ ", 2);
+	base64_encode(data, size, buf);
+	buffer_append(buf, "\r\n", 2);
 
+	o_stream_send(client->output, buffer_get_data(buf, NULL),
+		      buffer_get_used_size(buf));
 	o_stream_flush(client->output);
 
 	t_pop();
@@ -168,22 +171,21 @@
 			   size_t reply_data_size, void *context)
 {
 	Client *client = context;
+	const void *ptr;
+	size_t size;
 
 	if (auth_callback(request, auth_process, result,
 			  reply_data, reply_data_size, context)) {
-		auth_continue_request(request, client->plain_login,
-				      client->plain_login_len);
+                ptr = buffer_get_data(client->plain_login, &size);
+		auth_continue_request(request, ptr, size);
 
-		i_free(client->plain_login);
-                client->plain_login = NULL;
+		buffer_set_used_size(client->plain_login, 0);
 	}
 }
 
 int cmd_login(Client *client, const char *user, const char *pass)
 {
 	const char *error;
-	unsigned char *p;
-	size_t len, user_len, pass_len;
 
 	if (!client->tls && disable_plaintext_auth) {
 		client_send_tagline(client,
@@ -192,17 +194,12 @@
 	}
 
 	/* code it into user\0user\0password */
-	user_len = strlen(user);
-	pass_len = strlen(pass);
-	len = user_len + 1 + user_len + 1 + pass_len;
-
-	i_free(client->plain_login);
-	client->plain_login = p = i_malloc(len);
-	client->plain_login_len = len;
-
-	memcpy(p, user, user_len); p += user_len; *p++ = '\0';
-	memcpy(p, user, user_len); p += user_len; *p++ = '\0';
-	memcpy(p, pass, pass_len);
+	buffer_set_used_size(client->plain_login, 0);
+	buffer_append(client->plain_login, user, strlen(user));
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, user, strlen(user));
+	buffer_append_c(client->plain_login, '\0');
+	buffer_append(client->plain_login, pass, strlen(pass));
 
 	client_ref(client);
 	if (auth_init_request(AUTH_METHOD_PLAIN,
@@ -237,9 +234,9 @@
 			      IO io __attr_unused__)
 {
 	Client *client = context;
+	Buffer *buf;
 	char *line;
-	ssize_t size;
-	size_t linelen;
+	size_t linelen, bufsize;
 
 	if (!client_read(client))
 		return;
@@ -253,24 +250,29 @@
 		return;
 	}
 
+	t_push();
+
 	linelen = strlen(line);
-	size = base64_decode(line, &linelen, (unsigned char *) line);
-	if (size < 0) {
+	buf = buffer_create_static_hard(data_stack_pool, linelen);
+
+	if (base64_decode(line, linelen, NULL, buf) <= 0) {
 		/* failed */
 		client_auth_abort(client, "NO Invalid base64 data");
-		return;
+	} else if (client->auth_request == NULL) {
+		client_auth_abort(client, "NO Don't send unrequested data");
+	} else {
+		auth_continue_request(client->auth_request,
+				      buffer_get_data(buf, NULL),
+				      buffer_get_used_size(buf));
 	}
 
-	if (client->auth_request == NULL) {
-		client_auth_abort(client, "NO Don't send unrequested data");
-		return;
-	}
+	/* clear sensitive data */
+	memset(line, 0, linelen);
 
-	auth_continue_request(client->auth_request, (unsigned char *) line,
-			      (size_t)size);
+	bufsize = buffer_get_used_size(buf);
+	memset(buffer_free_without_data(buf), 0, bufsize);
 
-	/* clear sensitive data */
-	memset(line, 0, size);
+	t_pop();
 }
 
 int cmd_authenticate(Client *client, const char *method_name)
--- a/src/login/client.c	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/login/client.c	Sun Dec 08 07:23:07 2002 +0200
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "buffer.h"
 #include "hash.h"
 #include "ioloop.h"
 #include "istream.h"
@@ -289,7 +290,8 @@
 	client->input = i_stream_create_file(fd, default_pool, 8192, FALSE);
 	client->output = o_stream_create_file(fd, default_pool, 1024,
 					      IO_PRIORITY_DEFAULT, FALSE);
-        client->last_input = ioloop_time;
+	client->plain_login = buffer_create_dynamic(system_pool, 128, 8192);
+	client->last_input = ioloop_time;
 	hash_insert(clients, client, client);
 
 	main_ref();
@@ -334,7 +336,7 @@
 	o_stream_unref(client->output);
 
 	i_free(client->tag);
-	i_free(client->plain_login);
+	buffer_free(client->plain_login);
 	i_free(client);
 
 	main_unref();
--- a/src/login/client.h	Fri Dec 06 03:09:22 2002 +0200
+++ b/src/login/client.h	Sun Dec 08 07:23:07 2002 +0200
@@ -16,9 +16,7 @@
 	time_t last_input;
 	char *tag;
 
-	unsigned char *plain_login;
-	unsigned int plain_login_len;
-
+	Buffer *plain_login;
 	AuthRequest *auth_request;
 
 	unsigned int tls:1;