# HG changeset patch # User František Kučera # Date 1611611017 -3600 # Node ID 356d0b024abf3760e622145e122d3479fe318191 # Parent ea26b3359fed5f3077ecd23823176aa5fcf4e791 basic MIME header support diff -r ea26b3359fed -r 356d0b024abf src/XMLDocumentConstructor.h --- a/src/XMLDocumentConstructor.h Sat Jan 16 16:36:39 2021 +0100 +++ b/src/XMLDocumentConstructor.h Mon Jan 25 22:43:37 2021 +0100 @@ -35,30 +35,20 @@ xmlpp::DomParser* parser = nullptr; XMLNameCodec nameCodec; - enum class Mode { - ROOT, - SEQUENCE, - MAPPING, - MAP_KEY - }; - - std::string rootName = "mime"; - xmlpp::Element* current; - std::vector mode; + std::string rootName = "mime-message"; - /** - * Both MIME and XML strings are in UTF-8. - const char* y2x(mime_char_t* value) { - return value ? (const char*) value : ""; - } - - const Glib::ustring y2xname(mime_char_t* value) { - return nameCodec.encode(y2x(value)); - } - */ - - xmlpp::Element* parentOrSelf(xmlpp::Element* current) { - return current->get_parent() == nullptr ? current : current->get_parent(); + std::string format(std::shared_ptr value) { + std::stringstream timestamp; + int tz = value->getZone(); + timestamp << value->getYear() << "-"; + timestamp << std::setw(2) << std::setfill('0') << value->getMonth() << "-"; + timestamp << std::setw(2) << std::setfill('0') << value->getDay() << "T"; + timestamp << std::setw(2) << std::setfill('0') << value->getHour() << ":"; + timestamp << std::setw(2) << std::setfill('0') << value->getMinute() << ":"; + timestamp << std::setw(2) << std::setfill('0') << value->getSecond() << (tz >= 0 ? "+" : "-"); + timestamp << std::setw(2) << std::setfill('0') << std::abs(tz / 60) << ":"; + timestamp << std::setw(2) << std::setfill('0') << std::abs(tz % 60); + return timestamp.str(); } public: @@ -75,7 +65,101 @@ } void process() { - current = parser->get_document()->create_root_node(rootName); + vmime::utility::inputStreamAdapter is(*input); + vmime::string data; + vmime::utility::outputStreamStringAdapter os(data); + vmime::utility::bufferedStreamCopy(is, os); + + vmime::message m; + m.parse(data); + + // vmime::shared_ptr is = vmime::make_shared(*input); + // m.parse(is, 0); + + vmime::charset ch(vmime::charsets::UTF_8); + + //std::cerr << "Subject:" << m.getHeader()->Subject()->getValue()->getConvertedText(ch) << std::endl; + + xmlpp::Element* root = parser->get_document()->create_root_node(rootName); + + xmlpp::Element* headers = root->add_child("headers"); + + for (std::shared_ptr mimeField : m.getHeader()->getFieldList()) { + // TODO: Are names always ASCII and subset of UTF-8? + // TODO: Convert header names to lower case? (they should be case insensitive) + xmlpp::Element* field = headers->add_child(nameCodec.encode(mimeField->getName())); + + + if (auto value = mimeField->getValue()) { + field->add_child_text(value->getConvertedText(ch)); + } else if (auto value = mimeField->getValue()) { + std::string name = value->getName().getConvertedText(ch); + std::string email = value->getEmail().toString(); + if (name.size()) field->set_attribute("name", name); + if (email.size()) field->add_child_text(email); + } else if (auto value = mimeField->getValue()) { + for (auto address : value->getAddressList()) { + xmlpp::Element* addressField = field->add_child("address"); + if (std::shared_ptr mailbox = std::dynamic_pointer_cast (address)) { + std::string name = mailbox->getName().getConvertedText(ch); + std::string email = mailbox->getEmail().toString(); + if (name.size()) addressField->set_attribute("name", name); + if (email.size()) addressField->add_child_text(email); + } else if (std::shared_ptr mailbox = std::dynamic_pointer_cast (address)) { + // TODO: mailboxGroup? + } + } + } else if (auto value = mimeField->getValue()) { + field->set_attribute("year", std::to_string(value->getYear())); + field->set_attribute("month", std::to_string(value->getMonth())); + field->set_attribute("day", std::to_string(value->getDay())); + field->set_attribute("hour", std::to_string(value->getHour())); + field->set_attribute("minute", std::to_string(value->getMinute())); + field->set_attribute("second", std::to_string(value->getSecond())); + field->set_attribute("zone", std::to_string(value->getZone())); // timezone is in minutes + field->add_child_text(format(value)); + } else if (auto value = mimeField->getValue()) { + std::string type = value->getType(); + std::string subType = value->getSubType(); + if (type.size()) field->set_attribute("type", type); + if (subType.size()) field->set_attribute("subType", subType); + if (type.size() && subType.size()) field->add_child_text(type + "/" + subType); + // TODO: encoding from the "Content-Type: text/plain; charset=us-ascii" type header? + } else if (auto value = mimeField->getValue()) { + field->set_attribute("left", value->getLeft()); + field->set_attribute("right", value->getRight()); + field->add_child_text(value->getId()); + } else if (auto value = mimeField->getValue()) { + for (auto messageId : value->getMessageIdList()) { + xmlpp::Element* messageIdField = field->add_child("Mssage-ID"); // TODO: lower case? + messageIdField->set_attribute("left", messageId->getLeft()); + messageIdField->set_attribute("right", messageId->getRight()); + messageIdField->add_child_text(messageId->getId()); + } + } else if (auto value = mimeField->getValue()) { + field->add_child_text(value->getName()); + } else if (auto value = mimeField->getValue()) { + field->set_attribute("from", value->getFrom()); + field->set_attribute("via", value->getVia()); + field->set_attribute("by", value->getBy()); + field->set_attribute("id", value->getId()); + field->set_attribute("for", value->getFor()); + // TODO: date of Received/relay + // TODO: missing values or incomplete parsing of Received/relay in vmime + } else if (auto value = mimeField->getValue()) { + std::string local = value->getLocalPart(); + std::string domain = value->getDomain(); + if (local.size()) field->set_attribute("local", local); + if (domain.size()) field->set_attribute("domain", domain); + if (local.size() && domain.size()) field->add_child_text(local + "@" + domain); + } else if (auto value = mimeField->getValue()) { + field->add_child_text(value->getName()); + } else { + field->add_child_text("TODO: unknown header type"); // TODO: generic conversion as fallback? + } + } + + xmlpp::Element* body = root->add_child("body"); } };