File indexing completed on 2024-11-24 04:44:13

0001 /*
0002  * SPDX-FileCopyrightText: 2012 Sofia Balicka <balicka@kolabsys.com>
0003  * SPDX-FileCopyrightText: 2014 Christian Mollekopf <mollekopf@kolabsys.com>
0004  *
0005  * SPDX-License-Identifier: LGPL-3.0-or-later
0006  */
0007 
0008 #include "mimeobject.h"
0009 #include "conversion/commonconversion.h"
0010 #include "conversion/kabcconversion.h"
0011 #include "conversion/kcalconversion.h"
0012 #include "conversion/kolabconversion.h"
0013 #include "kolabformat/xmlobject.h"
0014 
0015 #include "kolabformat/v2helpers.h"
0016 #include "libkolab-version.h"
0017 #include "mime/mimeutils.h"
0018 #include "pimkolab_debug.h"
0019 
0020 #include <KRandom>
0021 #include <QString>
0022 #include <cstring>
0023 
0024 Q_DECLARE_METATYPE(Kolab::Event)
0025 Q_DECLARE_METATYPE(Kolab::Todo)
0026 Q_DECLARE_METATYPE(Kolab::Journal)
0027 Q_DECLARE_METATYPE(Kolab::Contact)
0028 Q_DECLARE_METATYPE(Kolab::DistList)
0029 Q_DECLARE_METATYPE(Kolab::Note)
0030 Q_DECLARE_METATYPE(Kolab::Freebusy)
0031 Q_DECLARE_METATYPE(Kolab::Configuration)
0032 
0033 static inline std::string eventKolabType()
0034 {
0035     return std::string(KOLAB_TYPE_EVENT);
0036 }
0037 
0038 static inline std::string todoKolabType()
0039 {
0040     return std::string(KOLAB_TYPE_TASK);
0041 }
0042 
0043 static inline std::string journalKolabType()
0044 {
0045     return std::string(KOLAB_TYPE_JOURNAL);
0046 }
0047 
0048 static inline std::string contactKolabType()
0049 {
0050     return std::string(KOLAB_TYPE_CONTACT);
0051 }
0052 
0053 static inline std::string distlistKolabType()
0054 {
0055     return std::string(KOLAB_TYPE_DISTLIST);
0056 }
0057 
0058 static inline std::string distlistKolabTypeCompat()
0059 {
0060     return std::string(KOLAB_TYPE_DISTLIST_V2);
0061 }
0062 
0063 static inline std::string noteKolabType()
0064 {
0065     return std::string(KOLAB_TYPE_NOTE);
0066 }
0067 
0068 static inline std::string configurationKolabType()
0069 {
0070     return std::string(KOLAB_TYPE_CONFIGURATION);
0071 }
0072 
0073 static inline std::string dictKolabType()
0074 {
0075     return std::string(KOLAB_TYPE_DICT);
0076 }
0077 
0078 static inline std::string freebusyKolabType()
0079 {
0080     return std::string(KOLAB_TYPE_FREEBUSY);
0081 }
0082 
0083 static inline std::string relationKolabType()
0084 {
0085     return std::string(KOLAB_TYPE_RELATION);
0086 }
0087 
0088 static inline std::string xCalMimeType()
0089 {
0090     return std::string(MIME_TYPE_XCAL);
0091 }
0092 
0093 static inline std::string xCardMimeType()
0094 {
0095     return std::string(MIME_TYPE_XCARD);
0096 }
0097 
0098 static inline std::string kolabMimeType()
0099 {
0100     return std::string(MIME_TYPE_KOLAB);
0101 }
0102 
0103 static std::string getProductId(const std::string &pId)
0104 {
0105     if (pId.empty()) {
0106         return LIBKOLAB_LIB_VERSION_STRING;
0107     }
0108     return pId + ' ' + LIBKOLAB_LIB_VERSION_STRING;
0109 }
0110 
0111 namespace Kolab
0112 {
0113 static Kolab::ObjectType getObjectType(const std::string &type)
0114 {
0115     if (type == eventKolabType()) {
0116         return EventObject;
0117     } else if (type == todoKolabType()) {
0118         return TodoObject;
0119     } else if (type == journalKolabType()) {
0120         return JournalObject;
0121     } else if (type == contactKolabType()) {
0122         return ContactObject;
0123     } else if (type == distlistKolabType() || type == distlistKolabTypeCompat()) {
0124         return DistlistObject;
0125     } else if (type == noteKolabType()) {
0126         return NoteObject;
0127     } else if (type == freebusyKolabType()) {
0128         return FreebusyObject;
0129     } else if (strstr(type.c_str(), KOLAB_TYPE_DICT)) { // Previous versions appended the language to the type
0130         return DictionaryConfigurationObject;
0131     } else if (type == relationKolabType()) {
0132         return RelationConfigurationObject;
0133     }
0134     qCWarning(PIMKOLAB_LOG) << "Unknown object type: " << type;
0135     return Kolab::InvalidObject;
0136 }
0137 
0138 static QByteArray getTypeString(Kolab::ObjectType type)
0139 {
0140     switch (type) {
0141     case EventObject:
0142         return KOLAB_TYPE_EVENT;
0143     case TodoObject:
0144         return KOLAB_TYPE_TASK;
0145     case JournalObject:
0146         return KOLAB_TYPE_JOURNAL;
0147     case FreebusyObject:
0148         return KOLAB_TYPE_FREEBUSY;
0149     case ContactObject:
0150         return KOLAB_TYPE_CONTACT;
0151     case DistlistObject:
0152         return KOLAB_TYPE_DISTLIST;
0153     case NoteObject:
0154         return KOLAB_TYPE_NOTE;
0155     case DictionaryConfigurationObject:
0156         return KOLAB_TYPE_CONFIGURATION;
0157     case RelationConfigurationObject:
0158         return KOLAB_TYPE_RELATION;
0159     default:
0160         qCCritical(PIMKOLAB_LOG) << "unknown type " << type;
0161     }
0162     return {};
0163 }
0164 
0165 static QByteArray getMimeType(Kolab::ObjectType type)
0166 {
0167     switch (type) {
0168     case EventObject:
0169     case TodoObject:
0170     case JournalObject:
0171     case FreebusyObject:
0172         return MIME_TYPE_XCAL;
0173     case ContactObject:
0174     case DistlistObject:
0175         return MIME_TYPE_XCARD;
0176     case NoteObject:
0177     case DictionaryConfigurationObject:
0178     case RelationConfigurationObject:
0179         return MIME_TYPE_KOLAB;
0180     default:
0181         qCCritical(PIMKOLAB_LOG) << "unknown type " << type;
0182     }
0183     return {};
0184 }
0185 
0186 static Kolab::ObjectType detectType(const KMime::Message::Ptr &msg)
0187 {
0188     const auto mimetypes{Mime::getContentMimeTypeList(msg)};
0189     for (const QByteArray &type : mimetypes) {
0190         Kolab::ObjectType t = getObjectType(type.toStdString()); // works for v2 types
0191         if (t != InvalidObject) {
0192             return t;
0193         }
0194     }
0195     return InvalidObject;
0196 }
0197 
0198 static void printMessageDebugInfo(const KMime::Message::Ptr &msg)
0199 {
0200     // TODO replace by Debug stream for Mimemessage
0201     qCDebug(PIMKOLAB_LOG) << "MessageId: " << msg->messageID()->asUnicodeString();
0202     qCDebug(PIMKOLAB_LOG) << "Subject: " << msg->subject()->asUnicodeString();
0203     //     Debug() << msg->encodedContent();
0204 }
0205 
0206 //@cond PRIVATE
0207 class MIMEObjectPrivate
0208 {
0209 public:
0210     MIMEObjectPrivate() = default;
0211 
0212     QVariant readKolabV2(const KMime::Message::Ptr &msg, Kolab::ObjectType objectType);
0213     QVariant readKolabV3(const KMime::Message::Ptr &msg, Kolab::ObjectType objectType);
0214     QVariant parseMimeMessage(const KMime::Message::Ptr &msg);
0215     QVariant parseMimeMessage(const std::string &s);
0216 
0217     ObjectType mObjectType = InvalidObject;
0218     Version mVersion = KolabV3;
0219     ObjectType mOverrideObjectType = InvalidObject;
0220     Version mOverrideVersion = KolabV3;
0221     bool mDoOverrideVersion = false;
0222     QVariant mObject;
0223 };
0224 //@endcond
0225 
0226 static std::vector<Kolab::Attachment> getAttachments(const std::vector<Kolab::Attachment> &attachments, const KMime::Message::Ptr &msg)
0227 {
0228     std::vector<Kolab::Attachment> allAttachments;
0229     for (const Kolab::Attachment &attachment : attachments) {
0230         if (!attachment.uri().empty()) {
0231             const Kolab::Attachment extracted = Mime::getAttachment(attachment.uri(), msg);
0232             if (extracted.isValid()) {
0233                 allAttachments.push_back(extracted);
0234             }
0235         } else {
0236             allAttachments.push_back(attachment);
0237         }
0238     }
0239     return allAttachments;
0240 }
0241 
0242 static std::vector<Kolab::Attachment> getAttachments(const QStringList &attachmentNames, const KMime::Message::Ptr &msg)
0243 {
0244     std::vector<Kolab::Attachment> allAttachments;
0245     for (const QString &name : attachmentNames) {
0246         const Kolab::Attachment extracted = Mime::getAttachmentByName(name, msg);
0247         if (extracted.isValid()) {
0248             allAttachments.push_back(extracted);
0249         }
0250     }
0251     return allAttachments;
0252 }
0253 
0254 QVariant MIMEObjectPrivate::readKolabV2(const KMime::Message::Ptr &msg, Kolab::ObjectType objectType)
0255 {
0256     if (objectType == DictionaryConfigurationObject) {
0257         KMime::Content *xmlContent = Mime::findContentByType(msg, "application/xml");
0258         if (!xmlContent) {
0259             qCCritical(PIMKOLAB_LOG) << "no application/xml part found";
0260             printMessageDebugInfo(msg);
0261             return InvalidObject;
0262         }
0263         const QByteArray &xmlData = xmlContent->decodedContent();
0264         QString dictionaryLanguage;
0265         const QStringList entries = Kolab::readLegacyDictionaryConfiguration(xmlData, dictionaryLanguage);
0266         mObjectType = objectType;
0267         Kolab::Dictionary dictionary(Conversion::toStdString(dictionaryLanguage));
0268         std::vector<std::string> convertedEntries;
0269         convertedEntries.reserve(entries.count());
0270         for (const QString &value : entries) {
0271             convertedEntries.push_back(Conversion::toStdString(value));
0272         }
0273         dictionary.setEntries(convertedEntries);
0274         return QVariant::fromValue(Kolab::Configuration(dictionary));
0275     }
0276     KMime::Content *xmlContent = Mime::findContentByType(msg, getTypeString(objectType));
0277     if (!xmlContent) {
0278         qCCritical(PIMKOLAB_LOG) << "no part with type" << getTypeString(objectType) << " found";
0279         printMessageDebugInfo(msg);
0280         return {};
0281     }
0282     const QByteArray &xmlData = xmlContent->decodedContent();
0283     if (xmlData.isEmpty()) {
0284         qCCritical(PIMKOLAB_LOG) << "no content in message part with type" << getTypeString(objectType);
0285         printMessageDebugInfo(msg);
0286         return {};
0287     }
0288 
0289     QVariant variant;
0290     switch (objectType) {
0291     case EventObject: {
0292         QStringList attachments;
0293         auto kEvent = fromXML<KCalendarCore::Event::Ptr, KolabV2::Event>(xmlData, attachments);
0294         if (kEvent) {
0295             Kolab::Event event = Kolab::Conversion::fromKCalendarCore(*kEvent);
0296             event.setAttachments(getAttachments(attachments, msg));
0297             variant = QVariant::fromValue(event);
0298         }
0299         break;
0300     }
0301     case TodoObject: {
0302         QStringList attachments;
0303         auto kTodo = fromXML<KCalendarCore::Todo::Ptr, KolabV2::Task>(xmlData, attachments);
0304         if (kTodo) {
0305             Kolab::Todo todo = Kolab::Conversion::fromKCalendarCore(*kTodo);
0306             todo.setAttachments(getAttachments(attachments, msg));
0307             variant = QVariant::fromValue(todo);
0308         }
0309         break;
0310     }
0311     case JournalObject: {
0312         QStringList attachments;
0313         auto kJournal = fromXML<KCalendarCore::Journal::Ptr, KolabV2::Journal>(xmlData, attachments);
0314         if (kJournal) {
0315             Kolab::Journal journal = Kolab::Conversion::fromKCalendarCore(*kJournal);
0316             journal.setAttachments(getAttachments(attachments, msg));
0317             variant = QVariant::fromValue(journal);
0318         }
0319         break;
0320     }
0321     case ContactObject: {
0322         KContacts::Addressee kContact = addresseeFromKolab(xmlData, msg);
0323         Kolab::Contact contact = Kolab::Conversion::fromKABC(kContact);
0324         variant = QVariant::fromValue(contact);
0325         break;
0326     }
0327     case DistlistObject: {
0328         KContacts::ContactGroup kContactGroup = contactGroupFromKolab(xmlData);
0329         Kolab::DistList distlist = Kolab::Conversion::fromKABC(kContactGroup);
0330         variant = QVariant::fromValue(distlist);
0331         break;
0332     }
0333     case NoteObject: {
0334         KMime::Message::Ptr kNote = noteFromKolab(xmlData, msg->date()->dateTime());
0335         Kolab::Note note = Kolab::Conversion::fromNote(kNote);
0336         variant = QVariant::fromValue(note);
0337         break;
0338     }
0339     default:
0340         CRITICAL(QStringLiteral("no kolab object found "));
0341         break;
0342     }
0343     if (ErrorHandler::errorOccured()) {
0344         printMessageDebugInfo(msg);
0345         return {};
0346     }
0347     mObjectType = objectType;
0348     return variant;
0349 }
0350 
0351 QVariant MIMEObjectPrivate::readKolabV3(const KMime::Message::Ptr &msg, Kolab::ObjectType objectType)
0352 {
0353     KMime::Content *const xmlContent = Mime::findContentByType(msg, getMimeType(objectType));
0354     if (!xmlContent) {
0355         qCCritical(PIMKOLAB_LOG) << "no " << getMimeType(objectType) << " part found";
0356         printMessageDebugInfo(msg);
0357         return InvalidObject;
0358     }
0359     const QByteArray &content = xmlContent->decodedContent();
0360     const std::string xml = std::string(content.data(), content.size());
0361     QVariant variant;
0362     switch (objectType) {
0363     case EventObject: {
0364         Kolab::Event event = Kolab::readEvent(xml, false);
0365         event.setAttachments(getAttachments(event.attachments(), msg));
0366         variant = QVariant::fromValue<Kolab::Event>(event);
0367         break;
0368     }
0369     case TodoObject: {
0370         Kolab::Todo todo = Kolab::readTodo(xml, false);
0371         todo.setAttachments(getAttachments(todo.attachments(), msg));
0372         variant = QVariant::fromValue<Kolab::Todo>(todo);
0373         break;
0374     }
0375     case JournalObject: {
0376         Kolab::Journal journal = Kolab::readJournal(xml, false);
0377         journal.setAttachments(getAttachments(journal.attachments(), msg));
0378         variant = QVariant::fromValue<Kolab::Journal>(journal);
0379         break;
0380     }
0381     case ContactObject:
0382         variant = QVariant::fromValue<Kolab::Contact>(Kolab::readContact(xml, false));
0383         break;
0384     case DistlistObject:
0385         variant = QVariant::fromValue<Kolab::DistList>(Kolab::readDistlist(xml, false));
0386         break;
0387     case NoteObject:
0388         variant = QVariant::fromValue<Kolab::Note>(Kolab::readNote(xml, false));
0389         break;
0390     case FreebusyObject:
0391         variant = QVariant::fromValue<Kolab::Freebusy>(Kolab::readFreebusy(xml, false));
0392         break;
0393     case DictionaryConfigurationObject:
0394     case RelationConfigurationObject:
0395         variant = QVariant::fromValue<Kolab::Configuration>(Kolab::readConfiguration(xml, false));
0396         break;
0397     default:
0398         qCCritical(PIMKOLAB_LOG) << "no kolab object found ";
0399         printMessageDebugInfo(msg);
0400         break;
0401     }
0402 
0403     if (ErrorHandler::errorOccured()) {
0404         printMessageDebugInfo(msg);
0405         return {};
0406     }
0407     mObjectType = objectType;
0408     return variant;
0409 }
0410 
0411 QVariant MIMEObjectPrivate::parseMimeMessage(const KMime::Message::Ptr &msg)
0412 {
0413     ErrorHandler::clearErrors();
0414     mObjectType = InvalidObject;
0415     if (msg->contents().isEmpty()) {
0416         qCCritical(PIMKOLAB_LOG) << "message has no contents (we likely failed to parse it correctly)";
0417         printMessageDebugInfo(msg);
0418         return {};
0419     }
0420     Kolab::ObjectType objectType = InvalidObject;
0421     if (mOverrideObjectType == InvalidObject) {
0422         if (KMime::Headers::Base *xKolabHeader = msg->headerByType(X_KOLAB_TYPE_HEADER)) {
0423             objectType = getObjectType(xKolabHeader->asUnicodeString().trimmed().toStdString());
0424         } else {
0425             qCWarning(PIMKOLAB_LOG) << "could not find the X-Kolab-Type Header, trying autodetection";
0426             // This works only for v2 messages atm.
0427             objectType = detectType(msg);
0428         }
0429     } else {
0430         objectType = mOverrideObjectType;
0431     }
0432     if (objectType == InvalidObject) {
0433         qCCritical(PIMKOLAB_LOG) << "unable to detect object type";
0434         printMessageDebugInfo(msg);
0435         return {};
0436     }
0437 
0438     if (!mDoOverrideVersion) {
0439         KMime::Headers::Base *xKolabVersion = msg->headerByType(X_KOLAB_MIME_VERSION_HEADER);
0440         if (!xKolabVersion) {
0441             // For backwards compatibility to development versions, can be removed in future versions
0442             xKolabVersion = msg->headerByType(X_KOLAB_MIME_VERSION_HEADER_COMPAT);
0443         }
0444         if (!xKolabVersion || xKolabVersion->asUnicodeString() == KOLAB_VERSION_V2) {
0445             mVersion = KolabV2;
0446         } else {
0447             if (xKolabVersion->asUnicodeString() != KOLAB_VERSION_V3) { // TODO version compatibility check?
0448                 qCWarning(PIMKOLAB_LOG) << "Kolab Version Header available but not on the same version as the implementation: "
0449                                         << xKolabVersion->asUnicodeString();
0450             }
0451             mVersion = KolabV3;
0452         }
0453     } else {
0454         mVersion = mOverrideVersion;
0455     }
0456 
0457     if (mVersion == KolabV2) {
0458         return readKolabV2(msg, objectType);
0459     }
0460     return readKolabV3(msg, objectType);
0461 }
0462 
0463 QVariant MIMEObjectPrivate::parseMimeMessage(const std::string &s)
0464 {
0465     KMime::Message::Ptr msg(new KMime::Message);
0466     msg->setContent(QByteArray(s.c_str()));
0467     msg->parse();
0468     return parseMimeMessage(msg);
0469 }
0470 
0471 MIMEObject::MIMEObject()
0472     : d(new MIMEObjectPrivate)
0473 {
0474 }
0475 
0476 MIMEObject::~MIMEObject() = default;
0477 
0478 void MIMEObject::setObjectType(ObjectType type)
0479 {
0480     d->mOverrideObjectType = type;
0481 }
0482 
0483 void MIMEObject::setVersion(Version version)
0484 {
0485     d->mOverrideVersion = version;
0486     d->mDoOverrideVersion = true;
0487 }
0488 
0489 static std::string createCid()
0490 {
0491     return QStringLiteral("cid:%1@%2").arg(KRandom::randomString(16), QStringLiteral("kolab.resource.akonadi")).toStdString();
0492 }
0493 
0494 std::vector<Kolab::Attachment> convertToReferences(const std::vector<Kolab::Attachment> &attachments, std::vector<std::string> &attachmentCids)
0495 {
0496     std::vector<Kolab::Attachment> attachmentsWithReferences;
0497     for (const Kolab::Attachment &a : attachments) {
0498         Kolab::Attachment attachment;
0499         attachment.setLabel(a.label());
0500         const std::string cid = a.uri().empty() ? createCid() : a.uri();
0501         attachmentCids.push_back(cid);
0502         attachment.setUri(cid, a.mimetype()); // Serialize the attachment as attachment with uri, referencing the created mime-part
0503         attachmentsWithReferences.push_back(attachment);
0504     }
0505     return attachmentsWithReferences;
0506 }
0507 
0508 template<class T>
0509 static T convertAttachmentsToReferences(const T &incidence, std::vector<std::string> &attachmentCids)
0510 {
0511     T removedAttachments = incidence;
0512     removedAttachments.setAttachments(convertToReferences(incidence.attachments(), attachmentCids));
0513     return removedAttachments;
0514 }
0515 
0516 static void addAttachments(KMime::Message::Ptr msg, const std::vector<Attachment> &attachments, std::vector<std::string> &attachmentCids)
0517 {
0518     int index = 0;
0519     for (const Attachment &attachment : attachments) {
0520         const std::string data = attachment.data();
0521         const std::string cid = attachmentCids.empty() ? attachment.uri() : attachmentCids.at(index);
0522         msg->appendContent(Mime::createAttachmentPart(Mime::fromCid(QString::fromStdString(cid.c_str())).toLatin1(),
0523                                                       QByteArray(attachment.mimetype().c_str()),
0524                                                       QString::fromStdString(attachment.label()),
0525                                                       QByteArray(data.c_str(), data.size())));
0526         index++;
0527     }
0528 }
0529 
0530 ObjectType MIMEObject::parseMessage(const std::string &msg)
0531 {
0532     d->mObject = d->parseMimeMessage(msg);
0533     return d->mObjectType;
0534 }
0535 
0536 ObjectType MIMEObject::getType() const
0537 {
0538     return d->mObjectType;
0539 }
0540 
0541 Version MIMEObject::getVersion() const
0542 {
0543     return d->mVersion;
0544 }
0545 
0546 Kolab::Event MIMEObject::getEvent() const
0547 {
0548     return d->mObject.value<Kolab::Event>();
0549 }
0550 
0551 Kolab::Todo MIMEObject::getTodo() const
0552 {
0553     return d->mObject.value<Kolab::Todo>();
0554 }
0555 
0556 Kolab::Journal MIMEObject::getJournal() const
0557 {
0558     return d->mObject.value<Kolab::Journal>();
0559 }
0560 
0561 Kolab::Note MIMEObject::getNote() const
0562 {
0563     return d->mObject.value<Kolab::Note>();
0564 }
0565 
0566 Kolab::Contact MIMEObject::getContact() const
0567 {
0568     return d->mObject.value<Kolab::Contact>();
0569 }
0570 
0571 Kolab::DistList MIMEObject::getDistlist() const
0572 {
0573     return d->mObject.value<Kolab::DistList>();
0574 }
0575 
0576 Kolab::Freebusy MIMEObject::getFreebusy() const
0577 {
0578     return d->mObject.value<Kolab::Freebusy>();
0579 }
0580 
0581 Kolab::Configuration MIMEObject::getConfiguration() const
0582 {
0583     return d->mObject.value<Kolab::Configuration>();
0584 }
0585 
0586 std::string MIMEObject::writeEvent(const Event &event, Version version, const std::string &pId)
0587 {
0588     ErrorHandler::clearErrors();
0589     const std::string productId = getProductId(pId);
0590 
0591     KMime::Message::Ptr msg;
0592     Kolab::XMLObject xmlObject;
0593     std::vector<std::string> attachmentCids;
0594     if (version == KolabV3) {
0595         const std::string xml = xmlObject.writeEvent(convertAttachmentsToReferences(event, attachmentCids), version, productId);
0596         msg = Mime::createMessage(xCalMimeType(), eventKolabType(), xml, true, productId, event.organizer().email(), event.organizer().name(), event.uid());
0597     } else if (version == KolabV2) {
0598         const std::string xml = xmlObject.writeEvent(event, version, productId);
0599         msg = Mime::createMessage(eventKolabType(), eventKolabType(), xml, false, productId, event.organizer().email(), event.organizer().name(), event.uid());
0600     }
0601     addAttachments(msg, event.attachments(), attachmentCids);
0602     msg->assemble();
0603     return msg->encodedContent().data();
0604 }
0605 
0606 Event MIMEObject::readEvent(const std::string &s)
0607 {
0608     return d->parseMimeMessage(s).value<Kolab::Event>();
0609 }
0610 
0611 std::string MIMEObject::writeTodo(const Todo &todo, Version version, const std::string &pId)
0612 {
0613     ErrorHandler::clearErrors();
0614     const std::string productId = getProductId(pId);
0615 
0616     KMime::Message::Ptr msg;
0617     Kolab::XMLObject xmlObject;
0618     std::vector<std::string> attachmentCids;
0619     if (version == KolabV3) {
0620         const std::string xml = xmlObject.writeTodo(convertAttachmentsToReferences(todo, attachmentCids), version, productId);
0621         msg = Mime::createMessage(xCalMimeType(), todoKolabType(), xml, true, productId, todo.organizer().email(), todo.organizer().name(), todo.uid());
0622     } else if (version == KolabV2) {
0623         const std::string xml = xmlObject.writeTodo(todo, version, productId);
0624         msg = Mime::createMessage(todoKolabType(), todoKolabType(), xml, false, productId, todo.organizer().email(), todo.organizer().name(), todo.uid());
0625     }
0626     addAttachments(msg, todo.attachments(), attachmentCids);
0627     msg->assemble();
0628     return msg->encodedContent().data();
0629 }
0630 
0631 Todo MIMEObject::readTodo(const std::string &s)
0632 {
0633     return d->parseMimeMessage(s).value<Kolab::Todo>();
0634 }
0635 
0636 std::string MIMEObject::writeJournal(const Journal &journal, Version version, const std::string &pId)
0637 {
0638     ErrorHandler::clearErrors();
0639     const std::string productId = getProductId(pId);
0640 
0641     KMime::Message::Ptr msg;
0642     Kolab::XMLObject xmlObject;
0643     std::vector<std::string> attachmentCids;
0644     if (version == KolabV3) {
0645         const std::string xml = xmlObject.writeJournal(convertAttachmentsToReferences(journal, attachmentCids), version, productId);
0646         msg = Mime::createMessage(xCalMimeType(), journalKolabType(), xml, true, productId, std::string(), std::string(), journal.uid());
0647     } else if (version == KolabV2) {
0648         const std::string xml = xmlObject.writeJournal(journal, version, productId);
0649         msg = Mime::createMessage(journalKolabType(), journalKolabType(), xml, false, productId, std::string(), std::string(), journal.uid());
0650     }
0651     addAttachments(msg, journal.attachments(), attachmentCids);
0652     msg->assemble();
0653     return msg->encodedContent().data();
0654 }
0655 
0656 Journal MIMEObject::readJournal(const std::string &s)
0657 {
0658     return d->parseMimeMessage(s).value<Kolab::Journal>();
0659 }
0660 
0661 std::string MIMEObject::writeNote(const Note &note, Version version, const std::string &pId)
0662 {
0663     ErrorHandler::clearErrors();
0664     const std::string productId = getProductId(pId);
0665 
0666     KMime::Message::Ptr msg;
0667     Kolab::XMLObject xmlObject;
0668     std::vector<std::string> attachmentCids;
0669     if (version == KolabV3) {
0670         const std::string xml = xmlObject.writeNote(convertAttachmentsToReferences(note, attachmentCids), version, productId);
0671         msg = Mime::createMessage(kolabMimeType(), noteKolabType(), xml, true, productId, std::string(), std::string(), note.uid());
0672     } else if (version == KolabV2) {
0673         const std::string xml = xmlObject.writeNote(note, version, productId);
0674         msg = Mime::createMessage(noteKolabType(), noteKolabType(), xml, false, productId, std::string(), std::string(), note.uid());
0675     }
0676     addAttachments(msg, note.attachments(), attachmentCids);
0677     msg->assemble();
0678     return msg->encodedContent().data();
0679 }
0680 
0681 Note MIMEObject::readNote(const std::string &s)
0682 {
0683     return d->parseMimeMessage(s).value<Kolab::Note>();
0684 }
0685 
0686 std::string MIMEObject::writeContact(const Contact &contact, Version version, const std::string &pId)
0687 {
0688     ErrorHandler::clearErrors();
0689     const std::string productId = getProductId(pId);
0690 
0691     KMime::Message::Ptr msg;
0692     Kolab::XMLObject xmlObject;
0693     const std::string xml = xmlObject.writeContact(contact, version, productId);
0694 
0695     Email preferredEmail = !contact.emailAddresses().empty() ? contact.emailAddresses().at(contact.emailAddressPreferredIndex()) : Email();
0696     QPair<std::string, std::string> pair = Conversion::fromMailto(preferredEmail.address());
0697     std::string name = pair.second;
0698     std::string email = pair.first;
0699     if (name.empty()) {
0700         name = contact.name();
0701     }
0702 
0703     if (version == KolabV3) {
0704         msg = Mime::createMessage(xCardMimeType(), contactKolabType(), xml, true, productId, email, name, contact.uid());
0705     } else if (version == KolabV2) {
0706         msg = Mime::createMessage(contactKolabType(), contactKolabType(), xml, false, productId, email, name, contact.uid());
0707     }
0708     msg->assemble();
0709     return msg->encodedContent().data();
0710 }
0711 
0712 Contact MIMEObject::readContact(const std::string &s)
0713 {
0714     return d->parseMimeMessage(s).value<Kolab::Contact>();
0715 }
0716 
0717 std::string MIMEObject::writeDistlist(const DistList &distlist, Version version, const std::string &pId)
0718 {
0719     ErrorHandler::clearErrors();
0720     const std::string productId = getProductId(pId);
0721 
0722     KMime::Message::Ptr msg;
0723     Kolab::XMLObject xmlObject;
0724     const std::string xml = xmlObject.writeDistlist(distlist, version, productId);
0725     if (version == KolabV3) {
0726         msg = Mime::createMessage(xCardMimeType(), distlistKolabType(), xml, true, productId, std::string(), std::string(), distlist.uid());
0727     } else if (version == KolabV2) {
0728         msg = Mime::createMessage(distlistKolabType(), distlistKolabType(), xml, false, productId, std::string(), std::string(), distlist.uid());
0729     }
0730     msg->assemble();
0731     return msg->encodedContent().data();
0732 }
0733 
0734 DistList MIMEObject::readDistlist(const std::string &s)
0735 {
0736     return d->parseMimeMessage(s).value<Kolab::DistList>();
0737 }
0738 
0739 std::string MIMEObject::writeConfiguration(const Configuration &configuration, Version version, const std::string &pId)
0740 {
0741     ErrorHandler::clearErrors();
0742     const std::string productId = getProductId(pId);
0743 
0744     KMime::Message::Ptr msg;
0745     Kolab::XMLObject xmlObject;
0746     const std::string xml = xmlObject.writeConfiguration(configuration, version, productId);
0747     std::string kolabType;
0748     switch (configuration.type()) {
0749     case Kolab::Configuration::TypeDictionary:
0750         kolabType = dictKolabType();
0751         break;
0752     case Kolab::Configuration::TypeRelation:
0753         kolabType = relationKolabType();
0754         break;
0755     case Kolab::Configuration::TypeSnippet:
0756         kolabType = configurationKolabType();
0757         break;
0758     case Kolab::Configuration::TypeFileDriver:
0759         kolabType = configurationKolabType();
0760         break;
0761     case Kolab::Configuration::TypeCategoryColor:
0762         kolabType = configurationKolabType();
0763         break;
0764     default:
0765         break;
0766     }
0767     if (version == KolabV3) {
0768         msg = Mime::createMessage(kolabMimeType(), kolabType, xml, true, productId, std::string(), std::string(), configuration.uid());
0769     } else if (version == KolabV2) {
0770         qCCritical(PIMKOLAB_LOG) << "Not available in KolabV2";
0771     }
0772     msg->assemble();
0773     return msg->encodedContent().data();
0774 }
0775 
0776 Configuration MIMEObject::readConfiguration(const std::string &s)
0777 {
0778     return d->parseMimeMessage(s).value<Kolab::Configuration>();
0779 }
0780 
0781 std::string MIMEObject::writeFreebusy(const Freebusy &freebusy, Version version, const std::string &pId)
0782 {
0783     ErrorHandler::clearErrors();
0784     const std::string productId = getProductId(pId);
0785 
0786     KMime::Message::Ptr msg;
0787     Kolab::XMLObject xmlObject;
0788     const std::string xml = xmlObject.writeFreebusy(freebusy, version, productId);
0789     if (version == KolabV3) {
0790         msg = Mime::createMessage(xCalMimeType(), freebusyKolabType(), xml, true, productId, std::string(), std::string(), freebusy.uid());
0791     } else if (version == KolabV2) {
0792         qCCritical(PIMKOLAB_LOG) << "Not available in KolabV2";
0793     }
0794     msg->assemble();
0795     return msg->encodedContent().data();
0796 }
0797 
0798 Freebusy MIMEObject::readFreebusy(const std::string &s)
0799 {
0800     return d->parseMimeMessage(s).value<Kolab::Freebusy>();
0801 }
0802 }