Mercurial > dovecot > core-2.2
view src/lib-storage/index/index-fetch-section.c @ 227:2499dad2932b HEAD
removed extra whitespace
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 15 Sep 2002 10:00:39 +0300 |
parents | cf4d065f2f85 |
children | b79b6da2f84a |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "temp-string.h" #include "iobuffer.h" #include "rfc822-tokenize.h" #include "message-send.h" #include "index-storage.h" #include "index-fetch.h" #include <ctype.h> #include <unistd.h> ImapCacheField index_fetch_body_get_cache(const char *section) { if (*section >= '0' && *section <= '9') return IMAP_CACHE_MESSAGE_PART | IMAP_CACHE_MESSAGE_OPEN; if (*section == '\0' || strcasecmp(section, "TEXT") == 0) { /* no IMAP_CACHE_MESSAGE_BODY_SIZE, so that we don't uselessly check it when we want to read partial data */ return IMAP_CACHE_MESSAGE_OPEN; } if (strncasecmp(section, "HEADER", 6) == 0 || strcasecmp(section, "MIME") == 0) return IMAP_CACHE_MESSAGE_HDR_SIZE | IMAP_CACHE_MESSAGE_OPEN; /* error */ return 0; } /* fetch BODY[] or BODY[TEXT] */ static int fetch_body(MailIndexRecord *rec, MailFetchBodyData *sect, FetchContext *ctx, const char *prefix, int fetch_header) { MessageSize size; IOBuffer *inbuf; const char *str; if (!imap_msgcache_get_rfc822_partial(ctx->cache, sect->skip, sect->max_size, fetch_header, &size, &inbuf)) { i_error("Couldn't get BODY[] for UID %u (index %s)", rec->uid, ctx->index->filepath); return FALSE; } str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n", prefix, size.virtual_size); if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0) return FALSE; return message_send(ctx->outbuf, inbuf, &size, 0, sect->max_size); } static char *const *get_fields_array(const char *fields) { char **field_list, **field; while (*fields == ' ') fields++; if (*fields == '(') fields++; field_list = (char **) t_strsplit(fields, " )"); /* array ends at ")" element */ for (field = field_list; *field != NULL; field++) { if (strcasecmp(*field, ")") == 0) *field = NULL; } return field_list; } static int header_match(char *const *fields, const char *name, size_t size) { const char *field, *name_start, *name_end; i_assert(size > 0); name_start = name; name_end = name + size; for (; *fields != NULL; fields++) { field = *fields; if (*field == '\0') continue; for (name = name_start; name != name_end; name++) { /* field has been uppercased long time ago while parsing FETCH command */ if (i_toupper(*name) != *field) break; field++; if (*field == '\0') { if (name+1 == name_end) return TRUE; break; } } } return FALSE; } static int header_match_not(char *const *fields, const char *name, size_t size) { return !header_match(fields, name, size); } static int header_match_mime(char *const *fields __attr_unused__, const char *name, size_t size) { if (size > 8 && strncasecmp(name, "Content-", 8) == 0) return TRUE; if (size == 12 && strncasecmp(name, "Mime-Version", 13) == 0) return TRUE; return FALSE; } typedef struct { char *dest; char *const *fields; int (*match_func) (char *const *, const char *, size_t); } FetchHeaderFieldContext; static void fetch_header_field(MessagePart *part __attr_unused__, const char *name, size_t name_len, const char *value __attr_unused__, size_t value_len __attr_unused__, void *context) { FetchHeaderFieldContext *ctx = context; const char *field_start, *field_end, *cr, *p; size_t len; /* see if we want this field */ if (!ctx->match_func(ctx->fields, name, name_len)) return; /* add the field, inserting CRs when needed. FIXME: is this too kludgy? we assume name continues with ": value".. */ field_start = name; field_end = value + value_len; cr = NULL; for (p = field_start; p != field_end; p++) { if (*p == '\r') cr = p; else if (*p == '\n' && cr != p-1) { /* missing CR */ len = (size_t) (p-field_start); memcpy(ctx->dest, field_start, len); ctx->dest[len++] = '\r'; ctx->dest[len++] = '\n'; ctx->dest += len; field_start = p+1; } } if (field_start != field_end) { len = (size_t) (field_end-field_start); memcpy(ctx->dest, field_start, len); ctx->dest += len; } ctx->dest[0] = '\r'; ctx->dest[1] = '\n'; ctx->dest += 2; } /* Store headers into dest, returns number of bytes written. */ static size_t fetch_header_fields(IOBuffer *inbuf, char *dest, char *const *fields, int (*match_func) (char *const *, const char *, size_t)) { FetchHeaderFieldContext ctx; ctx.dest = dest; ctx.fields = fields; ctx.match_func = match_func; message_parse_header(NULL, inbuf, NULL, fetch_header_field, &ctx); return (size_t) (ctx.dest - dest); } /* fetch wanted headers from given data */ static int fetch_header_from(IOBuffer *inbuf, MessageSize *size, const char *section, MailFetchBodyData *sect, FetchContext *ctx, const char *prefix) { const char *str; char *dest; size_t len; int failed; /* HEADER, MIME, HEADER.FIELDS (list), HEADER.FIELDS.NOT (list) */ if (strcasecmp(section, "HEADER") == 0) { /* all headers */ str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n", prefix, size->virtual_size); if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0) return FALSE; return message_send(ctx->outbuf, inbuf, size, sect->skip, sect->max_size); } /* partial headers - copy the wanted fields into temporary memory. Insert missing CRs on the way. FIXME: not a good idea with huge headers. */ if (size->virtual_size > SSIZE_T_MAX) { i_error("Message header too large"); return FALSE; } t_push(); dest = t_malloc((size_t)size->virtual_size); if (strncasecmp(section, "HEADER.FIELDS ", 14) == 0) { len = fetch_header_fields(inbuf, dest, get_fields_array(section + 14), header_match); } else if (strncasecmp(section, "HEADER.FIELDS.NOT ", 18) == 0) { len = fetch_header_fields(inbuf, dest, get_fields_array(section + 18), header_match_not); } else if (strcasecmp(section, "MIME") == 0) { /* Mime-Version + Content-* fields */ len = fetch_header_fields(inbuf, dest, NULL, header_match_mime); } else { /* error */ len = 0; } i_assert(len <= size->virtual_size); if (len <= sect->skip) len = 0; else { dest += sect->skip; len -= sect->skip; if (len > sect->max_size) len = sect->max_size; } str = t_strdup_printf("%s {%"PRIuSIZE_T"}\r\n", prefix, len); failed = io_buffer_send(ctx->outbuf, str, strlen(str)) < 0 || io_buffer_send(ctx->outbuf, dest, len); t_pop(); return !failed; } /* fetch BODY[HEADER...] */ static int fetch_header(MailFetchBodyData *sect, FetchContext *ctx, const char *prefix) { MessageSize hdr_size; IOBuffer *inbuf; if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, &hdr_size, NULL)) return FALSE; return fetch_header_from(inbuf, &hdr_size, sect->section, sect, ctx, prefix); } /* Find MessagePart for section (eg. 1.3.4) */ static MessagePart *part_find(MailFetchBodyData *sect, FetchContext *ctx, const char **section) { MessagePart *part; const char *path; unsigned int num; part = imap_msgcache_get_parts(ctx->cache); path = sect->section; while (*path >= '0' && *path <= '9' && part != NULL) { /* get part number */ num = 0; while (*path != '\0' && *path != '.') { if (*path < '0' || *path > '9') return NULL; num = num*10 + (*path - '0'); path++; } if (*path == '.') path++; if (part->flags & MESSAGE_PART_FLAG_MULTIPART) { /* find the part */ part = part->children; for (; num > 1 && part != NULL; num--) part = part->next; } else { /* only 1 allowed with non-multipart messages */ if (num != 1) return NULL; } } *section = path; return part; } /* fetch BODY[1.2] or BODY[1.2.TEXT] */ static int fetch_part_body(MessagePart *part, MailFetchBodyData *sect, FetchContext *ctx, const char *prefix) { IOBuffer *inbuf; const char *str; uoff_t skip_pos; if (!imap_msgcache_get_data(ctx->cache, &inbuf)) return FALSE; /* jump to beginning of wanted data */ skip_pos = part->physical_pos + part->header_size.physical_size; io_buffer_skip(inbuf, skip_pos); str = t_strdup_printf("%s {%"PRIuUOFF_T"}\r\n", prefix, part->body_size.virtual_size); if (io_buffer_send(ctx->outbuf, str, strlen(str)) < 0) return FALSE; /* FIXME: potential performance problem with big messages: FETCH BODY[1]<100000..1024>, hopefully no clients do this */ return message_send(ctx->outbuf, inbuf, &part->body_size, sect->skip, sect->max_size); } /* fetch BODY[1.2.MIME|HEADER...] */ static int fetch_part_header(MessagePart *part, const char *section, MailFetchBodyData *sect, FetchContext *ctx, const char *prefix) { IOBuffer *inbuf; if (!imap_msgcache_get_data(ctx->cache, &inbuf)) return FALSE; io_buffer_skip(inbuf, part->physical_pos); return fetch_header_from(inbuf, &part->header_size, section, sect, ctx, prefix); } static int fetch_part(MailFetchBodyData *sect, FetchContext *ctx, const char *prefix) { MessagePart *part; const char *section; part = part_find(sect, ctx, §ion); if (part == NULL) return FALSE; if (*section == '\0' || strcasecmp(section, "TEXT") == 0) return fetch_part_body(part, sect, ctx, prefix); if (strncasecmp(section, "HEADER", 6) == 0) return fetch_part_header(part, section, sect, ctx, prefix); if (strcasecmp(section, "MIME") == 0) return fetch_part_header(part, section, sect, ctx, prefix); return FALSE; } int index_fetch_body_section(MailIndexRecord *rec, unsigned int seq __attr_unused__, MailFetchBodyData *sect, FetchContext *ctx) { const char *prefix; prefix = !sect->skip_set ? t_strdup_printf(" BODY[%s]", sect->section) : t_strdup_printf(" BODY[%s]<%"PRIuUOFF_T">", sect->section, sect->skip); if (ctx->first) { prefix++; ctx->first = FALSE; } if (*sect->section == '\0') return fetch_body(rec, sect, ctx, prefix, TRUE); if (strcasecmp(sect->section, "TEXT") == 0) return fetch_body(rec, sect, ctx, prefix, FALSE); if (strncasecmp(sect->section, "HEADER", 6) == 0) return fetch_header(sect, ctx, prefix); if (*sect->section >= '0' && *sect->section <= '9') return fetch_part(sect, ctx, prefix); /* FIXME: point the error to user */ return FALSE; }