Mercurial > dovecot > original-hg > dovecot-1.2
view src/pop3/client.c @ 2039:f0925b2271e1 HEAD
Added pop3_mails_keep_recent option. Fixed recent assert crash.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 23 May 2004 01:36:46 +0300 |
parents | 62faff1d0047 |
children | 952a4106d3b8 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "ioloop.h" #include "network.h" #include "istream.h" #include "ostream.h" #include "mail-storage.h" #include "commands.h" #include "mail-search.h" #include <stdlib.h> /* max. length of input command line (spec says 512) */ #define MAX_INBUF_SIZE 2048 /* If we can't send a buffer in a minute, disconnect the client */ #define CLIENT_OUTPUT_TIMEOUT (60*1000) /* Disconnect client when it sends too many bad commands in a row */ #define CLIENT_MAX_BAD_COMMANDS 20 /* Disconnect client after idling this many seconds */ #define CLIENT_IDLE_TIMEOUT (60*30) extern struct mail_storage_callbacks mail_storage_callbacks; static struct client *my_client; /* we don't need more than one currently */ static struct timeout *to_idle; static void client_input(void *context); static void client_output_timeout(void *context) { struct client *client = context; i_stream_close(client->input); o_stream_close(client->output); } static int init_mailbox(struct client *client) { struct mail_search_arg search_arg; struct mailbox_transaction_context *t; struct mail_search_context *ctx; struct mail *mail; struct mailbox_status status; int i, failed; if (mailbox_get_status(client->mailbox, STATUS_MESSAGES | STATUS_UIDVALIDITY, &status) < 0) { client_send_storage_error(client); return FALSE; } client->messages_count = status.messages; client->deleted_size = 0; client->uidvalidity = status.uidvalidity; if (client->messages_count == 0) return TRUE; memset(&search_arg, 0, sizeof(search_arg)); search_arg.type = SEARCH_ALL; client->message_sizes = i_new(uoff_t, client->messages_count); for (i = 0; i < 2; i++) { t = mailbox_transaction_begin(client->mailbox, FALSE); ctx = mailbox_search_init(t, NULL, &search_arg, NULL, MAIL_FETCH_SIZE, NULL); if (ctx == NULL) { client_send_storage_error(client); mailbox_transaction_rollback(t); return FALSE; } client->total_size = 0; client->deleted_size = 0; failed = FALSE; while ((mail = mailbox_search_next(ctx)) != NULL) { uoff_t size = mail->get_size(mail); if (size == (uoff_t)-1) { failed = TRUE; break; } client->total_size += size; i_assert(mail->seq <= client->messages_count); client->message_sizes[mail->seq-1] = size; } if (mailbox_search_deinit(ctx) < 0) { client_send_storage_error(client); mailbox_transaction_rollback(t); return FALSE; } if (!failed) { mailbox_transaction_commit(t); return TRUE; } /* well, sync and try again */ mailbox_transaction_rollback(t); if (mailbox_sync(client->mailbox, 0) < 0) { client_send_storage_error(client); mailbox_transaction_rollback(t); return FALSE; } } client_send_line(client, "-ERR [IN-USE] Couldn't sync mailbox."); return FALSE; } struct client *client_create(int hin, int hout, struct mail_storage *storage) { struct client *client; enum mailbox_open_flags flags; client = i_new(struct client, 1); client->input = i_stream_create_file(hin, default_pool, MAX_INBUF_SIZE, FALSE); client->output = o_stream_create_file(hout, default_pool, 4096, FALSE); /* set timeout for sending data */ o_stream_set_blocking(client->output, CLIENT_OUTPUT_TIMEOUT, client_output_timeout, client); client->io = io_add(hin, IO_READ, client_input, client); client->last_input = ioloop_time; client->storage = storage; mail_storage_set_callbacks(storage, &mail_storage_callbacks, client); flags = 0; if (getenv("POP3_MAILS_KEEP_RECENT") != NULL) flags |= MAILBOX_OPEN_KEEP_RECENT; client->mailbox = mailbox_open(storage, "INBOX", flags); if (client->mailbox == NULL) { client_send_line(client, "-ERR No INBOX for user."); client_destroy(client); return NULL; } if (!init_mailbox(client)) { client_destroy(client); return NULL; } i_assert(my_client == NULL); my_client = client; if (hook_client_created != NULL) hook_client_created(&client); return client; } void client_destroy(struct client *client) { o_stream_flush(client->output); if (client->mailbox != NULL) mailbox_close(client->mailbox); mail_storage_destroy(client->storage); i_free(client->message_sizes); i_free(client->deleted_bitmask); io_remove(client->io); i_stream_unref(client->input); o_stream_unref(client->output); i_free(client); /* quit the program */ my_client = NULL; io_loop_stop(ioloop); } void client_disconnect(struct client *client) { o_stream_flush(client->output); i_stream_close(client->input); o_stream_close(client->output); } void client_send_line(struct client *client, const char *fmt, ...) { va_list va; if (client->output->closed) return; t_push(); va_start(va, fmt); (void)o_stream_send_str(client->output, t_strdup_vprintf(fmt, va)); (void)o_stream_send(client->output, "\r\n", 2); va_end(va); t_pop(); } void client_send_storage_error(struct client *client) { const char *error; int syntax; if (mailbox_is_inconsistent(client->mailbox)) { client_send_line(client, "-ERR Mailbox is in inconsistent " "state, please relogin."); client_disconnect(client); return; } error = mail_storage_get_last_error(client->storage, &syntax); client_send_line(client, "-ERR %s", error != NULL ? error : "BUG: Unknown error"); } static void client_input(void *context) { struct client *client = context; char *line, *args; client->last_input = ioloop_time; switch (i_stream_read(client->input)) { case -1: /* disconnected */ client_destroy(client); return; case -2: /* line too long, kill it */ client_send_line(client, "-ERR Input line too long."); client_destroy(client); return; } o_stream_cork(client->output); while (!client->output->closed && (line = i_stream_next_line(client->input)) != NULL) { args = strchr(line, ' '); if (args == NULL) args = ""; else *args++ = '\0'; if (client_command_execute(client, line, args)) client->bad_counter = 0; else if (++client->bad_counter > CLIENT_MAX_BAD_COMMANDS) { client_send_line(client, "-ERR Too many bad commands."); client_disconnect(client); } } o_stream_flush(client->output); if (client->output->closed) client_destroy(client); } static void idle_timeout(void *context __attr_unused__) { if (my_client == NULL) return; if (ioloop_time - my_client->last_input >= CLIENT_IDLE_TIMEOUT) { client_send_line(my_client, "-ERR Disconnected for inactivity."); client_destroy(my_client); } } void clients_init(void) { my_client = NULL; to_idle = timeout_add(10000, idle_timeout, NULL); } void clients_deinit(void) { if (my_client != NULL) { client_send_line(my_client, "-ERR Server shutting down."); client_destroy(my_client); } timeout_remove(to_idle); }