Pimcommon

aclmodifyjob.cpp
1/*
2 SPDX-FileCopyrightText: 2016-2024 Laurent Montel <montel@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5
6*/
7
8#include "aclmodifyjob.h"
9using namespace Qt::Literals::StringLiterals;
10
11#include "aclutils_p.h"
12#include "imapresourcesettings.h"
13#include "pimcommonakonadi_debug.h"
14#include "util/pimutil.h"
15#include <Akonadi/ServerManager>
16#include <KEmailAddress>
17
18#include "imapaclattribute.h"
19
20#include <Akonadi/CollectionFetchJob>
21#include <Akonadi/CollectionFetchScope>
22#include <Akonadi/CollectionModifyJob>
23#include <Akonadi/ContactGroupExpandJob>
24#include <Akonadi/ContactGroupSearchJob>
25#include <KContacts/Addressee>
26#include <KLocalizedString>
27#include <KMessageBox>
28#include <QDBusInterface>
29#include <QDBusReply>
30
31using namespace PimCommon;
32// #define SEARCHCONTACT_AKONADI 1
33
34AclModifyJob::AclModifyJob(QObject *parent)
35 : QObject(parent)
36{
37 connect(this, &AclModifyJob::searchContactDone, this, &AclModifyJob::slotModifyAcl);
38 connect(this, &AclModifyJob::searchNextContact, this, &AclModifyJob::searchContact);
39}
40
41AclModifyJob::~AclModifyJob() = default;
42
43void AclModifyJob::searchContact()
44{
46 if (mIt != itEnd) {
47 auto searchJob = new Akonadi::ContactGroupSearchJob(this);
49 searchJob->setLimit(1);
50 connect(searchJob, &Akonadi::ContactGroupSearchJob::result, this, &AclModifyJob::slotGroupSearchResult);
51 } else {
52 Q_EMIT searchContactDone();
53 }
54}
55
56void AclModifyJob::slotGroupSearchResult(KJob *job)
57{
59 if (!searchJob->contactGroups().isEmpty()) { // it has been a distribution list
60 auto expandJob = new Akonadi::ContactGroupExpandJob(searchJob->contactGroups().at(0), this);
61 if (expandJob->exec()) {
62 const KContacts::Addressee::List lstContacts = expandJob->contacts();
63 for (const KContacts::Addressee &contact : lstContacts) {
64 const QByteArray rawEmail = KEmailAddress::extractEmailAddress(contact.preferredEmail().toUtf8());
65 if (!rawEmail.isEmpty()) {
66 mNewRight[rawEmail] = mIt.value();
67 }
68 }
69 }
70 } else { // it has been a normal contact
72 if (!rawEmail.isEmpty()) {
73 mNewRight[rawEmail] = mIt.value();
74 }
75 }
76 ++mIt;
77 Q_EMIT searchNextContact();
78}
79
80void AclModifyJob::start()
81{
82 if (!mTopLevelCollection.isValid()) {
84 return;
85 }
86#ifdef SEARCHCONTACT_AKONADI
87 mIt = mCurrentRight.cbegin();
88 searchContact();
89#else
90 const QMap<QByteArray, KIMAP::Acl::Rights> rights = mCurrentRight;
93 for (; it != itEnd; ++it) {
94 const QByteArray rawEmail = KEmailAddress::extractEmailAddress(it.key());
95 if (!rawEmail.isEmpty()) {
96 mNewRight[rawEmail] = it.value();
97 }
98 }
99 slotModifyAcl();
100#endif
101}
102
103void AclModifyJob::slotModifyAcl()
104{
105 mCurrentIndex = 0;
106 if (mRecursive) {
107 auto job = new Akonadi::CollectionFetchJob(mTopLevelCollection, Akonadi::CollectionFetchJob::Recursive, this);
108 job->fetchScope().setAncestorRetrieval(Akonadi::CollectionFetchScope::All);
109 connect(job, &Akonadi::CollectionFetchJob::finished, this, [this](KJob *job) {
110 if (job->error()) {
111 qCWarning(PIMCOMMONAKONADI_LOG) << job->errorString();
112 slotFetchCollectionFailed();
113 } else {
114 auto fetch = static_cast<Akonadi::CollectionFetchJob *>(job);
115 slotFetchCollectionFinished(fetch->collections());
116 }
117 });
118 } else {
119 changeAcl(mTopLevelCollection);
120 }
121}
122
123bool AclModifyJob::canAdministrate(const PimCommon::ImapAclAttribute *attribute, const Akonadi::Collection &collection) const
124{
125 if (!attribute || !collection.isValid()) {
126 return false;
127 }
128 const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
129
130 QString resource = collection.resource();
131 if (resource.contains("akonadi_kolabproxy_resource"_L1)) {
132 const QString basename = Akonadi::ServerManager::agentServiceName(Akonadi::ServerManager::Agent, QStringLiteral("akonadi_kolabproxy_resource"));
133 QDBusInterface interface(basename, QStringLiteral("/KolabProxy"));
134 if (interface.isValid()) {
135 QDBusReply<QString> reply = interface.call(QStringLiteral("imapResourceForCollection"), collection.remoteId().toLongLong());
136 if (reply.isValid()) {
137 resource = reply;
138 }
139 }
140 }
141 OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface = PimCommon::Util::createImapSettingsInterface(resource);
142
143 QString loginName;
144 QString serverName;
145 if (imapSettingsInterface->isValid()) {
146 QDBusReply<QString> reply = imapSettingsInterface->userName();
147 if (reply.isValid()) {
148 loginName = reply;
149 }
150
151 reply = imapSettingsInterface->imapServer();
152 if (reply.isValid()) {
153 serverName = reply;
154 }
155 } else {
156 qCDebug(PIMCOMMONAKONADI_LOG) << " collection has not imap as resources: " << collection.resource();
157 }
158 delete imapSettingsInterface;
159
160 QString imapUserName = loginName;
161 if (!rights.contains(loginName.toUtf8())) {
162 const QString guessedUserName = AclUtils::guessUserName(loginName, serverName);
163 if (rights.contains(guessedUserName.toUtf8())) {
164 imapUserName = guessedUserName;
165 }
166 }
167 return rights[imapUserName.toUtf8()] & KIMAP::Acl::Admin;
168}
169
170void AclModifyJob::setCurrentRight(const QMap<QByteArray, KIMAP::Acl::Rights> &currentRight)
171{
172 mCurrentRight = currentRight;
173}
174
175void AclModifyJob::changeAcl(const Akonadi::Collection &collection)
176{
177 if (collection.hasAttribute<PimCommon::ImapAclAttribute>()) {
178 Akonadi::Collection mutableCollection = collection;
179 auto attribute = mutableCollection.attribute<PimCommon::ImapAclAttribute>();
180 if (canAdministrate(attribute, mutableCollection)) {
181 attribute->setRights(mNewRight);
182 auto modifyJob = new Akonadi::CollectionModifyJob(mutableCollection);
183 connect(modifyJob, &KJob::result, this, &AclModifyJob::slotModifyDone);
184 }
185 } else {
186 checkNewCollection();
187 }
188}
189
190void AclModifyJob::checkNewCollection()
191{
192 mCurrentIndex++;
193 if (mCurrentIndex < mRecursiveCollection.count()) {
194 changeAcl(mRecursiveCollection.at(mCurrentIndex));
195 } else {
196 deleteLater();
197 }
198}
199
200void AclModifyJob::slotModifyDone(KJob *job)
201{
202 if (job->error()) {
203 qCDebug(PIMCOMMONAKONADI_LOG) << " Error during modify collection " << job->errorString();
204 }
205 checkNewCollection();
206}
207
208void AclModifyJob::slotFetchCollectionFinished(const Akonadi::Collection::List &collectionList)
209{
210 QStringList folderNames;
211 for (const Akonadi::Collection &col : collectionList) {
212 if (col.hasAttribute<PimCommon::ImapAclAttribute>()) {
213 const auto attribute = col.attribute<PimCommon::ImapAclAttribute>();
214 if (canAdministrate(attribute, col)) {
216 bool parentFound;
217 Akonadi::Collection cur = col;
218 do {
219 parentFound = false;
220 // parentCollection() only returns the ID, but we have the
221 // collection in our list already because we've recursively
222 // fetched. So look it up in our list.
223 for (const Akonadi::Collection &it : collectionList) {
224 if (it.id() == cur.id()) {
225 fullName = "/"_L1 + it.displayName() + fullName;
226 parentFound = true;
227 cur = cur.parentCollection();
228 break;
229 }
230 }
231 } while (parentFound);
232 // Remove leading slash
233 folderNames << fullName.right(fullName.size() - 1);
234 } else {
235 qCDebug(PIMCOMMONAKONADI_LOG) << "AclModifyJob: No rights to administer " << col.name();
236 }
237 } else {
238 qCDebug(PIMCOMMONAKONADI_LOG) << "AclModifyJob: Collection " << col.name() << "has no ACL.";
239 }
240 }
241 folderNames.sort();
243 i18n("Do you really want to apply the folder's permissions to these subfolders?"),
244 folderNames,
245 i18nc("@title:window", "Apply Permissions"))
247 deleteLater();
248 qCDebug(PIMCOMMONAKONADI_LOG) << "AclModifyJob: User canceled .";
249 return;
250 }
251 mRecursiveCollection = collectionList;
252 changeAcl(mTopLevelCollection);
253}
254
255void AclModifyJob::slotFetchCollectionFailed()
256{
257 qCDebug(PIMCOMMONAKONADI_LOG) << "fetch collection failed";
258 deleteLater();
259}
260
261void AclModifyJob::setTopLevelCollection(const Akonadi::Collection &topLevelCollection)
262{
263 mTopLevelCollection = topLevelCollection;
264}
265
266void AclModifyJob::setRecursive(bool recursive)
267{
268 mRecursive = recursive;
269}
270
271#include "moc_aclmodifyjob.cpp"
QString resource() const
bool isValid() const
const T * attribute() const
Collection & parentCollection()
bool hasAttribute() const
QString remoteId() const
static QString agentServiceName(ServiceAgentType agentType, const QString &identifier)
AddresseeList List
virtual QString errorString() const
int error() const
void result(KJob *job)
void finished(KJob *job)
The ImapAclAttribute class.
KCODECS_EXPORT QByteArray extractEmailAddress(const QByteArray &address)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString fullName(const PartType &type)
ButtonCode warningContinueCancelList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
folderdialogacltab.h
bool isEmpty() const const
bool isValid() const const
const_reference at(qsizetype i) const const
qsizetype count() const const
const_iterator cbegin() const const
const_iterator cend() const const
bool contains(const Key &key) const const
Key key(const T &value, const Key &defaultKey) const const
T value(const Key &key, const T &defaultValue) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
T qobject_cast(QObject *object)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
QString fromLatin1(QByteArrayView str)
QString right(qsizetype n) const const
qsizetype size() const const
qlonglong toLongLong(bool *ok, int base) const const
QByteArray toUtf8() const const
void sort(Qt::CaseSensitivity cs)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:39:22 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.