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 "identitiessettingspage.h" |
22 | |
23 | #include <QInputDialog> |
24 | #include <QMessageBox> |
25 | |
26 | #include "client.h" |
27 | #include "iconloader.h" |
28 | #include "signalproxy.h" |
29 | |
30 | IdentitiesSettingsPage::IdentitiesSettingsPage(QWidget *parent) |
31 | : SettingsPage(tr("IRC" ), tr("Identities" ), parent), |
32 | _editSsl(false) |
33 | { |
34 | ui.setupUi(this); |
35 | ui.renameIdentity->setIcon(BarIcon("edit-rename" )); |
36 | ui.addIdentity->setIcon(BarIcon("list-add-user" )); |
37 | ui.deleteIdentity->setIcon(BarIcon("list-remove-user" )); |
38 | |
39 | coreConnectionStateChanged(Client::isConnected()); // need a core connection! |
40 | connect(Client::instance(), SIGNAL(coreConnectionStateChanged(bool)), this, SLOT(coreConnectionStateChanged(bool))); |
41 | |
42 | connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientIdentityCreated(IdentityId))); |
43 | connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientIdentityRemoved(IdentityId))); |
44 | |
45 | connect(ui.identityEditor, SIGNAL(widgetHasChanged()), this, SLOT(widgetHasChanged())); |
46 | #ifdef HAVE_SSL |
47 | connect(ui.identityEditor, SIGNAL(requestEditSsl()), this, SLOT(continueUnsecured())); |
48 | #endif |
49 | |
50 | currentId = 0; |
51 | |
52 | //connect(ui.identityList, SIGNAL(editTextChanged(const QString &)), this, SLOT(widgetHasChanged())); |
53 | } |
54 | |
55 | |
56 | void IdentitiesSettingsPage::coreConnectionStateChanged(bool connected) |
57 | { |
58 | setEnabled(connected); |
59 | if (connected) { |
60 | #ifdef HAVE_SSL |
61 | if (Client::signalProxy()->isSecure()) { |
62 | ui.identityEditor->setSslState(IdentityEditWidget::AllowSsl); |
63 | _editSsl = true; |
64 | } |
65 | else { |
66 | ui.identityEditor->setSslState(IdentityEditWidget::UnsecureSsl); |
67 | _editSsl = false; |
68 | } |
69 | #else |
70 | ui.identityEditor->setSslState(IdentityEditWidget::NoSsl); |
71 | #endif |
72 | load(); |
73 | } |
74 | else { |
75 | // reset |
76 | currentId = 0; |
77 | } |
78 | } |
79 | |
80 | |
81 | #ifdef HAVE_SSL |
82 | void IdentitiesSettingsPage::continueUnsecured() |
83 | { |
84 | _editSsl = true; |
85 | |
86 | QHash<IdentityId, CertIdentity *>::iterator idIter; |
87 | for (idIter = identities.begin(); idIter != identities.end(); idIter++) { |
88 | idIter.value()->enableEditSsl(); |
89 | } |
90 | |
91 | ui.identityEditor->setSslState(IdentityEditWidget::AllowSsl); |
92 | } |
93 | |
94 | |
95 | #endif |
96 | |
97 | void IdentitiesSettingsPage::save() |
98 | { |
99 | setEnabled(false); |
100 | QList<CertIdentity *> toCreate, toUpdate; |
101 | // we need to remove our temporarily created identities. |
102 | // these are going to be re-added after the core has propagated them back... |
103 | QHash<IdentityId, CertIdentity *>::iterator i = identities.begin(); |
104 | while (i != identities.end()) { |
105 | if ((*i)->id() < 0) { |
106 | CertIdentity *temp = *i; |
107 | i = identities.erase(i); |
108 | toCreate.append(temp); |
109 | ui.identityList->removeItem(ui.identityList->findData(temp->id().toInt())); |
110 | } |
111 | else { |
112 | if (**i != *Client::identity((*i)->id()) || (*i)->isDirty()) { |
113 | toUpdate.append(*i); |
114 | } |
115 | ++i; |
116 | } |
117 | } |
118 | SaveIdentitiesDlg dlg(toCreate, toUpdate, deletedIdentities, this); |
119 | int ret = dlg.exec(); |
120 | if (ret == QDialog::Rejected) { |
121 | // canceled -> reload everything to be safe |
122 | load(); |
123 | } |
124 | foreach(Identity *id, toCreate) { |
125 | id->deleteLater(); |
126 | } |
127 | changedIdentities.clear(); |
128 | deletedIdentities.clear(); |
129 | setChangedState(false); |
130 | setEnabled(true); |
131 | } |
132 | |
133 | |
134 | void IdentitiesSettingsPage::load() |
135 | { |
136 | currentId = 0; |
137 | foreach(Identity *identity, identities.values()) { |
138 | identity->deleteLater(); |
139 | } |
140 | identities.clear(); |
141 | deletedIdentities.clear(); |
142 | changedIdentities.clear(); |
143 | ui.identityList->clear(); |
144 | setWidgetStates(); |
145 | foreach(IdentityId id, Client::identityIds()) { |
146 | clientIdentityCreated(id); |
147 | } |
148 | setChangedState(false); |
149 | } |
150 | |
151 | |
152 | void IdentitiesSettingsPage::widgetHasChanged() |
153 | { |
154 | bool changed = testHasChanged(); |
155 | if (changed != hasChanged()) setChangedState(changed); |
156 | } |
157 | |
158 | |
159 | void IdentitiesSettingsPage::setWidgetStates() |
160 | { |
161 | bool enabled = (ui.identityList->count() > 0); |
162 | ui.identityEditor->setEnabled(enabled); |
163 | ui.renameIdentity->setEnabled(enabled); |
164 | ui.deleteIdentity->setEnabled(ui.identityList->count() > 1); |
165 | } |
166 | |
167 | |
168 | bool IdentitiesSettingsPage::testHasChanged() |
169 | { |
170 | if (deletedIdentities.count()) return true; |
171 | if (currentId < 0) { |
172 | return true; // new identity |
173 | } |
174 | else { |
175 | if (currentId != 0) { |
176 | changedIdentities.removeAll(currentId); |
177 | CertIdentity temp(currentId, this); |
178 | #ifdef HAVE_SSL |
179 | // we need to set the cert and key manually, as they aren't synced |
180 | CertIdentity *old = identities[currentId]; |
181 | temp.setSslKey(old->sslKey()); |
182 | temp.setSslCert(old->sslCert()); |
183 | #endif |
184 | ui.identityEditor->saveToIdentity(&temp); |
185 | temp.setIdentityName(identities[currentId]->identityName()); |
186 | if (temp != *Client::identity(currentId) || temp.isDirty()) |
187 | changedIdentities.append(currentId); |
188 | } |
189 | return changedIdentities.count(); |
190 | } |
191 | } |
192 | |
193 | |
194 | bool IdentitiesSettingsPage::aboutToSave() |
195 | { |
196 | ui.identityEditor->saveToIdentity(identities[currentId]); |
197 | QList<int> errors; |
198 | foreach(Identity *id, identities.values()) { |
199 | if (id->identityName().isEmpty()) errors.append(1); |
200 | if (!id->nicks().count()) errors.append(2); |
201 | if (id->realName().isEmpty()) errors.append(3); |
202 | if (id->ident().isEmpty()) errors.append(4); |
203 | } |
204 | if (!errors.count()) return true; |
205 | QString error(tr("<b>The following problems need to be corrected before your changes can be applied:</b><ul>" )); |
206 | if (errors.contains(1)) error += tr("<li>All identities need an identity name set</li>" ); |
207 | if (errors.contains(2)) error += tr("<li>Every identity needs at least one nickname defined</li>" ); |
208 | if (errors.contains(3)) error += tr("<li>You need to specify a real name for every identity</li>" ); |
209 | if (errors.contains(4)) error += tr("<li>You need to specify an ident for every identity</li>" ); |
210 | error += tr("</ul>" ); |
211 | QMessageBox::warning(this, tr("One or more identities are invalid" ), error); |
212 | return false; |
213 | } |
214 | |
215 | |
216 | void IdentitiesSettingsPage::clientIdentityCreated(IdentityId id) |
217 | { |
218 | CertIdentity *identity = new CertIdentity(*Client::identity(id), this); |
219 | #ifdef HAVE_SSL |
220 | identity->enableEditSsl(_editSsl); |
221 | #endif |
222 | insertIdentity(identity); |
223 | #ifdef HAVE_SSL |
224 | connect(identity, SIGNAL(sslSettingsUpdated()), this, SLOT(clientIdentityUpdated())); |
225 | #endif |
226 | connect(Client::identity(id), SIGNAL(updatedRemotely()), this, SLOT(clientIdentityUpdated())); |
227 | } |
228 | |
229 | |
230 | void IdentitiesSettingsPage::clientIdentityUpdated() |
231 | { |
232 | const Identity *clientIdentity = qobject_cast<Identity *>(sender()); |
233 | if (!clientIdentity) { |
234 | qWarning() << "Invalid identity to update!" ; |
235 | return; |
236 | } |
237 | if (!identities.contains(clientIdentity->id())) { |
238 | qWarning() << "Unknown identity to update:" << clientIdentity->identityName(); |
239 | return; |
240 | } |
241 | |
242 | CertIdentity *identity = identities[clientIdentity->id()]; |
243 | |
244 | if (identity->identityName() != clientIdentity->identityName()) |
245 | renameIdentity(identity->id(), clientIdentity->identityName()); |
246 | |
247 | identity->copyFrom(*clientIdentity); |
248 | |
249 | if (identity->id() == currentId) |
250 | ui.identityEditor->displayIdentity(identity); |
251 | } |
252 | |
253 | |
254 | void IdentitiesSettingsPage::clientIdentityRemoved(IdentityId id) |
255 | { |
256 | if (identities.contains(id)) { |
257 | removeIdentity(identities[id]); |
258 | changedIdentities.removeAll(id); |
259 | deletedIdentities.removeAll(id); |
260 | } |
261 | } |
262 | |
263 | |
264 | void IdentitiesSettingsPage::insertIdentity(CertIdentity *identity) |
265 | { |
266 | IdentityId id = identity->id(); |
267 | identities[id] = identity; |
268 | |
269 | QString name = identity->identityName(); |
270 | for (int j = 0; j < ui.identityList->count(); j++) { |
271 | if ((j > 0 || ui.identityList->itemData(0).toInt() != 1) && name.localeAwareCompare(ui.identityList->itemText(j)) < 0) { |
272 | ui.identityList->insertItem(j, name, id.toInt()); |
273 | widgetHasChanged(); |
274 | return; |
275 | } |
276 | } |
277 | // append |
278 | ui.identityList->insertItem(ui.identityList->count(), name, id.toInt()); |
279 | setWidgetStates(); |
280 | widgetHasChanged(); |
281 | } |
282 | |
283 | |
284 | void IdentitiesSettingsPage::renameIdentity(IdentityId id, const QString &newName) |
285 | { |
286 | Identity *identity = identities[id]; |
287 | ui.identityList->setItemText(ui.identityList->findData(identity->id().toInt()), newName); |
288 | identity->setIdentityName(newName); |
289 | } |
290 | |
291 | |
292 | void IdentitiesSettingsPage::removeIdentity(Identity *id) |
293 | { |
294 | identities.remove(id->id()); |
295 | ui.identityList->removeItem(ui.identityList->findData(id->id().toInt())); |
296 | changedIdentities.removeAll(id->id()); |
297 | if (currentId == id->id()) currentId = 0; |
298 | id->deleteLater(); |
299 | setWidgetStates(); |
300 | widgetHasChanged(); |
301 | } |
302 | |
303 | |
304 | void IdentitiesSettingsPage::on_identityList_currentIndexChanged(int index) |
305 | { |
306 | CertIdentity *previousIdentity = 0; |
307 | if (currentId != 0 && identities.contains(currentId)) |
308 | previousIdentity = identities[currentId]; |
309 | |
310 | if (index < 0) { |
311 | //ui.identityList->setEditable(false); |
312 | ui.identityEditor->displayIdentity(0, previousIdentity); |
313 | currentId = 0; |
314 | } |
315 | else { |
316 | IdentityId id = ui.identityList->itemData(index).toInt(); |
317 | if (identities.contains(id)) { |
318 | ui.identityEditor->displayIdentity(identities[id], previousIdentity); |
319 | currentId = id; |
320 | } |
321 | } |
322 | } |
323 | |
324 | |
325 | void IdentitiesSettingsPage::on_addIdentity_clicked() |
326 | { |
327 | CreateIdentityDlg dlg(ui.identityList->model(), this); |
328 | if (dlg.exec() == QDialog::Accepted) { |
329 | // find a free (negative) ID |
330 | IdentityId id; |
331 | for (id = 1; id <= identities.count(); id++) { |
332 | if (!identities.keys().contains(-id.toInt())) break; |
333 | } |
334 | id = -id.toInt(); |
335 | CertIdentity *newId = new CertIdentity(id, this); |
336 | #ifdef HAVE_SSL |
337 | newId->enableEditSsl(_editSsl); |
338 | #endif |
339 | if (dlg.duplicateId() != 0) { |
340 | // duplicate |
341 | newId->copyFrom(*identities[dlg.duplicateId()]); |
342 | newId->setId(id); |
343 | } |
344 | newId->setIdentityName(dlg.identityName()); |
345 | identities[id] = newId; |
346 | insertIdentity(newId); |
347 | ui.identityList->setCurrentIndex(ui.identityList->findData(id.toInt())); |
348 | widgetHasChanged(); |
349 | } |
350 | } |
351 | |
352 | |
353 | void IdentitiesSettingsPage::on_deleteIdentity_clicked() |
354 | { |
355 | Identity *id = identities[currentId]; |
356 | int ret = QMessageBox::question(this, tr("Delete Identity?" ), |
357 | tr("Do you really want to delete identity \"%1\"?" ).arg(id->identityName()), |
358 | QMessageBox::Yes|QMessageBox::No, QMessageBox::No); |
359 | if (ret != QMessageBox::Yes) return; |
360 | if (id->id() > 0) deletedIdentities.append(id->id()); |
361 | currentId = 0; |
362 | removeIdentity(id); |
363 | } |
364 | |
365 | |
366 | void IdentitiesSettingsPage::on_renameIdentity_clicked() |
367 | { |
368 | QString oldName = identities[currentId]->identityName(); |
369 | bool ok = false; |
370 | QString name = QInputDialog::getText(this, tr("Rename Identity" ), |
371 | tr("Please enter a new name for the identity \"%1\"!" ).arg(oldName), |
372 | QLineEdit::Normal, oldName, &ok); |
373 | if (ok && !name.isEmpty()) { |
374 | renameIdentity(currentId, name); |
375 | widgetHasChanged(); |
376 | } |
377 | } |
378 | |
379 | |
380 | /*****************************************************************************************/ |
381 | |
382 | CreateIdentityDlg::CreateIdentityDlg(QAbstractItemModel *model, QWidget *parent) |
383 | : QDialog(parent) |
384 | { |
385 | ui.setupUi(this); |
386 | |
387 | ui.identityList->setModel(model); // now we use the identity list of the main page... Trolltech <3 |
388 | on_identityName_textChanged("" ); // disable ok button :) |
389 | } |
390 | |
391 | |
392 | QString CreateIdentityDlg::identityName() const |
393 | { |
394 | return ui.identityName->text(); |
395 | } |
396 | |
397 | |
398 | IdentityId CreateIdentityDlg::duplicateId() const |
399 | { |
400 | if (!ui.duplicateIdentity->isChecked()) return 0; |
401 | if (ui.identityList->currentIndex() >= 0) { |
402 | return ui.identityList->itemData(ui.identityList->currentIndex()).toInt(); |
403 | } |
404 | return 0; |
405 | } |
406 | |
407 | |
408 | void CreateIdentityDlg::on_identityName_textChanged(const QString &text) |
409 | { |
410 | ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(text.count()); |
411 | } |
412 | |
413 | |
414 | /*********************************************************************************************/ |
415 | |
416 | SaveIdentitiesDlg::SaveIdentitiesDlg(const QList<CertIdentity *> &toCreate, const QList<CertIdentity *> &toUpdate, const QList<IdentityId> &toRemove, QWidget *parent) |
417 | : QDialog(parent) |
418 | { |
419 | ui.setupUi(this); |
420 | ui.abort->setIcon(SmallIcon("dialog-cancel" )); |
421 | |
422 | numevents = toCreate.count() + toUpdate.count() + toRemove.count(); |
423 | rcvevents = 0; |
424 | if (numevents) { |
425 | ui.progressBar->setMaximum(numevents); |
426 | ui.progressBar->setValue(0); |
427 | |
428 | connect(Client::instance(), SIGNAL(identityCreated(IdentityId)), this, SLOT(clientEvent())); |
429 | connect(Client::instance(), SIGNAL(identityRemoved(IdentityId)), this, SLOT(clientEvent())); |
430 | |
431 | foreach(CertIdentity *id, toCreate) { |
432 | Client::createIdentity(*id); |
433 | } |
434 | foreach(CertIdentity *id, toUpdate) { |
435 | const Identity *cid = Client::identity(id->id()); |
436 | if (!cid) { |
437 | qWarning() << "Invalid client identity!" ; |
438 | numevents--; |
439 | continue; |
440 | } |
441 | connect(cid, SIGNAL(updatedRemotely()), this, SLOT(clientEvent())); |
442 | Client::updateIdentity(id->id(), id->toVariantMap()); |
443 | #ifdef HAVE_SSL |
444 | id->requestUpdateSslSettings(); |
445 | #endif |
446 | } |
447 | foreach(IdentityId id, toRemove) { |
448 | Client::removeIdentity(id); |
449 | } |
450 | } |
451 | else { |
452 | qWarning() << "Sync dialog called without stuff to change!" ; |
453 | accept(); |
454 | } |
455 | } |
456 | |
457 | |
458 | void SaveIdentitiesDlg::clientEvent() |
459 | { |
460 | ui.progressBar->setValue(++rcvevents); |
461 | if (rcvevents >= numevents) accept(); |
462 | } |
463 | |
464 | |
465 | /*************************************************************************************************/ |
466 | |
467 | NickEditDlg::NickEditDlg(const QString &old, const QStringList &exist, QWidget *parent) |
468 | : QDialog(parent), oldNick(old), existing(exist) |
469 | { |
470 | ui.setupUi(this); |
471 | |
472 | // define a regexp for valid nicknames |
473 | // TODO: add max nicklength according to ISUPPORT |
474 | QString letter = "A-Za-z" ; |
475 | QString special = "\x5b-\x60\x7b-\x7d" ; |
476 | QRegExp rx(QString("[%1%2][%1%2\\d-]*" ).arg(letter, special)); |
477 | ui.nickEdit->setValidator(new QRegExpValidator(rx, ui.nickEdit)); |
478 | if (old.isEmpty()) { |
479 | // new nick |
480 | setWindowTitle(tr("Add Nickname" )); |
481 | on_nickEdit_textChanged("" ); // disable ok button |
482 | } |
483 | else ui.nickEdit->setText(old); |
484 | } |
485 | |
486 | |
487 | QString NickEditDlg::nick() const |
488 | { |
489 | return ui.nickEdit->text(); |
490 | } |
491 | |
492 | |
493 | void NickEditDlg::on_nickEdit_textChanged(const QString &text) |
494 | { |
495 | ui.buttonBox->button(QDialogButtonBox::Ok)->setDisabled(text.isEmpty() || existing.contains(text)); |
496 | } |
497 | |