~ubuntu-branches/ubuntu/vivid/kdepim/vivid

« back to all changes in this revision

Viewing changes to mailcommon/aclmanager.cpp

  • Committer: Package Import Robot
  • Author(s): Scott Kitterman, Jonathan Riddell, Rohan Garg, Scott Kitterman
  • Date: 2012-11-21 13:12:36 UTC
  • mfrom: (0.2.33)
  • Revision ID: package-import@ubuntu.com-20121121131236-32ijw9a2txrar80k
Tags: 4:4.9.80-0ubuntu1
[ Jonathan Riddell ]
* New upstream beta release

[ Rohan Garg ]
* Add nepomuk-core-dev to build-deps

[ Scott Kitterman ]
* Add new package, libpimcommon4
  - Add libpimcommon4.install
  - Add to debian/control, including kdepim-dbg and kdepim-dev depends
  - Add to kdepim-dev.install
* Remove usr/bin/backupmail and related files from kmail.install as they are
  not provided by upstream anymore
* Add usr/bin/pimsettingexporter and related files to kmail.install
* Add libnepomukwidgets-dev to build-depends

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>
29
 
#include <Akonadi/CollectionFetchJob>
30
 
#include <Akonadi/CollectionModifyJob>
31
 
#include <Akonadi/Contact/ContactGroupExpandJob>
32
 
#include <Akonadi/Contact/ContactGroupSearchJob>
33
 
 
34
 
#include <KPIMUtils/Email>
35
 
 
36
 
#include <KLocale>
37
 
#include <KMessageBox>
38
 
 
39
 
#include <QAbstractListModel>
40
 
#include <QAction>
41
 
#include <QItemSelectionModel>
42
 
 
43
 
using namespace MailCommon;
44
 
 
45
 
class AclModel : public QAbstractListModel
46
 
