instead.
--HG--
branch : HEAD
uoff_t skip, max_size;
const char *const *fields;
int (*match_func) (const char *const *, const char *, size_t);
+
+ unsigned int fix_nuls:1;
};
struct partial_cache {
body->skip);
ret = message_send(ctx->output, stream, &body_size,
- skip_cr, body->max_size, &last_cr);
+ skip_cr, body->max_size, &last_cr,
+ !mail->has_no_nuls);
if (ret > 0) {
partial.cr_skipped = last_cr != 0;
partial.pos.physical_size =
const void *data, size_t size)
{
const unsigned char *str = data;
+ size_t i;
if (ctx->skip > 0) {
if (ctx->skip >= size) {
size = ctx->max_size - ctx->dest_size;
}
- if (ctx->dest != NULL)
- buffer_append(ctx->dest, str, size);
ctx->dest_size += size;
+ if (ctx->fix_nuls && (ctx->dest != NULL || ctx->output != NULL)) {
+ for (i = 0; i < size; ) {
+ if (str[i] != 0) {
+ i++;
+ continue;
+ }
+
+ /* NUL found, change it to #128 */
+ if (ctx->dest != NULL) {
+ buffer_append(ctx->dest, str, i);
+ buffer_append(ctx->dest, "\x80", 1);
+ } else {
+ if (o_stream_send(ctx->output, str, i) < 0 ||
+ o_stream_send(ctx->output, "\x80", 1) < 0)
+ return FALSE;
+ }
+
+ str += i+1;
+ size -= i+1;
+ i = 0;
+ }
+ }
+
+ if (ctx->dest != NULL)
+ buffer_append(ctx->dest, str, size);
if (ctx->output != NULL) {
if (o_stream_send(ctx->output, str, size) < 0)
return FALSE;
}
+
return ctx->dest_size < ctx->max_size;
}
/* fetch wanted headers from given data */
static int fetch_header_from(struct imap_fetch_context *ctx,
struct istream *input,
- const struct message_size *size,
+ const struct message_size *size, struct mail *mail,
const struct imap_fetch_body_data *body,
const char *header_section)
{
if (o_stream_send_str(ctx->output, str) < 0)
return FALSE;
return message_send(ctx->output, input, size,
- body->skip, body->max_size, NULL) >= 0;
+ body->skip, body->max_size, NULL,
+ !mail->has_no_nuls) >= 0;
}
/* partial headers - copy the wanted fields into memory, inserting
memset(&hdr_ctx, 0, sizeof(hdr_ctx));
hdr_ctx.skip = body->skip;
hdr_ctx.max_size = body->max_size;
+ hdr_ctx.fix_nuls = !mail->has_no_nuls;
failed = FALSE;
start_offset = input->v_offset;
if (stream == NULL)
return FALSE;
- return fetch_header_from(ctx, stream, &hdr_size, body, body->section);
+ return fetch_header_from(ctx, stream, &hdr_size,
+ mail, body, body->section);
}
/* Find message_part for section (eg. 1.3.4) */
&partial, stream, part->physical_pos +
part->header_size.physical_size, body->skip);
ret = message_send(ctx->output, stream, &part->body_size,
- skip_cr, body->max_size, &last_cr);
+ skip_cr, body->max_size, &last_cr,
+ !mail->has_no_nuls);
if (ret > 0) {
partial.cr_skipped = last_cr != 0;
partial.pos.physical_size =
strcmp(section, "MIME") == 0) {
i_stream_seek(stream, part->physical_pos);
return fetch_header_from(ctx, stream, &part->header_size,
- body, section);
+ mail, body, section);
}
i_warning("BUG: Accepted invalid section from user: '%s'",
return FALSE;
return message_send(ctx->output, stream, &body_size,
- 0, (uoff_t)-1, NULL) >= 0;
+ 0, (uoff_t)-1, NULL, !mail->has_no_nuls) >= 0;
}
static int fetch_send_rfc822_header(struct imap_fetch_context *ctx,
return FALSE;
return message_send(ctx->output, stream, &hdr_size,
- 0, (uoff_t)-1, NULL) >= 0;
+ 0, (uoff_t)-1, NULL, !mail->has_no_nuls) >= 0;
}
static int fetch_send_rfc822_text(struct imap_fetch_context *ctx,
i_stream_seek(stream, hdr_size.physical_size);
return message_send(ctx->output, stream, &body_size,
- 0, (uoff_t)-1, NULL) >= 0;
+ 0, (uoff_t)-1, NULL, !mail->has_no_nuls) >= 0;
}
static int fetch_mail(struct imap_fetch_context *ctx, struct mail *mail)
if (part == NULL) {
part = message_parse(pool, input,
update_header_cb, &ctx);
+ if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+ update->rec->index_flags |=
+ INDEX_MAIL_FLAG_HAS_NULS;
+ } else {
+ update->rec->index_flags |=
+ INDEX_MAIL_FLAG_HAS_NO_NULS;
+ }
} else {
/* cached, construct the bodystructure using it.
also we need to parse the header.. */
INDEX_MAIL_FLAG_DIRTY = 0x0004,
/* Maildir: Mail file is in new/ dir instead of cur/ */
- INDEX_MAIL_FLAG_MAILDIR_NEW = 0x0008
+ INDEX_MAIL_FLAG_MAILDIR_NEW = 0x0008,
+
+ /* Mail header or body is known to contain NUL characters. */
+ INDEX_MAIL_FLAG_HAS_NULS = 0x0010,
+ /* Mail header or body is known to not contain NUL characters. */
+ INDEX_MAIL_FLAG_HAS_NO_NULS = 0x0020
};
enum mail_lock_type {
string_t *name;
buffer_t *value_buf;
size_t skip;
+
+ int has_nuls;
};
static struct message_part *
static struct message_part *
message_parse_body(struct istream *input, struct message_boundary *boundaries,
- struct message_size *body_size);
+ struct message_size *body_size, int *has_nuls);
static struct message_part *
message_skip_boundary(struct istream *input,
struct message_boundary *boundaries,
- struct message_size *boundary_size);
+ struct message_size *boundary_size, int *has_nuls);
static void message_size_add_part(struct message_size *dest,
struct message_part *part)
{
struct message_part *parent_part, *next_part, *part;
struct message_boundary *b;
+ int has_nuls;
/* multipart message. add new boundary */
b = t_new(struct message_boundary, 1);
/* skip the data before the first boundary */
parent_part = parser_ctx->part;
next_part = message_skip_boundary(input, parser_ctx->boundaries,
- &parent_part->body_size);
+ &parent_part->body_size, &has_nuls);
+ if (has_nuls)
+ parent_part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
/* now, parse the parts */
while (next_part == parent_part) {
parser_ctx->part = part;
next_part = message_parse_part(input, parser_ctx);
+ if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+ /* it also belongs to parent */
+ parent_part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
+ }
+
/* update our size */
message_size_add_part(&parent_part->body_size, part);
/* skip the boundary */
next_part = message_skip_boundary(input, parser_ctx->boundaries,
- &parent_part->body_size);
+ &parent_part->body_size,
+ &has_nuls);
+ if (has_nuls)
+ parent_part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
}
/* remove boundary */
struct message_header_line *hdr;
struct message_part *next_part, *part;
uoff_t hdr_size;
+ int has_nuls;
hdr_ctx = message_parse_header_init(input,
&parser_ctx->part->header_size);
parser_ctx->callback(parser_ctx->part, NULL,
parser_ctx->context);
}
+ if (hdr_ctx->has_nuls)
+ parser_ctx->part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
message_parse_header_deinit(hdr_ctx);
i_assert((parser_ctx->part->flags & MUTEX_FLAGS) != MUTEX_FLAGS);
/* normal message, read until the next boundary */
part = parser_ctx->part;
next_part = message_parse_body(input, parser_ctx->boundaries,
- &part->body_size);
+ &part->body_size, &has_nuls);
+ if (has_nuls)
+ part->flags |= MESSAGE_PART_FLAG_HAS_NULS;
+ }
+
+ if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0 &&
+ part->parent != NULL) {
+ /* it also belongs to parent */
+ part->parent->flags |= MESSAGE_PART_FLAG_HAS_NULS;
}
return next_part;
}
static void message_skip_line(struct istream *input,
- struct message_size *msg_size, int skip_lf)
+ struct message_size *msg_size, int skip_lf,
+ int *has_nuls)
{
const unsigned char *msg;
size_t i, size, startpos;
startpos = 0;
+ *has_nuls = FALSE;
while (i_stream_read_data(input, &msg, &size, startpos) > 0) {
for (i = startpos; i < size; i++) {
- if (msg[i] == '\n') {
+ if (msg[i] == '\0')
+ *has_nuls = TRUE;
+ else if (msg[i] == '\n') {
if (!skip_lf) {
if (i > 0 && msg[i-1] == '\r')
i--;
static struct message_boundary *
message_find_boundary(struct istream *input,
struct message_boundary *boundaries,
- struct message_size *msg_size, int skip_over)
+ struct message_size *msg_size, int skip_over,
+ int *has_nuls)
{
struct message_boundary *boundary;
const unsigned char *msg;
boundary = NULL;
missing_cr_count = startpos = line_start = 0;
+ *has_nuls = FALSE;
while (i_stream_read_data(input, &msg, &size, startpos) > 0) {
for (i = startpos; i < size; i++) {
- if (msg[i] != '\n')
+ if (msg[i] != '\n') {
+ if (msg[i] == '\0')
+ *has_nuls = TRUE;
continue;
+ }
if (line_start != (size_t)-1 &&
i >= line_start+2 && msg[line_start] == '-' &&
static struct message_part *
message_parse_body(struct istream *input, struct message_boundary *boundaries,
- struct message_size *msg_size)
+ struct message_size *msg_size, int *has_nuls)
{
struct message_boundary *boundary;
struct message_size body_size;
if (boundaries == NULL) {
- message_get_body_size(input, &body_size, (uoff_t)-1, NULL);
+ message_get_body_size(input, &body_size,
+ (uoff_t)-1, NULL, has_nuls);
message_size_add(msg_size, &body_size);
- return NULL;
+ boundary = NULL;
} else {
boundary = message_find_boundary(input, boundaries,
- msg_size, FALSE);
- return boundary == NULL ? NULL : boundary->part;
+ msg_size, FALSE, has_nuls);
}
+
+ return boundary == NULL ? NULL : boundary->part;
}
/* skip data until next boundary is found. if it's end boundary,
static struct message_part *
message_skip_boundary(struct istream *input,
struct message_boundary *boundaries,
- struct message_size *boundary_size)
+ struct message_size *boundary_size, int *has_nuls)
{
struct message_boundary *boundary;
const unsigned char *msg;
int end_boundary;
boundary = message_find_boundary(input, boundaries,
- boundary_size, TRUE);
+ boundary_size, TRUE, has_nuls);
if (boundary == NULL)
return NULL;
end_boundary = msg[0] == '-' && msg[1] == '-';
/* skip the rest of the line */
- message_skip_line(input, boundary_size, !end_boundary);
+ message_skip_line(input, boundary_size, !end_boundary, has_nuls);
if (end_boundary) {
/* skip the footer */
- return message_parse_body(input, boundary->next, boundary_size);
+ return message_parse_body(input, boundary->next,
+ boundary_size, has_nuls);
}
return boundary == NULL ? NULL : boundary->part;
if (colon_pos == UINT_MAX) {
/* header name is huge. just skip it. */
message_skip_line(ctx->input, ctx->hdr_size,
- TRUE);
+ TRUE, &ctx->has_nuls);
continue;
}
/* end of headers, or error */
break;
}
+
+ if (msg[i] == '\0')
+ ctx->has_nuls = TRUE;
}
}
}
/* find '\n' */
for (i = startpos; i < parse_size; i++) {
- if (msg[i] == '\n')
- break;
+ if (msg[i] <= '\n') {
+ if (msg[i] == '\n')
+ break;
+ if (msg[i] == '\0')
+ ctx->has_nuls = TRUE;
+ }
}
if (i < parse_size) {
MESSAGE_PART_FLAG_TEXT = 0x08,
/* content-transfer-encoding: binary */
- MESSAGE_PART_FLAG_BINARY = 0x10
+ MESSAGE_PART_FLAG_BINARY = 0x10,
+
+ /* message part header or body contains NULs */
+ MESSAGE_PART_FLAG_HAS_NULS = 0x20
};
struct message_part {
off_t message_send(struct ostream *output, struct istream *input,
const struct message_size *msg_size,
- uoff_t virtual_skip, uoff_t max_virtual_size, int *last_cr)
+ uoff_t virtual_skip, uoff_t max_virtual_size, int *last_cr,
+ int fix_nuls)
{
const unsigned char *msg;
uoff_t old_limit, limit;
size_t i, size;
off_t ret;
- int cr_skipped, add_cr;
+ int cr_skipped, add;
if (last_cr != NULL)
*last_cr = -1;
if (max_virtual_size > msg_size->virtual_size - virtual_skip)
max_virtual_size = msg_size->virtual_size - virtual_skip;
- if (msg_size->physical_size == msg_size->virtual_size) {
+ if (msg_size->physical_size == msg_size->virtual_size && !fix_nuls) {
/* no need to kludge with CRs, we can use sendfile() */
i_stream_skip(input, virtual_skip);
ret = 0;
while (max_virtual_size > 0 &&
i_stream_read_data(input, &msg, &size, 0) > 0) {
- add_cr = FALSE;
+ add = '\0';
for (i = 0; i < size && max_virtual_size > 0; i++) {
max_virtual_size--;
if ((i == 0 && !cr_skipped) ||
(i > 0 && msg[i-1] != '\r')) {
/* missing CR */
- add_cr = TRUE;
+ add = '\r';
break;
}
+ } else if (msg[i] == '\0') {
+ add = 128;
+ break;
}
}
if (o_stream_send(output, msg, i) < 0)
return -1;
- if (add_cr) {
+ if (add != '\0') {
ret++;
- if (o_stream_send(output, "\r", 1) < 0)
+ if (o_stream_send(output, &add, 1) < 0)
return -1;
- cr_skipped = TRUE;
+ cr_skipped = add == '\r';
+ if (add == 128) i++;
} else {
cr_skipped = i > 0 && msg[i-1] == '\r';
}
known. Returns number of bytes sent, or -1 if error. */
off_t message_send(struct ostream *output, struct istream *input,
const struct message_size *msg_size,
- uoff_t virtual_skip, uoff_t max_virtual_size, int *last_cr);
+ uoff_t virtual_skip, uoff_t max_virtual_size, int *last_cr,
+ int fix_nuls);
/* Skip number of virtual bytes from putfer. msg_size is updated if it's not
NULL. If cr_skipped is TRUE and first character is \n, it's not treated as
#include "message-parser.h"
#include "message-size.h"
-void message_get_header_size(struct istream *input, struct message_size *hdr)
+void message_get_header_size(struct istream *input, struct message_size *hdr,
+ int *has_nuls)
{
const unsigned char *msg;
size_t i, size, startpos, missing_cr_count;
memset(hdr, 0, sizeof(struct message_size));
+ if (has_nuls != NULL)
+ *has_nuls = FALSE;
missing_cr_count = 0; startpos = 0;
while (i_stream_read_data(input, &msg, &size, startpos) > 0) {
for (i = startpos; i < size; i++) {
- if (msg[i] != '\n')
+ if (msg[i] != '\n') {
+ if (msg[i] == '\0' && has_nuls != NULL)
+ *has_nuls = TRUE;
continue;
+ }
hdr->lines++;
if (i == 0 || msg[i-1] != '\r') {
}
void message_get_body_size(struct istream *input, struct message_size *body,
- uoff_t max_virtual_size, int *last_cr)
+ uoff_t max_virtual_size, int *last_cr, int *has_nuls)
{
const unsigned char *msg;
size_t i, size, startpos, missing_cr_count;
int cr;
memset(body, 0, sizeof(struct message_size));
+ if (has_nuls != NULL)
+ *has_nuls = FALSE;
cr = 0;
missing_cr_count = 0; startpos = 0;
for (i = startpos; i < size && max_virtual_size > 0; i++) {
max_virtual_size--;
- if (msg[i] == '\n') {
+ if (msg[i] == '\0') {
+ if (has_nuls != NULL)
+ *has_nuls = TRUE;
+ } else if (msg[i] == '\n') {
if (i == 0 || msg[i-1] != '\r') {
/* missing CR */
missing_cr_count++;
/* Calculate size of message header. Leave the input point to first
character in body. */
-void message_get_header_size(struct istream *input, struct message_size *hdr);
+void message_get_header_size(struct istream *input, struct message_size *hdr,
+ int *has_nuls);
/* Calculate size of message body. Read only max_virtual_size virtual bytes,
if you want it unlimited, use (uoff_t)-1. If last_cr is not NULL, it's set
to 1 if last character is CR, 2 if it's virtual CR. */
void message_get_body_size(struct istream *input, struct message_size *body,
- uoff_t max_virtual_size, int *last_cr);
+ uoff_t max_virtual_size, int *last_cr,
+ int *has_nuls);
/* Sum contents of src into dest. */
void message_size_add(struct message_size *dest,
return NULL;
}
+ /* we know the NULs now, update them */
+ if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+ mail->mail.has_nuls = TRUE;
+ mail->mail.has_no_nuls = FALSE;
+ } else {
+ mail->mail.has_nuls = FALSE;
+ mail->mail.has_no_nuls = TRUE;
+ }
+
return part;
}
index_mail_init_parse_header(mail);
data->parts = message_parse(mail->pool, data->stream,
index_mail_parse_header, mail);
+
+ /* we know the NULs now, update them */
+ if ((data->parts->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
+ _mail->has_nuls = TRUE;
+ _mail->has_no_nuls = FALSE;
+ } else {
+ _mail->has_nuls = FALSE;
+ _mail->has_no_nuls = TRUE;
+ }
+
return data->parts;
}
return (uoff_t)-1;
if (hdr_phys_size == (uoff_t)-1) {
- message_get_header_size(data->stream, &data->hdr_size);
+ message_get_header_size(data->stream, &data->hdr_size, NULL);
hdr_size = data->hdr_size.virtual_size;
data->hdr_size_set = TRUE;
}
if (body_size == (uoff_t)-1) {
message_get_body_size(data->stream, &data->body_size,
- (uoff_t)-1, NULL);
+ (uoff_t)-1, NULL, NULL);
body_size = data->body_size.virtual_size;
data->body_size_set = TRUE;
}
if (hdr_size != NULL) {
if (!data->hdr_size_set) {
- message_get_header_size(data->stream, &data->hdr_size);
+ message_get_header_size(data->stream, &data->hdr_size,
+ NULL);
data->hdr_size_set = TRUE;
}
data->hdr_size.physical_size);
message_get_body_size(data->stream, &data->body_size,
- (uoff_t)-1, NULL);
+ (uoff_t)-1, NULL, NULL);
data->body_size_set = TRUE;
}
}
static struct mail index_mail = {
- 0, 0, 0,
+ 0, 0, 0, 0, 0,
get_flags,
get_parts,
memset(data, 0, sizeof(*data));
p_clear(mail->pool);
+ mail->mail.has_nuls =
+ (rec->index_flags & INDEX_MAIL_FLAG_HAS_NULS) != 0;
+ mail->mail.has_no_nuls =
+ (rec->index_flags & INDEX_MAIL_FLAG_HAS_NO_NULS) != 0;
+
data->rec = rec;
data->size = (uoff_t)-1;
data->received_date = data->sent_time = (time_t)-1;
unsigned int uid;
unsigned int seen_updated:1; /* if update_seen was TRUE */
+ unsigned int has_nuls:1; /* message data is known to contain NULs */
+ unsigned int has_no_nuls:1; /* -''- known to not contain NULs */
const struct mail_full_flags *(*get_flags)(struct mail *mail);
const struct message_part *(*get_parts)(struct mail *mail);