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.h>
29
#include <akonadi/collectionfetchjob.h>
30
#include <akonadi/collectionmodifyjob.h>
31
#include <akonadi/contact/contactgroupexpandjob.h>
32
#include <akonadi/contact/contactgroupsearchjob.h>
34
#include <kmessagebox.h>
35
#include <kpimutils/email.h>
37
#include <QtCore/QAbstractListModel>
38
#include <QtGui/QAction>
39
#include <QtGui/QItemSelectionModel>
41
using namespace MailCommon;
43
class AclModel : public QAbstractListModel
47
UserIdRole = Qt::UserRole + 1,
52
AclModel( QObject *parent = 0 )
53
: QAbstractListModel( parent )
57
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
59
if ( index.row() < 0 || index.row() >= mRights.count() )
62
const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at( index.row() );
65
return QString::fromLatin1( "%1: %2" ).arg( QString::fromLatin1( right.first ) )
66
.arg( AclUtils::permissionsToUserString( right.second ) );
69
return QString::fromLatin1( right.first );
72
return QVariant( static_cast<int>( right.second ) );
74
case PermissionsTextRole:
75
return AclUtils::permissionsToUserString( right.second );
85
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
87
if ( index.row() < 0 || index.row() >= mRights.count() )
90
QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[ index.row() ];
93
right.first = value.toByteArray();
94
emit dataChanged( index, index );
98
right.second = static_cast<KIMAP::Acl::Rights>( value.toInt() );
99
emit dataChanged( index, index );
110
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const
112
if ( parent.isValid() )
115
return mRights.count();
118
void setRights( const QMap<QByteArray, KIMAP::Acl::Rights> &rights )
122
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
123
while ( it.hasNext() ) {
125
mRights.append( qMakePair( it.key(), it.value() ) );
131
QMap<QByteArray, KIMAP::Acl::Rights> rights() const
133
QMap<QByteArray, KIMAP::Acl::Rights> result;
135
typedef QPair<QByteArray, KIMAP::Acl::Rights> RightPair;
136
foreach ( const RightPair &right, mRights )
137
result.insert( right.first, right.second );
143
virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() )
145
beginInsertRows( parent, row, row + count - 1 );
146
for ( int i = 0; i < count; ++i )
147
mRights.insert( row, qMakePair( QByteArray(), KIMAP::Acl::Rights() ) );
153
virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() )
155
beginRemoveRows( parent, row, row + count - 1 );
156
for ( int i = 0; i < count; ++i )
157
mRights.remove( row, count );
164
QVector<QPair<QByteArray, KIMAP::Acl::Rights> > mRights;
167
class MailCommon::AclManager::Private
170
Private( AclManager *qq )
174
mAddAction = new QAction( i18n( "Add Entry..." ), q );
175
q->connect( mAddAction, SIGNAL( triggered( bool ) ),
176
q, SLOT( addAcl() ) );
178
mEditAction = new QAction( i18n( "Edit Entry..." ), q );
179
mEditAction->setEnabled( false );
180
q->connect( mEditAction, SIGNAL( triggered( bool ) ),
181
q, SLOT( editAcl() ) );
183
mDeleteAction = new QAction( i18n( "Remove Entry" ), q );
184
mDeleteAction->setEnabled( false );
185
q->connect( mDeleteAction, SIGNAL( triggered( bool ) ),
186
q, SLOT( deleteAcl() ) );
188
mModel = new AclModel( q );
190
mSelectionModel = new QItemSelectionModel( mModel );
191
q->connect( mSelectionModel, SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ),
192
q, SLOT( selectionChanged() ) );
199
void selectionChanged()
201
const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
203
bool canAdmin = (mUserRights & KIMAP::Acl::Admin);
205
bool canAdminThisItem = canAdmin;
206
if ( canAdmin && itemSelected ) {
207
const QModelIndex index = mSelectionModel->selectedIndexes().first();
208
const QString userId = index.data( AclModel::UserIdRole ).toString();
209
const KIMAP::Acl::Rights rights = static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
211
// Don't allow users to remove their own admin permissions - there's no way back
212
if ( mImapUserName == userId && (rights & KIMAP::Acl::Admin) )
213
canAdminThisItem = false;
216
mAddAction->setEnabled( canAdmin );
217
mEditAction->setEnabled( itemSelected && canAdminThisItem );
218
mDeleteAction->setEnabled( itemSelected && canAdminThisItem );
224
dlg.setCaption( i18n( "Add ACL" ) );
229
if ( mModel->insertRow( mModel->rowCount() ) ) {
230
const QModelIndex index = mModel->index( mModel->rowCount() - 1, 0 );
231
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
232
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
240
const QModelIndex index = mSelectionModel->selectedIndexes().first();
241
const QString userId = index.data( AclModel::UserIdRole ).toString();
242
const KIMAP::Acl::Rights permissions = static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
245
dlg.setCaption( i18n( "Edit ACL" ) );
246
dlg.setUserId( userId );
247
dlg.setPermissions( permissions );
252
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
253
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
259
const QModelIndex index = mSelectionModel->selectedIndexes().first();
260
const QString userId = index.data( AclModel::UserIdRole ).toString();
262
if ( mImapUserName == userId ) {
263
if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel( 0,
264
i18n( "Do you really want to remove your own permissions for this folder? You will not be able to access it afterwards." ), i18n( "Remove" ) ) )
268
mModel->removeRow( index.row(), QModelIndex() );
273
* We call this method if our first try to get the ACLs for the user fails.
274
* That's the case if the ACLs use a different user id than the login name.
277
* login: testuser acls: testuser@mydomain.org
278
* login: testuser@mydomain.org acls: testuser
280
static QString guessUserName( const QString &loginName, const QString &serverName )
282
if ( loginName.contains( QLatin1Char( '@' ) ) ) {
283
// strip of the domain part and use user name only
284
return loginName.left( loginName.indexOf( QLatin1Char( '@' ) ) );
286
int pos = serverName.lastIndexOf( QLatin1Char( '.' ) );
287
if ( pos == -1 ) // no qualified domain name, only hostname
288
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
290
pos = serverName.lastIndexOf( QLatin1Char( '.' ), pos - 1 );
291
if ( pos == -1 ) // a simple domain name e.g. mydomain.org
292
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
294
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName.mid( pos + 1 ) );
298
void setCollection( const Akonadi::Collection &collection )
300
mCollection = collection;
303
const MailCommon::ImapAclAttribute *attribute = collection.attribute<MailCommon::ImapAclAttribute>();
304
const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
306
OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface = MailCommon::Util::createImapSettingsInterface( collection.resource() );
310
if ( imapSettingsInterface->isValid() ) {
311
QDBusReply<QString> reply = imapSettingsInterface->userName();
312
if ( reply.isValid() )
315
reply = imapSettingsInterface->imapServer();
316
if ( reply.isValid() ) {
321
delete imapSettingsInterface;
323
mImapUserName = loginName;
324
if ( !rights.contains( loginName.toUtf8() ) ) {
325
const QString guessedUserName = guessUserName( loginName, serverName );
326
if ( rights.contains( guessedUserName.toUtf8() ) )
327
mImapUserName = guessedUserName;
330
mUserRights = rights[ mImapUserName.toUtf8() ];
332
mModel->setRights( rights );
338
QItemSelectionModel *mSelectionModel;
340
QAction *mEditAction;
341
QAction *mDeleteAction;
343
Akonadi::Collection mCollection;
344
QString mImapUserName;
345
KIMAP::Acl::Rights mUserRights;
349
AclManager::AclManager( QObject *parent )
350
: QObject( parent ), d( new Private( this ) )
354
AclManager::~AclManager()
359
void AclManager::setCollection( const Akonadi::Collection &collection )
361
d->setCollection( collection );
362
emit collectionChanged( d->mCollection );
365
Akonadi::Collection AclManager::collection() const
367
return d->mCollection;
370
QAbstractItemModel* AclManager::model() const
375
QItemSelectionModel* AclManager::selectionModel() const
377
return d->mSelectionModel;
380
QAction* AclManager::addAction() const
382
return d->mAddAction;
385
QAction* AclManager::editAction() const
387
return d->mEditAction;
390
QAction* AclManager::deleteAction() const
392
return d->mDeleteAction;
395
void AclManager::save()
397
if ( !d->mCollection.isValid() || !d->mChanged )
400
// refresh the collection, it might be outdated in the meantime
401
Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob( d->mCollection, Akonadi::CollectionFetchJob::Base );
405
if ( job->collections().isEmpty() )
408
d->mCollection = job->collections().first();
412
MailCommon::ImapAclAttribute *attribute = d->mCollection.attribute<MailCommon::ImapAclAttribute>();
413
QMap<QByteArray, KIMAP::Acl::Rights> newRights;
415
const QMap<QByteArray, KIMAP::Acl::Rights> rights = d->mModel->rights();
416
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
417
while ( it.hasNext() ) {
420
// we can use job->exec() here, it is not a hot path
421
Akonadi::ContactGroupSearchJob *searchJob = new Akonadi::ContactGroupSearchJob( this );
422
searchJob->setQuery( Akonadi::ContactGroupSearchJob::Name, it.key() );
423
searchJob->setLimit( 1 );
424
if ( !searchJob->exec() ) {
428
if ( !searchJob->contactGroups().isEmpty() ) { // it has been a distribution list
429
Akonadi::ContactGroupExpandJob *expandJob = new Akonadi::ContactGroupExpandJob( searchJob->contactGroups().first(), this );
430
if ( expandJob->exec() ) {
431
foreach ( const KABC::Addressee &contact, expandJob->contacts() ) {
432
const QByteArray rawEmail = KPIMUtils::extractEmailAddress( contact.preferredEmail().toUtf8() );
433
if ( !rawEmail.isEmpty() )
434
newRights[ rawEmail ] = it.value();
437
} else { // it has been a normal contact
438
const QByteArray rawEmail = KPIMUtils::extractEmailAddress( it.key() );
439
if ( !rawEmail.isEmpty() )
440
newRights[ rawEmail ] = it.value();
444
attribute->setRights( newRights );
446
new Akonadi::CollectionModifyJob( d->mCollection );
449
#include "aclmanager.moc"