/*
Copyright (C) 1999-2004 IC & S dbmail@ic-s.nl
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later
version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* $Id: header.c 2188 2006-06-25 18:20:34Z aaron $
*
* Header.c implements functions to read an email header
* and parse out certain goodies, such as deliver-to
* fields and common fields for the fast header cache
* */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "dbmail.h"
#include "list.h"
#include "auth.h"
#include "mime.h"
#include "header.h"
#include "db.h"
#include "debug.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define HEADER_BLOCK_SIZE 1024
/* Must be at least 998 or 1000 by RFC's */
#define MAX_LINE_SIZE 1024
/**
* get the rfc size of the body of a message. This function assumes that
* the message has lines that end in '\n', not in '\r\n'
* \param[in] body message body
* \param body_size size of body
* \param[out] rfcsize rfc size of body
*/
static void get_rfc_size(const char *body_start, u64_t body_size,
u64_t *rfcsize);
/**
* chew the next line of input
* \param[in] message_content part of a message
* \param[out] line_size size of line (including '\r\n' or '\n')
* \param[out] line_rfcsize size of line (assuming '\r\n')
* \return
* - -1 on error
* - 0 if empty line (only \r\n or \n)
* - 1 if line found
*/
static int consume_header_line(const char *message_content,
size_t *line_size,
size_t *line_rfcsize);
int split_message(const char *whole_message,
u64_t whole_message_size,
char **header, u64_t *header_size,
u64_t *header_rfcsize,
const char **body, u64_t *body_size,
u64_t *body_rfcsize)
{
const char *end_of_header;
size_t line_size;
size_t line_rfcsize;
size_t body_begin;
u64_t tmp_header_size = 0;
u64_t tmp_header_rfcsize = 0;
int result;
end_of_header = whole_message;
/* split off the header */
while (1) {
result = consume_header_line(end_of_header,
&line_size, &line_rfcsize);
if (result < 0)
return -1;
tmp_header_size += line_size;
tmp_header_rfcsize += line_rfcsize;
end_of_header = &whole_message[tmp_header_size];
if (result == 0) {
break;
}
}
/* copy the header */
*header = dm_malloc((tmp_header_size + 1) * sizeof(char));
if (header == NULL) {
trace(TRACE_ERROR, "%s,%s: error allocating memory",
__FILE__, __func__);
return -1;
}
memset(*header, '\0', tmp_header_size + 1);
strncpy(*header, whole_message, tmp_header_size);
*header_size = tmp_header_size;
*header_rfcsize = tmp_header_rfcsize;
/* MTAs seem to add another newline after the empty header line.
* that empty line needs to be skipped. So, if the next character
*/
body_begin = tmp_header_size;
if (whole_message_size - tmp_header_size >= 2) {
if (whole_message[body_begin] == '\n')
body_begin += 1;
else if (whole_message[body_begin] == '\r' &&
whole_message[body_begin + 1] == '\n')
body_begin += 2;
}
*body = &whole_message[body_begin];
*body_size = whole_message_size - body_begin;
get_rfc_size(*body, *body_size, body_rfcsize);
return 1;
}
void get_rfc_size(const char *body_start, u64_t body_size,
u64_t *rfcsize)
{
const char *current_pos;
size_t remaining_len;
u64_t rfcadd = 0;
current_pos = body_start;
remaining_len = body_size;
trace(TRACE_DEBUG, "%s,%s: remaining_len = %zd",
__FILE__, __func__, remaining_len);
if (remaining_len == 0) {
*rfcsize = 0;
return;
}
/* count all the newlines */
while((current_pos = memchr(current_pos, '\n', remaining_len))) {
rfcadd++;
remaining_len = body_size - (current_pos - body_start) - 1;
if (remaining_len > 0)
current_pos++;
}
*rfcsize = body_size + rfcadd;
}
static int consume_header_line(const char *message_content,
size_t *line_size,
size_t *line_rfcsize)
{
size_t line_content_size;
size_t tmp_line_size = 0;
size_t tmp_line_rfcsize = 0;
if (strlen(message_content) == 0) {
tmp_line_size = 0;
tmp_line_rfcsize = 0;
} else {
line_content_size = strcspn(message_content, "\r\n");
if (message_content[line_content_size] == '\n') {
tmp_line_size = line_content_size + 1;
tmp_line_rfcsize = tmp_line_size + 1;
} else if (message_content[line_content_size] == '\r') {
if (message_content[line_content_size + 1] == '\n') {
/* This is the right behaviour */
tmp_line_size = line_content_size + 2;
tmp_line_rfcsize = tmp_line_size;
} else {
/* This is broken behaviour, but it's better
* than not handling it at all.
*/
tmp_line_size = line_content_size + 1;
tmp_line_rfcsize = tmp_line_size + 1;
}
}
}
*line_size = tmp_line_size;
*line_rfcsize = tmp_line_rfcsize;
if (tmp_line_rfcsize == 2 || tmp_line_rfcsize == 0) {
/* only '\r\n', which is an empty line */
trace(TRACE_DEBUG, "%s,%s: end of header found",
__FILE__, __func__);
return 0;
}
return 1;
}
syntax highlighted by Code2HTML, v. 0.9.1