File indexing completed on 2024-04-21 04:56:49
0001 /** 0002 * SPDX-FileCopyrightText: 2018 Simon Redman <simon@ergotech.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include <contactsplugin.h> 0008 0009 #include <KPluginFactory> 0010 0011 #include <QDBusConnection> 0012 #include <QDBusMetaType> 0013 #include <QDir> 0014 #include <QFile> 0015 #include <QIODevice> 0016 0017 #include <core/device.h> 0018 0019 #include "plugin_contacts_debug.h" 0020 0021 K_PLUGIN_CLASS_WITH_JSON(ContactsPlugin, "kdeconnect_contacts.json") 0022 0023 ContactsPlugin::ContactsPlugin(QObject *parent, const QVariantList &args) 0024 : KdeConnectPlugin(parent, args) 0025 , vcardsPath(QString(*vcardsLocation).append(QStringLiteral("/kdeconnect-").append(device()->id()))) 0026 { 0027 // Register custom types with dbus 0028 qRegisterMetaType<uID>("uID"); 0029 qDBusRegisterMetaType<uID>(); 0030 0031 qRegisterMetaType<uIDList_t>("uIDList_t"); 0032 qDBusRegisterMetaType<uIDList_t>(); 0033 0034 // Create the storage directory if it doesn't exist 0035 if (!QDir().mkpath(vcardsPath)) { 0036 qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "Unable to create VCard directory"; 0037 } 0038 0039 qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Contacts constructor for device " << device()->name(); 0040 } 0041 0042 void ContactsPlugin::connected() 0043 { 0044 synchronizeRemoteWithLocal(); 0045 } 0046 0047 void ContactsPlugin::receivePacket(const NetworkPacket &np) 0048 { 0049 // qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Packet Received for device " << device()->name(); 0050 // qCDebug(KDECONNECT_PLUGIN_CONTACTS) << np.body(); 0051 0052 if (np.type() == PACKAGE_TYPE_CONTACTS_RESPONSE_UIDS_TIMESTAMPS) { 0053 handleResponseUIDsTimestamps(np); 0054 } else if (np.type() == PACKET_TYPE_CONTACTS_RESPONSE_VCARDS) { 0055 handleResponseVCards(np); 0056 } 0057 } 0058 0059 void ContactsPlugin::synchronizeRemoteWithLocal() 0060 { 0061 sendRequest(PACKET_TYPE_CONTACTS_REQUEST_ALL_UIDS_TIMESTAMP); 0062 } 0063 0064 bool ContactsPlugin::handleResponseUIDsTimestamps(const NetworkPacket &np) 0065 { 0066 if (!np.has(QStringLiteral("uids"))) { 0067 qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseUIDsTimestamps:" 0068 << "Malformed packet does not have uids key"; 0069 return false; 0070 } 0071 uIDList_t uIDsToUpdate; 0072 QDir vcardsDir(vcardsPath); 0073 0074 // Get a list of all file info in this directory 0075 // Clean out IDs returned from the remote. Anything leftover should be deleted 0076 QFileInfoList localVCards = vcardsDir.entryInfoList({QStringLiteral("*.vcard"), QStringLiteral("*.vcf")}); 0077 0078 const QStringList &uIDs = np.get<QStringList>(QStringLiteral("uids")); 0079 0080 // Check local storage for the contacts: 0081 // If the contact is not found in local storage, request its vcard be sent 0082 // If the contact is in local storage but not reported, delete it 0083 // If the contact is in local storage, compare its timestamp. If different, request the contact 0084 for (const QString &ID : uIDs) { 0085 QString filename = vcardsDir.filePath(ID + VCARD_EXTENSION); 0086 QFile vcardFile(filename); 0087 0088 if (!QFile().exists(filename)) { 0089 // We do not have a vcard for this contact. Request it. 0090 uIDsToUpdate.push_back(ID); 0091 continue; 0092 } 0093 0094 // Remove this file from the list of known files 0095 QFileInfo fileInfo(vcardFile); 0096 localVCards.removeOne(fileInfo); 0097 0098 // Check if the vcard needs to be updated 0099 if (!vcardFile.open(QIODevice::ReadOnly)) { 0100 qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseUIDsTimestamps:" 0101 << "Unable to open" << filename << "to read even though it was reported to exist"; 0102 continue; 0103 } 0104 0105 QTextStream fileReadStream(&vcardFile); 0106 QString line; 0107 while (!fileReadStream.atEnd()) { 0108 fileReadStream >> line; 0109 // TODO: Check that the saved ID is the same as the one we were expecting. This requires parsing the VCard 0110 if (!line.startsWith(QStringLiteral("X-KDECONNECT-TIMESTAMP:"))) { 0111 continue; 0112 } 0113 QStringList parts = line.split(QLatin1Char(':')); 0114 QString timestamp = parts[1]; 0115 0116 qint64 remoteTimestamp = np.get<qint64>(ID); 0117 qint64 localTimestamp = timestamp.toLongLong(); 0118 0119 if (!(localTimestamp == remoteTimestamp)) { 0120 uIDsToUpdate.push_back(ID); 0121 } 0122 } 0123 } 0124 0125 // Delete all locally-known files which were not reported by the remote device 0126 for (const QFileInfo &unknownFile : localVCards) { 0127 QFile toDelete(unknownFile.filePath()); 0128 toDelete.remove(); 0129 } 0130 0131 sendRequestWithIDs(PACKET_TYPE_CONTACTS_REQUEST_VCARDS_BY_UIDS, uIDsToUpdate); 0132 0133 return true; 0134 } 0135 0136 bool ContactsPlugin::handleResponseVCards(const NetworkPacket &np) 0137 { 0138 if (!np.has(QStringLiteral("uids"))) { 0139 qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" 0140 << "Malformed packet does not have uids key"; 0141 return false; 0142 } 0143 0144 QDir vcardsDir(vcardsPath); 0145 const QStringList &uIDs = np.get<QStringList>(QStringLiteral("uids")); 0146 0147 // Loop over all IDs, extract the VCard from the packet and write the file 0148 for (const auto &ID : uIDs) { 0149 // qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "Got VCard:" << np.get<QString>(ID); 0150 QString filename = vcardsDir.filePath(ID + VCARD_EXTENSION); 0151 QFile vcardFile(filename); 0152 bool vcardFileOpened = vcardFile.open(QIODevice::WriteOnly); // Want to smash anything that might have already been there 0153 if (!vcardFileOpened) { 0154 qCWarning(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" 0155 << "Unable to open" << filename; 0156 continue; 0157 } 0158 0159 QTextStream fileWriteStream(&vcardFile); 0160 const QString &vcard = np.get<QString>(ID); 0161 fileWriteStream << vcard; 0162 } 0163 qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "handleResponseVCards:" 0164 << "Got" << uIDs.size() << "VCards"; 0165 Q_EMIT localCacheSynchronized(uIDs); 0166 return true; 0167 } 0168 0169 bool ContactsPlugin::sendRequest(const QString &packetType) 0170 { 0171 NetworkPacket np(packetType); 0172 bool success = sendPacket(np); 0173 qCDebug(KDECONNECT_PLUGIN_CONTACTS) << "sendRequest: Sending " << packetType << success; 0174 0175 return success; 0176 } 0177 0178 bool ContactsPlugin::sendRequestWithIDs(const QString &packetType, const uIDList_t &uIDs) 0179 { 0180 NetworkPacket np(packetType); 0181 0182 np.set<uIDList_t>(QStringLiteral("uids"), uIDs); 0183 bool success = sendPacket(np); 0184 return success; 0185 } 0186 0187 QString ContactsPlugin::dbusPath() const 0188 { 0189 return QLatin1String("/modules/kdeconnect/devices/%1/contacts").arg(device()->id()); 0190 } 0191 0192 #include "contactsplugin.moc" 0193 #include "moc_contactsplugin.cpp"