int cmd_login(struct imap_client *imap_client, const struct imap_arg *args) { struct client *client = &imap_client->common; const char *user, *pass; string_t *plain_login, *base64; /* two arguments: username and password */ if (!imap_arg_get_astring(&args[0], &user) || !imap_arg_get_astring(&args[1], &pass) || !IMAP_ARG_IS_EOL(&args[2])) return -1; if (!client_check_plaintext_auth(client, TRUE)) return 1; /* authorization ID \0 authentication ID \0 pass */ plain_login = buffer_create_dynamic(pool_datastack_create(), 64); buffer_append_c(plain_login, '\0'); buffer_append(plain_login, user, strlen(user)); buffer_append_c(plain_login, '\0'); buffer_append(plain_login, pass, strlen(pass)); base64 = buffer_create_dynamic(pool_datastack_create(), MAX_BASE64_ENCODED_SIZE(plain_login->used)); base64_encode(plain_login->data, plain_login->used, base64); return imap_client_auth_begin(imap_client, "PLAIN", str_c(base64)); }
/* * Convert string to big-endian ucs2. */ void *ucs2be_str(pool_t pool, const char *str, size_t *size) { buffer_t *buf = buffer_create_dynamic(pool, 32); while (*str != '\0') { buffer_append_c(buf, '\0'); buffer_append_c(buf, *str++); } *size = buf->used; return buffer_free_without_data(&buf); }
void checkpassword_child_output(struct chkpw_auth_request *request) { /* Send: username \0 password \0 timestamp \0. Must be 512 bytes or less. The "timestamp" parameter is actually useful only for APOP authentication. We don't support it, so keep it empty */ struct auth_request *auth_request = request->request; buffer_t *buf; const unsigned char *data; size_t size; ssize_t ret; buf = buffer_create_dynamic(pool_datastack_create(), 512+1); buffer_append(buf, auth_request->user, strlen(auth_request->user)+1); if (request->password != NULL) buffer_append(buf, request->password, strlen(request->password)+1); else buffer_append_c(buf, '\0'); buffer_append_c(buf, '\0'); data = buffer_get_data(buf, &size); if (size > 512) { auth_request_log_error(request->request, "checkpassword", "output larger than 512 bytes: %"PRIuSIZE_T, size); request->finish_callback(request, request->internal_failure_code); return; } ret = write(request->fd_out, data + request->write_pos, size - request->write_pos); if (ret <= 0) { if (ret < 0) { auth_request_log_error(request->request, "checkpassword", "write() failed: %m"); } request->finish_callback(request, request->internal_failure_code); return; } request->write_pos += ret; if (request->write_pos < size) return; io_remove(&request->io_out); if (close(request->fd_out) < 0) i_error("checkpassword: close() failed: %m"); request->fd_out = -1; }
static unsigned char * t_unicode_str(const char *src, bool ucase, size_t *size) { buffer_t *wstr; wstr = buffer_create_dynamic(unsafe_data_stack_pool, 32); for ( ; *src; src++) { buffer_append_c(wstr, ucase ? i_toupper(*src) : *src); buffer_append_c(wstr, '\0'); } *size = buffer_get_used_size(wstr); return buffer_free_without_data(&wstr); }
static void test_foo(void) { buffer_t *buf = buffer_create_dynamic(default_pool, 100); for (int i = 1; i <= 24; i++) { buffer_set_used_size(buf, 0); buffer_append_c(buf, 0xff); buffer_append_c(buf, 0xff); buffer_append_c(buf, 0xff); buffer_truncate_rshift_bits(buf, i); printf("%2d bits: %24s %s\n", i, binary_to_hex(buf->data, buf->used), binary_to_10(buf->data, buf->used)); } }
static void put_uint32(buffer_t *output, uint32_t num) { buffer_append_c(output, num & 0xff); buffer_append_c(output, (num >> 8) & 0xff); buffer_append_c(output, (num >> 16) & 0xff); buffer_append_c(output, (num >> 24) & 0xff); }
const char *client_get_session_id(struct client *client) { buffer_t *buf, *base64_buf; struct timeval tv; uint64_t timestamp; unsigned int i; if (client->session_id != NULL) return client->session_id; buf = buffer_create_dynamic(pool_datastack_create(), 24); base64_buf = buffer_create_dynamic(pool_datastack_create(), 24*2); if (gettimeofday(&tv, NULL) < 0) i_fatal("gettimeofday(): %m"); timestamp = tv.tv_usec + (long long)tv.tv_sec * 1000ULL*1000ULL; /* add lowest 48 bits of the timestamp. this gives us a bit less than 9 years until it wraps */ for (i = 0; i < 48; i += 8) buffer_append_c(buf, (timestamp >> i) & 0xff); buffer_append_c(buf, client->remote_port & 0xff); buffer_append_c(buf, (client->remote_port >> 16) & 0xff); #ifdef HAVE_IPV6 if (IPADDR_IS_V6(&client->ip)) buffer_append(buf, &client->ip.u.ip6, sizeof(client->ip.u.ip6)); else #endif buffer_append(buf, &client->ip.u.ip4, sizeof(client->ip.u.ip4)); base64_encode(buf->data, buf->used, base64_buf); client->session_id = p_strdup(client->pool, str_c(base64_buf)); return client->session_id; }
static bool remove_subj_fwd_hdr(buffer_t *buf, size_t *start_pos, bool *is_reply_or_forward_r) { const char *data; size_t size; /* subj-fwd = subj-fwd-hdr subject subj-fwd-trl subj-fwd-hdr = "[fwd:" subj-fwd-trl = "]" */ data = buffer_get_data(buf, &size); if (strncmp(data + *start_pos, "[FWD:", 5) != 0) return FALSE; if (data[size-2] != ']') return FALSE; *is_reply_or_forward_r = TRUE; buffer_set_used_size(buf, size-2); buffer_append_c(buf, '\0'); *start_pos += 5; return TRUE; }
int hex_to_binary(const char *data, buffer_t *dest) { int value; while (*data != '\0') { if (*data >= '0' && *data <= '9') value = (*data - '0') << 4; else if (*data >= 'a' && *data <= 'f') value = (*data - 'a' + 10) << 4; else if (*data >= 'A' && *data <= 'F') value = (*data - 'A' + 10) << 4; else return -1; data++; if (*data >= '0' && *data <= '9') value |= *data - '0'; else if (*data >= 'a' && *data <= 'f') value |= *data - 'a' + 10; else if (*data >= 'A' && *data <= 'F') value |= *data - 'A' + 10; else return -1; buffer_append_c(dest, value); data++; } return 0; }
static void remove_subj_trailers(buffer_t *buf, size_t start_pos, bool *is_reply_or_forward_r) { const char *data; size_t orig_size, size; /* subj-trailer = "(fwd)" / WSP */ data = buffer_get_data(buf, &orig_size); if (orig_size < 1) /* size includes trailing \0 */ return; for (size = orig_size-1; size > start_pos; ) { if (data[size-1] == ' ') size--; else if (size >= 5 && memcmp(data + size - 5, "(FWD)", 5) == 0) { *is_reply_or_forward_r = TRUE; size -= 5; } else { break; } } if (size != orig_size-1) { buffer_set_used_size(buf, size); buffer_append_c(buf, '\0'); } }
static bool message_decode_header(struct message_decoder_context *ctx, struct message_header_line *hdr, struct message_block *output) { size_t value_len; if (hdr->continues) { hdr->use_full_value = TRUE; return FALSE; } T_BEGIN { if (hdr->name_len == 12 && strcasecmp(hdr->name, "Content-Type") == 0) parse_content_type(ctx, hdr); if (hdr->name_len == 25 && strcasecmp(hdr->name, "Content-Transfer-Encoding") == 0) ctx->message_cte = message_decoder_parse_cte(hdr); } T_END; buffer_set_used_size(ctx->buf, 0); message_header_decode_utf8(hdr->full_value, hdr->full_value_len, ctx->buf, ctx->normalizer); value_len = ctx->buf->used; if (ctx->normalizer != NULL) { (void)ctx->normalizer(hdr->name, hdr->name_len, ctx->buf); buffer_append_c(ctx->buf, '\0'); } else { if (!uni_utf8_get_valid_data((const unsigned char *)hdr->name, hdr->name_len, ctx->buf)) buffer_append_c(ctx->buf, '\0'); } ctx->hdr = *hdr; ctx->hdr.full_value = ctx->buf->data; ctx->hdr.full_value_len = value_len; ctx->hdr.value_len = 0; if (ctx->buf->used != value_len) { ctx->hdr.name = CONST_PTR_OFFSET(ctx->buf->data, ctx->hdr.full_value_len); ctx->hdr.name_len = ctx->buf->used - 1 - value_len; } output->hdr = &ctx->hdr; return TRUE; }
void askpass(const char *prompt, char *buf, size_t buf_size) { buffer_t str; buffer_create_from_data(&str, buf, buf_size); askpass_str(prompt, &str); buffer_append_c(&str, '\0'); }
static string_t *get_digest_challenge(struct digest_auth_request *request) { const struct auth_settings *set = request->auth_request.set; buffer_t buf; string_t *str; const char *const *tmp; unsigned char nonce[16]; unsigned char nonce_base64[MAX_BASE64_ENCODED_SIZE(sizeof(nonce))+1]; int i; bool first_qop; /* realm="hostname" (multiple allowed) nonce="randomized data, at least 64bit" qop="auth,auth-int,auth-conf" maxbuf=number (with auth-int, auth-conf, defaults to 64k) charset="utf-8" (iso-8859-1 if it doesn't exist) algorithm="md5-sess" cipher="3des,des,rc4-40,rc4,rc4-56" (with auth-conf) */ /* get 128bit of random data as nonce */ random_fill(nonce, sizeof(nonce)); buffer_create_from_data(&buf, nonce_base64, sizeof(nonce_base64)); base64_encode(nonce, sizeof(nonce), &buf); buffer_append_c(&buf, '\0'); request->nonce = p_strdup(request->pool, buf.data); str = t_str_new(256); if (*set->realms_arr == NULL) { /* If no realms are given, at least Cyrus SASL client defaults to destination host name */ str_append(str, "realm=\"\","); } else { for (tmp = set->realms_arr; *tmp != NULL; tmp++) str_printfa(str, "realm=\"%s\",", *tmp); } str_printfa(str, "nonce=\"%s\",", request->nonce); str_append(str, "qop=\""); first_qop = TRUE; for (i = 0; i < QOP_COUNT; i++) { if (request->qop & (1 << i)) { if (first_qop) first_qop = FALSE; else str_append_c(str, ','); str_append(str, qop_names[i]); } } str_append(str, "\","); str_append(str, "charset=\"utf-8\"," "algorithm=\"md5-sess\""); return str; }
const char *imap_get_base_subject_cased(pool_t pool, const char *subject, bool *is_reply_or_forward_r) { buffer_t *buf; size_t start_pos, subject_len; bool found; if (is_reply_or_forward_r != NULL) *is_reply_or_forward_r = FALSE; subject_len = strlen(subject); buf = buffer_create_dynamic(pool, subject_len); /* (1) Convert any RFC 2047 encoded-words in the subject to UTF-8. Convert all tabs and continuations to space. Convert all multiple spaces to a single space. */ message_header_decode_utf8((const unsigned char *)subject, subject_len, buf, TRUE); buffer_append_c(buf, '\0'); pack_whitespace(buf); start_pos = 0; do { /* (2) Remove all trailing text of the subject that matches the subj-trailer ABNF, repeat until no more matches are possible. */ remove_subj_trailers(buf, start_pos, is_reply_or_forward_r); do { /* (3) Remove all prefix text of the subject that matches the subj-leader ABNF. */ found = remove_subj_leader(buf, &start_pos, is_reply_or_forward_r); /* (4) If there is prefix text of the subject that matches the subj-blob ABNF, and removing that prefix leaves a non-empty subj-base, then remove the prefix text. */ found = remove_blob_when_nonempty(buf, &start_pos) || found; /* (5) Repeat (3) and (4) until no matches remain. */ } while (found); /* (6) If the resulting text begins with the subj-fwd-hdr ABNF and ends with the subj-fwd-trl ABNF, remove the subj-fwd-hdr and subj-fwd-trl and repeat from step (2). */ } while (remove_subj_fwd_hdr(buf, &start_pos, is_reply_or_forward_r)); /* (7) The resulting text is the "base subject" used in the SORT. */ return (const char *)buf->data + start_pos; }
static void askpass_str(const char *prompt, buffer_t *pass) { struct termios old_tio, tio; bool tty, restore_tio = FALSE; char ch; int fd; tty = isatty(STDIN_FILENO); if (tty) { fputs(prompt, stderr); fflush(stderr); fd = open("/dev/tty", O_RDONLY); if (fd < 0) i_fatal("open(/dev/tty) failed: %m"); /* turn off echo */ if (tcgetattr(fd, &old_tio) == 0) { restore_tio = TRUE; tio = old_tio; tio.c_lflag &= ~(ECHO | ECHONL); (void)tcsetattr(fd, TCSAFLUSH, &tio); } } else { /* read it from stdin without showing a prompt */ fd = STDIN_FILENO; } /* read the password */ while (read(fd, &ch, 1) > 0) { if (ch == '\n' || ch == '\r') break; buffer_append_c(pass, ch); } if (tty) { if (restore_tio) (void)tcsetattr(fd, TCSAFLUSH, &old_tio); fputs("\n", stderr); fflush(stderr); i_close_fd(&fd); } }
int quoted_printable_q_decode(const unsigned char *src, size_t src_size, buffer_t *dest) { char hexbuf[3]; size_t src_pos, next; bool errors = FALSE; hexbuf[2] = '\0'; next = 0; for (src_pos = 0; src_pos < src_size; src_pos++) { if (src[src_pos] != '_' && src[src_pos] != '=') continue; buffer_append(dest, src + next, src_pos - next); next = src_pos; if (src[src_pos] == '_') { buffer_append_c(dest, ' '); next++; continue; } if (src_pos+2 >= src_size) break; /* =<hex> */ hexbuf[0] = src[src_pos+1]; hexbuf[1] = src[src_pos+2]; if (hex_to_binary(hexbuf, dest) == 0) { src_pos += 2; next = src_pos+1; } else { /* non-hex data, show as-is */ errors = TRUE; next = src_pos; } } buffer_append(dest, src + next, src_size - next); return errors ? -1 : 0; }
static bool remove_subj_fwd_hdr(buffer_t *buf, size_t *start_pos, bool *is_reply_or_forward_r) { const char *data = buf->data; size_t size = buf->used; /* subj-fwd = subj-fwd-hdr subject subj-fwd-trl subj-fwd-hdr = "[fwd:" subj-fwd-trl = "]" */ if (!str_begins(data + *start_pos, "[FWD:")) return FALSE; if (data[size-2] != ']') return FALSE; *is_reply_or_forward_r = TRUE; buffer_set_used_size(buf, size-2); buffer_append_c(buf, '\0'); *start_pos += 5; return TRUE; }
void str_append_c(string_t *str, unsigned char chr) { buffer_append_c(str, chr); }
int message_parse_header_next(struct message_header_parser_ctx *ctx, struct message_header_line **hdr_r) { struct message_header_line *line = &ctx->line; const unsigned char *msg; size_t i, size, startpos, colon_pos, parse_size; int ret; bool continued, continues, last_no_newline, last_crlf; bool no_newline, crlf_newline; *hdr_r = NULL; if (line->eoh) return -1; if (ctx->skip > 0) { i_stream_skip(ctx->input, ctx->skip); ctx->skip = 0; } if (line->continues) colon_pos = 0; else { /* new header line */ line->name_offset = ctx->input->v_offset; colon_pos = UINT_MAX; buffer_set_used_size(ctx->value_buf, 0); } no_newline = FALSE; crlf_newline = FALSE; continued = line->continues; continues = FALSE; for (startpos = 0;;) { ret = i_stream_read_data(ctx->input, &msg, &size, startpos+1); if (ret >= 0) { /* we want to know one byte in advance to find out if it's multiline header */ parse_size = size == 0 ? 0 : size-1; } else { parse_size = size; } if (ret <= 0 && startpos == parse_size) { if (ret == -1) { if (startpos > 0) { /* header ended unexpectedly. */ no_newline = TRUE; ctx->skip = startpos; break; } /* error / EOF with no bytes */ return -1; } if (size > 0 && !ctx->skip_line && !continued && (msg[0] == '\n' || (msg[0] == '\r' && size > 1 && msg[1] == '\n'))) { /* end of headers - this mostly happens just with mbox where headers are read separately from body */ size = 0; if (ctx->hdr_size != NULL) ctx->hdr_size->lines++; if (msg[0] == '\r') { ctx->skip = 2; crlf_newline = TRUE; } else { ctx->skip = 1; if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; } break; } if (ret == 0 && !ctx->input->eof) { /* stream is nonblocking - need more data */ return 0; } i_assert(size > 0); /* a) line is larger than input buffer b) header ended unexpectedly */ if (ret == -2) { /* go back to last LWSP if found. */ size_t min_pos = !continued ? colon_pos : 0; for (i = size-1; i > min_pos; i--) { if (IS_LWSP(msg[i])) { size = i; break; } } if (i == min_pos && (msg[size-1] == '\r' || msg[size-1] == '\n')) { /* we may or may not have a full header, but we don't know until we get the next character. leave out the linefeed and finish the header on the next run. */ size--; if (size > 0 && msg[size-1] == '\r') size--; } /* the buffer really has to be more than 2 to avoid CRLF looping forever */ i_assert(size > 0); continues = TRUE; } no_newline = TRUE; ctx->skip = size; break; } /* find ':' */ if (colon_pos == UINT_MAX) { for (i = startpos; i < parse_size; i++) { if (msg[i] > ':') continue; if (msg[i] == ':' && !ctx->skip_line) { colon_pos = i; line->full_value_offset = ctx->input->v_offset + i + 1; break; } if (msg[i] == '\n') { /* end of headers, or error */ break; } if (msg[i] == '\0') ctx->has_nuls = TRUE; } } else { i = startpos; } /* find '\n' */ for (; i < parse_size; i++) { if (msg[i] <= '\n') { if (msg[i] == '\n') break; if (msg[i] == '\0') ctx->has_nuls = TRUE; } } if (i < parse_size && i+1 == size && ret == -2) { /* we don't know if the line continues. */ i++; } else if (i < parse_size) { /* got a line */ if (ctx->skip_line) { /* skipping a line with a huge header name */ if (ctx->hdr_size != NULL) { ctx->hdr_size->lines++; ctx->hdr_size->physical_size += i + 1; ctx->hdr_size->virtual_size += i + 1; } if (i == 0 || msg[i-1] != '\r') { /* missing CR */ if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; } i_stream_skip(ctx->input, i + 1); startpos = 0; ctx->skip_line = FALSE; continue; } continues = i+1 < size && IS_LWSP(msg[i+1]); if (ctx->hdr_size != NULL) ctx->hdr_size->lines++; if (i == 0 || msg[i-1] != '\r') { /* missing CR */ if (ctx->hdr_size != NULL) ctx->hdr_size->virtual_size++; size = i; } else { size = i-1; crlf_newline = TRUE; } ctx->skip = i+1; break; } startpos = i; } last_crlf = line->crlf_newline && (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_DROP_CR) == 0; last_no_newline = line->no_newline || (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) != 0; line->continues = continues; line->continued = continued; line->crlf_newline = crlf_newline; line->no_newline = no_newline; if (size == 0 && !continued) { /* end of headers */ line->eoh = TRUE; line->name_len = line->value_len = line->full_value_len = 0; line->name = ""; line->value = line->full_value = NULL; line->middle = NULL; line->middle_len = 0; line->full_value_offset = line->name_offset; line->continues = FALSE; } else if (line->continued) { line->value = msg; line->value_len = size; } else if (colon_pos == UINT_MAX) { /* missing ':', assume the whole line is name */ line->value = NULL; line->value_len = 0; str_truncate(ctx->name, 0); buffer_append(ctx->name, msg, size); line->name = str_c(ctx->name); line->name_len = str_len(ctx->name); line->middle = NULL; line->middle_len = 0; } else { size_t pos; line->value = msg + colon_pos+1; line->value_len = size - colon_pos - 1; if (ctx->flags & MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP) { /* get value. skip all LWSP after ':'. Note that RFC2822 doesn't say we should, but history behind it.. Exception to this is if the value consists only of LWSP, then skip only the one LWSP after ':'. */ for (pos = 0; pos < line->value_len; pos++) { if (!IS_LWSP(line->value[pos])) break; } if (pos == line->value_len) { /* everything was LWSP */ if (line->value_len > 0 && IS_LWSP(line->value[0])) pos = 1; } } else { pos = line->value_len > 0 && IS_LWSP(line->value[0]) ? 1 : 0; } line->value += pos; line->value_len -= pos; line->full_value_offset += pos; /* get name, skip LWSP before ':' */ while (colon_pos > 0 && IS_LWSP(msg[colon_pos-1])) colon_pos--; str_truncate(ctx->name, 0); /* use buffer_append() so the name won't be truncated if there are NULs. */ buffer_append(ctx->name, msg, colon_pos); str_append_c(ctx->name, '\0'); /* keep middle stored also in ctx->name so it's available with use_full_value */ line->middle = msg + colon_pos; line->middle_len = (size_t)(line->value - line->middle); str_append_n(ctx->name, line->middle, line->middle_len); line->name = str_c(ctx->name); line->name_len = colon_pos; line->middle = str_data(ctx->name) + line->name_len + 1; } if (!line->continued) { /* first header line. make a copy of the line since we can't really trust input stream not to lose it. */ buffer_append(ctx->value_buf, line->value, line->value_len); line->value = line->full_value = ctx->value_buf->data; line->full_value_len = line->value_len; } else if (line->use_full_value) { /* continue saving the full value. */ if (last_no_newline) { /* line is longer than fit into our buffer, so we were forced to break it into multiple message_header_lines */ } else { if (last_crlf) buffer_append_c(ctx->value_buf, '\r'); buffer_append_c(ctx->value_buf, '\n'); } if ((ctx->flags & MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE) && line->value_len > 0 && line->value[0] != ' ' && IS_LWSP(line->value[0])) { buffer_append_c(ctx->value_buf, ' '); buffer_append(ctx->value_buf, line->value + 1, line->value_len - 1); } else { buffer_append(ctx->value_buf, line->value, line->value_len); } line->full_value = buffer_get_data(ctx->value_buf, &line->full_value_len); } else { /* we didn't want full_value, and this is a continued line. */ line->full_value = NULL; line->full_value_len = 0; } /* always reset it */ line->use_full_value = FALSE; if (ctx->hdr_size != NULL) { ctx->hdr_size->physical_size += ctx->skip; ctx->hdr_size->virtual_size += ctx->skip; } *hdr_r = line; return 1; }
static int http_header_parse(struct http_header_parser *parser) { int ret; /* RFC 7230, Section 3.2: Header Fields 'header' = *( header-field CRLF ) CRLF ; Actually part of HTTP-message syntax header-field = field-name ":" OWS field-value OWS field-name = token field-value = *( field-content / obs-fold ) field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] field-vchar = VCHAR / obs-text obs-fold = CRLF 1*( SP / HTAB ) ; obsolete line folding ; see Section 3.2.4 */ for (;;) { switch (parser->state) { case HTTP_HEADER_PARSE_STATE_INIT: buffer_set_used_size(parser->value_buf, 0); str_truncate(parser->name, 0); if (*parser->cur == '\r') { /* last CRLF */ parser->cur++; parser->state = HTTP_HEADER_PARSE_STATE_EOH; if (parser->cur == parser->end) return 0; break; } else if (*parser->cur == '\n') { /* last LF */ parser->state = HTTP_HEADER_PARSE_STATE_EOH; break; } /* next line */ parser->state = HTTP_HEADER_PARSE_STATE_NAME; /* fall through */ case HTTP_HEADER_PARSE_STATE_NAME: if ((ret=http_header_parse_name(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_COLON; /* fall through */ case HTTP_HEADER_PARSE_STATE_COLON: if (*parser->cur != ':') { parser->error = t_strdup_printf ("Expected ':' after header field name '%s', but found %s", str_sanitize(str_c(parser->name),64), _chr_sanitize(*parser->cur)); return -1; } parser->cur++; if (str_len(parser->name) == 0) { parser->error = "Empty header field name"; return -1; } if (++parser->field_count > parser->limits.max_fields) { parser->error = "Excessive number of header fields"; return -1; } parser->state = HTTP_HEADER_PARSE_STATE_OWS; /* fall through */ case HTTP_HEADER_PARSE_STATE_OWS: if ((ret=http_header_parse_ows(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_CONTENT; /* fall through */ case HTTP_HEADER_PARSE_STATE_CONTENT: if ((ret=http_header_parse_content(parser)) <= 0) return ret; parser->state = HTTP_HEADER_PARSE_STATE_CR; /* fall through */ case HTTP_HEADER_PARSE_STATE_CR: if (*parser->cur == '\r') { parser->cur++; } else if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Invalid character %s in content of header field '%s'", _chr_sanitize(*parser->cur), str_sanitize(str_c(parser->name),64)); return -1; } parser->state = HTTP_HEADER_PARSE_STATE_LF; if (parser->cur == parser->end) return 0; /* fall through */ case HTTP_HEADER_PARSE_STATE_LF: if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Expected LF after CR at end of header field '%s', but found %s", str_sanitize(str_c(parser->name),64), _chr_sanitize(*parser->cur)); return -1; } parser->cur++; parser->state = HTTP_HEADER_PARSE_STATE_NEW_LINE; if (parser->cur == parser->end) return 0; /* fall through */ case HTTP_HEADER_PARSE_STATE_NEW_LINE: if (*parser->cur == ' ' || *parser->cur == '\t') { /* obs-fold */ buffer_append_c(parser->value_buf, ' '); parser->state = HTTP_HEADER_PARSE_STATE_OWS; break; } /* next header line */ parser->state = HTTP_HEADER_PARSE_STATE_INIT; return 1; case HTTP_HEADER_PARSE_STATE_EOH: if (*parser->cur != '\n') { parser->error = t_strdup_printf ("Encountered stray CR at beginning of header line, followed by %s", _chr_sanitize(*parser->cur)); return -1; } /* header fully parsed */ parser->cur++; return 1; default: i_unreached(); } } i_unreached(); return -1; }
static int quoted_printable_decode_full(const unsigned char *src, size_t src_size, size_t *src_pos_r, buffer_t *dest, bool eof) { char hexbuf[3]; size_t src_pos, pos, next; bool errors = FALSE; int ret; hexbuf[2] = '\0'; next = 0; for (src_pos = 0; src_pos < src_size; src_pos++) { if (src[src_pos] != '=' && src[src_pos] != '\n') continue; if (src[src_pos] == '\n') { /* drop trailing whitespace */ pos = src_pos; if (pos > 0 && src[pos-1] == '\r') pos--; while (pos > 0 && QP_IS_TRAILING_SPACE(src[pos-1])) pos--; buffer_append(dest, src + next, pos - next); next = src_pos+1; buffer_append_c(dest, '\r'); buffer_append_c(dest, '\n'); continue; } /* '=' */ buffer_append(dest, src + next, src_pos - next); next = src_pos; if ((ret = qp_is_end_of_line(src, &src_pos, src_size)) > 0) { /* =[whitespace][\r]\n */ next = src_pos+1; continue; } if (ret < 0) { /* '=' was followed only by whitespace */ break; } if (src_pos+2 >= src_size) { /* '=' was followed by non-whitespace */ if (eof) errors = TRUE; break; } /* =<hex> */ hexbuf[0] = src[src_pos+1]; hexbuf[1] = src[src_pos+2]; if (hex_to_binary(hexbuf, dest) == 0) { src_pos += 2; next = src_pos + 1; } else { /* non-hex data, show as-is */ errors = TRUE; next = src_pos; } } if (src_pos == src_size) { /* add everything but trailing spaces */ if (src_pos > 0 && src[src_pos-1] == '\r') src_pos--; while (src_pos > 0 && QP_IS_TRAILING_SPACE(src[src_pos-1])) src_pos--; buffer_append(dest, src + next, src_pos - next); next = src_pos; } *src_pos_r = next; return errors ? -1 : 0; }