danbooru-client/src/danbooruconnectwidget.cpp
Luca Beltrame b951cddee4 Put a TODO for "password salts"
The way Danbooru handles passwords is simplistic to stay the least. It's
also impossible to know the "password salt" without visiting the API
page. As such, "salts" need to be put along with the board they
originate from.

The code doesn't handle missing keys, hence this TODO.
2015-02-15 22:05:17 +01:00

223 lines
6.3 KiB
C++

/*
* This file is part of Danbooru Client.
* Copyright 2013 Luca Beltrame <lbeltrame@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "danbooruconnectwidget.h"
#include "libdanbooru/danbooruservice.h"
#include <QCryptographicHash>
#include <QDebug>
#include <KWallet>
using KWallet::Wallet;
namespace Danbooru
{
const QMap< QUrl, QString > initBoardSalts()
{
QMap< QUrl, QString > boardSalts;
boardSalts.insert(QUrl("http://konachan.com"),
QString("So-I-Heard-You-Like-Mupkids-?--%1--"));
boardSalts.insert(QUrl("http://konachan.net"),
QString("So-I-Heard-You-Like-Mupkids-?--%1--"));
boardSalts.insert(QUrl("http://yande.re"),
QString("choujin-steiner--%1--"));
boardSalts.insert(QUrl("http://danbooru.donmai.us"),
QString("choujin-steiner--%1--"));
return boardSalts;
}
const QMap<QUrl, QString> DanbooruConnectWidget::boardSalts = initBoardSalts();
DanbooruConnectWidget::DanbooruConnectWidget(QVector< QUrl > urlList,
QWidget *parent):
QWidget(parent),
m_wallet(0)
{
setupUi(this);
danbooruUrlComboBox->setFocus();
closeButton->setIcon(QIcon::fromTheme(QLatin1String("dialog-close")));
closeButton->setToolTip(i18n("Close dialog and discard changes"));
userLineEdit->setClearButtonEnabled(true);
passwdLineEdit->setClearButtonEnabled(true);
passwdLineEdit->setEchoMode(QLineEdit::Password);
if (anonCheckBox->isChecked()) {
userLineEdit->setEnabled(false);
passwdLineEdit->setEnabled(false);
}
danbooruUrlComboBox->clear();
for (auto item : urlList) {
danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item);
}
m_wallet = Wallet::openWallet(Wallet::NetworkWallet(), 0,
Wallet::Asynchronous
);
connect(m_wallet, &KWallet::Wallet::walletOpened, this, &DanbooruConnectWidget::checkWallet);
connect(danbooruUrlComboBox, static_cast<void (KComboBox::*)(int)>(&KComboBox::currentIndexChanged), this, &DanbooruConnectWidget::getWalletData);
connect(anonCheckBox, &QCheckBox::stateChanged, this, &DanbooruConnectWidget::toggleLineEdits);
connect(buttonBox, &QDialogButtonBox::accepted, this, &DanbooruConnectWidget::accept);
connect(closeButton, &QPushButton::clicked, this, &DanbooruConnectWidget::emitRejected);
}
DanbooruConnectWidget::~DanbooruConnectWidget()
{
delete m_wallet;
}
void DanbooruConnectWidget::checkWallet(bool result)
{
if (!result) {
return;
}
if (!m_wallet->hasFolder(Wallet::PasswordFolder())) {
m_wallet->createFolder(Wallet::PasswordFolder());
}
getWalletData();
}
void DanbooruConnectWidget::getWalletData()
{
if (!m_wallet) {
return;
}
QMap<QString, QString> valueMap;
QString key = danbooruUrlComboBox->currentText();
if (m_wallet->hasEntry(key)) {
if (m_wallet->readMap(key, valueMap) != 0) {
return;
}
m_username = valueMap[QLatin1String("username")];
QString hashedPassword;
// TODO: Handle the case where the "salt" is not known
hashedPassword = boardSalts.value(key);
hashedPassword = hashedPassword.arg(valueMap[QLatin1String("password")]);
hashedPassword = QCryptographicHash::hash(hashedPassword.toUtf8(),
QCryptographicHash::Sha1).toHex();
m_password = hashedPassword;
userLineEdit->setText(m_username);
passwdLineEdit->setText(valueMap[QLatin1String("password")]);
}
}
void DanbooruConnectWidget::toggleLineEdits(int state)
{
if (state == Qt::Unchecked) {
userLineEdit->setEnabled(true);
passwdLineEdit->setEnabled(true);
} else if (state == Qt::Checked) {
userLineEdit->setEnabled(false);
passwdLineEdit->setEnabled(false);
}
}
void DanbooruConnectWidget::setBoards(const QVector<QUrl> &urlList) {
danbooruUrlComboBox->clear();
for (auto item : urlList) {
danbooruUrlComboBox->insertUrl(urlList.indexOf(item), item);
}
}
void DanbooruConnectWidget::emitRejected()
{
Q_EMIT rejected();
}
QUrl DanbooruConnectWidget::boardUrl() const
{
return m_boardUrl;
}
QString DanbooruConnectWidget::username() const
{
return m_username;
}
QString DanbooruConnectWidget::password() const
{
return m_password;
}
void DanbooruConnectWidget::accept()
{
QString hashedPassword;
if (!userLineEdit->text().isEmpty() && !passwdLineEdit->text().isEmpty()) {
if (m_wallet && !m_wallet->hasEntry(m_boardUrl.url())) {
QMap<QString, QString> dataMap;
dataMap.insert(QLatin1String("username"), m_username);
dataMap.insert(QLatin1String("password"), passwdLineEdit->text());
m_wallet->writeMap(m_boardUrl.url(), dataMap);
}
// Only calculate if we haven't set a password from the wallet already
if (m_password.isEmpty()) {
hashedPassword = boardSalts.value(m_boardUrl);
hashedPassword = hashedPassword.arg(passwdLineEdit->text());
hashedPassword = QCryptographicHash::hash(hashedPassword.toUtf8(),
QCryptographicHash::Sha1).toHex();
m_password = hashedPassword;
}
}
m_boardUrl = QUrl::fromUserInput(danbooruUrlComboBox->currentText());
Q_EMIT(accepted());
}
bool DanbooruConnectWidget::isAnonymous() const {
return anonCheckBox->isChecked();
}
}; // namespace Danbooru