File indexing completed on 2024-04-14 04:52:53

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2002 Patrick Charbonnier <pch@valleeurpe.net>
0004    Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
0005    Copyright (C) 2007 Urs Wolfer <uwolfer @ kde.org>
0006    Copyright (C) 2010 Dawit Alemayehu <adawit@kde.org>
0007    Copyright (C) 2010 Matthias Fuchs <mat69@gmx.net>
0008 
0009    This program is free software; you can redistribute it and/or
0010    modify it under the terms of the GNU General Public
0011    License as published by the Free Software Foundation; either
0012    version 2 of the License, or (at your option) any later version.
0013 */
0014 
0015 #include "kget_plugin.h"
0016 
0017 #include "kget_interface.h"
0018 #include "konq_kpart_plugin.h"
0019 
0020 #include <QDBusConnection>
0021 #include <QIcon>
0022 #include <QMenu>
0023 
0024 #include <KActionCollection>
0025 #include <KActionMenu>
0026 #include <KDialogJobUiDelegate>
0027 #include <KFileItem>
0028 #include <KIO/CommandLauncherJob>
0029 #include <KIconLoader>
0030 #include <KLocalizedString>
0031 #include <KMessageBox>
0032 #include <KParts/FileInfoExtension>
0033 #include <KParts/Part>
0034 #include <KParts/PartManager>
0035 #include <KParts/ReadOnlyPart>
0036 #include <KPluginFactory>
0037 #include <KProtocolInfo>
0038 #include <KToggleAction>
0039 
0040 #include <konq_kpart_plugin.h>
0041 #include <asyncselectorinterface.h>
0042 #include <htmlextension.h>
0043 
0044 #define QL1S(x) QLatin1String(x)
0045 
0046 K_PLUGIN_CLASS_WITH_JSON(KGetPlugin, "kget_plugin.json")
0047 
0048 static QWidget *partWidget(QObject *obj)
0049 {
0050     auto *part = qobject_cast<KParts::ReadOnlyPart *>(obj);
0051     return part ? part->widget() : nullptr;
0052 }
0053 
0054 KGetPlugin::KGetPlugin(QObject *parent, const QVariantList &)
0055     : KonqParts::Plugin(parent)
0056 {
0057     auto *menu = new KActionMenu(QIcon::fromTheme("kget"), i18n("Download Manager"), actionCollection());
0058     actionCollection()->addAction("kget_menu", menu);
0059 
0060     menu->setPopupMode(QToolButton::InstantPopup);
0061     connect(menu->menu(), &QMenu::aboutToShow, this, &KGetPlugin::showPopup);
0062 
0063     m_dropTargetAction = new KToggleAction(i18n("Show Drop Target"), actionCollection());
0064 
0065     connect(m_dropTargetAction, &QAction::triggered, this, &KGetPlugin::slotShowDrop);
0066     actionCollection()->addAction(QL1S("show_drop"), m_dropTargetAction);
0067     menu->addAction(m_dropTargetAction);
0068 
0069     QAction *showLinksAction = actionCollection()->addAction(QL1S("show_links"));
0070     showLinksAction->setText(i18n("List All Links"));
0071     connect(showLinksAction, &QAction::triggered, this, &KGetPlugin::slotShowLinks);
0072     menu->addAction(showLinksAction);
0073 
0074     QAction *showSelectedLinksAction = actionCollection()->addAction(QL1S("show_selected_links"));
0075     showSelectedLinksAction->setText(i18n("List Selected Links"));
0076     connect(showSelectedLinksAction, &QAction::triggered, this, &KGetPlugin::slotShowSelectedLinks);
0077     menu->addAction(showSelectedLinksAction);
0078 
0079     // Hide this plugin if the parent part does not support either
0080     // The FileInfo or Html extensions...
0081     if (!HtmlExtension::childObject(parent) && !KParts::FileInfoExtension::childObject(parent)) {
0082         menu->setVisible(false);
0083     }
0084 }
0085 
0086 KGetPlugin::~KGetPlugin()
0087 {
0088 }
0089 
0090 static bool hasDropTarget()
0091 {
0092     bool found = false;
0093 
0094     if (QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) {
0095         OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus());
0096         QDBusReply<bool> reply = kgetInterface.dropTargetVisible();
0097         if (reply.isValid()) {
0098             found = reply.value();
0099         }
0100     }
0101 
0102     return found;
0103 }
0104 
0105 KGetPlugin::SelectorInterface::SelectorInterface(HtmlExtension* ext)
0106 {
0107 #if QT_VERSION_MAJOR < 6
0108     KParts::SelectorInterface *syncIface = qobject_cast<KParts::SelectorInterface*>(ext);
0109     if (syncIface) {
0110         interfaceType = SelectorInterfaceType::Sync;
0111         syncInterface = syncIface;
0112     } else {
0113 #endif
0114         AsyncSelectorInterface *asyncIface = qobject_cast<AsyncSelectorInterface*>(ext);
0115         if (asyncIface) {
0116             interfaceType = SelectorInterfaceType::Async;
0117             asyncInterface = asyncIface;
0118         }
0119 #if QT_VERSION_MAJOR < 6
0120     }
0121 #endif
0122 }
0123 
0124 bool KGetPlugin::SelectorInterface::hasInterface() const
0125 {
0126 #if QT_VERSION_MAJOR < 6
0127     return ((interfaceType == SelectorInterfaceType::Sync && syncInterface) || (interfaceType == SelectorInterfaceType::Async && asyncInterface));
0128 #else
0129     return interfaceType == SelectorInterfaceType::Async && asyncInterface;
0130 #endif
0131 }
0132 
0133 AsyncSelectorInterface::QueryMethods KGetPlugin::SelectorInterface::supportedMethods() const
0134 {
0135 #if QT_VERSION_MAJOR < 6
0136     if (interfaceType == SelectorInterfaceType::Sync) {
0137         auto methods = syncInterface->supportedQueryMethods();
0138         AsyncSelectorInterface::QueryMethods res;
0139         if (methods & KParts::SelectorInterface::SelectedContent) {
0140             res |= AsyncSelectorInterface::SelectedContent;
0141         }
0142         if (methods & KParts::SelectorInterface::EntireContent) {
0143             res |= AsyncSelectorInterface::EntireContent;
0144         }
0145         return res;
0146     } else {
0147         return asyncInterface->supportedAsyncQueryMethods();
0148     }
0149 #else
0150     return asyncInterface->supportedAsyncQueryMethods();
0151 #endif
0152 }
0153 
0154 void KGetPlugin::showPopup()
0155 {
0156     // Check for HtmlExtension support...
0157     HtmlExtension *htmlExtn = HtmlExtension::childObject(parent());
0158     if (htmlExtn) {
0159         SelectorInterface iface(htmlExtn);
0160         AsyncSelectorInterface::QueryMethods methods = iface.supportedMethods();
0161         m_dropTargetAction->setChecked(hasDropTarget());
0162 
0163         bool enable = (methods & AsyncSelectorInterface::EntireContent);
0164         actionCollection()->action(QL1S("show_links"))->setEnabled(enable);
0165 
0166         enable = (htmlExtn->hasSelection() && (methods & AsyncSelectorInterface::SelectedContent));
0167         actionCollection()->action(QL1S("show_selected_links"))->setEnabled(enable);
0168 
0169         enable = (actionCollection()->action(QL1S("show_links"))->isEnabled() || actionCollection()->action(QL1S("show_selected_links"))->isEnabled());
0170         actionCollection()->action(QL1S("show_drop"))->setEnabled(enable);
0171 
0172         return;
0173     }
0174 
0175     // Check for FileInfoExtension support...
0176     KParts::FileInfoExtension *fileinfoExtn = KParts::FileInfoExtension::childObject(parent());
0177     if (fileinfoExtn) {
0178         m_dropTargetAction->setChecked(hasDropTarget());
0179         const KParts::FileInfoExtension::QueryModes modes = fileinfoExtn->supportedQueryModes();
0180         bool enable = (modes & KParts::FileInfoExtension::AllItems);
0181         actionCollection()->action(QL1S("show_links"))->setEnabled(enable);
0182         enable = (fileinfoExtn->hasSelection() && (modes & KParts::FileInfoExtension::SelectedItems));
0183         actionCollection()->action(QL1S("show_selected_links"))->setEnabled(enable);
0184         enable = (actionCollection()->action(QL1S("show_links"))->isEnabled() || actionCollection()->action(QL1S("show_selected_links"))->isEnabled());
0185         actionCollection()->action(QL1S("show_drop"))->setEnabled(enable);
0186         return;
0187     }
0188 
0189     actionCollection()->action(QL1S("show_selected_links"))->setEnabled(false);
0190     actionCollection()->action(QL1S("show_links"))->setEnabled(false);
0191     actionCollection()->action(QL1S("show_drop"))->setEnabled(false);
0192     if (m_dropTargetAction->isChecked()) {
0193         m_dropTargetAction->setChecked(false);
0194     }
0195 }
0196 
0197 void KGetPlugin::slotShowDrop()
0198 {
0199     if (!QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.kget")) {
0200         const QString command = QStringLiteral("kget --showDropTarget --hideMainWindow");
0201         auto *job = new KIO::CommandLauncherJob(command);
0202         job->setDesktopName(QStringLiteral("org.kde.kget"));
0203         job->setUiDelegate(new KDialogJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, partWidget(parent())));
0204         job->start();
0205     } else {
0206         OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus());
0207         kgetInterface.setDropTargetVisible(m_dropTargetAction->isChecked());
0208     }
0209 }
0210 
0211 void KGetPlugin::slotShowLinks()
0212 {
0213     getLinks(false);
0214 }
0215 
0216 void KGetPlugin::slotShowSelectedLinks()
0217 {
0218     getLinks(true);
0219 }
0220 
0221 void KGetPlugin::slotImportLinks()
0222 {
0223     if (m_linkList.isEmpty()) {
0224         KMessageBox::error(partWidget(parent()), i18n("No downloadable links were found."), i18n("No Links"));
0225         return;
0226     }
0227 
0228     // Remove any duplicates links from the list...
0229     m_linkList.removeDuplicates();
0230 
0231     OrgKdeKgetMainInterface kgetInterface("org.kde.kget", "/KGet", QDBusConnection::sessionBus());
0232     kgetInterface.importLinks(m_linkList);
0233 }
0234 
0235 void KGetPlugin::fillLinkListFromHtml(const QUrl& baseUrl, const QList<AsyncSelectorInterface::Element >& elements)
0236 {
0237     QString attr;
0238     for (const AsyncSelectorInterface::Element &element : elements) {
0239         if (element.hasAttribute(QL1S("href")))
0240             attr = QL1S("href");
0241         else if (element.hasAttribute(QL1S("src")))
0242             attr = QL1S("src");
0243         else if (element.hasAttribute(QL1S("data")))
0244             attr = QL1S("data");
0245         const QUrl resolvedUrl(baseUrl.resolved(QUrl(element.attribute(attr))));
0246         // Only select valid and non-local links for download...
0247         if (resolvedUrl.isValid() && !resolvedUrl.isLocalFile() && !resolvedUrl.host().isEmpty()) {
0248             if (element.hasAttribute(QL1S("type")))
0249                 m_linkList << QString(QL1S("url ") + resolvedUrl.url() + QL1S(" type ") + element.attribute(QL1S("type")));
0250             else
0251                 m_linkList << resolvedUrl.url();
0252         }
0253     }
0254     slotImportLinks();
0255 }
0256 
0257 void KGetPlugin::getLinks(bool selectedOnly)
0258 {
0259     HtmlExtension *htmlExtn = HtmlExtension::childObject(parent());
0260     if (htmlExtn) {
0261         SelectorInterface iface(htmlExtn);
0262         if (iface.hasInterface()) {
0263             m_linkList.clear();
0264             const QUrl baseUrl = htmlExtn->baseUrl();
0265             const QString query = QL1S("a[href], img[src], audio[src], video[src], embed[src], object[data]");
0266             const AsyncSelectorInterface::QueryMethod method = (selectedOnly ? AsyncSelectorInterface::SelectedContent : AsyncSelectorInterface::EntireContent);
0267             if (iface.interfaceType == SelectorInterfaceType::Sync) {
0268 #if QT_VERSION_MAJOR < 6
0269                 const QList<AsyncSelectorInterface::Element> elements = iface.syncInterface->querySelectorAll(query, static_cast<KParts::SelectorInterface::QueryMethod>(method));
0270                 fillLinkListFromHtml(baseUrl, elements);
0271 #endif
0272             } else if (iface.interfaceType == SelectorInterfaceType::Async) {
0273                 auto callback = [this, baseUrl](const QList<AsyncSelectorInterface::Element>& elements){
0274                     fillLinkListFromHtml(baseUrl, elements);
0275                 };
0276                 iface.asyncInterface->querySelectorAllAsync(query, method, callback);
0277             }
0278         }
0279     }
0280 
0281     KParts::FileInfoExtension *fileinfoExtn = KParts::FileInfoExtension::childObject(parent());
0282     if (fileinfoExtn) {
0283         m_linkList.clear();
0284         const KParts::FileInfoExtension::QueryMode mode = (selectedOnly ? KParts::FileInfoExtension::SelectedItems : KParts::FileInfoExtension::AllItems);
0285         const KFileItemList items = fileinfoExtn->queryFor(mode);
0286         for (const KFileItem &item: items) {
0287             const QUrl url = item.url();
0288             // Only select valid and non local links for download...
0289             if (item.isReadable() && item.isFile() && !item.isLocalFile() && !url.host().isEmpty()) {
0290                 if (item.mimetype().isEmpty())
0291                     m_linkList << url.url();
0292                 else
0293                     m_linkList << QString(QL1S("url ") + url.url() + QL1S(" type ") + item.mimetype());
0294             }
0295         }
0296         slotImportLinks();
0297     }
0298 }
0299 
0300 #include "kget_plugin.moc"