~ubuntu-branches/ubuntu/oneiric/kdepim/oneiric-updates

« back to all changes in this revision

Viewing changes to mailcommon/aclmanager.cpp

  • Committer: Package Import Robot
  • Author(s): Philip Muškovac
  • Date: 2011-06-28 19:33:24 UTC
  • mfrom: (0.2.13) (0.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20110628193324-8yvjs8sdv9rdoo6c
Tags: 4:4.7.0-0ubuntu1
* New upstream release
  - update install files
  - add missing kdepim-doc package to control file
  - Fix Vcs lines
  - kontact breaks/replaces korganizer << 4:4.6.80
  - tighten the dependency of kdepim-dev on libkdepim4 to fix lintian error

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (c) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
 
3
 * Copyright (c) 2010 Tobias Koenig <tokoe@kdab.com>
 
4
 *
 
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.
 
9
 *
 
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.
 
14
 *
 
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.
 
18
 */
 
19
 
 
20
#include "aclmanager.h"
 
21
 
 
22
#include "aclentrydialog_p.h"
 
23
#include "aclutils_p.h"
 
24
#include "imapaclattribute.h"
 
25
#include "imapsettings.h"
 
26
#include "mailutil.h"
 
27
 
 
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>
 
33
#include <klocale.h>
 
34
#include <kmessagebox.h>
 
35
#include <kpimutils/email.h>
 
36
 
 
37
#include <QtCore/QAbstractListModel>
 
38
#include <QtGui/QAction>
 
39
#include <QtGui/QItemSelectionModel>
 
40
 
 
41
using namespace MailCommon;
 
42
 
 
43
class AclModel : public QAbstractListModel
 
44
{
 
45
  public:
 
46
    enum Role {
 
47
      UserIdRole = Qt::UserRole + 1,
 
48
      PermissionsRole,
 
49
      PermissionsTextRole
 
50
    };
 
51
 
 
52
    AclModel( QObject *parent = 0 )
 
53
      : QAbstractListModel( parent )
 
54
    {
 
55
    }
 
56
 
 
57
    virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
 
58
    {
 
59
      if ( index.row() < 0 || index.row() >= mRights.count() )
 
60
        return QVariant();
 
61
 
 
62
      const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at( index.row() );
 
63
      switch ( role ) {
 
64
        case Qt::DisplayRole:
 
65
          return QString::fromLatin1( "%1: %2" ).arg( QString::fromLatin1( right.first ) )
 
66
                                                .arg( AclUtils::permissionsToUserString( right.second ) );
 
67
          break;
 
68
        case UserIdRole:
 
69
          return QString::fromLatin1( right.first );
 
70
          break;
 
71
        case PermissionsRole:
 
72
          return QVariant( static_cast<int>( right.second ) );
 
73
          break;
 
74
        case PermissionsTextRole:
 
75
          return AclUtils::permissionsToUserString( right.second );
 
76
          break;
 
77
        default:
 
78
          return QVariant();
 
79
          break;
 
80
      }
 
81
 
 
82
      return QVariant();
 
83
    }
 
84
 
 
85
    virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
 
86
    {
 
87
      if ( index.row() < 0 || index.row() >= mRights.count() )
 
88
        return false;
 
89
 
 
90
      QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[ index.row() ];
 
91
      switch ( role ) {
 
92
        case UserIdRole:
 
93
          right.first = value.toByteArray();
 
94
          emit dataChanged( index, index );
 
95
          return true;
 
96
          break;
 
97
        case PermissionsRole:
 
98
          right.second = static_cast<KIMAP::Acl::Rights>( value.toInt() );
 
99
          emit dataChanged( index, index );
 
100
          return true;
 
101
          break;
 
102
        default:
 
103
          return false;
 
104
          break;
 
105
      }
 
106
 
 
107
      return false;
 
108
    }
 
109
 
 
110
    virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const
 
111
    {
 
112
      if ( parent.isValid() )
 
113
        return 0;
 
114
      else
 
115
        return mRights.count();
 
116
    }
 
117
 
 
118
    void setRights( const QMap<QByteArray, KIMAP::Acl::Rights> &rights )
 
119
    {
 
120
      mRights.clear();
 
121
 
 
122
      QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
 
123
      while ( it.hasNext() ) {
 
124
        it.next();
 
125
        mRights.append( qMakePair( it.key(), it.value() ) );
 
126
      }
 
127
 
 
128
      reset();
 
129
    }
 
130
 
 
131
    QMap<QByteArray, KIMAP::Acl::Rights> rights() const
 
132
    {
 
133
      QMap<QByteArray, KIMAP::Acl::Rights> result;
 
134
 
 
135
      typedef QPair<QByteArray, KIMAP::Acl::Rights> RightPair;
 
136
      foreach ( const RightPair &right, mRights )
 
137
        result.insert( right.first, right.second );
 
138
 
 
139
      return result;
 
140
    }
 
141
 
 
142
  protected:
 
143
    virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() )
 
144
    {
 
145
      beginInsertRows( parent, row, row + count - 1 );
 
146
      for ( int i = 0; i < count; ++i )
 
147
        mRights.insert( row, qMakePair( QByteArray(), KIMAP::Acl::Rights() ) );
 
148
      endInsertRows();
 
149
 
 
150
      return true;
 
151
    }
 
152
 
 
153
    virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() )
 
