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