File indexing completed on 2024-04-28 08:51:04

0001 /*
0002     SPDX-FileCopyrightText: 2008 Urs Wolfer <uwolfer@kde.org>
0003     SPDX-FileCopyrightText: 2009 Tony Murray <murraytony@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 #include "remotedesktopsmodel.h"
0009 #include "krdc_debug.h"
0010 
0011 #include <KLocalizedString>
0012 #include <KFormat>
0013 #ifdef BUILD_ZEROCONF
0014 #include <kdnssd_version.h>
0015 #include <KDNSSD/ServiceBrowser>
0016 #include <KDNSSD/RemoteService>
0017 #endif
0018 #include <QLoggingCategory>
0019 #include <QStandardPaths>
0020 #include <QObject>
0021 
0022 RemoteDesktopsModel::RemoteDesktopsModel(QObject *parent, KBookmarkManager *manager)
0023         : QAbstractTableModel(parent)
0024 {
0025     m_manager = manager;
0026     connect(m_manager, SIGNAL(changed(QString,QString)), SLOT(bookmarksChanged()));
0027     buildModelFromBookmarkGroup(m_manager->root());
0028 
0029 #ifdef BUILD_ZEROCONF
0030     // Add RDP and NX if they start announcing via Zeroconf:
0031     m_protocols[QLatin1String("_rfb._tcp")] = QLatin1String("vnc");
0032 
0033     zeroconfBrowser = new KDNSSD::ServiceBrowser(QLatin1String("_rfb._tcp"), true);
0034     connect(zeroconfBrowser, SIGNAL(finished()), this, SLOT(servicesChanged()));
0035     zeroconfBrowser->startBrowse();
0036     qCDebug(KRDC) << "Browsing for zeroconf hosts.";
0037 #endif
0038 }
0039 
0040 RemoteDesktopsModel::~RemoteDesktopsModel()
0041 {
0042 }
0043 
0044 int RemoteDesktopsModel::columnCount(const QModelIndex &) const
0045 {
0046     return 6;  // same as count of RemoteDesktopsModel::DisplayItems enum
0047 }
0048 
0049 int RemoteDesktopsModel::rowCount(const QModelIndex &) const
0050 {
0051     return remoteDesktops.size();
0052 }
0053 
0054 Qt::ItemFlags RemoteDesktopsModel::flags(const QModelIndex &index) const
0055 {
0056     if (!index.isValid())
0057         return Qt::NoItemFlags;
0058     if (index.column() == RemoteDesktopsModel::Favorite) {
0059         return Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
0060     }
0061     return Qt::ItemIsEnabled;
0062 }
0063 
0064 bool RemoteDesktopsModel::setData(const QModelIndex &index, const QVariant &value, int role)
0065 {
0066     if (index.isValid() && role == Qt::CheckStateRole && index.column() == RemoteDesktopsModel::Favorite) {
0067         bool checked = (Qt::CheckState)value.toUInt() == Qt::Checked;
0068         remoteDesktops[index.row()].favorite = checked;
0069 
0070         RemoteDesktop rd = remoteDesktops.at(index.row());
0071         if (checked) {
0072             KBookmarkGroup root = m_manager->root();
0073             root.addBookmark(rd.title, QUrl(rd.url), QString());
0074             m_manager->emitChanged(root);
0075         } else {
0076             BookmarkManager::removeByUrl(m_manager, rd.url, true, rd.title);
0077         }
0078         return true;
0079     }
0080     return false;
0081 }
0082 
0083 QVariant RemoteDesktopsModel::data(const QModelIndex &index, int role) const
0084 {
0085     if (!index.isValid())
0086         return QVariant();
0087 
0088     RemoteDesktop item = remoteDesktops.at(index.row());
0089 
0090     switch (role) {
0091     case Qt::DisplayRole:
0092         switch (index.column()) {
0093         case RemoteDesktopsModel::Favorite:
0094             return item.favorite;
0095         case RemoteDesktopsModel::Title:
0096             return item.title;
0097         case RemoteDesktopsModel::LastConnected:
0098             return QVariant(item.lastConnected);
0099         case RemoteDesktopsModel::VisitCount:
0100             return item.visits;
0101         case RemoteDesktopsModel::Created:
0102             if (item.created.isNull()) return QVariant();
0103             return QLocale().toString(item.created.toLocalTime(), QLocale::FormatType::ShortFormat);
0104         case RemoteDesktopsModel::Source:
0105             switch (item.source) {
0106             case RemoteDesktop::Bookmarks:
0107                 return i18nc("Where each displayed link comes from", "Bookmarks");
0108             case RemoteDesktop::History:
0109                 return i18nc("Where each displayed link comes from", "History");
0110             case RemoteDesktop::Zeroconf:
0111                 return i18nc("Where each displayed link comes from", "Zeroconf");
0112             case RemoteDesktop::None:
0113                 return i18nc("Where each displayed link comes from", "None");
0114             }
0115             // fall-through
0116         default:
0117             return QVariant();
0118         }
0119 
0120     case Qt::CheckStateRole:
0121         if (index.column() == RemoteDesktopsModel::Favorite)
0122             return item.favorite ? Qt::Checked : Qt::Unchecked;
0123         return QVariant();
0124 
0125     case Qt::ToolTipRole:
0126         switch(index.column()) {
0127         case RemoteDesktopsModel::Favorite:
0128             if (item.favorite) {
0129                 return i18nc("Remove the selected url from the bookarks menu", "Remove the bookmark for %1.", item.title);
0130             } else {
0131                 return i18nc("Add the selected url to the bookmarks menu", "Bookmark %1.", item.title);
0132             }
0133         case RemoteDesktopsModel::LastConnected:
0134             if (!item.lastConnected.isNull()) {
0135                 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat);
0136             }
0137             break; // else show default tooltip
0138         case RemoteDesktopsModel::Created:
0139             if (!item.created.isNull()) {
0140                 return QLocale().toString(item.lastConnected.toLocalTime(), QLocale::FormatType::LongFormat);
0141             }
0142             break; // else show default tooltip
0143         default:
0144             break;
0145         }
0146         return item.url;  //use the url for the tooltip
0147 
0148     case 10001: //url for dockwidget
0149         return item.url;
0150 
0151     case 10002: //filter
0152         return QUrl::fromPercentEncoding(QString(item.url + item.title).toUtf8()); // return both user visible title and url data, percent encoded
0153 
0154     case 10003: //title for dockwidget
0155         return item.title;
0156 
0157     default:
0158         return QVariant();
0159     }
0160 }
0161 
0162 QVariant RemoteDesktopsModel::headerData(int section, Qt::Orientation orientation,
0163         int role) const
0164 {
0165     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
0166         switch (section) {
0167         case RemoteDesktopsModel::Favorite:
0168             return QVariant(); // the favorite column is to small for a header
0169         case RemoteDesktopsModel::Title:
0170             return i18nc("Header of the connections list, title/url for remote connection", "Remote Desktop");
0171         case RemoteDesktopsModel::LastConnected:
0172             return i18nc("Header of the connections list, the last time this connection was initiated", "Last Connected");
0173         case RemoteDesktopsModel::VisitCount:
0174             return i18nc("Header of the connections list, the number of times this connection has been visited", "Visits");
0175         case RemoteDesktopsModel::Created:
0176             return i18nc("Header of the connections list, the time when this entry was created", "Created");
0177         case RemoteDesktopsModel::Source:
0178             return i18nc("Header of the connections list, where this entry comes from", "Source");
0179         }
0180     }
0181     return QVariant();
0182 }
0183 
0184 // does not trigger view update, you must do this by hand after using this function
0185 void RemoteDesktopsModel::removeAllItemsFromSources(RemoteDesktop::Sources sources)
0186 {
0187     QMutableListIterator<RemoteDesktop> iter(remoteDesktops);
0188     while (iter.hasNext()) {
0189         iter.next();
0190         // if it matches any of the specified sources, remove it
0191         if ((iter.value().source & sources) > 0)
0192             iter.remove();
0193     }
0194 }
0195 
0196 void RemoteDesktopsModel::bookmarksChanged()
0197 {
0198     removeAllItemsFromSources(RemoteDesktop::Bookmarks | RemoteDesktop::History);
0199     buildModelFromBookmarkGroup(m_manager->root());
0200     beginResetModel();
0201     endResetModel();
0202 }
0203 
0204 // Danger Will Roobinson, confusing code ahead!
0205 void RemoteDesktopsModel::buildModelFromBookmarkGroup(const KBookmarkGroup &group)
0206 {
0207     KBookmark bm = group.first();
0208     while (!bm.isNull()) {
0209         if (bm.isGroup()) {
0210             // recurse subfolders and treat it special if it is the history folder
0211             buildModelFromBookmarkGroup(bm.toGroup());
0212         } else { // not a group
0213 
0214             RemoteDesktop item;
0215             item.title = bm.fullText();
0216             item.url = bm.url().url();
0217             int index = remoteDesktops.indexOf(item); //search for this url to see if we need to update it
0218             bool newItem = index < 0; // do we need to create a new item?
0219 
0220             // we want to merge all copies of a url into one link, so if the item exists, update it
0221             if (group.metaDataItem(QLatin1String("krdc-history")) == QLatin1String("historyfolder")) {
0222                 // set source and favorite (will override later if needed)
0223                 item.source = RemoteDesktop::History;
0224                 item.favorite = false;
0225 
0226                 // since we are in the history folder collect statistics and add them
0227                 QDateTime connected = QDateTime();
0228                 QDateTime created = QDateTime();
0229                 bool ok = false;
0230                 // first the created datetime
0231                 created = QDateTime::fromSecsSinceEpoch(bm.metaDataItem(QLatin1String("time_added")).toLongLong(&ok));
0232                 if (ok) (newItem ? item : remoteDesktops[index]).created = created;
0233                 // then the last visited datetime
0234                 ok = false;
0235                 connected = QDateTime::fromSecsSinceEpoch(bm.metaDataItem(QLatin1String("time_visited")).toLongLong(&ok));
0236                 if (ok) (newItem ? item : remoteDesktops[index]).lastConnected = connected;
0237                 // finally the visited count
0238                 ok = false;
0239                 int visits = bm.metaDataItem(QLatin1String("visit_count")).toInt(&ok);
0240                 if (ok) (newItem ? item : remoteDesktops[index]).visits = visits;
0241             } else {
0242                 if (newItem) {
0243                     // if this is a new item, just add the rest of the required data
0244                     item.lastConnected = QDateTime();
0245                     item.created = QDateTime();
0246                     item.visits = 0;
0247                     item.favorite = true;
0248                     item.source = RemoteDesktop::Bookmarks;
0249                 } else {
0250                     // otherwise override these fields with the info from the bookmark
0251                     remoteDesktops[index].title = bm.fullText();
0252                     remoteDesktops[index].favorite = true;
0253                     remoteDesktops[index].source = RemoteDesktop::Bookmarks;
0254                 }
0255             }
0256             // if we have a new item, add it
0257             if (newItem)
0258                 remoteDesktops.append(item);
0259         }
0260         bm = group.next(bm); // next item in the group
0261     }
0262 }
0263 
0264 #ifdef BUILD_ZEROCONF
0265 void RemoteDesktopsModel::servicesChanged()
0266 {
0267     //redo list because it is easier than finding and removing one that disappeared
0268     const QList<KDNSSD::RemoteService::Ptr> services = zeroconfBrowser->services();
0269     QUrl url;
0270     removeAllItemsFromSources(RemoteDesktop::Zeroconf);
0271     for (const KDNSSD::RemoteService::Ptr& service : services) {
0272         url.setScheme(m_protocols[service->type()].toLower());
0273         url.setHost(service->hostName());
0274         url.setPort(service->port());
0275 
0276         RemoteDesktop item;
0277         item.url = url.url();
0278 
0279         if (!remoteDesktops.contains(item)) {
0280             item.title = service->serviceName();
0281             item.source = RemoteDesktop::Zeroconf;
0282             item.created = QDateTime::currentDateTime();
0283             item.favorite = false;
0284             item.visits = 0;
0285             remoteDesktops.append(item);
0286         }
0287     }
0288     beginResetModel();
0289     endResetModel();
0290 }
0291 #endif
0292