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