src/pop3/client.c
author Timo Sirainen <tss@iki.fi>
Tue Feb 10 23:21:02 2009 -0500 (2009-02-10)
branchHEAD
changeset 8745 22d70947597c
parent 8723 eaffedbc23f2
child 9532 00cd9aacd03c
permissions -rw-r--r--
pop3: Don't crash at startup if mailbox is empty.
tss@8590
     1
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
tss@1043
     2
tss@1043
     3
#include "common.h"
tss@2722
     4
#include "buffer.h"
tss@1043
     5
#include "ioloop.h"
tss@1043
     6
#include "network.h"
tss@1043
     7
#include "istream.h"
tss@1043
     8
#include "ostream.h"
tss@2421
     9
#include "str.h"
tss@3384
    10
#include "var-expand.h"
tss@1043
    11
#include "mail-storage.h"
tss@1043
    12
#include "commands.h"
tss@7642
    13
#include "mail-search-build.h"
tss@5500
    14
#include "mail-namespace.h"
tss@1043
    15
tss@1043
    16
#include <stdlib.h>
tss@3960
    17
#include <unistd.h>
tss@1043
    18
tss@1053
    19
/* max. length of input command line (spec says 512) */
tss@1053
    20
#define MAX_INBUF_SIZE 2048
tss@1043
    21
tss@2421
    22
/* Stop reading input when output buffer has this many bytes. Once the buffer
tss@2421
    23
   size has dropped to half of it, start reading input again. */
tss@2421
    24
#define OUTBUF_THROTTLE_SIZE 4096
tss@2421
    25
tss@1043
    26
/* Disconnect client when it sends too many bad commands in a row */
tss@1043
    27
#define CLIENT_MAX_BAD_COMMANDS 20
tss@1043
    28
tss@7103
    29
/* Disconnect client after idling this many milliseconds */
tss@7103
    30
#define CLIENT_IDLE_TIMEOUT_MSECS (10*60*1000)
tss@8718
    31
/* If client starts idling for this many milliseconds, commit the current
tss@8718
    32
   transaction. This allows the mailbox to become unlocked. */
tss@8718
    33
#define CLIENT_COMMIT_TIMEOUT_MSECS (10*1000)
tss@1043
    34
tss@1043
    35
static struct client *my_client; /* we don't need more than one currently */
tss@1043
    36
tss@4907
    37
static void client_input(struct client *client);
tss@4907
    38
static int client_output(struct client *client);
tss@1043
    39
tss@8718
    40
static void client_commit_timeout(struct client *client)
tss@8718
    41
{
tss@8718
    42
	if (client->cmd != NULL) {
tss@8718
    43
		/* Can't commit while commands are running */
tss@8718
    44
		return;
tss@8718
    45
	}
tss@8718
    46
tss@8718
    47
	(void)mailbox_transaction_commit(&client->trans);
tss@8718
    48
	client->trans = mailbox_transaction_begin(client->mailbox, 0);
tss@8718
    49
}
tss@8718
    50
tss@7103
    51
static void client_idle_timeout(struct client *client)
tss@7103
    52
{
tss@7103
    53
	if (client->cmd != NULL) {
tss@7103
    54
		client_destroy(client,
tss@7103
    55
			"Disconnected for inactivity in reading our output");
tss@7103
    56
	} else {
tss@7103
    57
		client_send_line(client, "-ERR Disconnected for inactivity.");
tss@7103
    58
		client_destroy(client, "Disconnected for inactivity");
tss@7103
    59
	}
tss@7103
    60
}
tss@7103
    61
tss@6311
    62
