Exemplo n.º 1
0
FullMessageCombiner::FullMessageCombiner(const QModelIndex &messageIndex, QObject *parent) :
    QObject(parent), m_model(0), m_messageIndex(messageIndex)
{
    Imap::Mailbox::Model::realTreeItem(messageIndex, &m_model);
    Q_ASSERT(m_model);
    Imap::Mailbox::TreeItemPart *headerPart = headerPartPtr();
    Imap::Mailbox::TreeItemPart *bodyPart = bodyPartPtr();

    Q_ASSERT(headerPart);
    Q_ASSERT(bodyPart);

    m_headerPartIndex = headerPart->toIndex(const_cast<Mailbox::Model *>(m_model));
    Q_ASSERT(m_headerPartIndex.isValid());

    m_bodyPartIndex = bodyPart->toIndex(const_cast<Mailbox::Model *>(m_model));
    Q_ASSERT(m_bodyPartIndex.isValid());

    connect(m_model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), SLOT(slotDataChanged(QModelIndex,QModelIndex)));
}
Exemplo n.º 2
0
/** @short Prepare a network request

This function handles delegating access to the other body parts using various schemes (ie. the special trojita-imap:// one used
by Trojita for internal purposes and the cid: one for referencing to other body parts).  Policy checks for filtering access to
the public Internet are also performed at this level.
*/
QNetworkReply *MsgPartNetAccessManager::createRequest(Operation op, const QNetworkRequest &req, QIODevice *outgoingData)
{
    Q_UNUSED(op);
    Q_UNUSED(outgoingData);

    if (!message.isValid()) {
        // Our message got removed in the meanwhile
        // FIXME: add a better class here
        return new Imap::Network::ForbiddenReply(this);
    }

    Q_ASSERT(message.isValid());
    const Mailbox::Model *constModel = 0;
    Mailbox::Model::realTreeItem(message, &constModel);
    Q_ASSERT(constModel);
    Mailbox::Model *model = const_cast<Mailbox::Model *>(constModel);
    Q_ASSERT(model);
    Imap::Mailbox::TreeItemPart *part = pathToPart(message, req.url().path());
    QModelIndex partIndex = part ? part->toIndex(model) : QModelIndex();

    if (req.url().scheme() == QLatin1String("trojita-imap") && req.url().host() == QLatin1String("msg")) {
        // Internal Trojita reference
        if (part) {
            return new Imap::Network::MsgPartNetworkReply(this, partIndex);
        } else {
            qDebug() << "No such part:" << req.url();
            return new Imap::Network::ForbiddenReply(this);
        }
    } else if (req.url().scheme() == QLatin1String("cid")) {
        // The cid: scheme for cross-part references
        QByteArray cid = req.url().path().toUtf8();
        if (!cid.startsWith("<"))
            cid = QByteArray("<") + cid;
        if (!cid.endsWith(">"))
            cid += ">";
        Imap::Mailbox::TreeItemPart *target = cidToPart(cid, model, model->realTreeItem(message));
        if (target) {
            return new Imap::Network::MsgPartNetworkReply(this, target->toIndex(model));
        } else {
            qDebug() << "Content-ID not found" << cid;
            return new Imap::Network::ForbiddenReply(this);
        }
    } else if (req.url() == QUrl(QLatin1String("about:blank"))) {
        // about:blank is a relatively harmless URL which is used for opening an empty page
        return QNetworkAccessManager::createRequest(op, req, outgoingData);
    } else if (req.url().scheme() == QLatin1String("data")) {
        // data: scheme shall be safe, it's just a method of local access after all
        return QNetworkAccessManager::createRequest(op, req, outgoingData);
    } else {
        // Regular access -- we've got to check policy here
        if (req.url().scheme() == QLatin1String("http") || req.url().scheme() == QLatin1String("https")) {
            if (externalsEnabled) {
                return QNetworkAccessManager::createRequest(op, req, outgoingData);
            } else {
                emit requestingExternal(req.url());
                return new Imap::Network::ForbiddenReply(this);
            }
        } else {
            qDebug() << "Forbidden per policy:" << req.url();
            return new Imap::Network::ForbiddenReply(this);
        }
    }
}
Exemplo n.º 3
0
QWidget *PartWidgetFactory::create(const QModelIndex &partIndex, int recursionDepth, const PartLoadingMode loadingMode)
{
    using namespace Imap::Mailbox;
    Q_ASSERT(partIndex.isValid());

    if (recursionDepth > 1000) {
        return new QLabel(tr("This message contains too deep nesting of MIME message parts.\n"
                             "To prevent stack exhaustion and your head from exploding, only\n"
                             "the top-most thousand items or so are shown."), 0);
    }

    bool userPrefersPlaintext = QSettings().value(Common::SettingsNames::guiPreferPlaintextRendering, QVariant(true)).toBool();

    QString mimeType = partIndex.data(Imap::Mailbox::RolePartMimeType).toString();
    if (mimeType.startsWith(QLatin1String("multipart/"))) {
        // it's a compound part
        if (mimeType == QLatin1String("multipart/alternative")) {
            return new MultipartAlternativeWidget(0, this, partIndex, recursionDepth,
                                                  userPrefersPlaintext ?
                                                      QLatin1String("text/plain") :
                                                      QLatin1String("text/html"));
        } else if (mimeType == QLatin1String("multipart/signed")) {
            return new MultipartSignedWidget(0, this, partIndex, recursionDepth);
        } else if (mimeType == QLatin1String("multipart/related")) {
            // The purpose of this section is to find a text/html e-mail, along with its associated body parts, and hide
            // everything else than the HTML widget.

            // At this point, it might be interesting to somehow respect the user's preference about using text/plain
            // instead of text/html. However, things are a bit complicated; the widget used at this point really wants
            // to either show just a single part or alternatively all of them in a sequence.
            // Furthermore, if someone sends a text/plain and a text/html together inside a multipart/related, they're
            // just wrong.

            // Let's see if we know what the root part is
            QModelIndex mainPartIndex;
            QVariant mainPartCID = partIndex.data(RolePartMultipartRelatedMainCid);
            if (mainPartCID.isValid()) {
                const Imap::Mailbox::Model *constModel = 0;
                Imap::Mailbox::TreeItemPart *part = dynamic_cast<Imap::Mailbox::TreeItemPart *>(Imap::Mailbox::Model::realTreeItem(partIndex, &constModel));
                Imap::Mailbox::Model *model = const_cast<Imap::Mailbox::Model *>(constModel);
                Imap::Mailbox::TreeItemPart *mainPartPtr = Imap::Network::MsgPartNetAccessManager::cidToPart(mainPartCID.toByteArray(), model, part);
                if (mainPartPtr) {
                    mainPartIndex = mainPartPtr->toIndex(model);
                }
            }

            if (!mainPartIndex.isValid()) {
                // The Content-Type-based start parameter was not terribly useful. Let's find the HTML part manually.
                QModelIndex candidate = partIndex.child(0, 0);
                while (candidate.isValid()) {
                    if (candidate.data(RolePartMimeType).toString() == QLatin1String("text/html")) {
                        mainPartIndex = candidate;
                        break;
                    }
                    candidate = candidate.sibling(candidate.row() + 1, 0);
                }
            }

            if (mainPartIndex.isValid()) {
                if (mainPartIndex.data(RolePartMimeType).toString() == QLatin1String("text/html")) {
                    return PartWidgetFactory::create(mainPartIndex, recursionDepth+1);
                } else {
                    // Sorry, but anything else than text/html is by definition suspicious here. Better than picking some random
                    // choice, let's just show everything.
                    return new GenericMultipartWidget(0, this, partIndex, recursionDepth);
                }
            } else {
                // The RFC2387's wording is clear that in absence of an explicit START argument, the first part is the starting one.
                // On the other hand, I've seen real-world messages whose first part is some utter garbage (an image sent as
                // application/octet-stream, for example) and some *other* part is an HTML text. In that case (and if we somehow
                // failed to pick the HTML part by a heuristic), it's better to show everything.
                return new GenericMultipartWidget(0, this, partIndex, recursionDepth);
            }
        } else {
            return new GenericMultipartWidget(0, this, partIndex, recursionDepth);
        }
    } else if (mimeType == QLatin1String("message/rfc822")) {
        return new Message822Widget(0, this, partIndex, recursionDepth);
    } else {
        QStringList allowedMimeTypes;
        allowedMimeTypes << "text/html" << "text/plain" << "image/jpeg" <<
                         "image/jpg" << "image/pjpeg" << "image/png" << "image/gif";
        // The problem is that some nasty MUAs (hint hint Thunderbird) would
        // happily attach a .tar.gz and call it "inline"
        bool showInline = partIndex.data(Imap::Mailbox::RolePartBodyDisposition).toByteArray().toLower() != "attachment" &&
                          allowedMimeTypes.contains(mimeType);

        if (showInline) {
            const Imap::Mailbox::Model *constModel = 0;
            Imap::Mailbox::TreeItemPart *part = dynamic_cast<Imap::Mailbox::TreeItemPart *>(Imap::Mailbox::Model::realTreeItem(partIndex, &constModel));
            Imap::Mailbox::Model *model = const_cast<Imap::Mailbox::Model *>(constModel);
            Q_ASSERT(model);
            Q_ASSERT(part);
            part->fetchFromCache(model);

            bool showDirectly = loadingMode == LOAD_IMMEDIATELY;
            if (!part->fetched())
                showDirectly &= model->isNetworkOnline() || part->octets() <= ExpensiveFetchThreshold;

            QWidget *widget = 0;
            if (showDirectly) {
                widget = new SimplePartWidget(0, manager, partIndex, m_messageView);
            } else if (model->isNetworkAvailable() || part->fetched()) {
                widget = new LoadablePartWidget(0, manager, partIndex, m_messageView,
                                                loadingMode == LOAD_ON_SHOW && part->octets() <= ExpensiveFetchThreshold ?
                                                    LoadablePartWidget::LOAD_ON_SHOW :
                                                    LoadablePartWidget::LOAD_ON_CLICK);
            } else {
                widget = new QLabel(tr("Offline"), 0);
            }
            return widget;
        } else {
            return new AttachmentView(0, manager, partIndex);
        }
    }
    QLabel *lbl = new QLabel(mimeType, 0);
    return lbl;
}