Ejemplo n.º 1
0
Archivo: html.c Proyecto: aabc/blists
static void buffer_append_filename(struct buffer *dst, const char *fn, int text)
{
	if (!fn || !*fn)
		fn = "attachment";

	char prev = 0;
	const char *p;
	for (p = fn; *p && p - fn < MAX_FILENAME_LENGTH; p++) {
		if ((*p >= 'a' && *p <= 'z') ||
		    (*p >= 'A' && *p <= 'Z') ||
		    (*p >= '0' && *p <= '9'))
			buffer_appendc(dst, prev = *p);
		else if (prev != '_')
			buffer_appendc(dst, prev = '_');
	}
	buffer_appends(dst, text ? ".txt" : ".bin");
}
Ejemplo n.º 2
0
void strstack_push( strstack_t* stack, const char* data, size_t len ) {
  size_t offset = stack->buf.length;

  /* buffer_append() may realloc; set stack->top after that. */
  if( len )
    buffer_append( &(stack->buf), data, len );
  buffer_appendc( &(stack->buf), '\0' );
  stack->top = &(stack->buf.data[offset]);

  assert_inside_buffer( stack->buf, stack->top );
}
Ejemplo n.º 3
0
Archivo: html.c Proyecto: aabc/blists
int html_attachment(const char *list, unsigned int y, unsigned int m, unsigned int d, unsigned int n, unsigned int a)
{
	unsigned int aday;
	char *list_file;
	off_t idx_offset;
	int fd, error, got, trunc;
	idx_msgnum_t m1, m1r;
	struct idx_message idx_msg;
	idx_off_t offset;
	idx_size_t size;
	struct buffer src, dst;
	struct mime_ctx mime;
	char *body, *bend;

	if (y < MIN_YEAR || y > MAX_YEAR ||
	    m < 1 || m > 12 ||
	    d < 1 || d > 31 ||
	    n < 1 || n > 999999)
		return html_error("Invalid date or message number");
	aday = YMD2ADAY(y - MIN_YEAR, m, d);

	list_file = concat(MAIL_SPOOL_PATH "/", list, NULL);
	if (!list_file)
		return html_error(NULL);

	fd = idx_open(list);
	if (fd < 0) {
		error = errno;
		free(list_file);
		return html_error(error == ENOENT ?
		    "No such mailing list" : (error == ESRCH ?
		    "Index needs rebuild" : NULL));
	}

	error = !idx_read_aday_ok(fd, aday, &m1, sizeof(m1));
	if (error || m1 < 1 || m1 >= MAX_MAILBOX_MESSAGES) {
		idx_close(fd);
		free(list_file);
		return html_error((error || m1 > 0) ? NULL : "No such message");
	}
	m1r = m1 + n - 2; /* both m1 and n are 1-based; m1r is 0-based */
	idx_offset = IDX2MSG(m1r);
	got = idx_read(fd, idx_offset, &idx_msg, sizeof(idx_msg));
	if (got != sizeof(idx_msg))
		error = 1;

	if (idx_close(fd) || error) {
		free(list_file);
		return html_error(got ? NULL : "No such message");
	}

	if (y - MIN_YEAR != idx_msg.y ||
	    m != idx_msg.m ||
	    d != idx_msg.d) {
		free(list_file);
		return html_error("No such message");
	}

	offset = idx_msg.offset;
	size = idx_msg.size;

	trunc = size > MAX_MESSAGE_SIZE;
	if (trunc)
		size = MAX_MESSAGE_SIZE;
	if (buffer_init(&src, size)) {
		free(list_file);
		return html_error(NULL);
	}
	fd = open(list_file, O_RDONLY);
	free(list_file);
	if (fd < 0) {
		buffer_free(&src);
		return html_error("mbox open error");
	}
	error =
	    lseek(fd, offset, SEEK_SET) != offset ||
	    read_loop(fd, src.start, size) != size;
	if (close(fd) || error || mime_init(&mime, &src)) {
		buffer_free(&src);
		return html_error("mbox read error");
	}
	if (buffer_init(&dst, size)) {
		buffer_free(&src);
		mime_free(&mime);
		return html_error(NULL);
	}

	body = NULL;
	while (src.end - src.ptr > 9 && *src.ptr != '\n') {
		switch (*src.ptr) {
		case 'C':
		case 'c':
			mime_decode_header(&mime);
			continue;
		}
		mime_skip_header(&mime);
	}
	if (src.ptr >= src.end) {
		buffer_free(&src);
		buffer_free(&dst);
		mime_free(&mime);
		return html_error(NULL);
	}
	if (*src.ptr == '\n')
		body = ++src.ptr;

	const char *error_msg = "Attachment not found";
	unsigned int attachment_count = 0;
	if (a)
	do {
		if (mime.entities->boundary) {
			body = mime_next_body_part(&mime);
			if (!body || body >= src.end)
				break;
			body = mime_next_body(&mime);
		}
		if (mime.entities->boundary || !is_attachment(&mime) || ++attachment_count != a)
			body = NULL;
		if (!body) {
			bend = mime_skip_body(&mime);
			if (!bend)
				break;
			continue;
		}

		int text = !strncasecmp(mime.entities->type, "text/", 5);
		if (text) {
			buffer_appends(&dst, "Content-Type: text/plain");
			if (mime.entities->charset && enc_allowed_charset(mime.entities->charset))
				buffer_appendf(&dst, "; charset=%s", mime.entities->charset);
			buffer_appendc(&dst, '\n');
		} else {
			buffer_appends(&dst, "Content-Type: application/octet-stream\n");
		}
		buffer_appendf(&dst, "Content-Disposition: %s; filename=\"", text ? "inline" : "attachment");
		buffer_append_filename(&dst, mime.entities->filename, text);
		buffer_appends(&dst, "\"\n");

		body = mime_decode_body(&mime, RECODE_NO, &bend);
		if (trunc && (!body || bend >= src.end)) {
			error_msg = "Attachment is truncated";
			break;
		}

		buffer_appendf(&dst, "Content-Length: %llu\n\n", (unsigned long long)(mime.dst.ptr - body));
		buffer_append(&dst, body, mime.dst.ptr - body);
		error_msg = NULL;
		break;
	} while (bend < src.end && mime.entities);

	buffer_free(&src);

	if (error_msg || mime.dst.error || dst.error) {
		mime_free(&mime);
		buffer_free(&dst);
		return html_error(error_msg);
	}

	mime_free(&mime);

	return html_send(&dst);
}
Ejemplo n.º 4
0
Archivo: html.c Proyecto: aabc/blists
int html_message(const char *list, unsigned int y, unsigned int m, unsigned int d, unsigned int n)
{
	unsigned int aday, n0, n2;
	char *list_file;
	off_t idx_offset;
	int fd, error, got, trunc, prev, next;
	idx_msgnum_t m0, m1, m1r;
	struct idx_message idx_msg[3];
	idx_off_t offset;
	idx_size_t size;
	struct buffer src, dst;
	struct mime_ctx mime;
	char *p, *q, *date, *from, *to, *cc, *subject, *body, *bend;

	if (y < MIN_YEAR || y > MAX_YEAR ||
	    m < 1 || m > 12 ||
	    d < 1 || d > 31 ||
	    n < 1 || n > 999999)
		return html_error("Invalid date or message number");
	aday = YMD2ADAY(y - MIN_YEAR, m, d);

	list_file = concat(MAIL_SPOOL_PATH "/", list, NULL);
	if (!list_file)
		return html_error(NULL);

	fd = idx_open(list);
	if (fd < 0) {
		error = errno;
		free(list_file);
		return html_error(error == ENOENT ?
		    "No such mailing list" : (error == ESRCH ?
		    "Index needs rebuild" : NULL));
	}

	error = !idx_read_aday_ok(fd, aday, &m1, sizeof(m1));
	if (error || m1 < 1 || m1 >= MAX_MAILBOX_MESSAGES) {
		idx_close(fd);
		free(list_file);
		return html_error((error || m1 > 0) ? NULL : "No such message");
	}
	m1r = m1 + n - (1 + 1); /* both m1 and n are 1-based; m1r is 0-based */
	idx_offset = IDX2MSG(m1r);
	prev = next = 1;
	if (m1r >= 1) {
		idx_offset -= sizeof(idx_msg[0]);
		got = idx_read(fd, idx_offset, &idx_msg, sizeof(idx_msg));
		if (got != sizeof(idx_msg)) {
			error = got != sizeof(idx_msg[0]) * 2;
			idx_msg[2] = idx_msg[1];
			next = 0;
		}
	} else {
		prev = 0;
		got = idx_read(fd, idx_offset, &idx_msg[1], sizeof(idx_msg[1]) * 2);
		if (got != sizeof(idx_msg[1]) * 2) {
			error = got != sizeof(idx_msg[1]);
			idx_msg[2] = idx_msg[1];
			next = 0;
		}
		idx_msg[0] = idx_msg[1];
	}

	n0 = n - 1;
	if (!n0 && prev && !error) {
		aday = YMD2ADAY(idx_msg[0].y, idx_msg[0].m, idx_msg[0].d);
		error = !idx_read_aday_ok(fd, aday, &m0, sizeof(m0));
		if (m1 > m0)
			n0 = m1 - m0;
		else
			error = 1;
	}

	if (idx_close(fd) || error) {
		free(list_file);
		return html_error(got ? NULL : "No such message");
	}

	n2 = n + 1;
	if (idx_msg[2].y != idx_msg[1].y ||
	    idx_msg[2].m != m || idx_msg[2].d != d)
		n2 = 1;

	if (y - MIN_YEAR != idx_msg[1].y ||
	    m != idx_msg[1].m || d != idx_msg[1].d) {
		free(list_file);
		return html_error("No such message");
	}

	offset = idx_msg[1].offset;
	size = idx_msg[1].size;

	trunc = size > MAX_MESSAGE_SIZE;
	if (trunc)
		size = MAX_MESSAGE_SIZE;
	if (buffer_init(&src, size)) {
		free(list_file);
		return html_error(NULL);
	}
	fd = open(list_file, O_RDONLY);
	free(list_file);
	if (fd < 0) {
		buffer_free(&src);
		return html_error("mbox open error");
	}
	error =
	    lseek(fd, offset, SEEK_SET) != offset ||
	    read_loop(fd, src.start, size) != size;
	if (close(fd) || error || mime_init(&mime, &src)) {
		buffer_free(&src);
		return html_error("mbox read error");
	}
	if (buffer_init(&dst, size)) {
		buffer_free(&src);
		mime_free(&mime);
		return html_error(NULL);
	}

	date = from = to = cc = subject = body = NULL;
	while (src.end - src.ptr > 9 && *src.ptr != '\n') {
		switch (*src.ptr) {
		case 'D':
		case 'd':
			if (!strncasecmp(src.ptr, "Date:", 5)) {
				date = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'F':
		case 'f':
			if (!strncasecmp(src.ptr, "From:", 5)) {
				from = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'T':
		case 't':
			if (!strncasecmp(src.ptr, "To:", 3)) {
				to = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'S':
		case 's':
			if (!strncasecmp(src.ptr, "Subject:", 8)) {
				subject = mime_decode_header(&mime);
				continue;
			}
			break;
		case 'C':
		case 'c':
			if (!strncasecmp(src.ptr, "CC:", 3))
				cc = mime_decode_header(&mime);
			else
				mime_decode_header(&mime);
			continue;
		}
		mime_skip_header(&mime);
	}
	if (src.ptr >= src.end) {
		buffer_free(&src);
		buffer_free(&dst);
		mime_free(&mime);
		return html_error(NULL);
	}
	if (*src.ptr == '\n')
		body = ++src.ptr;

	if ((p = subject)) {
		while ((p = strchr(p, '['))) {
			if (strncasecmp(++p, list, strlen(list)))
				continue;
			q = p + strlen(list);
			if (*q != ']')
				continue;
			if (*++q == ' ') q++;
			memmove(--p, q, strlen(q) + 1);
		}
	}

	buffer_appends(&dst, "\n");

	if (html_flags & HTML_HEADER) {
		buffer_appends(&dst, "<title>");
		buffer_appends_html(&dst, list);
		if (subject && strlen(subject) > 9) {
			buffer_appends(&dst, " - ");
			buffer_appends_html(&dst, subject + 9);
		}
		buffer_appends(&dst, "</title>\n");
		if (html_flags & HTML_CENSOR)
			buffer_appends(&dst, "<meta name=\"robots\" content=\"noindex\">\n");
	}

	if (html_flags & HTML_BODY) {
		unsigned int attachment_count = 0;

		if (prev) {
			buffer_appends(&dst, "<a href=\"");
			if (n == 1)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[0].y, idx_msg[0].m, idx_msg[0].d);
			buffer_appendf(&dst, "%u\">[&lt;prev]</a> ", n0);
		}
		if (next) {
			buffer_appends(&dst, "<a href=\"");
			if (n2 == 1)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[2].y, idx_msg[2].m, idx_msg[2].d);
			buffer_appendf(&dst, "%u\">[next&gt;]</a> ", n2);
		}
		if (idx_msg[1].t.pn) {
			buffer_appends(&dst, "<a href=\"");
			if (idx_msg[1].t.py != idx_msg[1].y ||
			    idx_msg[1].t.pm != idx_msg[1].m ||
			    idx_msg[1].t.pd != idx_msg[1].d)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[1].t.py, idx_msg[1].t.pm, idx_msg[1].t.pd);
			buffer_appendf(&dst, "%u\">[&lt;thread-prev]</a> ", idx_msg[1].t.pn);
		}
		if (idx_msg[1].t.nn) {
			buffer_appends(&dst, "<a href=\"");
			if (idx_msg[1].t.ny != idx_msg[1].y ||
			    idx_msg[1].t.nm != idx_msg[1].m ||
			    idx_msg[1].t.nd != idx_msg[1].d)
				buffer_appendf(&dst, "../../../%u/%02u/%02u/",
				    MIN_YEAR + idx_msg[1].t.ny, idx_msg[1].t.nm, idx_msg[1].t.nd);
			buffer_appendf(&dst, "%u\">[thread-next&gt;]</a> ", idx_msg[1].t.nn);
		}
		buffer_appends(&dst,
		    "<a href=\".\">[day]</a>"
		    " <a href=\"..\">[month]</a>"
		    " <a href=\"../..\">[year]</a>"
		    " <a href=\"../../..\">[list]</a>\n");

		buffer_appends(&dst, "<pre style=\"white-space: pre-wrap\">\n");
		if (date)
			buffer_append_header(&dst, date);
		if (from)
			buffer_append_header(&dst, from);
		if (to)
			buffer_append_header(&dst, to);
		if (cc)
			buffer_append_header(&dst, cc);
		if (subject)
			buffer_append_header(&dst, subject);

		if (!(html_flags & HTML_CENSOR))
		do {
			if (mime.entities->boundary) {
				body = mime_next_body_part(&mime);
				if (!body || body >= src.end)
					break;
				body = mime_next_body(&mime);
			}
			if (mime.entities->boundary)
				body = NULL;
			if (!body) {
				bend = mime_skip_body(&mime);
				if (!bend)
					break;
				continue;
			}

			/* mime_decode_body() will break mime vars, so,
			 * remember them now */
			char *filename = mime.entities->filename;
			char *type = mime.entities->type;
			const int isattachment = is_attachment(&mime);
			const int isinline = is_inline(&mime);
			int skip = 0;

			body = mime_decode_body(&mime, isattachment ? RECODE_NO : RECODE_YES, &bend);
			if (!body)
				break;
			if (bend >= src.end)
				skip = trunc;
			bend = src.ptr;
			if (!skip && isattachment) {
				int text = !strncasecmp(type, "text/", 5);
				attachment_count++;
				buffer_appendf(&dst, "\n<span style=\"font-family: times;\"><strong>"
				    "%s attachment \"</strong><a href=\"%u/%u\"%s>",
				    text ? "View" : "Download", n, attachment_count,
				    text ? "" :  " rel=\"nofollow\" download");
				if (filename)
					buffer_appends_html(&dst, filename);
				buffer_appends(&dst, "</a><strong>\" of type \"</strong>");
				buffer_appends_html(&dst, type);
				buffer_appends(&dst, "<strong>\"");
				if (body)
					buffer_appendf(&dst, " (%llu bytes)", (unsigned long long)(mime.dst.ptr - body));
				buffer_appends(&dst, "</strong></span>\n");
				continue;
			} else if (!isinline) {
				skip = 1;
			} else {
				skip = 0; /* do not skip non-attachments */
			}
			if (skip) {
				buffer_appends(&dst, "\n<span style=\"font-family: times;\"><strong>"
				    "Content of type \"</strong>");
				buffer_appends_html(&dst, type);
				buffer_appends(&dst, "<strong>\" skipped</strong></span>\n");
				continue;
			}
			/* inline */
			buffer_appendc(&dst, '\n');
			buffer_append_html_generic(&dst, body, mime.dst.ptr - body, 0, 1);
			mime.dst.ptr = body;
		} while (bend < src.end && mime.entities);

		if ((html_flags & HTML_CENSOR) || trunc)
			buffer_appendf(&dst, "\n<span style=\"font-family: times;\"><strong>"
			    "Content %s</strong></span>\n", (html_flags & HTML_CENSOR) ? "removed" : "truncated");

		buffer_appends(&dst, "</pre>\n");
	}

	buffer_free(&src);

	if (mime.dst.error || dst.error) {
		mime_free(&mime);
		buffer_free(&dst);
		return html_error(NULL);
	}

	mime_free(&mime);

	return html_send(&dst);
}
Ejemplo n.º 5
0
Archivo: html.c Proyecto: aabc/blists
static void buffer_append_header(struct buffer *dst, const char *what)
{
	buffer_appends_html(dst, what);
	buffer_appendc(dst, '\n');
}
Ejemplo n.º 6
0
Archivo: html.c Proyecto: aabc/blists
static void buffer_append_html_generic(struct buffer *dst, const char *what, size_t length, int quotes, int detect_urls)
{
	const char *ptr, *end, *url;
	size_t url_length;
	int url_safe;
	unsigned char c;

	ptr = what;
	end = what + length;

	while (ptr < end) {
		switch ((c = (unsigned char)*ptr++)) {
		case '<':
			buffer_appends(dst, "&lt;");
			break;
		case '>':
			buffer_appends(dst, "&gt;");
			break;
		case '&':
			buffer_appends(dst, "&amp;");
			break;
		case '"':
			if (quotes)
				buffer_appends(dst, "&quot;");
			else
				buffer_appendc(dst, c);
			break;
		case ':':
			url = NULL;
			if (detect_urls && ptr < end && *ptr == '/')
				url = detect_url(what, ptr - 1, end, &url_length, &url_safe);
			if (url && url_length <= MAX_URL_LENGTH && dst->ptr - dst->start >= ptr - 1 - url) {
				dst->ptr -= ptr - 1 - url;
				buffer_appends(dst, "<a href=\"");
				buffer_append_html_generic(dst, url, url_length, 1, 0);
				if (url_safe)
					buffer_appends(dst, "\">");
				else
					buffer_appends(dst, "\" rel=\"nofollow\">");
				buffer_append_html_generic(dst, url, url_length, 0, 0);
				buffer_appends(dst, "</a>");
				ptr = url + url_length;
			} else {
				buffer_appendc(dst, c);
			}
			break;
		case '@':
			if (ptr - what >= 2 && end - ptr >= 4 &&
			    *(ptr - 2) > ' ' && *ptr > ' ' && *(ptr + 1) > ' ' && *(ptr + 2) > ' ') {
				buffer_appends(dst, "&#64;...");
				ptr += 3;
				break;
			}
			/* FALLTHRU */
		case '\t':
		case '\n':
			buffer_appendc(dst, c);
		case '\r':
			break;
		default:
			if (c >= 0x20)
				buffer_appendc(dst, c);
			else
				buffer_appendc(dst, '.');
		}
	}
}