#include "MessageHeaders.h" #include #include namespace mail { void splitMessage(const std::string &message, std::string &headerBlock, std::string &bodyBlock) { size_t split = message.find("\r\n\r\n"); size_t skip = 4; if (split == std::string::npos) { split = message.find("\n\n"); skip = 2; } if (split == std::string::npos) { headerBlock = message; bodyBlock = ""; } else { headerBlock = message.substr(0, split); bodyBlock = message.substr(split + skip); } } std::vector> parseHeaderLines(const std::string &headerBlock) { std::vector lines; size_t pos = 0; while (pos <= headerBlock.size()) { size_t next = headerBlock.find("\r\n", pos); size_t lineEnd = (next == std::string::npos) ? headerBlock.size() : next; lines.push_back(headerBlock.substr(pos, lineEnd - pos)); if (next == std::string::npos) break; pos = next + 2; } std::vector> headers; for (const std::string &line : lines) { if (line.empty()) continue; if ((line[0] == ' ' || line[0] == '\t') && !headers.empty()) { std::string continuation = line; size_t start = continuation.find_first_not_of(" \t"); headers.back().second += " "; headers.back().second += (start == std::string::npos) ? "" : continuation.substr(start); continue; } size_t colon = line.find(':'); if (colon == std::string::npos) continue; std::string name = line.substr(0, colon); std::string value = line.substr(colon + 1); size_t start = value.find_first_not_of(" \t"); value = (start == std::string::npos) ? "" : value.substr(start); headers.push_back({name, value}); } return headers; } std::string getHeader(const std::vector> &headers, const std::string &name) { for (const auto &header : headers) { if (header.first.size() != name.size()) continue; bool match = true; for (size_t i = 0; i < name.size(); ++i) if (std::toupper((unsigned char)header.first[i]) != std::toupper((unsigned char)name[i])) { match = false; break; } if (match) return header.second; } return ""; } namespace { std::string trim(const std::string &s) { size_t start = s.find_first_not_of(" \t\r\n"); size_t end = s.find_last_not_of(" \t\r\n"); if (start == std::string::npos) return ""; return s.substr(start, end - start + 1); } std::string stripQuotes(const std::string &s) { std::string value = trim(s); if (value.size() >= 2 && value.front() == '"' && value.back() == '"') return value.substr(1, value.size() - 2); return value; } Address parseOneAddress(const std::string &text) { Address address; size_t lt = text.find('<'); size_t gt = text.find('>'); std::string addrPart; if (lt != std::string::npos && gt != std::string::npos && gt > lt) { address.name = stripQuotes(trim(text.substr(0, lt))); addrPart = text.substr(lt + 1, gt - lt - 1); } else { addrPart = text; } addrPart = trim(addrPart); size_t at = addrPart.find_last_of('@'); if (at == std::string::npos) { address.mailbox = addrPart; } else { address.mailbox = addrPart.substr(0, at); address.host = addrPart.substr(at + 1); } return address; } } std::vector
parseAddressList(const std::string &headerValue) { std::vector
addresses; std::string current; bool inQuotes = false; int angleDepth = 0; for (char c : headerValue) { if (c == '"') inQuotes = !inQuotes; else if (!inQuotes && c == '<') ++angleDepth; else if (!inQuotes && c == '>' && angleDepth > 0) --angleDepth; if (c == ',' && !inQuotes && angleDepth == 0) { std::string piece = trim(current); if (!piece.empty()) addresses.push_back(parseOneAddress(piece)); current.clear(); continue; } current += c; } std::string piece = trim(current); if (!piece.empty()) addresses.push_back(parseOneAddress(piece)); return addresses; } std::string imapQuoteOrNil(const std::string &value) { if (value.empty()) return "NIL"; std::string escaped; escaped.reserve(value.size() + 2); escaped += '"'; for (char c : value) { if (c == '"' || c == '\\') escaped += '\\'; if (c == '\r' || c == '\n') continue; escaped += c; } escaped += '"'; return escaped; } namespace { std::string addressListToEnvelope(const std::vector
&addresses) { if (addresses.empty()) return "NIL"; std::string result = "("; for (const Address &address : addresses) { result += "(" + imapQuoteOrNil(address.name) + " NIL " + imapQuoteOrNil(address.mailbox) + " " + imapQuoteOrNil(address.host) + ")"; } result += ")"; return result; } } std::string buildEnvelope(const std::string &message) { std::string headerBlock, bodyBlock; splitMessage(message, headerBlock, bodyBlock); auto headers = parseHeaderLines(headerBlock); std::string date = getHeader(headers, "Date"); std::string subject = getHeader(headers, "Subject"); std::vector
from = parseAddressList(getHeader(headers, "From")); std::string senderHeader = getHeader(headers, "Sender"); std::vector
sender = senderHeader.empty() ? from : parseAddressList(senderHeader); std::string replyToHeader = getHeader(headers, "Reply-To"); std::vector
replyTo = replyToHeader.empty() ? from : parseAddressList(replyToHeader); std::vector
to = parseAddressList(getHeader(headers, "To")); std::vector
cc = parseAddressList(getHeader(headers, "Cc")); std::vector
bcc = parseAddressList(getHeader(headers, "Bcc")); std::string inReplyTo = getHeader(headers, "In-Reply-To"); std::string messageId = getHeader(headers, "Message-ID"); std::string envelope = "("; envelope += imapQuoteOrNil(date) + " "; envelope += imapQuoteOrNil(subject) + " "; envelope += addressListToEnvelope(from) + " "; envelope += addressListToEnvelope(sender) + " "; envelope += addressListToEnvelope(replyTo) + " "; envelope += addressListToEnvelope(to) + " "; envelope += addressListToEnvelope(cc) + " "; envelope += addressListToEnvelope(bcc) + " "; envelope += imapQuoteOrNil(inReplyTo) + " "; envelope += imapQuoteOrNil(messageId); envelope += ")"; return envelope; } }