changeset 3384:3b75956d20c4 HEAD

Added configurable logging for login process. Added configurable pop3 logout string. Based on a patch by Andrey Panin.
author Timo Sirainen <tss@iki.fi>
date Sat, 14 May 2005 23:32:03 +0300
parents bfba2219b586
children fe4a297379fc
files dovecot-example.conf src/imap-login/client-authenticate.c src/imap-login/client.c src/login-common/client-common.c src/login-common/client-common.h src/login-common/common.h src/login-common/main.c src/login-common/sasl-server.c src/master/login-process.c src/master/mail-process.c src/master/master-settings.c src/master/master-settings.h src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3/client.c src/pop3/client.h src/pop3/commands.c src/pop3/common.h src/pop3/main.c
diffstat 19 files changed, 258 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Sat May 14 20:17:29 2005 +0300
+++ b/dovecot-example.conf	Sat May 14 23:32:03 2005 +0300
@@ -128,6 +128,15 @@
 # Greeting message for clients.
 #login_greeting = Dovecot ready.
 
+# Space-separated list of elements we want to log. The elements which have
+# a non-empty variable value are joined together to form a comma-separated
+# string.
+#login_log_format_elements = user=<%u> method=%m rip=%r lip=%l %c
+
+# Login log format. %$ contains login_log_format_elements string, %s contains
+# the data we want to log.
+#login_log_format = %$: %s
+
 ##
 ## Mail processes
 ##
@@ -464,6 +473,16 @@
   # idea to change this. %08Xu%08Xv should be pretty fail-safe.
   #pop3_uidl_format = %v.%u
 
+  # POP3 logout format string:
+  #  %t - number of TOP commands
+  #  %T - number of bytes sent to client as a result of TOP command
+  #  %r - number of RETR commands
+  #  %R - number of bytes sent to client as a result of RETR command
+  #  %d - number of deleted messages
+  #  %m - number of messages (before deletion)
+  #  %s - mailbox size in bytes (before deletion)
+  #pop3_logout_format = top=%t/%T, retr=%r/%R, del=%d/%m, size=%s
+
   # Support for dynamically loadable modules.
   #mail_use_modules = no
   #mail_modules = /usr/lib/dovecot/pop3
--- a/src/imap-login/client-authenticate.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/imap-login/client-authenticate.c	Sat May 14 23:32:03 2005 +0300
@@ -116,7 +116,9 @@
 		if (imap_proxy_new(client, host, port, destuser, pass) < 0)
 			client_destroy_internal_failure(client);
 		return TRUE;
-	} else if (host != NULL) {
+	}
+
+	if (host != NULL) {
 		/* IMAP referral
 
 		   [nologin] referral host=.. [port=..] [destuser=..]
@@ -142,6 +144,10 @@
 				   "this server instead.");
 		}
 		client_send_tagline(client, str_c(reply));
+		if (!nologin) {
+			client_destroy(client, "Login with referral");
+			return TRUE;
+		}
 	} else if (nologin) {
 		/* Authentication went ok, but for some reason user isn't
 		   allowed to log in. Shouldn't probably happen. */
@@ -158,16 +164,12 @@
 		return FALSE;
 	}
 
-	if (!nologin) {
-		client_destroy(client, t_strconcat(
-			"Login: ", client->common.virtual_user, NULL));
-	} else {
-		/* get back to normal client input. */
-		if (client->io != NULL)
-			io_remove(client->io);
-		client->io = io_add(client->common.fd, IO_READ,
-				    client_input, client);
-	}
+	i_assert(nologin);
+
+	/* get back to normal client input. */
+	if (client->io != NULL)
+		io_remove(client->io);
+	client->io = io_add(client->common.fd, IO_READ, client_input, client);
 	return TRUE;
 }
 
@@ -187,8 +189,7 @@
 		}
 
 		client_send_tagline(client, "OK Logged in.");
