kmail

folderdiaacltab.cpp

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