File indexing completed on 2024-04-21 03:55:44

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