File indexing completed on 2025-02-02 05:05:40

0001 /*
0002     SPDX-FileCopyrightText: 2008 Tobias Koenig <tokoe@kde.org>
0003     SPDX-FileCopyrightText: 2008 Bertjan Broeksema <broeksema@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "vcarddirresource.h"
0009 
0010 #include "settingsadaptor.h"
0011 #include "vcarddirresource_debug.h"
0012 
0013 #include <QDir>
0014 #include <QDirIterator>
0015 #include <QFile>
0016 
0017 #include <Akonadi/ChangeRecorder>
0018 #include <Akonadi/EntityDisplayAttribute>
0019 #include <Akonadi/ItemFetchScope>
0020 
0021 #include <KLocalizedString>
0022 
0023 using namespace Akonadi;
0024 
0025 VCardDirResource::VCardDirResource(const QString &id)
0026     : ResourceBase(id)
0027 {
0028     VcardDirResourceSettings::instance(KSharedConfig::openConfig());
0029     // setup the resource
0030     new SettingsAdaptor(VcardDirResourceSettings::self());
0031     QDBusConnection::sessionBus().registerObject(QStringLiteral("/Settings"), VcardDirResourceSettings::self(), QDBusConnection::ExportAdaptors);
0032 
0033     changeRecorder()->itemFetchScope().fetchFullPayload();
0034     connect(this, &VCardDirResource::reloadConfiguration, this, &VCardDirResource::slotReloadConfig);
0035 }
0036 
0037 VCardDirResource::~VCardDirResource()
0038 {
0039     // clear cache
0040     mAddressees.clear();
0041 }
0042 
0043 void VCardDirResource::slotReloadConfig()
0044 {
0045     VcardDirResourceSettings::self()->load();
0046 
0047     initializeVCardDirectory();
0048     loadAddressees();
0049 
0050     synchronize();
0051 }
0052 
0053 void VCardDirResource::aboutToQuit()
0054 {
0055     VcardDirResourceSettings::self()->save();
0056 }
0057 
0058 void VCardDirResource::loadAddressees()
0059 {
0060     mAddressees.clear();
0061 
0062     QDirIterator it(vCardDirectoryName());
0063     while (it.hasNext()) {
0064         it.next();
0065         const QString filename = it.fileName();
0066         if (filename != QLatin1StringView(".") && filename != QLatin1StringView("..") && filename != QLatin1StringView("WARNING_README.txt")) {
0067             QFile file(it.filePath());
0068             if (file.open(QIODevice::ReadOnly)) {
0069                 const QByteArray data = file.readAll();
0070                 file.close();
0071 
0072                 const KContacts::Addressee addr = mConverter.parseVCard(data);
0073                 if (!addr.isEmpty()) {
0074                     mAddressees.insert(addr.uid(), addr);
0075                 }
0076             } else {
0077                 qCCritical(VCARDDIRRESOURCE_LOG) << " file can't be load " << it.filePath();
0078             }
0079         }
0080     }
0081 
0082     Q_EMIT status(Idle);
0083 }
0084 
0085 bool VCardDirResource::retrieveItems(const Akonadi::Item::List &items, const QSet<QByteArray> &parts)
0086 {
0087     Q_UNUSED(parts);
0088 
0089     Akonadi::Item::List resultItems;
0090     resultItems.reserve(items.size());
0091 
0092     for (const Akonadi::Item &item : items) {
0093         Item newItem(item);
0094         if (!doRetrieveItem(newItem)) {
0095             return false;
0096         }
0097         resultItems.append(newItem);
0098     }
0099 
0100     itemsRetrieved(resultItems);
0101 
0102     return true;
0103 }
0104 
0105 bool VCardDirResource::retrieveItem(const Akonadi::Item &item, const QSet<QByteArray> &)
0106 {
0107     Item newItem(item);
0108 
0109     if (!doRetrieveItem(newItem)) {
0110         return false;
0111     }
0112 
0113     itemRetrieved(newItem);
0114 
0115     return true;
0116 }
0117 
0118 bool VCardDirResource::doRetrieveItem(Akonadi::Item &item)
0119 {
0120     const QString remoteId = item.remoteId();
0121     if (!mAddressees.contains(remoteId)) {
0122         Q_EMIT error(i18n("Contact with uid '%1' not found.", remoteId));
0123         return false;
0124     }
0125 
0126     item.setPayload<KContacts::Addressee>(mAddressees.value(remoteId));
0127 
0128     return true;
0129 }
0130 
0131 void VCardDirResource::itemAdded(const Akonadi::Item &item, const Akonadi::Collection &)
0132 {
0133     if (VcardDirResourceSettings::self()->readOnly()) {
0134         Q_EMIT error(i18n("Trying to write to a read-only directory: '%1'", vCardDirectoryName()));
0135         cancelTask();
0136         return;
0137     }
0138 
0139     KContacts::Addressee addressee;
0140     if (item.hasPayload<KContacts::Addressee>()) {
0141         addressee = item.payload<KContacts::Addressee>();
0142     }
0143 
0144     if (!addressee.isEmpty()) {
0145         // add it to the cache...
0146         mAddressees.insert(addressee.uid(), addressee);
0147 
0148         // ... and write it through to the file system
0149         const QByteArray data = mConverter.createVCard(addressee);
0150 
0151         QFile file(vCardDirectoryFileName(addressee.uid()));
0152         file.open(QIODevice::WriteOnly);
0153         file.write(data);
0154         file.close();
0155 
0156         // report everything ok
0157         Item newItem(item);
0158         newItem.setRemoteId(addressee.uid());
0159         changeCommitted(newItem);
0160     } else {
0161         changeProcessed();
0162     }
0163 }
0164 
0165 void VCardDirResource::itemChanged(const Akonadi::Item &item, const QSet<QByteArray> &)
0166 {
0167     if (VcardDirResourceSettings::self()->readOnly()) {
0168         Q_EMIT error(i18n("Trying to write to a read-only directory: '%1'", vCardDirectoryName()));
0169         cancelTask();
0170         return;
0171     }
0172 
0173     KContacts::Addressee addressee;
0174     if (item.hasPayload<KContacts::Addressee>()) {
0175         addressee = item.payload<KContacts::Addressee>();
0176     }
0177 
0178     if (!addressee.isEmpty()) {
0179         // change it in the cache...
0180         mAddressees.insert(addressee.uid(), addressee);
0181 
0182         // ... and write it through to the file system
0183         const QByteArray data = mConverter.createVCard(addressee);
0184 
0185         QFile file(vCardDirectoryFileName(addressee.uid()));
0186         if (file.open(QIODevice::WriteOnly)) {
0187             file.write(data);
0188             file.close();
0189 
0190             Item newItem(item);
0191             newItem.setRemoteId(addressee.uid());
0192             changeCommitted(newItem);
0193         } else {
0194             qCritical() << " We can't write in file " << file.fileName();
0195         }
0196     } else {
0197         changeProcessed();
0198     }
0199 }
0200 
0201 void VCardDirResource::itemRemoved(const Akonadi::Item &item)
0202 {
0203     if (VcardDirResourceSettings::self()->readOnly()) {
0204         Q_EMIT error(i18n("Trying to write to a read-only directory: '%1'", vCardDirectoryName()));
0205         cancelTask();
0206         return;
0207     }
0208 
0209     // remove it from the cache...
0210     mAddressees.remove(item.remoteId());
0211 
0212     // ... and remove it from the file system
0213     QFile::remove(vCardDirectoryFileName(item.remoteId()));
0214 
0215     changeProcessed();
0216 }
0217 
0218 void VCardDirResource::retrieveCollections()
0219 {
0220     Collection c;
0221     c.setParentCollection(Collection::root());
0222     c.setRemoteId(vCardDirectoryName());
0223     c.setName(name());
0224     QStringList mimeTypes;
0225     mimeTypes << KContacts::Addressee::mimeType();
0226     c.setContentMimeTypes(mimeTypes);
0227     if (VcardDirResourceSettings::self()->readOnly()) {
0228         c.setRights(Collection::ReadOnly);
0229     } else {
0230         Collection::Rights rights;
0231         rights |= Collection::CanChangeItem;
0232         rights |= Collection::CanCreateItem;
0233         rights |= Collection::CanDeleteItem;
0234         rights |= Collection::CanChangeCollection;
0235         c.setRights(rights);
0236     }
0237 
0238     auto attr = c.attribute<EntityDisplayAttribute>(Collection::AddIfMissing);
0239     attr->setDisplayName(i18n("Contacts Folder"));
0240     attr->setIconName(QStringLiteral("x-office-address-book"));
0241 
0242     Collection::List list;
0243     list << c;
0244     collectionsRetrieved(list);
0245 }
0246 
0247 void VCardDirResource::retrieveItems(const Akonadi::Collection &)
0248 {
0249     Item::List items;
0250     items.reserve(mAddressees.count());
0251 
0252     for (const KContacts::Addressee &addressee : std::as_const(mAddressees)) {
0253         Item item;
0254         item.setRemoteId(addressee.uid());
0255         item.setMimeType(KContacts::Addressee::mimeType());
0256         items.append(item);
0257     }
0258 
0259     itemsRetrieved(items);
0260 }
0261 
0262 QString VCardDirResource::vCardDirectoryName() const
0263 {
0264     return VcardDirResourceSettings::self()->path();
0265 }
0266 
0267 QString VCardDirResource::vCardDirectoryFileName(const QString &file) const
0268 {
0269     return VcardDirResourceSettings::self()->path() + QLatin1Char('/') + file;
0270 }
0271 
0272 void VCardDirResource::initializeVCardDirectory() const
0273 {
0274     QDir dir(vCardDirectoryName());
0275 
0276     // if folder does not exists, create it
0277     if (!dir.exists()) {
0278         if (!QDir::root().mkpath(dir.absolutePath())) {
0279             qCritical() << "Failed to create vCard directory" << dir.absolutePath();
0280         } else {
0281             qDebug() << "vCard directory " << dir.absolutePath() << "successfuly created";
0282         }
0283     }
0284 
0285     // check whether warning file is in place...
0286     QFile file(dir.absolutePath() + QStringLiteral("/WARNING_README.txt"));
0287     if (!file.exists()) {
0288         // ... if not, create it
0289         file.open(QIODevice::WriteOnly);
0290         file.write(
0291             "Important Warning!!!\n\n"
0292             "Don't create or copy vCards inside this folder manually, they are managed by the Akonadi framework!\n");
0293         file.close();
0294     }
0295 }
0296 
0297 AKONADI_RESOURCE_MAIN(VCardDirResource)
0298 
0299 #include "moc_vcarddirresource.cpp"