File indexing completed on 2024-05-12 05:21:26
0001 /* 0002 SPDX-FileCopyrightText: 2015 Aleix Pol Gonzalez <aleixpol@kde.org> 0003 SPDX-FileCopyrightText: 2015 Martin Klapetek <mklapetek@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "kpeoplevcard.h" 0009 #include <KContacts/Picture> 0010 #include <KContacts/VCardConverter> 0011 #include <KLocalizedString> 0012 #include <QDebug> 0013 #include <QDir> 0014 #include <QImage> 0015 #include <QStandardPaths> 0016 0017 #include <KFileUtils> 0018 #include <KPluginFactory> 0019 0020 using namespace KPeople; 0021 0022 #ifdef Q_OS_WIN 0023 Q_GLOBAL_STATIC_WITH_ARGS(QString, vcardsLocation, 0024 (QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QString::fromLatin1("/Contacts"))) 0025 #else 0026 Q_GLOBAL_STATIC_WITH_ARGS(QString, vcardsLocation, 0027 (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString::fromLatin1("/kpeoplevcard"))) 0028 #endif 0029 0030 #ifdef Q_OS_WIN 0031 Q_GLOBAL_STATIC_WITH_ARGS(QString, vcardsWriteLocation, 0032 (QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + QString::fromLatin1("/Contacts/own"))) 0033 #else 0034 Q_GLOBAL_STATIC_WITH_ARGS(QString, vcardsWriteLocation, 0035 (QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QString::fromLatin1("/kpeoplevcard/own/"))) 0036 #endif 0037 0038 class VCardContact : public AbstractEditableContact 0039 { 0040 public: 0041 VCardContact() 0042 { 0043 } 0044 VCardContact(const KContacts::Addressee &addr, const QUrl &location) 0045 : m_addressee(addr) 0046 , m_location(location) 0047 { 0048 } 0049 void setAddressee(const KContacts::Addressee &addr) 0050 { 0051 m_addressee = addr; 0052 } 0053 void setUrl(const QUrl &url) 0054 { 0055 m_location = url; 0056 } 0057 0058 QVariant customProperty(const QString &key) const override 0059 { 0060 QVariant ret; 0061 if (key == NameProperty) { 0062 const QString name = m_addressee.realName(); 0063 if (!name.isEmpty()) { 0064 return name; 0065 } 0066 0067 // If both first and last name are set combine them to a full name 0068 if (!m_addressee.givenName().isEmpty() && !m_addressee.familyName().isEmpty()) 0069 return i18ndc("kpeoplevcard", "given-name family-name", "%1 %2", m_addressee.givenName(), m_addressee.familyName()); 0070 0071 // If only one of them is set just return what we know 0072 if (!m_addressee.givenName().isEmpty()) 0073 return m_addressee.givenName(); 0074 if (!m_addressee.familyName().isEmpty()) 0075 return m_addressee.familyName(); 0076 0077 // Fall back to other identifiers 0078 if (!m_addressee.preferredEmail().isEmpty()) { 0079 return m_addressee.preferredEmail(); 0080 } 0081 if (!m_addressee.phoneNumbers().isEmpty()) { 0082 return m_addressee.phoneNumbers().at(0).number(); 0083 } 0084 return QVariant(); 0085 } else if (key == EmailProperty) 0086 return m_addressee.preferredEmail(); 0087 else if (key == AllEmailsProperty) 0088 return m_addressee.emails(); 0089 else if (key == PictureProperty) 0090 return m_addressee.photo().data(); 0091 else if (key == AllPhoneNumbersProperty) { 0092 const auto phoneNumbers = m_addressee.phoneNumbers(); 0093 QVariantList numbers; 0094 for (const KContacts::PhoneNumber &phoneNumber : phoneNumbers) { 0095 // convert from KContacts specific format to QString 0096 numbers << phoneNumber.number(); 0097 } 0098 return numbers; 0099 } else if (key == PhoneNumberProperty) { 0100 return m_addressee.phoneNumbers().isEmpty() ? QVariant() : m_addressee.phoneNumbers().at(0).number(); 0101 } else if (key == VCardProperty) { 0102 KContacts::VCardConverter converter; 0103 return converter.createVCard(m_addressee); 0104 } 0105 0106 return ret; 0107 } 0108 0109 bool setCustomProperty(const QString &key, const QVariant &value) override 0110 { 0111 if (key == VCardProperty) { 0112 QFile f(m_location.toLocalFile()); 0113 if (!f.open(QIODevice::WriteOnly)) 0114 return false; 0115 f.write(value.toByteArray()); 0116 return true; 0117 } 0118 return false; 0119 } 0120 0121 static QString createUri(const QString &path) 0122 { 0123 return QStringLiteral("vcard:/") + path; 0124 } 0125 0126 private: 0127 KContacts::Addressee m_addressee; 0128 QUrl m_location; 0129 }; 0130 0131 bool VCardDataSource::addContact(const QVariantMap &properties) 0132 { 0133 if (!properties.contains("vcard")) 0134 return false; 0135 0136 if (!QDir().mkpath(*vcardsWriteLocation)) 0137 return false; 0138 0139 QFile f(*vcardsWriteLocation + KFileUtils::suggestName(QUrl::fromLocalFile(*vcardsWriteLocation), QStringLiteral("contact.vcard"))); 0140 if (!f.open(QFile::WriteOnly)) { 0141 qWarning() << "could not open file to write" << f.fileName(); 0142 return false; 0143 } 0144 0145 f.write(properties.value("vcard").toByteArray()); 0146 return true; 0147 } 0148 0149 bool VCardDataSource::deleteContact(const QString &uri) 0150 { 0151 if (!uri.startsWith("vcard:/")) 0152 return false; 0153 0154 QString path = uri; 0155 path.remove("vcard:/"); 0156 0157 if (!path.startsWith(*vcardsLocation)) 0158 return false; 0159 0160 return QFile::remove(path); 0161 } 0162 0163 KPeopleVCard::KPeopleVCard() 0164 : KPeople::AllContactsMonitor() 0165 , m_fs(new KDirWatch(this)) 0166 { 0167 QDir().mkpath(*vcardsLocation); 0168 0169 processDirectory(QFileInfo(*vcardsLocation)); 0170 0171 emitInitialFetchComplete(true); 0172 0173 connect(m_fs, &KDirWatch::dirty, this, [this](const QString &path) { 0174 const QFileInfo fi(path); 0175 if (fi.isFile()) 0176 processVCard(path); 0177 else 0178 processDirectory(fi); 0179 }); 0180 connect(m_fs, &KDirWatch::created, this, [this](const QString &path) { 0181 const QFileInfo fi(path); 0182 if (fi.isFile()) 0183 processVCard(path); 0184 else 0185 processDirectory(fi); 0186 }); 0187 connect(m_fs, &KDirWatch::deleted, this, &KPeopleVCard::deleteVCard); 0188 } 0189 0190 KPeopleVCard::~KPeopleVCard() 0191 { 0192 } 0193 0194 QMap<QString, AbstractContact::Ptr> KPeopleVCard::contacts() 0195 { 0196 return m_contactForUri; 0197 } 0198 0199 void KPeopleVCard::processDirectory(const QFileInfo &fi) 0200 { 0201 const QDir dir(fi.absoluteFilePath()); 0202 { 0203 const auto subdirs = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot); // includes '.', ie. vcards from no subdir 0204 0205 for (const auto &subdir : subdirs) { 0206 processDirectory(subdir); 0207 } 0208 } 0209 0210 { 0211 const QFileInfoList subdirVcards = dir.entryInfoList({"*.vcard", "*.vcf"}); 0212 for (const QFileInfo &vcardFile : subdirVcards) { 0213 processVCard(vcardFile.absoluteFilePath()); 0214 } 0215 } 0216 m_fs->addDir(dir.absolutePath(), KDirWatch::WatchDirOnly | KDirWatch::WatchSubDirs | KDirWatch::WatchFiles); 0217 } 0218 0219 void KPeopleVCard::processVCard(const QString &path) 0220 { 0221 m_fs->addFile(path); 0222 0223 QFile f(path); 0224 bool b = f.open(QIODevice::ReadOnly); 0225 if (!b) { 0226 qWarning() << "error: couldn't open:" << path; 0227 return; 0228 } 0229 0230 KContacts::VCardConverter conv; 0231 const KContacts::Addressee addressee = conv.parseVCard(f.readAll()); 0232 0233 QString uri = VCardContact::createUri(path); 0234 auto it = m_contactForUri.find(uri); 0235 if (it != m_contactForUri.end()) { 0236 static_cast<VCardContact *>(it->data())->setAddressee(addressee); 0237 static_cast<VCardContact *>(it->data())->setUrl(QUrl::fromLocalFile(path)); 0238 Q_EMIT contactChanged(uri, *it); 0239 } else { 0240 KPeople::AbstractContact::Ptr contact(new VCardContact(addressee, QUrl::fromLocalFile(path))); 0241 m_contactForUri.insert(uri, contact); 0242 Q_EMIT contactAdded(uri, contact); 0243 } 0244 } 0245 0246 void KPeopleVCard::deleteVCard(const QString &path) 0247 { 0248 if (QFile::exists(path)) 0249 return; 0250 QString uri = VCardContact::createUri(path); 0251 0252 const int r = m_contactForUri.remove(uri); 0253 if (r) 0254 Q_EMIT contactRemoved(uri); 0255 } 0256 0257 QString KPeopleVCard::contactsVCardPath() 0258 { 0259 return *vcardsLocation; 0260 } 0261 0262 QString KPeopleVCard::contactsVCardWritePath() 0263 { 0264 return *vcardsWriteLocation; 0265 } 0266 0267 VCardDataSource::VCardDataSource(QObject *parent, const QVariantList &args) 0268 : BasePersonsDataSourceV2(parent) 0269 { 0270 Q_UNUSED(args); 0271 } 0272 0273 VCardDataSource::~VCardDataSource() 0274 { 0275 } 0276 0277 QString VCardDataSource::sourcePluginId() const 0278 { 0279 return QStringLiteral("vcard"); 0280 } 0281 0282 AllContactsMonitor *VCardDataSource::createAllContactsMonitor() 0283 { 0284 return new KPeopleVCard(); 0285 } 0286 0287 K_PLUGIN_CLASS_WITH_JSON(VCardDataSource, "kpeoplevcard.json") 0288 0289 #include "kpeoplevcard.moc" 0290 0291 #include "moc_kpeoplevcard.cpp"