view src/imap/cmd-fetch.c @ 0:3b1985cbc908 HEAD

Initial revision
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Aug 2002 12:15:38 +0300
parents
children 1b34ec11fff8
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "common.h"
#include "commands.h"

/* Parse next digits in string into integer. Returns FALSE if the integer
   becomes too big and wraps. */
static int read_int(char **p, unsigned int *value)
{
	unsigned int prev;
	*value = 0;

	while (**p >= '0' && **p <= '9') {
		prev = *value;
		*value = *value * 10 + **p - '0';

		if (*value < prev)
			return FALSE;

		(*p)++;
	}

	return TRUE;
}

/* BODY[] and BODY.PEEK[] items. item points to next character after '[' */
static int parse_body_section(Client *client, const char *item,
			      MailFetchData *data, int peek)
{
	MailFetchBodyData *body;
	unsigned int num;
	const char *section;
	char *p;

	body = t_new(MailFetchBodyData, 1);
	body->peek = peek;

	p = (char *) t_strdup(item);

	/* read section */
	body->section = p;
	for (section = p; *p != ']'; p++) {
		if (*p == '\0') {
			client_send_tagline(client, t_strconcat(
				"BAD Missing ']' with ", item, NULL));
			return FALSE;
		}
	}
	*p++ = '\0';

	/* <start.end> */
	body->skip = body->max_size = 0;
	if (*p != '<' && *p != '\0') {
		client_send_tagline(client, t_strconcat(
			"BAD Unexpected character after ']' with ",
			item, NULL));
	} else if (*p == '<') {
		/* read start */
		p++;

		body->skip_set = TRUE;
		if (!read_int(&p, &num) || num > INT_MAX) {
			/* wrapped */
			client_send_tagline(client, t_strconcat(
				"BAD Too big partial start with ", item, NULL));
			return FALSE;
		}
		body->skip = num;

		if (*p == '.') {
			/* read end */
			p++;
			if (!read_int(&p, &num) || num > INT_MAX) {
				/* wrapped */
				client_send_tagline(client, t_strconcat(
					"BAD Too big partial end with ",
					item, NULL));
				return FALSE;
			}

                        body->max_size = num;
		}

		if (*p != '>') {
			client_send_tagline(client, t_strconcat(
				"BAD Invalid partial ", item, NULL));
			return FALSE;
		}
	}

	body->next = data->body_sections;
	data->body_sections = body;

	return TRUE;
}

static int parse_arg(Client *client, ImapArg *arg, MailFetchData *data)
{
	char *item;

	if (arg->type != IMAP_ARG_ATOM) {
		client_send_command_error(client,
					  "FETCH list contains non-atoms.");
		return FALSE;
	}

	item = arg->data.str;
	str_ucase(item);

	switch (*item) {
	case 'A':
		if (strcmp(item, "ALL") == 0) {
			data->flags = TRUE;
			data->internaldate = TRUE;
			data->rfc822_size = TRUE;
			data->envelope = TRUE;
		} else
			item = NULL;
		break;
	case 'B':
		/* all start with BODY so skip it */
		if (strncmp(item, "BODY", 4) != 0) {
			item = NULL;
			break;
		}
		item += 4;

		if (*item == '\0') {
			/* BODY */
			data->body = TRUE;
		} else if (*item == '[') {
			/* BODY[...] */
			if (!parse_body_section(client, item+1, data, FALSE))
				return FALSE;
		} else if (strncmp(item, ".PEEK[", 6) == 0) {
			/* BODY.PEEK[..] */
			if (!parse_body_section(client, item+6, data, TRUE))
				return FALSE;
		} else if (strcmp(item, "STRUCTURE") == 0) {
			/* BODYSTRUCTURE */
			data->bodystructure = TRUE;
		} else
			item = NULL;
		break;
	case 'E':
		if (strcmp(item, "ENVELOPE") == 0)
			data->envelope = TRUE;
		else
			item = NULL;
		break;
	case 'F':
		if (strcmp(item, "FLAGS") == 0)
			data->flags = TRUE;
		else if (strcmp(item, "FAST") == 0) {
			data->flags = TRUE;
			data->internaldate = TRUE;
			data->rfc822_size = TRUE;
		} else if (strcmp(item, "FULL") == 0) {
			data->flags = TRUE;
			data->internaldate = TRUE;
			data->rfc822_size = TRUE;
			data->envelope = TRUE;
			data->body = TRUE;
		} else
			item = NULL;
		break;
	case 'I':
		if (strcmp(item, "INTERNALDATE") == 0)
			data->internaldate = TRUE;
		else
			item = NULL;
		break;
	case 'R':
		/* all start with RFC822 so skip it */
		if (strncmp(item, "RFC822", 6) != 0) {
			item = NULL;
			break;
		}
		item += 6;

		if (*item == '\0') {
			/* RFC822 */
			data->rfc822 = TRUE;
			break;
		}

		/* only items beginning with "RFC822." left */
		if (*item != '.') {
			item = NULL;
			break;
		}
		item++;

		if (strcmp(item, "HEADER") == 0)
			data->rfc822_header = TRUE;
		else if (strcmp(item, "SIZE") == 0)
			data->rfc822_size = TRUE;
		else if (strcmp(item, "TEXT") == 0)
			data->rfc822_text = TRUE;
		else
			item = NULL;
		break;
	case 'U':
		if (strcmp(item, "UID") == 0)
			data->uid = TRUE;
		else
			item = NULL;
		break;
	default:
		item = NULL;
		break;
	}

	if (item == NULL) {
		/* unknown item */
		client_send_tagline(client, t_strconcat(
			"BAD Invalid item ", arg->data.str, NULL));
		return FALSE;
	}

	return TRUE;
}

int cmd_fetch(Client *client)
{
	ImapArg *args;
	ImapArgList *list;
	MailFetchData data;
	const char *messageset;
	int all_found;

	if (!client_read_args(client, 2, 0, &args))
		return FALSE;

	if (!client_verify_open_mailbox(client))
		return TRUE;

	messageset = imap_arg_string(&args[0]);
	if (messageset == NULL ||
	    (args[1].type != IMAP_ARG_LIST && args[1].type != IMAP_ARG_ATOM)) {
		client_send_command_error(client, "Invalid FETCH arguments.");
		return TRUE;
	}

	/* parse items argument */
	memset(&data, 0, sizeof(MailFetchData));
	if (args[1].type == IMAP_ARG_ATOM) {
		if (!parse_arg(client, &args[1], &data))
			return TRUE;
	} else {
		list = args[1].data.list;
		while (list != NULL) {
			ImapArg *arg = &list->arg;

			if (!parse_arg(client, arg, &data))
				return TRUE;

			list = list->next;
		}
	}

	data.messageset = messageset;
	data.uidset = client->cmd_uid;
	if (data.uidset)
                data.uid = TRUE;

	/* fetch it */
	if (client->mailbox->fetch(client->mailbox, &data,
				   client->outbuf, &all_found)) {
		/* NOTE: syncing isn't allowed here */
		client_send_tagline(client, all_found ? "OK Fetch completed." :
				    "NO Some messages were not found.");
	} else {
		client_send_storage_error(client);
	}

	return TRUE;
}