/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "istream.h"
#include "ostream.h"
#include "strescape.h"
#include "imap-parser.h"
/* We use this macro to read atoms from input. It should probably contain
everything some day, but for now we can't handle some input otherwise:
']' is required for parsing section (FETCH BODY[])
'%', '*' and ']' are valid list-chars for LIST patterns
'\' is used in flags */
#define IS_ATOM_PARSER_INPUT(c) \
((c) == '(' || (c) == ')' || (c) == '{' || \
(c) == '"' || (c) <= 32 || (c) == 0x7f)
#define is_linebreak(c) \
((c) == '\r' || (c) == '\n')
enum arg_parse_type {
ARG_PARSE_NONE = 0,
};
struct imap_parser {
/* permanent */
int refcount;
/* reset by imap_parser_reset(): */
const char *error_msg;
};
struct imap_parser *
{
1024);
return parser;
}
{
}
{
return;
}
{
}
{
parser->str_first_escape = 0;
parser->literal_size = 0;
}
{
}
enum imap_parser_error *error_r)
{
}
/* skip over everything parsed so far, plus the following whitespace */
const unsigned char **data,
{
size_t i;
if ((*data)[i] != ' ')
break;
}
*data += i;
*data_size -= i;
return *data_size > 0;
}
{
return arg;
}
{
}
{
/* we're not inside list */
return TRUE;
}
return FALSE;
}
} else {
}
return TRUE;
}
static char *
{
char *ret;
return ret;
}
{
char *str;
case ARG_PARSE_ATOM:
case ARG_PARSE_TEXT:
/* NIL argument. it might be an actual NIL, but if
we're reading astring, it's an atom and we can't
lose its case. */
} else {
/* simply save the string */
}
break;
case ARG_PARSE_STRING:
/* data is quoted and may contain escapes. */
/* remove the escapes */
if (parser->str_first_escape >= 0 &&
/* -1 because we skipped the '"' prefix */
}
break;
case ARG_PARSE_LITERAL_DATA:
/* save literal size */
break;
}
/* fall through */
else
break;
default:
i_unreached();
}
}
{
const char *error_msg;
if (IS_ATOM_PARSER_INPUT((unsigned char)chr))
error_msg = "Invalid characters in atom";
else if ((chr & 0x80) != 0)
error_msg = "8bit data in atom";
else
return TRUE;
return TRUE;
return FALSE;
}
{
size_t i;
/* read until we've found space, CR or LF. */
break;
} else if (data[i] == ')') {
break;
IMAP_PARSE_FLAG_ATOM_ALLCHARS) == 0) {
return FALSE;
}
/* assume it's part of the atom */
return FALSE;
}
}
{
size_t i;
/* read until we've found non-escaped ", CR or LF */
if (data[i] == '"') {
i++; /* skip the trailing '"' too */
break;
}
if (data[i] == '\\') {
if (i+1 == data_size) {
/* known data ends with '\' - leave it to
next time as well if it happens to be \" */
break;
}
/* save the first escaped char */
if (parser->str_first_escape < 0)
parser->str_first_escape = i;
/* skip the escaped char */
i++;
}
string always ends with '"', so it's an error if we found
a linebreak.. */
if (is_linebreak(data[i]) &&
return FALSE;
}
}
}
{
return FALSE;
}
/* too long string, abort. */
return FALSE;
}
/* make sure this continuation is sent to the
client as soon as possible */
}
}
}
return TRUE;
}
const unsigned char *data,
{
/* expecting digits + "}" */
if (data[i] == '}') {
return imap_parser_literal_end(parser);
}
if (parser->literal_nonsync) {
return FALSE;
}
if (data[i] == '+') {
continue;
}
return FALSE;
}
/* wrapped around, abort. */
return FALSE;
}
}
return FALSE;
}
const unsigned char *data,
{
if (parser->literal_skip_crlf) {
/* skip \r\n or \n, anything else gives an error */
if (data_size == 0)
return FALSE;
if (*data == '\r') {
if (data_size == 0)
return FALSE;
}
if (*data != '\n') {
return FALSE;
}
}
/* now we just wait until we've read enough data */
return FALSE;
else {
return TRUE;
}
} else {
/* we want to save only literal size, not the literal itself. */
return FALSE;
}
}
{
return FALSE;
return FALSE;
}
{
return FALSE;
return FALSE;
}
{
size_t i;
/* read until end of line */
if (is_linebreak(data[i])) {
break;
}
}
}
/* Returns TRUE if argument was fully processed. Also returns TRUE if
an argument inside a list was processed. */
{
const unsigned char *data;
if (data_size == 0)
return FALSE;
/* we haven't started parsing yet */
return FALSE;
if (parser->cur_resp_text &&
/* we just parsed [resp-text-code] */
break;
}
switch (data[0]) {
case '\r':
if (data_size == 1) {
/* wait for LF */
return FALSE;
}
return FALSE;
}
/* fall through */
case '\n':
/* unexpected end of line */
return FALSE;
}
return FALSE;
case '"':
break;
case '~':
/* This could be either literal8 or atom */
if (data_size == 1) {
/* wait for the next char */
return FALSE;
}
break;
}
return FALSE;
}
parser->literal_size = 0;
break;
case '{':
parser->literal_size = 0;
break;
case '(':
return FALSE;
}
break;
case ')':
if (!imap_parser_close_list(parser))
return FALSE;
/* end of argument */
return TRUE;
}
break;
default:
return FALSE;
break;
}
}
case ARG_PARSE_ATOM:
return FALSE;
break;
if (imap_parser_is_next_resp_text(parser)) {
[resp-text-code] the rest of the message can contain
pretty much any random text, which we can't parse
as if it was valid IMAP input */
}
break;
case ARG_PARSE_STRING:
return FALSE;
break;
case ARG_PARSE_LITERAL8:
return FALSE;
return FALSE;
}
/* fall through */
case ARG_PARSE_LITERAL:
return FALSE;
/* pass through to parsing data. since input->skip was
modified, we need to get the data start position again. */
/* fall through */
case ARG_PARSE_LITERAL_DATA:
return FALSE;
break;
case ARG_PARSE_TEXT:
return FALSE;
break;
default:
i_unreached();
}
return TRUE;
}
/* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */
{
return -1;
}
return ret;
}
enum imap_parser_flags flags,
{
if (parser->args_added_extra_eol) {
/* delete EOL */
}
if (!imap_parser_read_arg(parser))
break;
break;
}
}
/* error, abort */
return -1;
/* all arguments read / end of line. */
} else {
/* need more data */
return -2;
}
}
static struct imap_arg *
{
unsigned int count;
count--;
return NULL;
/* maybe the list ends with literal size */
if (count == 0)
return NULL;
}
}
{
return FALSE;
}
{
/* delete EOL */
/* delete literal size */
}
enum imap_parser_flags flags,
{
const unsigned char *data;
int ret;
if (ret == -1)
return -1;
if (ret == -2) {
/* we should have noticed end of everything except atom */
}
}
}
{
const unsigned char *data;
for (i = 0; i < data_size; i++) {
break;
}
if (i < data_size) {
} else {
return NULL;
}
}