-		client_destroy(client, t_strconcat(
-			"Login: ", client->common.virtual_user, NULL));
+		client_destroy(client, "Login");
 		break;
 	case SASL_SERVER_REPLY_AUTH_FAILED:
 		if (args != NULL) {
--- a/src/imap-login/client.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/imap-login/client.c	Sat May 14 23:32:03 2005 +0300
@@ -43,6 +43,8 @@
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
+const char *login_protocol = "IMAP";
+
 static struct hash_table *clients;
 static struct timeout *to_idle;
 
@@ -443,7 +445,7 @@
 	client->destroyed = TRUE;
 
 	if (reason != NULL)
-		client_syslog(&client->common, "%s", reason);
+		client_syslog(&client->common, reason);
 
 	hash_remove(clients, client);
 
@@ -496,8 +498,7 @@
 {
 	client_send_line(client, "* BYE Internal login failure. "
 			 "Refer to server log for more information.");
-	client_destroy(client, t_strconcat("Internal login failure: ",
-					   client->common.virtual_user, NULL));
+	client_destroy(client, "Internal login failure");
 }
 
 void client_ref(struct imap_client *client)
--- a/src/login-common/client-common.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/login-common/client-common.c	Sat May 14 23:32:03 2005 +0300
@@ -1,20 +1,108 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2005 Timo Sirainen */
 
 #include "common.h"
+#include "hostpid.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "var-expand.h"
 #include "client-common.h"
 
-void client_syslog(struct client *client, const char *format, ...)
+#include <stdlib.h>
+
+static const struct var_expand_table *
+get_var_expand_table(struct client *client)
 {
-	const char *addr;
-	va_list args;
+	static struct var_expand_table static_tab[] = {
+		{ 'u', NULL },
+		{ 'n', NULL },
+		{ 'd', NULL },
+		{ 's', NULL },
+		{ 'h', NULL },
+		{ 'l', NULL },
+		{ 'r', NULL },
+		{ 'p', NULL },
+		{ 'm', NULL },
+		{ 'c', NULL },
+		{ '\0', NULL }
+	};
+	struct var_expand_table *tab;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
 
-	addr = net_ip2addr(&client->ip);
-	if (addr == NULL)
-		addr = "??";
+	if (client->virtual_user != NULL) {
+		tab[0].value = client->virtual_user;
+		tab[1].value = t_strcut(client->virtual_user, '@');
+		tab[2].value = strchr(client->virtual_user, '@');
+		if (tab[2].value != NULL) tab[2].value++;
+	}
+	tab[3].value = login_protocol;
+	tab[4].value = getenv("HOME");
+	tab[5].value = net_ip2addr(&client->local_ip);
+	tab[6].value = net_ip2addr(&client->ip);
+	tab[7].value = my_pid;
+	tab[8].value = client->auth_mech_name == NULL ? NULL :
+		str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
+	tab[9].value = client->tls ? "TLS" : client->secured ? "SSL" : NULL;
+
+	return tab;
+}
+
+static int have_key(const struct var_expand_table *table, const char *str)
+{
+	char key;
+	unsigned int i;
+
+	key = var_get_key(str);
+	for (i = 0; table[i].key != '\0'; i++) {
+		if (table[i].key == key) {
+			return table[i].value != NULL &&
+				table[i].value[0] != '\0';
+		}
+	}
+	return FALSE;
+}
+
+void client_syslog(struct client *client, const char *msg)
+{
+	static struct var_expand_table static_tab[3] = {
+		{ 's', NULL },
+		{ '$', NULL },
+		{ '\0', NULL }
+	};
+	const struct var_expand_table *var_expand_table;
+	struct var_expand_table *tab;
+	const char *p, *const *e;
+	string_t *str;
 
 	t_push();
-	va_start(args, format);
-	i_info("%s [%s]", t_strdup_vprintf(format, args), addr);
-	va_end(args);
+	var_expand_table = get_var_expand_table(client);
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	str = t_str_new(256);
+	for (e = log_format_elements; *e != NULL; e++) {
+		for (p = *e; *p != '\0'; p++) {
+			if (*p != '%' || p[1] == '\0')
+				continue;
+
+			p++;
+			if (have_key(var_expand_table, p)) {
+				if (str_len(str) > 0)
+					str_append(str, ", ");
+				var_expand(str, *e, var_expand_table);
+				break;
+			}
+		}
+	}
+
+	tab[0].value = t_strdup(str_c(str));
+	tab[1].value = msg;
+	str_truncate(str, 0);
+
+	var_expand(str, log_format, tab);
+	i_info("%s", str_c(str));
+
 	t_pop();
 }
--- a/src/login-common/client-common.h	Sat May 14 20:17:29 2005 +0300
+++ b/src/login-common/client-common.h	Sat May 14 23:32:03 2005 +0300
@@ -29,8 +29,7 @@
 struct client *client_create(int fd, int ssl, const struct ip_addr *local_ip,
 			     const struct ip_addr *ip);
 
-void client_syslog(struct client *client, const char *format, ...)
-	__attr_format__(2, 3);
+void client_syslog(struct client *client, const char *msg);
 
 unsigned int clients_get_count(void);
 void clients_notify_auth_connected(void);
--- a/src/login-common/common.h	Sat May 14 20:17:29 2005 +0300
+++ b/src/login-common/common.h	Sat May 14 23:32:03 2005 +0300
@@ -3,12 +3,18 @@
 
 #include "lib.h"
 
+/* Used only for string sanitization */
+#define MAX_MECH_NAME 64
+
 #define AUTH_FAILED_MSG "Authentication failed."
 #define AUTH_TEMP_FAILED_MSG "Temporary authentication failure."
 
+extern const char *login_protocol;
+
 extern int disable_plaintext_auth, process_per_connection, greeting_capability;
 extern int verbose_proctitle, verbose_ssl, verbose_auth;
-char *greeting;
+extern const char *greeting, *log_format;
+extern const char *const *log_format_elements;
 extern unsigned int max_logging_users;
 extern unsigned int login_process_uid;
 extern struct auth_client *auth_client;
--- a/src/login-common/main.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/login-common/main.c	Sat May 14 23:32:03 2005 +0300
@@ -20,7 +20,8 @@
 
 int disable_plaintext_auth, process_per_connection, greeting_capability;
 int verbose_proctitle, verbose_ssl, verbose_auth;
-char *greeting;
+const char *greeting, *log_format;
+const char *const *log_format_elements;
 unsigned int max_logging_users;
 unsigned int login_process_uid;
 struct auth_client *auth_client;
@@ -170,6 +171,15 @@
 		greeting = PACKAGE" ready.";
 	greeting_capability = getenv("GREETING_CAPABILITY") != NULL;
 
+	value = getenv("LOG_FORMAT_ELEMENTS");
+	if (value == NULL)
+		value = "user=<%u> method=%m rip=%r lip=%l %c : %$";
+	log_format_elements = t_strsplit(value, " ");
+
+	log_format = getenv("LOG_FORMAT");
+	if (log_format == NULL)
+		log_format = "%$: %s";
+
 	value = getenv("PROCESS_UID");
 	if (value == NULL)
 		i_fatal("BUG: PROCESS_UID environment not given");
--- a/src/login-common/sasl-server.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/login-common/sasl-server.c	Sat May 14 23:32:03 2005 +0300
@@ -9,9 +9,6 @@
 #include "client-common.h"
 #include "master.h"
 
-/* Used only for string sanitization while verbose_auth is set. */
-#define MAX_MECH_NAME 64
-
 static enum auth_request_flags
 client_get_auth_flags(struct client *client)
 {
@@ -154,9 +151,11 @@
 void sasl_server_auth_cancel(struct client *client, const char *reason)
 {
 	if (verbose_auth && reason != NULL) {
-		client_syslog(client, "Authenticate %s failed: %s",
-			      str_sanitize(client->auth_mech_name,
-					   MAX_MECH_NAME), reason);
+		const char *auth_name =
+			str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
+		client_syslog(client,
+			t_strdup_printf("Authenticate %s failed: %s",
+					auth_name, reason));
 	}
 
 	client->authenticating = FALSE;
--- a/src/master/login-process.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/master/login-process.c	Sat May 14 23:32:03 2005 +0300
@@ -439,6 +439,9 @@
 
 	env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
 	env_put(t_strconcat("GREETING=", set->login_greeting, NULL));
+	env_put(t_strconcat("LOG_FORMAT_ELEMENTS=",
+			    set->login_log_format_elements, NULL));
+	env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL));
 	if (set->login_greeting_capability)
 		env_put("GREETING_CAPABILITY=1");
 }
