@@ 340,6 340,50 @@ export fn header_set(head: *header, key: str, val: str) void = {
assert(vals[0] == "text/x-hare");
};
// Writes a MIME [[header]] to an [[io::handle]].
export fn write_header(sink: io::handle, head: *header) (size | io::error) = {
let z = 0z;
for (let i = len(head.fields); i > 0; i -= 1) {
const field = head.fields[i - 1];
const raw = header_field_raw(field)!;
z += io::writeall(sink, raw)?;
};
z += fmt::fprint(sink, "\r\n")?;
return z;
};
@test fn write_header() void = {
const head = new_header();
defer header_finish(&head);
const dkim = "DKIM-Signature: a=rsa-sha256; bh=uI/rVH7mLBSWkJVvQYKz3TbpdI2BLZWTIMKcuo0KHOI=; c=simple/simple; d=example.org; h=Subject:To:From; s=default; t=1577562184; v=1; b=;\r\n";
header_add(&head, "DKIM-Signature", "a=rsa-sha256; bh=uI/rVH7mLBSWkJVvQYKz3TbpdI2BLZWTIMKcuo0KHOI=; c=simple/simple; d=example.org; h=Subject:To:From; s=default; t=1577562184; v=1; b=;");
header_add_raw(&head, strings::toutf8(dkim));
header_add(&head, "Content-Type", "text/html");
header_add(&head, "Content-Type", "text/plain");
header_add(&head, "FROM", "Harriet <harriet@harelang.org>");
header_add(&head, "to", "Drew DeVault <sir@cmpwn.com>");
const sink = strio::dynamic();
defer io::close(&sink)!;
write_header(&sink, &head)!;
const result = strio::string(&sink);
const expect =
"To: Drew DeVault <sir@cmpwn.com>\r\n"
"From: Harriet <harriet@harelang.org>\r\n"
"Content-Type: text/plain\r\n"
"Content-Type: text/html\r\n"
// Should not fold or canonicalize
"DKIM-Signature: a=rsa-sha256; bh=uI/rVH7mLBSWkJVvQYKz3TbpdI2BLZWTIMKcuo0KHOI=; c=simple/simple; d=example.org; h=Subject:To:From; s=default; t=1577562184; v=1; b=;\r\n"
// Should fold & canonicalize
"Dkim-Signature: a=rsa-sha256;\r\n"
" bh=uI/rVH7mLBSWkJVvQYKz3TbpdI2BLZWTIMKcuo0KHOI=; c=simple/simple;\r\n"
" d=example.org; h=Subject:To:From; s=default; t=1577562184; v=1; b=;\r\n"
"\r\n";
assert(result == expect);
};
def PREFERRED_HEADER_LEN = 76z;
def MAX_HEADER_LEN = 998z;