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

0001 /*
0002  * SPDX-FileCopyrightText: 2012 Christian Mollekopf <mollekopf@kolabsys.com>
0003  *
0004  * SPDX-License-Identifier: LGPL-3.0-or-later
0005  */
0006 
0007 #include "mimeutils.h"
0008 #include "kolabformat/kolabdefinitions.h"
0009 #include <QDateTime>
0010 #include <qdom.h>
0011 
0012 #include "libkolab-version.h"
0013 #include "pimkolab_debug.h"
0014 #include <kolabformat.h>
0015 namespace Kolab
0016 {
0017 namespace Mime
0018 {
0019 KMime::Content *findContentByType(const KMime::Message::Ptr &data, const QByteArray &type)
0020 {
0021     if (type.isEmpty()) {
0022         qCCritical(PIMKOLAB_LOG) << "Empty type";
0023         return nullptr;
0024     }
0025     Q_ASSERT(!data->contents().isEmpty());
0026     const auto contents = data->contents();
0027     for (KMime::Content *c : contents) {
0028         //         qCDebug(PIMKOLAB_LOG) << c->contentType()->mimeType() << type;
0029         if (c->contentType()->mimeType() == type) {
0030             return c;
0031         }
0032     }
0033     return nullptr;
0034 }
0035 
0036 KMime::Content *findContentByName(const KMime::Message::Ptr &data, const QString &name, QByteArray &type)
0037 {
0038     Q_ASSERT(!data->contents().isEmpty());
0039     const auto contents = data->contents();
0040     for (KMime::Content *c : contents) {
0041         //         qCDebug(PIMKOLAB_LOG) << "searching: " << c->contentType()->name().toUtf8();
0042         if (c->contentType()->name() == name) {
0043             type = c->contentType()->mimeType();
0044             return c;
0045         }
0046     }
0047     return nullptr;
0048 }
0049 
0050 KMime::Content *findContentById(const KMime::Message::Ptr &data, const QByteArray &id, QByteArray &type, QString &name)
0051 {
0052     if (id.isEmpty()) {
0053         qCCritical(PIMKOLAB_LOG) << "looking for empty cid";
0054         return nullptr;
0055     }
0056     Q_ASSERT(!data->contents().isEmpty());
0057     const auto contents = data->contents();
0058     for (KMime::Content *c : contents) {
0059         //         qCDebug(PIMKOLAB_LOG) << "searching: " << c->contentID()->identifier();
0060         if (c->contentID()->identifier() == id) {
0061             type = c->contentType()->mimeType();
0062             name = c->contentType()->name();
0063             return c;
0064         }
0065     }
0066     return nullptr;
0067 }
0068 
0069 QList<QByteArray> getContentMimeTypeList(const KMime::Message::Ptr &data)
0070 {
0071     QList<QByteArray> typeList;
0072     Q_ASSERT(!data->contents().isEmpty());
0073     typeList.reserve(data->contents().count());
0074     const auto contents = data->contents();
0075     for (KMime::Content *c : contents) {
0076         typeList.append(c->contentType()->mimeType());
0077     }
0078     return typeList;
0079 }
0080 
0081 QString fromCid(const QString &cid)
0082 {
0083     if (cid.left(4) != QLatin1StringView("cid:")) { // Don't set if not a cid, happens when serializing format v2
0084         return {};
0085     }
0086     return cid.right(cid.size() - 4);
0087 }
0088 
0089 KMime::Message::Ptr createMessage(const QByteArray &mimetype,
0090                                   const QByteArray &xKolabType,
0091                                   const QByteArray &xml,
0092                                   bool v3,
0093                                   const QByteArray &productId,
0094                                   const QByteArray &fromEmail,
0095                                   const QString &fromName,
0096                                   const QString &subject)
0097 {
0098     KMime::Message::Ptr message = createMessage(xKolabType, v3, productId);
0099     message->subject()->fromUnicodeString(subject, "utf-8");
0100     if (!fromEmail.isEmpty()) {
0101         KMime::Types::Mailbox mb;
0102         mb.setName(fromName);
0103         mb.setAddress(fromEmail);
0104         message->from()->addAddress(mb);
0105     }
0106     message->appendContent(createMainPart(mimetype, xml));
0107     return message;
0108 }
0109 
0110 KMime::Message::Ptr createMessage(const std::string &mimetype,
0111                                   const std::string &xKolabType,
0112                                   const std::string &xml,
0113                                   bool v3,
0114                                   const std::string &productId,
0115                                   const std::string &fromEmail,
0116                                   const std::string &fromName,
0117                                   const std::string &subject)
0118 {
0119     return createMessage(QByteArray(mimetype.c_str()),
0120                          QByteArray(xKolabType.c_str()),
0121                          QByteArray(xml.c_str()),
0122                          v3,
0123                          QByteArray(productId.data()),
0124                          QByteArray(fromEmail.c_str()),
0125                          QString::fromStdString(fromName),
0126                          QString::fromStdString(subject));
0127 }
0128 
0129 KMime::Message::Ptr
0130 createMessage(const QString &subject, const QString &mimetype, const QString &xKolabType, const QByteArray &xml, bool v3, const QString &prodid)
0131 {
0132     KMime::Message::Ptr message = createMessage(xKolabType.toLatin1(), v3, prodid.toLatin1());
0133     if (!subject.isEmpty()) {
0134         message->subject()->fromUnicodeString(subject, "utf-8");
0135     }
0136 
0137     KMime::Content *content = createMainPart(mimetype.toLatin1(), xml);
0138     message->appendContent(content);
0139 
0140     message->assemble();
0141     return message;
0142 }
0143 
0144 KMime::Content *createExplanationPart(bool v3)
0145 {
0146     Q_UNUSED(v3)
0147     auto content = new KMime::Content();
0148     content->contentType()->setMimeType("text/plain");
0149     content->contentType()->setCharset("us-ascii");
0150     content->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit);
0151     content->setBody(
0152         "This is a Kolab Groupware object.\n"
0153         "To view this object you will need an email client that can understand the Kolab Groupware format.\n"
0154         "For a list of such email clients please visit\n"
0155         "http://www.kolab.org/get-kolab\n");
0156     return content;
0157 }
0158 
0159 KMime::Message::Ptr createMessage(const QByteArray &xKolabType, bool v3, const QByteArray &prodid)
0160 {
0161     KMime::Message::Ptr message(new KMime::Message);
0162     message->date()->setDateTime(QDateTime::currentDateTimeUtc());
0163     auto h = new KMime::Headers::Generic(X_KOLAB_TYPE_HEADER);
0164     h->fromUnicodeString(QString::fromUtf8(xKolabType), "utf-8");
0165     message->appendHeader(h);
0166     if (v3) {
0167         auto hv3 = new KMime::Headers::Generic(X_KOLAB_MIME_VERSION_HEADER);
0168         hv3->fromUnicodeString(KOLAB_VERSION_V3, "utf-8");
0169         message->appendHeader(hv3);
0170     }
0171     message->userAgent()->from7BitString(prodid);
0172     message->contentType()->setMimeType("multipart/mixed");
0173     message->contentType()->setBoundary(KMime::multiPartBoundary());
0174     message->appendContent(createExplanationPart(v3));
0175     return message;
0176 }
0177 
0178 KMime::Content *createMainPart(const QByteArray &mimeType, const QByteArray &decodedContent)
0179 {
0180     auto content = new KMime::Content();
0181     content->contentType()->setMimeType(mimeType);
0182     content->contentType()->setName(KOLAB_OBJECT_FILENAME, "us-ascii");
0183     content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
0184     content->contentDisposition()->setDisposition(KMime::Headers::CDattachment);
0185     content->contentDisposition()->setFilename(KOLAB_OBJECT_FILENAME);
0186     content->setBody(decodedContent);
0187     return content;
0188 }
0189 
0190 KMime::Content *createAttachmentPart(const QByteArray &cid, const QByteArray &mimeType, const QString &fileName, const QByteArray &base64EncodedContent)
0191 {
0192     auto content = new KMime::Content();
0193     if (!cid.isEmpty()) {
0194         content->contentID()->setIdentifier(cid);
0195     }
0196     content->contentType()->setMimeType(mimeType);
0197     content->contentType()->setName(fileName, "utf-8");
0198     content->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64);
0199     content->contentDisposition()->setDisposition(KMime::Headers::CDattachment);
0200     content->contentDisposition()->setFilename(fileName);
0201     content->setBody(base64EncodedContent);
0202     return content;
0203 }
0204 
0205 Kolab::Attachment getAttachment(const std::string &id, const KMime::Message::Ptr &mimeData)
0206 {
0207     if (!QString::fromStdString(id).contains(QLatin1StringView("cid:"))) {
0208         qCCritical(PIMKOLAB_LOG) << "not a cid reference";
0209         return {};
0210     }
0211     QByteArray type;
0212     QString name;
0213     KMime::Content *content = findContentById(mimeData, fromCid(QString::fromStdString(id)).toLatin1(), type, name);
0214     if (!content) { // guard against malformed events with non-existent attachments
0215         qCCritical(PIMKOLAB_LOG) << "could not find attachment: " << name << type;
0216         return {};
0217     }
0218     // Debug() << id << content->decodedContent().toBase64().toStdString();
0219     Kolab::Attachment attachment;
0220     attachment.setData(content->decodedContent().toStdString(), type.toStdString());
0221     attachment.setLabel(name.toStdString());
0222     return attachment;
0223 }
0224 
0225 Kolab::Attachment getAttachmentByName(const QString &name, const KMime::Message::Ptr &mimeData)
0226 {
0227     QByteArray type;
0228     KMime::Content *content = findContentByName(mimeData, name, type);
0229     if (!content) { // guard against malformed events with non-existent attachments
0230         qCWarning(PIMKOLAB_LOG) << "could not find attachment: " << name.toUtf8() << type;
0231         return {};
0232     }
0233     Kolab::Attachment attachment;
0234     attachment.setData(content->decodedContent().toStdString(), type.toStdString());
0235     attachment.setLabel(name.toStdString());
0236     return attachment;
0237 }
0238 } // Namespace
0239 } // Namespace