diff --git a/Makefile.am b/Makefile.am index 8737d93ef..bc55ba6fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -93,7 +93,7 @@ libkmailprivate_la_SOURCES = kmmessage.cpp kmmainwin.cpp configuredialog.cpp \ korganizeriface.stub messagecomposer.cpp \ globalsettings.kcfgc \ regexplineedit.cpp rulewidgethandlermanager.cpp \ - headerlistquicksearch.cpp + headerlistquicksearch.cpp acljobs.cpp folderdiaacltab.cpp kmail_SOURCES = main.cpp diff --git a/acljobs.cpp b/acljobs.cpp new file mode 100644 index 000000000..7b8148069 --- /dev/null +++ b/acljobs.cpp @@ -0,0 +1,202 @@ +/** + * acljobs.cpp + * + * Copyright (c) 2004 David Faure + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#include "acljobs.h" +#include +#include + +using namespace KMail; + +static unsigned int IMAPRightsToPermission( const QString& str ) { + unsigned int perm = 0; + bool foundSeenPerm = false; + uint len = str.length(); + for (uint i = 0; i < len; ++i) { + QChar ch = str[i]; + switch ( ch.latin1() ) { + case 'l': perm |= ACLJobs::List; break; + case 'r': perm |= ACLJobs::Read; break; + case 's': foundSeenPerm = true; break; + case 'w': perm |= ACLJobs::WriteFlags; break; + case 'i': perm |= ACLJobs::Insert; break; + case 'c': perm |= ACLJobs::Create; break; + case 'd': perm |= ACLJobs::Delete; break; + case 'a': perm |= ACLJobs::Administer; break; + default: break; + } + } + if ( ( perm & ACLJobs::Read ) && str.find( 's' ) == -1 ) { + // Reading without 'seen' is, well, annoying. Unusable, even. + // So we treat 'rs' as a single one. + // But if the permissions were set out of kmail, better check that both are set + kdWarning(5006) << "IMAPRightsToPermission: found read (r) but not seen (s). Things will not work well." << endl; + if ( perm & ACLJobs::Administer ) + kdWarning(5006) << "You can change this yourself in the ACL dialog" << endl; + else + kdWarning(5006) << "Ask your admin for 's' permissions." << endl; + // Is the above correct enough to be turned into a KMessageBox? + } + + return perm; +} + +static QCString permissionsToIMAPRights( unsigned int permissions ) { + QCString str = ""; + if ( permissions & ACLJobs::List ) + str += 'l'; + if ( permissions & ACLJobs::Read ) + str += "rs"; + if ( permissions & ACLJobs::WriteFlags ) + str += 'w'; + if ( permissions & ACLJobs::Insert ) + str += 'i'; + if ( permissions & ACLJobs::Create ) + str += 'c'; + if ( permissions & ACLJobs::Delete ) + str += 'd'; + if ( permissions & ACLJobs::Administer ) + str += 'a'; + return str; +} + +#ifndef NDEBUG +QString ACLJobs::permissionsToString( unsigned int permissions ) +{ + QString str; + if ( permissions & ACLJobs::List ) + str += "List "; + if ( permissions & ACLJobs::Read ) + str += "Read "; + if ( permissions & ACLJobs::WriteFlags ) + str += "Write "; + if ( permissions & ACLJobs::Insert ) + str += "Insert "; + if ( permissions & ACLJobs::Create ) + str += "Create "; + if ( permissions & ACLJobs::Delete ) + str += "Delete "; + if ( permissions & ACLJobs::Administer ) + str += "Administer "; + if ( !str.isEmpty() ) + str.truncate( str.length() - 1 ); + return str; +} +#endif + +KIO::SimpleJob* ACLJobs::setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions ) +{ + QString perm = QString::fromLatin1( permissionsToIMAPRights( permissions ) ); + + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)'A' << (int)'S' << url << user << perm; + + KIO::SimpleJob* job = KIO::special( url, packedArgs, false ); + KIO::Scheduler::assignJobToSlave( slave, job ); + return job; +} + +ACLJobs::DeleteACLJob* ACLJobs::deleteACL( KIO::Slave* slave, const KURL& url, const QString& user ) +{ + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)'A' << (int)'D' << url << user; + + ACLJobs::DeleteACLJob* job = new ACLJobs::DeleteACLJob( url, user, packedArgs, false ); + KIO::Scheduler::assignJobToSlave( slave, job ); + return job; +} + +ACLJobs::GetACLJob* ACLJobs::getACL( KIO::Slave* slave, const KURL& url ) +{ + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)'A' << (int)'G' << url; + + ACLJobs::GetACLJob* job = new ACLJobs::GetACLJob( url, packedArgs, false ); + KIO::Scheduler::assignJobToSlave( slave, job ); + return job; +} + +ACLJobs::GetUserRightsJob* ACLJobs::getUserRights( KIO::Slave* slave, const KURL& url ) +{ + QByteArray packedArgs; + QDataStream stream( packedArgs, IO_WriteOnly ); + stream << (int)'A' << (int)'M' << url; + + ACLJobs::GetUserRightsJob* job = new ACLJobs::GetUserRightsJob( url, packedArgs, false ); + KIO::Scheduler::assignJobToSlave( slave, job ); + return job; +} + +ACLJobs::GetACLJob::GetACLJob( const KURL& url, const QByteArray &packedArgs, + bool showProgressInfo ) + : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ) +{ + connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)), + SLOT(slotInfoMessage(KIO::Job*,const QString&)) ); +} + +void ACLJobs::GetACLJob::slotInfoMessage( KIO::Job*, const QString& str ) +{ + // Parse the result + QStringList lst = QStringList::split( " ", str ); + while ( lst.count() >= 2 ) // we take items 2 by 2 + { + QString user = lst.front(); lst.pop_front(); + QString imapRights = lst.front(); lst.pop_front(); + unsigned int perm = IMAPRightsToPermission( imapRights ); + m_entries.append( ACLListEntry( user, imapRights, perm ) ); + } +} + +ACLJobs::GetUserRightsJob::GetUserRightsJob( const KURL& url, const QByteArray &packedArgs, + bool showProgressInfo ) + : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ) +{ + connect( this, SIGNAL(infoMessage(KIO::Job*,const QString&)), + SLOT(slotInfoMessage(KIO::Job*,const QString&)) ); +} + +void ACLJobs::GetUserRightsJob::slotInfoMessage( KIO::Job*, const QString& str ) +{ + // Parse the result + m_permissions = IMAPRightsToPermission( str ); +} + +ACLJobs::DeleteACLJob::DeleteACLJob( const KURL& url, const QString& userId, + const QByteArray &packedArgs, + bool showProgressInfo ) + : KIO::SimpleJob( url, KIO::CMD_SPECIAL, packedArgs, showProgressInfo ), + mUserId( userId ) +{ +} + +#include "acljobs.moc" diff --git a/acljobs.h b/acljobs.h new file mode 100644 index 000000000..31b356fbb --- /dev/null +++ b/acljobs.h @@ -0,0 +1,144 @@ +/** + * acljobs.h + * + * Copyright (c) 2004 David Faure + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#ifndef KMACLJOBS_H +#define KMACLJOBS_H + +#include +#include + +namespace KMail { + +/** + * This namespace contains functions that return jobs for ACL operations. + * + * The current implementation is tied to IMAP. + * If someone wants to extend this to other protocols, turn the class into a namespace + * and use virtual methods. + */ +namespace ACLJobs { + + /// Bitfield modelling the possible permissions. + /// This is modelled after the imap4 permissions except that Read is "rs" + /// and "p" (post) isn't there. The semantics of the bits is protocol-dependent. + enum ACLPermissions { + List = 1, + Read = 2, + WriteFlags = 4, + Insert = 8, + Create = 16, + Delete = 32, + Administer = 64, + // alias for "all read/write permissions except admin" + AllWrite = List | Read | WriteFlags | Insert | Create | Delete, + // alias for "all permissions" + All = List | Read | WriteFlags | Insert | Create | Delete | Administer + }; + /// Set the permissions for a given user on a given url + KIO::SimpleJob* setACL( KIO::Slave* slave, const KURL& url, const QString& user, unsigned int permissions ); + + class DeleteACLJob; + /// Delete the permissions for a given user on a given url + DeleteACLJob* deleteACL( KIO::Slave* slave, const KURL& url, const QString& user ); + + class GetACLJob; + /// List all ACLs for a given url + GetACLJob* getACL( KIO::Slave* slave, const KURL& url ); + + class GetUserRightsJob; + /// Get the users' rights for a given url + GetUserRightsJob* getUserRights( KIO::Slave* slave, const KURL& url ); + + /// One entry in the ACL list: user and permissions + struct ACLListEntry { + ACLListEntry() {} // for QValueVector + ACLListEntry( const QString& u, const QString& irl, unsigned int p ) + : userid( u ), internalRightsList( irl ), permissions( p ) {} + QString userid; + QString internalRightsList; ///< protocol-dependent string (e.g. IMAP rights list) + unsigned int permissions; ///< based on the ACLPermissions enum + }; + + /// List all ACLs for a given url + class GetACLJob : public KIO::SimpleJob + { + Q_OBJECT + public: + GetACLJob( const KURL& url, const QByteArray &packedArgs, + bool showProgressInfo ); + + const QValueVector& entries() const { return m_entries; } + + protected slots: + void slotInfoMessage( KIO::Job*, const QString& ); + private: + QValueVector m_entries; + }; + + /// Get the users' rights for a given url + class GetUserRightsJob : public KIO::SimpleJob + { + Q_OBJECT + public: + GetUserRightsJob( const KURL& url, const QByteArray &packedArgs, + bool showProgressInfo ); + unsigned int permissions() const { return m_permissions; } + + protected slots: + void slotInfoMessage( KIO::Job*, const QString& ); + private: + unsigned int m_permissions; + }; + + /// Delete the permissions for a given user on a given url + /// This class only exists to store the userid in the job + class DeleteACLJob : public KIO::SimpleJob + { + Q_OBJECT + public: + DeleteACLJob( const KURL& url, const QString& userId, + const QByteArray &packedArgs, + bool showProgressInfo ); + + QString userId() const { return mUserId; } + + private: + QString mUserId; + }; + +#ifndef NDEBUG + QString permissionsToString( unsigned int permissions ); +#endif +} + +} // namespace + +#endif /* KMACLJOBS_H */ diff --git a/folderdiaacltab.cpp b/folderdiaacltab.cpp new file mode 100644 index 000000000..aa0cc07f5 --- /dev/null +++ b/folderdiaacltab.cpp @@ -0,0 +1,484 @@ +// -*- mode: C++; c-file-style: "gnu" -*- +/** + * folderdiaacltab.cpp + * + * Copyright (c) 2004 David Faure + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ + +#include "folderdiaacltab.h" +#include "acljobs.h" +#include "kmfolderimap.h" +#include "kmfoldercachedimap.h" +#include "kmacctcachedimap.h" +#include "kmfolder.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace KMail; + +// In case your kdelibs is < 3.3 +#ifndef I18N_NOOP2 +#define I18N_NOOP2( comment,x ) x +#endif + +// The set of standard permission sets +static const struct { + unsigned int permissions; + const char* userString; +} standardPermissions[] = { + { 0, I18N_NOOP2( "Permissions", "None" ) }, + { ACLJobs::List | ACLJobs::Read, I18N_NOOP2( "Permissions", "Read" ) }, + { ACLJobs::List | ACLJobs::Read | ACLJobs::Insert, I18N_NOOP2( "Permissions", "Append" ) }, + { ACLJobs::AllWrite, I18N_NOOP2( "Permissions", "Write" ) }, + { ACLJobs::All, I18N_NOOP2( "Permissions", "All" ) } +}; + + +KMail::ACLEntryDialog::ACLEntryDialog( const QString& caption, QWidget* parent, const char* name ) + : KDialogBase( parent, name, true /*modal*/, caption, + KDialogBase::Ok|KDialogBase::Cancel, KDialogBase::Ok, true /*sep*/ ) +{ + QWidget *page = new QWidget( this ); + setMainWidget(page); + QGridLayout *topLayout = new QGridLayout( page, 3, 2, 0, spacingHint() ); + + QLabel *label = new QLabel( i18n( "&User Identifier" ), page ); + topLayout->addWidget( label, 0, 0 ); + + mUserIdLineEdit = new KLineEdit( page ); + topLayout->addWidget( mUserIdLineEdit, 0, 1 ); + label->setBuddy( mUserIdLineEdit ); + 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." ) ); + + mButtonGroup = new QVButtonGroup( i18n( "Permissions" ), page ); + topLayout->addMultiCellWidget( mButtonGroup, 1, 1, 0, 1 ); + + for ( unsigned int i = 0; + i < sizeof( standardPermissions ) / sizeof( *standardPermissions ); + ++i ) { + QRadioButton* cb = new QRadioButton( i18n( "Permissions", standardPermissions[i].userString ), mButtonGroup ); + // We store the permission value (bitfield) as the id of the radiobutton in the group + mButtonGroup->insert( cb, standardPermissions[i].permissions ); + } + topLayout->setRowStretch(2, 10); + + connect( mUserIdLineEdit, SIGNAL( textChanged( const QString& ) ), SLOT( slotChanged() ) ); + connect( mButtonGroup, SIGNAL( clicked( int ) ), SLOT( slotChanged() ) ); + enableButtonOK( false ); +} + +void KMail::ACLEntryDialog::slotChanged() +{ + enableButtonOK( !mUserIdLineEdit->text().isEmpty() && mButtonGroup->selected() != 0 ); +} + +void KMail::ACLEntryDialog::setValues( const QString& userId, unsigned int permissions ) +{ + mUserIdLineEdit->setText( userId ); + mButtonGroup->setButton( permissions ); + enableButtonOK( !userId.isEmpty() ); +} + +QString KMail::ACLEntryDialog::userId() const +{ + return mUserIdLineEdit->text(); +} + +unsigned int KMail::ACLEntryDialog::permissions() const +{ + return mButtonGroup->selectedId(); +} + +// class KMail::FolderDiaACLTab::ListView : public KListView +// { +// public: +// ListView( QWidget* parent, const char* name = 0 ) : KListView( parent, name ) {} +// }; + +class KMail::FolderDiaACLTab::ListViewItem : public KListViewItem +{ +public: + ListViewItem( QListView* listview ) + : KListViewItem( listview, listview->lastItem() ), + mJob( 0 ), mModified( false ) {} + + void load( const ACLJobs::ACLListEntry& entry ); + + QString userId() const { return text( 0 ); } + void setUserId( const QString& userId ) { setText( 0, userId ); } + + unsigned int permissions() const { return mPermissions; } + void setPermissions( unsigned int permissions ); + + bool isModified() const { return mModified; } + void setModified( bool b ) { mModified = b; } + + KIO::Job* job() const { return mJob; } + void setJob( KIO::Job* job ) { mJob = job; } + +private: + KIO::Job* mJob; + unsigned int mPermissions; + bool mModified; +}; + +// internalRightsList is only used if permissions doesn't match the standard set +static QString permissionsToUserString( unsigned int permissions, const QString& internalRightsList ) +{ + for ( unsigned int i = 0; + i < sizeof( standardPermissions ) / sizeof( *standardPermissions ); + ++i ) { + if ( permissions == standardPermissions[i].permissions ) + return i18n( "Permissions", standardPermissions[i].userString ); + } + if ( internalRightsList.isEmpty() ) + return i18n( "Custom Permissions" ); // not very helpful, but shouldn't happen + else + return i18n( "Custom Permissions (%1)" ).arg( internalRightsList ); +} + +void KMail::FolderDiaACLTab::ListViewItem::setPermissions( unsigned int permissions ) +{ + mPermissions = permissions; + setText( 1, permissionsToUserString( permissions, QString::null ) ); +} + +void KMail::FolderDiaACLTab::ListViewItem::load( const ACLJobs::ACLListEntry& entry ) +{ + setUserId( entry.userid ); + mPermissions = entry.permissions; + setText( 1, permissionsToUserString( entry.permissions, entry.internalRightsList ) ); +} + +//// + +KMail::FolderDiaACLTab::FolderDiaACLTab( KMFolderDialog* dlg, QWidget* parent, const char* name ) + : FolderDiaTab( parent, name ), mDlg( dlg ), + mJobCounter( 0 ), + mChanged( false ), mAccepting( false ) +{ + QVBoxLayout* topLayout = new QVBoxLayout( this ); + // We need a widget stack to show either a label ("no acl support", "please wait"...) + // or a listview. + mStack = new QWidgetStack( this ); + topLayout->addWidget( mStack ); + + mLabel = new QLabel( mStack ); + mLabel->setAlignment( AlignHCenter | AlignVCenter | WordBreak ); + mStack->addWidget( mLabel ); + + mACLWidget = new QHBox( mStack ); + mListView = new KListView( mACLWidget ); + mListView->setAllColumnsShowFocus( true ); + mStack->addWidget( mACLWidget ); + mListView->addColumn( i18n( "User Id" ) ); + mListView->addColumn( i18n( "Permissions" ) ); + + connect( mListView, SIGNAL(doubleClicked(QListViewItem*,const QPoint&,int)), + SLOT(slotEditACL(QListViewItem*)) ); + connect( mListView, SIGNAL(returnPressed(QListViewItem*)), + SLOT(slotEditACL(QListViewItem*)) ); + connect( mListView, SIGNAL(selectionChanged(QListViewItem*)), + SLOT(slotSelectionChanged(QListViewItem*)) ); + + QVBox* buttonBox = new QVBox( mACLWidget ); + mAddACL = new KPushButton( i18n( "Add entry" ), buttonBox ); + mEditACL = new KPushButton( i18n( "Modify entry" ), buttonBox ); + mRemoveACL = new KPushButton( i18n( "Remove entry" ), buttonBox ); + QSpacerItem* spacer = new QSpacerItem( 0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding ); + static_cast( buttonBox->layout() )->addItem( spacer ); + + connect( mAddACL, SIGNAL( clicked() ), SLOT( slotAddACL() ) ); + connect( mEditACL, SIGNAL( clicked() ), SLOT( slotEditACL() ) ); + connect( mRemoveACL, SIGNAL( clicked() ), SLOT( slotRemoveACL() ) ); + mEditACL->setEnabled( false ); + mRemoveACL->setEnabled( false ); + + connect( this, SIGNAL( changed(bool) ), SLOT( slotChanged(bool) ) ); +} + +// This can probably be removed once KMFolderImap and KMFolderCachedImap have a common base class +KMail::ImapAccountBase* KMail::FolderDiaACLTab::imapAccount() const +{ + KMFolder* folder = mDlg->folder(); + if ( folder->folderType() == KMFolderTypeImap ) + return static_cast( folder->storage() )->account(); + else if ( folder->folderType() == KMFolderTypeCachedImap ) + return static_cast( folder->storage() )->account(); + else + assert( 0 ); // see KMFolderDialog constructor + return 0; +} + +#if 0 // not needed right now +// This can probably be removed once KMFolderImap and KMFolderCachedImap have a common base class +QString KMail::FolderDiaACLTab::imapPath() const +{ + KMFolder* folder = mDlg->folder(); + if ( folder->folderType() == KMFolderTypeImap ) + return static_cast( folder->storage() )->imapPath(); + else if ( folder->folderType() == KMFolderTypeCachedImap ) + return static_cast( folder->storage() )->imapPath(); + else + assert( 0 ); // see KMFolderDialog constructor + return 0; +} +#endif + +// This can probably be removed once KMFolderImap and KMFolderCachedImap have a common base class +KURL KMail::FolderDiaACLTab::imapURL() const +{ + ImapAccountBase* account = imapAccount(); + KURL url = account->getUrl(); + KMFolder* folder = mDlg->folder(); + if ( folder->folderType() == KMFolderTypeImap ) + url.setPath( static_cast( folder->storage() )->imapPath() ); + else if ( folder->folderType() == KMFolderTypeCachedImap ) + url.setPath( static_cast( folder->storage() )->imapPath() ); + else + assert( 0 ); // see KMFolderDialog constructor + return url; +} + +void KMail::FolderDiaACLTab::load() +{ + // First ensure we are connected + ImapAccountBase* account = imapAccount(); + mLabel->setText( i18n( "Connecting to server %1, please wait..." ).arg( account->host() ) ); + mStack->raiseWidget( mLabel ); + ImapAccountBase::ConnectionState state = account->makeConnection(); + if ( state == ImapAccountBase::Error ) { // Cancelled by user, or slave can't start + slotConnectionResult( 1 ); // any error code != 0 + } else if ( state == ImapAccountBase::Connecting ) { + connect( account, SIGNAL( connectionResult(int) ), + this, SLOT( slotConnectionResult(int) ) ); + } else { // Connected + slotConnectionResult( 0 ); + } +} + +void KMail::FolderDiaACLTab::slotConnectionResult( int errorCode ) +{ + ImapAccountBase* account = imapAccount(); + disconnect( account, SIGNAL( connectionResult(int) ), + this, SLOT( slotConnectionResult(int) ) ); + if ( errorCode ) { + // Error (error message already shown by the account) + mLabel->setText( i18n( "Error connecting to server %1" ).arg( account->host() ) ); + return; + } + + ACLJobs::GetACLJob* job = ACLJobs::getACL( account->slave(), imapURL() ); + + ImapAccountBase::jobData jd; + jd.total = 1; jd.done = 0; jd.parent = NULL; + account->insertJob(job, jd); + + connect(job, SIGNAL(result(KIO::Job *)), + SLOT(slotGetACLResult(KIO::Job *))); +} + +void KMail::FolderDiaACLTab::slotGetACLResult(KIO::Job *job) +{ + ImapAccountBase* account = imapAccount(); + ImapAccountBase::JobIterator it = account->findJob( job ); + if ( it == account->jobsEnd() ) return; + account->removeJob( it ); + if ( job->error() ) { + mLabel->setText( i18n( "Error retrieving access control list (ACL) from server\n%1" ).arg( job->errorString() ) ); + return; + } + + ACLJobs::GetACLJob* aclJob = static_cast( job ); + // Now we can populate the listview + const QValueVector& aclList = aclJob->entries(); + for( QValueVector::ConstIterator it = aclList.begin(); it != aclList.end(); ++it ) { + ListViewItem* item = new ListViewItem( mListView ); + item->load( *it ); + } + mStack->raiseWidget( mACLWidget ); + slotSelectionChanged( mListView->selectedItem() ); +} + +void KMail::FolderDiaACLTab::slotEditACL(QListViewItem* item) +{ + if ( !item ) return; + ListViewItem* ACLitem = static_cast( mListView->currentItem() ); + ACLEntryDialog dlg( i18n( "Modify Permissions" ), this ); + dlg.setValues( ACLitem->userId(), ACLitem->permissions() ); + if ( dlg.exec() == QDialog::Accepted ) { + ACLitem->setUserId( dlg.userId() ); + ACLitem->setPermissions( dlg.permissions() ); + ACLitem->setModified( true ); + emit changed(true); + } +} + +void KMail::FolderDiaACLTab::slotEditACL() +{ + slotEditACL( mListView->currentItem() ); +} + +void KMail::FolderDiaACLTab::slotAddACL() +{ + ACLEntryDialog dlg( i18n( "Add Permissions" ), this ); + if ( dlg.exec() == QDialog::Accepted ) { + ListViewItem* ACLitem = new ListViewItem( mListView ); + ACLitem->setUserId( dlg.userId() ); + ACLitem->setPermissions( dlg.permissions() ); + ACLitem->setModified( true ); + emit changed(true); + } +} + +void KMail::FolderDiaACLTab::slotSelectionChanged(QListViewItem* item) +{ + bool lvVisible = mStack->visibleWidget() == mACLWidget; + mAddACL->setEnabled( lvVisible ); + mEditACL->setEnabled( item && lvVisible ); + mRemoveACL->setEnabled( item && lvVisible ); +} + +void KMail::FolderDiaACLTab::ACLJobDone(KIO::Job* job) +{ + --mJobCounter; + if ( job->error() ) { + job->showErrorDialog( this ); + if ( mAccepting ) { + emit cancelAccept(); + mAccepting = false; // don't emit readyForAccept anymore + } + } + if ( mJobCounter == 0 && mAccepting ) + emit readyForAccept(); +} + +// Called by 'add' and 'edit' jobs fired from save() +void KMail::FolderDiaACLTab::slotSetACLResult(KIO::Job* job) +{ + bool ok = false; + for ( QListViewItem* item = mListView->firstChild(); item; item = item->nextSibling() ) { + ListViewItem* ACLitem = static_cast( item ); + if ( ACLitem->job() == job ) { + if ( !job->error() ) { + // Success -> reset flags + ACLitem->setModified( false ); + } + ACLitem->setJob( 0 ); + ok = true; + break; + } + } + if ( !ok ) + kdWarning(5006) << k_funcinfo << " no item found for job " << job << endl; + ACLJobDone( job ); +} + +void KMail::FolderDiaACLTab::slotDeleteACLResult(KIO::Job* job) +{ + if ( !job->error() ) { + // Success -> remove from list + ACLJobs::DeleteACLJob* delJob = static_cast( job ); + Q_ASSERT( mRemovedACLs.contains( delJob->userId() ) ); + mRemovedACLs.remove( delJob->userId() ); + } + ACLJobDone( job ); +} + +void KMail::FolderDiaACLTab::slotRemoveACL() +{ + ListViewItem* ACLitem = static_cast( mListView->currentItem() ); + if ( !ACLitem ) + return; + mRemovedACLs.append( ACLitem->userId() ); + delete ACLitem; + emit changed(true); +} + +bool KMail::FolderDiaACLTab::accept() +{ + if ( !mChanged ) + return true; // no change, ok for accepting the dialog immediately + // If there were changes, we need to apply them first (which is async) + mAccepting = true; + save(); + return false; // i.e. don't close yet +} + +void KMail::FolderDiaACLTab::save() +{ + if ( !mChanged ) + return; + mJobCounter = 0; + ImapAccountBase* account = imapAccount(); + for ( QListViewItem* item = mListView->firstChild(); item; item = item->nextSibling() ) { + ListViewItem* ACLitem = static_cast( item ); + if ( ACLitem->isModified() ) { + kdDebug(5006) << "Modified item: " << ACLitem->userId() << endl; + + KIO::Job* job = ACLJobs::setACL( account->slave(), imapURL(), ACLitem->userId(), ACLitem->permissions() ); + ACLitem->setJob( job ); + ImapAccountBase::jobData jd; + jd.total = 1; jd.done = 0; jd.parent = NULL; + account->insertJob(job, jd); + + connect(job, SIGNAL(result(KIO::Job *)), + SLOT(slotSetACLResult(KIO::Job *))); + ++mJobCounter; + } + } + for( QStringList::Iterator rit = mRemovedACLs.begin(); rit != mRemovedACLs.end(); ++rit ) { + kdDebug(5006) << "Removed item: " << (*rit) << endl; + KIO::Job* job = ACLJobs::deleteACL( account->slave(), imapURL(), (*rit) ); + ImapAccountBase::jobData jd; + jd.total = 1; jd.done = 0; jd.parent = NULL; + account->insertJob(job, jd); + + connect(job, SIGNAL(result(KIO::Job *)), + SLOT(slotDeleteACLResult(KIO::Job *))); + ++mJobCounter; + } +} + +void KMail::FolderDiaACLTab::slotChanged( bool b ) +{ + mChanged = b; +} + +#include "folderdiaacltab.moc" diff --git a/folderdiaacltab.h b/folderdiaacltab.h new file mode 100644 index 000000000..11218b586 --- /dev/null +++ b/folderdiaacltab.h @@ -0,0 +1,133 @@ +// -*- mode: C++; c-file-style: "gnu" -*- +/** + * folderdiaacltab.h + * + * Copyright (c) 2004 David Faure + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * In addition, as a special exception, the copyright holders give + * permission to link the code of this program with any edition of + * the Qt library by Trolltech AS, Norway (or with modified versions + * of Qt that use the same license as Qt), and distribute linked + * combinations including the two. You must obey the GNU General + * Public License in all respects for all of the code used other than + * Qt. If you modify this file, you may extend this exception to + * your version of the file, but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from + * your version. + */ +#ifndef FOLDERDIAACL_H +#define FOLDERDIAACL_H + +#include "kmfolderdia.h" + +class KPushButton; +class QWidgetStack; +class QHBox; +class QVButtonGroup; +class KListView; +namespace KIO { class Job; } + +namespace KMail { + +class ImapAccountBase; + +/** + * "New Access Control Entry" dialog. + * Internal class, only used by FolderDiaACLTab + */ +class ACLEntryDialog :public KDialogBase { + Q_OBJECT + +public: + ACLEntryDialog( const QString& caption, QWidget* parent, const char* name = 0 ); + + void setValues( const QString& userId, unsigned int permissions ); + + QString userId() const; + unsigned int permissions() const; + +private slots: + void slotChanged(); + +private: + QVButtonGroup* mButtonGroup; + KLineEdit* mUserIdLineEdit; +}; + +/** + * "Access Control" tab in the folder dialog + * Internal class, only used by KMFolderDialog + */ +class FolderDiaACLTab : public FolderDiaTab +{ + Q_OBJECT + +public: + FolderDiaACLTab( KMFolderDialog* dlg, QWidget* parent, const char* name = 0 ); + + virtual void load(); + virtual void save(); + virtual bool accept(); + +private slots: + // Network (KIO) slots + void slotConnectionResult( int ); + void slotGetACLResult(KIO::Job *); + void slotSetACLResult(KIO::Job *); + void slotDeleteACLResult(KIO::Job *); + + // User (KListView) slots + void slotEditACL(QListViewItem*); + void slotSelectionChanged(QListViewItem*); + + // User (pushbuttons) slots + void slotAddACL(); + void slotEditACL(); + void slotRemoveACL(); + + void slotChanged( bool b ); + +private: + void ACLJobDone(KIO::Job* job); + KMail::ImapAccountBase* imapAccount() const; + KURL imapURL() const; + +private: + // The widget containing the ACL widgets (listview and buttons) + QHBox* mACLWidget; + //class ListView; + class ListViewItem; + KListView* mListView; + KPushButton* mAddACL; + KPushButton* mEditACL; + KPushButton* mRemoveACL; + + QStringList mRemovedACLs; + + QLabel* mLabel; + QWidgetStack* mStack; + KMFolderDialog* mDlg; + + int mJobCounter; + bool mChanged; + bool mAccepting; // i.e. close when done +}; + +} // end of namespace KMail + +#endif /* FOLDERDIAACL_H */ + diff --git a/imapaccountbase.cpp b/imapaccountbase.cpp index b0cb9dec4..ce15f10ce 100644 --- a/imapaccountbase.cpp +++ b/imapaccountbase.cpp @@ -56,6 +56,7 @@ using KIO::PasswordDialog; //using KIO::Scheduler; // use FQN below #include +#include "acljobs.h" namespace KMail { @@ -458,6 +459,38 @@ namespace KMail { if (mSlave) removeJob(job); } + //----------------------------------------------------------------------------- + void ImapAccountBase::getUserRights( const QString& imapPath ) + { + KURL url = getUrl(); + url.setPath(imapPath); + + ACLJobs::GetUserRightsJob* job = ACLJobs::getUserRights( mSlave, url ); + + jobData jd; + jd.total = 1; jd.done = 0; jd.parent = NULL; + insertJob(job, jd); + + connect(job, SIGNAL(result(KIO::Job *)), + SLOT(slotGetUserRightsResult(KIO::Job *))); + } + + void ImapAccountBase::slotGetUserRightsResult( KIO::Job* _job ) + { + ACLJobs::GetUserRightsJob* job = static_cast( _job ); + // result of a get-users-right-job + JobIterator it = findJob( job ); + if ( it == jobsEnd() ) return; + + if ( job->error() ) + kdWarning(5006) << "slotGetUserRightsResult: " << job->errorString() << endl; + else { + kdDebug(5006) << "User Rights: " << ACLJobs::permissionsToString( job->permissions() ) << endl; + // TODO store the permissions somewhere (in the folder? storage?) + } + if (mSlave) removeJob(job); + } + //----------------------------------------------------------------------------- void ImapAccountBase::slotSchedulerSlaveError(KIO::Slave *aSlave, int errorCode, const QString &errorMsg) diff --git a/imapaccountbase.h b/imapaccountbase.h index f926191ef..c4ec9aa9c 100644 --- a/imapaccountbase.h +++ b/imapaccountbase.h @@ -158,17 +158,25 @@ class AttachmentStrategy; void listDirectory(QString path, bool onlySubscribed, bool secondStep = FALSE, KMFolder* parent = NULL, bool reset = false); - /** + /** * Starts the folderlisting for the root folder - */ + */ virtual void listDirectory() = 0; /** * Subscribe (@p subscribe = TRUE) / Unsubscribe the folder - * identified by @p imapPath + * identified by @p imapPath. + * Emits subscriptionChanged signal on success. */ void changeSubscription(bool subscribe, QString imapPath); + /** + * Retrieve the users' right on the folder + * identified by @p imapPath. + * (async, will emit some signal) + */ + void getUserRights( const QString& imapPath ); + /** * The KIO-Slave died */ @@ -179,15 +187,15 @@ class AttachmentStrategy; */ void killAllJobs( bool disconnectSlave=false ) = 0; - /** - * Init a new-mail-check for a single folder - */ - void processNewMailSingleFolder(KMFolder* folder); + /** + * Init a new-mail-check for a single folder + */ + void processNewMailSingleFolder(KMFolder* folder); /** * Check whether we're checking for new mail * and the folder is included - */ + */ bool checkingMail( KMFolder *folder ); bool checkingMail() { return NetworkAccount::checkingMail(); } @@ -200,13 +208,13 @@ class AttachmentStrategy; /** * Handles the result from a BODYSTRUCTURE fetch - */ + */ void handleBodyStructure( QDataStream & stream, KMMessage * msg, const AttachmentStrategy *as ); /** * Reimplemented. Additionally set the folder label - */ + */ virtual void setFolder(KMFolder*, bool addAccount = false); public slots: @@ -256,6 +264,9 @@ class AttachmentStrategy; */ void slotSetStatusResult(KIO::Job * job); + /// Result of getUserRights() job + void slotGetUserRightsResult( KIO::Job* _job ); + protected: virtual QString protocol() const; virtual unsigned short int defaultPort() const; @@ -264,7 +275,7 @@ class AttachmentStrategy; /** * Build KMMessageParts and DwBodyParts from the bodystructure-stream - */ + */ void constructParts( QDataStream & stream, int count, KMMessagePart* parentKMPart, DwBodyPart * parent, const DwMessage * dwmsg ); diff --git a/kmfolderdia.cpp b/kmfolderdia.cpp index 9b94053be..ab70dcc23 100644 --- a/kmfolderdia.cpp +++ b/kmfolderdia.cpp @@ -32,6 +32,7 @@ #include +#include "kmfolderdia.h" #include "kmacctfolder.h" #include "kmfoldermgr.h" #include "identitycombo.h" @@ -41,6 +42,7 @@ #include "kmcommands.h" #include "mailinglist-magic.h" #include "kmfoldertree.h" +#include "folderdiaacltab.h" #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include @@ -60,7 +63,6 @@ #include -#include "kmfolderdia.h" //has to be later because of KMFolder* fdcls #include "kmfolderdia.moc" using namespace KMail; @@ -77,44 +79,101 @@ KMFolderDialog::KMFolderDialog(KMFolder *aFolder, KMFolderDir *aFolderDir, { kdDebug(5006)<<"KMFolderDialog::KMFolderDialog()" << endl; - ConfigModuleTab* tab; + FolderDiaTab* tab; QVBox* box; box = addVBoxPage( i18n("General") ); tab = new FolderDiaGeneralTab( this, aParent, aName, box ); - //connect( tab, SIGNAL(changed( bool )), - // this, SIGNAL(changed( bool )) ); - mTabs.append( tab ); + addTab( tab ); box = addVBoxPage( i18n("Mailing List") ); tab = new FolderDiaMailingListTab( this, box ); + addTab( tab ); + + if ( mFolder->folderType() == KMFolderTypeImap || mFolder->folderType() == KMFolderTypeCachedImap ) + { + //KMFolderImap* imapFolder = static_cast(folder->storage()); + // TODO check if the capabilities of the IMAP server include "acl" + + box = addVBoxPage( i18n("Access Control") ); + tab = new FolderDiaACLTab( this, box ); + addTab( tab ); + } + + for ( unsigned int i = 0 ; i < mTabs.count() ; ++i ) + mTabs[i]->load(); +} + +void KMFolderDialog::addTab( FolderDiaTab* tab ) +{ + connect( tab, SIGNAL( readyForAccept() ), + this, SLOT( slotReadyForAccept() ) ); + connect( tab, SIGNAL( cancelAccept() ), + this, SLOT( slotCancelAccept() ) ); //connect( tab, SIGNAL(changed( bool )), - // this, SIGNAL(changed( bool )) ); + // this, SLOT(slotChanged( bool )) ); mTabs.append( tab ); +} +// Not used yet (no button), but ready to be used :) +void KMFolderDialog::slotApply() +{ for ( unsigned int i = 0 ; i < mTabs.count() ; ++i ) - mTabs[i]->load(); + mTabs[i]->save(); + KDialogBase::slotApply(); } +// Called when pressing Ok +// We want to apply the changes first (which is async), before closing the dialog, +// in case of errors during the upload. void KMFolderDialog::slotOk() { + mDelayedSavingTabs = 0; // number of tabs which need delayed saving for ( unsigned int i = 0 ; i < mTabs.count() ; ++i ) - mTabs[i]->save(); - KDialogBase::slotOk(); + if ( !mTabs[i]->accept() ) + ++mDelayedSavingTabs; + + if ( mDelayedSavingTabs ) + enableButtonOK( false ); + else + KDialogBase::slotOk(); +} + +void KMFolderDialog::slotReadyForAccept() +{ + --mDelayedSavingTabs; + if ( mDelayedSavingTabs == 0 ) + KDialogBase::slotOk(); +} + +void KMFolderDialog::slotCancelAccept() +{ + mDelayedSavingTabs = -1; + enableButtonOK( true ); + // Other tabs might call slotReadyForAccept. -1 ensures that it won't close the dialog, + // but the OK button being enabled means that people might succeed in running + // the same job from save more than once. + // Solution: mAcceptCanceled = true instead of -1. + // Bah for now we only have one tab which can delay saving -> later. } void KMFolderDialog::slotChanged( bool ) { // TODO, support for 'changed', and Apply button. - //if ( b ) - // m_changed = true; + // sample code for here: KCMultiDialog calls bool changed() on every KCModuleProxy... +} + +void KMFolderDialog::setFolder( KMFolder* folder ) +{ + Q_ASSERT( mFolder.isNull() ); + mFolder = folder; } //---------------------------------------------------------------------------- KMail::FolderDiaGeneralTab::FolderDiaGeneralTab( KMFolderDialog* dlg, KMFolderTree* aParent, const QString& aName, QWidget* parent, const char* name ) - : ConfigModuleTab( parent, name ), mDlg( dlg ) + : FolderDiaTab( parent, name ), mDlg( dlg ) { QVBoxLayout *topLayout = new QVBoxLayout( this, 0, KDialog::spacingHint() ); @@ -753,7 +812,7 @@ void FolderDiaGeneralTab::slotChangeIcon( QString icon ) // can't use a const-re //---------------------------------------------------------------------------- FolderDiaMailingListTab::FolderDiaMailingListTab( KMFolderDialog* dlg, QWidget* parent, const char* name ) - : ConfigModuleTab( parent, name ), mDlg( dlg ) + : FolderDiaTab( parent, name ), mDlg( dlg ) { if ( mDlg->folder() && mDlg->folder()->noContent() ) { return; @@ -1007,9 +1066,3 @@ void FolderDiaMailingListTab::slotInvokeHandler() } if ( command ) command->start(); } - -void KMFolderDialog::setFolder( KMFolder* folder ) -{ - Q_ASSERT( mFolder.isNull() ); - mFolder = folder; -} diff --git a/kmfolderdia.h b/kmfolderdia.h index a1ed664a0..e2e714f10 100644 --- a/kmfolderdia.h +++ b/kmfolderdia.h @@ -58,11 +58,44 @@ template class QGuardedPtr; namespace KMail { +/** + * This is the base class for tabs in the folder dialog. + * It uses the API from ConfigModuleTab (basically: it's a widget that can load and save) + * but it also adds support for delayed-saving: + * when save() needs to use async jobs (e.g. KIO) for saving, + * we need to delay the closing until after the jobs are finished, + * and to cancel the saving on error. + * + * Feel free to rename and move this base class somewhere else if it + * can be useful for other dialogs. + */ +class FolderDiaTab : public ConfigModuleTab +{ + Q_OBJECT +public: + FolderDiaTab( QWidget *parent=0, const char* name=0 ) + : ConfigModuleTab( parent, name ) {} + + /// Called when clicking OK. + /// If a module returns false, the closing is cancelled for now, + /// and the module can close the dialog later on (i.e. after an async + /// operation like a KIO job). + virtual bool accept() { save(); return true; } + +signals: + /// Emit this to tell the dialog that you're done with the async jobs, + /// and that the dialog can be closed. + void readyForAccept(); + /// Emit this, i.e. after a job had an error, to tell the dialog to cancel + /// the closing. + void cancelAccept(); +}; + /** * "General" tab in the folder dialog * Internal class, only used by KMFolderDialog */ -class FolderDiaGeneralTab : public ConfigModuleTab +class FolderDiaGeneralTab : public FolderDiaTab { Q_OBJECT @@ -116,7 +149,7 @@ private: * "Mailing List" tab in the folder dialog * Internal class, only used by KMFolderDialog */ -class FolderDiaMailingListTab : public ConfigModuleTab +class FolderDiaMailingListTab : public FolderDiaTab { Q_OBJECT @@ -177,14 +210,22 @@ public: protected slots: void slotChanged( bool ); - virtual void slotOk( void ); + virtual void slotOk(); + virtual void slotApply(); + + void slotReadyForAccept(); + void slotCancelAccept(); -protected: +private: + void addTab( KMail::FolderDiaTab* tab ); + +private: QGuardedPtr mFolder; QGuardedPtr mFolderDir; FolderList mFolders; - QValueVector mTabs; + QValueVector mTabs; + int mDelayedSavingTabs; // this should go into a base class one day }; #endif /*__KMFOLDERDIA*/