{
47
 
  public:
48
 
    enum Role {
49
 
      UserIdRole = Qt::UserRole + 1,
50
 
      PermissionsRole,
51
 
      PermissionsTextRole
52
 
    };
53
 
 
54
 
    AclModel( QObject *parent = 0 )
55
 
      : QAbstractListModel( parent )
56
 
    {
57
 
    }
58
 
 
59
 
    virtual QVariant data( const QModelIndex &index, int role = Qt::DisplayRole ) const
60
 
    {
61
 
      if ( index.row() < 0 || index.row() >= mRights.count() ) {
62
 
        return QVariant();
63
 
      }
64
 
 
65
 
      const QPair<QByteArray, KIMAP::Acl::Rights> right = mRights.at( index.row() );
66
 
      switch ( role ) {
67
 
      case Qt::DisplayRole:
68
 
        return QString::fromLatin1( "%1: %2" ).
69
 
                 arg( QString::fromLatin1( right.first ) ).
70
 
                 arg( AclUtils::permissionsToUserString( right.second ) );
71
 
        break;
72
 
      case UserIdRole:
73
 
        return QString::fromLatin1( right.first );
74
 
        break;
75
 
      case PermissionsRole:
76
 
        return QVariant( static_cast<int>( right.second ) );
77
 
        break;
78
 
      case PermissionsTextRole:
79
 
        return AclUtils::permissionsToUserString( right.second );
80
 
        break;
81
 
      default:
82
 
        return QVariant();
83
 
        break;
84
 
      }
85
 
 
86
 
      return QVariant();
87
 
    }
88
 
 
89
 
    virtual bool setData( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole )
90
 
    {
91
 
      if ( index.row() < 0 || index.row() >= mRights.count() ) {
92
 
        return false;
93
 
      }
94
 
 
95
 
      QPair<QByteArray, KIMAP::Acl::Rights> &right = mRights[ index.row() ];
96
 
      switch ( role ) {
97
 
      case UserIdRole:
98
 
        right.first = value.toByteArray();
99
 
        emit dataChanged( index, index );
100
 
        return true;
101
 
        break;
102
 
      case PermissionsRole:
103
 
        right.second = static_cast<KIMAP::Acl::Rights>( value.toInt() );
104
 
        emit dataChanged( index, index );
105
 
        return true;
106
 
        break;
107
 
      default:
108
 
        return false;
109
 
        break;
110
 
      }
111
 
 
112
 
      return false;
113
 
    }
114
 
 
115
 
    virtual int rowCount( const QModelIndex &parent = QModelIndex() ) const
116
 
    {
117
 
      if ( parent.isValid() ) {
118
 
        return 0;
119
 
      } else {
120
 
        return mRights.count();
121
 
      }
122
 
    }
123
 
 
124
 
    void setRights( const QMap<QByteArray, KIMAP::Acl::Rights> &rights )
125
 
    {
126
 
      mRights.clear();
127
 
 
128
 
      QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
129
 
      while ( it.hasNext() ) {
130
 
        it.next();
131
 
        mRights.append( qMakePair( it.key(), it.value() ) );
132
 
      }
133
 
 
134
 
      reset();
135
 
    }
136
 
 
137
 
    QMap<QByteArray, KIMAP::Acl::Rights> rights() const
138
 
    {
139
 
      QMap<QByteArray, KIMAP::Acl::Rights> result;
140
 
 
141
 
      typedef QPair<QByteArray, KIMAP::Acl::Rights> RightPair;
142
 
      foreach ( const RightPair &right, mRights ) {
143
 
        result.insert( right.first, right.second );
144
 
      }
145
 
 
146
 
      return result;
147
 
    }
148
 
 
149
 
  protected:
150
 
    virtual bool insertRows( int row, int count, const QModelIndex &parent = QModelIndex() )
151
 
    {
152
 
      beginInsertRows( parent, row, row + count - 1 );
153
 
      for ( int i = 0; i < count; ++i ) {
154
 
        mRights.insert( row, qMakePair( QByteArray(), KIMAP::Acl::Rights() ) );
155
 
      }
156
 
      endInsertRows();
157
 
 
158
 
      return true;
159
 
    }
160
 
 
161
 
    virtual bool removeRows( int row, int count, const QModelIndex &parent = QModelIndex() )
162
 
    {
163
 
      beginRemoveRows( parent, row, row + count - 1 );
164
 
      for ( int i = 0; i < count; ++i ) {
165
 
        mRights.remove( row, count );
166
 
      }
167
 
      endRemoveRows();
168
 
 
169
 
      return true;
170
 
    }
171
 
 
172
 
  private:
173
 
    QVector<QPair<QByteArray, KIMAP::Acl::Rights> > mRights;
174
 
};
175
 
 
176
 
class MailCommon::AclManager::Private
177
 
{
178
 
  public:
179
 
    Private( AclManager *qq )
180
 
      : q( qq ),
181
 
        mChanged( false )
182
 
    {
183
 
      mAddAction = new QAction( i18n( "Add Entry..." ), q );
184
 
      q->connect( mAddAction, SIGNAL(triggered(bool)),
185
 
                  q, SLOT(addAcl()) );
186
 
 
187
 
      mEditAction = new QAction( i18n( "Edit Entry..." ), q );
188
 
      mEditAction->setEnabled( false );
189
 
      q->connect( mEditAction, SIGNAL(triggered(bool)),
190
 
                  q, SLOT(editAcl()) );
191
 
 
192
 
      mDeleteAction = new QAction( i18n( "Remove Entry" ), q );
193
 
      mDeleteAction->setEnabled( false );
194
 
      q->connect( mDeleteAction, SIGNAL(triggered(bool)),
195
 
                  q, SLOT(deleteAcl()) );
196
 
 
197
 
      mModel = new AclModel( q );
198
 
 
199
 
      mSelectionModel = new QItemSelectionModel( mModel );
200
 
      q->connect( mSelectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
201
 
                  q, SLOT(selectionChanged()) );
202
 
    }
203
 
 
204
 
    ~Private()
205
 
    {
206
 
    }
207
 
 
208
 
    void selectionChanged()
209
 
    {
210
 
      const bool itemSelected = !mSelectionModel->selectedIndexes().isEmpty();
211
 
 
212
 
      bool canAdmin = ( mUserRights & KIMAP::Acl::Admin );
213
 
 
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() );
220
 
 
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;
224
 
        }
