kmail

folderdiaacltab.cpp

Go to the documentation of this file.
00001 // -*- mode: C++; c-file-style: "gnu" -*-
00033 #include <config.h>
00034 
00035 #include "folderdiaacltab.h"
00036 #include "acljobs.h"
00037 #include "kmfolderimap.h"
00038 #include "kmfoldercachedimap.h"
00039 #include "kmacctcachedimap.h"
00040 #include "kmfolder.h"
00041 
00042 #include <addressesdialog.h>
00043 #include <kabc/addresseelist.h>
00044 #ifdef KDEPIM_NEW_DISTRLISTS
00045 #include <libkdepim/distributionlist.h> // libkdepim
00046 #else
00047 #include <kabc/distributionlist.h>
00048 #endif
00049 #include <kabc/stdaddressbook.h>
00050 #include <kaddrbook.h>
00051 #include <kpushbutton.h>
00052 #include <kdebug.h>
00053 #include <klocale.h>
00054 
00055 #include <qlayout.h>
00056 #include <qlabel.h>
00057 #include <qvbox.h>
00058 #include <qvbuttongroup.h>
00059 #include <qwidgetstack.h>
00060 #include <qradiobutton.h>
00061 #include <qwhatsthis.h>
00062 
00063 #include <assert.h>
00064 #include <kmessagebox.h>
00065 
00066 using namespace KMail;
00067 
00068 // In case your kdelibs is < 3.3
00069 #ifndef I18N_NOOP2
00070 #define I18N_NOOP2( comment,x ) x
00071 #endif
00072 
00073 // The set of standard permission sets
00074 static const struct {
00075   unsigned int permissions;
00076   const char* userString;
00077 } standardPermissions[] = {
00078   { 0, I18N_NOOP2( "Permissions", "None" ) },
00079   { ACLJobs::List | ACLJobs::Read | ACLJobs::WriteSeenFlag, I18N_NOOP2( "Permissions", "Read" ) },
00080   { ACLJobs::List | ACLJobs::Read | ACLJobs::WriteSeenFlag | ACLJobs::Insert | ACLJobs::Post, I18N_NOOP2( "Permissions", "Append" ) },
00081   { ACLJobs::AllWrite, I18N_NOOP2( "Permissions", "Write" ) },
00082   { ACLJobs::All, I18N_NOOP2( "Permissions", "All" ) }
00083 };
00084 
00085 
00086 KMail::ACLEntryDialog::ACLEntryDialog( IMAPUserIdFormat userIdFormat, const QString& caption, QWidget* parent, const char* name )
00087   : KDialogBase( parent, name, true /*modal*/, caption,
00088                  KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true /*sep*/ )
00089   , mUserIdFormat( userIdFormat )
00090 {
00091   QWidget *page = new QWidget( this );
00092   setMainWidget(page);
00093   QGridLayout *topLayout = new QGridLayout( page, 3 /*rows*/, 3 /*cols*/, 0, spacingHint() );
00094 
00095   QLabel *label = new QLabel( i18n( "&User identifier:" ), page );
00096   topLayout->addWidget( label, 0, 0 );
00097 
00098   mUserIdLineEdit = new KLineEdit( page );
00099   topLayout->addWidget( mUserIdLineEdit, 0, 1 );
00100   label->setBuddy( mUserIdLineEdit );
00101   QWhatsThis::add( mUserIdLineEdit, i18n( "The User Identifier is the login of the user on the IMAP server. This can be a simple user name or the full email address of the user; the login for your own account on the server will tell you which one it is." ) );
00102 
00103   QPushButton* kabBtn = new QPushButton( "...", page );
00104   topLayout->addWidget( kabBtn, 0, 2 );
00105 
00106   mButtonGroup = new QVButtonGroup( i18n( "Permissions" ), page );
00107   topLayout->addMultiCellWidget( mButtonGroup, 1, 1, 0, 2 );
00108 
00109   for ( unsigned int i = 0;
00110         i < sizeof( standardPermissions ) / sizeof( *standardPermissions );
00111         ++i ) {
00112     QRadioButton* cb = new QRadioButton( i18n( "Permissions", standardPermissions[i].userString ), mButtonGroup );
00113     // We store the permission value (bitfield) as the id of the radiobutton in the group
00114     mButtonGroup->insert( cb, standardPermissions[i].permissions );
00115   }
00116   topLayout->setRowStretch(2, 10);
00117 
00118   connect( mUserIdLineEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotChanged() ) );
00119   connect( kabBtn, SIGNAL( clicked() ), SLOT( slotSelectAddresses() ) );
00120   connect( mButtonGroup, SIGNAL( clicked( int ) ), SLOT( slotChanged() ) );
00121   enableButtonOK( false );
00122 
00123   mUserIdLineEdit->setFocus();
00124   // Ensure the lineedit is rather wide so that email addresses can be read in it
00125   incInitialSize( QSize( 200, 0 ) );
00126 }
00127 
00128 void KMail::ACLEntryDialog::slotChanged()
00129 {
00130   enableButtonOK( !mUserIdLineEdit->text().isEmpty() && mButtonGroup->selected() != 0 );
00131 }
00132 
00133 static QString addresseeToUserId( const KABC::Addressee& addr, IMAPUserIdFormat userIdFormat )
00134 {
00135   QString email = addr.preferredEmail();
00136   if ( userIdFormat == FullEmail )
00137     return email;
00138   else { // mUserIdFormat == UserName
00139     email.truncate( email.find( '@' ) );
00140     return email;
00141   }
00142 }
00143 
00144 void KMail::ACLEntryDialog::slotSelectAddresses()
00145 {
00146   KPIM::AddressesDialog dlg( this );
00147   dlg.setShowCC( false );
00148   dlg.setShowBCC( false );
00149   if ( mUserIdFormat == FullEmail ) // otherwise we have no way to go back from userid to email
00150     dlg.setSelectedTo( userIds() );
00151   if ( dlg.exec() != QDialog::Accepted )
00152     return;
00153 
00154   const QStringList distrLists = dlg.toDistributionLists();
00155   QString txt = distrLists.join( ", " );
00156   const KABC::Addressee::List lst = dlg.toAddresses();
00157   if ( !lst.isEmpty() ) {
00158     for( QValueList<KABC::Addressee>::ConstIterator it = lst.begin(); it != lst.end(); ++it ) {
00159       if ( !txt.isEmpty() )
00160         txt += ", ";
00161       txt += addresseeToUserId( *it, mUserIdFormat );
00162     }
00163   }
00164   mUserIdLineEdit->setText( txt );
00165 }
00166 
00167 void KMail::ACLEntryDialog::setValues( const QString& userId, unsigned int permissions )
00168 {
00169   mUserIdLineEdit->setText( userId );
00170   mButtonGroup->setButton( permissions );
00171   enableButtonOK( !userId.isEmpty() );
00172 }
00173 
00174 QString KMail::ACLEntryDialog::userId() const
00175 {
00176   return mUserIdLineEdit->text();
00177 }
00178 
00179 QStringList KMail::ACLEntryDialog::userIds() const
00180 {
00181   QStringList lst = QStringList::split( ",", mUserIdLineEdit->text() );
00182   for( QStringList::Iterator it = lst.begin(); it != lst.end(); ++it ) {
00183     // Strip white space (in particular, due to ", ")
00184     *it = (*it).stripWhiteSpace();
00185   }
00186   return lst;
00187 }
00188 
00189 unsigned int KMail::ACLEntryDialog::permissions() const
00190 {
00191   return mButtonGroup->selectedId();
00192 }
00193 
00194 // class KMail::FolderDiaACLTab::ListView : public KListView
00195 // {
00196 // public:
00197 //   ListView( QWidget* parent, const char* name = 0 ) : KListView( parent, name ) {}
00198 // };
00199 
00200 class KMail::FolderDiaACLTab::ListViewItem : public KListViewItem
00201 {
00202 public:
00203   ListViewItem( QListView* listview )
00204     : KListViewItem( listview, listview->lastItem() ),
00205       mModified( false ), mNew( false ) {}
00206 
00207   void load( const ACLListEntry& entry );
00208   void save( ACLList& list,
00209 #ifdef KDEPIM_NEW_DISTRLISTS
00210              KABC::AddressBook* abook,
00211 #else
00212              KABC::DistributionListManager& manager,
00213 #endif
00214              IMAPUserIdFormat userIdFormat );
00215 
00216   QString userId() const { return text( 0 ); }
00217   void setUserId( const QString& userId ) { setText( 0, userId ); }
00218 
00219   unsigned int permissions() const { return mPermissions; }
00220   void setPermissions( unsigned int permissions );
00221 
00222   bool isModified() const { return mModified; }
00223   void setModified( bool b ) { mModified = b; }
00224 
00225   // The fact that an item is new doesn't matter much.
00226   // This bool is only used to handle deletion differently
00227   bool isNew() const { return mNew; }
00228   void setNew( bool b ) { mNew = b; }
00229 
00230 private:
00231   unsigned int mPermissions;
00232   QString mInternalRightsList; 
00233   bool mModified;
00234   bool mNew;
00235 };
00236 
00237 // internalRightsList is only used if permissions doesn't match the standard set
00238 static QString permissionsToUserString( unsigned int permissions, const QString& internalRightsList )
00239 {
00240   for ( unsigned int i = 0;
00241         i < sizeof( standardPermissions ) / sizeof( *standardPermissions );
00242         ++i ) {
00243     if ( permissions == standardPermissions[i].permissions )
00244       return i18n( "Permissions", standardPermissions[i].userString );
00245   }
00246   if ( internalRightsList.isEmpty() )
00247     return i18n( "Custom Permissions" ); // not very helpful, but shouldn't happen
00248   else
00249     return i18n( "Custom Permissions (%1)" ).arg( internalRightsList );
00250 }
00251 
00252 void KMail::FolderDiaACLTab::ListViewItem::setPermissions( unsigned int permissions )
00253 {
00254   mPermissions = permissions;
00255   setText( 1, permissionsToUserString( permissions, QString::null ) );
00256 }
00257 
00258 void KMail::FolderDiaACLTab::ListViewItem::load( const ACLListEntry& entry )
00259 {
00260   // Don't allow spaces in userids. If you need this, fix the slave->app communication,
00261   // since it uses space as a separator (imap4.cc, look for GETACL)
00262   // It's ok in distribution list names though, that's why this check is only done here
00263   // and also why there's no validator on the lineedit.
00264   if ( entry.userId.contains( ' ' ) )
00265     kdWarning(5006) << "Userid contains a space!!!  '" << entry.userId << "'" << endl;
00266 
00267   setUserId( entry.userId );
00268   mPermissions = entry.permissions;
00269   mInternalRightsList = entry.internalRightsList;
00270   setText( 1, permissionsToUserString( entry.permissions, entry.internalRightsList ) );
00271   mModified = entry.changed; // for dimap, so that earlier changes are still marked as changes
00272 }
00273 
00274 void KMail::FolderDiaACLTab::ListViewItem::save( ACLList& aclList,
00275 #ifdef KDEPIM_NEW_DISTRLISTS
00276                                                  KABC::AddressBook* addressBook,
00277 #else
00278                                                  KABC::DistributionListManager& manager,
00279 #endif
00280                                                  IMAPUserIdFormat userIdFormat )
00281 {
00282   // expand distribution lists
00283 #ifdef KDEPIM_NEW_DISTRLISTS
00284   KPIM::DistributionList list = KPIM::DistributionList::findByName( addressBook, userId(), false );
00285   if ( !list.isEmpty() ) {
00286     Q_ASSERT( mModified ); // it has to be new, it couldn't be stored as a distr list name....
00287     KPIM::DistributionList::Entry::List entryList = list.entries(addressBook);
00288     KPIM::DistributionList::Entry::List::ConstIterator it;
00289     // (we share for loop with the old-distrlist-code)
00290 #else
00291   // kaddrbook.cpp has a strange two-pass case-insensitive lookup; is it ok to be case sensitive?
00292   KABC::DistributionList* list = manager.list( userId() );
00293   if ( list ) {
00294     Q_ASSERT( mModified ); // it has to be new, it couldn't be stored as a distr list name....
00295     KABC::DistributionList::Entry::List entryList = list->entries();
00296     KABC::DistributionList::Entry::List::ConstIterator it; // nice number of "::"!
00297 #endif
00298     for( it = entryList.begin(); it != entryList.end(); ++it ) {
00299       QString email = (*it).email;
00300       if ( email.isEmpty() )
00301         email = addresseeToUserId( (*it).addressee, userIdFormat );
00302       ACLListEntry entry( email, QString::null, mPermissions );
00303       entry.changed = true;
00304       aclList.append( entry );
00305     }
00306   } else { // it wasn't a distribution list
00307     ACLListEntry entry( userId(), mInternalRightsList, mPermissions );
00308     if ( mModified ) {
00309       entry.internalRightsList = QString::null;
00310       entry.changed = true;
00311     }
00312     aclList.append( entry );
00313   }
00314 }
00315 
00317 
00318 KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, QWidget* parent, const char* name )
00319   : FolderDiaTab( parent, name ),
00320     mImapAccount( 0 ),
00321     mUserRights( 0 ),
00322     mDlg( dlg ),
00323     mChanged( false ), mAccepting( false ), mSaving( false )
00324 {
00325   QVBoxLayout* topLayout = new QVBoxLayout( this );
00326   // We need a widget stack to show either a label ("no acl support", "please wait"...)
00327   // or a listview.
00328   mStack = new QWidgetStack( this );
00329   topLayout->addWidget( mStack );
00330 
00331   mLabel = new QLabel( mStack );
00332   mLabel->setAlignment( AlignHCenter | AlignVCenter | WordBreak );
00333   mStack->addWidget( mLabel );
00334 
00335   mACLWidget = new QHBox( mStack );
00336   mACLWidget->setSpacing( KDialog::spacingHint() );
00337   mListView = new KListView( mACLWidget );
00338   mListView->setAllColumnsShowFocus( true );
00339   mStack->addWidget( mACLWidget );
00340   mListView->addColumn( i18n( "User Id" ) );
00341   mListView->addColumn( i18n( "Permissions" ) );
00342 
00343   connect( mListView, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)),
00344        SLOT(slotEditACL(QListViewItem*)) );
00345   connect( mListView, SIGNAL(returnPressed(QListViewItem*)),
00346        SLOT(slotEditACL(QListViewItem*)) );
00347   connect( mListView, SIGNAL(selectionChanged(QListViewItem*)),
00348        SLOT(slotSelectionChanged(QListViewItem*)) );
00349 
00350   QVBox* buttonBox = new QVBox( mACLWidget );
00351   buttonBox->setSpacing( KDialog::spacingHint() );
00352   mAddACL = new KPushButton( i18n( "Add Entry..." ), buttonBox );
00353   mEditACL = new KPushButton( i18n( "Modify Entry..." ), buttonBox );
00354   mRemoveACL = new KPushButton( i18n( "Remove Entry" ), buttonBox );
00355   QWidget *spacer = new QWidget( buttonBox );
00356   spacer->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Expanding );
00357 
00358   connect( mAddACL, SIGNAL( clicked() ), SLOT( slotAddACL() ) );
00359   connect( mEditACL, SIGNAL( clicked() ), SLOT( slotEditACL() ) );
00360   connect( mRemoveACL, SIGNAL( clicked() ), SLOT( slotRemoveACL() ) );
00361   mEditACL->setEnabled( false );
00362   mRemoveACL->setEnabled( false );
00363 
00364   connect( this, SIGNAL( changed(bool) ), SLOT( slotChanged(bool) ) );
00365 }
00366 
00367 // Warning before save() this will return the url of the _parent_ folder, when creating a new one
00368 KURL KMail::FolderDiaACLTab::imapURL() const
00369 {
00370   KURL url = mImapAccount->getUrl();
00371   url.setPath( mImapPath );
00372   return url;
00373 }
00374 
00375 void KMail::FolderDiaACLTab::initializeWithValuesFromFolder( KMFolder* folder )
00376 {
00377   // This can be simplified once KMFolderImap and KMFolderCachedImap have a common base class
00378   mFolderType = folder->folderType();
00379   if ( mFolderType == KMFolderTypeImap ) {
00380     KMFolderImap* folderImap = static_cast<KMFolderImap*>( folder->storage() );
00381     mImapPath = folderImap->imapPath();
00382     mImapAccount = folderImap->account();
00383     mUserRights = folderImap->userRights();
00384   }
00385   else if ( mFolderType == KMFolderTypeCachedImap ) {
00386     KMFolderCachedImap* folderImap = static_cast<KMFolderCachedImap*>( folder->storage() );
00387     mImapPath = folderImap->imapPath();
00388     mImapAccount = folderImap->account();
00389     mUserRights = folderImap->userRights();
00390   }
00391   else
00392     assert( 0 ); // see KMFolderDialog constructor
00393 }
00394 
00395 void KMail::FolderDiaACLTab::load()
00396 {
00397   if ( mDlg->folder() ) {
00398     // existing folder
00399     initializeWithValuesFromFolder( mDlg->folder() );
00400   } else if ( mDlg->parentFolder() ) {
00401     // new folder
00402     initializeWithValuesFromFolder( mDlg->parentFolder() );
00403     mChanged = true; // ensure that saving happens
00404   }
00405 
00406   // KABC knows email addresses.
00407   // We want LDAP userids.
00408   // Depending on the IMAP server setup, the userid can be the full email address,
00409   // or just the username part of it.
00410   // To know which one it is, we currently have a hidden config option,
00411   // but the default value is determined from the current user's own id.
00412   QString defaultFormat = "fullemail";
00413   // warning mImapAccount can be 0 if creating a subsubsubfolder with dimap...  (bug?)
00414   if ( mImapAccount && mImapAccount->login().find('@') == -1 )
00415     defaultFormat = "username"; // no @ found, so we assume it's just the username
00416   KConfigGroup configGroup( kmkernel->config(), "IMAP" );
00417   QString str = configGroup.readEntry( "UserIdFormat", defaultFormat );
00418   mUserIdFormat = FullEmail;
00419   if ( str == "username" )
00420     mUserIdFormat = UserName;
00421 
00422   if ( mFolderType == KMFolderTypeCachedImap ) {
00423     KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder();
00424     KMFolderCachedImap* folderImap = static_cast<KMFolderCachedImap*>( folder->storage() );
00425     if ( mUserRights == -1 ) { // error
00426       mLabel->setText( i18n( "Error retrieving user permissions." ) );
00427     } else if ( mUserRights == 0 /* can't happen anymore*/ || folderImap->aclList().isEmpty() ) {
00428       /* We either synced, or we read user rights from the config, so we can
00429          assume the server supports acls and an empty list means we haven't
00430          synced yet. */
00431       mLabel->setText( i18n( "Information not retrieved from server yet, please use \"Check Mail\"." ) );
00432     } else {
00433       loadFinished( folderImap->aclList() );
00434     }
00435     return;
00436   }
00437 
00438   // Loading, for online IMAP, consists of four steps:
00439   // 1) connect
00440   // 2) get user rights
00441   // 3) load ACLs
00442 
00443   // First ensure we are connected
00444   mStack->raiseWidget( mLabel );
00445   if ( !mImapAccount ) { // hmmm?
00446     mLabel->setText( i18n( "Error: no IMAP account defined for this folder" ) );
00447     return;
00448   }
00449   KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder();
00450   if ( folder && folder->storage() == mImapAccount->rootFolder() )
00451     return; // nothing to be done for the (virtual) account folder
00452   mLabel->setText( i18n( "Connecting to server %1, please wait..." ).arg( mImapAccount->host() ) );
00453   ImapAccountBase::ConnectionState state = mImapAccount->makeConnection();
00454   if ( state == ImapAccountBase::Error ) { // Cancelled by user, or slave can't start
00455     slotConnectionResult( -1, QString::null );
00456   } else if ( state == ImapAccountBase::Connecting ) {
00457     connect( mImapAccount, SIGNAL( connectionResult(int, const QString&) ),
00458              this, SLOT( slotConnectionResult(int, const QString&) ) );
00459   } else { // Connected
00460     slotConnectionResult( 0, QString::null );
00461   }
00462 }
00463 
00464 void KMail::FolderDiaACLTab::slotConnectionResult( int errorCode, const QString& errorMsg )
00465 {
00466   disconnect( mImapAccount, SIGNAL( connectionResult(int, const QString&) ),
00467               this, SLOT( slotConnectionResult(int, const QString&) ) );
00468   if ( errorCode ) {
00469     if ( errorCode == -1 ) // unspecified error
00470       mLabel->setText( i18n( "Error connecting to server %1" ).arg( mImapAccount->host() ) );
00471     else
00472       // Connection error (error message box already shown by the account)
00473       mLabel->setText( KIO::buildErrorString( errorCode, errorMsg ) );
00474     return;
00475   }
00476 
00477   if ( mUserRights == 0 ) {
00478     connect( mImapAccount, SIGNAL( receivedUserRights( KMFolder* ) ),
00479              this, SLOT( slotReceivedUserRights( KMFolder* ) ) );
00480     KMFolder* folder = mDlg->folder() ? mDlg->folder() : mDlg->parentFolder();
00481     mImapAccount->getUserRights( folder, mImapPath );
00482   }
00483   else
00484     startListing();
00485 }
00486 
00487 void KMail::FolderDiaACLTab::slotReceivedUserRights( KMFolder* folder )
00488 {
00489   if ( !mImapAccount->hasACLSupport() ) {
00490     mLabel->setText( i18n( "This IMAP server does not have support for access control lists (ACL)" ) );
00491     return;
00492   }
00493 
00494   if ( folder == mDlg->folder() ? mDlg->folder() : mDlg->parentFolder() ) {
00495     KMFolderImap* folderImap = static_cast<KMFolderImap*>( folder->storage() );
00496     mUserRights = folderImap->userRights();
00497     startListing();
00498   }
00499 }
00500 
00501 void KMail::FolderDiaACLTab::startListing()
00502 {
00503   // List ACLs of folder - or its parent, if creating a new folder
00504   mImapAccount->getACL( mDlg->folder() ? mDlg->folder() : mDlg->parentFolder(), mImapPath );
00505   connect( mImapAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
00506            this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
00507 }
00508 
00509 void KMail::FolderDiaACLTab::slotReceivedACL( KMFolder* folder, KIO::Job* job, const KMail::ACLList& aclList )
00510 {
00511   if ( folder == ( mDlg->folder() ? mDlg->folder() : mDlg->parentFolder() ) ) {
00512     disconnect( mImapAccount, SIGNAL(receivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )),
00513                 this, SLOT(slotReceivedACL( KMFolder*, KIO::Job*, const KMail::ACLList& )) );
00514 
00515     if ( job && job->error() ) {
00516       if ( job->error() == KIO::ERR_UNSUPPORTED_ACTION )
00517         mLabel->setText( i18n( "This IMAP server does not have support for access control lists (ACL)" ) );
00518       else
00519         mLabel->setText( i18n( "Error retrieving access control list (ACL) from server\n%1" ).arg( job->errorString() ) );
00520       return;
00521     }
00522 
00523     loadFinished( aclList );
00524   }
00525 }
00526 
00527 void KMail::FolderDiaACLTab::loadListView( const ACLList& aclList )
00528 {
00529   mListView->clear();
00530   for( ACLList::const_iterator it = aclList.begin(); it != aclList.end(); ++it ) {
00531     // -1 means deleted (for cachedimap), don't show those
00532     if ( (*it).permissions > -1 ) {
00533       ListViewItem* item = new ListViewItem( mListView );
00534       item->load( *it );
00535       if ( !mDlg->folder() ) // new folder? everything is new then
00536           item->setModified( true );
00537     }
00538   }
00539 }
00540 
00541 void KMail::FolderDiaACLTab::loadFinished( const ACLList& aclList )
00542 {
00543   loadListView( aclList );
00544   if ( mDlg->folder() ) // not when creating a new folder
00545     mInitialACLList = aclList;
00546   mStack->raiseWidget( mACLWidget );
00547   slotSelectionChanged( mListView->selectedItem() );
00548 }
00549 
00550 void KMail::FolderDiaACLTab::slotEditACL(QListViewItem* item)
00551 {
00552   if ( !item ) return;
00553   bool canAdmin = ( mUserRights & ACLJobs::Administer );
00554   // Same logic as in slotSelectionChanged, but this is also needed for double-click IIRC
00555   if ( canAdmin && mImapAccount && item ) {
00556     // Don't allow users to remove their own admin permissions - there's no way back
00557     ListViewItem* ACLitem = static_cast<ListViewItem *>( item );
00558     if ( mImapAccount->login() == ACLitem->userId() && ACLitem->permissions() == ACLJobs::All )
00559       canAdmin = false;
00560   }
00561   if ( !canAdmin ) return;
00562 
00563   ListViewItem* ACLitem = static_cast<ListViewItem *>( mListView->currentItem() );
00564   ACLEntryDialog dlg( mUserIdFormat, i18n( "Modify Permissions" ), this );
00565   dlg.setValues( ACLitem->userId(), ACLitem->permissions() );
00566   if ( dlg.exec() == QDialog::Accepted ) {
00567     QStringList userIds = dlg.userIds();
00568     Q_ASSERT( !userIds.isEmpty() ); // impossible, the OK button is disabled in that case
00569     ACLitem->setUserId( dlg.userIds().front() );
00570     ACLitem->setPermissions( dlg.permissions() );
00571     ACLitem->setModified( true );
00572     emit changed(true);
00573     if ( userIds.count() > 1 ) { // more emails were added, append them
00574       userIds.pop_front();
00575       addACLs( userIds, dlg.permissions() );
00576     }
00577   }
00578 }
00579 
00580 void KMail::FolderDiaACLTab::slotEditACL()
00581 {
00582   slotEditACL( mListView->currentItem() );
00583 }
00584 
00585 void KMail::FolderDiaACLTab::addACLs( const QStringList& userIds, unsigned int permissions )
00586 {
00587   for( QStringList::const_iterator it = userIds.begin(); it != userIds.end(); ++it ) {
00588     ListViewItem* ACLitem = new ListViewItem( mListView );
00589     ACLitem->setUserId( *it );
00590     ACLitem->setPermissions( permissions );
00591     ACLitem->setModified( true );
00592     ACLitem->setNew( true );
00593   }
00594 }
00595 
00596 void KMail::FolderDiaACLTab::slotAddACL()
00597 {
00598   ACLEntryDialog dlg( mUserIdFormat, i18n( "Add Permissions" ), this );
00599   if ( dlg.exec() == QDialog::Accepted ) {
00600     const QStringList userIds = dlg.userIds();
00601     addACLs( dlg.userIds(), dlg.permissions() );
00602     emit changed(true);
00603   }
00604 }
00605 
00606 void KMail::FolderDiaACLTab::slotSelectionChanged(QListViewItem* item)
00607 {
00608   bool canAdmin = ( mUserRights & ACLJobs::Administer );
00609   bool canAdminThisItem = canAdmin;
00610   if ( canAdmin && mImapAccount && item ) {
00611     // Don't allow users to remove their own admin permissions - there's no way back
00612     ListViewItem* ACLitem = static_cast<ListViewItem *>( item );
00613     if ( mImapAccount->login() == ACLitem->userId() && ACLitem->permissions() == ACLJobs::All )
00614       canAdminThisItem = false;
00615   }
00616 
00617   bool lvVisible = mStack->visibleWidget() == mACLWidget;
00618   mAddACL->setEnabled( lvVisible && canAdmin && !mSaving );
00619   mEditACL->setEnabled( item && lvVisible && canAdminThisItem && !mSaving );
00620   mRemoveACL->setEnabled( item && lvVisible && canAdminThisItem && !mSaving );
00621 }
00622 
00623 void KMail::FolderDiaACLTab::slotRemoveACL()
00624 {
00625   ListViewItem* ACLitem = static_cast<ListViewItem *>( mListView->currentItem() );
00626   if ( !ACLitem )
00627     return;
00628   if ( !ACLitem->isNew() ) {
00629     if ( mImapAccount && mImapAccount->login() == ACLitem->userId() ) {
00630       if ( KMessageBox::Cancel == KMessageBox::warningContinueCancel( topLevelWidget(),
00631          i18n( "Do you really want to remove your own permissions for this folder? You will not be able to access it afterwards." ), i18n( "Remove" ) ) )
00632         return;
00633     }
00634     mRemovedACLs.append( ACLitem->userId() );
00635   }
00636   delete ACLitem;
00637   emit changed(true);
00638 }
00639 
00640 KMail::FolderDiaTab::AcceptStatus KMail::FolderDiaACLTab::accept()
00641 {
00642   if ( !mChanged || !mImapAccount )
00643     return Accepted; // (no change made), ok for accepting the dialog immediately
00644   // If there were changes, we need to apply them first (which is async)
00645   save();
00646   if ( mFolderType == KMFolderTypeCachedImap )
00647     return Accepted; // cached imap: changes saved immediately into the folder
00648   // disconnected imap: async job[s] running
00649   mAccepting = true;
00650   return Delayed;
00651 }
00652 
00653 bool KMail::FolderDiaACLTab::save()
00654 {
00655   if ( !mChanged || !mImapAccount ) // no changes
00656     return true;
00657   assert( mDlg->folder() ); // should have been created already
00658 
00659   // Expand distribution lists. This is necessary because after Apply
00660   // we would otherwise be able to "modify" the permissions for a distr list,
00661   // which wouldn't work since the ACLList and the server only know about the
00662   // individual addresses.
00663   // slotACLChanged would have trouble matching the item too.
00664   // After reloading we'd see the list expanded anyway,
00665   // so this is more consistent.
00666   // But we do it now and not when inserting it, because this allows to
00667   // immediately remove a wrongly inserted distr list without having to
00668   // remove 100 items.
00669   // Now, how to expand them? Playing with listviewitem iterators and inserting
00670   // listviewitems at the same time sounds dangerous, so let's just save into
00671   // ACLList and reload that.
00672   KABC::AddressBook *addressBook = KABC::StdAddressBook::self( true );
00673 #ifndef KDEPIM_NEW_DISTRLISTS
00674   KABC::DistributionListManager manager( addressBook );
00675   manager.load();
00676 #endif
00677   ACLList aclList;
00678   for ( QListViewItem* item = mListView->firstChild(); item; item = item->nextSibling() ) {
00679     ListViewItem* ACLitem = static_cast<ListViewItem *>( item );
00680     ACLitem->save( aclList,
00681 #ifdef KDEPIM_NEW_DISTRLISTS
00682                    addressBook,
00683 #else
00684                    manager,
00685 #endif
00686                    mUserIdFormat );
00687   }
00688   loadListView( aclList );
00689 
00690   // Now compare with the initial ACLList, because if the user renamed a userid
00691   // we have to add the old userid to the "to be deleted" list.
00692   for( ACLList::ConstIterator init = mInitialACLList.begin(); init != mInitialACLList.end(); ++init ) {
00693     bool isInNewList = false;
00694     QString uid = (*init).userId;
00695     for( ACLList::ConstIterator it = aclList.begin(); it != aclList.end() && !isInNewList; ++it )
00696       isInNewList = uid == (*it).userId;
00697     if ( !isInNewList && !mRemovedACLs.contains(uid) )
00698       mRemovedACLs.append( uid );
00699   }
00700 
00701   for ( QStringList::ConstIterator rit = mRemovedACLs.begin(); rit != mRemovedACLs.end(); ++rit ) {
00702     // We use permissions == -1 to signify deleting. At least on cyrus, setacl(0) or deleteacl are the same,
00703     // but I'm not sure if that's true for all servers.
00704     ACLListEntry entry( *rit, QString::null, -1 );
00705     entry.changed = true;
00706     aclList.append( entry );
00707   }
00708 
00709   // aclList is finally ready. We can save it (dimap) or apply it (imap).
00710 
00711   if ( mFolderType == KMFolderTypeCachedImap ) {
00712     // Apply the changes to the aclList stored in the folder.
00713     // We have to do this now and not before, so that cancel really cancels.
00714     KMFolderCachedImap* folderImap = static_cast<KMFolderCachedImap*>( mDlg->folder()->storage() );
00715     folderImap->setACLList( aclList );
00716     return true;
00717   }
00718 
00719   mACLList = aclList;
00720 
00721   KMFolderImap* parentImap = mDlg->parentFolder() ? static_cast<KMFolderImap*>( mDlg->parentFolder()->storage() ) : 0;
00722 
00723   if ( mDlg->isNewFolder() ) {
00724     // The folder isn't created yet, wait for it
00725     // It's a two-step process (mkdir+listDir) so we wait for the dir listing to be complete
00726     connect( parentImap, SIGNAL( directoryListingFinished(KMFolderImap*) ),
00727              this, SLOT( slotDirectoryListingFinished(KMFolderImap*) ) );
00728   } else {
00729       slotDirectoryListingFinished( parentImap );
00730   }
00731   return true;
00732 }
00733 
00734 void KMail::FolderDiaACLTab::slotDirectoryListingFinished(KMFolderImap* f)
00735 {
00736   if ( !f ||
00737        f != static_cast<KMFolderImap*>( mDlg->parentFolder()->storage() ) ||
00738        !mDlg->folder() ||
00739        !mDlg->folder()->storage() ) {
00740     emit readyForAccept();
00741     return;
00742   }
00743 
00744   // When creating a new folder with online imap, update mImapPath
00745   KMFolderImap* folderImap = static_cast<KMFolderImap*>( mDlg->folder()->storage() );
00746   if ( !folderImap || folderImap->imapPath().isEmpty() )
00747     return;
00748   mImapPath = folderImap->imapPath();
00749 
00750   KIO::Job* job = ACLJobs::multiSetACL( mImapAccount->slave(), imapURL(), mACLList );
00751   ImapAccountBase::jobData jd;
00752   jd.total = 1; jd.done = 0; jd.parent = 0;
00753   mImapAccount->insertJob(job, jd);
00754 
00755   connect(job, SIGNAL(result(KIO::Job *)),
00756           SLOT(slotMultiSetACLResult(KIO::Job *)));
00757   connect(job, SIGNAL(aclChanged( const QString&, int )),
00758           SLOT(slotACLChanged( const QString&, int )) );
00759 }
00760 
00761 void KMail::FolderDiaACLTab::slotMultiSetACLResult(KIO::Job* job)
00762 {
00763   ImapAccountBase::JobIterator it = mImapAccount->findJob( job );
00764   if ( it == mImapAccount->jobsEnd() ) return;
00765   mImapAccount->removeJob( it );
00766 
00767   if ( job->error() ) {
00768     job->showErrorDialog( this );
00769     if ( mAccepting ) {
00770       emit cancelAccept();
00771       mAccepting = false; // don't emit readyForAccept anymore
00772     }
00773   } else {
00774     if ( mAccepting )
00775       emit readyForAccept();
00776   }
00777 }
00778 
00779 void KMail::FolderDiaACLTab::slotACLChanged( const QString& userId, int permissions )
00780 {
00781   // The job indicates success in changing the permissions for this user
00782   // -> we note that it's been done.
00783   bool ok = false;
00784   if ( permissions > -1 ) {
00785     for ( QListViewItem* item = mListView->firstChild(); item; item = item->nextSibling() ) {
00786       ListViewItem* ACLitem = static_cast<ListViewItem *>( item );
00787       if ( ACLitem->userId() == userId ) {
00788         ACLitem->setModified( false );
00789         ACLitem->setNew( false );
00790         ok = true;
00791         break;
00792       }
00793     }
00794   } else {
00795     uint nr = mRemovedACLs.remove( userId );
00796     ok = ( nr > 0 );
00797   }
00798   if ( !ok )
00799     kdWarning(5006) << k_funcinfo << " no item found for userId " << userId << endl;
00800 }
00801 
00802 void KMail::FolderDiaACLTab::slotChanged( bool b )
00803 {
00804   mChanged = b;
00805 }
00806 
00807 bool KMail::FolderDiaACLTab::supports( KMFolder* refFolder )
00808 {
00809   ImapAccountBase* imapAccount = 0;
00810   if ( refFolder->folderType() == KMFolderTypeImap )
00811     imapAccount = static_cast<KMFolderImap*>( refFolder->storage() )->account();
00812   else
00813     imapAccount = static_cast<KMFolderCachedImap*>( refFolder->storage() )->account();
00814   return imapAccount && imapAccount->hasACLSupport(); // support for ACLs (or not tried connecting yet)
00815 }
00816 
00817 #include "folderdiaacltab.moc"