--- a/src/master/mail-process.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/master/mail-process.c	Sat May 14 23:32:03 2005 +0300
@@ -205,6 +205,8 @@
 			    set->pop3_uidl_format, NULL));
 	env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=",
 			    set->pop3_client_workarounds, NULL));
+	env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
+			    set->pop3_logout_format, NULL));
 
 	if (set->mail_save_crlf)
 		env_put("MAIL_SAVE_CRLF=1");
--- a/src/master/master-settings.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/master/master-settings.c	Sat May 14 23:32:03 2005 +0300
@@ -69,6 +69,8 @@
 	DEF(SET_STR, login_executable),
 	DEF(SET_STR, login_user),
 	DEF(SET_STR, login_greeting),
+	DEF(SET_STR, login_log_format_elements),
+	DEF(SET_STR, login_log_format),
 
 	DEF(SET_BOOL, login_process_per_connection),
 	DEF(SET_BOOL, login_chroot),
@@ -131,6 +133,7 @@
 	DEF(SET_BOOL, pop3_enable_last),
 	DEF(SET_STR, pop3_uidl_format),
 	DEF(SET_STR, pop3_client_workarounds),
+	DEF(SET_STR, pop3_logout_format),
 
 	{ 0, NULL, 0 }
 };
@@ -258,6 +261,8 @@
 	MEMBER(login_executable) NULL,
 	MEMBER(login_user) "dovecot",
 	MEMBER(login_greeting) "Dovecot ready.",