static bool init_mailbox(struct client *client, const char **error_r)
tss@1044
    63
{
tss@7642
    64
	struct mail_search_args *search_args;
tss@1915
    65
        struct mailbox_transaction_context *t;
tss@1845
    66
	struct mail_search_context *ctx;
tss@2976
    67
        struct mailbox_status status;
tss@1044
    68
	struct mail *mail;
tss@2722
    69
	buffer_t *message_sizes_buf;
tss@6311
    70
	uint32_t failed_uid = 0;
tss@6280
    71
	uoff_t size;
tss@3863
    72
	int i;
tss@6311
    73
	bool failed, expunged;
tss@1044
    74
tss@2722
    75
	message_sizes_buf = buffer_create_dynamic(default_pool, 512);
tss@2722
    76
tss@7642
    77
	search_args = mail_search_build_init();
tss@7642
    78
	mail_search_build_add_all(search_args);
tss@1845
    79
tss@1044
    80
	for (i = 0; i < 2; i++) {
tss@6652
    81
		expunged = FALSE;
tss@6463
    82
		if (mailbox_sync(client->mailbox, MAILBOX_SYNC_FLAG_FULL_READ,
tss@6463
    83
				 STATUS_UIDVALIDITY, &status) < 0) {
tss@2322
    84
			client_send_storage_error(client);
tss@2722
    85
			break;
tss@2322
    86
		}
tss@2976
    87
		client->uid_validity = status.uidvalidity;
tss@2092
    88
tss@3209
    89
		t = mailbox_transaction_begin(client->mailbox, 0);
tss@7642
    90
		ctx = mailbox_search_init(t, search_args, NULL);
tss@1044
    91
tss@2722
    92
		client->last_seen = 0;
tss@2722
    93
		client->total_size = 0;
tss@2722
    94
		buffer_set_used_size(message_sizes_buf, 0);
tss@2722
    95
tss@1044
    96
		failed = FALSE;
tss@3209
    97
		mail = mail_alloc(t, MAIL_FETCH_VIRTUAL_SIZE, NULL);
tss@3209
    98
		while (mailbox_search_next(ctx, mail) > 0) {
tss@6280
    99
			if (mail_get_virtual_size(mail, &size) < 0) {
tss@6312
   100
				expunged = mail->expunged;
tss@6312
   101
				failed = TRUE;
tss@6311
   102
				if (failed_uid == mail->uid) {
tss@6311
   103
					i_error("Getting size of message "
tss@6311
   104
						"UID=%u failed", mail->uid);
tss@6311
   105
					break;
tss@6311
   106
				}
tss@6311
   107
				failed_uid = mail->uid;
tss@1044
   108
				break;
tss@1044
   109
			}
tss@2621
   110
tss@3209
   111
			if ((mail_get_flags(mail) & MAIL_SEEN) != 0)
tss@2621
   112
				client->last_seen = mail->seq;
tss@1044
   113
                        client->total_size += size;
tss@1044
   114
tss@2722
   115
			buffer_append(message_sizes_buf, &size, sizeof(size));
tss@1044
   116
		}
tss@2722
   117
		client->messages_count =
tss@2722
   118
			message_sizes_buf->used / sizeof(uoff_t);
tss@1044
   119
tss@3879
   120
		mail_free(&mail);
tss@6311
   121
		if (mailbox_search_deinit(&ctx) < 0 || (failed && !expunged)) {
tss@1044
   122
			client_send_storage_error(client);
tss@6512
   123
			(void)mailbox_transaction_commit(&t);
tss@2722
   124
			break;
tss@1044
   125
		}
tss@1044
   126
tss@2010
   127
		if (!failed) {
tss@2722
   128
			client->trans = t;
tss@2722
   129
			client->message_sizes =
tss@6414
   130
				buffer_free_without_data(&message_sizes_buf);
tss@7642
   131
			mail_search_args_unref(&search_args);
tss@1044
   132
			return TRUE;
tss@2010
   133
		}
tss@1044
   134
tss@5761
   135
		/* well, sync and try again. we might have cached virtual
tss@5761
   136
		   sizes, make sure they get committed. */
tss@6512
   137
		(void)mailbox_transaction_commit(&t);
tss@1044
   138
	}
tss@7642
   139
	mail_search_args_unref(&search_args);
tss@1044
   140
tss@6311
   141
	if (expunged) {
tss@6311
   142
		client_send_line(client,
tss@6311
   143
				 "-ERR [IN-USE] Couldn't sync mailbox.");
tss@6311
   144
		*error_r = "Can't sync mailbox: Messages keep getting expunged";
tss@6311
   145
	} else {
tss@6311
   146
		struct mail_storage *storage = client->inbox_ns->storage;
tss@6311
   147
		enum mail_error error;
tss@6311
   148
tss@6311
   149
		*error_r = mail_storage_get_last_error(storage, &error);
tss@6311
   150
	}
tss@6414
   151
	buffer_free(&message_sizes_buf);
tss@1044
   152
	return FALSE;
tss@1044
   153
}
tss@1044
   154
