Index: framework/domain/mimetreeparser/interface.h =================================================================== --- /dev/null +++ framework/domain/mimetreeparser/interface.h @@ -0,0 +1,327 @@ +/* + Copyright (c) 2016 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#pragma once + +#include +#include + +class Part; +class EncryptionPart; +class SignaturePart; + +class MimePart; +class MimePartPrivate; + +class ContentPart; +class ContentPartPrivate; + +class EncryptionErrorPart; +class EncryptionErrorPartPrivate; + +class AttachmentPart; +class AttachmentPartPrivate; + +class EncapsulatedPart; +class EncapsulatedPart; + +class CertPart; +class CertPart; + +class Key; +class Signature; +class Encryption; + +class Parser; +class ParserPrivate; + +class Parser +{ +public: + Parser(const QByteArray &mimeMessage); + + std::shared_ptr getPart(QUrl url); + + QVector> collect() const; + QVector> collect() const; + QVector> collect(Part start, std::function select, std::function &)> filter) const; + +private: + std::unique_ptr d; +}; + +class Part +{ +public: + virtual QByteArray type() const = 0; + + bool hasSubParts() const; + QList subParts() const; + Part partent() const; +}; + +/* + * A MessagePart that is based on a KMime::Content + */ +class MimePart : public Part +{ +public: + /** + * Various possible values for the "Content-Disposition" header. + */ + enum Disposition { + Invalid, ///< Default, invalid value + Inline, ///< inline + Attachment, ///< attachment + Parallel ///< parallel (invalid, do not use) + }; + + // interessting header parts of a KMime::Content + QByteArray content() const; + QMimeType mimetype() const; + Disposition dispossition() const + QUrl label() const; + QByteArray cid() const; + QByteArray charset() const; + + // we wanna overrwrite the charset of the content, because some clients set the charset wrong + void setCharset(QByteArray charset); + + // Unique identifier to ecactly this KMime::Content + QByteArray link() const; + + + QByteArray type() const Q_DECL_OVERRIDE; +private: + std::unique_ptr d; +}; + +/* + * The main ContentPart + * is MimePart a good parent class? + * do we wanna need parts of the header of the connected KMime::Contents + * usecases: + * - + * for htmlonly it is representating only one MimePart (ok) + * for plaintext only also only one MimePart (ok) + * for alternative, we are represating three messageparts + * - "headers" do we return?, we can use setType to make it possible to select and than return these headers + */ +class ContentPart : public MimePart +{ +public: + enum Types { + PlainText, + Html + }; + Q_DECLARE_FLAGS(Types, Type) + + QByteArray content(Content::Type ct) const; + + // convert content with charset + QString content(Content::Type ct) const; + + Content::Types availableContent() const; + QVector signature() const; + QVector encryption() const; + + QByteArray type() const Q_DECL_OVERRIDE; + +private: + std::unique_ptr d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(ContentPart::Type) + +class AttachmentPart : public MimePart +{ +public: + QByteArray type() const Q_DECL_OVERRIDE; + +private: + std::unique_ptr d; +}; + +/* + * Faild to decrypt part + * thigs liks this can happen: + * decryption in progress + * have not tried at all to decrypt + * wrong passphrase + * no private key + * cryptobackend is not configured correctly (no gpg available) + * -> S/Mime and PGP have different meaning in their errors + * + * Open Questions: + * - How to make the string translateable for multiple clients, so that multiple clients can show same error messages, + * that helps users to understand what is going on ? + * - Does openpgp have translations already? + */ +class EncryptionErrorPart : public Part +{ +public: + Error errorId() const; + + CryptoBackend cryptoBackend(); + + QByteArray type() const Q_DECL_OVERRIDE; + +private: + std::unique_ptr d; +}; + +/* + * we want to request complete headers like: + * from/to... + */ + +class EncapsulatedPart :: public AttachmentPart +{ +public: + QByteArray type() const Q_DECL_OVERRIDE; + + QByteArray header(); +private: + std::unique_ptr d; +}; + +/* + * importing a cert GpgMe::ImportResult + * checking a cert (if it is a valid cert) + */ + +class CertPart :: public AttachmentPart +{ +public: + QByteArray type() const Q_DECL_OVERRIDE; + + bool checkCert() const; + Status importCert() const; + +private: + std::unique_ptr d; +}; + +/* +the ggme error class + +// class GPGMEPP_EXPORT ErrorImportResult +{ +public: + Error() : mErr(0), mMessage() {} + explicit Error(unsigned int e) : mErr(e), mMessage() {} + + const char *source() const; + const char *asString() const; + + int code() const; + int sourceID() const; + + bool isCanceled() const; + + unsigned int encodedError() const + { + return mErr; + } + int toErrno() const; + + static bool hasSystemError(); + static Error fromSystemError(unsigned int src = GPGMEPP_ERR_SOURCE_DEFAULT); + static void setSystemError(gpg_err_code_t err); + static void setErrno(int err); + static Error fromErrno(int err, unsigned int src = GPGMEPP_ERR_SOURCE_DEFAULT); + static Error fromCode(unsigned int err, unsigned int src = GPGMEPP_ERR_SOURCE_DEFAULT); + + GPGMEPP_MAKE_SAFE_BOOL_OPERATOR(mErr &&!isCanceled()) +private: + unsigned int mErr; + mutable std::string mMessage; +}; +*/ + +/* + * a used smime/PGP key + * in the end we also need things like: + bool isRevokation() const; + bool isInvalid() const; + bool isExpired() const; + + -> so we end up wrapping GpgME::Key + */ +class Key +{ + QString keyid() const; + QString name() const; + QString email() const; + QString comment() const; + QVector emails() const; + KeyTrust keyTrust() const; + CryptoBackend cryptoBackend() const; + + std::vector subkeys(); + Key parentkey() const; +}; + +class Signature +{ + Key key() const; + QDateTime creationDateTime() const; + QDateTime expirationTime() const; + bool neverExpires() const; + + bool inProgress(); //if the verfication is inProgress + + enum Validity { + Unknown, Undefined, Never, Marginal, Full, Ultimate + }; + Validity validity() const; + + // to determine if we need this in our usecase (email) + // GpgME::VerificationResult + enum Summary { + None = 0x000, + Valid = 0x001, + Green = 0x002, + Red = 0x004, + KeyRevoked = 0x008, + KeyExpired = 0x010, + SigExpired = 0x020, + KeyMissing = 0x040, + CrlMissing = 0x080, + CrlTooOld = 0x100, + BadPolicy = 0x200, + SysError = 0x400 + }; + Summary summary() const; + + const char *policyURL() const; + GpgME::Notation notation(unsigned int index) const; + std::vector notations() const; + +}; + +/* + * Normally the Keys for encryption are subkeys + * for clients the parentkeys are "more interessting", because they store the name, email etc. + * but a client may also wants show to what subkey the mail is really encrypted, an if this subkey isRevoked or something else + */ +class Encryption +{ + std::vector recipients() const; +}; \ No newline at end of file Index: framework/domain/mimetreeparser/test.cpp =================================================================== --- /dev/null +++ framework/domain/mimetreeparser/test.cpp @@ -0,0 +1,146 @@ +Usecases: + +# plaintext msg + attachment +* ContentPart => cp1 +* AttachmentPart => ap1 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1) == collect(select=NoEncapsulatedMessages) + +(PlainText) == cp1.availableContent() + +# html msg + related attachment + normal attachment +* ContentPart => cp1 +* AttachmentPart(mimetype="*/related", cid="12345678") => ap1 +* AttachmentPart => ap2 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1, ap2) == collect(select=NoEncapsulatedMessages) +(ap2) == collect(select=NoEncapsulatedMessages, filter=filterelated) + +ap1 == getPart("cid:12345678") + +(Html) == cp1.availableContent() + +# alternative msg + attachment +* ContentPart(html="HTML", plaintext="Text") => cp1 +* AttachmentPart => ap1 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1) == collect(select=NoEncapsulatedMessages) + +(Html, PlainText) == cp1.availableContent() +"HTML" == cp1.content(Html) +"text" == cp1.content(Plaintext) + +# alternative msg with GPGInline +* ContentPart(html="HTML", plaintext="Text cypted") => cp1 + * TextPart(text="Text") + * TextPart(text=foo, encryption=(enc1) + +(Html, PlainText) == cp1.availableContent() + +TODO: but how to get plaintext/html content? + +# encrypted msg (not encrypted/error) with unencrypted attachment +* EncryptionErrorPart => cp1 +* AttachmentPart => ap1 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1) == collect(select=NoEncapsulatedMessages) + +#encrypted msg (decrypted with attachment) + unencrypted attachment +* encrytion=(rec1,rec2) => enc1 + * ContentPart(encrytion = (enc1,)) => cp1 + * AttachmentPart(encryption = (enc1,)) => ap1 +* AttachmentPart => ap2 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1, ap2) == collect(select=NoEncapsulatedMessages) + +#INLINE GPG encrypted msg + attachment +* ContentPart => cp1 + * TextPart + * TextPart(encrytion = (enc1(rec1,rec2),)) + * TextPart(signed = (sig1,)) + * TextPart +* AttachmentPart => ap1 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1) == collect(select=NoEncapsulatedMessages) + +#forwared encrypted msg + attachments +* ContentPart => cp1 +* EncapsulatedPart => ep1 + * Encrytion=(rec1,rec2) => enc1 + * Signature => sig1 + * ContentPart(encrytion = (enc1,), signature = (sig1,)) => cp2 + * TextPart(encrytion = (enc1,), signature = (sig1,)) + * TextPart(encrytion = (enc1, enc2(rec3,rec4),), signature = (sig1,)) + * AttachmentPart(encrytion = (enc1,), signature = (sig1,)) => ap1 +* AttachmentPart => ap2 + +(cp1) = collect(select=NoEncapsulatedMessages) +(ap2) = collect(select=NoEncapsulatedMessages) + +(cp2) = collect(ep1, select=NoEncapsulatedMessages) +(ap1) = collect(ep1, select=NoEncapsulatedMessages) + +(cp1, cp2) == collect() +(ap1, ap2) == collect() + + +# plaintext msg + attachment + cert +* ContentPart => cp1 +* AttachmentPart => ap1 +* CertPart => cep1 + +(cp1) == collect(select=NoEncapsulatedMessages) +(ap1, cep1) == collect(select=NoEncapsulatedMessages) +(ap1) == collect(select=NoEncapsulatedMessages, filter=filterSubAttachmentParts) + +(cep1) == collect(select=NoEncapsulatedMessages) + + +collect function: + +bool noEncapsulatedMessages(Part part) +{ + if (is(part)) { + return false; + } + return true; +} + +bool filterRelated(T part) +{ + if (part.mimetype == related && !part.cid.isEmpty()) { + return false; //filter out related parts + } + return true; +} + +bool filterSubAttachmentParts(AttachmentPart part) +{ + if (isSubPart(part)) { + return false; // filter out CertPart f.ex. + } + return true; +} + +List collect(Part start, std::function select, std::function &)> filter) { + List col; + if (!select(start)) { + return col; + } + + if(isOrSubTypeIs(start) && filter(start.staticCast)){ + col.append(p); + } + foreach(childs as child) { + if (select(child)) { + col.expand(collect(child,select,filter); + } + } + return col; +} \ No newline at end of file