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