225
 
      }
226
 
 
227
 
      mAddAction->setEnabled( canAdmin );
228
 
      mEditAction->setEnabled( itemSelected && canAdminThisItem );
229
 
      mDeleteAction->setEnabled( itemSelected && canAdminThisItem );
230
 
    }
231
 
 
232
 
    void addAcl()
233
 
    {
234
 
      AclEntryDialog dlg;
235
 
      dlg.setCaption( i18n( "Add ACL" ) );
236
 
 
237
 
      if ( !dlg.exec() ) {
238
 
        return;
239
 
      }
240
 
 
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 );
245
 
 
246
 
        mChanged = true;
247
 
      }
248
 
    }
249
 
 
250
 
    void editAcl()
251
 
    {
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() );
256
 
 
257
 
      AclEntryDialog dlg;
258
 
      dlg.setCaption( i18n( "Edit ACL" ) );
259
 
      dlg.setUserId( userId );
260
 
      dlg.setPermissions( permissions );
261
 
 
262
 
      if ( !dlg.exec() ) {
263
 
        return;
264
 
      }
265
 
 
266
 
      mModel->setData( index, dlg.userId(), AclModel::UserIdRole );
267
 
      mModel->setData( index, static_cast<int>( dlg.permissions() ), AclModel::PermissionsRole );
268
 
      mChanged = true;
269
 
    }
270
 
 
271
 
    void deleteAcl()
272
 
    {
273
 
      const QModelIndex index = mSelectionModel->selectedIndexes().first();
274
 
      const QString userId = index.data( AclModel::UserIdRole ).toString();
275
 
 
276
 
      if ( mImapUserName == userId ) {
277
 
        if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel(
278
 
               0,
279
 
               i18n( "Do you really want to remove your own permissions for this folder? "
280
 
                     "You will not be able to access it afterwards." ),
281
 
               i18n( "Remove" ) ) )
282
 
          return;
283
 
      }
284
 
 
285
 
      mModel->removeRow( index.row(), QModelIndex() );
286
 
      mChanged = true;
287
 
    }
288
 
 
289
 
    /**
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.
292
 
     *
293
 
     * Examples:
294
 
     *   login: testuser                acls: testuser@mydomain.org
295
 
     *   login: testuser@mydomain.org   acls: testuser
296
 
     */
297
 
    static QString guessUserName( const QString &loginName, const QString &serverName )
298
 
    {
299
 
      if ( loginName.contains( QLatin1Char( '@' ) ) ) {
300
 
        // strip of the domain part and use user name only
301
 
        return loginName.left( loginName.indexOf( QLatin1Char( '@' ) ) );
302
 
      } else {
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 );
306
 
        }
307
 
 
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 );
311
 
        } else {
312
 
          return QString::fromLatin1( "%1@%2" ).arg( loginName ).arg( serverName.mid( pos + 1 ) );
313
 
        }
314
 
      }
315
 
    }
316
 
 
317
 
    void setCollection( const Akonadi::Collection &collection )
318
 
    {
319
 
      mCollection = collection;
320
 
      mChanged = false;
321
 
 
322
 
      const MailCommon::ImapAclAttribute *attribute =
323
 
        collection.attribute<MailCommon::ImapAclAttribute>();
324
 
      const QMap<QByteArray, KIMAP::Acl::Rights> rights = attribute->rights();
325
 
 
326
 
      OrgKdeAkonadiImapSettingsInterface *imapSettingsInterface =
327
 
        MailCommon::Util::createImapSettingsInterface( collection.resource() );
328
 
 
329
 
      QString loginName;
330
 
      QString serverName;
331
 
      if ( imapSettingsInterface->isValid() ) {
332
 
        QDBusReply<QString> reply = imapSettingsInterface->userName();
333
 
        if ( reply.isValid() ) {
334
 
          loginName = reply;
335
 
        }
336
 
 
337
 
        reply = imapSettingsInterface->imapServer();
338
 
        if ( reply.isValid() ) {
339
 
          serverName = reply;
340
 
        }
341
 
      }
342
 
 
343
 
      delete imapSettingsInterface;
344
 
 
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;
350
 
        }
