mirror of
https://bitbucket.org/smil3y/kde-playground.git
synced 2025-02-24 10:52:52 +00:00
487 lines
15 KiB
C++
487 lines
15 KiB
C++
/*
|
|
* Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
|
|
* Copyright (c) 2010 Tobias Koenig <tokoe@kdab.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include "aclmanager.h"
|
|
|
|
#include "aclentrydialog_p.h"
|
|
#include "aclutils_p.h"
|
|
#include "imapaclattribute.h"
|
|
#include "imapresourcesettings.h"
|
|
#include "pimutil.h"
|
|
|
|
#include <Akonadi/Collection>
|
|
#include <Akonadi/CollectionFetchJob>
|
|
#include <Akonadi/CollectionModifyJob>
|
|
#include <Akonadi/Contact/ContactGroupExpandJob>
|
|
#include <Akonadi/Contact/ContactGroupSearchJob>
|
|
|
|
#include <KPIMUtils/Email>
|
|
|
|
#include <KLocalizedString>
|
|
#include <KMessageBox>
|
|
|
|
#include <QAbstractListModel>
|
|
#include <QAction>
|
|
#include <QItemSelectionModel>
|
|
|
|
using namespace PimCommon;
|
|
|
|
class AclModel : public QAbstractListModel
|
|
{
|
|
public:
|
|
enum Role {
|
|
UserIdRole = Qt::UserRole + 1,
|
|
PermissionsRole,
|
|
PermissionsTextRole
|
|
};
|
|
|
|
AclModel( QObject *parent = 0 )
|
|
: QAbstractListModel( parent )
|
|
{
|
|
}
|
|
|
|
virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
|
|
{
|
|
if ( index.row() < 0 || index.row() >= mRights.count() ) {
|
|
return QVariant();
|
|
}
|
|
|
|
const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at( index.row() );
|
|
switch ( role ) {
|
|
case Qt::DisplayRole:
|
|
return QString::fromLatin1( "%1: %2" ).
|
|
arg( QString::fromLatin1( right.first ) ).
|
|
arg( AclUtils::permissionsToUserString( right.second ) );
|
|
case UserIdRole:
|
|
return QString::fromLatin1( right.first );
|
|
case PermissionsRole:
|
|
return QVariant( static_cast<int>( right.second ) );
|
|
case PermissionsTextRole:
|
|
return AclUtils::permissionsToUserString( right.second );
|
|
default:
|
|
return QVariant();
|
|
}
|
|
|
|
}
|
|
|
|
virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
|
|
{
|
|
if ( index.row() < 0 || index.row() >= mRights.count() ) {
|
|
return false;
|
|
}
|
|
|
|
QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[ index.row() ];
|
|
switch ( role ) {
|
|
case UserIdRole:
|
|
right.first = value.toByteArray();
|
|
emit dataChanged( index, index );
|
|
return true;
|
|
break;
|
|
case PermissionsRole:
|
|
right.second = static_cast<KIMAP::Acl::Rights>( value.toInt() );
|
|
emit dataChanged( index, index );
|
|
return true;
|
|
break;
|
|
default:
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const
|
|
{
|
|
if ( parent.isValid() ) {
|
|
return 0;
|
|
} else {
|
|
return mRights.count();
|
|
}
|
|
}
|
|
|
|
void setRights( const QMap<QByteArray, KIMAP::Acl::Rights> &rights )
|
|
{
|
|
mRights.clear();
|
|
|
|
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
|
|
while ( it.hasNext() ) {
|
|
it.next();
|
|
mRights.append( qMakePair( it.key(), it.value() ) );
|
|
}
|
|
|
|
reset();
|
|
}
|
|
|
|
QMap<QByteArray, KIMAP::Acl::Rights> rights() const
|
|
{
|
|
QMap<QByteArray, KIMAP::Acl::Rights> result;
|
|
|
|
typedef QPair<QByteArray, KIMAP::Acl::Rights> RightPair;
|
|
foreach ( const RightPair &right, mRights ) {
|
|
result.insert( right.first, right.second );
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected:
|
|
virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() )
|
|
{
|
|
beginInsertRows( parent, row, row + count - 1 );
|
|
for ( int i = 0; i < count; ++i ) {
|
|
mRights.insert( row, qMakePair( QByteArray(), KIMAP::Acl::Rights() ) );
|
|
}
|
|
endInsertRows();
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() )
|
|
{
|
|
beginRemoveRows( parent, row, row + count - 1 );
|
|
for ( int i = 0; i < count; ++i ) {
|
|
mRights.remove( row, count );
|
|
}
|
|
endRemoveRows();
|
|
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
QVector<QPair<QByteArray, KIMAP::Acl::Rights> > mRights;
|
|
};
|
|
|
|
class PimCommon::AclManager::Private
|
|
{
|
|
public:
|
|
Private( AclManager *qq )
|
|
: q( qq ),
|
|
mChanged( false )
|
|
{
|
|
mAddAction = new QAction( i18n( "Add Entry..." ), q );
|
|
q->connect( mAddAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(addAcl()) );
|
|
|
|
mEditAction = new QAction( i18n( "Edit Entry..." ), q );
|
|
mEditAction->setEnabled( false );
|
|
q->connect( mEditAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(editAcl()) );
|
|
|
|
mDeleteAction = new QAction( i18n( "Remove Entry" ), q );
|
|
mDeleteAction->setEnabled( false );
|
|
q->connect( mDeleteAction, SIGNAL(triggered(bool)),
|
|
q, SLOT(deleteAcl()) );
|
|
|
|
mModel = new AclModel( q );
|
|
|
|
mSelectionModel = new QItemSelectionModel( mModel );
|
|
q->connect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
q, SLOT(selectionChanged()) );
|
|
}
|
|
|
|
~Private()
|
|
{
|
|
}
|
|
|
|
void selectionChanged()
|
|
{
|
|
const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
|
|
|
|
bool canAdmin = ( mUserRights & KIMAP::Acl::Admin );
|
|
|
|
bool canAdminThisItem = canAdmin;
|
|
if ( canAdmin && itemSelected ) {
|
|
const QModelIndex index = mSelectionModel->selectedIndexes().first();
|
|
const QString userId = index.data( AclModel::UserIdRole ).toString();
|
|
const KIMAP::Acl::Rights rights =
|
|
static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
|
|
|
|
// Don't allow users to remove their own admin permissions - there's no way back
|
|
if ( mImapUserName == userId && ( rights & KIMAP::Acl::Admin ) ) {
|
|
canAdminThisItem = false;
|
|
}
|
|
}
|
|
|
|
mAddAction->setEnabled( canAdmin );
|
|
mEditAction->setEnabled( itemSelected && canAdminThisItem );
|
|
mDeleteAction->setEnabled( itemSelected && canAdminThisItem );
|
|
}
|
|
|
|
void addAcl()
|
|
{
|
|
AclEntryDialog dlg;
|
|
dlg.setCaption( i18n( "Add ACL" ) );
|
|
|
|
if ( !dlg.exec() ) {
|
|
return;
|
|
}
|
|
|
|
if ( mModel->insertRow( mModel->rowCount() ) ) {
|
|
const QModelIndex index = mModel->index( mModel->rowCount() - 1, 0 );
|
|
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
|
|
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
|
|
|
|
mChanged = true;
|
|
}
|
|
}
|
|
|
|
void editAcl()
|
|
{
|
|
const QModelIndex index = mSelectionModel->selectedIndexes().first();
|
|
const QString userId = index.data( AclModel::UserIdRole ).toString();
|
|
const KIMAP::Acl::Rights permissions =
|
|
static_cast<KIMAP::Acl::Rights>( index.data( AclModel::PermissionsRole ).toInt() );
|
|
|
|
AclEntryDialog dlg;
|
|
dlg.setCaption( i18n( "Edit ACL" ) );
|
|
dlg.setUserId( userId );
|
|
dlg.setPermissions( permissions );
|
|
|
|
if ( !dlg.exec() ) {
|
|
return;
|
|
}
|
|
|
|
mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
|
|
mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
|
|
mChanged = true;
|
|
}
|
|
|
|
void deleteAcl()
|
|
{
|
|
const QModelIndex index = mSelectionModel->selectedIndexes().first();
|
|
const QString userId = index.data( AclModel::UserIdRole ).toString();
|
|
|
|
if ( mImapUserName == userId ) {
|
|
if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel(
|
|
0,
|
|
i18n( "Do you really want to remove your own permissions for this folder? "
|
|
"You will not be able to access it afterwards." ),
|
|
i18n( "Remove" ) ) )
|
|
return;
|
|
}
|
|
|
|
mModel->removeRow( index.row(), QModelIndex() );
|
|
mChanged = true;
|
|
}
|
|
|
|
/**
|
|
* We call this method if our first try to get the ACLs for the user fails.
|
|
* That's the case if the ACLs use a different user id than the login name.
|
|
*
|
|
* Examples:
|
|
* login: testuser acls: testuser@mydomain.org
|
|
* login: testuser@mydomain.org acls: testuser
|
|
*/
|
|
static QString guessUserName( const QString &loginName, const QString &serverName )
|
|
{
|
|
if ( loginName.contains( QLatin1Char( '@' ) ) ) {
|
|
// strip of the domain part and use user name only
|
|
return loginName.left( loginName.indexOf( QLatin1Char( '@' ) ) );
|
|
} else {
|
|
int pos = serverName.lastIndexOf( QLatin1Char( '.' ) );
|
|
if ( pos == -1 ) { // no qualified domain name, only hostname
|
|
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
|
|
}
|
|
|
|
pos = serverName.lastIndexOf( QLatin1Char( '.' ), pos - 1 );
|
|
if ( pos == -1 ) { // a simple domain name e.g. mydomain.org
|
|
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName );
|
|
} else {
|
|
return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName.mid( pos + 1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
void setCollection( const Akonadi::Collection &collection )
|
|
{
|
|
mCollection = collection;
|
|
mChanged = false;
|
|
|
|
const PimCommon::ImapAclAttribute *attribute =
|
|
collection.attribute<PimCommon::ImapAclAttribute>();
|
|
const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
|
|
|
|
QString resource = collection.resource();
|
|
if (resource.contains(QLatin1String("akonadi_kolabproxy_resource"))) {
|
|
QDBusInterface interface( QLatin1String("org.freedesktop.Akonadi.Agent.akonadi_kolabproxy_resource"), QLatin1String("/KolabProxy") );
|
|
if (interface.isValid()) {
|
|
QDBusReply<QString> reply = interface.call(QLatin1String("imapResourceForCollection"), collection.remoteId().toLongLong());
|
|
if (reply.isValid()) {
|
|
resource = reply;
|
|
}
|
|
}
|
|
}
|
|
OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface =
|
|
PimCommon::Util::createImapSettingsInterface( resource );
|
|
|
|
QString loginName;
|
|
QString serverName;
|
|
if ( imapSettingsInterface->isValid() ) {
|
|
QDBusReply<QString> reply = imapSettingsInterface->userName();
|
|
if ( reply.isValid() ) {
|
|
loginName = reply;
|
|
}
|
|
|
|
reply = imapSettingsInterface->imapServer();
|
|
if ( reply.isValid() ) {
|
|
serverName = reply;
|
|
}
|
|
} else {
|
|
qDebug()<<" collection has not imap as resources: "<<collection.resource();
|
|
}
|
|
delete imapSettingsInterface;
|
|
|
|
mImapUserName = loginName;
|
|
if ( !rights.contains( loginName.toUtf8() ) ) {
|
|
const QString guessedUserName = guessUserName( loginName, serverName );
|
|
if ( rights.contains( guessedUserName.toUtf8() ) ) {
|
|
mImapUserName = guessedUserName;
|
|
}
|
|
}
|
|
|
|
mUserRights = rights[ mImapUserName.toUtf8() ];
|
|
|
|
mModel->setRights( rights );
|
|
selectionChanged();
|
|
}
|
|
|
|
AclManager *q;
|
|
AclModel *mModel;
|
|
QItemSelectionModel *mSelectionModel;
|
|
QAction *mAddAction;
|
|
QAction *mEditAction;
|
|
QAction *mDeleteAction;
|
|
|
|
Akonadi::Collection mCollection;
|
|
QString mImapUserName;
|
|
KIMAP::Acl::Rights mUserRights;
|
|
bool mChanged;
|
|
};
|
|
|
|
AclManager::AclManager( QObject *parent )
|
|
: QObject( parent ), d( new Private( this ) )
|
|
{
|
|
}
|
|
|
|
AclManager::~AclManager()
|
|
{
|
|
delete d;
|
|
}
|
|
|
|
void AclManager::setCollection( const Akonadi::Collection &collection )
|
|
{
|
|
d->setCollection( collection );
|
|
emit collectionChanged( d->mCollection );
|
|
}
|
|
|
|
Akonadi::Collection AclManager::collection() const
|
|
{
|
|
return d->mCollection;
|
|
}
|
|
|
|
QAbstractItemModel *AclManager::model() const
|
|
{
|
|
return d->mModel;
|
|
}
|
|
|
|
QItemSelectionModel *AclManager::selectionModel() const
|
|
{
|
|
return d->mSelectionModel;
|
|
}
|
|
|
|
QAction *AclManager::addAction() const
|
|
{
|
|
return d->mAddAction;
|
|
}
|
|
|
|
QAction *AclManager::editAction() const
|
|
{
|
|
return d->mEditAction;
|
|
}
|
|
|
|
QAction *AclManager::deleteAction() const
|
|
{
|
|
return d->mDeleteAction;
|
|
}
|
|
|
|
void AclManager::save()
|
|
{
|
|
if ( !d->mCollection.isValid() || !d->mChanged ) {
|
|
return;
|
|
}
|
|
|
|
// refresh the collection, it might be outdated in the meantime
|
|
Akonadi::CollectionFetchJob *job =
|
|
new Akonadi::CollectionFetchJob( d->mCollection, Akonadi::CollectionFetchJob::Base );
|
|
if ( !job->exec() ) {
|
|
return;
|
|
}
|
|
|
|
if ( job->collections().isEmpty() ) {
|
|
return;
|
|
}
|
|
|
|
d->mCollection = job->collections().first();
|
|
|
|
d->mChanged = false;
|
|
|
|
PimCommon::ImapAclAttribute *attribute =
|
|
d->mCollection.attribute<PimCommon::ImapAclAttribute>();
|
|
|
|
QMap<QByteArray, KIMAP::Acl::Rights> newRights;
|
|
|
|
const QMap<QByteArray, KIMAP::Acl::Rights> rights = d->mModel->rights();
|
|
QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
|
|
while ( it.hasNext() ) {
|
|
it.next();
|
|
|
|
// we can use job->exec() here, it is not a hot path
|
|
Akonadi::ContactGroupSearchJob *searchJob = new Akonadi::ContactGroupSearchJob( this );
|
|
searchJob->setQuery( Akonadi::ContactGroupSearchJob::Name, QString::fromLatin1(it.key()) );
|
|
searchJob->setLimit( 1 );
|
|
if ( !searchJob->exec() ) {
|
|
continue;
|
|
}
|
|
|
|
if ( !searchJob->contactGroups().isEmpty() ) { // it has been a distribution list
|
|
Akonadi::ContactGroupExpandJob *expandJob =
|
|
new Akonadi::ContactGroupExpandJob( searchJob->contactGroups().first(), this );
|
|
if ( expandJob->exec() ) {
|
|
foreach ( const KABC::Addressee &contact, expandJob->contacts() ) {
|
|
const QByteArray rawEmail =
|
|
KPIMUtils::extractEmailAddress( contact.preferredEmail().toUtf8() );
|
|
if ( !rawEmail.isEmpty() ) {
|
|
newRights[ rawEmail ] = it.value();
|
|
}
|
|
}
|
|
}
|
|
} else { // it has been a normal contact
|
|
const QByteArray rawEmail = KPIMUtils::extractEmailAddress( it.key() );
|
|
if ( !rawEmail.isEmpty() ) {
|
|
newRights[ rawEmail ] = it.value();
|
|
}
|
|
}
|
|
}
|
|
|
|
attribute->setRights( newRights );
|
|
|
|
new Akonadi::CollectionModifyJob( d->mCollection );
|
|
}
|
|
|
|
#include "moc_aclmanager.cpp"
|