File indexing completed on 2024-10-13 06:36:37
0001 /* 0002 SPDX-FileCopyrightText: 2005 Sean Harmer <sh@rama.homelinux.org> 0003 SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kacleditwidget.h" 0009 #include "kacleditwidget_p.h" 0010 #include "kio_widgets_debug.h" 0011 0012 #if HAVE_POSIX_ACL 0013 0014 #include <QButtonGroup> 0015 #include <QCheckBox> 0016 #include <QComboBox> 0017 #include <QDialog> 0018 #include <QDialogButtonBox> 0019 #include <QGroupBox> 0020 #include <QHeaderView> 0021 #include <QLabel> 0022 #include <QLayout> 0023 #include <QMouseEvent> 0024 #include <QPainter> 0025 #include <QPushButton> 0026 #include <QRadioButton> 0027 #include <QScrollBar> 0028 #include <QStackedWidget> 0029 0030 #include <KLocalizedString> 0031 #include <kfileitem.h> 0032 0033 #if HAVE_ACL_LIBACL_H 0034 #include <acl/libacl.h> 0035 #endif 0036 extern "C" { 0037 #include <grp.h> 0038 #include <pwd.h> 0039 } 0040 #include <assert.h> 0041 0042 class KACLEditWidget::KACLEditWidgetPrivate 0043 { 0044 public: 0045 KACLEditWidgetPrivate() 0046 { 0047 } 0048 0049 // slots 0050 void slotUpdateButtons(); 0051 0052 KACLListView *m_listView; 0053 QPushButton *m_AddBtn; 0054 QPushButton *m_EditBtn; 0055 QPushButton *m_DelBtn; 0056 }; 0057 0058 KACLEditWidget::KACLEditWidget(QWidget *parent) 0059 : QWidget(parent) 0060 , d(new KACLEditWidgetPrivate) 0061 { 0062 QHBoxLayout *hbox = new QHBoxLayout(this); 0063 hbox->setContentsMargins(0, 0, 0, 0); 0064 d->m_listView = new KACLListView(this); 0065 hbox->addWidget(d->m_listView); 0066 connect(d->m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() { 0067 d->slotUpdateButtons(); 0068 }); 0069 QVBoxLayout *vbox = new QVBoxLayout(); 0070 hbox->addLayout(vbox); 0071 d->m_AddBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add")), i18nc("@action:button", "Add..."), this); 0072 vbox->addWidget(d->m_AddBtn); 0073 d->m_AddBtn->setObjectName(QStringLiteral("add_entry_button")); 0074 connect(d->m_AddBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotAddEntry); 0075 d->m_EditBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit")), i18nc("@action:button", "Edit..."), this); 0076 vbox->addWidget(d->m_EditBtn); 0077 d->m_EditBtn->setObjectName(QStringLiteral("edit_entry_button")); 0078 connect(d->m_EditBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotEditEntry); 0079 d->m_DelBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("list-remove")), i18nc("@action:button", "Delete"), this); 0080 vbox->addWidget(d->m_DelBtn); 0081 d->m_DelBtn->setObjectName(QStringLiteral("delete_entry_button")); 0082 connect(d->m_DelBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotRemoveEntry); 0083 vbox->addItem(new QSpacerItem(10, 10, QSizePolicy::Fixed, QSizePolicy::Expanding)); 0084 d->slotUpdateButtons(); 0085 } 0086 0087 KACLEditWidget::~KACLEditWidget() = default; 0088 0089 void KACLEditWidget::KACLEditWidgetPrivate::slotUpdateButtons() 0090 { 0091 bool atLeastOneIsNotDeletable = false; 0092 bool atLeastOneIsNotAllowedToChangeType = false; 0093 int selectedCount = 0; 0094 QList<QTreeWidgetItem *> selected = m_listView->selectedItems(); 0095 QListIterator<QTreeWidgetItem *> it(selected); 0096 while (it.hasNext()) { 0097 KACLListViewItem *item = static_cast<KACLListViewItem *>(it.next()); 0098 ++selectedCount; 0099 if (!item->isDeletable()) { 0100 atLeastOneIsNotDeletable = true; 0101 } 0102 if (!item->isAllowedToChangeType()) { 0103 atLeastOneIsNotAllowedToChangeType = true; 0104 } 0105 } 0106 m_EditBtn->setEnabled(selectedCount && !atLeastOneIsNotAllowedToChangeType); 0107 m_DelBtn->setEnabled(selectedCount && !atLeastOneIsNotDeletable); 0108 } 0109 0110 KACL KACLEditWidget::getACL() const 0111 { 0112 return d->m_listView->getACL(); 0113 } 0114 0115 KACL KACLEditWidget::getDefaultACL() const 0116 { 0117 return d->m_listView->getDefaultACL(); 0118 } 0119 0120 void KACLEditWidget::setACL(const KACL &acl) 0121 { 0122 d->m_listView->setACL(acl); 0123 } 0124 0125 void KACLEditWidget::setDefaultACL(const KACL &acl) 0126 { 0127 d->m_listView->setDefaultACL(acl); 0128 } 0129 0130 void KACLEditWidget::setAllowDefaults(bool value) 0131 { 0132 d->m_listView->setAllowDefaults(value); 0133 } 0134 0135 KACLListViewItem::KACLListViewItem(QTreeWidget *parent, KACLListView::EntryType _type, unsigned short _value, bool defaults, const QString &_qualifier) 0136 : QTreeWidgetItem(parent) 0137 , type(_type) 0138 , value(_value) 0139 , isDefault(defaults) 0140 , qualifier(_qualifier) 0141 , isPartial(false) 0142 { 0143 m_pACLListView = qobject_cast<KACLListView *>(parent); 0144 repaint(); 0145 } 0146 0147 KACLListViewItem::~KACLListViewItem() 0148 { 0149 } 0150 0151 QString KACLListViewItem::key() const 0152 { 0153 QString key; 0154 if (!isDefault) { 0155 key = QLatin1Char('A'); 0156 } else { 0157 key = QLatin1Char('B'); 0158 } 0159 switch (type) { 0160 case KACLListView::User: 0161 key += QLatin1Char('A'); 0162 break; 0163 case KACLListView::Group: 0164 key += QLatin1Char('B'); 0165 break; 0166 case KACLListView::Others: 0167 key += QLatin1Char('C'); 0168 break; 0169 case KACLListView::Mask: 0170 key += QLatin1Char('D'); 0171 break; 0172 case KACLListView::NamedUser: 0173 key += QLatin1Char('E') + text(1); 0174 break; 0175 case KACLListView::NamedGroup: 0176 key += QLatin1Char('F') + text(1); 0177 break; 0178 default: 0179 key += text(0); 0180 break; 0181 } 0182 return key; 0183 } 0184 0185 bool KACLListViewItem::operator<(const QTreeWidgetItem &other) const 0186 { 0187 return key() < static_cast<const KACLListViewItem &>(other).key(); 0188 } 0189 0190 void KACLListViewItem::updatePermissionIcons() 0191 { 0192 unsigned int partialPerms = value; 0193 0194 if (value & ACL_READ) { 0195 setIcon(2, QIcon::fromTheme(QStringLiteral("checkmark"))); 0196 } else if (partialPerms & ACL_READ) { 0197 setIcon(2, QIcon::fromTheme(QStringLiteral("checkmark-partial"))); 0198 } else { 0199 setIcon(2, QIcon()); 0200 } 0201 0202 if (value & ACL_WRITE) { 0203 setIcon(3, QIcon::fromTheme(QStringLiteral("checkmark"))); 0204 } else if (partialPerms & ACL_WRITE) { 0205 setIcon(3, QIcon::fromTheme(QStringLiteral("checkmark-partial"))); 0206 } else { 0207 setIcon(3, QIcon()); 0208 } 0209 0210 if (value & ACL_EXECUTE) { 0211 setIcon(4, QIcon::fromTheme(QStringLiteral("checkmark"))); 0212 } else if (partialPerms & ACL_EXECUTE) { 0213 setIcon(4, QIcon::fromTheme(QStringLiteral("checkmark-partial"))); 0214 } else { 0215 setIcon(4, QIcon()); 0216 } 0217 } 0218 0219 void KACLListViewItem::repaint() 0220 { 0221 QString text; 0222 QString icon; 0223 0224 switch (type) { 0225 case KACLListView::User: 0226 default: 0227 text = i18nc("Unix permissions", "Owner"); 0228 icon = QStringLiteral("user-gray"); 0229 break; 0230 case KACLListView::Group: 0231 text = i18nc("UNIX permissions", "Owning Group"); 0232 icon = QStringLiteral("group-gray"); 0233 break; 0234 case KACLListView::Others: 0235 text = i18nc("UNIX permissions", "Others"); 0236 icon = QStringLiteral("user-others-gray"); 0237 break; 0238 case KACLListView::Mask: 0239 text = i18nc("UNIX permissions", "Mask"); 0240 icon = QStringLiteral("view-filter"); 0241 break; 0242 case KACLListView::NamedUser: 0243 text = i18nc("UNIX permissions", "Named User"); 0244 icon = QStringLiteral("user"); 0245 break; 0246 case KACLListView::NamedGroup: 0247 text = i18nc("UNIX permissions", "Others"); 0248 icon = QStringLiteral("user-others"); 0249 break; 0250 } 0251 setText(0, text); 0252 setIcon(0, QIcon::fromTheme(icon)); 0253 if (isDefault) { 0254 setText(0, i18n("Owner (Default)")); 0255 } 0256 setText(1, qualifier); 0257 // Set the icons for which of the perms are set 0258 updatePermissionIcons(); 0259 } 0260 0261 void KACLListViewItem::calcEffectiveRights() 0262 { 0263 QString strEffective = QStringLiteral("---"); 0264 0265 // Do we need to worry about the mask entry? It applies to named users, 0266 // owning group, and named groups 0267 if (m_pACLListView->hasMaskEntry() // clang-format off 0268 && (type == KACLListView::NamedUser || type == KACLListView::Group || type == KACLListView::NamedGroup) 0269 && !isDefault) { // clang-format on 0270 strEffective[0] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_READ) ? 'r' : '-'); 0271 strEffective[1] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_WRITE) ? 'w' : '-'); 0272 strEffective[2] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_EXECUTE) ? 'x' : '-'); 0273 /* 0274 // What about any partial perms? 0275 if ( maskPerms & partialPerms & ACL_READ || // Partial perms on entry 0276 maskPartialPerms & perms & ACL_READ || // Partial perms on mask 0277 maskPartialPerms & partialPerms & ACL_READ ) // Partial perms on mask and entry 0278 strEffective[0] = 'R'; 0279 if ( maskPerms & partialPerms & ACL_WRITE || // Partial perms on entry 0280 maskPartialPerms & perms & ACL_WRITE || // Partial perms on mask 0281 maskPartialPerms & partialPerms & ACL_WRITE ) // Partial perms on mask and entry 0282 strEffective[1] = 'W'; 0283 if ( maskPerms & partialPerms & ACL_EXECUTE || // Partial perms on entry 0284 maskPartialPerms & perms & ACL_EXECUTE || // Partial perms on mask 0285 maskPartialPerms & partialPerms & ACL_EXECUTE ) // Partial perms on mask and entry 0286 strEffective[2] = 'X'; 0287 */ 0288 } else { 0289 // No, the effective value are just the value in this entry 0290 strEffective[0] = QLatin1Char((value & ACL_READ) ? 'r' : '-'); 0291 strEffective[1] = QLatin1Char((value & ACL_WRITE) ? 'w' : '-'); 0292 strEffective[2] = QLatin1Char((value & ACL_EXECUTE) ? 'x' : '-'); 0293 0294 /* 0295 // What about any partial perms? 0296 if ( partialPerms & ACL_READ ) 0297 strEffective[0] = 'R'; 0298 if ( partialPerms & ACL_WRITE ) 0299 strEffective[1] = 'W'; 0300 if ( partialPerms & ACL_EXECUTE ) 0301 strEffective[2] = 'X'; 0302 */ 0303 } 0304 setText(5, strEffective); 0305 } 0306 0307 bool KACLListViewItem::isDeletable() const 0308 { 0309 bool isMaskAndDeletable = false; 0310 if (type == KACLListView::Mask) { 0311 if (!isDefault && m_pACLListView->maskCanBeDeleted()) { 0312 isMaskAndDeletable = true; 0313 } else if (isDefault && m_pACLListView->defaultMaskCanBeDeleted()) { 0314 isMaskAndDeletable = true; 0315 } 0316 } 0317 0318 /* clang-format off */ 0319 return type != KACLListView::User 0320 && type != KACLListView::Group 0321 && type != KACLListView::Others 0322 && (type != KACLListView::Mask || isMaskAndDeletable); 0323 /* clang-format on */ 0324 } 0325 0326 bool KACLListViewItem::isAllowedToChangeType() const 0327 { 0328 /* clang-format off */ 0329 return type != KACLListView::User 0330 && type != KACLListView::Group 0331 && type != KACLListView::Others 0332 && type != KACLListView::Mask; 0333 /* clang-format on */ 0334 } 0335 0336 void KACLListViewItem::togglePerm(acl_perm_t perm) 0337 { 0338 value ^= perm; // Toggle the perm 0339 if (type == KACLListView::Mask && !isDefault) { 0340 m_pACLListView->setMaskPermissions(value); 0341 } 0342 calcEffectiveRights(); 0343 updatePermissionIcons(); 0344 /* 0345 // If the perm is in the partial perms then remove it. i.e. Once 0346 // a user changes a partial perm it then applies to all selected files. 0347 if ( m_pEntry->m_partialPerms & perm ) 0348 m_pEntry->m_partialPerms ^= perm; 0349 0350 m_pEntry->setPartialEntry( false ); 0351 // Make sure that all entries have their effective rights calculated if 0352 // we are changing the ACL_MASK entry. 0353 if ( type == Mask ) 0354 { 0355 m_pACLListView->setMaskPartialPermissions( m_pEntry->m_partialPerms ); 0356 m_pACLListView->setMaskPermissions( value ); 0357 m_pACLListView->calculateEffectiveRights(); 0358 } 0359 */ 0360 } 0361 0362 EditACLEntryDialog::EditACLEntryDialog(KACLListView *listView, 0363 KACLListViewItem *item, 0364 const QStringList &users, 0365 const QStringList &groups, 0366 const QStringList &defaultUsers, 0367 const QStringList &defaultGroups, 0368 int allowedTypes, 0369 int allowedDefaultTypes, 0370 bool allowDefaults) 0371 : QDialog(listView) 0372 , m_listView(listView) 0373 , m_item(item) 0374 , m_users(users) 0375 , m_groups(groups) 0376 , m_defaultUsers(defaultUsers) 0377 , m_defaultGroups(defaultGroups) 0378 , m_allowedTypes(allowedTypes) 0379 , m_allowedDefaultTypes(allowedDefaultTypes) 0380 , m_defaultCB(nullptr) 0381 { 0382 setObjectName(QStringLiteral("edit_entry_dialog")); 0383 setModal(true); 0384 setWindowTitle(i18n("Edit ACL Entry")); 0385 0386 QVBoxLayout *mainLayout = new QVBoxLayout(this); 0387 QGroupBox *gb = new QGroupBox(i18n("Entry Type"), this); 0388 QVBoxLayout *gbLayout = new QVBoxLayout(gb); 0389 0390 m_buttonGroup = new QButtonGroup(this); 0391 0392 if (allowDefaults) { 0393 m_defaultCB = new QCheckBox(i18n("Default for new files in this folder"), this); 0394 m_defaultCB->setObjectName(QStringLiteral("defaultCB")); 0395 mainLayout->addWidget(m_defaultCB); 0396 connect(m_defaultCB, &QAbstractButton::toggled, this, &EditACLEntryDialog::slotUpdateAllowedUsersAndGroups); 0397 connect(m_defaultCB, &QAbstractButton::toggled, this, &EditACLEntryDialog::slotUpdateAllowedTypes); 0398 } 0399 0400 QRadioButton *ownerType = new QRadioButton(i18n("Owner"), gb); 0401 ownerType->setObjectName(QStringLiteral("ownerType")); 0402 gbLayout->addWidget(ownerType); 0403 m_buttonGroup->addButton(ownerType); 0404 m_buttonIds.insert(ownerType, KACLListView::User); 0405 QRadioButton *owningGroupType = new QRadioButton(i18n("Owning Group"), gb); 0406 owningGroupType->setObjectName(QStringLiteral("owningGroupType")); 0407 gbLayout->addWidget(owningGroupType); 0408 m_buttonGroup->addButton(owningGroupType); 0409 m_buttonIds.insert(owningGroupType, KACLListView::Group); 0410 QRadioButton *othersType = new QRadioButton(i18n("Others"), gb); 0411 othersType->setObjectName(QStringLiteral("othersType")); 0412 gbLayout->addWidget(othersType); 0413 m_buttonGroup->addButton(othersType); 0414 m_buttonIds.insert(othersType, KACLListView::Others); 0415 QRadioButton *maskType = new QRadioButton(i18n("Mask"), gb); 0416 maskType->setObjectName(QStringLiteral("maskType")); 0417 gbLayout->addWidget(maskType); 0418 m_buttonGroup->addButton(maskType); 0419 m_buttonIds.insert(maskType, KACLListView::Mask); 0420 QRadioButton *namedUserType = new QRadioButton(i18n("Named user"), gb); 0421 namedUserType->setObjectName(QStringLiteral("namesUserType")); 0422 gbLayout->addWidget(namedUserType); 0423 m_buttonGroup->addButton(namedUserType); 0424 m_buttonIds.insert(namedUserType, KACLListView::NamedUser); 0425 QRadioButton *namedGroupType = new QRadioButton(i18n("Named group"), gb); 0426 namedGroupType->setObjectName(QStringLiteral("namedGroupType")); 0427 gbLayout->addWidget(namedGroupType); 0428 m_buttonGroup->addButton(namedGroupType); 0429 m_buttonIds.insert(namedGroupType, KACLListView::NamedGroup); 0430 0431 mainLayout->addWidget(gb); 0432 0433 connect(m_buttonGroup, &QButtonGroup::buttonClicked, this, &EditACLEntryDialog::slotSelectionChanged); 0434 0435 m_widgetStack = new QStackedWidget(this); 0436 mainLayout->addWidget(m_widgetStack); 0437 0438 // users box 0439 QWidget *usersBox = new QWidget(m_widgetStack); 0440 QHBoxLayout *usersLayout = new QHBoxLayout(usersBox); 0441 m_widgetStack->addWidget(usersBox); 0442 0443 QLabel *usersLabel = new QLabel(i18n("User: "), usersBox); 0444 m_usersCombo = new QComboBox(usersBox); 0445 m_usersCombo->setEditable(false); 0446 m_usersCombo->setObjectName(QStringLiteral("users")); 0447 usersLabel->setBuddy(m_usersCombo); 0448 0449 usersLayout->addWidget(usersLabel); 0450 usersLayout->addWidget(m_usersCombo); 0451 0452 // groups box 0453 QWidget *groupsBox = new QWidget(m_widgetStack); 0454 QHBoxLayout *groupsLayout = new QHBoxLayout(usersBox); 0455 m_widgetStack->addWidget(groupsBox); 0456 0457 QLabel *groupsLabel = new QLabel(i18n("Group: "), groupsBox); 0458 m_groupsCombo = new QComboBox(groupsBox); 0459 m_groupsCombo->setEditable(false); 0460 m_groupsCombo->setObjectName(QStringLiteral("groups")); 0461 groupsLabel->setBuddy(m_groupsCombo); 0462 0463 groupsLayout->addWidget(groupsLabel); 0464 groupsLayout->addWidget(m_groupsCombo); 0465 0466 if (m_item) { 0467 m_buttonIds.key(m_item->type)->setChecked(true); 0468 if (m_defaultCB) { 0469 m_defaultCB->setChecked(m_item->isDefault); 0470 } 0471 slotUpdateAllowedTypes(); 0472 slotSelectionChanged(m_buttonIds.key(m_item->type)); 0473 slotUpdateAllowedUsersAndGroups(); 0474 if (m_item->type == KACLListView::NamedUser) { 0475 m_usersCombo->setItemText(m_usersCombo->currentIndex(), m_item->qualifier); 0476 } else if (m_item->type == KACLListView::NamedGroup) { 0477 m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), m_item->qualifier); 0478 } 0479 } else { 0480 // new entry, preselect "named user", arguably the most common one 0481 m_buttonIds.key(KACLListView::NamedUser)->setChecked(true); 0482 slotUpdateAllowedTypes(); 0483 slotSelectionChanged(m_buttonIds.key(KACLListView::NamedUser)); 0484 slotUpdateAllowedUsersAndGroups(); 0485 } 0486 0487 QDialogButtonBox *buttonBox = new QDialogButtonBox(this); 0488 buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); 0489 connect(buttonBox, &QDialogButtonBox::accepted, this, &EditACLEntryDialog::slotOk); 0490 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); 0491 mainLayout->addWidget(buttonBox); 0492 0493 adjustSize(); 0494 } 0495 0496 void EditACLEntryDialog::slotUpdateAllowedTypes() 0497 { 0498 int allowedTypes = m_allowedTypes; 0499 if (m_defaultCB && m_defaultCB->isChecked()) { 0500 allowedTypes = m_allowedDefaultTypes; 0501 } 0502 for (int i = 1; i < KACLListView::AllTypes; i = i * 2) { 0503 if (allowedTypes & i) { 0504 m_buttonIds.key(i)->show(); 0505 } else { 0506 m_buttonIds.key(i)->hide(); 0507 } 0508 } 0509 } 0510 0511 void EditACLEntryDialog::slotUpdateAllowedUsersAndGroups() 0512 { 0513 const QString oldUser = m_usersCombo->currentText(); 0514 const QString oldGroup = m_groupsCombo->currentText(); 0515 m_usersCombo->clear(); 0516 m_groupsCombo->clear(); 0517 if (m_defaultCB && m_defaultCB->isChecked()) { 0518 m_usersCombo->addItems(m_defaultUsers); 0519 if (m_defaultUsers.contains(oldUser)) { 0520 m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); 0521 } 0522 m_groupsCombo->addItems(m_defaultGroups); 0523 if (m_defaultGroups.contains(oldGroup)) { 0524 m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); 0525 } 0526 } else { 0527 m_usersCombo->addItems(m_users); 0528 if (m_users.contains(oldUser)) { 0529 m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); 0530 } 0531 m_groupsCombo->addItems(m_groups); 0532 if (m_groups.contains(oldGroup)) { 0533 m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); 0534 } 0535 } 0536 } 0537 void EditACLEntryDialog::slotOk() 0538 { 0539 KACLListView::EntryType type = static_cast<KACLListView::EntryType>(m_buttonIds[m_buttonGroup->checkedButton()]); 0540 0541 qCWarning(KIO_WIDGETS) << "Type 2: " << type; 0542 0543 QString qualifier; 0544 if (type == KACLListView::NamedUser) { 0545 qualifier = m_usersCombo->currentText(); 0546 } 0547 if (type == KACLListView::NamedGroup) { 0548 qualifier = m_groupsCombo->currentText(); 0549 } 0550 0551 if (!m_item) { 0552 m_item = new KACLListViewItem(m_listView, type, ACL_READ | ACL_WRITE | ACL_EXECUTE, false, qualifier); 0553 } else { 0554 m_item->type = type; 0555 m_item->qualifier = qualifier; 0556 } 0557 if (m_defaultCB) { 0558 m_item->isDefault = m_defaultCB->isChecked(); 0559 } 0560 m_item->repaint(); 0561 0562 QDialog::accept(); 0563 } 0564 0565 void EditACLEntryDialog::slotSelectionChanged(QAbstractButton *button) 0566 { 0567 switch (m_buttonIds[button]) { 0568 case KACLListView::User: 0569 case KACLListView::Group: 0570 case KACLListView::Others: 0571 case KACLListView::Mask: 0572 m_widgetStack->setEnabled(false); 0573 break; 0574 case KACLListView::NamedUser: 0575 m_widgetStack->setEnabled(true); 0576 m_widgetStack->setCurrentIndex(0 /* User */); 0577 break; 0578 case KACLListView::NamedGroup: 0579 m_widgetStack->setEnabled(true); 0580 m_widgetStack->setCurrentIndex(1 /* Group */); 0581 break; 0582 default: 0583 break; 0584 } 0585 } 0586 0587 KACLListView::KACLListView(QWidget *parent) 0588 : QTreeWidget(parent) 0589 , m_hasMask(false) 0590 , m_allowDefaults(false) 0591 { 0592 // Add the columns 0593 setColumnCount(6); 0594 const QStringList headers{ 0595 i18n("Type"), 0596 i18n("Name"), 0597 i18nc("read permission", "r"), 0598 i18nc("write permission", "w"), 0599 i18nc("execute permission", "x"), 0600 i18n("Effective"), 0601 }; 0602 setHeaderLabels(headers); 0603 0604 setSortingEnabled(false); 0605 setSelectionMode(QAbstractItemView::ExtendedSelection); 0606 header()->setSectionResizeMode(QHeaderView::ResizeToContents); 0607 setRootIsDecorated(false); 0608 0609 connect(this, &QTreeWidget::itemClicked, this, &KACLListView::slotItemClicked); 0610 0611 connect(this, &KACLListView::itemDoubleClicked, this, &KACLListView::slotItemDoubleClicked); 0612 } 0613 0614 KACLListView::~KACLListView() 0615 { 0616 } 0617 0618 QSize KACLListView::sizeHint() const 0619 { 0620 const int width = header()->length() + verticalScrollBar()->width(); 0621 const int height = 7 * rowHeight(model()->index(0, 0, QModelIndex())); 0622 return {width, height}; 0623 } 0624 0625 QStringList KACLListView::allowedUsers(bool defaults, KACLListViewItem *allowedItem) 0626 { 0627 if (m_allUsers.isEmpty()) { 0628 struct passwd *user = nullptr; 0629 setpwent(); 0630 while ((user = getpwent()) != nullptr) { 0631 m_allUsers << QString::fromLatin1(user->pw_name); 0632 } 0633 endpwent(); 0634 m_allUsers.sort(); 0635 } 0636 0637 QStringList allowedUsers = m_allUsers; 0638 QTreeWidgetItemIterator it(this); 0639 while (*it) { 0640 const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); 0641 ++it; 0642 if (item->type != NamedUser || item->isDefault != defaults) { 0643 continue; 0644 } 0645 if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { 0646 continue; 0647 } 0648 allowedUsers.removeAll(item->qualifier); 0649 } 0650 return allowedUsers; 0651 } 0652 0653 QStringList KACLListView::allowedGroups(bool defaults, KACLListViewItem *allowedItem) 0654 { 0655 if (m_allGroups.isEmpty()) { 0656 struct group *gr = nullptr; 0657 setgrent(); 0658 while ((gr = getgrent()) != nullptr) { 0659 m_allGroups << QString::fromLatin1(gr->gr_name); 0660 } 0661 endgrent(); 0662 m_allGroups.sort(); 0663 } 0664 0665 QStringList allowedGroups = m_allGroups; 0666 QTreeWidgetItemIterator it(this); 0667 while (*it) { 0668 const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); 0669 ++it; 0670 if (item->type != NamedGroup || item->isDefault != defaults) { 0671 continue; 0672 } 0673 if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { 0674 continue; 0675 } 0676 allowedGroups.removeAll(item->qualifier); 0677 } 0678 return allowedGroups; 0679 } 0680 0681 void KACLListView::fillItemsFromACL(const KACL &pACL, bool defaults) 0682 { 0683 // clear out old entries of that ilk 0684 QTreeWidgetItemIterator it(this); 0685 while (KACLListViewItem *item = static_cast<KACLListViewItem *>(*it)) { 0686 ++it; 0687 if (item->isDefault == defaults) { 0688 delete item; 0689 } 0690 } 0691 0692 new KACLListViewItem(this, User, pACL.ownerPermissions(), defaults); 0693 0694 new KACLListViewItem(this, Group, pACL.owningGroupPermissions(), defaults); 0695 0696 new KACLListViewItem(this, Others, pACL.othersPermissions(), defaults); 0697 0698 bool hasMask = false; 0699 unsigned short mask = pACL.maskPermissions(hasMask); 0700 if (hasMask) { 0701 new KACLListViewItem(this, Mask, mask, defaults); 0702 } 0703 0704 // read all named user entries 0705 const ACLUserPermissionsList &userList = pACL.allUserPermissions(); 0706 ACLUserPermissionsConstIterator itu = userList.begin(); 0707 while (itu != userList.end()) { 0708 new KACLListViewItem(this, NamedUser, (*itu).second, defaults, (*itu).first); 0709 ++itu; 0710 } 0711 0712 // and now all named groups 0713 const ACLUserPermissionsList &groupList = pACL.allGroupPermissions(); 0714 ACLUserPermissionsConstIterator itg = groupList.begin(); 0715 while (itg != groupList.end()) { 0716 new KACLListViewItem(this, NamedGroup, (*itg).second, defaults, (*itg).first); 0717 ++itg; 0718 } 0719 } 0720 0721 void KACLListView::setACL(const KACL &acl) 0722 { 0723 if (!acl.isValid()) { 0724 return; 0725 } 0726 // Remove any entries left over from displaying a previous ACL 0727 m_ACL = acl; 0728 fillItemsFromACL(m_ACL); 0729 0730 m_mask = acl.maskPermissions(m_hasMask); 0731 calculateEffectiveRights(); 0732 } 0733 0734 void KACLListView::setDefaultACL(const KACL &acl) 0735 { 0736 if (!acl.isValid()) { 0737 return; 0738 } 0739 m_defaultACL = acl; 0740 fillItemsFromACL(m_defaultACL, true); 0741 calculateEffectiveRights(); 0742 } 0743 0744 KACL KACLListView::itemsToACL(bool defaults) const 0745 { 0746 KACL newACL(0); 0747 bool atLeastOneEntry = false; 0748 ACLUserPermissionsList users; 0749 ACLGroupPermissionsList groups; 0750 QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); 0751 while (QTreeWidgetItem *qlvi = *it) { 0752 ++it; 0753 const KACLListViewItem *item = static_cast<KACLListViewItem *>(qlvi); 0754 if (item->isDefault != defaults) { 0755 continue; 0756 } 0757 atLeastOneEntry = true; 0758 switch (item->type) { 0759 case User: 0760 newACL.setOwnerPermissions(item->value); 0761 break; 0762 case Group: 0763 newACL.setOwningGroupPermissions(item->value); 0764 break; 0765 case Others: 0766 newACL.setOthersPermissions(item->value); 0767 break; 0768 case Mask: 0769 newACL.setMaskPermissions(item->value); 0770 break; 0771 case NamedUser: 0772 users.append(qMakePair(item->text(1), item->value)); 0773 break; 0774 case NamedGroup: 0775 groups.append(qMakePair(item->text(1), item->value)); 0776 break; 0777 default: 0778 break; 0779 } 0780 } 0781 if (atLeastOneEntry) { 0782 newACL.setAllUserPermissions(users); 0783 newACL.setAllGroupPermissions(groups); 0784 if (newACL.isValid()) { 0785 return newACL; 0786 } 0787 } 0788 return KACL(); 0789 } 0790 0791 KACL KACLListView::getACL() 0792 { 0793 return itemsToACL(false); 0794 } 0795 0796 KACL KACLListView::getDefaultACL() 0797 { 0798 return itemsToACL(true); 0799 } 0800 0801 void KACLListView::contentsMousePressEvent(QMouseEvent * /*e*/) 0802 { 0803 /* 0804 QTreeWidgetItem *clickedItem = itemAt( e->pos() ); 0805 if ( !clickedItem ) return; 0806 // if the click is on an as yet unselected item, select it first 0807 if ( !clickedItem->isSelected() ) 0808 QAbstractItemView::contentsMousePressEvent( e ); 0809 0810 if ( !currentItem() ) return; 0811 int column = header()->sectionAt( e->x() ); 0812 acl_perm_t perm; 0813 switch ( column ) 0814 { 0815 case 2: 0816 perm = ACL_READ; 0817 break; 0818 case 3: 0819 perm = ACL_WRITE; 0820 break; 0821 case 4: 0822 perm = ACL_EXECUTE; 0823 break; 0824 default: 0825 return QTreeWidget::contentsMousePressEvent( e ); 0826 } 0827 KACLListViewItem* referenceItem = static_cast<KACLListViewItem*>( clickedItem ); 0828 unsigned short referenceHadItSet = referenceItem->value & perm; 0829 QTreeWidgetItemIterator it( this ); 0830 while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( *it ) ) { 0831 ++it; 0832 if ( !item->isSelected() ) continue; 0833 // toggle those with the same value as the clicked item, leave the others 0834 if ( referenceHadItSet == ( item->value & perm ) ) 0835 item->togglePerm( perm ); 0836 } 0837 */ 0838 } 0839 0840 void KACLListView::slotItemClicked(QTreeWidgetItem *pItem, int col) 0841 { 0842 if (!pItem) { 0843 return; 0844 } 0845 0846 QTreeWidgetItemIterator it(this); 0847 while (KACLListViewItem *item = static_cast<KACLListViewItem *>(*it)) { 0848 ++it; 0849 if (!item->isSelected()) { 0850 continue; 0851 } 0852 switch (col) { 0853 case 2: 0854 item->togglePerm(ACL_READ); 0855 break; 0856 case 3: 0857 item->togglePerm(ACL_WRITE); 0858 break; 0859 case 4: 0860 item->togglePerm(ACL_EXECUTE); 0861 break; 0862 0863 default:; // Do nothing 0864 } 0865 } 0866 /* 0867 // Has the user changed one of the required entries in a default ACL? 0868 if ( m_pACL->aclType() == ACL_TYPE_DEFAULT && 0869 ( col == 2 || col == 3 || col == 4 ) && 0870 ( pACLItem->entryType() == ACL_USER_OBJ || 0871 pACLItem->entryType() == ACL_GROUP_OBJ || 0872 pACLItem->entryType() == ACL_OTHER ) ) 0873 { 0874 // Mark the required entries as no longer being partial entries. 0875 // That is, they will get applied to all selected directories. 0876 KACLListViewItem* pUserObj = findACLEntryByType( this, ACL_USER_OBJ ); 0877 pUserObj->entry()->setPartialEntry( false ); 0878 0879 KACLListViewItem* pGroupObj = findACLEntryByType( this, ACL_GROUP_OBJ ); 0880 pGroupObj->entry()->setPartialEntry( false ); 0881 0882 KACLListViewItem* pOther = findACLEntryByType( this, ACL_OTHER ); 0883 pOther->entry()->setPartialEntry( false ); 0884 0885 update(); 0886 } 0887 */ 0888 } 0889 0890 void KACLListView::slotItemDoubleClicked(QTreeWidgetItem *item, int column) 0891 { 0892 if (!item) { 0893 return; 0894 } 0895 0896 // avoid conflict with clicking to toggle permission 0897 if (column >= 2 && column <= 4) { 0898 return; 0899 } 0900 0901 KACLListViewItem *aclListItem = static_cast<KACLListViewItem *>(item); 0902 if (!aclListItem->isAllowedToChangeType()) { 0903 return; 0904 } 0905 0906 setCurrentItem(item); 0907 slotEditEntry(); 0908 } 0909 0910 void KACLListView::calculateEffectiveRights() 0911 { 0912 QTreeWidgetItemIterator it(this); 0913 KACLListViewItem *pItem; 0914 while ((pItem = dynamic_cast<KACLListViewItem *>(*it)) != nullptr) { 0915 ++it; 0916 pItem->calcEffectiveRights(); 0917 } 0918 } 0919 0920 unsigned short KACLListView::maskPermissions() const 0921 { 0922 return m_mask; 0923 } 0924 0925 void KACLListView::setMaskPermissions(unsigned short maskPerms) 0926 { 0927 m_mask = maskPerms; 0928 calculateEffectiveRights(); 0929 } 0930 0931 acl_perm_t KACLListView::maskPartialPermissions() const 0932 { 0933 // return m_pMaskEntry->m_partialPerms; 0934 return 0; 0935 } 0936 0937 void KACLListView::setMaskPartialPermissions(acl_perm_t /*maskPartialPerms*/) 0938 { 0939 // m_pMaskEntry->m_partialPerms = maskPartialPerms; 0940 calculateEffectiveRights(); 0941 } 0942 0943 bool KACLListView::hasDefaultEntries() const 0944 { 0945 QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); 0946 while (*it) { 0947 const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); 0948 ++it; 0949 if (item->isDefault) { 0950 return true; 0951 } 0952 } 0953 return false; 0954 } 0955 0956 const KACLListViewItem *KACLListView::findDefaultItemByType(EntryType type) const 0957 { 0958 return findItemByType(type, true); 0959 } 0960 0961 const KACLListViewItem *KACLListView::findItemByType(EntryType type, bool defaults) const 0962 { 0963 QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); 0964 while (*it) { 0965 const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); 0966 ++it; 0967 if (item->isDefault == defaults && item->type == type) { 0968 return item; 0969 } 0970 } 0971 return nullptr; 0972 } 0973 0974 unsigned short KACLListView::calculateMaskValue(bool defaults) const 0975 { 0976 // KACL auto-adds the relevant mask entries, so we can simply query 0977 bool dummy; 0978 return itemsToACL(defaults).maskPermissions(dummy); 0979 } 0980 0981 void KACLListView::slotAddEntry() 0982 { 0983 int allowedTypes = NamedUser | NamedGroup; 0984 if (!m_hasMask) { 0985 allowedTypes |= Mask; 0986 } 0987 int allowedDefaultTypes = NamedUser | NamedGroup; 0988 if (!findDefaultItemByType(Mask)) { 0989 allowedDefaultTypes |= Mask; 0990 } 0991 if (!hasDefaultEntries()) { 0992 allowedDefaultTypes |= User | Group; 0993 } 0994 EditACLEntryDialog dlg(this, 0995 nullptr, 0996 allowedUsers(false), 0997 allowedGroups(false), 0998 allowedUsers(true), 0999 allowedGroups(true), 1000 allowedTypes, 1001 allowedDefaultTypes, 1002 m_allowDefaults); 1003 dlg.exec(); 1004 KACLListViewItem *item = dlg.item(); 1005 if (!item) { 1006 return; // canceled 1007 } 1008 if (item->type == Mask && !item->isDefault) { 1009 m_hasMask = true; 1010 m_mask = item->value; 1011 } 1012 if (item->isDefault && !hasDefaultEntries()) { 1013 // first default entry, fill in what is needed 1014 if (item->type != User) { 1015 unsigned short v = findDefaultItemByType(User)->value; 1016 new KACLListViewItem(this, User, v, true); 1017 } 1018 if (item->type != Group) { 1019 unsigned short v = findDefaultItemByType(Group)->value; 1020 new KACLListViewItem(this, Group, v, true); 1021 } 1022 if (item->type != Others) { 1023 unsigned short v = findDefaultItemByType(Others)->value; 1024 new KACLListViewItem(this, Others, v, true); 1025 } 1026 } 1027 const KACLListViewItem *defaultMaskItem = findDefaultItemByType(Mask); 1028 if (item->isDefault && !defaultMaskItem) { 1029 unsigned short v = calculateMaskValue(true); 1030 new KACLListViewItem(this, Mask, v, true); 1031 } 1032 if (!item->isDefault && !m_hasMask && (item->type == Group || item->type == NamedUser || item->type == NamedGroup)) { 1033 // auto-add a mask entry 1034 unsigned short v = calculateMaskValue(false); 1035 new KACLListViewItem(this, Mask, v, false); 1036 m_hasMask = true; 1037 m_mask = v; 1038 } 1039 calculateEffectiveRights(); 1040 sortItems(sortColumn(), Qt::AscendingOrder); 1041 setCurrentItem(item); 1042 // QTreeWidget doesn't seem to emit, in this case, and we need to update 1043 // the buttons... 1044 if (topLevelItemCount() == 1) { 1045 Q_EMIT currentItemChanged(item, item); 1046 } 1047 } 1048 1049 void KACLListView::slotEditEntry() 1050 { 1051 QTreeWidgetItem *current = currentItem(); 1052 if (!current) { 1053 return; 1054 } 1055 KACLListViewItem *item = static_cast<KACLListViewItem *>(current); 1056 int allowedTypes = item->type | NamedUser | NamedGroup; 1057 bool itemWasMask = item->type == Mask; 1058 if (!m_hasMask || itemWasMask) { 1059 allowedTypes |= Mask; 1060 } 1061 int allowedDefaultTypes = item->type | NamedUser | NamedGroup; 1062 if (!findDefaultItemByType(Mask)) { 1063 allowedDefaultTypes |= Mask; 1064 } 1065 if (!hasDefaultEntries()) { 1066 allowedDefaultTypes |= User | Group; 1067 } 1068 1069 EditACLEntryDialog dlg(this, 1070 item, 1071 allowedUsers(false, item), 1072 allowedGroups(false, item), 1073 allowedUsers(true, item), 1074 allowedGroups(true, item), 1075 allowedTypes, 1076 allowedDefaultTypes, 1077 m_allowDefaults); 1078 dlg.exec(); 1079 if (itemWasMask && item->type != Mask) { 1080 m_hasMask = false; 1081 m_mask = 0; 1082 } 1083 if (!itemWasMask && item->type == Mask) { 1084 m_mask = item->value; 1085 m_hasMask = true; 1086 } 1087 calculateEffectiveRights(); 1088 sortItems(sortColumn(), Qt::AscendingOrder); 1089 } 1090 1091 void KACLListView::slotRemoveEntry() 1092 { 1093 QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected); 1094 while (*it) { 1095 KACLListViewItem *item = static_cast<KACLListViewItem *>(*it); 1096 ++it; 1097 /* First check if it's a mask entry and if so, make sure that there is 1098 * either no name user or group entry, which means the mask can be 1099 * removed, or don't remove it, but reset it. That is allowed. */ 1100 if (item->type == Mask) { 1101 bool itemWasDefault = item->isDefault; 1102 if (!itemWasDefault && maskCanBeDeleted()) { 1103 m_hasMask = false; 1104 m_mask = 0; 1105 delete item; 1106 } else if (itemWasDefault && defaultMaskCanBeDeleted()) { 1107 delete item; 1108 } else { 1109 item->value = 0; 1110 item->repaint(); 1111 } 1112 if (!itemWasDefault) { 1113 calculateEffectiveRights(); 1114 } 1115 } else { 1116 // for the base permissions, disable them, which is what libacl does 1117 if (!item->isDefault && (item->type == User || item->type == Group || item->type == Others)) { 1118 item->value = 0; 1119 item->repaint(); 1120 } else { 1121 delete item; 1122 } 1123 } 1124 } 1125 } 1126 1127 bool KACLListView::maskCanBeDeleted() const 1128 { 1129 return !findItemByType(NamedUser) && !findItemByType(NamedGroup); 1130 } 1131 1132 bool KACLListView::defaultMaskCanBeDeleted() const 1133 { 1134 return !findDefaultItemByType(NamedUser) && !findDefaultItemByType(NamedGroup); 1135 } 1136 1137 #include "moc_kacleditwidget.cpp" 1138 #include "moc_kacleditwidget_p.cpp" 1139 #endif