diff --git a/src/widgets/kacleditwidget.cpp b/src/widgets/kacleditwidget.cpp index 852f74a4..03398522 100644 --- a/src/widgets/kacleditwidget.cpp +++ b/src/widgets/kacleditwidget.cpp @@ -1,1167 +1,1190 @@ /*************************************************************************** * Copyright (C) 2005 by Sean Harmer * * 2005 - 2007 Till Adam * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include "kacleditwidget.h" #include "kacleditwidget_p.h" #include "kio_widgets_debug.h" #if HAVE_POSIX_ACL #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_ACL_LIBACL_H # include #endif extern "C" { #include #include } #include static struct { const char *label; const char *pixmapName; QPixmap *pixmap; } s_itemAttributes[] = { { I18N_NOOP("Owner"), "user-grey", nullptr }, { I18N_NOOP("Owning Group"), "group-grey", nullptr }, { I18N_NOOP("Others"), "others-grey", nullptr }, { I18N_NOOP("Mask"), "mask", nullptr }, { I18N_NOOP("Named User"), "user", nullptr }, { I18N_NOOP("Named Group"), "group", nullptr }, }; class KACLEditWidget::KACLEditWidgetPrivate { public: KACLEditWidgetPrivate() { } // slots void _k_slotUpdateButtons(); KACLListView *m_listView; QPushButton *m_AddBtn; QPushButton *m_EditBtn; QPushButton *m_DelBtn; }; KACLEditWidget::KACLEditWidget(QWidget *parent) : QWidget(parent), d(new KACLEditWidgetPrivate) { QHBoxLayout *hbox = new QHBoxLayout(this); hbox->setMargin(0); d->m_listView = new KACLListView(this); hbox->addWidget(d->m_listView); connect(d->m_listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(_k_slotUpdateButtons())); QVBoxLayout *vbox = new QVBoxLayout(); hbox->addLayout(vbox); d->m_AddBtn = new QPushButton(i18n("Add Entry..."), this); vbox->addWidget(d->m_AddBtn); d->m_AddBtn->setObjectName(QStringLiteral("add_entry_button")); connect(d->m_AddBtn, SIGNAL(clicked()), d->m_listView, SLOT(slotAddEntry())); d->m_EditBtn = new QPushButton(i18n("Edit Entry..."), this); vbox->addWidget(d->m_EditBtn); d->m_EditBtn->setObjectName(QStringLiteral("edit_entry_button")); connect(d->m_EditBtn, SIGNAL(clicked()), d->m_listView, SLOT(slotEditEntry())); d->m_DelBtn = new QPushButton(i18n("Delete Entry"), this); vbox->addWidget(d->m_DelBtn); d->m_DelBtn->setObjectName(QStringLiteral("delete_entry_button")); connect(d->m_DelBtn, SIGNAL(clicked()), d->m_listView, SLOT(slotRemoveEntry())); vbox->addItem(new QSpacerItem(10, 10, QSizePolicy::Fixed, QSizePolicy::Expanding)); d->_k_slotUpdateButtons(); } KACLEditWidget::~KACLEditWidget() { delete d; } void KACLEditWidget::KACLEditWidgetPrivate::_k_slotUpdateButtons() { bool atLeastOneIsNotDeletable = false; bool atLeastOneIsNotAllowedToChangeType = false; int selectedCount = 0; QList selected = m_listView->selectedItems(); QListIterator it(selected); while (it.hasNext()) { KACLListViewItem *item = static_cast(it.next()); ++selectedCount; if (!item->isDeletable()) { atLeastOneIsNotDeletable = true; } if (!item->isAllowedToChangeType()) { atLeastOneIsNotAllowedToChangeType = true; } } m_EditBtn->setEnabled(selectedCount && !atLeastOneIsNotAllowedToChangeType); m_DelBtn->setEnabled(selectedCount && !atLeastOneIsNotDeletable); } KACL KACLEditWidget::getACL() const { return d->m_listView->getACL(); } KACL KACLEditWidget::getDefaultACL() const { return d->m_listView->getDefaultACL(); } void KACLEditWidget::setACL(const KACL &acl) { return d->m_listView->setACL(acl); } void KACLEditWidget::setDefaultACL(const KACL &acl) { return d->m_listView->setDefaultACL(acl); } void KACLEditWidget::setAllowDefaults(bool value) { d->m_listView->setAllowDefaults(value); } KACLListViewItem::KACLListViewItem(QTreeWidget *parent, KACLListView::EntryType _type, unsigned short _value, bool defaults, const QString &_qualifier) : QTreeWidgetItem(parent), type(_type), value(_value), isDefault(defaults), qualifier(_qualifier), isPartial(false) { m_pACLListView = qobject_cast(parent); repaint(); } KACLListViewItem::~ KACLListViewItem() { } QString KACLListViewItem::key() const { QString key; if (!isDefault) { key = 'A'; } else { key = 'B'; } switch (type) { case KACLListView::User: key += 'A'; break; case KACLListView::Group: key += 'B'; break; case KACLListView::Others: key += 'C'; break; case KACLListView::Mask: key += 'D'; break; case KACLListView::NamedUser: key += 'E' + text(1); break; case KACLListView::NamedGroup: key += 'F' + text(1); break; default: key += text(0); break; } return key; } bool KACLListViewItem::operator< (const QTreeWidgetItem &other) const { return key() < static_cast(other).key(); } #if 0 void KACLListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment) { if (isDefault) { setForeground(QColor(0, 0, 255)); } if (isPartial) { QFont font = p->font(); font.setItalic(true); setForeground(QColor(100, 100, 100)); p->setFont(font); } QTreeWidgetItem::paintCell(p, mycg, column, width, alignment); KACLListViewItem *below = 0; if (itemBelow()) { below = static_cast(itemBelow()); } const bool lastUser = type == KACLListView::NamedUser && below && below->type == KACLListView::NamedGroup; const bool lastNonDefault = !isDefault && below && below->isDefault; if (type == KACLListView::Mask || lastUser || lastNonDefault) { p->setPen(QPen(Qt::gray, 0, Qt::DotLine)); if (type == KACLListView::Mask) { p->drawLine(0, 0, width - 1, 0); } p->drawLine(0, height() - 1, width - 1, height() - 1); } } #endif void KACLListViewItem::updatePermPixmaps() { unsigned int partialPerms = value; if (value & ACL_READ) { setIcon(2, m_pACLListView->getYesPixmap()); } else if (partialPerms & ACL_READ) { setIcon(2, m_pACLListView->getYesPartialPixmap()); } else { setIcon(2, QIcon()); } if (value & ACL_WRITE) { setIcon(3, m_pACLListView->getYesPixmap()); } else if (partialPerms & ACL_WRITE) { setIcon(3, m_pACLListView->getYesPartialPixmap()); } else { setIcon(3, QIcon()); } if (value & ACL_EXECUTE) { setIcon(4, m_pACLListView->getYesPixmap()); } else if (partialPerms & ACL_EXECUTE) { setIcon(4, m_pACLListView->getYesPartialPixmap()); } else { setIcon(4, QIcon()); } } void KACLListViewItem::repaint() { int idx = 0; switch (type) { case KACLListView::User: idx = KACLListView::OWNER_IDX; break; case KACLListView::Group: idx = KACLListView::GROUP_IDX; break; case KACLListView::Others: idx = KACLListView::OTHERS_IDX; break; case KACLListView::Mask: idx = KACLListView::MASK_IDX; break; case KACLListView::NamedUser: idx = KACLListView::NAMED_USER_IDX; break; case KACLListView::NamedGroup: idx = KACLListView::NAMED_GROUP_IDX; break; default: idx = KACLListView::OWNER_IDX; break; } setText(0, i18n(s_itemAttributes[idx].label)); setIcon(0, *s_itemAttributes[idx].pixmap); if (isDefault) { setText(0, text(0) + i18n(" (Default)")); } setText(1, qualifier); // Set the pixmaps for which of the perms are set updatePermPixmaps(); } void KACLListViewItem::calcEffectiveRights() { QString strEffective = QStringLiteral("---"); // Do we need to worry about the mask entry? It applies to named users, // owning group, and named groups if (m_pACLListView->hasMaskEntry() && (type == KACLListView::NamedUser || type == KACLListView::Group || type == KACLListView::NamedGroup) && !isDefault) { strEffective[0] = (m_pACLListView->maskPermissions() & value & ACL_READ) ? 'r' : '-'; strEffective[1] = (m_pACLListView->maskPermissions() & value & ACL_WRITE) ? 'w' : '-'; strEffective[2] = (m_pACLListView->maskPermissions() & value & ACL_EXECUTE) ? 'x' : '-'; /* // What about any partial perms? if ( maskPerms & partialPerms & ACL_READ || // Partial perms on entry maskPartialPerms & perms & ACL_READ || // Partial perms on mask maskPartialPerms & partialPerms & ACL_READ ) // Partial perms on mask and entry strEffective[0] = 'R'; if ( maskPerms & partialPerms & ACL_WRITE || // Partial perms on entry maskPartialPerms & perms & ACL_WRITE || // Partial perms on mask maskPartialPerms & partialPerms & ACL_WRITE ) // Partial perms on mask and entry strEffective[1] = 'W'; if ( maskPerms & partialPerms & ACL_EXECUTE || // Partial perms on entry maskPartialPerms & perms & ACL_EXECUTE || // Partial perms on mask maskPartialPerms & partialPerms & ACL_EXECUTE ) // Partial perms on mask and entry strEffective[2] = 'X'; */ } else { // No, the effective value are just the value in this entry strEffective[0] = (value & ACL_READ) ? 'r' : '-'; strEffective[1] = (value & ACL_WRITE) ? 'w' : '-'; strEffective[2] = (value & ACL_EXECUTE) ? 'x' : '-'; /* // What about any partial perms? if ( partialPerms & ACL_READ ) strEffective[0] = 'R'; if ( partialPerms & ACL_WRITE ) strEffective[1] = 'W'; if ( partialPerms & ACL_EXECUTE ) strEffective[2] = 'X'; */ } setText(5, strEffective); } bool KACLListViewItem::isDeletable() const { bool isMaskAndDeletable = false; if (type == KACLListView::Mask) { if (!isDefault && m_pACLListView->maskCanBeDeleted()) { isMaskAndDeletable = true; } else if (isDefault && m_pACLListView->defaultMaskCanBeDeleted()) { isMaskAndDeletable = true; } } return type != KACLListView::User && type != KACLListView::Group && type != KACLListView::Others && (type != KACLListView::Mask || isMaskAndDeletable); } bool KACLListViewItem::isAllowedToChangeType() const { return type != KACLListView::User && type != KACLListView::Group && type != KACLListView::Others && type != KACLListView::Mask; } void KACLListViewItem::togglePerm(acl_perm_t perm) { value ^= perm; // Toggle the perm if (type == KACLListView::Mask && !isDefault) { m_pACLListView->setMaskPermissions(value); } calcEffectiveRights(); updatePermPixmaps(); /* // If the perm is in the partial perms then remove it. i.e. Once // a user changes a partial perm it then applies to all selected files. if ( m_pEntry->m_partialPerms & perm ) m_pEntry->m_partialPerms ^= perm; m_pEntry->setPartialEntry( false ); // Make sure that all entries have their effective rights calculated if // we are changing the ACL_MASK entry. if ( type == Mask ) { m_pACLListView->setMaskPartialPermissions( m_pEntry->m_partialPerms ); m_pACLListView->setMaskPermissions( value ); m_pACLListView->calculateEffectiveRights(); } */ } EditACLEntryDialog::EditACLEntryDialog(KACLListView *listView, KACLListViewItem *item, const QStringList &users, const QStringList &groups, const QStringList &defaultUsers, const QStringList &defaultGroups, int allowedTypes, int allowedDefaultTypes, bool allowDefaults) : QDialog(listView), m_listView(listView), m_item(item), m_users(users), m_groups(groups), m_defaultUsers(defaultUsers), m_defaultGroups(defaultGroups), m_allowedTypes(allowedTypes), m_allowedDefaultTypes(allowedDefaultTypes), m_defaultCB(nullptr) { setObjectName(QStringLiteral("edit_entry_dialog")); setModal(true); setWindowTitle(i18n("Edit ACL Entry")); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QGroupBox *gb = new QGroupBox(i18n("Entry Type"), this); QVBoxLayout *gbLayout = new QVBoxLayout(gb); m_buttonGroup = new QButtonGroup(this); if (allowDefaults) { m_defaultCB = new QCheckBox(i18n("Default for new files in this folder"), this); m_defaultCB->setObjectName(QStringLiteral("defaultCB")); mainLayout->addWidget(m_defaultCB); connect(m_defaultCB, SIGNAL(toggled(bool)), this, SLOT(slotUpdateAllowedUsersAndGroups())); connect(m_defaultCB, SIGNAL(toggled(bool)), this, SLOT(slotUpdateAllowedTypes())); } QRadioButton *ownerType = new QRadioButton(i18n("Owner"), gb); ownerType->setObjectName(QStringLiteral("ownerType")); gbLayout->addWidget(ownerType); m_buttonGroup->addButton(ownerType); m_buttonIds.insert(ownerType, KACLListView::User); QRadioButton *owningGroupType = new QRadioButton(i18n("Owning Group"), gb); owningGroupType->setObjectName(QStringLiteral("owningGroupType")); gbLayout->addWidget(owningGroupType); m_buttonGroup->addButton(owningGroupType); m_buttonIds.insert(owningGroupType, KACLListView::Group); QRadioButton *othersType = new QRadioButton(i18n("Others"), gb); othersType->setObjectName(QStringLiteral("othersType")); gbLayout->addWidget(othersType); m_buttonGroup->addButton(othersType); m_buttonIds.insert(othersType, KACLListView::Others); QRadioButton *maskType = new QRadioButton(i18n("Mask"), gb); maskType->setObjectName(QStringLiteral("maskType")); gbLayout->addWidget(maskType); m_buttonGroup->addButton(maskType); m_buttonIds.insert(maskType, KACLListView::Mask); QRadioButton *namedUserType = new QRadioButton(i18n("Named user"), gb); namedUserType->setObjectName(QStringLiteral("namesUserType")); gbLayout->addWidget(namedUserType); m_buttonGroup->addButton(namedUserType); m_buttonIds.insert(namedUserType, KACLListView::NamedUser); QRadioButton *namedGroupType = new QRadioButton(i18n("Named group"), gb); namedGroupType->setObjectName(QStringLiteral("namedGroupType")); gbLayout->addWidget(namedGroupType); m_buttonGroup->addButton(namedGroupType); m_buttonIds.insert(namedGroupType, KACLListView::NamedGroup); mainLayout->addWidget(gb); connect(m_buttonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(slotSelectionChanged(QAbstractButton*))); m_widgetStack = new QStackedWidget(this); mainLayout->addWidget(m_widgetStack); // users box QWidget *usersBox = new QWidget(m_widgetStack); QHBoxLayout *usersLayout = new QHBoxLayout(usersBox); usersBox->setLayout(usersLayout); m_widgetStack->addWidget(usersBox); QLabel *usersLabel = new QLabel(i18n("User: "), usersBox); m_usersCombo = new KComboBox(usersBox); m_usersCombo->setEditable(false); m_usersCombo->setObjectName(QStringLiteral("users")); usersLabel->setBuddy(m_usersCombo); usersLayout->addWidget(usersLabel); usersLayout->addWidget(m_usersCombo); // groups box QWidget *groupsBox = new QWidget(m_widgetStack); QHBoxLayout *groupsLayout = new QHBoxLayout(usersBox); groupsBox->setLayout(groupsLayout); m_widgetStack->addWidget(groupsBox); QLabel *groupsLabel = new QLabel(i18n("Group: "), groupsBox); m_groupsCombo = new KComboBox(groupsBox); m_groupsCombo->setEditable(false); m_groupsCombo->setObjectName(QStringLiteral("groups")); groupsLabel->setBuddy(m_groupsCombo); groupsLayout->addWidget(groupsLabel); groupsLayout->addWidget(m_groupsCombo); if (m_item) { m_buttonIds.key(m_item->type)->setChecked(true); if (m_defaultCB) { m_defaultCB->setChecked(m_item->isDefault); } slotUpdateAllowedTypes(); slotSelectionChanged(m_buttonIds.key(m_item->type)); slotUpdateAllowedUsersAndGroups(); if (m_item->type == KACLListView::NamedUser) { m_usersCombo->setItemText(m_usersCombo->currentIndex(), m_item->qualifier); } else if (m_item->type == KACLListView::NamedGroup) { m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), m_item->qualifier); } } else { // new entry, preselect "named user", arguably the most common one m_buttonIds.key(KACLListView::NamedUser)->setChecked(true); slotUpdateAllowedTypes(); slotSelectionChanged(m_buttonIds.key(KACLListView::NamedUser)); slotUpdateAllowedUsersAndGroups(); } QDialogButtonBox *buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotOk())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); mainLayout->addWidget(buttonBox); adjustSize(); } void EditACLEntryDialog::slotUpdateAllowedTypes() { int allowedTypes = m_allowedTypes; if (m_defaultCB && m_defaultCB->isChecked()) { allowedTypes = m_allowedDefaultTypes; } for (int i = 1; i < KACLListView::AllTypes; i = i * 2) { if (allowedTypes & i) { m_buttonIds.key(i)->show(); } else { m_buttonIds.key(i)->hide(); } } } void EditACLEntryDialog::slotUpdateAllowedUsersAndGroups() { const QString oldUser = m_usersCombo->currentText(); const QString oldGroup = m_groupsCombo->currentText(); m_usersCombo->clear(); m_groupsCombo->clear(); if (m_defaultCB && m_defaultCB->isChecked()) { m_usersCombo->addItems(m_defaultUsers); if (m_defaultUsers.contains(oldUser)) { m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); } m_groupsCombo->addItems(m_defaultGroups); if (m_defaultGroups.contains(oldGroup)) { m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); } } else { m_usersCombo->addItems(m_users); if (m_users.contains(oldUser)) { m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); } m_groupsCombo->addItems(m_groups); if (m_groups.contains(oldGroup)) { m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); } } } void EditACLEntryDialog::slotOk() { KACLListView::EntryType type = static_cast(m_buttonIds[m_buttonGroup->checkedButton()]); qCWarning(KIO_WIDGETS) << "Type 2: " << type; QString qualifier; if (type == KACLListView::NamedUser) { qualifier = m_usersCombo->currentText(); } if (type == KACLListView::NamedGroup) { qualifier = m_groupsCombo->currentText(); } if (!m_item) { m_item = new KACLListViewItem(m_listView, type, ACL_READ | ACL_WRITE | ACL_EXECUTE, false, qualifier); } else { m_item->type = type; m_item->qualifier = qualifier; } if (m_defaultCB) { m_item->isDefault = m_defaultCB->isChecked(); } m_item->repaint(); QDialog::accept(); } void EditACLEntryDialog::slotSelectionChanged(QAbstractButton *button) { switch (m_buttonIds[ button ]) { case KACLListView::User: case KACLListView::Group: case KACLListView::Others: case KACLListView::Mask: m_widgetStack->setEnabled(false); break; case KACLListView::NamedUser: m_widgetStack->setEnabled(true); m_widgetStack->setCurrentIndex(0 /* User */); break; case KACLListView::NamedGroup: m_widgetStack->setEnabled(true); m_widgetStack->setCurrentIndex(1 /* Group */); break; default: break; } } KACLListView::KACLListView(QWidget *parent) : QTreeWidget(parent), m_hasMask(false), m_allowDefaults(false) { // Add the columns setColumnCount(6); QStringList headers; headers << i18n("Type"); headers << i18n("Name"); headers << i18nc("read permission", "r"); headers << i18nc("write permission", "w"); headers << i18nc("execute permission", "x"); headers << i18n("Effective"); setHeaderLabels(headers); setSortingEnabled(false); setSelectionMode(QAbstractItemView::ExtendedSelection); header()->setSectionResizeMode(QHeaderView::ResizeToContents); setRootIsDecorated(false); // Load the avatars for (int i = 0; i < LAST_IDX; ++i) { s_itemAttributes[i].pixmap = new QPixmap(QStringLiteral(":/images/%1").arg(s_itemAttributes[i].pixmapName)); } m_yesPixmap = new QPixmap(QStringLiteral(":/images/yes.png")); m_yesPartialPixmap = new QPixmap(QStringLiteral(":/images/yespartial.png")); // fill the lists of all legal users and groups struct passwd *user = nullptr; setpwent(); while ((user = getpwent()) != nullptr) { m_allUsers << QString::fromLatin1(user->pw_name); } endpwent(); struct group *gr = nullptr; setgrent(); while ((gr = getgrent()) != nullptr) { m_allGroups << QString::fromLatin1(gr->gr_name); } endgrent(); m_allUsers.sort(); m_allGroups.sort(); connect(this, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(slotItemClicked(QTreeWidgetItem*,int))); + + connect(this, &KACLListView::itemDoubleClicked, + this, &KACLListView::slotItemDoubleClicked); } KACLListView::~KACLListView() { for (int i = 0; i < LAST_IDX; ++i) { delete s_itemAttributes[i].pixmap; } delete m_yesPixmap; delete m_yesPartialPixmap; } QStringList KACLListView::allowedUsers(bool defaults, KACLListViewItem *allowedItem) { QStringList allowedUsers = m_allUsers; QTreeWidgetItemIterator it(this); while (*it) { const KACLListViewItem *item = static_cast(*it); ++it; if (item->type != NamedUser || item->isDefault != defaults) { continue; } if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { continue; } allowedUsers.removeAll(item->qualifier); } return allowedUsers; } QStringList KACLListView::allowedGroups(bool defaults, KACLListViewItem *allowedItem) { QStringList allowedGroups = m_allGroups; QTreeWidgetItemIterator it(this); while (*it) { const KACLListViewItem *item = static_cast(*it); ++it; if (item->type != NamedGroup || item->isDefault != defaults) { continue; } if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { continue; } allowedGroups.removeAll(item->qualifier); } return allowedGroups; } void KACLListView::fillItemsFromACL(const KACL &pACL, bool defaults) { // clear out old entries of that ilk QTreeWidgetItemIterator it(this); while (KACLListViewItem *item = static_cast(*it)) { ++it; if (item->isDefault == defaults) { delete item; } } new KACLListViewItem(this, User, pACL.ownerPermissions(), defaults); new KACLListViewItem(this, Group, pACL.owningGroupPermissions(), defaults); new KACLListViewItem(this, Others, pACL.othersPermissions(), defaults); bool hasMask = false; unsigned short mask = pACL.maskPermissions(hasMask); if (hasMask) { new KACLListViewItem(this, Mask, mask, defaults); } // read all named user entries const ACLUserPermissionsList &userList = pACL.allUserPermissions(); ACLUserPermissionsConstIterator itu = userList.begin(); while (itu != userList.end()) { new KACLListViewItem(this, NamedUser, (*itu).second, defaults, (*itu).first); ++itu; } // and now all named groups const ACLUserPermissionsList &groupList = pACL.allGroupPermissions(); ACLUserPermissionsConstIterator itg = groupList.begin(); while (itg != groupList.end()) { new KACLListViewItem(this, NamedGroup, (*itg).second, defaults, (*itg).first); ++itg; } } void KACLListView::setACL(const KACL &acl) { if (!acl.isValid()) { return; } // Remove any entries left over from displaying a previous ACL m_ACL = acl; fillItemsFromACL(m_ACL); m_mask = acl.maskPermissions(m_hasMask); calculateEffectiveRights(); } void KACLListView::setDefaultACL(const KACL &acl) { if (!acl.isValid()) { return; } m_defaultACL = acl; fillItemsFromACL(m_defaultACL, true); calculateEffectiveRights(); } KACL KACLListView::itemsToACL(bool defaults) const { KACL newACL(0); bool atLeastOneEntry = false; ACLUserPermissionsList users; ACLGroupPermissionsList groups; QTreeWidgetItemIterator it(const_cast(this)); while (QTreeWidgetItem *qlvi = *it) { ++it; const KACLListViewItem *item = static_cast(qlvi); if (item->isDefault != defaults) { continue; } atLeastOneEntry = true; switch (item->type) { case User: newACL.setOwnerPermissions(item->value); break; case Group: newACL.setOwningGroupPermissions(item->value); break; case Others: newACL.setOthersPermissions(item->value); break; case Mask: newACL.setMaskPermissions(item->value); break; case NamedUser: users.append(qMakePair(item->text(1), item->value)); break; case NamedGroup: groups.append(qMakePair(item->text(1), item->value)); break; default: break; } } if (atLeastOneEntry) { newACL.setAllUserPermissions(users); newACL.setAllGroupPermissions(groups); if (newACL.isValid()) { return newACL; } } return KACL(); } KACL KACLListView::getACL() { return itemsToACL(false); } KACL KACLListView::getDefaultACL() { return itemsToACL(true); } void KACLListView::contentsMousePressEvent(QMouseEvent * /*e*/) { /* QTreeWidgetItem *clickedItem = itemAt( e->pos() ); if ( !clickedItem ) return; // if the click is on an as yet unselected item, select it first if ( !clickedItem->isSelected() ) QAbstractItemView::contentsMousePressEvent( e ); if ( !currentItem() ) return; int column = header()->sectionAt( e->x() ); acl_perm_t perm; switch ( column ) { case 2: perm = ACL_READ; break; case 3: perm = ACL_WRITE; break; case 4: perm = ACL_EXECUTE; break; default: return QTreeWidget::contentsMousePressEvent( e ); } KACLListViewItem* referenceItem = static_cast( clickedItem ); unsigned short referenceHadItSet = referenceItem->value & perm; QTreeWidgetItemIterator it( this ); while ( KACLListViewItem* item = static_cast( *it ) ) { ++it; if ( !item->isSelected() ) continue; // toggle those with the same value as the clicked item, leave the others if ( referenceHadItSet == ( item->value & perm ) ) item->togglePerm( perm ); } */ } void KACLListView::slotItemClicked(QTreeWidgetItem *pItem, int col) { if (!pItem) { return; } QTreeWidgetItemIterator it(this); while (KACLListViewItem *item = static_cast(*it)) { ++it; if (!item->isSelected()) { continue; } switch (col) { case 2: item->togglePerm(ACL_READ); break; case 3: item->togglePerm(ACL_WRITE); break; case 4: item->togglePerm(ACL_EXECUTE); break; default: ; // Do nothing } } /* // Has the user changed one of the required entries in a default ACL? if ( m_pACL->aclType() == ACL_TYPE_DEFAULT && ( col == 2 || col == 3 || col == 4 ) && ( pACLItem->entryType() == ACL_USER_OBJ || pACLItem->entryType() == ACL_GROUP_OBJ || pACLItem->entryType() == ACL_OTHER ) ) { // Mark the required entries as no longer being partial entries. // That is, they will get applied to all selected directories. KACLListViewItem* pUserObj = findACLEntryByType( this, ACL_USER_OBJ ); pUserObj->entry()->setPartialEntry( false ); KACLListViewItem* pGroupObj = findACLEntryByType( this, ACL_GROUP_OBJ ); pGroupObj->entry()->setPartialEntry( false ); KACLListViewItem* pOther = findACLEntryByType( this, ACL_OTHER ); pOther->entry()->setPartialEntry( false ); update(); } */ } +void KACLListView::slotItemDoubleClicked(QTreeWidgetItem *item, int column) +{ + if (!item) { + return; + } + + // avoid conflict with clicking to toggle permission + if (column >= 2 && column <= 4) { + return; + } + + KACLListViewItem *aclListItem = static_cast(item); + if (!aclListItem->isAllowedToChangeType()) { + return; + } + + setCurrentItem(item); + slotEditEntry(); +} + void KACLListView::calculateEffectiveRights() { QTreeWidgetItemIterator it(this); KACLListViewItem *pItem; while ((pItem = dynamic_cast(*it)) != nullptr) { ++it; pItem->calcEffectiveRights(); } } unsigned short KACLListView::maskPermissions() const { return m_mask; } void KACLListView::setMaskPermissions(unsigned short maskPerms) { m_mask = maskPerms; calculateEffectiveRights(); } acl_perm_t KACLListView::maskPartialPermissions() const { // return m_pMaskEntry->m_partialPerms; return 0; } void KACLListView::setMaskPartialPermissions(acl_perm_t /*maskPartialPerms*/) { //m_pMaskEntry->m_partialPerms = maskPartialPerms; calculateEffectiveRights(); } bool KACLListView::hasDefaultEntries() const { QTreeWidgetItemIterator it(const_cast(this)); while (*it) { const KACLListViewItem *item = static_cast(*it); ++it; if (item->isDefault) { return true; } } return false; } const KACLListViewItem *KACLListView::findDefaultItemByType(EntryType type) const { return findItemByType(type, true); } const KACLListViewItem *KACLListView::findItemByType(EntryType type, bool defaults) const { QTreeWidgetItemIterator it(const_cast(this)); while (*it) { const KACLListViewItem *item = static_cast(*it); ++it; if (item->isDefault == defaults && item->type == type) { return item; } } return nullptr; } unsigned short KACLListView::calculateMaskValue(bool defaults) const { // KACL auto-adds the relevant maks entries, so we can simply query bool dummy; return itemsToACL(defaults).maskPermissions(dummy); } void KACLListView::slotAddEntry() { int allowedTypes = NamedUser | NamedGroup; if (!m_hasMask) { allowedTypes |= Mask; } int allowedDefaultTypes = NamedUser | NamedGroup; if (!findDefaultItemByType(Mask)) { allowedDefaultTypes |= Mask; } if (!hasDefaultEntries()) { allowedDefaultTypes |= User | Group; } EditACLEntryDialog dlg(this, nullptr, allowedUsers(false), allowedGroups(false), allowedUsers(true), allowedGroups(true), allowedTypes, allowedDefaultTypes, m_allowDefaults); dlg.exec(); KACLListViewItem *item = dlg.item(); if (!item) { return; // canceled } if (item->type == Mask && !item->isDefault) { m_hasMask = true; m_mask = item->value; } if (item->isDefault && !hasDefaultEntries()) { // first default entry, fill in what is needed if (item->type != User) { unsigned short v = findDefaultItemByType(User)->value; new KACLListViewItem(this, User, v, true); } if (item->type != Group) { unsigned short v = findDefaultItemByType(Group)->value; new KACLListViewItem(this, Group, v, true); } if (item->type != Others) { unsigned short v = findDefaultItemByType(Others)->value; new KACLListViewItem(this, Others, v, true); } } const KACLListViewItem *defaultMaskItem = findDefaultItemByType(Mask); if (item->isDefault && !defaultMaskItem) { unsigned short v = calculateMaskValue(true); new KACLListViewItem(this, Mask, v, true); } if (!item->isDefault && !m_hasMask && (item->type == Group || item->type == NamedUser || item->type == NamedGroup)) { // auto-add a mask entry unsigned short v = calculateMaskValue(false); new KACLListViewItem(this, Mask, v, false); m_hasMask = true; m_mask = v; } calculateEffectiveRights(); sortItems(sortColumn(), Qt::AscendingOrder); setCurrentItem(item); // QTreeWidget doesn't seem to emit, in this case, and we need to update // the buttons... if (topLevelItemCount() == 1) { emit currentItemChanged(item, item); } } void KACLListView::slotEditEntry() { QTreeWidgetItem *current = currentItem(); if (!current) { return; } KACLListViewItem *item = static_cast(current); int allowedTypes = item->type | NamedUser | NamedGroup; bool itemWasMask = item->type == Mask; if (!m_hasMask || itemWasMask) { allowedTypes |= Mask; } int allowedDefaultTypes = item->type | NamedUser | NamedGroup; if (!findDefaultItemByType(Mask)) { allowedDefaultTypes |= Mask; } if (!hasDefaultEntries()) { allowedDefaultTypes |= User | Group; } EditACLEntryDialog dlg(this, item, allowedUsers(false, item), allowedGroups(false, item), allowedUsers(true, item), allowedGroups(true, item), allowedTypes, allowedDefaultTypes, m_allowDefaults); dlg.exec(); if (itemWasMask && item->type != Mask) { m_hasMask = false; m_mask = 0; } if (!itemWasMask && item->type == Mask) { m_mask = item->value; m_hasMask = true; } calculateEffectiveRights(); sortItems(sortColumn(), Qt::AscendingOrder); } void KACLListView::slotRemoveEntry() { QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected); while (*it) { KACLListViewItem *item = static_cast(*it); ++it; /* First check if it's a mask entry and if so, make sure that there is * either no name user or group entry, which means the mask can be * removed, or don't remove it, but reset it. That is allowed. */ if (item->type == Mask) { bool itemWasDefault = item->isDefault; if (!itemWasDefault && maskCanBeDeleted()) { m_hasMask = false; m_mask = 0; delete item; } else if (itemWasDefault && defaultMaskCanBeDeleted()) { delete item; } else { item->value = 0; item->repaint(); } if (!itemWasDefault) { calculateEffectiveRights(); } } else { // for the base permissions, disable them, which is what libacl does if (!item->isDefault && (item->type == User || item->type == Group || item->type == Others)) { item->value = 0; item->repaint(); } else { delete item; } } } } bool KACLListView::maskCanBeDeleted() const { return !findItemByType(NamedUser) && !findItemByType(NamedGroup); } bool KACLListView::defaultMaskCanBeDeleted() const { return !findDefaultItemByType(NamedUser) && !findDefaultItemByType(NamedGroup); } #include "moc_kacleditwidget.cpp" #include "moc_kacleditwidget_p.cpp" #endif diff --git a/src/widgets/kacleditwidget_p.h b/src/widgets/kacleditwidget_p.h index 4fd7104c..0eecc523 100644 --- a/src/widgets/kacleditwidget_p.h +++ b/src/widgets/kacleditwidget_p.h @@ -1,219 +1,220 @@ /*************************************************************************** * Copyright (C) 2005 by Sean Harmer * * 2005 - 2007 Till Adam * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Library General Public License as * * published by the Free Software Foundation; either version 2 of the * * License, or (at your option) any later version. * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #ifndef KACLEDITWIDGET_P_H #define KACLEDITWIDGET_P_H #include #if HAVE_POSIX_ACL || defined(Q_MOC_RUN) #include #include #include #include #include #include #include #include class KACLListViewItem; class QButtonGroup; class KACLListView; class QStackedWidget; class QCheckBox; class QAbstractButton; class QColorGroup; /** @author Sean Harmer */ class KACLListView : public QTreeWidget { Q_OBJECT friend class KACLListViewItem; public: enum Types { OWNER_IDX = 0, GROUP_IDX, OTHERS_IDX, MASK_IDX, NAMED_USER_IDX, NAMED_GROUP_IDX, LAST_IDX }; enum EntryType { User = 1, Group = 2, Others = 4, Mask = 8, NamedUser = 16, NamedGroup = 32, AllTypes = 63 }; KACLListView(QWidget *parent = nullptr); ~KACLListView(); bool hasMaskEntry() const { return m_hasMask; } bool hasDefaultEntries() const; bool allowDefaults() const { return m_allowDefaults; } void setAllowDefaults(bool v) { m_allowDefaults = v; } unsigned short maskPermissions() const; void setMaskPermissions(unsigned short maskPerms); acl_perm_t maskPartialPermissions() const; void setMaskPartialPermissions(acl_perm_t maskPerms); bool maskCanBeDeleted() const; bool defaultMaskCanBeDeleted() const; const KACLListViewItem *findDefaultItemByType(EntryType type) const; const KACLListViewItem *findItemByType(EntryType type, bool defaults = false) const; unsigned short calculateMaskValue(bool defaults) const; void calculateEffectiveRights(); QStringList allowedUsers(bool defaults, KACLListViewItem *allowedItem = nullptr); QStringList allowedGroups(bool defaults, KACLListViewItem *allowedItem = nullptr); KACL getACL(); KACL getDefaultACL(); QPixmap getYesPixmap() const { return *m_yesPixmap; } QPixmap getYesPartialPixmap() const { return *m_yesPartialPixmap; } public Q_SLOTS: void slotAddEntry(); void slotEditEntry(); void slotRemoveEntry(); void setACL(const KACL &anACL); void setDefaultACL(const KACL &anACL); protected Q_SLOTS: void slotItemClicked(QTreeWidgetItem *pItem, int col); + void slotItemDoubleClicked(QTreeWidgetItem *item, int col); protected: void contentsMousePressEvent(QMouseEvent *e); private: void fillItemsFromACL(const KACL &pACL, bool defaults = false); KACL itemsToACL(bool defaults) const; KACL m_ACL; KACL m_defaultACL; unsigned short m_mask; bool m_hasMask; bool m_allowDefaults; QStringList m_allUsers; QStringList m_allGroups; QPixmap *m_yesPixmap; QPixmap *m_yesPartialPixmap; }; class EditACLEntryDialog : public QDialog { Q_OBJECT public: EditACLEntryDialog(KACLListView *listView, KACLListViewItem *item, const QStringList &users, const QStringList &groups, const QStringList &defaultUsers, const QStringList &defaultGroups, int allowedTypes = KACLListView::AllTypes, int allowedDefaultTypes = KACLListView::AllTypes, bool allowDefault = false); KACLListViewItem *item() const { return m_item; } public Q_SLOTS: void slotOk(); void slotSelectionChanged(QAbstractButton *); private Q_SLOTS: void slotUpdateAllowedUsersAndGroups(); void slotUpdateAllowedTypes(); private: KACLListView *m_listView; KACLListViewItem *m_item; QStringList m_users; QStringList m_groups; QStringList m_defaultUsers; QStringList m_defaultGroups; int m_allowedTypes; int m_allowedDefaultTypes; QButtonGroup *m_buttonGroup; KComboBox *m_usersCombo; KComboBox *m_groupsCombo; QStackedWidget *m_widgetStack; QCheckBox *m_defaultCB; QHash m_buttonIds; }; class KACLListViewItem : public QTreeWidgetItem { public: KACLListViewItem(QTreeWidget *parent, KACLListView::EntryType type, unsigned short value, bool defaultEntry, const QString &qualifier = QString()); virtual ~KACLListViewItem(); QString key() const; bool operator< (const QTreeWidgetItem &other) const Q_DECL_OVERRIDE; void calcEffectiveRights(); bool isDeletable() const; bool isAllowedToChangeType() const; void togglePerm(acl_perm_t perm); #if 0 virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment); #endif void updatePermPixmaps(); void repaint(); KACLListView::EntryType type; unsigned short value; bool isDefault; QString qualifier; bool isPartial; private: KACLListView *m_pACLListView; }; #endif #endif