Mercurial > dovecot > core-2.2
view src/pop3/commands.c @ 1071:d71e0aa00d75 HEAD
Don't crash if we couldn't open message.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sat, 01 Feb 2003 13:44:40 +0200 |
parents | d805c2f1d6a9 |
children | 1fad8b3d7ef1 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "common.h" #include "istream.h" #include "ostream.h" #include "str.h" #include "message-size.h" #include "mail-storage.h" #include "capability.h" #include "commands.h" #define MSGS_BITMASK_SIZE(client) \ ((client->messages_count + (CHAR_BIT-1)) / CHAR_BIT) 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 == 0 || num > client->messages_count) { client_send_line(client, "-ERR There's no message %u.", num); return NULL; } num--; 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 int cmd_capa(struct client *client, const char *args __attr_unused__) { client_send_line(client, "+OK\r\n"POP3_CAPABILITY_REPLY"."); return TRUE; } static int cmd_dele(struct client *client, const char *args) { unsigned int msgnum; if (get_msgnum(client, args, &msgnum) == NULL) return FALSE; 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."); return TRUE; } static int cmd_list(struct client *client, const char *args) { unsigned int i; if (*args == '\0') { client_send_line(client, "+OK %u messages:", client->messages_count); for (i = 0; i < client->messages_count; i++) { client_send_line(client, "%u %"PRIuUOFF_T, i+1, client->message_sizes[i]); } client_send_line(client, "."); } else { unsigned int msgnum; if (get_msgnum(client, args, &msgnum) == NULL) return FALSE; client_send_line(client, "+OK %u %"PRIuUOFF_T, msgnum+1, client->message_sizes[msgnum]); } return TRUE; } static int cmd_noop(struct client *client, const char *args __attr_unused__) { client_send_line(client, "+OK"); return TRUE; } static int 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 TRUE; } 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 != 0) { 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); } memset(&flags, 0, sizeof(flags)); flags.flags = MAIL_DELETED; if (str_len(set) == 0) client_send_line(client, "+OK Logging out."); else if (client->mailbox->update_flags(client->mailbox, str_c(set)+1, 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); return TRUE; } static void stream_send_escaped(struct ostream *output, struct istream *input, uoff_t body_lines) { const unsigned char *data; unsigned char last, add; size_t i, size; int cr_skipped, in_header; if (body_lines != (uoff_t)-1) body_lines++; /* internally we count the empty line too */ cr_skipped = FALSE; in_header = TRUE; last = '\0'; while ((body_lines > 0 || in_header) && i_stream_read_data(input, &data, &size, 0) > 0) { add = '\0'; for (i = 0; i < size; i++) { if (in_header && (data[i] == '\r' || data[i] == '\n')) { if (i == 0 && (last == '\0' || last == '\n')) in_header = FALSE; else if (i > 0 && data[i-1] == '\n') in_header = FALSE; } if (data[i] == '\n') { if ((i == 0 && last != '\r') || (i > 0 && data[i-1] != '\r')) { /* missing CR */ add = '\r'; break; } if (!in_header) { if (--body_lines == 0) { i++; break; } } } else if (data[i] == '.' && ((i == 0 && last == '\n') || (i > 0 && data[i-1] == '\n'))) { /* escape the dot */ add = '.'; i++; break; } } if (o_stream_send(output, data, i) < 0) return; if (add != '\0') { if (o_stream_send(output, &add, 1) < 0) return; last = add; } else { last = data[i-1]; } i_stream_skip(input, i); } } static void fetch(struct client *client, unsigned int msgnum, uoff_t body_lines) { struct mail_fetch_context *ctx; struct mail *mail; struct istream *stream; ctx = client->mailbox->fetch_init(client->mailbox, MAIL_FETCH_STREAM_HEADER | MAIL_FETCH_STREAM_BODY, NULL, dec2str(msgnum+1), FALSE); if (ctx == NULL) { client_send_storage_error(client); return; } mail = client->mailbox->fetch_next(ctx); stream = mail == NULL ? NULL : mail->get_stream(mail, NULL, NULL); if (stream == NULL) client_send_line(client, "-ERR Message not found."); else { if (body_lines == (uoff_t)-1) { client_send_line(client, "+OK %"PRIuUOFF_T" octets", client->message_sizes[msgnum]); } else { client_send_line(client, "+OK"); } stream_send_escaped(client->output, stream, body_lines); client_send_line(client, "."); } (void)client->mailbox->fetch_deinit(ctx, NULL); } static int cmd_retr(struct client *client, const char *args) { unsigned int msgnum; if (get_msgnum(client, args, &msgnum) == NULL) return FALSE; fetch(client, msgnum, (uoff_t)-1); return TRUE; } static int 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"); return TRUE; } static int cmd_stat(struct client *client, const char *args __attr_unused__) { client_send_line(client, "+OK %u %"PRIuUOFF_T, client-> messages_count, client->total_size); return TRUE; } static int cmd_top(struct client *client, const char *args) { unsigned int msgnum; uoff_t max_lines; args = get_msgnum(client, args, &msgnum); if (args == NULL) return FALSE; if (get_size(client, args, &max_lines) == NULL) return FALSE; fetch(client, msgnum, max_lines); return TRUE; } 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.%u" : "+OK %u %u.%u", mail->seq, client->uidvalidity, mail->uid); found = TRUE; } (void)client->mailbox->fetch_deinit(ctx, NULL); if (!found && message != 0) client_send_line(client, "-ERR Message not found."); } static int 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) return FALSE; list_uids(client, msgnum+1); } return TRUE; } int 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 'C': if (strcmp(name, "CAPA") == 0) return cmd_capa(client, args); break; case 'D': if (strcmp(name, "DELE") == 0) return cmd_dele(client, args); break; case 'L': if (strcmp(name, "LIST") == 0) return cmd_list(client, args); break; case 'N': if (strcmp(name, "NOOP") == 0) return cmd_noop(client, args); break; case 'Q': if (strcmp(name, "QUIT") == 0) return cmd_quit(client, args); break; case 'R': if (strcmp(name, "RETR") == 0) return cmd_retr(client, args); if (strcmp(name, "RSET") == 0) return cmd_rset(client, args); break; case 'S': if (strcmp(name, "STAT") == 0) return cmd_stat(client, args); break; case 'T': if (strcmp(name, "TOP") == 0) return cmd_top(client, args); break; case 'U': if (strcmp(name, "UIDL") == 0) return cmd_uidl(client, args); break; } client_send_line(client, "-ERR Unknown command: %s", name); return FALSE; }