+	MEMBER(login_log_format_elements) "user=<%u> method=%m rip=%r lip=%l %c",
+	MEMBER(login_log_format) "%$: %s",
 
 	MEMBER(login_process_per_connection) TRUE,
 	MEMBER(login_chroot) TRUE,
@@ -324,6 +329,7 @@
 	MEMBER(pop3_enable_last) FALSE,
 	MEMBER(pop3_uidl_format) "%v.%u",
 	MEMBER(pop3_client_workarounds) NULL,
+	MEMBER(pop3_logout_format) "top=%t/%T, retr=%r/%R, del=%d/%m, size=%s",
 
 	/* .. */
 	MEMBER(login_uid) 0,
--- a/src/master/master-settings.h	Sat May 14 20:17:29 2005 +0300
+++ b/src/master/master-settings.h	Sat May 14 23:32:03 2005 +0300
@@ -40,6 +40,8 @@
 	const char *login_executable;
 	const char *login_user;
 	const char *login_greeting;
+	const char *login_log_format_elements;
+	const char *login_log_format;
 
 	int login_process_per_connection;
 	int login_chroot;
@@ -100,6 +102,7 @@
 	int pop3_enable_last;
 	const char *pop3_uidl_format;
 	const char *pop3_client_workarounds;
+	const char *pop3_logout_format;
 
 	/* .. */
 	uid_t login_uid;
--- a/src/pop3-login/client-authenticate.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3-login/client-authenticate.c	Sat May 14 23:32:03 2005 +0300
@@ -160,8 +160,7 @@
 		}
 
 		client_send_line(client, "+OK Logged in.");
-		client_destroy(client, t_strconcat(
-			"Login: ", client->common.virtual_user, NULL));
+		client_destroy(client, "Login");
 		break;
 	case SASL_SERVER_REPLY_AUTH_FAILED:
 		if (args != NULL) {
--- a/src/pop3-login/client.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3-login/client.c	Sat May 14 23:32:03 2005 +0300
@@ -41,6 +41,8 @@
 #  error client idle timeout must be smaller than authentication timeout
 #endif
 
+const char *login_protocol = "POP3";
+
 static struct hash_table *clients;
 static struct timeout *to_idle;
 
@@ -343,7 +345,7 @@
 	client->destroyed = TRUE;
 
 	if (reason != NULL)
-		client_syslog(&client->common, "%s", reason);
+		client_syslog(&client->common, reason);
 
 	hash_remove(clients, client);
 
@@ -394,8 +396,7 @@
 {
 	client_send_line(client, "-ERR [IN-USE] Internal login failure. "
 			 "Refer to server log for more information.");
-	client_destroy(client, t_strconcat("Internal login failure: ",
-					   client->common.virtual_user, NULL));
+	client_destroy(client, "Internal login failure");
 }
 
 void client_ref(struct pop3_client *client)
--- a/src/pop3/client.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3/client.c	Sat May 14 23:32:03 2005 +0300
@@ -7,6 +7,7 @@
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
+#include "var-expand.h"
 #include "mail-storage.h"
 #include "commands.h"
 #include "mail-search.h"
@@ -156,12 +157,12 @@
 		i_error("Couldn't open INBOX: %s",
 			mail_storage_get_last_error(storage, &syntax_error));
 		client_send_line(client, "-ERR No INBOX for user.");
-		client_destroy(client);
+		client_destroy(client, "No INBOX for user.");
 		return NULL;
 	}
 
 	if (!init_mailbox(client)) {
-		client_destroy(client);
+		client_destroy(client, "Mailbox init failed");
 		return NULL;
 	}
 