tss@8082
   155
struct client *client_create(int fd_in, int fd_out, struct mail_user *user)
tss@1043
   156
{
tss@5500
   157
	struct mail_storage *storage;
tss@5500
   158
	const char *inbox;
tss@1043
   159
	struct client *client;
tss@2039
   160
        enum mailbox_open_flags flags;
tss@5178
   161
	const char *errmsg;
tss@5613
   162
	enum mail_error error;
tss@1043
   163
tss@2421
   164
	/* always use nonblocking I/O */
tss@3960
   165
	net_set_nonblock(fd_in, TRUE);
tss@3960
   166
	net_set_nonblock(fd_out, TRUE);
tss@2421
   167
tss@1043
   168
	client = i_new(struct client, 1);
tss@3960
   169
	client->fd_in = fd_in;
tss@3960
   170
	client->fd_out = fd_out;
tss@6162
   171
	client->input = i_stream_create_fd(fd_in, MAX_INBUF_SIZE, FALSE);
tss@6161
   172
	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
tss@2421
   173
	o_stream_set_flush_callback(client->output, client_output, client);
tss@1043
   174
tss@3960
   175
	client->io = io_add(fd_in, IO_READ, client_input, client);
tss@1043
   176
        client->last_input = ioloop_time;
tss@7103
   177
	client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
tss@7103
   178
				      client_idle_timeout, client);
tss@8718
   179
	if (!lock_session) {
tss@8718
   180
		client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
tss@8718
   181
						client_commit_timeout, client);
tss@8718
   182
	}
tss@1043
   183
tss@8082
   184
	client->user = user;
tss@5500
   185
tss@5500
   186
	inbox = "INBOX";
tss@8082
   187
	client->inbox_ns = mail_namespace_find(user->namespaces, &inbox);
tss@5500
   188
	if (client->inbox_ns == NULL) {
tss@5500
   189
		client_send_line(client, "-ERR No INBOX namespace for user.");
tss@5500
   190
		client_destroy(client, "No INBOX namespace for user.");
tss@5500
   191
		return NULL;
tss@5500
   192
	}
tss@5500
   193
tss@5500
   194
	storage = client->inbox_ns->storage;
tss@1043
   195
tss@8259
   196
	flags = MAILBOX_OPEN_POP3_SESSION;
tss@2719
   197
	if (no_flag_updates)
tss@2039
   198
		flags |= MAILBOX_OPEN_KEEP_RECENT;
tss@4153
   199
	if (lock_session)
tss@4153
   200
		flags |= MAILBOX_OPEN_KEEP_LOCKED;
tss@8468
   201
	client->mailbox = mailbox_open(&storage, "INBOX", NULL, flags);
tss@1363
   202
	if (client->mailbox == NULL) {
tss@5178
   203
		errmsg = t_strdup_printf("Couldn't open INBOX: %s",
tss@5178
   204
				mail_storage_get_last_error(storage,
tss@5613
   205
							    &error));
tss@5178
   206
		i_error("%s", errmsg);
tss@5178
   207
		client_send_line(client, "-ERR [IN-USE] %s", errmsg);
tss@5178
   208
		client_destroy(client, "Couldn't open INBOX");
tss@1363
   209
		return NULL;
tss@1363
   210
	}