154
    {
 
155
      beginRemoveRows( parent, row, row + count - 1 );
 
156
      for ( int i = 0; i < count; ++i )
 
157
        mRights.remove( row, count );
 
158
      endRemoveRows();
 
159
 
 
160
      return true;
 
161
    }
 
162
 
 
163
  private:
 
164
    QVector<QPair<QByteArray, KIMAP::Acl::Rights> > mRights;
 
165
};
 
166
 
 
167
class MailCommon::AclManager::Private
 
168
{
 
169
  public:
 
170
    Private( AclManager *qq )
 
171
      : q( qq ),
 
172
        mChanged( false )
 
173
    {
 
174
      mAddAction = new QAction( i18n( "Add Entry..." ), q );
 
175
      q->connect( mAddAction, SIGNAL( triggered( bool ) ),
 
176
                  q, SLOT( addAcl() ) );
 
177
 
 
178
      mEditAction = new QAction( i18n( "Edit Entry..." ), q );
 
179
      mEditAction->setEnabled( false );
 
180
      q->connect( mEditAction, SIGNAL( triggered( bool ) ),
 
181
                  q, SLOT( editAcl() ) );
 
182
 
 
183
      mDeleteAction = new QAction( i18n( "Remove Entry" ), q );
 
184
      mDeleteAction->setEnabled( false );
 
185
      q->connect( mDeleteAction, SIGNAL( triggered( bool ) ),
 
186
                  q, SLOT( deleteAcl() ) );
 
187
 
 
188
      mModel = new AclModel( q );
 
189
 
 
190
      mSelectionModel = new QItemSelectionModel( mModel );
 
191
      q->connect( mSelectionModel, SIGNAL( selectionChanged( const QItemSelection&, const QItemSelection& ) ),
 
192
                  q, SLOT( selectionChanged() ) );
 
193
    }
 
194
 
 
195
    ~Private()
 
196
    {
 
197
    }
 
198
 
 
199
    void selectionChanged()
 
200
    {
 
201
      const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
 
202
 
 
203
      bool canAdmin = (mUserRights & KIMAP::Acl::Admin);
 
204
 
 
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() );
 
210
 
 
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;
 
214
      }
 
215
 
 
216
      mAddAction->setEnabled( canAdmin );
 
217
      mEditAction->setEnabled( itemSelected && canAdminThisItem );
 
218
      mDeleteAction->setEnabled( itemSelected && canAdminThisItem );
 
219
    }
 
220
 
 
221
    void addAcl()
 
222
    {
 
223
      AclEntryDialog dlg;
 
224
      dlg.setCaption( i18n( "Add ACL" ) );
 
225
 
 
226
      if ( !dlg.exec() )
 
227
        return;
 
228
 
 
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 );
 
233
 
 
234
        mChanged = true;
 
235
      }
 
236
    }
 
237
 
 
238
    void editAcl()
 
239
    {
 
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() );
 
243
 
 
244
      AclEntryDialog dlg;
 
245
      dlg.setCaption( i18n( "Edit ACL" ) );
 
246
      dlg.setUserId( userId );
 
247
      dlg.setPermissions( permissions );
 
248
 
 
249
      if ( !dlg.exec() )
 
250
        return;
 
251
 
 
252
      mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
 
253
      mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
 
254
      mChanged = true;
 
255
    }
 
256
 
 
257
    void deleteAcl()
 
258
    {
 
259
      const QModelIndex index = mSelectionModel->selectedIndexes().first();
 
260
      const QString userId = index.data( AclModel::UserIdRole ).toString();
 
261
 
 
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" ) ) )
 
265
          return;
 
266
      }
 
267
 
 
268
      mModel->removeRow( index.row(), QModelIndex() );
 
269
      mChanged = true;
 
270
    }
 
271
 
 
272
    /**
 
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.
 
275
     *
 
276
     * Examples:
 
277
     *   login: testuser                acls: testuser@mydomain.org
 
278
     *   login: testuser@mydomain.org   acls: testuser
 
279
     */
 
280
    static QString guessUserName( const QString &loginName, const QString &serverName )
 
281
    {
 
282
      if ( loginName.contains( QLatin1Char( '@' ) ) ) {
 
283
        // strip of the domain part and use user name only
 
284
        return loginName.left( loginName.indexOf( QLatin1Char( '@' ) ) );
 
285
      } else {
 
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 );
 
289
 
 
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 );
 