@@ -173,8 +174,42 @@
 	return client;
 }
 
-void client_destroy(struct client *client)
+static const char *client_stats(struct client *client)
 {
+	static struct var_expand_table static_tab[] = {
+		{ 'T', NULL },
+		{ 't', NULL },
+		{ 'R', NULL },
+		{ 'r', NULL },
+		{ 'd', NULL },
+		{ 'm', NULL },
+		{ 's', NULL },
+		{ '\0', NULL }
+	};
+	struct var_expand_table *tab;
+	string_t *str;
+
+	tab = t_malloc(sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	tab[0].value = dec2str(client->top_bytes);
+	tab[1].value = dec2str(client->top_count);
+	tab[2].value = dec2str(client->retr_bytes);
+	tab[3].value = dec2str(client->retr_count);
+	tab[4].value = dec2str(client->deleted_count);
+	tab[5].value = dec2str(client->messages_count);
+	tab[6].value = dec2str(client->total_size);
+
+	str = t_str_new(128);
+	var_expand(str, logout_format, tab);
+	return str_c(str);
+}
+
+void client_destroy(struct client *client, const char *reason)
+{
+	if (reason != NULL)
+		i_info("%s %s", reason, client_stats(client));
+
 	if (client->cmd != NULL) {
 		/* deinitialize command */
 		i_stream_close(client->input);
@@ -204,8 +239,11 @@
 	io_loop_stop(ioloop);
 }
 
-void client_disconnect(struct client *client)
+void client_disconnect(struct client *client, const char *reason)
 {
+	if (reason != NULL)
+		i_info("%s %s", reason, client_stats(client));
+
 	(void)o_stream_flush(client->output);
 
 	i_stream_close(client->input);
@@ -266,7 +304,7 @@
 	if (mailbox_is_inconsistent(client->mailbox)) {
 		client_send_line(client, "-ERR Mailbox is in inconsistent "
 				 "state, please relogin.");
-		client_disconnect(client);
+		client_disconnect(client, "Mailbox is in inconsistent state.");
 		return;
 	}
 
@@ -295,12 +333,12 @@
 	switch (i_stream_read(client->input)) {
 	case -1:
 		/* disconnected */
-		client_destroy(client);
+		client_destroy(client, "Disconnected");
 		return;
 	case -2:
 		/* line too long, kill it */
 		client_send_line(client, "-ERR Input line too long.");
-		client_destroy(client);
+		client_destroy(client, "Input line too long.");
 		return;
 	}
 
@@ -323,13 +361,13 @@
 			}
 		} else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
 			client_send_line(client, "-ERR Too many bad commands.");
-			client_disconnect(client);
+			client_disconnect(client, "Too many bad commands.");
 		}
 	}
 	o_stream_uncork(client->output);
 
 	if (client->output->closed)
-		client_destroy(client);
+		client_destroy(client, NULL);
 }
 
 static int client_output(void *context)
@@ -338,7 +376,7 @@
 	int ret;
 
 	if ((ret = o_stream_flush(client->output)) < 0) {
-		client_destroy(client);
+		client_destroy(client, NULL);
 		return 1;
 	}
 
@@ -374,13 +412,13 @@
 	if (my_client->cmd != NULL) {
 		if (ioloop_time - my_client->last_output >=
 		    CLIENT_OUTPUT_TIMEOUT)
-			client_destroy(my_client);
+			client_destroy(my_client, "Disconnected for inactivity.");
 	} else {
 		if (ioloop_time - my_client->last_input >=
 		    CLIENT_IDLE_TIMEOUT) {
 			client_send_line(my_client,
 					 "-ERR Disconnected for inactivity.");
-			client_destroy(my_client);
+			client_destroy(my_client, "Disconnected for inactivity.");
 		}
 	}
 }
