1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 by the Quassel Project * |
3 | * devel@quassel-irc.org * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) version 3. * |
9 | * * |
10 | * This program is distributed in the hope that it will be useful, * |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
13 | * GNU General Public License for more details. * |
14 | * * |
15 | * You should have received a copy of the GNU General Public License * |
16 | * along with this program; if not, write to the * |
17 | * Free Software Foundation, Inc., * |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #include "identityeditwidget.h" |
22 | |
23 | #include <QDragEnterEvent> |
24 | #include <QDropEvent> |
25 | #include <QFileDialog> |
26 | #include <QMimeData> |
27 | #include <QUrl> |
28 | #include <QMessageBox> |
29 | |
30 | #if QT_VERSION < 0x050000 |
31 | # include <QDesktopServices> |
32 | #else |
33 | # include <QStandardPaths> |
34 | #endif |
35 | |
36 | #include "client.h" |
37 | #include "iconloader.h" |
38 | |
39 | IdentityEditWidget::IdentityEditWidget(QWidget *parent) |
40 | : QWidget(parent) |
41 | { |
42 | ui.setupUi(this); |
43 | |
44 | ui.addNick->setIcon(SmallIcon("list-add" )); |
45 | ui.deleteNick->setIcon(SmallIcon("edit-delete" )); |
46 | ui.renameNick->setIcon(SmallIcon("edit-rename" )); |
47 | ui.nickUp->setIcon(SmallIcon("go-up" )); |
48 | ui.nickDown->setIcon(SmallIcon("go-down" )); |
49 | |
50 | // We need to know whenever the state of input widgets changes... |
51 | connect(ui.realName, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
52 | connect(ui.nicknameList, SIGNAL(itemChanged(QListWidgetItem *)), this, SIGNAL(widgetHasChanged())); |
53 | connect(ui.awayNick, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
54 | connect(ui.awayReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
55 | connect(ui.autoAwayEnabled, SIGNAL(clicked(bool)), this, SIGNAL(widgetHasChanged())); |
56 | connect(ui.autoAwayTime, SIGNAL(valueChanged(int)), this, SIGNAL(widgetHasChanged())); |
57 | connect(ui.autoAwayReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
58 | connect(ui.autoAwayReasonEnabled, SIGNAL(clicked(bool)), this, SIGNAL(widgetHasChanged())); |
59 | connect(ui.detachAwayEnabled, SIGNAL(clicked(bool)), this, SIGNAL(widgetHasChanged())); |
60 | connect(ui.detachAwayReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
61 | connect(ui.ident, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
62 | connect(ui.kickReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
63 | connect(ui.partReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
64 | connect(ui.quitReason, SIGNAL(textEdited(const QString &)), this, SIGNAL(widgetHasChanged())); |
65 | |
66 | setWidgetStates(); |
67 | connect(ui.nicknameList, SIGNAL(itemSelectionChanged()), this, SLOT(setWidgetStates())); |
68 | |
69 | connect(ui.continueUnsecured, SIGNAL(clicked()), this, SIGNAL(requestEditSsl())); |
70 | |
71 | // we would need this if we enabled drag and drop in the nicklist... |
72 | //connect(ui.nicknameList, SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(setWidgetStates())); |
73 | //connect(ui.nicknameList->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(nicklistHasChanged())); |
74 | |
75 | // disabling unused stuff |
76 | ui.autoAwayEnabled->hide(); |
77 | ui.awayNick->hide(); |
78 | ui.awayNickLabel->hide(); |
79 | |
80 | ui.detachAwayEnabled->setVisible(!Client::internalCore()); |
81 | |
82 | #ifdef HAVE_SSL |
83 | ui.sslKeyGroupBox->setAcceptDrops(true); |
84 | ui.sslKeyGroupBox->installEventFilter(this); |
85 | ui.sslCertGroupBox->setAcceptDrops(true); |
86 | ui.sslCertGroupBox->installEventFilter(this); |
87 | #endif |
88 | } |
89 | |
90 | |
91 | void IdentityEditWidget::setWidgetStates() |
92 | { |
93 | if (ui.nicknameList->selectedItems().count()) { |
94 | ui.renameNick->setEnabled(true); |
95 | ui.nickUp->setEnabled(ui.nicknameList->row(ui.nicknameList->selectedItems()[0]) > 0); |
96 | ui.nickDown->setEnabled(ui.nicknameList->row(ui.nicknameList->selectedItems()[0]) < ui.nicknameList->count()-1); |
97 | } |
98 | else { |
99 | ui.renameNick->setDisabled(true); |
100 | ui.nickUp->setDisabled(true); |
101 | ui.nickDown->setDisabled(true); |
102 | } |
103 | ui.deleteNick->setEnabled(ui.nicknameList->count() > 1); |
104 | } |
105 | |
106 | |
107 | void IdentityEditWidget::displayIdentity(CertIdentity *id, CertIdentity *saveId) |
108 | { |
109 | if (saveId) { |
110 | saveToIdentity(saveId); |
111 | } |
112 | |
113 | if (!id) |
114 | return; |
115 | |
116 | ui.realName->setText(id->realName()); |
117 | ui.nicknameList->clear(); |
118 | ui.nicknameList->addItems(id->nicks()); |
119 | //for(int i = 0; i < ui.nicknameList->count(); i++) { |
120 | // ui.nicknameList->item(i)->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsEnabled); |
121 | //} |
122 | if (ui.nicknameList->count()) ui.nicknameList->setCurrentRow(0); |
123 | ui.awayNick->setText(id->awayNick()); |
124 | ui.awayReason->setText(id->awayReason()); |
125 | ui.autoAwayEnabled->setChecked(id->autoAwayEnabled()); |
126 | ui.autoAwayTime->setValue(id->autoAwayTime()); |
127 | ui.autoAwayReason->setText(id->autoAwayReason()); |
128 | ui.autoAwayReasonEnabled->setChecked(id->autoAwayReasonEnabled()); |
129 | ui.detachAwayEnabled->setChecked(id->detachAwayEnabled()); |
130 | ui.detachAwayReason->setText(id->detachAwayReason()); |
131 | ui.ident->setText(id->ident()); |
132 | ui.kickReason->setText(id->kickReason()); |
133 | ui.partReason->setText(id->partReason()); |
134 | ui.quitReason->setText(id->quitReason()); |
135 | #ifdef HAVE_SSL |
136 | showKeyState(id->sslKey()); |
137 | showCertState(id->sslCert()); |
138 | #endif |
139 | } |
140 | |
141 | |
142 | void IdentityEditWidget::saveToIdentity(CertIdentity *id) |
143 | { |
144 | QRegExp linebreaks = QRegExp("[\\r\\n]" ); |
145 | id->setRealName(ui.realName->text()); |
146 | QStringList nicks; |
147 | for (int i = 0; i < ui.nicknameList->count(); i++) { |
148 | nicks << ui.nicknameList->item(i)->text(); |
149 | } |
150 | id->setNicks(nicks); |
151 | id->setAwayNick(ui.awayNick->text()); |
152 | id->setAwayNickEnabled(true); |
153 | id->setAwayReason(ui.awayReason->text().remove(linebreaks)); |
154 | id->setAwayReasonEnabled(true); |
155 | id->setAutoAwayEnabled(ui.autoAwayEnabled->isChecked()); |
156 | id->setAutoAwayTime(ui.autoAwayTime->value()); |
157 | id->setAutoAwayReason(ui.autoAwayReason->text().remove(linebreaks)); |
158 | id->setAutoAwayReasonEnabled(ui.autoAwayReasonEnabled->isChecked()); |
159 | id->setDetachAwayEnabled(ui.detachAwayEnabled->isChecked()); |
160 | id->setDetachAwayReason(ui.detachAwayReason->text().remove(linebreaks)); |
161 | id->setDetachAwayReasonEnabled(true); |
162 | id->setIdent(ui.ident->text()); |
163 | id->setKickReason(ui.kickReason->text().remove(linebreaks)); |
164 | id->setPartReason(ui.partReason->text().remove(linebreaks)); |
165 | id->setQuitReason(ui.quitReason->text().remove(linebreaks)); |
166 | #ifdef HAVE_SSL |
167 | id->setSslKey(QSslKey(ui.keyTypeLabel->property("sslKey" ).toByteArray(), (QSsl::KeyAlgorithm)(ui.keyTypeLabel->property("sslKeyType" ).toInt()))); |
168 | id->setSslCert(QSslCertificate(ui.certOrgLabel->property("sslCert" ).toByteArray())); |
169 | #endif |
170 | } |
171 | |
172 | |
173 | void IdentityEditWidget::on_addNick_clicked() |
174 | { |
175 | QStringList existing; |
176 | for (int i = 0; i < ui.nicknameList->count(); i++) existing << ui.nicknameList->item(i)->text(); |
177 | NickEditDlg dlg(QString(), existing, this); |
178 | if (dlg.exec() == QDialog::Accepted) { |
179 | ui.nicknameList->addItem(dlg.nick()); |
180 | ui.nicknameList->setCurrentRow(ui.nicknameList->count()-1); |
181 | setWidgetStates(); |
182 | emit widgetHasChanged(); |
183 | } |
184 | } |
185 | |
186 | |
187 | void IdentityEditWidget::on_deleteNick_clicked() |
188 | { |
189 | // no confirmation, since a nickname is really nothing hard to recreate |
190 | if (ui.nicknameList->selectedItems().count()) { |
191 | delete ui.nicknameList->takeItem(ui.nicknameList->row(ui.nicknameList->selectedItems()[0])); |
192 | ui.nicknameList->setCurrentRow(qMin(ui.nicknameList->currentRow()+1, ui.nicknameList->count()-1)); |
193 | setWidgetStates(); |
194 | emit widgetHasChanged(); |
195 | } |
196 | } |
197 | |
198 | |
199 | void IdentityEditWidget::on_renameNick_clicked() |
200 | { |
201 | if (!ui.nicknameList->selectedItems().count()) return; |
202 | QString old = ui.nicknameList->selectedItems()[0]->text(); |
203 | QStringList existing; |
204 | for (int i = 0; i < ui.nicknameList->count(); i++) existing << ui.nicknameList->item(i)->text(); |
205 | NickEditDlg dlg(old, existing, this); |
206 | if (dlg.exec() == QDialog::Accepted) { |
207 | ui.nicknameList->selectedItems()[0]->setText(dlg.nick()); |
208 | } |
209 | } |
210 | |
211 | |
212 | void IdentityEditWidget::on_nickUp_clicked() |
213 | { |
214 | if (!ui.nicknameList->selectedItems().count()) return; |
215 | int row = ui.nicknameList->row(ui.nicknameList->selectedItems()[0]); |
216 | if (row > 0) { |
217 | ui.nicknameList->insertItem(row-1, ui.nicknameList->takeItem(row)); |
218 | ui.nicknameList->setCurrentRow(row-1); |
219 | setWidgetStates(); |
220 | emit widgetHasChanged(); |
221 | } |
222 | } |
223 | |
224 | |
225 | void IdentityEditWidget::on_nickDown_clicked() |
226 | { |
227 | if (!ui.nicknameList->selectedItems().count()) return; |
228 | int row = ui.nicknameList->row(ui.nicknameList->selectedItems()[0]); |
229 | if (row < ui.nicknameList->count()-1) { |
230 | ui.nicknameList->insertItem(row+1, ui.nicknameList->takeItem(row)); |
231 | ui.nicknameList->setCurrentRow(row+1); |
232 | setWidgetStates(); |
233 | emit widgetHasChanged(); |
234 | } |
235 | } |
236 | |
237 | |
238 | void IdentityEditWidget::showAdvanced(bool advanced) |
239 | { |
240 | int idx = ui.tabWidget->indexOf(ui.advancedTab); |
241 | if (advanced) { |
242 | if (idx != -1) |
243 | return; // already added |
244 | ui.tabWidget->addTab(ui.advancedTab, tr("Advanced" )); |
245 | } |
246 | else { |
247 | if (idx == -1) |
248 | return; // already removed |
249 | ui.tabWidget->removeTab(idx); |
250 | } |
251 | } |
252 | |
253 | |
254 | void IdentityEditWidget::setSslState(SslState state) |
255 | { |
256 | switch (state) { |
257 | case NoSsl: |
258 | ui.keyAndCertSettings->setCurrentIndex(0); |
259 | break; |
260 | case UnsecureSsl: |
261 | ui.keyAndCertSettings->setCurrentIndex(1); |
262 | break; |
263 | case AllowSsl: |
264 | ui.keyAndCertSettings->setCurrentIndex(2); |
265 | break; |
266 | } |
267 | } |
268 | |
269 | |
270 | #ifdef HAVE_SSL |
271 | bool IdentityEditWidget::eventFilter(QObject *watched, QEvent *event) |
272 | { |
273 | bool isCert = (watched == ui.sslCertGroupBox); |
274 | switch (event->type()) { |
275 | case QEvent::DragEnter: |
276 | sslDragEnterEvent(static_cast<QDragEnterEvent *>(event)); |
277 | return true; |
278 | case QEvent::Drop: |
279 | sslDropEvent(static_cast<QDropEvent *>(event), isCert); |
280 | return true; |
281 | default: |
282 | return false; |
283 | } |
284 | } |
285 | |
286 | |
287 | void IdentityEditWidget::sslDragEnterEvent(QDragEnterEvent *event) |
288 | { |
289 | if (event->mimeData()->hasFormat("text/uri-list" ) || event->mimeData()->hasFormat("text/uri" )) { |
290 | event->setDropAction(Qt::CopyAction); |
291 | event->accept(); |
292 | } |
293 | } |
294 | |
295 | |
296 | void IdentityEditWidget::sslDropEvent(QDropEvent *event, bool isCert) |
297 | { |
298 | QByteArray rawUris; |
299 | if (event->mimeData()->hasFormat("text/uri-list" )) |
300 | rawUris = event->mimeData()->data("text/uri-list" ); |
301 | else |
302 | rawUris = event->mimeData()->data("text/uri" ); |
303 | |
304 | QTextStream uriStream(rawUris); |
305 | QString filename = QUrl(uriStream.readLine()).toLocalFile(); |
306 | |
307 | if (isCert) { |
308 | QSslCertificate cert = certByFilename(filename); |
309 | if (!cert.isNull()) |
310 | showCertState(cert); |
311 | } |
312 | else { |
313 | QSslKey key = keyByFilename(filename); |
314 | if (!key.isNull()) |
315 | showKeyState(key); |
316 | } |
317 | event->accept(); |
318 | emit widgetHasChanged(); |
319 | } |
320 | |
321 | |
322 | void IdentityEditWidget::on_clearOrLoadKeyButton_clicked() |
323 | { |
324 | QSslKey key; |
325 | |
326 | if (ui.keyTypeLabel->property("sslKey" ).toByteArray().isEmpty()) |
327 | key = keyByFilename(QFileDialog::getOpenFileName(this, tr("Load a Key" ), |
328 | #if QT_VERSION < 0x050000 |
329 | QDesktopServices::storageLocation(QDesktopServices::HomeLocation))); |
330 | #else |
331 | QStandardPaths::writableLocation(QStandardPaths::HomeLocation))); |
332 | #endif |
333 | |
334 | showKeyState(key); |
335 | emit widgetHasChanged(); |
336 | } |
337 | |
338 | |
339 | QSslKey IdentityEditWidget::keyByFilename(const QString &filename) |
340 | { |
341 | QSslKey key; |
342 | |
343 | QFile keyFile(filename); |
344 | keyFile.open(QIODevice::ReadOnly); |
345 | QByteArray keyRaw = keyFile.read(2 << 20); |
346 | keyFile.close(); |
347 | |
348 | for (int i = 0; i < 2; i++) { |
349 | for (int j = 0; j < 2; j++) { |
350 | key = QSslKey(keyRaw, (QSsl::KeyAlgorithm)j, (QSsl::EncodingFormat)i); |
351 | if (!key.isNull()) |
352 | goto returnKey; |
353 | } |
354 | } |
355 | QMessageBox::information(this, tr("Failed to read key" ), tr("Failed to read the key file. It is either incompatible or invalid. Note that the key file must not have a passphrase." )); |
356 | returnKey: |
357 | return key; |
358 | } |
359 | |
360 | |
361 | void IdentityEditWidget::showKeyState(const QSslKey &key) |
362 | { |
363 | if (key.isNull()) { |
364 | ui.keyTypeLabel->setText(tr("No Key loaded" )); |
365 | ui.clearOrLoadKeyButton->setText(tr("Load" )); |
366 | } |
367 | else { |
368 | switch (key.algorithm()) { |
369 | case QSsl::Rsa: |
370 | ui.keyTypeLabel->setText(tr("RSA" )); |
371 | break; |
372 | case QSsl::Dsa: |
373 | ui.keyTypeLabel->setText(tr("DSA" )); |
374 | break; |
375 | default: |
376 | ui.keyTypeLabel->setText(tr("No Key loaded" )); |
377 | } |
378 | ui.clearOrLoadKeyButton->setText(tr("Clear" )); |
379 | } |
380 | ui.keyTypeLabel->setProperty("sslKey" , key.toPem()); |
381 | ui.keyTypeLabel->setProperty("sslKeyType" , (int)key.algorithm()); |
382 | } |
383 | |
384 | |
385 | void IdentityEditWidget::on_clearOrLoadCertButton_clicked() |
386 | { |
387 | QSslCertificate cert; |
388 | |
389 | if (ui.certOrgLabel->property("sslCert" ).toByteArray().isEmpty()) |
390 | cert = certByFilename(QFileDialog::getOpenFileName(this, tr("Load a Certificate" ), |
391 | #if QT_VERSION < 0x050000 |
392 | QDesktopServices::storageLocation(QDesktopServices::HomeLocation))); |
393 | #else |
394 | QStandardPaths::writableLocation(QStandardPaths::HomeLocation))); |
395 | #endif |
396 | showCertState(cert); |
397 | emit widgetHasChanged(); |
398 | } |
399 | |
400 | |
401 | QSslCertificate IdentityEditWidget::certByFilename(const QString &filename) |
402 | { |
403 | QSslCertificate cert; |
404 | QFile certFile(filename); |
405 | certFile.open(QIODevice::ReadOnly); |
406 | QByteArray certRaw = certFile.read(2 << 20); |
407 | certFile.close(); |
408 | |
409 | for (int i = 0; i < 2; i++) { |
410 | cert = QSslCertificate(certRaw, (QSsl::EncodingFormat)i); |
411 | if (!cert.isNull()) |
412 | break; |
413 | } |
414 | return cert; |
415 | } |
416 | |
417 | |
418 | void IdentityEditWidget::showCertState(const QSslCertificate &cert) |
419 | { |
420 | if (cert.isNull()) { |
421 | ui.certOrgLabel->setText(tr("No Certificate loaded" )); |
422 | ui.certCNameLabel->setText(tr("No Certificate loaded" )); |
423 | ui.clearOrLoadCertButton->setText(tr("Load" )); |
424 | } |
425 | else { |
426 | #if QT_VERSION < 0x050000 |
427 | ui.certOrgLabel->setText(cert.subjectInfo(QSslCertificate::Organization)); |
428 | ui.certCNameLabel->setText(cert.subjectInfo(QSslCertificate::CommonName)); |
429 | #else |
430 | ui.certOrgLabel->setText(cert.subjectInfo(QSslCertificate::Organization).join(", " )); |
431 | ui.certCNameLabel->setText(cert.subjectInfo(QSslCertificate::CommonName).join(", " )); |
432 | #endif |
433 | ui.clearOrLoadCertButton->setText(tr("Clear" )); |
434 | } |
435 | ui.certOrgLabel->setProperty("sslCert" , cert.toPem()); |
436 | } |
437 | |
438 | |
439 | #endif //HAVE_SSL |
440 | |