mirror of
https://github.com/qTox/qTox.git
synced 2024-03-22 14:00:36 +08:00
parent
e1305f1f6f
commit
a141284e10
|
@ -17,7 +17,6 @@
|
||||||
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
along with qTox. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#include "toxme.h"
|
#include "toxme.h"
|
||||||
#include "src/core/core.h"
|
#include "src/core/core.h"
|
||||||
#include <QtDebug>
|
#include <QtDebug>
|
||||||
|
@ -29,25 +28,69 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
const QString Toxme::apiUrl{"https://toxme.se/api"};
|
QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error)
|
||||||
|
|
||||||
const unsigned char Toxme::pinnedPk[] = {0x5D, 0x72, 0xC5, 0x17, 0xDF, 0x6A, 0xEC, 0x54, 0xF1, 0xE9, 0x77, 0xA6, 0xB6, 0xF2, 0x59, 0x14,
|
|
||||||
0xEA, 0x4C, 0xF7, 0x27, 0x7A, 0x85, 0x02, 0x7C, 0xD9, 0xF5, 0x19, 0x6D, 0xF1, 0x7E, 0x0B, 0x13};
|
|
||||||
|
|
||||||
QByteArray Toxme::makeJsonRequest(QString json)
|
|
||||||
{
|
{
|
||||||
QNetworkAccessManager netman;
|
if (error)
|
||||||
QNetworkRequest request{apiUrl};
|
return QByteArray();
|
||||||
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
||||||
|
|
||||||
|
QNetworkAccessManager netman;
|
||||||
|
QNetworkRequest request{url};
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
QNetworkReply* reply = netman.post(request,json.toUtf8());
|
QNetworkReply* reply = netman.post(request,json.toUtf8());
|
||||||
while (!reply->isFinished())
|
|
||||||
|
while (reply->isRunning()) {
|
||||||
|
error = reply->error();
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
|
||||||
|
reply->waitForReadyRead(100);
|
||||||
qApp->processEvents();
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
return reply->readAll();
|
return reply->readAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
|
QByteArray Toxme::getServerPubkey(QString url, QNetworkReply::NetworkError &error)
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
// Get key
|
||||||
|
QNetworkAccessManager netman;
|
||||||
|
QNetworkRequest request{url};
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
QNetworkReply* reply = netman.get(request);
|
||||||
|
|
||||||
|
while (reply->isRunning()) {
|
||||||
|
error = reply->error();
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
|
||||||
|
reply->waitForReadyRead(100);
|
||||||
|
qApp->processEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract key
|
||||||
|
static const QByteArray pattern{"key\":\""};
|
||||||
|
|
||||||
|
QString json = reply->readAll();
|
||||||
|
json = json.remove(' ');
|
||||||
|
int start = json.indexOf(pattern) + pattern.length();
|
||||||
|
int end = json.indexOf("\"", start);
|
||||||
|
int pubkeySize = (end - start) / 2;
|
||||||
|
QString rawKey = json.mid(start, pubkeySize*2);
|
||||||
|
|
||||||
|
QByteArray key;
|
||||||
|
// I think, exist more easy way to convert key to ByteArray
|
||||||
|
for (int i = 0; i < pubkeySize; i++) {
|
||||||
|
QString byte = rawKey.mid(i*2, 2);
|
||||||
|
key[i] = byte.toInt(nullptr, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Toxme::prepareEncryptedJson(QString url, int action, QString payload)
|
||||||
{
|
{
|
||||||
QPair<QByteArray, QByteArray> keypair = Core::getInstance()->getKeypair();
|
QPair<QByteArray, QByteArray> keypair = Core::getInstance()->getKeypair();
|
||||||
if (keypair.first.isEmpty() || keypair.second.isEmpty())
|
if (keypair.first.isEmpty() || keypair.second.isEmpty())
|
||||||
|
@ -56,6 +99,11 @@ QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
|
||||||
return QByteArray();
|
return QByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError error = QNetworkReply::NoError;
|
||||||
|
QByteArray key = getServerPubkey(url, error);
|
||||||
|
if (error != QNetworkReply::NoError)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
QByteArray nonce(crypto_box_NONCEBYTES, 0);
|
QByteArray nonce(crypto_box_NONCEBYTES, 0);
|
||||||
randombytes((uint8_t*)nonce.data(), crypto_box_NONCEBYTES);
|
randombytes((uint8_t*)nonce.data(), crypto_box_NONCEBYTES);
|
||||||
|
|
||||||
|
@ -63,8 +111,13 @@ QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
|
||||||
const size_t cypherlen = crypto_box_MACBYTES+payloadData.size();
|
const size_t cypherlen = crypto_box_MACBYTES+payloadData.size();
|
||||||
unsigned char* payloadEnc = new unsigned char[cypherlen];
|
unsigned char* payloadEnc = new unsigned char[cypherlen];
|
||||||
|
|
||||||
crypto_box_easy(payloadEnc,(uint8_t*)payloadData.data(),payloadData.size(),
|
int cryptResult = crypto_box_easy(payloadEnc,(uint8_t*)payloadData.data(),payloadData.size(),
|
||||||
(uint8_t*)nonce.data(),pinnedPk,(uint8_t*)keypair.second.data());
|
(uint8_t*)nonce.data(),(unsigned char*)key.constData(),
|
||||||
|
(uint8_t*)keypair.second.data());
|
||||||
|
|
||||||
|
if (cryptResult != 0) // error
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
QByteArray payloadEncData(reinterpret_cast<char*>(payloadEnc), cypherlen);
|
QByteArray payloadEncData(reinterpret_cast<char*>(payloadEnc), cypherlen);
|
||||||
delete[] payloadEnc;
|
delete[] payloadEnc;
|
||||||
|
|
||||||
|
@ -78,88 +131,196 @@ QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
|
||||||
ToxId Toxme::lookup(QString address)
|
ToxId Toxme::lookup(QString address)
|
||||||
{
|
{
|
||||||
// JSON injection ?
|
// JSON injection ?
|
||||||
|
address = address.trimmed();
|
||||||
address.replace('\\',"\\\\");
|
address.replace('\\',"\\\\");
|
||||||
address.replace('"',"\"");
|
address.replace('"',"\"");
|
||||||
|
|
||||||
ToxId id;
|
|
||||||
const QString json{"{\"action\":3,\"name\":\""+address+"\"}"};
|
const QString json{"{\"action\":3,\"name\":\""+address+"\"}"};
|
||||||
static const QByteArray pattern{"public_key\""};
|
|
||||||
|
|
||||||
QByteArray response = makeJsonRequest(json);
|
QString apiUrl = "https://" + address.split(QLatin1Char('@')).last() + "/api";
|
||||||
|
QNetworkReply::NetworkError error = QNetworkReply::NoError;
|
||||||
|
QByteArray response = makeJsonRequest(apiUrl, json, error);
|
||||||
|
|
||||||
|
if (error != QNetworkReply::NoError)
|
||||||
|
return ToxId();
|
||||||
|
|
||||||
|
static const QByteArray pattern{"tox_id\""};
|
||||||
const int index = response.indexOf(pattern);
|
const int index = response.indexOf(pattern);
|
||||||
if (index == -1)
|
if (index == -1)
|
||||||
return id;
|
return ToxId();
|
||||||
|
|
||||||
response = response.mid(index+pattern.size());
|
response = response.mid(index+pattern.size());
|
||||||
|
|
||||||
const int idStart = response.indexOf('"');
|
const int idStart = response.indexOf('"');
|
||||||
if (idStart == -1)
|
if (idStart == -1)
|
||||||
return id;
|
return ToxId();
|
||||||
|
|
||||||
response = response.mid(idStart+1);
|
response = response.mid(idStart+1);
|
||||||
|
|
||||||
const int idEnd = response.indexOf('"');
|
const int idEnd = response.indexOf('"');
|
||||||
if (idEnd == -1)
|
if (idEnd == -1)
|
||||||
return id;
|
return ToxId();
|
||||||
|
|
||||||
response.truncate(idEnd);
|
response.truncate(idEnd);
|
||||||
|
|
||||||
id = ToxId(response);
|
return ToxId(response);
|
||||||
return id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Toxme::extractError(QString json)
|
Toxme::ExecCode Toxme::extractError(QString json)
|
||||||
{
|
{
|
||||||
static const QByteArray pattern{"c\":"};
|
static const QByteArray pattern{"c\":"};
|
||||||
|
|
||||||
|
if (json.isEmpty())
|
||||||
|
return ServerError;
|
||||||
|
|
||||||
json = json.remove(' ');
|
json = json.remove(' ');
|
||||||
const int index = json.indexOf(pattern);
|
const int start = json.indexOf(pattern);
|
||||||
if (index == -1)
|
if (start == -1)
|
||||||
return INT_MIN;
|
return ServerError;
|
||||||
|
|
||||||
json = json.mid(index+pattern.size());
|
json = json.mid(start+pattern.size());
|
||||||
|
int end = json.indexOf(",");
|
||||||
const int end = json.indexOf('}');
|
|
||||||
if (end == -1)
|
if (end == -1)
|
||||||
return INT_MIN;
|
{
|
||||||
|
end = json.indexOf("}");
|
||||||
|
if (end == -1)
|
||||||
|
return IncorrectResponce;
|
||||||
|
}
|
||||||
|
|
||||||
json.truncate(end);
|
json.truncate(end);
|
||||||
|
|
||||||
bool ok;
|
bool ok;
|
||||||
int r = json.toInt(&ok);
|
int r = json.toInt(&ok);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return INT_MIN;
|
return IncorrectResponce;
|
||||||
|
|
||||||
return r;
|
return ExecCode(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toxme::createAddress(ToxId id, QString address,
|
QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString address,
|
||||||
bool keepPrivate, QString bio)
|
bool keepPrivate, QString bio)
|
||||||
{
|
{
|
||||||
int privacy = keepPrivate ? 0 : 2;
|
int privacy = keepPrivate ? 0 : 2;
|
||||||
// JSON injection ?
|
// JSON injection ?
|
||||||
bio.replace('\\',"\\\\");
|
bio.replace('\\',"\\\\");
|
||||||
bio.replace('"',"\"");
|
bio.replace('"',"\"");
|
||||||
|
|
||||||
address.replace('\\',"\\\\");
|
address.replace('\\',"\\\\");
|
||||||
address.replace('"',"\"");
|
address.replace('"',"\"");
|
||||||
|
|
||||||
|
bio = bio.trimmed();
|
||||||
|
address = address.trimmed();
|
||||||
|
server = server.trimmed();
|
||||||
|
if (!server.contains("://"))
|
||||||
|
server = "https://" + server;
|
||||||
|
|
||||||
const QString payload{"{\"tox_id\":\""+id.toString()+"\","
|
const QString payload{"{\"tox_id\":\""+id.toString()+"\","
|
||||||
"\"name\":\""+address+"\","
|
"\"name\":\""+address+"\","
|
||||||
"\"privacy\":"+QString().setNum(privacy)+","
|
"\"privacy\":"+QString().setNum(privacy)+","
|
||||||
"\"bio\":\""+bio+"\","
|
"\"bio\":\""+bio+"\","
|
||||||
"\"timestamp\":"+QString().setNum(time(0))+"}"};
|
"\"timestamp\":"+QString().setNum(time(0))+"}"};
|
||||||
|
|
||||||
QByteArray response = makeJsonRequest(prepareEncryptedJson(1,payload));
|
qDebug() << payload;
|
||||||
|
QString pubkeyUrl = server + "/pk";
|
||||||
|
QString apiUrl = server + "/api";
|
||||||
|
QNetworkReply::NetworkError error = QNetworkReply::NoError;
|
||||||
|
QByteArray response = makeJsonRequest(apiUrl, prepareEncryptedJson(pubkeyUrl, 1, payload), error);
|
||||||
|
qDebug() << response;
|
||||||
|
|
||||||
return (extractError(response) == 0);
|
code = extractError(response);
|
||||||
|
if ((code != Registered && code != Updated) || error != QNetworkReply::NoError)
|
||||||
|
return QString();
|
||||||
|
|
||||||
|
return getPass(response, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Toxme::deleteAddress(ToxId id)
|
QString Toxme::getPass(QString json, ExecCode &code) {
|
||||||
|
static const QByteArray pattern{"password\":"};
|
||||||
|
|
||||||
|
json = json.remove(' ');
|
||||||
|
const int start = json.indexOf(pattern);
|
||||||
|
if (start == -1)
|
||||||
|
{
|
||||||
|
code = NoPassword;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
json = json.mid(start+pattern.size());
|
||||||
|
if (json.startsWith("null"))
|
||||||
|
{
|
||||||
|
code = Updated;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
json = json.mid(1, json.length());
|
||||||
|
int end = json.indexOf("\"");
|
||||||
|
if (end == -1)
|
||||||
|
{
|
||||||
|
code = IncorrectResponce;
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
json.truncate(end);
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Toxme::deleteAddress(QString server, ToxId id)
|
||||||
{
|
{
|
||||||
const QString payload{"{\"public_key\":\""+id.toString().left(64)+"\","
|
const QString payload{"{\"public_key\":\""+id.toString().left(64)+"\","
|
||||||
"\"timestamp\":"+QString().setNum(time(0))+"}"};
|
"\"timestamp\":"+QString().setNum(time(0))+"}"};
|
||||||
|
|
||||||
QByteArray response = makeJsonRequest(prepareEncryptedJson(2,payload));
|
server = server.trimmed();
|
||||||
|
if (!server.contains("://"))
|
||||||
|
server = "https://" + server;
|
||||||
|
|
||||||
return (extractError(response) == 0);
|
QString pubkeyUrl = server + "/pk";
|
||||||
|
QString apiUrl = server + "/api";
|
||||||
|
QNetworkReply::NetworkError error = QNetworkReply::NoError;
|
||||||
|
QByteArray response = makeJsonRequest(apiUrl, prepareEncryptedJson(pubkeyUrl, 2, payload), error);
|
||||||
|
if (error != QNetworkReply::NoError)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return extractError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Toxme::getErrorMessage(int errorCode)
|
||||||
|
{
|
||||||
|
switch (errorCode) {
|
||||||
|
case IncorrectResponce:
|
||||||
|
return QObject::tr("Incorrect responce");
|
||||||
|
case NoPassword:
|
||||||
|
return QObject::tr("No password in response");
|
||||||
|
case ServerError:
|
||||||
|
return QObject::tr("Server doesn't support Toxme");
|
||||||
|
case -1:
|
||||||
|
return QObject::tr("You must send POST requests to /api");
|
||||||
|
case -2:
|
||||||
|
return QObject::tr("Please try again using a HTTPS connection");
|
||||||
|
case -3:
|
||||||
|
return QObject::tr("I was unable to read your encrypted payload");
|
||||||
|
case -4:
|
||||||
|
return QObject::tr("You're making too many requests. Wait an hour and try again");
|
||||||
|
case -25:
|
||||||
|
return QObject::tr("This name is already in use");
|
||||||
|
case -26:
|
||||||
|
return QObject::tr("This Tox ID is already registered under another name");
|
||||||
|
case -27:
|
||||||
|
return QObject::tr("Please don't use a space in your name");
|
||||||
|
case -28:
|
||||||
|
return QObject::tr("Password incorrect");
|
||||||
|
case -29:
|
||||||
|
return QObject::tr("You can't use this name");
|
||||||
|
case -30:
|
||||||
|
return QObject::tr("Name not found");
|
||||||
|
case -31:
|
||||||
|
return QObject::tr("Tox ID not sent");
|
||||||
|
case -41:
|
||||||
|
return QObject::tr("Lookup failed because the other server replied with invalid data");
|
||||||
|
case -42:
|
||||||
|
return QObject::tr("That user does not exist");
|
||||||
|
case -43:
|
||||||
|
return QObject::tr("Internal lookup error. Please file a bug");
|
||||||
|
default:
|
||||||
|
return QObject::tr("Unknown error (%1)").arg(errorCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,9 @@
|
||||||
#define TOXME_H
|
#define TOXME_H
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
#include <QMutex>
|
#include <QMutex>
|
||||||
|
#include <QNetworkReply>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "src/core/toxid.h"
|
#include "src/core/toxid.h"
|
||||||
|
|
||||||
|
@ -34,25 +36,39 @@ class QNetworkAccessManager;
|
||||||
class Toxme
|
class Toxme
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum ExecCode {
|
||||||
|
ExecError = -50,
|
||||||
|
Registered = 0,
|
||||||
|
Updated = 1,
|
||||||
|
ServerError = 2,
|
||||||
|
IncorrectResponce = 3,
|
||||||
|
NoPassword = 4
|
||||||
|
};
|
||||||
|
|
||||||
/// Converts a toxme.se address to a Tox ID, returns an empty ID on error
|
/// Converts a toxme.se address to a Tox ID, returns an empty ID on error
|
||||||
static ToxId lookup(QString address);
|
static ToxId lookup(QString address);
|
||||||
/// Creates a new toxme.se address associated with a Tox ID.
|
/// Creates a new toxme.se address associated with a Tox ID.
|
||||||
/// If keepPrivate, the address will not be published on toxme.se
|
/// If keepPrivate, the address will not be published on toxme.se
|
||||||
/// The bio is a short optional description of yourself if you want to publish your address.
|
/// The bio is a short optional description of yourself if you want to publish your address.
|
||||||
static bool createAddress(ToxId id, QString address,
|
/// If it passed without error, return password, else return errorCode in QString
|
||||||
|
static QString createAddress(ExecCode &code, QString server, ToxId id, QString address,
|
||||||
bool keepPrivate=true, QString bio=QString());
|
bool keepPrivate=true, QString bio=QString());
|
||||||
/// Deletes the address associated with your current Tox ID
|
/// Deletes the address associated with your current Tox ID
|
||||||
static bool deleteAddress(ToxId id);
|
static int deleteAddress(QString server, ToxId id);
|
||||||
|
/// Return string of the corresponding error code
|
||||||
|
static QString getErrorMessage(int errorCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Toxme()=delete;
|
Toxme()=delete;
|
||||||
static QByteArray makeJsonRequest(QString json);
|
static QByteArray makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error);
|
||||||
static QByteArray prepareEncryptedJson(int action, QString payload);
|
static QByteArray prepareEncryptedJson(QString url, int action, QString payload);
|
||||||
static int extractError(QString json);
|
static QByteArray getServerPubkey(QString url, QNetworkReply::NetworkError &error);
|
||||||
|
static QString getPass(QString json, ExecCode &code);
|
||||||
|
static ExecCode extractError(QString json);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const QString apiUrl;
|
static const QMap<QString, QString> pubkeyUrls;
|
||||||
static const unsigned char pinnedPk[];
|
static const QMap<QString, QString> apiUrls;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // TOXME_H
|
#endif // TOXME_H
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "src/widget/gui.h"
|
#include "src/widget/gui.h"
|
||||||
#include "src/widget/translator.h"
|
#include "src/widget/translator.h"
|
||||||
#include "src/widget/contentlayout.h"
|
#include "src/widget/contentlayout.h"
|
||||||
|
#include "src/net/toxme.h"
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
AddFriendForm::AddFriendForm()
|
AddFriendForm::AddFriendForm()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user