1 /*
2     SPDX-FileCopyrightText: 2002 Jean-Baptiste Mardelle <bj@altern.org>
3     SPDX-FileCopyrightText: 2007, 2008, 2009, 2010, 2011, 2012 Rolf Eike Beer <kde@opensource.sf-tec.de>
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "selectpublickeydialog.h"
8 
9 #include "kgpgsettings.h"
10 #include "core/images.h"
11 #include "core/KGpgRootNode.h"
12 #include "model/kgpgitemmodel.h"
13 #include "model/selectkeyproxymodel.h"
14 
15 #include <QAction>
16 #include <QCheckBox>
17 #include <QDialogButtonBox>
18 #include <QHBoxLayout>
19 #include <QLabel>
20 #include <QLineEdit>
21 #include <QPushButton>
22 #include <QTableView>
23 
24 #include <KActionCollection>
25 #include <KConfigGroup>
26 #include <KLocalizedString>
27 
28 using namespace KgpgCore;
29 
KgpgSelectPublicKeyDlg(QWidget * parent,KGpgItemModel * model,const QKeySequence & goDefaultKey,const bool hideasciioption,const QList<QUrl> & files)30 KgpgSelectPublicKeyDlg::KgpgSelectPublicKeyDlg(QWidget *parent, KGpgItemModel *model, const QKeySequence &goDefaultKey, const bool hideasciioption, const QList<QUrl> &files)
31 	: QDialog(parent),
32 	m_customoptions(nullptr),
33 	imodel(model),
34 	m_files(files),
35 	m_hideasciioption(hideasciioption)
36 {
37     QWidget *mainWidget = new QWidget(this);
38     QVBoxLayout *mainLayout = new QVBoxLayout(this);
39     setLayout(mainLayout);
40     mainLayout->setSizeConstraint(QLayout::SetMinimumSize);
41     mainLayout->addWidget(mainWidget);
42 
43     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
44     m_okButton = buttonBox->button(QDialogButtonBox::Ok);
45     m_okButton->setDefault(true);
46     m_okButton->setShortcut(Qt::CTRL | Qt::Key_Return);
47     m_detailsButton = new QPushButton(this);
48     m_detailsButton->setText(i18n("&Options") + QStringLiteral(" >>"));
49     m_detailsButton->setIcon(QIcon::fromTheme(QStringLiteral("help-about")));
50     connect(m_detailsButton, &QPushButton::clicked, this, &KgpgSelectPublicKeyDlg::toggleDetails);
51     buttonBox->addButton(m_detailsButton, QDialogButtonBox::ActionRole);
52     connect(buttonBox, &QDialogButtonBox::accepted, this, &KgpgSelectPublicKeyDlg::accept);
53     connect(buttonBox, &QDialogButtonBox::rejected, this, &KgpgSelectPublicKeyDlg::reject);
54     m_okButton->setDefault(true);
55 
56     int fcount = files.count();
57     bool fmode = (fcount > 0);
58 
59     switch (fcount) {
60     case 0:
61 	setWindowTitle(i18n("Select Public Key"));
62 	break;
63     case 1:
64 	setWindowTitle(i18n("Select Public Key for %1", files.first().fileName()));
65 	break;
66     default:
67 	setWindowTitle(i18np("Select Public Key for %2 and one more file", "Select Public Key for %2 and %1 more files", files.count() - 1, files.first().fileName()));
68     }
69 
70     QWidget *page = new QWidget(this);
71 
72     m_searchbar = new QWidget(page);
73     QHBoxLayout *searchbarHBoxLayout = new QHBoxLayout(m_searchbar);
74     searchbarHBoxLayout->setContentsMargins(0, 0, 0, 0);
75     QLabel *searchlabel = new QLabel(i18n("&Search: "), m_searchbar);
76     searchbarHBoxLayout->addWidget(searchlabel);
77 
78     m_searchlineedit = new QLineEdit(m_searchbar);
79     searchbarHBoxLayout->addWidget(m_searchlineedit);
80     m_searchlineedit->setClearButtonEnabled(true);
81     searchlabel->setBuddy(m_searchlineedit);
82 
83     iproxy = new SelectKeyProxyModel(this);
84     iproxy->setKeyModel(imodel);
85 
86     m_keyslist = new QTableView(page);
87     m_keyslist->setSortingEnabled(true);
88     m_keyslist->setSelectionMode(QAbstractItemView::ExtendedSelection);
89     m_keyslist->setSelectionBehavior(QAbstractItemView::SelectRows);
90     m_keyslist->setModel(iproxy);
91     m_keyslist->resizeColumnsToContents();
92     m_keyslist->setWhatsThis(i18n("<b>Public keys list</b>: select the key that will be used for encryption."));
93     connect(m_searchlineedit, &QLineEdit::textChanged, iproxy, &SelectKeyProxyModel::setFilterFixedString);
94 
95     optionsbox = new QWidget();
96     QVBoxLayout *optionsboxVBoxLayout = new QVBoxLayout(optionsbox);
97     optionsboxVBoxLayout->setContentsMargins(0, 0, 0, 0);
98     optionsbox->hide();
99 
100     if (m_hideasciioption)
101         m_cbarmor = nullptr;
102     else
103     {
104         m_cbarmor = new QCheckBox(i18n("ASCII armored encryption"), optionsbox);
105         optionsboxVBoxLayout->addWidget(m_cbarmor);
106         m_cbarmor->setChecked(KGpgSettings::asciiArmor());
107         m_cbarmor->setWhatsThis(i18n("<b>ASCII encryption</b>: makes it possible to open the encrypted file/message in a text editor"));
108     }
109 
110     m_cbuntrusted = new QCheckBox(i18n("Allow encryption with untrusted keys"), optionsbox);
111     optionsboxVBoxLayout->addWidget(m_cbuntrusted);
112     // connect before setting the state so the model gets the right state from the start
113     connect(m_cbuntrusted, &QCheckBox::toggled, this, &KgpgSelectPublicKeyDlg::slotUntrusted);
114     m_cbuntrusted->setChecked(KGpgSettings::allowUntrustedKeys());
115     m_cbuntrusted->setWhatsThis(i18n("<b>Allow encryption with untrusted keys</b>: when you import a public key, it is usually "
116                     "marked as untrusted and you cannot use it unless you sign it in order to make it 'trusted'. Checking this "
117                     "box enables you to use any key, even if it has not be signed."));
118 
119     m_cbhideid = new QCheckBox(i18n("Hide user id"), optionsbox);
120     optionsboxVBoxLayout->addWidget(m_cbhideid);
121     m_cbhideid->setChecked(KGpgSettings::hideUserID());
122     m_cbhideid->setWhatsThis(i18n("<b>Hide user ID</b>: Do not put the keyid into encrypted packets. This option hides the receiver "
123                     "of the message and is a countermeasure against traffic analysis. It may slow down the decryption process because "
124                     "all available secret keys are tried."));
125 
126     m_cbsymmetric = new QCheckBox(i18n("Symmetrical encryption"), optionsbox);
127     optionsboxVBoxLayout->addWidget(m_cbsymmetric);
128     m_cbsymmetric->setWhatsThis(i18n("<b>Symmetrical encryption</b>: encryption does not use keys. You just need to give a password "
129                                      "to encrypt/decrypt the file"));
130 
131     QVBoxLayout *dialoglayout = new QVBoxLayout(page);
132     dialoglayout->setContentsMargins(0, 0, 0, 0);
133     dialoglayout->addWidget(m_searchbar);
134     dialoglayout->addWidget(m_keyslist);
135     page->setLayout(dialoglayout);
136 
137     if (KGpgSettings::allowCustomEncryptionOptions())
138     {
139         QWidget *expertbox = new QWidget(page);
140         QHBoxLayout *expertboxHBoxLayout = new QHBoxLayout(expertbox);
141         expertboxHBoxLayout->setContentsMargins(0, 0, 0, 0);
142         (void) new QLabel(i18n("Custom option:"), expertbox);
143 
144         m_customoptions = new QLineEdit(expertbox);
145         expertboxHBoxLayout->addWidget(m_customoptions);
146         m_customoptions->setText(KGpgSettings::customEncryptionOptions());
147         m_customoptions->setWhatsThis(i18n("<b>Custom option</b>: for experienced users only, allows you to enter a gpg command line option, like: '--armor'"));
148 
149         dialoglayout->addWidget(expertbox);
150     }
151 
152     KActionCollection *actcol = new KActionCollection(this);
153     QAction *action = actcol->addAction(QLatin1String( "go_default_key" ));
154     action->setText(i18n("&Go to Default Key"));
155     actcol->setDefaultShortcut(action, goDefaultKey);
156 
157     connect(action, &QAction::triggered, this, &KgpgSelectPublicKeyDlg::slotGotoDefaultKey);
158     connect(m_cbsymmetric, &QCheckBox::toggled, this, &KgpgSelectPublicKeyDlg::slotSymmetric);
159     connect(m_keyslist->selectionModel(), &QItemSelectionModel::selectionChanged, this, &KgpgSelectPublicKeyDlg::slotSelectionChanged);
160     connect(m_keyslist, &QTableView::doubleClicked, this, &KgpgSelectPublicKeyDlg::slotOk);
161 
162     setMinimumSize(550, 200);
163     updateGeometry();
164 
165     mainLayout->addWidget(page);
166     mainLayout->addWidget(optionsbox);
167     mainLayout->addWidget(buttonBox);
168     slotSelectionChanged();
169 
170     if (fmode)
171         slotGotoDefaultKey();
172 }
173 
selectedKeys() const174 QStringList KgpgSelectPublicKeyDlg::selectedKeys() const
175 {
176 	if (getSymmetric())
177 		return QStringList();
178 
179 	QStringList selectedKeys;
180 
181 	const QModelIndexList indexes = m_keyslist->selectionModel()->selectedIndexes();
182 	for (const QModelIndex &idx : indexes) {
183 		if (idx.column() != 0)
184 			continue;
185 		KGpgNode *nd = iproxy->nodeForIndex(idx);
186 		if (nd->getType() == ITYPE_GROUP)
187 			selectedKeys << nd->getName();
188 		else
189 			selectedKeys << nd->getId();
190 	}
191 
192 	return selectedKeys;
193 }
194 
getSymmetric() const195 bool KgpgSelectPublicKeyDlg::getSymmetric() const
196 {
197     return m_cbsymmetric->isChecked();
198 }
199 
getCustomOptions() const200 QString KgpgSelectPublicKeyDlg::getCustomOptions() const
201 {
202     if (m_customoptions == nullptr)
203         return QString();
204     return m_customoptions->text().simplified();
205 }
206 
getUntrusted() const207 bool KgpgSelectPublicKeyDlg::getUntrusted() const
208 {
209     return m_cbuntrusted->isChecked();
210 }
211 
getArmor() const212 bool KgpgSelectPublicKeyDlg::getArmor() const
213 {
214     return m_hideasciioption || m_cbarmor->isChecked();
215 }
216 
getFiles() const217 const QList<QUrl> &KgpgSelectPublicKeyDlg::getFiles() const
218 {
219 	return m_files;
220 }
221 
getHideId() const222 bool KgpgSelectPublicKeyDlg::getHideId() const
223 {
224     return m_cbhideid->isChecked();
225 }
226 
slotOk()227 void KgpgSelectPublicKeyDlg::slotOk()
228 {
229     if (getSymmetric() || m_keyslist->selectionModel()->hasSelection())
230         accept();
231 }
232 
slotSelectionChanged()233 void KgpgSelectPublicKeyDlg::slotSelectionChanged()
234 {
235     m_okButton->setEnabled(getSymmetric() || m_keyslist->selectionModel()->hasSelection());
236 }
237 
slotSymmetric(const bool state)238 void KgpgSelectPublicKeyDlg::slotSymmetric(const bool state)
239 {
240     m_keyslist->setDisabled(state);
241     m_cbuntrusted->setDisabled(state);
242     m_cbhideid->setDisabled(state);
243     m_searchbar->setDisabled(state);
244     slotSelectionChanged();
245 }
246 
slotUntrusted(const bool state)247 void KgpgSelectPublicKeyDlg::slotUntrusted(const bool state)
248 {
249 	iproxy->setShowUntrusted(state);
250 }
251 
slotGotoDefaultKey()252 void KgpgSelectPublicKeyDlg::slotGotoDefaultKey()
253 {
254 	KGpgNode *nd = imodel->getRootNode()->findKey(KGpgSettings::defaultKey());
255 	if (nd == nullptr)
256 		return;
257 	QModelIndex sidx = imodel->nodeIndex(nd);
258 	QModelIndex pidx = iproxy->mapFromSource(sidx);
259 	m_keyslist->selectionModel()->setCurrentIndex(pidx, QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent | QItemSelectionModel::Rows);
260 }
261 
toggleDetails()262 void KgpgSelectPublicKeyDlg::toggleDetails()
263 {
264     const bool isVisible = optionsbox->isVisible();
265     optionsbox->setVisible(!isVisible);
266     m_detailsButton->setText(i18n("&Options") + (isVisible ? QStringLiteral(" >>") : QStringLiteral(" <<")));
267 }
268