Mercurial > dovecot > original-hg > dovecot-1.2
diff src/pop3/commands.c @ 1043:cacabd33c68a HEAD
Initial code for POP3 server. RETR isn't working right yet, there's some
syncing problems to figure out (pop3 wants to keep the mailbox locked) and
the whole pop3-login process is still missing.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Mon, 27 Jan 2003 07:45:47 +0200 |
parents | |
children | 50d258907c99 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pop3/commands.c Mon Jan 27 07:45:47 2003 +0200 @@ -0,0 +1,428 @@ +/* Copyright (C) 2002 Timo Sirainen */ + +#include "common.h" +#include "str.h" +#include "message-size.h" +#include "mail-storage.h" +#include "commands.h" + +#define MSGS_BITMASK_SIZE(client) \ + ((client->messages_count + (CHAR_BIT-1)) / CHAR_BIT) + +static void client_send_storage_error(struct client *client) +{ + const char *error; + + error = client->storage->get_last_error(client->storage, NULL); + client_send_line(client, "-ERR %s", error); +} + +static const char *get_msgnum(struct client *client, const char *args, + unsigned int *msgnum) +{ + unsigned int num, last_num; + + num = 0; + while (*args != '\0' && *args != ' ') { + if (*args < '0' || *args > '9') { + client_send_line(client, + "-ERR Invalid message number: %s", args); + return NULL; + } + + last_num = num; + num = num*10 + (*args - '0'); + if (num < last_num) { + client_send_line(client, + "-ERR Message number too large: %s", args); + return NULL; + } + args++; + } + + if (num > client->messages_count) { + client_send_line(client, + "-ERR There's only %u messages.", + client->messages_count); + return NULL; + } + + if (client->deleted) { + if (client->deleted_bitmask[num / CHAR_BIT] & + (1 << (num % CHAR_BIT))) { + client_send_line(client, "-ERR Message is deleted."); + return NULL; + } + } + + while (*args == ' ') args++; + + *msgnum = num; + return args; +} + +static const char *get_size(struct client *client, const char *args, + uoff_t *size) +{ + uoff_t num, last_num; + + num = 0; + while (*args != '\0' && *args != ' ') { + if (*args < '0' || *args > '9') { + client_send_line(client, "-ERR Invalid size: %s", + args); + return NULL; + } + + last_num = num; + num = num*10 + (*args - '0'); + if (num < last_num) { + client_send_line(client, "-ERR Size too large: %s", + args); + return NULL; + } + args++; + } + + while (*args == ' ') args++; + + *size = num; + return args; +} + +static void cmd_dele(struct client *client, const char *args) +{ + unsigned int msgnum; + + if (get_msgnum(client, args, &msgnum) == NULL) + return; + + if (!client->deleted) { + client->deleted_bitmask = i_malloc(MSGS_BITMASK_SIZE(client)); + client->deleted = TRUE; + } + + client->deleted_bitmask[msgnum / CHAR_BIT] |= 1 << (msgnum % CHAR_BIT); + client_send_line(client, "+OK Marked to be deleted."); +} + +static void list_sizes(struct client *client, unsigned int message) +{ + struct mail_fetch_context *ctx; + struct mail *mail; + const char *messageset; + int found = FALSE; + + if (client->messages_count == 0 && message == 0) + return; + + messageset = message == 0 ? + t_strdup_printf("1:%u", client->messages_count) : + t_strdup_printf("%u", message); + + ctx = client->mailbox->fetch_init(client->mailbox, MAIL_FETCH_SIZE, + NULL, messageset, FALSE); + if (ctx == NULL) { + client_send_storage_error(client); + return; + } + + while ((mail = client->mailbox->fetch_next(ctx)) != NULL) { + uoff_t size = mail->get_size(mail); + + client_send_line(client, message == 0 ? "%u %"PRIuUOFF_T : + "+OK %u %"PRIuUOFF_T, mail->seq, size); + found = TRUE; + } + + (void)client->mailbox->fetch_deinit(ctx, NULL); + + if (!found && message != 0) + client_send_line(client, "-ERR Message not found."); +} + +static void cmd_list(struct client *client, const char *args) +{ + if (*args == '\0') { + client_send_line(client, "+OK %u messages:", + client->messages_count); + list_sizes(client, 0); + client_send_line(client, "."); + } else { + unsigned int msgnum; + + if (get_msgnum(client, args, &msgnum) != NULL) + list_sizes(client, msgnum); + } +} + +static void cmd_noop(struct client *client, const char *args __attr_unused__) +{ + client_send_line(client, "+OK"); +} + +static void cmd_quit(struct client *client, const char *args __attr_unused__) +{ + unsigned int first, last, msgnum, max, i, j; + struct mail_full_flags flags; + string_t *set; + + if (!client->deleted) { + client_send_line(client, "+OK Logging out."); + client_disconnect(client); + return; + } + + set = t_str_new(1024); + first = last = 0; msgnum = 1; + max = MSGS_BITMASK_SIZE(client); + for (i = 0; i < max; i++) { + if (client->deleted_bitmask[i] == 0) { + msgnum += CHAR_BIT; + continue; + } + + for (j = 0; j < CHAR_BIT; j++, msgnum++) { + if ((client->deleted_bitmask[i] & (1 << j)) == 0) + continue; + + if (last == msgnum-1 && last != 0) + last++; + else { + if (first == last) + str_printfa(set, ",%u", first); + else + str_printfa(set, ",%u:%u", first, last); + first = last = msgnum; + } + } + } + + if (first != 0) { + if (first == last) + str_printfa(set, ",%u", first); + else + str_printfa(set, ",%u:%u", first, last); + } + + if (str_len(set) == 0) + client_send_line(client, "+OK Logging out."); + else if (client->mailbox->update_flags(client->mailbox, str_c(set), + FALSE, &flags, MODIFY_ADD, + FALSE, NULL) && + client->mailbox->expunge(client->mailbox, FALSE)) + client_send_line(client, "+OK Logging out, messages deleted."); + else + client_send_storage_error(client); + + client_disconnect(client); +} + +static void fetch(struct client *client, unsigned int msgnum, uoff_t max_lines) +{ + struct mail_fetch_context *ctx; + struct mail *mail; + struct istream *stream; + struct message_size hdr_size, body_size; + + ctx = client->mailbox->fetch_init(client->mailbox, + MAIL_FETCH_STREAM_HEADER | + MAIL_FETCH_STREAM_BODY, + NULL, t_strdup_printf("%u", msgnum), + FALSE); + if (ctx == NULL) { + client_send_storage_error(client); + return; + } + + mail = client->mailbox->fetch_next(ctx); + if (mail == NULL) + client_send_line(client, "-ERR Message not found."); + else { + stream = mail->get_stream(mail, &hdr_size, &body_size); + message_size_add(&body_size, &hdr_size); + + client_send_line(client, "+OK %"PRIuUOFF_T" octets", + body_size.virtual_size); + + // FIXME: "." lines needs to be escaped + // FIXME: and send only max_lines + message_send(client->output, stream, &body_size, 0, (uoff_t)-1); + } + + (void)client->mailbox->fetch_deinit(ctx, NULL); +} + +static void cmd_retr(struct client *client, const char *args) +{ + unsigned int msgnum; + + if (get_msgnum(client, args, &msgnum) != NULL) + fetch(client, msgnum, (uoff_t)-1); +} + +static void cmd_rset(struct client *client, const char *args __attr_unused__) +{ + if (client->deleted) { + client->deleted = FALSE; + memset(client->deleted_bitmask, 0, MSGS_BITMASK_SIZE(client)); + } + + client_send_line(client, "+OK"); +} + +static void cmd_stat(struct client *client, const char *args __attr_unused__) +{ + struct mail_fetch_context *ctx; + struct mail *mail; + uoff_t size, total_size; + const char *messageset; + + if (client->messages_count == 0) { + client_send_line(client, "+OK 0 0"); + return; + } + + messageset = t_strdup_printf("1:%u", client->messages_count); + ctx = client->mailbox->fetch_init(client->mailbox, MAIL_FETCH_SIZE, + NULL, messageset, FALSE); + if (ctx == NULL) { + client_send_storage_error(client); + return; + } + + total_size = 0; + while ((mail = client->mailbox->fetch_next(ctx)) != NULL) { + size = mail->get_size(mail); + if (size != (uoff_t)-1) + total_size += size; + } + + (void)client->mailbox->fetch_deinit(ctx, NULL); + + client_send_line(client, "+OK %u %"PRIuUOFF_T, + client->messages_count, total_size); +} + +static void cmd_top(struct client *client, const char *args) +{ + unsigned int msgnum; + uoff_t max_lines; + + if (get_msgnum(client, args, &msgnum) != NULL && + get_size(client, args, &max_lines)) + fetch(client, msgnum, max_lines); +} + +static void list_uids(struct client *client, unsigned int message) +{ + struct mail_fetch_context *ctx; + struct mail *mail; + const char *messageset; + int found = FALSE; + + if (client->messages_count == 0 && message == 0) + return; + + messageset = message == 0 ? + t_strdup_printf("1:%u", client->messages_count) : + t_strdup_printf("%u", message); + + ctx = client->mailbox->fetch_init(client->mailbox, 0, + NULL, messageset, FALSE); + if (ctx == NULL) { + client_send_storage_error(client); + return; + } + + while ((mail = client->mailbox->fetch_next(ctx)) != NULL) { + client_send_line(client, message == 0 ? + "%u %u" : "+OK %u %u", mail->seq, mail->uid); + found = TRUE; + } + + (void)client->mailbox->fetch_deinit(ctx, NULL); + + if (!found && message != 0) + client_send_line(client, "-ERR Message not found."); +} + +static void cmd_uidl(struct client *client, const char *args) +{ + if (*args == '\0') { + client_send_line(client, "+OK"); + list_uids(client, 0); + client_send_line(client, "."); + } else { + unsigned int msgnum; + + if (get_msgnum(client, args, &msgnum) != NULL) + list_uids(client, msgnum); + } +} + +void client_command_execute(struct client *client, + const char *name, const char *args) +{ + /* keep the command uppercased */ + name = str_ucase(t_strdup_noconst(name)); + + while (*args == ' ') args++; + + switch (*name) { + case 'D': + if (strcmp(name, "DELE") == 0) { + cmd_dele(client, args); + return; + } + break; + case 'L': + if (strcmp(name, "LIST") == 0) { + cmd_list(client, args); + return; + } + break; + case 'N': + if (strcmp(name, "NOOP") == 0) { + cmd_noop(client, args); + return; + } + break; + case 'Q': + if (strcmp(name, "QUIT") == 0) { + cmd_quit(client, args); + return; + } + break; + case 'R': + if (strcmp(name, "RETR") == 0) { + cmd_retr(client, args); + return; + } + if (strcmp(name, "RSET") == 0) { + cmd_rset(client, args); + return; + } + break; + case 'S': + if (strcmp(name, "STAT") == 0) { + cmd_stat(client, args); + return; + } + break; + case 'T': + if (strcmp(name, "TOP") == 0) { + cmd_top(client, args); + return; + } + break; + case 'U': + if (strcmp(name, "UIDL") == 0) { + cmd_uidl(client, args); + return; + } + break; + } + + client_send_line(client, "-ERR Unknown command: %s", name); +}