| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2005 Sean Harmer <sh@rama.homelinux.org> |
| 3 | SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org> |
| 4 | |
| 5 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 6 | */ |
| 7 | |
| 8 | #include "kacleditwidget.h" |
| 9 | #include "kacleditwidget_p.h" |
| 10 | #include "kio_widgets_debug.h" |
| 11 | |
| 12 | #if HAVE_POSIX_ACL |
| 13 | |
| 14 | #include <QButtonGroup> |
| 15 | #include <QCheckBox> |
| 16 | #include <QComboBox> |
| 17 | #include <QDialog> |
| 18 | #include <QDialogButtonBox> |
| 19 | #include <QGroupBox> |
| 20 | #include <QHeaderView> |
| 21 | #include <QLabel> |
| 22 | #include <QLayout> |
| 23 | #include <QMouseEvent> |
| 24 | #include <QPainter> |
| 25 | #include <QPushButton> |
| 26 | #include <QRadioButton> |
| 27 | #include <QScrollBar> |
| 28 | #include <QStackedWidget> |
| 29 | |
| 30 | #include <KLocalizedString> |
| 31 | #include <kfileitem.h> |
| 32 | |
| 33 | #if HAVE_ACL_LIBACL_H |
| 34 | #include <acl/libacl.h> |
| 35 | #endif |
| 36 | extern "C" { |
| 37 | #include <grp.h> |
| 38 | #include <pwd.h> |
| 39 | } |
| 40 | #include <assert.h> |
| 41 | |
| 42 | class KACLEditWidget::KACLEditWidgetPrivate |
| 43 | { |
| 44 | public: |
| 45 | KACLEditWidgetPrivate() |
| 46 | { |
| 47 | } |
| 48 | |
| 49 | // slots |
| 50 | void slotUpdateButtons(); |
| 51 | |
| 52 | KACLListView *m_listView; |
| 53 | QPushButton *m_AddBtn; |
| 54 | QPushButton *m_EditBtn; |
| 55 | QPushButton *m_DelBtn; |
| 56 | }; |
| 57 | |
| 58 | KACLEditWidget::KACLEditWidget(QWidget *parent) |
| 59 | : QWidget(parent) |
| 60 | , d(new KACLEditWidgetPrivate) |
| 61 | { |
| 62 | QHBoxLayout *hbox = new QHBoxLayout(this); |
| 63 | hbox->setContentsMargins(0, 0, 0, 0); |
| 64 | d->m_listView = new KACLListView(this); |
| 65 | hbox->addWidget(d->m_listView); |
| 66 | connect(d->m_listView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this]() { |
| 67 | d->slotUpdateButtons(); |
| 68 | }); |
| 69 | QVBoxLayout *vbox = new QVBoxLayout(); |
| 70 | hbox->addLayout(vbox); |
| 71 | d->m_AddBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("list-add" )), i18nc("@action:button" , "Add…" ), this); |
| 72 | vbox->addWidget(d->m_AddBtn); |
| 73 | d->m_AddBtn->setObjectName(QStringLiteral("add_entry_button" )); |
| 74 | connect(d->m_AddBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotAddEntry); |
| 75 | d->m_EditBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("document-edit" )), i18nc("@action:button" , "Edit…" ), this); |
| 76 | vbox->addWidget(d->m_EditBtn); |
| 77 | d->m_EditBtn->setObjectName(QStringLiteral("edit_entry_button" )); |
| 78 | connect(d->m_EditBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotEditEntry); |
| 79 | d->m_DelBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("list-remove" )), i18nc("@action:button" , "Delete" ), this); |
| 80 | vbox->addWidget(d->m_DelBtn); |
| 81 | d->m_DelBtn->setObjectName(QStringLiteral("delete_entry_button" )); |
| 82 | connect(d->m_DelBtn, &QAbstractButton::clicked, d->m_listView, &KACLListView::slotRemoveEntry); |
| 83 | vbox->addItem(new QSpacerItem(10, 10, QSizePolicy::Fixed, QSizePolicy::Expanding)); |
| 84 | d->slotUpdateButtons(); |
| 85 | } |
| 86 | |
| 87 | KACLEditWidget::~KACLEditWidget() = default; |
| 88 | |
| 89 | void KACLEditWidget::KACLEditWidgetPrivate::slotUpdateButtons() |
| 90 | { |
| 91 | bool atLeastOneIsNotDeletable = false; |
| 92 | bool atLeastOneIsNotAllowedToChangeType = false; |
| 93 | int selectedCount = 0; |
| 94 | QList<QTreeWidgetItem *> selected = m_listView->selectedItems(); |
| 95 | QListIterator<QTreeWidgetItem *> it(selected); |
| 96 | while (it.hasNext()) { |
| 97 | KACLListViewItem *item = static_cast<KACLListViewItem *>(it.next()); |
| 98 | ++selectedCount; |
| 99 | if (!item->isDeletable()) { |
| 100 | atLeastOneIsNotDeletable = true; |
| 101 | } |
| 102 | if (!item->isAllowedToChangeType()) { |
| 103 | atLeastOneIsNotAllowedToChangeType = true; |
| 104 | } |
| 105 | } |
| 106 | m_EditBtn->setEnabled(selectedCount && !atLeastOneIsNotAllowedToChangeType); |
| 107 | m_DelBtn->setEnabled(selectedCount && !atLeastOneIsNotDeletable); |
| 108 | } |
| 109 | |
| 110 | KACL KACLEditWidget::getACL() const |
| 111 | { |
| 112 | return d->m_listView->getACL(); |
| 113 | } |
| 114 | |
| 115 | KACL KACLEditWidget::getDefaultACL() const |
| 116 | { |
| 117 | return d->m_listView->getDefaultACL(); |
| 118 | } |
| 119 | |
| 120 | void KACLEditWidget::setACL(const KACL &acl) |
| 121 | { |
| 122 | d->m_listView->setACL(acl); |
| 123 | } |
| 124 | |
| 125 | void KACLEditWidget::setDefaultACL(const KACL &acl) |
| 126 | { |
| 127 | d->m_listView->setDefaultACL(acl); |
| 128 | } |
| 129 | |
| 130 | void KACLEditWidget::setAllowDefaults(bool value) |
| 131 | { |
| 132 | d->m_listView->setAllowDefaults(value); |
| 133 | } |
| 134 | |
| 135 | KACLListViewItem::KACLListViewItem(QTreeWidget *parent, KACLListView::EntryType _type, unsigned short _value, bool defaults, const QString &_qualifier) |
| 136 | : QTreeWidgetItem(parent) |
| 137 | , type(_type) |
| 138 | , value(_value) |
| 139 | , isDefault(defaults) |
| 140 | , qualifier(_qualifier) |
| 141 | , isPartial(false) |
| 142 | { |
| 143 | m_pACLListView = qobject_cast<KACLListView *>(parent); |
| 144 | repaint(); |
| 145 | } |
| 146 | |
| 147 | KACLListViewItem::~KACLListViewItem() |
| 148 | { |
| 149 | } |
| 150 | |
| 151 | QString KACLListViewItem::key() const |
| 152 | { |
| 153 | QString key; |
| 154 | if (!isDefault) { |
| 155 | key = QLatin1Char('A'); |
| 156 | } else { |
| 157 | key = QLatin1Char('B'); |
| 158 | } |
| 159 | switch (type) { |
| 160 | case KACLListView::User: |
| 161 | key += QLatin1Char('A'); |
| 162 | break; |
| 163 | case KACLListView::Group: |
| 164 | key += QLatin1Char('B'); |
| 165 | break; |
| 166 | case KACLListView::Others: |
| 167 | key += QLatin1Char('C'); |
| 168 | break; |
| 169 | case KACLListView::Mask: |
| 170 | key += QLatin1Char('D'); |
| 171 | break; |
| 172 | case KACLListView::NamedUser: |
| 173 | key += QLatin1Char('E') + text(1); |
| 174 | break; |
| 175 | case KACLListView::NamedGroup: |
| 176 | key += QLatin1Char('F') + text(1); |
| 177 | break; |
| 178 | default: |
| 179 | key += text(0); |
| 180 | break; |
| 181 | } |
| 182 | return key; |
| 183 | } |
| 184 | |
| 185 | bool KACLListViewItem::operator<(const QTreeWidgetItem &other) const |
| 186 | { |
| 187 | return key() < static_cast<const KACLListViewItem &>(other).key(); |
| 188 | } |
| 189 | |
| 190 | void KACLListViewItem::updatePermissionIcons() |
| 191 | { |
| 192 | unsigned int partialPerms = value; |
| 193 | |
| 194 | if (value & ACL_READ) { |
| 195 | setIcon(2, QIcon::fromTheme(QStringLiteral("checkmark" ))); |
| 196 | } else if (partialPerms & ACL_READ) { |
| 197 | setIcon(2, QIcon::fromTheme(QStringLiteral("checkmark-partial" ))); |
| 198 | } else { |
| 199 | setIcon(2, QIcon()); |
| 200 | } |
| 201 | |
| 202 | if (value & ACL_WRITE) { |
| 203 | setIcon(3, QIcon::fromTheme(QStringLiteral("checkmark" ))); |
| 204 | } else if (partialPerms & ACL_WRITE) { |
| 205 | setIcon(3, QIcon::fromTheme(QStringLiteral("checkmark-partial" ))); |
| 206 | } else { |
| 207 | setIcon(3, QIcon()); |
| 208 | } |
| 209 | |
| 210 | if (value & ACL_EXECUTE) { |
| 211 | setIcon(4, QIcon::fromTheme(QStringLiteral("checkmark" ))); |
| 212 | } else if (partialPerms & ACL_EXECUTE) { |
| 213 | setIcon(4, QIcon::fromTheme(QStringLiteral("checkmark-partial" ))); |
| 214 | } else { |
| 215 | setIcon(4, QIcon()); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | void KACLListViewItem::repaint() |
| 220 | { |
| 221 | QString text; |
| 222 | QString icon; |
| 223 | |
| 224 | switch (type) { |
| 225 | case KACLListView::User: |
| 226 | default: |
| 227 | text = i18nc("Unix permissions" , "Owner" ); |
| 228 | icon = QStringLiteral("user-gray" ); |
| 229 | break; |
| 230 | case KACLListView::Group: |
| 231 | text = i18nc("UNIX permissions" , "Owning Group" ); |
| 232 | icon = QStringLiteral("group-gray" ); |
| 233 | break; |
| 234 | case KACLListView::Others: |
| 235 | text = i18nc("UNIX permissions" , "Others" ); |
| 236 | icon = QStringLiteral("user-others-gray" ); |
| 237 | break; |
| 238 | case KACLListView::Mask: |
| 239 | text = i18nc("UNIX permissions" , "Mask" ); |
| 240 | icon = QStringLiteral("view-filter" ); |
| 241 | break; |
| 242 | case KACLListView::NamedUser: |
| 243 | text = i18nc("UNIX permissions" , "Named User" ); |
| 244 | icon = QStringLiteral("user" ); |
| 245 | break; |
| 246 | case KACLListView::NamedGroup: |
| 247 | text = i18nc("UNIX permissions" , "Others" ); |
| 248 | icon = QStringLiteral("user-others" ); |
| 249 | break; |
| 250 | } |
| 251 | setText(0, text); |
| 252 | setIcon(0, QIcon::fromTheme(icon)); |
| 253 | if (isDefault) { |
| 254 | setText(0, i18n("Owner (Default)" )); |
| 255 | } |
| 256 | setText(1, qualifier); |
| 257 | // Set the icons for which of the perms are set |
| 258 | updatePermissionIcons(); |
| 259 | } |
| 260 | |
| 261 | void KACLListViewItem::calcEffectiveRights() |
| 262 | { |
| 263 | QString strEffective = QStringLiteral("---" ); |
| 264 | |
| 265 | // Do we need to worry about the mask entry? It applies to named users, |
| 266 | // owning group, and named groups |
| 267 | if (m_pACLListView->hasMaskEntry() // clang-format off |
| 268 | && (type == KACLListView::NamedUser || type == KACLListView::Group || type == KACLListView::NamedGroup) |
| 269 | && !isDefault) { // clang-format on |
| 270 | strEffective[0] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_READ) ? 'r' : '-'); |
| 271 | strEffective[1] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_WRITE) ? 'w' : '-'); |
| 272 | strEffective[2] = QLatin1Char((m_pACLListView->maskPermissions() & value & ACL_EXECUTE) ? 'x' : '-'); |
| 273 | /* |
| 274 | // What about any partial perms? |
| 275 | if ( maskPerms & partialPerms & ACL_READ || // Partial perms on entry |
| 276 | maskPartialPerms & perms & ACL_READ || // Partial perms on mask |
| 277 | maskPartialPerms & partialPerms & ACL_READ ) // Partial perms on mask and entry |
| 278 | strEffective[0] = 'R'; |
| 279 | if ( maskPerms & partialPerms & ACL_WRITE || // Partial perms on entry |
| 280 | maskPartialPerms & perms & ACL_WRITE || // Partial perms on mask |
| 281 | maskPartialPerms & partialPerms & ACL_WRITE ) // Partial perms on mask and entry |
| 282 | strEffective[1] = 'W'; |
| 283 | if ( maskPerms & partialPerms & ACL_EXECUTE || // Partial perms on entry |
| 284 | maskPartialPerms & perms & ACL_EXECUTE || // Partial perms on mask |
| 285 | maskPartialPerms & partialPerms & ACL_EXECUTE ) // Partial perms on mask and entry |
| 286 | strEffective[2] = 'X'; |
| 287 | */ |
| 288 | } else { |
| 289 | // No, the effective value are just the value in this entry |
| 290 | strEffective[0] = QLatin1Char((value & ACL_READ) ? 'r' : '-'); |
| 291 | strEffective[1] = QLatin1Char((value & ACL_WRITE) ? 'w' : '-'); |
| 292 | strEffective[2] = QLatin1Char((value & ACL_EXECUTE) ? 'x' : '-'); |
| 293 | |
| 294 | /* |
| 295 | // What about any partial perms? |
| 296 | if ( partialPerms & ACL_READ ) |
| 297 | strEffective[0] = 'R'; |
| 298 | if ( partialPerms & ACL_WRITE ) |
| 299 | strEffective[1] = 'W'; |
| 300 | if ( partialPerms & ACL_EXECUTE ) |
| 301 | strEffective[2] = 'X'; |
| 302 | */ |
| 303 | } |
| 304 | setText(5, strEffective); |
| 305 | } |
| 306 | |
| 307 | bool KACLListViewItem::isDeletable() const |
| 308 | { |
| 309 | bool isMaskAndDeletable = false; |
| 310 | if (type == KACLListView::Mask) { |
| 311 | if (!isDefault && m_pACLListView->maskCanBeDeleted()) { |
| 312 | isMaskAndDeletable = true; |
| 313 | } else if (isDefault && m_pACLListView->defaultMaskCanBeDeleted()) { |
| 314 | isMaskAndDeletable = true; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | /* clang-format off */ |
| 319 | return type != KACLListView::User |
| 320 | && type != KACLListView::Group |
| 321 | && type != KACLListView::Others |
| 322 | && (type != KACLListView::Mask || isMaskAndDeletable); |
| 323 | /* clang-format on */ |
| 324 | } |
| 325 | |
| 326 | bool KACLListViewItem::isAllowedToChangeType() const |
| 327 | { |
| 328 | /* clang-format off */ |
| 329 | return type != KACLListView::User |
| 330 | && type != KACLListView::Group |
| 331 | && type != KACLListView::Others |
| 332 | && type != KACLListView::Mask; |
| 333 | /* clang-format on */ |
| 334 | } |
| 335 | |
| 336 | void KACLListViewItem::togglePerm(acl_perm_t perm) |
| 337 | { |
| 338 | value ^= perm; // Toggle the perm |
| 339 | if (type == KACLListView::Mask && !isDefault) { |
| 340 | m_pACLListView->setMaskPermissions(value); |
| 341 | } |
| 342 | calcEffectiveRights(); |
| 343 | updatePermissionIcons(); |
| 344 | /* |
| 345 | // If the perm is in the partial perms then remove it. i.e. Once |
| 346 | // a user changes a partial perm it then applies to all selected files. |
| 347 | if ( m_pEntry->m_partialPerms & perm ) |
| 348 | m_pEntry->m_partialPerms ^= perm; |
| 349 | |
| 350 | m_pEntry->setPartialEntry( false ); |
| 351 | // Make sure that all entries have their effective rights calculated if |
| 352 | // we are changing the ACL_MASK entry. |
| 353 | if ( type == Mask ) |
| 354 | { |
| 355 | m_pACLListView->setMaskPartialPermissions( m_pEntry->m_partialPerms ); |
| 356 | m_pACLListView->setMaskPermissions( value ); |
| 357 | m_pACLListView->calculateEffectiveRights(); |
| 358 | } |
| 359 | */ |
| 360 | } |
| 361 | |
| 362 | EditACLEntryDialog::EditACLEntryDialog(KACLListView *listView, |
| 363 | KACLListViewItem *item, |
| 364 | const QStringList &users, |
| 365 | const QStringList &groups, |
| 366 | const QStringList &defaultUsers, |
| 367 | const QStringList &defaultGroups, |
| 368 | int allowedTypes, |
| 369 | int allowedDefaultTypes, |
| 370 | bool allowDefaults) |
| 371 | : QDialog(listView) |
| 372 | , m_listView(listView) |
| 373 | , m_item(item) |
| 374 | , m_users(users) |
| 375 | , m_groups(groups) |
| 376 | , m_defaultUsers(defaultUsers) |
| 377 | , m_defaultGroups(defaultGroups) |
| 378 | , m_allowedTypes(allowedTypes) |
| 379 | , m_allowedDefaultTypes(allowedDefaultTypes) |
| 380 | , m_defaultCB(nullptr) |
| 381 | { |
| 382 | setObjectName(QStringLiteral("edit_entry_dialog" )); |
| 383 | setModal(true); |
| 384 | setWindowTitle(i18n("Edit ACL Entry" )); |
| 385 | |
| 386 | QVBoxLayout *mainLayout = new QVBoxLayout(this); |
| 387 | QGroupBox *gb = new QGroupBox(i18n("Entry Type" ), this); |
| 388 | gb->setFlat(true); |
| 389 | QVBoxLayout *gbLayout = new QVBoxLayout(gb); |
| 390 | |
| 391 | m_buttonGroup = new QButtonGroup(this); |
| 392 | |
| 393 | if (allowDefaults) { |
| 394 | m_defaultCB = new QCheckBox(i18n("Default for new files in this folder" ), this); |
| 395 | m_defaultCB->setObjectName(QStringLiteral("defaultCB" )); |
| 396 | mainLayout->addWidget(m_defaultCB); |
| 397 | connect(m_defaultCB, &QAbstractButton::toggled, this, &EditACLEntryDialog::slotUpdateAllowedUsersAndGroups); |
| 398 | connect(m_defaultCB, &QAbstractButton::toggled, this, &EditACLEntryDialog::slotUpdateAllowedTypes); |
| 399 | } |
| 400 | |
| 401 | QRadioButton *ownerType = new QRadioButton(i18n("Owner" ), gb); |
| 402 | ownerType->setObjectName(QStringLiteral("ownerType" )); |
| 403 | gbLayout->addWidget(ownerType); |
| 404 | m_buttonGroup->addButton(ownerType); |
| 405 | m_buttonIds.insert(ownerType, KACLListView::User); |
| 406 | QRadioButton *owningGroupType = new QRadioButton(i18n("Owning Group" ), gb); |
| 407 | owningGroupType->setObjectName(QStringLiteral("owningGroupType" )); |
| 408 | gbLayout->addWidget(owningGroupType); |
| 409 | m_buttonGroup->addButton(owningGroupType); |
| 410 | m_buttonIds.insert(owningGroupType, KACLListView::Group); |
| 411 | QRadioButton *othersType = new QRadioButton(i18n("Others" ), gb); |
| 412 | othersType->setObjectName(QStringLiteral("othersType" )); |
| 413 | gbLayout->addWidget(othersType); |
| 414 | m_buttonGroup->addButton(othersType); |
| 415 | m_buttonIds.insert(othersType, KACLListView::Others); |
| 416 | QRadioButton *maskType = new QRadioButton(i18n("Mask" ), gb); |
| 417 | maskType->setObjectName(QStringLiteral("maskType" )); |
| 418 | gbLayout->addWidget(maskType); |
| 419 | m_buttonGroup->addButton(maskType); |
| 420 | m_buttonIds.insert(maskType, KACLListView::Mask); |
| 421 | QRadioButton *namedUserType = new QRadioButton(i18n("Named user" ), gb); |
| 422 | namedUserType->setObjectName(QStringLiteral("namesUserType" )); |
| 423 | gbLayout->addWidget(namedUserType); |
| 424 | m_buttonGroup->addButton(namedUserType); |
| 425 | m_buttonIds.insert(namedUserType, KACLListView::NamedUser); |
| 426 | QRadioButton *namedGroupType = new QRadioButton(i18n("Named group" ), gb); |
| 427 | namedGroupType->setObjectName(QStringLiteral("namedGroupType" )); |
| 428 | gbLayout->addWidget(namedGroupType); |
| 429 | m_buttonGroup->addButton(namedGroupType); |
| 430 | m_buttonIds.insert(namedGroupType, KACLListView::NamedGroup); |
| 431 | |
| 432 | mainLayout->addWidget(gb); |
| 433 | |
| 434 | connect(m_buttonGroup, &QButtonGroup::buttonClicked, this, &EditACLEntryDialog::slotSelectionChanged); |
| 435 | |
| 436 | m_widgetStack = new QStackedWidget(this); |
| 437 | mainLayout->addWidget(m_widgetStack); |
| 438 | |
| 439 | // users box |
| 440 | QWidget *usersBox = new QWidget(m_widgetStack); |
| 441 | QHBoxLayout *usersLayout = new QHBoxLayout(usersBox); |
| 442 | m_widgetStack->addWidget(usersBox); |
| 443 | |
| 444 | QLabel *usersLabel = new QLabel(i18n("User: " ), usersBox); |
| 445 | m_usersCombo = new QComboBox(usersBox); |
| 446 | m_usersCombo->setEditable(false); |
| 447 | m_usersCombo->setObjectName(QStringLiteral("users" )); |
| 448 | usersLabel->setBuddy(m_usersCombo); |
| 449 | |
| 450 | usersLayout->addWidget(usersLabel); |
| 451 | usersLayout->addWidget(m_usersCombo); |
| 452 | |
| 453 | // groups box |
| 454 | QWidget *groupsBox = new QWidget(m_widgetStack); |
| 455 | QHBoxLayout *groupsLayout = new QHBoxLayout(usersBox); |
| 456 | m_widgetStack->addWidget(groupsBox); |
| 457 | |
| 458 | QLabel *groupsLabel = new QLabel(i18n("Group: " ), groupsBox); |
| 459 | m_groupsCombo = new QComboBox(groupsBox); |
| 460 | m_groupsCombo->setEditable(false); |
| 461 | m_groupsCombo->setObjectName(QStringLiteral("groups" )); |
| 462 | groupsLabel->setBuddy(m_groupsCombo); |
| 463 | |
| 464 | groupsLayout->addWidget(groupsLabel); |
| 465 | groupsLayout->addWidget(m_groupsCombo); |
| 466 | |
| 467 | if (m_item) { |
| 468 | m_buttonIds.key(m_item->type)->setChecked(true); |
| 469 | if (m_defaultCB) { |
| 470 | m_defaultCB->setChecked(m_item->isDefault); |
| 471 | } |
| 472 | slotUpdateAllowedTypes(); |
| 473 | slotSelectionChanged(m_buttonIds.key(m_item->type)); |
| 474 | slotUpdateAllowedUsersAndGroups(); |
| 475 | if (m_item->type == KACLListView::NamedUser) { |
| 476 | m_usersCombo->setItemText(m_usersCombo->currentIndex(), m_item->qualifier); |
| 477 | } else if (m_item->type == KACLListView::NamedGroup) { |
| 478 | m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), m_item->qualifier); |
| 479 | } |
| 480 | } else { |
| 481 | // new entry, preselect "named user", arguably the most common one |
| 482 | m_buttonIds.key(KACLListView::NamedUser)->setChecked(true); |
| 483 | slotUpdateAllowedTypes(); |
| 484 | slotSelectionChanged(m_buttonIds.key(KACLListView::NamedUser)); |
| 485 | slotUpdateAllowedUsersAndGroups(); |
| 486 | } |
| 487 | |
| 488 | QDialogButtonBox *buttonBox = new QDialogButtonBox(this); |
| 489 | buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); |
| 490 | connect(buttonBox, &QDialogButtonBox::accepted, this, &EditACLEntryDialog::slotOk); |
| 491 | connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); |
| 492 | mainLayout->addWidget(buttonBox); |
| 493 | |
| 494 | adjustSize(); |
| 495 | } |
| 496 | |
| 497 | void EditACLEntryDialog::slotUpdateAllowedTypes() |
| 498 | { |
| 499 | int allowedTypes = m_allowedTypes; |
| 500 | if (m_defaultCB && m_defaultCB->isChecked()) { |
| 501 | allowedTypes = m_allowedDefaultTypes; |
| 502 | } |
| 503 | for (int i = 1; i < KACLListView::AllTypes; i = i * 2) { |
| 504 | if (allowedTypes & i) { |
| 505 | m_buttonIds.key(i)->show(); |
| 506 | } else { |
| 507 | m_buttonIds.key(i)->hide(); |
| 508 | } |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | void EditACLEntryDialog::slotUpdateAllowedUsersAndGroups() |
| 513 | { |
| 514 | const QString oldUser = m_usersCombo->currentText(); |
| 515 | const QString oldGroup = m_groupsCombo->currentText(); |
| 516 | m_usersCombo->clear(); |
| 517 | m_groupsCombo->clear(); |
| 518 | if (m_defaultCB && m_defaultCB->isChecked()) { |
| 519 | m_usersCombo->addItems(m_defaultUsers); |
| 520 | if (m_defaultUsers.contains(oldUser)) { |
| 521 | m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); |
| 522 | } |
| 523 | m_groupsCombo->addItems(m_defaultGroups); |
| 524 | if (m_defaultGroups.contains(oldGroup)) { |
| 525 | m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); |
| 526 | } |
| 527 | } else { |
| 528 | m_usersCombo->addItems(m_users); |
| 529 | if (m_users.contains(oldUser)) { |
| 530 | m_usersCombo->setItemText(m_usersCombo->currentIndex(), oldUser); |
| 531 | } |
| 532 | m_groupsCombo->addItems(m_groups); |
| 533 | if (m_groups.contains(oldGroup)) { |
| 534 | m_groupsCombo->setItemText(m_groupsCombo->currentIndex(), oldGroup); |
| 535 | } |
| 536 | } |
| 537 | } |
| 538 | void EditACLEntryDialog::slotOk() |
| 539 | { |
| 540 | KACLListView::EntryType type = static_cast<KACLListView::EntryType>(m_buttonIds[m_buttonGroup->checkedButton()]); |
| 541 | |
| 542 | qCWarning(KIO_WIDGETS) << "Type 2: " << type; |
| 543 | |
| 544 | QString qualifier; |
| 545 | if (type == KACLListView::NamedUser) { |
| 546 | qualifier = m_usersCombo->currentText(); |
| 547 | } |
| 548 | if (type == KACLListView::NamedGroup) { |
| 549 | qualifier = m_groupsCombo->currentText(); |
| 550 | } |
| 551 | |
| 552 | if (!m_item) { |
| 553 | m_item = new KACLListViewItem(m_listView, type, ACL_READ | ACL_WRITE | ACL_EXECUTE, false, qualifier); |
| 554 | } else { |
| 555 | m_item->type = type; |
| 556 | m_item->qualifier = qualifier; |
| 557 | } |
| 558 | if (m_defaultCB) { |
| 559 | m_item->isDefault = m_defaultCB->isChecked(); |
| 560 | } |
| 561 | m_item->repaint(); |
| 562 | |
| 563 | QDialog::accept(); |
| 564 | } |
| 565 | |
| 566 | void EditACLEntryDialog::slotSelectionChanged(QAbstractButton *button) |
| 567 | { |
| 568 | switch (m_buttonIds[button]) { |
| 569 | case KACLListView::User: |
| 570 | case KACLListView::Group: |
| 571 | case KACLListView::Others: |
| 572 | case KACLListView::Mask: |
| 573 | m_widgetStack->setEnabled(false); |
| 574 | break; |
| 575 | case KACLListView::NamedUser: |
| 576 | m_widgetStack->setEnabled(true); |
| 577 | m_widgetStack->setCurrentIndex(0 /* User */); |
| 578 | break; |
| 579 | case KACLListView::NamedGroup: |
| 580 | m_widgetStack->setEnabled(true); |
| 581 | m_widgetStack->setCurrentIndex(1 /* Group */); |
| 582 | break; |
| 583 | default: |
| 584 | break; |
| 585 | } |
| 586 | } |
| 587 | |
| 588 | KACLListView::KACLListView(QWidget *parent) |
| 589 | : QTreeWidget(parent) |
| 590 | , m_hasMask(false) |
| 591 | , m_allowDefaults(false) |
| 592 | { |
| 593 | // Add the columns |
| 594 | setColumnCount(6); |
| 595 | const QStringList headers{ |
| 596 | i18n("Type" ), |
| 597 | i18n("Name" ), |
| 598 | i18nc("read permission" , "r" ), |
| 599 | i18nc("write permission" , "w" ), |
| 600 | i18nc("execute permission" , "x" ), |
| 601 | i18n("Effective" ), |
| 602 | }; |
| 603 | setHeaderLabels(headers); |
| 604 | |
| 605 | setSortingEnabled(false); |
| 606 | setSelectionMode(QAbstractItemView::ExtendedSelection); |
| 607 | header()->setSectionResizeMode(QHeaderView::ResizeToContents); |
| 608 | setRootIsDecorated(false); |
| 609 | |
| 610 | connect(this, &QTreeWidget::itemClicked, this, &KACLListView::slotItemClicked); |
| 611 | |
| 612 | connect(this, &KACLListView::itemDoubleClicked, this, &KACLListView::slotItemDoubleClicked); |
| 613 | } |
| 614 | |
| 615 | KACLListView::~KACLListView() |
| 616 | { |
| 617 | } |
| 618 | |
| 619 | QSize KACLListView::sizeHint() const |
| 620 | { |
| 621 | const int width = header()->length() + verticalScrollBar()->width(); |
| 622 | const int height = 7 * rowHeight(model()->index(0, 0, QModelIndex())); |
| 623 | return {width, height}; |
| 624 | } |
| 625 | |
| 626 | QStringList KACLListView::allowedUsers(bool defaults, KACLListViewItem *allowedItem) |
| 627 | { |
| 628 | if (m_allUsers.isEmpty()) { |
| 629 | struct passwd *user = nullptr; |
| 630 | setpwent(); |
| 631 | while ((user = getpwent()) != nullptr) { |
| 632 | m_allUsers << QString::fromLatin1(user->pw_name); |
| 633 | } |
| 634 | endpwent(); |
| 635 | m_allUsers.sort(); |
| 636 | } |
| 637 | |
| 638 | QStringList allowedUsers = m_allUsers; |
| 639 | QTreeWidgetItemIterator it(this); |
| 640 | while (*it) { |
| 641 | const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); |
| 642 | ++it; |
| 643 | if (item->type != NamedUser || item->isDefault != defaults) { |
| 644 | continue; |
| 645 | } |
| 646 | if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { |
| 647 | continue; |
| 648 | } |
| 649 | allowedUsers.removeAll(item->qualifier); |
| 650 | } |
| 651 | return allowedUsers; |
| 652 | } |
| 653 | |
| 654 | QStringList KACLListView::allowedGroups(bool defaults, KACLListViewItem *allowedItem) |
| 655 | { |
| 656 | if (m_allGroups.isEmpty()) { |
| 657 | struct group *gr = nullptr; |
| 658 | setgrent(); |
| 659 | while ((gr = getgrent()) != nullptr) { |
| 660 | m_allGroups << QString::fromLatin1(gr->gr_name); |
| 661 | } |
| 662 | endgrent(); |
| 663 | m_allGroups.sort(); |
| 664 | } |
| 665 | |
| 666 | QStringList allowedGroups = m_allGroups; |
| 667 | QTreeWidgetItemIterator it(this); |
| 668 | while (*it) { |
| 669 | const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); |
| 670 | ++it; |
| 671 | if (item->type != NamedGroup || item->isDefault != defaults) { |
| 672 | continue; |
| 673 | } |
| 674 | if (allowedItem && item == allowedItem && allowedItem->isDefault == defaults) { |
| 675 | continue; |
| 676 | } |
| 677 | allowedGroups.removeAll(item->qualifier); |
| 678 | } |
| 679 | return allowedGroups; |
| 680 | } |
| 681 | |
| 682 | void KACLListView::fillItemsFromACL(const KACL &pACL, bool defaults) |
| 683 | { |
| 684 | // clear out old entries of that ilk |
| 685 | QTreeWidgetItemIterator it(this); |
| 686 | while (KACLListViewItem *item = static_cast<KACLListViewItem *>(*it)) { |
| 687 | ++it; |
| 688 | if (item->isDefault == defaults) { |
| 689 | delete item; |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | new KACLListViewItem(this, User, pACL.ownerPermissions(), defaults); |
| 694 | |
| 695 | new KACLListViewItem(this, Group, pACL.owningGroupPermissions(), defaults); |
| 696 | |
| 697 | new KACLListViewItem(this, Others, pACL.othersPermissions(), defaults); |
| 698 | |
| 699 | bool hasMask = false; |
| 700 | unsigned short mask = pACL.maskPermissions(hasMask); |
| 701 | if (hasMask) { |
| 702 | new KACLListViewItem(this, Mask, mask, defaults); |
| 703 | } |
| 704 | |
| 705 | // read all named user entries |
| 706 | const ACLUserPermissionsList &userList = pACL.allUserPermissions(); |
| 707 | ACLUserPermissionsConstIterator itu = userList.begin(); |
| 708 | while (itu != userList.end()) { |
| 709 | new KACLListViewItem(this, NamedUser, (*itu).second, defaults, (*itu).first); |
| 710 | ++itu; |
| 711 | } |
| 712 | |
| 713 | // and now all named groups |
| 714 | const ACLUserPermissionsList &groupList = pACL.allGroupPermissions(); |
| 715 | ACLUserPermissionsConstIterator itg = groupList.begin(); |
| 716 | while (itg != groupList.end()) { |
| 717 | new KACLListViewItem(this, NamedGroup, (*itg).second, defaults, (*itg).first); |
| 718 | ++itg; |
| 719 | } |
| 720 | } |
| 721 | |
| 722 | void KACLListView::setACL(const KACL &acl) |
| 723 | { |
| 724 | if (!acl.isValid()) { |
| 725 | return; |
| 726 | } |
| 727 | // Remove any entries left over from displaying a previous ACL |
| 728 | m_ACL = acl; |
| 729 | fillItemsFromACL(m_ACL); |
| 730 | |
| 731 | m_mask = acl.maskPermissions(m_hasMask); |
| 732 | calculateEffectiveRights(); |
| 733 | } |
| 734 | |
| 735 | void KACLListView::setDefaultACL(const KACL &acl) |
| 736 | { |
| 737 | if (!acl.isValid()) { |
| 738 | return; |
| 739 | } |
| 740 | m_defaultACL = acl; |
| 741 | fillItemsFromACL(m_defaultACL, true); |
| 742 | calculateEffectiveRights(); |
| 743 | } |
| 744 | |
| 745 | KACL KACLListView::itemsToACL(bool defaults) const |
| 746 | { |
| 747 | KACL newACL(0); |
| 748 | bool atLeastOneEntry = false; |
| 749 | ACLUserPermissionsList users; |
| 750 | ACLGroupPermissionsList groups; |
| 751 | QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); |
| 752 | while (QTreeWidgetItem *qlvi = *it) { |
| 753 | ++it; |
| 754 | const KACLListViewItem *item = static_cast<KACLListViewItem *>(qlvi); |
| 755 | if (item->isDefault != defaults) { |
| 756 | continue; |
| 757 | } |
| 758 | atLeastOneEntry = true; |
| 759 | switch (item->type) { |
| 760 | case User: |
| 761 | newACL.setOwnerPermissions(item->value); |
| 762 | break; |
| 763 | case Group: |
| 764 | newACL.setOwningGroupPermissions(item->value); |
| 765 | break; |
| 766 | case Others: |
| 767 | newACL.setOthersPermissions(item->value); |
| 768 | break; |
| 769 | case Mask: |
| 770 | newACL.setMaskPermissions(item->value); |
| 771 | break; |
| 772 | case NamedUser: |
| 773 | users.append(qMakePair(item->text(1), item->value)); |
| 774 | break; |
| 775 | case NamedGroup: |
| 776 | groups.append(qMakePair(item->text(1), item->value)); |
| 777 | break; |
| 778 | default: |
| 779 | break; |
| 780 | } |
| 781 | } |
| 782 | if (atLeastOneEntry) { |
| 783 | newACL.setAllUserPermissions(users); |
| 784 | newACL.setAllGroupPermissions(groups); |
| 785 | if (newACL.isValid()) { |
| 786 | return newACL; |
| 787 | } |
| 788 | } |
| 789 | return KACL(); |
| 790 | } |
| 791 | |
| 792 | KACL KACLListView::getACL() |
| 793 | { |
| 794 | return itemsToACL(false); |
| 795 | } |
| 796 | |
| 797 | KACL KACLListView::getDefaultACL() |
| 798 | { |
| 799 | return itemsToACL(true); |
| 800 | } |
| 801 | |
| 802 | void KACLListView::contentsMousePressEvent(QMouseEvent * /*e*/) |
| 803 | { |
| 804 | /* |
| 805 | QTreeWidgetItem *clickedItem = itemAt( e->pos() ); |
| 806 | if ( !clickedItem ) return; |
| 807 | // if the click is on an as yet unselected item, select it first |
| 808 | if ( !clickedItem->isSelected() ) |
| 809 | QAbstractItemView::contentsMousePressEvent( e ); |
| 810 | |
| 811 | if ( !currentItem() ) return; |
| 812 | int column = header()->sectionAt( e->x() ); |
| 813 | acl_perm_t perm; |
| 814 | switch ( column ) |
| 815 | { |
| 816 | case 2: |
| 817 | perm = ACL_READ; |
| 818 | break; |
| 819 | case 3: |
| 820 | perm = ACL_WRITE; |
| 821 | break; |
| 822 | case 4: |
| 823 | perm = ACL_EXECUTE; |
| 824 | break; |
| 825 | default: |
| 826 | return QTreeWidget::contentsMousePressEvent( e ); |
| 827 | } |
| 828 | KACLListViewItem* referenceItem = static_cast<KACLListViewItem*>( clickedItem ); |
| 829 | unsigned short referenceHadItSet = referenceItem->value & perm; |
| 830 | QTreeWidgetItemIterator it( this ); |
| 831 | while ( KACLListViewItem* item = static_cast<KACLListViewItem*>( *it ) ) { |
| 832 | ++it; |
| 833 | if ( !item->isSelected() ) continue; |
| 834 | // toggle those with the same value as the clicked item, leave the others |
| 835 | if ( referenceHadItSet == ( item->value & perm ) ) |
| 836 | item->togglePerm( perm ); |
| 837 | } |
| 838 | */ |
| 839 | } |
| 840 | |
| 841 | void KACLListView::slotItemClicked(QTreeWidgetItem *pItem, int col) |
| 842 | { |
| 843 | if (!pItem) { |
| 844 | return; |
| 845 | } |
| 846 | |
| 847 | QTreeWidgetItemIterator it(this); |
| 848 | while (KACLListViewItem *item = static_cast<KACLListViewItem *>(*it)) { |
| 849 | ++it; |
| 850 | if (!item->isSelected()) { |
| 851 | continue; |
| 852 | } |
| 853 | switch (col) { |
| 854 | case 2: |
| 855 | item->togglePerm(ACL_READ); |
| 856 | break; |
| 857 | case 3: |
| 858 | item->togglePerm(ACL_WRITE); |
| 859 | break; |
| 860 | case 4: |
| 861 | item->togglePerm(ACL_EXECUTE); |
| 862 | break; |
| 863 | |
| 864 | default:; // Do nothing |
| 865 | } |
| 866 | } |
| 867 | /* |
| 868 | // Has the user changed one of the required entries in a default ACL? |
| 869 | if ( m_pACL->aclType() == ACL_TYPE_DEFAULT && |
| 870 | ( col == 2 || col == 3 || col == 4 ) && |
| 871 | ( pACLItem->entryType() == ACL_USER_OBJ || |
| 872 | pACLItem->entryType() == ACL_GROUP_OBJ || |
| 873 | pACLItem->entryType() == ACL_OTHER ) ) |
| 874 | { |
| 875 | // Mark the required entries as no longer being partial entries. |
| 876 | // That is, they will get applied to all selected directories. |
| 877 | KACLListViewItem* pUserObj = findACLEntryByType( this, ACL_USER_OBJ ); |
| 878 | pUserObj->entry()->setPartialEntry( false ); |
| 879 | |
| 880 | KACLListViewItem* pGroupObj = findACLEntryByType( this, ACL_GROUP_OBJ ); |
| 881 | pGroupObj->entry()->setPartialEntry( false ); |
| 882 | |
| 883 | KACLListViewItem* pOther = findACLEntryByType( this, ACL_OTHER ); |
| 884 | pOther->entry()->setPartialEntry( false ); |
| 885 | |
| 886 | update(); |
| 887 | } |
| 888 | */ |
| 889 | } |
| 890 | |
| 891 | void KACLListView::slotItemDoubleClicked(QTreeWidgetItem *item, int column) |
| 892 | { |
| 893 | if (!item) { |
| 894 | return; |
| 895 | } |
| 896 | |
| 897 | // avoid conflict with clicking to toggle permission |
| 898 | if (column >= 2 && column <= 4) { |
| 899 | return; |
| 900 | } |
| 901 | |
| 902 | KACLListViewItem *aclListItem = static_cast<KACLListViewItem *>(item); |
| 903 | if (!aclListItem->isAllowedToChangeType()) { |
| 904 | return; |
| 905 | } |
| 906 | |
| 907 | setCurrentItem(item); |
| 908 | slotEditEntry(); |
| 909 | } |
| 910 | |
| 911 | void KACLListView::calculateEffectiveRights() |
| 912 | { |
| 913 | QTreeWidgetItemIterator it(this); |
| 914 | KACLListViewItem *pItem; |
| 915 | while ((pItem = dynamic_cast<KACLListViewItem *>(*it)) != nullptr) { |
| 916 | ++it; |
| 917 | pItem->calcEffectiveRights(); |
| 918 | } |
| 919 | } |
| 920 | |
| 921 | unsigned short KACLListView::maskPermissions() const |
| 922 | { |
| 923 | return m_mask; |
| 924 | } |
| 925 | |
| 926 | void KACLListView::setMaskPermissions(unsigned short maskPerms) |
| 927 | { |
| 928 | m_mask = maskPerms; |
| 929 | calculateEffectiveRights(); |
| 930 | } |
| 931 | |
| 932 | acl_perm_t KACLListView::maskPartialPermissions() const |
| 933 | { |
| 934 | // return m_pMaskEntry->m_partialPerms; |
| 935 | return 0; |
| 936 | } |
| 937 | |
| 938 | void KACLListView::setMaskPartialPermissions(acl_perm_t /*maskPartialPerms*/) |
| 939 | { |
| 940 | // m_pMaskEntry->m_partialPerms = maskPartialPerms; |
| 941 | calculateEffectiveRights(); |
| 942 | } |
| 943 | |
| 944 | bool KACLListView::hasDefaultEntries() const |
| 945 | { |
| 946 | QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); |
| 947 | while (*it) { |
| 948 | const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); |
| 949 | ++it; |
| 950 | if (item->isDefault) { |
| 951 | return true; |
| 952 | } |
| 953 | } |
| 954 | return false; |
| 955 | } |
| 956 | |
| 957 | const KACLListViewItem *KACLListView::findDefaultItemByType(EntryType type) const |
| 958 | { |
| 959 | return findItemByType(type, true); |
| 960 | } |
| 961 | |
| 962 | const KACLListViewItem *KACLListView::findItemByType(EntryType type, bool defaults) const |
| 963 | { |
| 964 | QTreeWidgetItemIterator it(const_cast<KACLListView *>(this)); |
| 965 | while (*it) { |
| 966 | const KACLListViewItem *item = static_cast<const KACLListViewItem *>(*it); |
| 967 | ++it; |
| 968 | if (item->isDefault == defaults && item->type == type) { |
| 969 | return item; |
| 970 | } |
| 971 | } |
| 972 | return nullptr; |
| 973 | } |
| 974 | |
| 975 | unsigned short KACLListView::calculateMaskValue(bool defaults) const |
| 976 | { |
| 977 | // KACL auto-adds the relevant mask entries, so we can simply query |
| 978 | bool dummy; |
| 979 | return itemsToACL(defaults).maskPermissions(dummy); |
| 980 | } |
| 981 | |
| 982 | void KACLListView::slotAddEntry() |
| 983 | { |
| 984 | int allowedTypes = NamedUser | NamedGroup; |
| 985 | if (!m_hasMask) { |
| 986 | allowedTypes |= Mask; |
| 987 | } |
| 988 | int allowedDefaultTypes = NamedUser | NamedGroup; |
| 989 | if (!findDefaultItemByType(Mask)) { |
| 990 | allowedDefaultTypes |= Mask; |
| 991 | } |
| 992 | if (!hasDefaultEntries()) { |
| 993 | allowedDefaultTypes |= User | Group; |
| 994 | } |
| 995 | EditACLEntryDialog dlg(this, |
| 996 | nullptr, |
| 997 | allowedUsers(false), |
| 998 | allowedGroups(false), |
| 999 | allowedUsers(true), |
| 1000 | allowedGroups(true), |
| 1001 | allowedTypes, |
| 1002 | allowedDefaultTypes, |
| 1003 | m_allowDefaults); |
| 1004 | dlg.exec(); |
| 1005 | KACLListViewItem *item = dlg.item(); |
| 1006 | if (!item) { |
| 1007 | return; // canceled |
| 1008 | } |
| 1009 | if (item->type == Mask && !item->isDefault) { |
| 1010 | m_hasMask = true; |
| 1011 | m_mask = item->value; |
| 1012 | } |
| 1013 | if (item->isDefault && !hasDefaultEntries()) { |
| 1014 | // first default entry, fill in what is needed |
| 1015 | if (item->type != User) { |
| 1016 | unsigned short v = findDefaultItemByType(User)->value; |
| 1017 | new KACLListViewItem(this, User, v, true); |
| 1018 | } |
| 1019 | if (item->type != Group) { |
| 1020 | unsigned short v = findDefaultItemByType(Group)->value; |
| 1021 | new KACLListViewItem(this, Group, v, true); |
| 1022 | } |
| 1023 | if (item->type != Others) { |
| 1024 | unsigned short v = findDefaultItemByType(Others)->value; |
| 1025 | new KACLListViewItem(this, Others, v, true); |
| 1026 | } |
| 1027 | } |
| 1028 | const KACLListViewItem *defaultMaskItem = findDefaultItemByType(Mask); |
| 1029 | if (item->isDefault && !defaultMaskItem) { |
| 1030 | unsigned short v = calculateMaskValue(true); |
| 1031 | new KACLListViewItem(this, Mask, v, true); |
| 1032 | } |
| 1033 | if (!item->isDefault && !m_hasMask && (item->type == Group || item->type == NamedUser || item->type == NamedGroup)) { |
| 1034 | // auto-add a mask entry |
| 1035 | unsigned short v = calculateMaskValue(false); |
| 1036 | new KACLListViewItem(this, Mask, v, false); |
| 1037 | m_hasMask = true; |
| 1038 | m_mask = v; |
| 1039 | } |
| 1040 | calculateEffectiveRights(); |
| 1041 | sortItems(sortColumn(), Qt::AscendingOrder); |
| 1042 | setCurrentItem(item); |
| 1043 | // QTreeWidget doesn't seem to emit, in this case, and we need to update |
| 1044 | // the buttons... |
| 1045 | if (topLevelItemCount() == 1) { |
| 1046 | Q_EMIT currentItemChanged(item, item); |
| 1047 | } |
| 1048 | } |
| 1049 | |
| 1050 | void KACLListView::slotEditEntry() |
| 1051 | { |
| 1052 | QTreeWidgetItem *current = currentItem(); |
| 1053 | if (!current) { |
| 1054 | return; |
| 1055 | } |
| 1056 | KACLListViewItem *item = static_cast<KACLListViewItem *>(current); |
| 1057 | int allowedTypes = item->type | NamedUser | NamedGroup; |
| 1058 | bool itemWasMask = item->type == Mask; |
| 1059 | if (!m_hasMask || itemWasMask) { |
| 1060 | allowedTypes |= Mask; |
| 1061 | } |
| 1062 | int allowedDefaultTypes = item->type | NamedUser | NamedGroup; |
| 1063 | if (!findDefaultItemByType(Mask)) { |
| 1064 | allowedDefaultTypes |= Mask; |
| 1065 | } |
| 1066 | if (!hasDefaultEntries()) { |
| 1067 | allowedDefaultTypes |= User | Group; |
| 1068 | } |
| 1069 | |
| 1070 | EditACLEntryDialog dlg(this, |
| 1071 | item, |
| 1072 | allowedUsers(false, item), |
| 1073 | allowedGroups(false, item), |
| 1074 | allowedUsers(true, item), |
| 1075 | allowedGroups(true, item), |
| 1076 | allowedTypes, |
| 1077 | allowedDefaultTypes, |
| 1078 | m_allowDefaults); |
| 1079 | dlg.exec(); |
| 1080 | if (itemWasMask && item->type != Mask) { |
| 1081 | m_hasMask = false; |
| 1082 | m_mask = 0; |
| 1083 | } |
| 1084 | if (!itemWasMask && item->type == Mask) { |
| 1085 | m_mask = item->value; |
| 1086 | m_hasMask = true; |
| 1087 | } |
| 1088 | calculateEffectiveRights(); |
| 1089 | sortItems(sortColumn(), Qt::AscendingOrder); |
| 1090 | } |
| 1091 | |
| 1092 | void KACLListView::slotRemoveEntry() |
| 1093 | { |
| 1094 | QTreeWidgetItemIterator it(this, QTreeWidgetItemIterator::Selected); |
| 1095 | while (*it) { |
| 1096 | KACLListViewItem *item = static_cast<KACLListViewItem *>(*it); |
| 1097 | ++it; |
| 1098 | /* First check if it's a mask entry and if so, make sure that there is |
| 1099 | * either no name user or group entry, which means the mask can be |
| 1100 | * removed, or don't remove it, but reset it. That is allowed. */ |
| 1101 | if (item->type == Mask) { |
| 1102 | bool itemWasDefault = item->isDefault; |
| 1103 | if (!itemWasDefault && maskCanBeDeleted()) { |
| 1104 | m_hasMask = false; |
| 1105 | m_mask = 0; |
| 1106 | delete item; |
| 1107 | } else if (itemWasDefault && defaultMaskCanBeDeleted()) { |
| 1108 | delete item; |
| 1109 | } else { |
| 1110 | item->value = 0; |
| 1111 | item->repaint(); |
| 1112 | } |
| 1113 | if (!itemWasDefault) { |
| 1114 | calculateEffectiveRights(); |
| 1115 | } |
| 1116 | } else { |
| 1117 | // for the base permissions, disable them, which is what libacl does |
| 1118 | if (!item->isDefault && (item->type == User || item->type == Group || item->type == Others)) { |
| 1119 | item->value = 0; |
| 1120 | item->repaint(); |
| 1121 | } else { |
| 1122 | delete item; |
| 1123 | } |
| 1124 | } |
| 1125 | } |
| 1126 | } |
| 1127 | |
| 1128 | bool KACLListView::maskCanBeDeleted() const |
| 1129 | { |
| 1130 | return !findItemByType(NamedUser) && !findItemByType(NamedGroup); |
| 1131 | } |
| 1132 | |
| 1133 | bool KACLListView::defaultMaskCanBeDeleted() const |
| 1134 | { |
| 1135 | return !findDefaultItemByType(NamedUser) && !findDefaultItemByType(NamedGroup); |
| 1136 | } |
| 1137 | |
| 1138 | #include "moc_kacleditwidget.cpp" |
| 1139 | #include "moc_kacleditwidget_p.cpp" |
| 1140 | #endif |
| 1141 | |