@@ -395,7 +433,7 @@
 {
 	if (my_client != NULL) {
 		client_send_line(my_client, "-ERR Server shutting down.");
-		client_destroy(my_client);
+		client_destroy(my_client, "Server shutting down.");
 	}
 
 	timeout_remove(to_idle);
--- a/src/pop3/client.h	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3/client.h	Sat May 14 23:32:03 2005 +0300
@@ -30,6 +30,14 @@
 	uoff_t deleted_size;
 	uint32_t last_seen;
 
+	uoff_t top_bytes;
+	uoff_t retr_bytes;
+	unsigned int top_count;
+	unsigned int retr_count;
+
+	uoff_t *byte_counter;
+	uoff_t byte_counter_offset;
+
 	unsigned char *deleted_bitmask;
 
 	unsigned int deleted:1;
@@ -39,10 +47,10 @@
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
 struct client *client_create(int hin, int hout, struct mail_storage *storage);
-void client_destroy(struct client *client);
+void client_destroy(struct client *client, const char *reason);
 
 /* Disconnect client connection */
-void client_disconnect(struct client *client);
+void client_disconnect(struct client *client, const char *reason);
 
 /* Send a line of data to client */
 int client_send_line(struct client *client, const char *fmt, ...)
--- a/src/pop3/commands.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3/commands.c	Sat May 14 23:32:03 2005 +0300
@@ -217,7 +217,7 @@
 	if (client->deleted) {
 		if (!expunge_mails(client)) {
 			client_send_storage_error(client);
-			client_disconnect(client);
+			client_disconnect(client, "Storage error during logout.");
 			return TRUE;
 		}
 	}
@@ -230,7 +230,7 @@
 	else
 		client_send_line(client, "+OK Logging out, messages deleted.");
 
-	client_disconnect(client);
+	client_disconnect(client, "Logout.");
 	return TRUE;
 }
 
@@ -342,6 +342,10 @@
 		(void)o_stream_send(client->output, "\r\n", 2);
 	}
 
+	*client->byte_counter +=
+		client->output->offset - client->byte_counter_offset;
+        client->byte_counter = NULL;
+
 	client_send_line(client, ".");
 	fetch_deinit(ctx);
 	client->cmd = NULL;
@@ -405,6 +409,10 @@
 	if (client->last_seen <= msgnum)
 		client->last_seen = msgnum+1;
 
+	client->retr_count++;
+	client->byte_counter = &client->retr_bytes;
+	client->byte_counter_offset = client->output->offset;
+
 	fetch(client, msgnum, (uoff_t)-1);
 	return TRUE;
 }
@@ -468,6 +476,10 @@
 	if (get_size(client, args, &max_lines) == NULL)
 		return FALSE;
 
+	client->top_count++;
+	client->byte_counter = &client->top_bytes;
+	client->byte_counter_offset = client->output->offset;
+
 	fetch(client, msgnum, max_lines);
 	return TRUE;
 }
--- a/src/pop3/common.h	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3/common.h	Sat May 14 23:32:03 2005 +0300
@@ -19,7 +19,7 @@
 extern struct ioloop *ioloop;
 extern enum client_workarounds client_workarounds;
 extern int enable_last_command, no_flag_updates;
-extern const char *uidl_format;
+extern const char *uidl_format, *logout_format;
 extern enum uidl_keys uidl_keymask;
 
 extern void (*hook_mail_storage_created)(struct mail_storage **storage);
--- a/src/pop3/main.c	Sat May 14 20:17:29 2005 +0300
+++ b/src/pop3/main.c	Sat May 14 23:32:03 2005 +0300
@@ -41,7 +41,7 @@
 enum client_workarounds client_workarounds = 0;
 int enable_last_command = FALSE;
 int no_flag_updates = FALSE;
-const char *uidl_format;
+const char *uidl_format, *logout_format;
 enum uidl_keys uidl_keymask;
 
 static void sig_quit(int signo __attr_unused__)
@@ -178,6 +178,9 @@
 	uidl_format = getenv("POP3_UIDL_FORMAT");
 	if (uidl_format == NULL)
 		uidl_format = "%v.%u";
+	logout_format = getenv("POP3_LOGOUT_FORMAT");
+	if (logout_format == NULL)
+		logout_format = "top=%t/%T, retr=%r/%R, del=%d/%m, size=%s";
 	uidl_keymask = parse_uidl_keymask(uidl_format);
 
 	flags = 0;