From 1974c19eadd497e355ac985a00d0571f3e6c7712 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sandro=20Knau=C3=9F?= Date: Tue, 11 Oct 2016 16:18:50 +0200 Subject: create model for new mailviewer --- framework/domain/CMakeLists.txt | 2 + framework/domain/messageparser.cpp | 123 +---------------- framework/domain/messageparser.h | 57 ++++++++ framework/domain/messageparser_new.cpp | 148 +++++++++++++++++++++ framework/domain/messageparser_old.cpp | 140 +++++++++++++++++++ framework/domain/mimetreeparser/CMakeLists.txt | 5 +- framework/domain/mimetreeparser/interface.cpp | 14 +- framework/domain/mimetreeparser/interface.h | 2 +- .../data/openpgp-inline-encrypted+nonenc.mbox | 31 +++++ .../domain/mimetreeparser/tests/interfacetest.cpp | 27 +++- 10 files changed, 426 insertions(+), 123 deletions(-) create mode 100644 framework/domain/messageparser_new.cpp create mode 100644 framework/domain/messageparser_old.cpp create mode 100644 framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox (limited to 'framework') diff --git a/framework/domain/CMakeLists.txt b/framework/domain/CMakeLists.txt index 07f01367..c41da377 100644 --- a/framework/domain/CMakeLists.txt +++ b/framework/domain/CMakeLists.txt @@ -7,6 +7,8 @@ set(mailplugin_SRCS stringhtmlwriter.cpp composercontroller.cpp messageparser.cpp + messageparser_new.cpp + messageparser_old.cpp mailtemplates.cpp retriever.cpp accountfactory.cpp diff --git a/framework/domain/messageparser.cpp b/framework/domain/messageparser.cpp index febd1363..ef9fb0d2 100644 --- a/framework/domain/messageparser.cpp +++ b/framework/domain/messageparser.cpp @@ -33,125 +33,6 @@ #include #include -PartModel::PartModel(QSharedPointer partTree, std::shared_ptr parser) - : mPartTree(partTree) - , mParser(parser) -{ -} - -QHash PartModel::roleNames() const -{ - QHash roles; - roles[Text] = "text"; - roles[IsHtml] = "isHtml"; - roles[IsHidden] = "isHidden"; - roles[IsEncrypted] = "isEncrypted"; - roles[IsAttachment] = "isAttachment"; - roles[HasContent] = "hasContent"; - roles[Type] = "type"; - roles[IsHidden] = "isHidden"; - return roles; -} - -QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) const -{ - // qDebug() << "index " << parent << row << column << mPartTree->subParts().size(); - if (!parent.isValid()) { - if (row < mPartTree->subParts().size()) { - auto part = mPartTree->subParts().at(row); - return createIndex(row, column, part.data()); - } - } else { - auto part = static_cast(parent.internalPointer()); - auto subPart = part->subParts().at(row); - return createIndex(row, column, subPart.data()); - } - return QModelIndex(); -} - -QVariant PartModel::data(const QModelIndex &index, int role) const -{ - // qDebug() << "Getting data for index"; - if (index.isValid()) { - auto part = static_cast(index.internalPointer()); - switch (role) { - case Text: { - // qDebug() << "Getting text: " << part->property("text").toString(); - // FIXME: we should have a list per part, and not one for all parts. - auto text = part->property("htmlContent").toString(); - auto rx = QRegExp("src=(\"|')cid:([^\1]*)\1"); - int pos = 0; - while ((pos = rx.indexIn(text, pos)) != -1) { - auto repl = mParser->getPart(rx.cap(2).toUtf8()); - if (repl.isValid()) { - text.replace(rx.cap(0), QString("src=\"%1\"").arg(repl.toString())); - } - pos += rx.matchedLength(); - } - return text; - } - case IsAttachment: - return part->property("attachment").toBool(); - case IsEncrypted: - return part->property("isEncrypted").toBool(); - case IsHtml: - return part->property("isHtml").toBool(); - case HasContent: - return !part->property("htmlContent").toString().isEmpty(); - case Type: - return part->metaObject()->className(); - case IsHidden: - return false; - //return part->property("isHidden").toBool(); - - } - } - return QVariant(); -} - -QModelIndex PartModel::parent(const QModelIndex &index) const -{ - // qDebug() << "parent " << index; - if (index.isValid()) { - auto part = static_cast(index.internalPointer()); - auto parentPart = static_cast(part->parentPart()); - auto row = 0;//get the parents parent to find the index - if (!parentPart) { - parentPart = mPartTree.data(); - } - int i = 0; - for (const auto &p : parentPart->subParts()) { - if (p.data() == part) { - row = i; - break; - } - i++; - } - return createIndex(row, index.column(), parentPart); - } - return QModelIndex(); -} - -int PartModel::rowCount(const QModelIndex &parent) const -{ - // qDebug() << "Row count " << parent; - if (!parent.isValid()) { - // qDebug() << "Row count " << mPartTree->subParts().size(); - return mPartTree->subParts().size(); - } else { - auto part = static_cast(parent.internalPointer()); - if (part) { - return part->subParts().size(); - } - } - return 0; -} - -int PartModel::columnCount(const QModelIndex &parent) const -{ - // qDebug() << "Column count " << parent; - return 1; -} class MessagePartPrivate { @@ -217,3 +98,7 @@ QAbstractItemModel *MessageParser::partTree() const return new PartModel(d->mPartTree, d->mParser); } +QAbstractItemModel *MessageParser::newTree() const +{ + return new NewModel(d->mParser); +} diff --git a/framework/domain/messageparser.h b/framework/domain/messageparser.h index 9469f2b5..b3d7537d 100644 --- a/framework/domain/messageparser.h +++ b/framework/domain/messageparser.h @@ -32,6 +32,10 @@ class QAbstractItemModel; class Parser; +class Part; +typedef std::shared_ptr PartPtr; +class Content; +typedef std::shared_ptr ContentPtr; class MessagePartPrivate; class MessageParser : public QObject @@ -40,6 +44,7 @@ class MessageParser : public QObject Q_PROPERTY (QVariant message READ message WRITE setMessage) Q_PROPERTY (QString html READ html NOTIFY htmlChanged) Q_PROPERTY (QAbstractItemModel* partTree READ partTree NOTIFY htmlChanged) + Q_PROPERTY (QAbstractItemModel* newTree READ newTree NOTIFY htmlChanged) public: explicit MessageParser(QObject *parent = Q_NULLPTR); @@ -50,6 +55,7 @@ public: QVariant message() const; void setMessage(const QVariant &to); QAbstractItemModel *partTree() const; + QAbstractItemModel *newTree() const; signals: void htmlChanged(); @@ -87,3 +93,54 @@ private: std::shared_ptr mParser; }; + +class NewContentModel : public QAbstractItemModel { + Q_OBJECT +public: + NewContentModel (const PartPtr &part); + +public: + enum Roles { + TypeRole = Qt::UserRole + 1, + ContentRole, + IsEmbededRole, + SecurityLevelRole + }; + + QHash roleNames() const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + +private: + const PartPtr &mPart; +}; + +class NewModel : public QAbstractItemModel { + Q_OBJECT +public: + NewModel(std::shared_ptr parser); + +public: + enum Roles { + TypeRole = Qt::UserRole + 1, + ContentsRole, + IsEmbededRole, + SecurityLevelRole + }; + + QHash roleNames() const Q_DECL_OVERRIDE; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; + QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; + int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; + +private: + std::shared_ptr mParser; + QVector mParts; + QMap> mContentMap; +}; + diff --git a/framework/domain/messageparser_new.cpp b/framework/domain/messageparser_new.cpp new file mode 100644 index 00000000..d1b956f5 --- /dev/null +++ b/framework/domain/messageparser_new.cpp @@ -0,0 +1,148 @@ + +/* + 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. +*/ + +#include "messageparser.h" +#include "mimetreeparser/interface.h" + +#include + +NewModel::NewModel(std::shared_ptr parser) + : mParser(parser) +{ + mParts = mParser->collectContentParts(); + foreach(const auto &part, mParts) { + mContentMap.insert(part.get(), std::shared_ptr(new NewContentModel(part))); + } +} + +QHash NewModel::roleNames() const +{ + QHash roles; + roles[TypeRole] = "type"; + roles[ContentsRole] = "contents"; + roles[IsEmbededRole] = "embeded"; + roles[SecurityLevelRole] = "securityLevel"; + return roles; +} + +QModelIndex NewModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) { + if (row < mParts.size()) { + auto part = mParts.at(row); + return createIndex(row, column, part.get()); + } + } + return QModelIndex(); +} + +QVariant NewModel::data(const QModelIndex &index, int role) const +{ + if (!index.parent().isValid()) { + auto part = static_cast(index.internalPointer()); + switch (role) { + case TypeRole: + return QString::fromLatin1(part->type()); + case IsEmbededRole: + return index.parent().isValid(); + case SecurityLevelRole: + return QStringLiteral("GRAY"); + case ContentsRole: + return QVariant::fromValue(mContentMap.value(part).get()); + } + } + return QVariant(); +} + +QModelIndex NewModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +int NewModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mParts.size(); + } + return 0; +} + +int NewModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +NewContentModel::NewContentModel(const Part::Ptr &part) + : mPart(part) +{ +} + +QHash NewContentModel::roleNames() const +{ + QHash roles; + roles[TypeRole] = "type"; + roles[ContentRole] = "content"; + roles[IsEmbededRole] = "embeded"; + roles[SecurityLevelRole] = "securityLevel"; + return roles; +} + +QModelIndex NewContentModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!parent.isValid()) { + if (row < mPart->content().size()) { + auto part = mPart->content().at(row); + return createIndex(row, column, part.get()); + } + } + return QModelIndex(); +} + +QVariant NewContentModel::data(const QModelIndex &index, int role) const +{ + auto content = static_cast(index.internalPointer()); + switch (role) { + case TypeRole: + return QString::fromLatin1(content->type()); + case IsEmbededRole: + return false; + case ContentRole: + return content->encodedContent(); + case SecurityLevelRole: + return content->encryptions().size() > mPart->encryptions().size() ? "red": "black"; //test for gpg inline + } + return QVariant(); +} + +QModelIndex NewContentModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +int NewContentModel::rowCount(const QModelIndex &parent) const +{ + if (!parent.isValid()) { + return mPart->content().size(); + } + return 0; +} + +int NewContentModel::columnCount(const QModelIndex &parent) const +{ + return 1; +} diff --git a/framework/domain/messageparser_old.cpp b/framework/domain/messageparser_old.cpp new file mode 100644 index 00000000..a364c8ab --- /dev/null +++ b/framework/domain/messageparser_old.cpp @@ -0,0 +1,140 @@ +/* + 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. +*/ + +#include "messageparser.h" +#include "mimetreeparser/interface.h" + +PartModel::PartModel(QSharedPointer partTree, std::shared_ptr parser) + : mPartTree(partTree) + , mParser(parser) +{ +} + +QHash PartModel::roleNames() const +{ + QHash roles; + roles[Text] = "text"; + roles[IsHtml] = "isHtml"; + roles[IsHidden] = "isHidden"; + roles[IsEncrypted] = "isEncrypted"; + roles[IsAttachment] = "isAttachment"; + roles[HasContent] = "hasContent"; + roles[Type] = "type"; + roles[IsHidden] = "isHidden"; + return roles; +} + +QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) const +{ + // qDebug() << "index " << parent << row << column << mPartTree->subParts().size(); + if (!parent.isValid()) { + if (row < mPartTree->subParts().size()) { + auto part = mPartTree->subParts().at(row); + return createIndex(row, column, part.data()); + } + } else { + auto part = static_cast(parent.internalPointer()); + auto subPart = part->subParts().at(row); + return createIndex(row, column, subPart.data()); + } + return QModelIndex(); +} + +QVariant PartModel::data(const QModelIndex &index, int role) const +{ + // qDebug() << "Getting data for index"; + if (index.isValid()) { + auto part = static_cast(index.internalPointer()); + switch (role) { + case Text: { + // qDebug() << "Getting text: " << part->property("text").toString(); + // FIXME: we should have a list per part, and not one for all parts. + auto text = part->property("htmlContent").toString(); + auto rx = QRegExp("src=(\"|')cid:([^\1]*)\1"); + int pos = 0; + while ((pos = rx.indexIn(text, pos)) != -1) { + auto repl = mParser->getPart(rx.cap(2).toUtf8()); + if (repl.isValid()) { + text.replace(rx.cap(0), QString("src=\"%1\"").arg(repl.toString())); + } + pos += rx.matchedLength(); + } + return text; + } + case IsAttachment: + return part->property("attachment").toBool(); + case IsEncrypted: + return part->property("isEncrypted").toBool(); + case IsHtml: + return part->property("isHtml").toBool(); + case HasContent: + return !part->property("htmlContent").toString().isEmpty(); + case Type: + return part->metaObject()->className(); + case IsHidden: + return false; + //return part->property("isHidden").toBool(); + + } + } + return QVariant(); +} + +QModelIndex PartModel::parent(const QModelIndex &index) const +{ + // qDebug() << "parent " << index; + if (index.isValid()) { + auto part = static_cast(index.internalPointer()); + auto parentPart = static_cast(part->parentPart()); + auto row = 0;//get the parents parent to find the index + if (!parentPart) { + parentPart = mPartTree.data(); + } + int i = 0; + for (const auto &p : parentPart->subParts()) { + if (p.data() == part) { + row = i; + break; + } + i++; + } + return createIndex(row, index.column(), parentPart); + } + return QModelIndex(); +} + +int PartModel::rowCount(const QModelIndex &parent) const +{ + // qDebug() << "Row count " << parent; + if (!parent.isValid()) { + // qDebug() << "Row count " << mPartTree->subParts().size(); + return mPartTree->subParts().size(); + } else { + auto part = static_cast(parent.internalPointer()); + if (part) { + return part->subParts().size(); + } + } + return 0; +} + +int PartModel::columnCount(const QModelIndex &parent) const +{ + // qDebug() << "Column count " << parent; + return 1; +} + diff --git a/framework/domain/mimetreeparser/CMakeLists.txt b/framework/domain/mimetreeparser/CMakeLists.txt index e1c04893..64da2656 100644 --- a/framework/domain/mimetreeparser/CMakeLists.txt +++ b/framework/domain/mimetreeparser/CMakeLists.txt @@ -9,4 +9,7 @@ add_library(mimetreeparser SHARED ${mimetreeparser_SRCS}) qt5_use_modules(mimetreeparser Core Gui) target_link_libraries(mimetreeparser KF5::Mime KF5::MimeTreeParser) -add_subdirectory(tests) \ No newline at end of file +install(TARGETS mimetreeparser + DESTINATION ${LIB_INSTALL_DIR}) + +add_subdirectory(tests) diff --git a/framework/domain/mimetreeparser/interface.cpp b/framework/domain/mimetreeparser/interface.cpp index c3ecf79c..efa0fd40 100644 --- a/framework/domain/mimetreeparser/interface.cpp +++ b/framework/domain/mimetreeparser/interface.cpp @@ -30,6 +30,7 @@ #include #include +#include #include class MailMimePrivate @@ -356,6 +357,17 @@ QByteArray Content::charset() const return d->mCodec; } +QString Content::encodedContent() const +{ + return encodedContent(charset()); +} + +QString Content::encodedContent(const QByteArray &charset) const +{ + QTextCodec *codec = QTextCodec::codecForName(charset); + return codec->toUnicode(content()); +} + QByteArray Content::type() const { return "Content"; @@ -496,7 +508,7 @@ void SinglePartPrivate::fillFrom(MimeTreeParser::TextMessagePart::Ptr part) mContent.clear(); foreach (const auto &mp, part->subParts()) { auto d_ptr = new ContentPrivate; - d_ptr->mContent = part->text().toLocal8Bit(); + d_ptr->mContent = mp->text().toLocal8Bit(); d_ptr->mParent = q; d_ptr->mCodec = "utf-8"; const auto enc = mp.dynamicCast(); diff --git a/framework/domain/mimetreeparser/interface.h b/framework/domain/mimetreeparser/interface.h index 7eadc311..67246f37 100644 --- a/framework/domain/mimetreeparser/interface.h +++ b/framework/domain/mimetreeparser/interface.h @@ -125,7 +125,7 @@ public: QString encodedContent() const; // overwrite default charset with given charset - QString encodedContent(QByteArray charset) const; + QString encodedContent(const QByteArray &charset) const; QVector signatures() const; QVector encryptions() const; diff --git a/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox b/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox new file mode 100644 index 00000000..b98dc336 --- /dev/null +++ b/framework/domain/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox @@ -0,0 +1,31 @@ +From test@kolab.org Wed, 25 May 2011 23:49:40 +0100 +From: OpenPGP Test +To: test@kolab.org +Subject: inlinepgpencrypted + non enc text +Date: Wed, 25 May 2011 23:49:40 +0100 +Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost> +X-KMail-Transport: GMX +X-KMail-Fcc: 28 +X-KMail-Drafts: 7 +X-KMail-Templates: 9 +User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19) +MIME-Version: 1.0 +Content-Transfer-Encoding: 7Bit +Content-Type: text/plain; charset="us-ascii" + +Not encrypted not signed :( + +-----BEGIN PGP MESSAGE----- +Version: GnuPG v2.0.15 (GNU/Linux) + +hQEMAwzOQ1qnzNo7AQf/a3aNTLpQBfcUr+4AKsZQLj4h6z7e7a5AaCW8AG0wrbxN +kBYB7E5jdZh45DX/99gvoZslthWryUCX2kKZ3LtIllxKVjqNuK5hSt+SAuKkwiMR +Xcbf1KFKENKupgGSO9B2NJRbjoExdJ+fC3mGXnO3dT7xJJAo3oLE8Nivu+Bj1peY +E1wCf+vcTwVHFrA7SV8eMRb9Z9wBXmU8Q8e9ekJ7ZsRX3tMeBs6jvscVvfMf6DYY +N14snZBZuGNKT9a3DPny7IC1S0lHcaam34ogWwMi3FxPGJt/Lg52kARlkF5TDhcP +N6H0EB/iqDRjOOUoEVm8um5XOSR1FpEiAdD0DON3y9JPATnrYq7sgYZz3BVImYY+ +N/jV8fEiN0a34pcOq8NQedMuOsJHNBS5MtbQH/kJLq0MXBpXekGlHo4MKw0trISc +Rw3pW6/BFfhPJLni29g9tw== +=fRFW +-----END PGP MESSAGE----- + diff --git a/framework/domain/mimetreeparser/tests/interfacetest.cpp b/framework/domain/mimetreeparser/tests/interfacetest.cpp index ac77b025..5a3cbb87 100644 --- a/framework/domain/mimetreeparser/tests/interfacetest.cpp +++ b/framework/domain/mimetreeparser/tests/interfacetest.cpp @@ -198,7 +198,32 @@ private slots: auto contentAttachmentList = parser.collectAttachmentParts(); QCOMPARE(contentAttachmentList.size(), 0); } + + void testOpenPPGInlineWithNonEncText() + { + Parser parser(readMailFromFile("openpgp-inline-encrypted+nonenc.mbox")); + printTree(parser.d->mTree,QString()); + auto contentPartList = parser.collectContentParts(); + QCOMPARE(contentPartList.size(), 1); + auto contentPart = contentPartList[0]; + QVERIFY((bool)contentPart); + QCOMPARE(contentPart->availableContents(), QVector() << "plaintext"); + QCOMPARE(contentPart->encryptions().size(), 0); + QCOMPARE(contentPart->signatures().size(), 0); + auto contentList = contentPart->content("plaintext"); + QCOMPARE(contentList.size(), 2); + QCOMPARE(contentList[0]->content(), QStringLiteral("Not encrypted not signed :(\n\n").toLocal8Bit()); + QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit()); + QCOMPARE(contentList[0]->encryptions().size(), 0); + QCOMPARE(contentList[0]->signatures().size(), 0); + QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit()); + QCOMPARE(contentList[1]->charset(), QStringLiteral("utf-8").toLocal8Bit()); + QCOMPARE(contentList[1]->encryptions().size(), 1); + QCOMPARE(contentList[1]->signatures().size(), 0); + auto contentAttachmentList = parser.collectAttachmentParts(); + QCOMPARE(contentAttachmentList.size(), 0); + } }; QTEST_GUILESS_MAIN(InterfaceTest) -#include "interfacetest.moc" \ No newline at end of file +#include "interfacetest.moc" -- cgit v1.2.3