tss@1363
   211
tss@6311
   212
	if (!init_mailbox(client, &errmsg)) {
tss@6311
   213
		i_error("Couldn't init INBOX: %s", errmsg);
tss@3384
   214
		client_destroy(client, "Mailbox init failed");
tss@1363
   215
		return NULL;
tss@1363
   216
	}
tss@1043
   217
tss@8745
   218
	if (!no_flag_updates && client->messages_count > 0)
tss@8717
   219
		client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
tss@8717
   220
tss@1043
   221
	i_assert(my_client == NULL);
tss@1043
   222
	my_client = client;
tss@1043
   223
tss@1646
   224
	if (hook_client_created != NULL)
tss@1646
   225
		hook_client_created(&client);
tss@1043
   226
	return client;
tss@1043
   227
}
tss@1043
   228
tss@3384
   229
static const char *client_stats(struct client *client)
tss@1043
   230
{
tss@3384
   231
	static struct var_expand_table static_tab[] = {
tss@8544
   232
		{ 'p', NULL, "top_bytes" },
tss@8544
   233
		{ 't', NULL, "top_count" },
tss@8544
   234
		{ 'b', NULL, "retr_bytes" },
tss@8544
   235
		{ 'r', NULL, "retr_count" },
tss@8544
   236
		{ 'd', NULL, "deleted_count" },
tss@8544
   237
		{ 'm', NULL, "message_count" },
tss@8544
   238
		{ 's', NULL, "message_bytes" },
tss@8544
   239
		{ 'i', NULL, "input" },
tss@8544
   240
		{ 'o', NULL, "output" },
tss@8544
   241
		{ '\0', NULL, NULL }
tss@3384
   242
	};
tss@3384
   243
	struct var_expand_table *tab;
tss@3384
   244
	string_t *str;
tss@3384
   245
tss@3384
   246
	tab = t_malloc(sizeof(static_tab));
tss@3384
   247
	memcpy(tab, static_tab, sizeof(static_tab));
tss@3384
   248
tss@3384
   249
	tab[0].value = dec2str(client->top_bytes);
tss@3384
   250
	tab[1].value = dec2str(client->top_count);
tss@3384
   251
	tab[2].value = dec2str(client->retr_bytes);
tss@3384
   252
	tab[3].value = dec2str(client->retr_count);
tss@4566
   253
	tab[4].value = dec2str(client->expunged_count);
tss@3384
   254
	tab[5].value = dec2str(client->messages_count);
tss@3384
   255
	tab[6].value = dec2str(client->total_size);
tss@5979
   256
	tab[7].value = dec2str(client->input->v_offset);
tss@5979
   257
	tab[8].value = dec2str(client->output->offset);
tss@3384
   258
tss@3384
   259
	str = t_str_new(128);
tss@3384
   260
	var_expand(str, logout_format, tab);
tss@3384
   261
	return str_c(str);
tss@3384
   262
}
tss@3384
   263
tss@7453
   264
static const char *client_get_disconnect_reason(struct client *client)
tss@7453
   265
{
tss@7453
   266
	errno = client->input->stream_errno != 0 ?
tss@7453
   267
		client->input->stream_errno :
tss@7453
   268
		client->output->stream_errno;
tss@7453
   269
	return errno == 0 || errno == EPIPE ? "Connection closed" :
tss@7453
   270
		t_strdup_printf("Connection closed: %m");
tss@7453
   271
}
tss@7453
   272
tss@3384
   273
