File indexing completed on 2024-05-12 05:46:45

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