2
* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3
* Copyright (c) 2010 Tobias Koenig <tokoe@kdab.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
#include "aclmanager.h"
22
#include "aclentrydialog_p.h"
23
#include "aclutils_p.h"
24
#include "imapaclattribute.h"
25
#include "imapsettings.h"
28
#include <Akonadi/Collection>
29
#include <Akonadi/CollectionFetchJob>
30
#include <Akonadi/CollectionModifyJob>
31
#include <Akonadi/Contact/ContactGroupExpandJob>
32
#include <Akonadi/Contact/ContactGroupSearchJob>
34
#include <KPIMUtils/Email>
37
#include <KMessageBox>
39
#include <QAbstractListModel>
41
#include <QItemSelectionModel>
43
using namespace MailCommon;
45
class AclModel : public QAbstractListModel
49
UserIdRole = Qt::UserRole + 1,
54
AclModel( QObject *parent = 0 )
55
: QAbstractListModel( parent )
59
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
61
if ( index.row() < 0 || index.row() >= mRights.count() ) {
65
const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at( index.row() );
68
return QString::fromLatin1( "%1: %2" ).
69
arg( QString::fromLatin1( right.first ) ).
70
arg( AclUtils::permissionsToUserString( right.second ) );
73
return QString::fromLatin1( right.first );
76
return QVariant( static_cast<int>( right.second ) );
78
case PermissionsTextRole:
79
return AclUtils::permissionsToUserString( right.second );
89
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
91
if ( index.row() < 0 || index.row() >= mRights.count() ) {
95
QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[ index.row() ];
98
right.first = value.toByteArray();
99
emit dataChanged( index, index );
102
case PermissionsRole:
103
right.second = static_cast<KIMAP::Acl::Rights>( value.toInt() );
104
emit dataChanged( index, index );
115
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const
117
if ( parent.isValid() ) {
120
return mRights.count();
124
void setRights( const QMap<QByteArray, KIMAP::Acl::Rights> &rights )
128
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
129
while ( it.hasNext() ) {
131
mRights.append( qMakePair( it.key(), it.value() ) );
137
QMap<QByteArray, KIMAP::Acl::Rights> rights() const
139
QMap<QByteArray, KIMAP::Acl::Rights> result;
141
typedef QPair<QByteArray, KIMAP::Acl::Rights> RightPair;
142
foreach ( const RightPair &right, mRights ) {
143
result.insert( right.first, right.second );
150
virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() )
152
beginInsertRows( parent, row, row + count - 1 );
153
for ( int i = 0; i < count; ++i ) {
154
mRights.insert( row, qMakePair( QByteArray(), KIMAP::Acl::Rights() ) );
161
virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() )
163
beginRemoveRows( parent, row, row + count - 1 );
164
for ( int i = 0; i < count; ++i ) {
165
mRights.remove( row, count );
173
QVector<QPair<QByteArray, KIMAP::Acl::Rights> > mRights;
176
class MailCommon::AclManager::Private
179
Private( AclManager *qq )
183
mAddAction = new QAction( i18n( "Add Entry..." ), q );
184
q->connect( mAddAction, SIGNAL(triggered(bool)),
187
mEditAction = new QAction( i18n( "Edit Entry..." ), q );
188
mEditAction->setEnabled( false );
189
q->connect( mEditAction, SIGNAL(triggered(bool)),
190
q, SLOT(editAcl()) );
192
mDeleteAction = new QAction( i18n( "Remove Entry" ), q );
193
mDeleteAction->setEnabled( false );
194
q->connect( mDeleteAction, SIGNAL(triggered(bool)),
195
q, SLOT(deleteAcl()) );
197
mModel = new AclModel( q );
199
mSelectionModel = new QItemSelectionModel( mModel );
200
q->connect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
201
q, SLOT(selectionChanged()) );
208
void selectionChanged()
210
const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
212
bool canAdmin = ( mUserRights & KIMAP::Acl::Admin );
214
bool canAdminThisItem = canAdmin;
215
if ( canAdmin && itemSelected ) {
216
const QModelIndex index = mSelectionModel->selectedIndexes().first();
217
const QString userId = index.data( AclModel::UserIdRole ).toString();
218
const KIMAP::Acl::Rights rights =
219
static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
221
// Don't allow users to remove their own admin permissions - there's no way back
222
if ( mImapUserName == userId && ( rights & KIMAP::Acl::Admin ) ) {
223
canAdminThisItem = false;
227
mAddAction->setEnabled( canAdmin );
228
mEditAction->setEnabled( itemSelected && canAdminThisItem );
229
mDeleteAction->setEnabled( itemSelected && canAdminThisItem );
235
dlg.setCaption( i18n( "Add ACL" ) );
241
if ( mModel->insertRow( mModel->rowCount() ) ) {
242
const QModelIndex index = mModel->index( mModel->rowCount() - 1, 0 );
243
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
244
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
252
const QModelIndex index = mSelectionModel->selectedIndexes().first();
253
const QString userId = index.data( AclModel::UserIdRole ).toString();
254
const KIMAP::Acl::Rights permissions =
255
static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
258
dlg.setCaption( i18n( "Edit ACL" ) );
259
dlg.setUserId( userId );
260
dlg.setPermissions( permissions );
266
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
267
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
273
const QModelIndex index = mSelectionModel->selectedIndexes().first();
274
const QString userId = index.data( AclModel::UserIdRole ).toString();
276
if ( mImapUserName == userId ) {
277
if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel(
279
i18n( "Do you really want to remove your own permissions for this folder? "
280
"You will not be able to access it afterwards." ),
285
mModel->removeRow( index.row(), QModelIndex() );
290
* We call this method if our first try to get the ACLs for the user fails.
291
* That's the case if the ACLs use a different user id than the login name.
294
* login: testuser acls: testuser@mydomain.org
295
* login: testuser@mydomain.org acls: testuser
297
static QString guessUserName( const QString &loginName, const QString &serverName )
299
if ( loginName.contains( QLatin1Char( '@' ) ) ) {
300
// strip of the domain part and use user name only
301
return loginName.left( loginName.indexOf( QLatin1Char( '@' ) ) );
303
int pos = serverName.lastIndexOf( QLatin1Char( '.' ) );
304
if ( pos == -1 ) { // no qualified domain name, only hostname
305
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
308
pos = serverName.lastIndexOf( QLatin1Char( '.' ), pos - 1 );
309
if ( pos == -1 ) { // a simple domain name e.g. mydomain.org
310
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
312
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName.mid( pos + 1 ) );
317
void setCollection( const Akonadi::Collection &collection )
319
mCollection = collection;
322
const MailCommon::ImapAclAttribute *attribute =
323
collection.attribute<MailCommon::ImapAclAttribute>();
324
const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
326
OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface =
327
MailCommon::Util::createImapSettingsInterface( collection.resource() );
331
if ( imapSettingsInterface->isValid() ) {
332
QDBusReply<QString> reply = imapSettingsInterface->userName();
333
if ( reply.isValid() ) {
337
reply = imapSettingsInterface->imapServer();
338
if ( reply.isValid() ) {
343
delete imapSettingsInterface;
345
mImapUserName = loginName;
346
if ( !rights.contains( loginName.toUtf8() ) ) {
347
const QString guessedUserName = guessUserName( loginName, serverName );
348
if ( rights.contains( guessedUserName.toUtf8() ) ) {
349
mImapUserName = guessedUserName;
353
mUserRights = rights[ mImapUserName.toUtf8() ];
355
mModel->setRights( rights );
361
QItemSelectionModel *mSelectionModel;
363
QAction *mEditAction;
364
QAction *mDeleteAction;
366
Akonadi::Collection mCollection;
367
QString mImapUserName;
368
KIMAP::Acl::Rights mUserRights;
372
AclManager::AclManager( QObject *parent )
373
: QObject( parent ), d( new Private( this ) )
377
AclManager::~AclManager()
382
void AclManager::setCollection( const Akonadi::Collection &collection )
384
d->setCollection( collection );
385
emit collectionChanged( d->mCollection );
388
Akonadi::Collection AclManager::collection() const
390
return d->mCollection;
393
QAbstractItemModel *AclManager::model() const
398
QItemSelectionModel *AclManager::selectionModel() const
400
return d->mSelectionModel;
403
QAction *AclManager::addAction() const
405
return d->mAddAction;
408
QAction *AclManager::editAction() const
410
return d->mEditAction;
413
QAction *AclManager::deleteAction() const
415
return d->mDeleteAction;
418
void AclManager::save()
420
if ( !d->mCollection.isValid() || !d->mChanged ) {
424
// refresh the collection, it might be outdated in the meantime
425
Akonadi::CollectionFetchJob *job =
426
new Akonadi::CollectionFetchJob( d->mCollection, Akonadi::CollectionFetchJob::Base );
427
if ( !job->exec() ) {
431
if ( job->collections().isEmpty() ) {
435
d->mCollection = job->collections().first();
439
MailCommon::ImapAclAttribute *attribute =
440
d->mCollection.attribute<MailCommon::ImapAclAttribute>();
442
QMap<QByteArray, KIMAP::Acl::Rights> newRights;
444
const QMap<QByteArray, KIMAP::Acl::Rights> rights = d->mModel->rights();
445
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
446
while ( it.hasNext() ) {
449
// we can use job->exec() here, it is not a hot path
450
Akonadi::ContactGroupSearchJob *searchJob = new Akonadi::ContactGroupSearchJob( this );
451
searchJob->setQuery( Akonadi::ContactGroupSearchJob::Name, it.key() );
452
searchJob->setLimit( 1 );
453
if ( !searchJob->exec() ) {
457
if ( !searchJob->contactGroups().isEmpty() ) { // it has been a distribution list
458
Akonadi::ContactGroupExpandJob *expandJob =
459
new Akonadi::ContactGroupExpandJob( searchJob->contactGroups().first(), this );
460
if ( expandJob->exec() ) {
461
foreach ( const KABC::Addressee &contact, expandJob->contacts() ) {
462
const QByteArray rawEmail =
463
KPIMUtils::extractEmailAddress( contact.preferredEmail().toUtf8() );
464
if ( !rawEmail.isEmpty() ) {
465
newRights[ rawEmail ] = it.value();
469
} else { // it has been a normal contact
470
const QByteArray rawEmail = KPIMUtils::extractEmailAddress( it.key() );
471
if ( !rawEmail.isEmpty() ) {
472
newRights[ rawEmail ] = it.value();
477
attribute->setRights( newRights );
479
new Akonadi::CollectionModifyJob( d->mCollection );
482
#include "aclmanager.moc"