351
 
      }
352
 
 
353
 
      mUserRights = rights[ mImapUserName.toUtf8() ];
354
 
 
355
 
      mModel->setRights( rights );
356
 
      selectionChanged();
357
 
    }
358
 
 
359
 
    AclManager *q;
360
 
    AclModel *mModel;
361
 
    QItemSelectionModel *mSelectionModel;
362
 
    QAction *mAddAction;
363
 
    QAction *mEditAction;
364
 
    QAction *mDeleteAction;
365
 
 
366
 
    Akonadi::Collection mCollection;
367
 
    QString mImapUserName;
368
 
    KIMAP::Acl::Rights mUserRights;
369
 
    bool mChanged;
370
 
};
371
 
 
372
 
AclManager::AclManager( QObject *parent )
373
 
  : QObject( parent ), d( new Private( this ) )
374
 
{
375
 
}
376
 
 
377
 
AclManager::~AclManager()
378
 
{
379
 
  delete d;
380
 
}
381
 
 
382
 
void AclManager::setCollection( const Akonadi::Collection &collection )
383
 
{
384
 
  d->setCollection( collection );
385
 
  emit collectionChanged( d->mCollection );
386
 
}
387
 
 
388
 
Akonadi::Collection AclManager::collection() const
389
 
{
390
 
  return d->mCollection;
391
 
}
392
 
 
393
 
QAbstractItemModel *AclManager::model() const
394
 
{
395
 
  return d->mModel;
396
 
}
397
 
 
398
 
QItemSelectionModel *AclManager::selectionModel() const
399
 
{
400
 
  return d->mSelectionModel;
401
 
}
402
 
 
403
 
QAction *AclManager::addAction() const
404
 
{
405
 
  return d->mAddAction;
406
 
}
407
 
 
408
 
QAction *AclManager::editAction() const
409
 
{
410
 
  return d->mEditAction;
411
 
}
412
 
 
413
 
QAction *AclManager::deleteAction() const
414
 
{
415
 
  return d->mDeleteAction;
416
 
}
417
 
 
418
 
void AclManager::save()
419
 
{
420
 
  if ( !d->mCollection.isValid() || !d->mChanged ) {
421
 
    return;
422
 
  }
423
 
 
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() ) {
428
 
    return;
429
 
  }
430
 
 
431
 
  if ( job->collections().isEmpty() ) {
432
 
    return;
433
 
  }
434
 
 
435
 
  d->mCollection = job->collections().first();
436
 
 
437
 
  d->mChanged = false;
438
 
 
439
 
  MailCommon::ImapAclAttribute *attribute =
440
 
    d->mCollection.attribute<MailCommon::ImapAclAttribute>();
441
 
 
442
 
  QMap<QByteArray, KIMAP::Acl::Rights> newRights;
443
 
 
444
 
  const QMap<QByteArray, KIMAP::Acl::Rights> rights = d->mModel->rights();
445
 
  QMapIterator<QByteArray, KIMAP::Acl::Rights> it( rights );
446
 
  while ( it.hasNext() ) {
447
 
    it.next();
448
 
 
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() ) {
454
 
      continue;
455
 
    }
456
 
 
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();
466
 
          }
467
 
        }
468
 
      }
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();
473
 
      }
474
 
    }
475
 
  }
476
 
 
477
 
  attribute->setRights( newRights );
478
 
 
479
 
  new Akonadi::CollectionModifyJob( d->mCollection );
480
 
}
481
 
 
482
 
#include "aclmanager.moc"