KIO

kacleditwidget.cpp
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
36extern "C" {
37#include <grp.h>
38#include <pwd.h>
39}
40#include <assert.h>
41
42class KACLEditWidget::KACLEditWidgetPrivate
43{
44public:
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
58KACLEditWidget::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);
84 d->slotUpdateButtons();
85}
86
87KACLEditWidget::~KACLEditWidget() = default;
88
89void KACLEditWidget::KACLEditWidgetPrivate::slotUpdateButtons()
90{
91 bool atLeastOneIsNotDeletable = false;
92 bool atLeastOneIsNotAllowedToChangeType = false;
93 int selectedCount = 0;
94 QList<QTreeWidgetItem *> selected = m_listView->selectedItems();
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
110KACL KACLEditWidget::getACL() const
111{
112 return d->m_listView->getACL();
113}
114
115KACL KACLEditWidget::getDefaultACL() const
116{
117 return d->m_listView->getDefaultACL();
118}
119
120void KACLEditWidget::setACL(const KACL &acl)
121{
122 d->m_listView->setACL(acl);
123}
124
125void KACLEditWidget::setDefaultACL(const KACL &acl)
126{
127 d->m_listView->setDefaultACL(acl);
128}
129
130void KACLEditWidget::setAllowDefaults(bool value)
131{
132 d->m_listView->setAllowDefaults(value);
133}
134
135KACLListViewItem::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
147KACLListViewItem::~KACLListViewItem()
148{
149}
150
151QString 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
185bool KACLListViewItem::operator<(const QTreeWidgetItem &other) const
186{
187 return key() < static_cast<const KACLListViewItem &>(other).key();
188}
189
190void 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
219void 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
261void 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
307bool 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
326bool 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
336void 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
362EditACLEntryDialog::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);
490 connect(buttonBox, &QDialogButtonBox::accepted, this, &EditACLEntryDialog::slotOk);
492 mainLayout->addWidget(buttonBox);
493
494 adjustSize();
495}
496
497void 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
512void 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}
538void 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
564}
565
566void 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
588KACLListView::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
615KACLListView::~KACLListView()
616{
617}
618
619QSize 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
626QStringList 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;
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
654QStringList 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;
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
682void KACLListView::fillItemsFromACL(const KACL &pACL, bool defaults)
683{
684 // clear out old entries of that ilk
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
722void 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
735void 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
745KACL KACLListView::itemsToACL(bool defaults) const
746{
747 KACL newACL(0);
748 bool atLeastOneEntry = false;
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
792KACL KACLListView::getACL()
793{
794 return itemsToACL(false);
795}
796
797KACL KACLListView::getDefaultACL()
798{
799 return itemsToACL(true);
800}
801
802void 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
841void KACLListView::slotItemClicked(QTreeWidgetItem *pItem, int col)
842{
843 if (!pItem) {
844 return;
845 }
846
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
891void 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
911void KACLListView::calculateEffectiveRights()
912{
914 KACLListViewItem *pItem;
915 while ((pItem = dynamic_cast<KACLListViewItem *>(*it)) != nullptr) {
916 ++it;
917 pItem->calcEffectiveRights();
918 }
919}
920
921unsigned short KACLListView::maskPermissions() const
922{
923 return m_mask;
924}
925
926void KACLListView::setMaskPermissions(unsigned short maskPerms)
927{
928 m_mask = maskPerms;
929 calculateEffectiveRights();
930}
931
932acl_perm_t KACLListView::maskPartialPermissions() const
933{
934 // return m_pMaskEntry->m_partialPerms;
935 return 0;
936}
937
938void KACLListView::setMaskPartialPermissions(acl_perm_t /*maskPartialPerms*/)
939{
940 // m_pMaskEntry->m_partialPerms = maskPartialPerms;
941 calculateEffectiveRights();
942}
943
944bool 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
957const KACLListViewItem *KACLListView::findDefaultItemByType(EntryType type) const
958{
959 return findItemByType(type, true);
960}
961
962const 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
975unsigned 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
982void 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
1050void 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
1092void KACLListView::slotRemoveEntry()
1093{
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
1128bool KACLListView::maskCanBeDeleted() const
1129{
1130 return !findItemByType(NamedUser) && !findItemByType(NamedGroup);
1131}
1132
1133bool 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
The KACL class encapsulates a POSIX Access Control List.
Definition kacl.h:38
unsigned short maskPermissions(bool &exists) const
Return the entry for the permissions mask if there is one and sets exists to true.
Definition kacl.cpp:300
bool setACL(const QString &aclStr)
Sets the whole list from a string.
Definition kacl.cpp:608
bool isValid() const
Returns whether the KACL object represents a valid acl.
Definition kacl.cpp:116
ACLGroupPermissionsList allGroupPermissions() const
Returns the list of all group permission entries.
Definition kacl.cpp:571
unsigned short othersPermissions() const
Definition kacl.cpp:246
ACLUserPermissionsList allUserPermissions() const
Returns the list of all group permission entries.
Definition kacl.cpp:439
unsigned short ownerPermissions() const
The standard (non-extended) part of an ACL.
Definition kacl.cpp:208
unsigned short owningGroupPermissions() const
Definition kacl.cpp:227
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Type type(const QSqlDatabase &db)
KGuiItem defaults()
void clicked(bool checked)
void toggled(bool checked)
virtual void addItem(QLayoutItem *item) override
void addLayout(QLayout *layout, int stretch)
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void buttonClicked(QAbstractButton *button)
virtual void accept()
virtual void reject()
void setStandardButtons(StandardButtons buttons)
void setFlat(bool flat)
QIcon fromTheme(const QString &name)
void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected)
void setBuddy(QWidget *buddy)
void setContentsMargins(const QMargins &margins)
void append(QList< T > &&value)
iterator begin()
iterator end()
qsizetype removeAll(const AT &t)
void setObjectName(QAnyStringView name)
int width() const const
void clear()
QString fromLatin1(QByteArrayView str)
AscendingOrder
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void itemClicked(QTreeWidgetItem *item, int column)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 16:58:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.