1 /* Copyright (C) 2006 - 2013 Jan Kundrát <jkt@flaska.net>
3 This file is part of the Trojita Qt IMAP e-mail client,
4 http://trojita.flaska.net/
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "PartWidgetFactory.h"
23 #include "AttachmentView.h"
24 #include "MessageView.h" // so that the ocmpiler knows that it's an QObject
25 #include "LoadablePartWidget.h"
26 #include "PartWidget.h"
27 #include "SimplePartWidget.h"
28 #include "Common/SettingsNames.h"
29 #include "Imap/Model/ItemRoles.h"
30 #include "Imap/Model/MailboxTree.h"
31 #include "Imap/Model/Model.h"
34 #include <QHBoxLayout>
36 #include <QPushButton>
40 #include <QVBoxLayout>
45 PartWidgetFactory::PartWidgetFactory(Imap::Network::MsgPartNetAccessManager
*manager
, MessageView
*messageView
):
46 manager(manager
), m_messageView(messageView
)
50 QWidget
*PartWidgetFactory::create(const QModelIndex
&partIndex
)
52 return create(partIndex
, 0);
55 QWidget
*PartWidgetFactory::create(const QModelIndex
&partIndex
, int recursionDepth
, const PartLoadingMode loadingMode
)
57 using namespace Imap::Mailbox
;
58 Q_ASSERT(partIndex
.isValid());
60 if (recursionDepth
> 1000) {
61 return new QLabel(tr("This message contains too deep nesting of MIME message parts.\n"
62 "To prevent stack exhaustion and your head from exploding, only\n"
63 "the top-most thousand items or so are shown."), 0);
66 bool userPrefersPlaintext
= QSettings().value(Common::SettingsNames::guiPreferPlaintextRendering
, QVariant(true)).toBool();
68 QString mimeType
= partIndex
.data(Imap::Mailbox::RolePartMimeType
).toString();
69 if (mimeType
.startsWith(QLatin1String("multipart/"))) {
70 // it's a compound part
71 if (mimeType
== QLatin1String("multipart/alternative")) {
72 return new MultipartAlternativeWidget(0, this, partIndex
, recursionDepth
,
73 userPrefersPlaintext
?
74 QLatin1String("text/plain") :
75 QLatin1String("text/html"));
76 } else if (mimeType
== QLatin1String("multipart/signed")) {
77 return new MultipartSignedWidget(0, this, partIndex
, recursionDepth
);
78 } else if (mimeType
== QLatin1String("multipart/related")) {
79 // The purpose of this section is to find a text/html e-mail, along with its associated body parts, and hide
80 // everything else than the HTML widget.
82 // At this point, it might be interesting to somehow respect the user's preference about using text/plain
83 // instead of text/html. However, things are a bit complicated; the widget used at this point really wants
84 // to either show just a single part or alternatively all of them in a sequence.
85 // Furthermore, if someone sends a text/plain and a text/html together inside a multipart/related, they're
88 // Let's see if we know what the root part is
89 QModelIndex mainPartIndex
;
90 QVariant mainPartCID
= partIndex
.data(RolePartMultipartRelatedMainCid
);
91 if (mainPartCID
.isValid()) {
92 const Imap::Mailbox::Model
*constModel
= 0;
93 Imap::Mailbox::TreeItemPart
*part
= dynamic_cast<Imap::Mailbox::TreeItemPart
*>(Imap::Mailbox::Model::realTreeItem(partIndex
, &constModel
));
94 Imap::Mailbox::Model
*model
= const_cast<Imap::Mailbox::Model
*>(constModel
);
95 Imap::Mailbox::TreeItemPart
*mainPartPtr
= Imap::Network::MsgPartNetAccessManager::cidToPart(mainPartCID
.toByteArray(), model
, part
);
97 mainPartIndex
= mainPartPtr
->toIndex(model
);
101 if (!mainPartIndex
.isValid()) {
102 // The Content-Type-based start parameter was not terribly useful. Let's find the HTML part manually.
103 QModelIndex candidate
= partIndex
.child(0, 0);
104 while (candidate
.isValid()) {
105 if (candidate
.data(RolePartMimeType
).toString() == QLatin1String("text/html")) {
106 mainPartIndex
= candidate
;
109 candidate
= candidate
.sibling(candidate
.row() + 1, 0);
113 if (mainPartIndex
.isValid()) {
114 if (mainPartIndex
.data(RolePartMimeType
).toString() == QLatin1String("text/html")) {
115 return PartWidgetFactory::create(mainPartIndex
, recursionDepth
+1);
117 // Sorry, but anything else than text/html is by definition suspicious here. Better than picking some random
118 // choice, let's just show everything.
119 return new GenericMultipartWidget(0, this, partIndex
, recursionDepth
);
122 // The RFC2387's wording is clear that in absence of an explicit START argument, the first part is the starting one.
123 // On the other hand, I've seen real-world messages whose first part is some utter garbage (an image sent as
124 // application/octet-stream, for example) and some *other* part is an HTML text. In that case (and if we somehow
125 // failed to pick the HTML part by a heuristic), it's better to show everything.
126 return new GenericMultipartWidget(0, this, partIndex
, recursionDepth
);
129 return new GenericMultipartWidget(0, this, partIndex
, recursionDepth
);
131 } else if (mimeType
== QLatin1String("message/rfc822")) {
132 return new Message822Widget(0, this, partIndex
, recursionDepth
);
134 QStringList allowedMimeTypes
;
135 allowedMimeTypes
<< "text/html" << "text/plain" << "image/jpeg" <<
136 "image/jpg" << "image/pjpeg" << "image/png" << "image/gif";
137 // The problem is that some nasty MUAs (hint hint Thunderbird) would
138 // happily attach a .tar.gz and call it "inline"
139 bool showInline
= partIndex
.data(Imap::Mailbox::RolePartBodyDisposition
).toByteArray().toLower() != "attachment" &&
140 allowedMimeTypes
.contains(mimeType
);
143 const Imap::Mailbox::Model
*constModel
= 0;
144 Imap::Mailbox::TreeItemPart
*part
= dynamic_cast<Imap::Mailbox::TreeItemPart
*>(Imap::Mailbox::Model::realTreeItem(partIndex
, &constModel
));
145 Imap::Mailbox::Model
*model
= const_cast<Imap::Mailbox::Model
*>(constModel
);
148 part
->fetchFromCache(model
);
150 bool showDirectly
= loadingMode
== LOAD_IMMEDIATELY
;
151 if (!part
->fetched())
152 showDirectly
&= model
->isNetworkOnline() || part
->octets() <= ExpensiveFetchThreshold
;
156 widget
= new SimplePartWidget(0, manager
, partIndex
, m_messageView
);
157 } else if (model
->isNetworkAvailable() || part
->fetched()) {
158 widget
= new LoadablePartWidget(0, manager
, partIndex
, m_messageView
,
159 loadingMode
== LOAD_ON_SHOW
&& part
->octets() <= ExpensiveFetchThreshold
?
160 LoadablePartWidget::LOAD_ON_SHOW
:
161 LoadablePartWidget::LOAD_ON_CLICK
);
163 widget
= new QLabel(tr("Offline"), 0);
167 return new AttachmentView(0, manager
, partIndex
, m_messageView
);
170 QLabel
*lbl
= new QLabel(mimeType
, 0);
174 MessageView
*PartWidgetFactory::messageView() const
176 return m_messageView
;