void client_destroy(struct client *client, const char *reason)
tss@3384
   274
{
tss@8723
   275
	if (client->seen_change_count > 0)
tss@8723
   276
		client_update_mails(client);
tss@8723
   277
tss@4096
   278
	if (!client->disconnected) {
tss@4096
   279
		if (reason == NULL)
tss@7453
   280
			reason = client_get_disconnect_reason(client);
tss@3384
   281
		i_info("%s %s", reason, client_stats(client));
tss@4096
   282
	}
tss@3384
   283
tss@2502
   284
	if (client->cmd != NULL) {
tss@2502
   285
		/* deinitialize command */
tss@2502
   286
		i_stream_close(client->input);
tss@2502
   287
		o_stream_close(client->output);
tss@2502
   288
		client->cmd(client);
tss@2695
   289
		i_assert(client->cmd == NULL);
tss@2502
   290
	}
tss@5102
   291
	if (client->trans != NULL) {
tss@5102
   292
		/* client didn't QUIT, but we still want to save any changes
tss@5102
   293
		   done in this transaction. especially the cached virtual
tss@5102
   294
		   message sizes. */
tss@6512
   295
		(void)mailbox_transaction_commit(&client->trans);
tss@5102
   296
	}
tss@2722
   297
	if (client->mailbox != NULL)
tss@3879
   298
		mailbox_close(&client->mailbox);
tss@8467
   299
	mail_user_unref(&client->user);
tss@1043
   300
tss@1044
   301
	i_free(client->message_sizes);
tss@1044
   302
	i_free(client->deleted_bitmask);
tss@8717
   303
	i_free(client->seen_bitmask);
tss@1043
   304
tss@2421
   305
	if (client->io != NULL)
tss@3879
   306
		io_remove(&client->io);
tss@7103
   307
	timeout_remove(&client->to_idle);
tss@8718
   308
	if (client->to_commit != NULL)
tss@8718
   309
		timeout_remove(&client->to_commit);
tss@1043
   310
tss@4070
   311
	i_stream_destroy(&client->input);
tss@4070
   312
	o_stream_destroy(&client->output);
tss@1043
   313
tss@3960
   314
	if (close(client->fd_in) < 0)
tss@3960
   315
		i_error("close(client in) failed: %m");
tss@3960
   316
	if (client->fd_in != client->fd_out) {
tss@3960
   317
		if (close(client->fd_out) < 0)
tss@3960
   318
			i_error("close(client out) failed: %m");
tss@3960
   319
	}
tss@3960
   320
tss@1043
   321
	i_free(client);
tss@1043
   322
tss@1043
   323
	/* quit the program */
tss@1043
   324
	my_client = NULL;
tss@1043
   325
	io_loop_stop(ioloop);
tss@1043
   326
}
tss@1043
   327
tss@3384
   328
void client_disconnect(struct client *client, const char *reason)
tss@1043
   329
{
tss@4096
   330
	if (client->disconnected)
tss@4096
   331
		return;
tss@4096
   332
tss@4096
   333
	client->disconnected = TRUE;
tss@4096
   334
	i_info("Disconnected: %s %s", reason, client_stats(client));
tss@3384
   335
tss@2421
   336
	(void)o_stream_flush(client->output);
tss@1043
   337
tss@1043
   338
	i_stream_close(client->input);
tss@1043
   339
	o_stream_close(client->output);
tss@1043
   340
}
tss@1043
   341
tss@2421
   342