293
        else
 
294
          return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName.mid( pos + 1 ) );
 
295
      }
 
296
    }
 
297
 
 
298
    void setCollection( const Akonadi::Collection &collection )
 
299
    {
 
300
      mCollection = collection;
 
301
      mChanged = false;
 
302
 
 
303
      const MailCommon::ImapAclAttribute *attribute = collection.attribute<MailCommon::ImapAclAttribute>();
 
304
      const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
 
305
 
 
306
      OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface = MailCommon::Util::createImapSettingsInterface( collection.resource() );
 
307
      
 
308
      QString loginName;
 
309
      QString serverName;
 
310
      if ( imapSettingsInterface->isValid() ) {
 
311
        QDBusReply<QString> reply = imapSettingsInterface->userName();
 
312
        if ( reply.isValid() )
 
313
          loginName = reply;
 
314
 
 
315
        reply = imapSettingsInterface->imapServer();
 
316
        if ( reply.isValid() ) {
 
317
          serverName = reply;
 
318
        }
 
319
      }
 
320
 
 
321
      delete imapSettingsInterface;
 
322
 
 
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;
 
328
      }
 
329
 
 
330
      mUserRights = rights[ mImapUserName.toUtf8() ];
 
331
 
 
332
      mModel->setRights( rights );
 
333
      selectionChanged();
 
334
    }
 
335
 
 
336
    AclManager *q;
 
337
    AclModel *mModel;
 
338
    QItemSelectionModel *mSelectionModel;
 
339
    QAction *mAddAction;
 
340
    QAction *mEditAction;
 
341
    QAction *mDeleteAction;
 
342
 
 
343
    Akonadi::Collection mCollection;
 
344
    QString mImapUserName;
 
345
    KIMAP::Acl::Rights mUserRights;
 
346
    bool mChanged;
 
347
};
 
348
 
 
349
AclManager::AclManager( QObject *parent )
 
350
  : QObject( parent ), d( new Private( this ) )
 
351
{
 
352
}
 
353
 
 
354
AclManager::~AclManager()
 
355
{
 
356
  delete d;
 
357
}
 
358
 
 
359
void AclManager::setCollection( const Akonadi::Collection &collection )
 
360
{
 
361
  d->setCollection( collection );
 
362
  emit collectionChanged( d->mCollection );
 
363
}
 
364
 
 
365
Akonadi::Collection AclManager::collection() const
 
366
{
 
367
  return d->mCollection;
 
368
}
 
369
 
 
370
QAbstractItemModel* AclManager::model() const
 
371
{
 
372
  return d->mModel;
 
373
}
 
374
 
 
375
QItemSelectionModel* AclManager::selectionModel() const
 
376
{
 
377
  return d->mSelectionModel; 
 
378
}
 
379
 
 
380
QAction* AclManager::addAction() const
 
381
{
 
382
  return d->mAddAction;
 
383
}
 
384
 
 
385
QAction* AclManager::editAction() const
 
386
{
 
387
  return d->mEditAction;
 
388
}
 
389
 
 
390
QAction* AclManager::deleteAction() const
 
391
{
 
392
  return d->mDeleteAction;
 
393
}
 
394
 
 
395
void AclManager::save()
 
396
{
 
397
  if ( !d->mCollection.isValid() || !d->mChanged )
 
398
    return;
 
399
 
 
400
  // refresh the collection, it might be outdated in the meantime
 
401
  Akonadi::CollectionFetchJob *job = new Akonadi::CollectionFetchJob( d->mCollection, Akonadi::CollectionFetchJob::Base );
 
402
  if ( !job->exec() )
 
403
    return;
 
404
 
 
405
  if ( job->collections().isEmpty() )
 
406
    return;
 
407
 
 
408
  d->mCollection = job->collections().first();
 
409
 
 
410
  d->mChanged = false;
 
411
 
 
412
  MailCommon::ImapAclAttribute *attribute = d->mCollection.attribute<MailCommon::ImapAclAttribute>();
 
413
  QMap<QByteArray, KIMAP::Acl::Rights> newRights;
 
414
 
 
415
  const QMap<QByteArray, KIMAP::Acl::Rights> rights = d->mModel->rights();
 
416
  QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
 
417
  while ( it.hasNext() ) {
 
418
    it.next();
 
419
 
 
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() ) {
 
425
      continue;
 
426
    }
 
427
 
 
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();
 
435
        }
 
436
      }
 
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();
 
441
    }
 
442
  }
 
443
 
 
444
  attribute->setRights( newRights );
 
445
 
 
446
  new Akonadi::CollectionModifyJob( d->mCollection );
 
447
}
 
448
 
 
449
#include "aclmanager.moc"