int client_send_line(struct client *client, const char *fmt, ...)
tss@1043
   343
{
tss@1043
   344
	va_list va;
tss@2421
   345
	ssize_t ret;
tss@1043
   346
tss@1043
   347
	if (client->output->closed)
tss@2421
   348
		return -1;
tss@1043
   349
tss@1043
   350
	va_start(va, fmt);
tss@2421
   351
tss@7226
   352
	T_BEGIN {
tss@6940
   353
		string_t *str;
tss@2421
   354
tss@6940
   355
		str = t_str_new(256);
tss@6940
   356
		str_vprintfa(str, fmt, va);
tss@6940
   357
		str_append(str, "\r\n");
tss@6940
   358
tss@6940
   359
		ret = o_stream_send(client->output,
tss@6940
   360
				    str_data(str), str_len(str));
tss@6940
   361
		i_assert(ret < 0 || (size_t)ret == str_len(str));
tss@7226
   362
	} T_END;
tss@2735
   363
	if (ret >= 0) {
tss@2421
   364
		if (o_stream_get_buffer_used_size(client->output) <
tss@2421
   365
		    OUTBUF_THROTTLE_SIZE) {
tss@2421
   366
			ret = 1;
tss@2421
   367
			client->last_output = ioloop_time;
tss@2421
   368
		} else {
tss@2421
   369
			ret = 0;
tss@2421
   370
			if (client->io != NULL) {
tss@2421
   371
				/* no more input until client has read
tss@2421
   372
				   our output */
tss@3879
   373
				io_remove(&client->io);
tss@3352
   374
tss@3352
   375
				/* If someone happens to flush output,
tss@3352
   376
				   we want to get our IO handler back in
tss@3352
   377
				   flush callback */
tss@3352
   378
				o_stream_set_flush_pending(client->output,
tss@3352
   379
							   TRUE);
tss@2421
   380
			}
tss@2421
   381
		}
tss@2421
   382
	}
tss@2421
   383
tss@1043
   384
	va_end(va);
tss@2421
   385
	return (int)ret;
tss@1043
   386
}
tss@1043
   387
tss@1044
   388
void client_send_storage_error(struct client *client)
tss@1044
   389
{
tss@5613
   390
	enum mail_error error;
tss@1044
   391
tss@1915
   392
	if (mailbox_is_inconsistent(client->mailbox)) {
tss@1415
   393
		client_send_line(client, "-ERR Mailbox is in inconsistent "
tss@1415
   394
				 "state, please relogin.");
tss@3384
   395
		client_disconnect(client, "Mailbox is in inconsistent state.");
tss@1415
   396
		return;
tss@1415
   397
	}
tss@1415
   398
tss@5613
   399
	client_send_line(client, "-ERR %s",
tss@5613
   400
			 mail_storage_get_last_error(client->inbox_ns->storage,
tss@5613
   401
						     &error));
tss@1044
   402
}
tss@1044
   403
tss@7927
   404
bool client_handle_input(struct client *client)
tss@1043
   405
{
tss@1043
   406
	char *line, *args;
tss@3469
   407
	int ret;
tss@1043
   408
tss@1043
   409
	o_stream_cork(client->output);
tss@1043
   410
	while (!client->output->closed &&
tss@1043
   411
	       (line = i_stream_next_line(client->input)) != NULL) {
tss@1043
   412
		args = strchr(line, ' ');
tss@4816
   413
		if (args != NULL)
tss@1043
   414
			*args++ = '\0';
tss@1043
   415
tss@7226
   416
		T_BEGIN {
tss@6940
   417
			ret = client_command_execute(client, line,
tss@6940
   418
						     args != NULL ? args : "");
tss@7226
   419
		} T_END;
tss@3469
   420
		if (ret >= 0) {
tss@1056
   421
			client->bad_counter = 0;
tss@2421
   422
			if (client->cmd != NULL) {
tss@3336
   423
				o_stream_set_flush_pending(client->output,
tss@3336
   424
							   TRUE);
tss@2421
   425
				client->waiting_input = TRUE;
tss@2421
   426
				break;
tss@2421
   427
			}
tss@2421
   428
		} else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) {
tss@1056
   429
			client_send_line(client, "-ERR Too many bad commands.");
tss@3384
   430
			client_disconnect(client, "Too many bad commands.");
tss@1056
   431
		}
tss@1043
   432
	}
tss@2421
   433
	o_stream_uncork(client->output);
tss@1043
   434
tss@7927
   435
	if (client->output->closed) {
tss@3384
   436
		client_destroy(client, NULL);
tss@7927
   437
		return FALSE;
tss@7927
   438
	}
tss@7927
   439
	return TRUE;
tss@7927
   440
}
tss@7927
   441
tss@7927
   442
static void client_input(struct client *client)
tss@7927
   443
{
tss@7927
   444
	if (client->cmd != NULL) {
tss@7927
   445
		/* we're still processing a command. wait until it's
tss@7927
   446
		   finished. */
tss@7927
   447
		io_remove(&client->io);
tss@7927
   448
		client->waiting_input = TRUE;
tss@7927
   449
		return;
tss@7927
   450
	}
tss@7927
   451
tss@7927
   452
	client->waiting_input = FALSE;
tss@7927
   453
	client->last_input = ioloop_time;
tss@7927
   454
	timeout_reset(client->to_idle);
tss@8718
   455
	if (client->to_commit != NULL)
tss@8718
   456
		timeout_reset(client->to_commit);
tss@7927
   457
tss@7927
   458
	switch (i_stream_read(client->input)) {
tss@7927
   459
	case -1:
tss@7927
   460
		/* disconnected */
tss@7927
   461
		client_destroy(client, NULL);
tss@7927
   462
		return;
tss@7927
   463
	case -2:
tss@7927
   464
		/* line too long, kill it */
tss@7927
   465
		client_send_line(client, "-ERR Input line too long.");
tss@7927
   466
		client_destroy(client, "Input line too long");
tss@7927
   467
		return;
tss@7927
   468
	}
tss@7927
   469
tss@7927
   470
	(void)client_handle_input(client);
tss@1043
   471
}
tss@1043
   472
tss@4907
   473
static int client_output(struct client *client)
tss@2421
   474
{
tss@2421
   475
	int ret;
tss@2421
   476
tss@2421
   477
	if ((ret = o_stream_flush(client->output)) < 0) {
tss@3384
   478
		client_destroy(client, NULL);
tss@2790
   479
		return 1;
tss@2421
   480
	}
tss@2421
   481
tss@2421
   482
	client->last_output = ioloop_time;
tss@7103
   483
	timeout_reset(client->to_idle);
tss@8718
   484
	if (client->to_commit != NULL)
tss@8718
   485
		timeout_reset(client->to_commit);
tss@2421
   486
tss@3394
   487
	if (client->cmd != NULL) {
tss@3394
   488
		o_stream_cork(client->output);
tss@2494
   489
		client->cmd(client);
tss@3394
   490
		o_stream_uncork(client->output);
tss@3394
   491
	}
tss@2494
   492
tss@3391
   493
	if (client->cmd == NULL) {
tss@3391
   494
		if (o_stream_get_buffer_used_size(client->output) <
tss@3391
   495
		    OUTBUF_THROTTLE_SIZE/2 && client->io == NULL) {
tss@3391
   496
			/* enable input again */
tss@3391
   497
			client->io = io_add(i_stream_get_fd(client->input),
tss@3391
   498
					    IO_READ, client_input, client);
tss@3391
   499
		}
tss@3391
   500
		if (client->io != NULL && client->waiting_input)
tss@3391
   501
			client_input(client);
tss@2421
   502
	}
tss@2494
   503
tss@2790
   504
	return client->cmd == NULL;
tss@2421
   505
}
tss@2421
   506
tss@1043
   507
void clients_init(void)
tss@1043
   508
{
tss@1043
   509
	my_client = NULL;
tss@1043
   510
}
tss@1043
   511
tss@1043
   512
void clients_deinit(void)
tss@1043
   513
{
tss@1043
   514
	if (my_client != NULL) {
tss@1043
   515
		client_send_line(my_client, "-ERR Server shutting down.");
tss@4096
   516
		client_destroy(my_client, "Server shutting down");
tss@1043
   517
	}
tss@1043
   518
}