1
0
mirror of https://github.com/qTox/qTox.git synced 2024-03-22 14:00:36 +08:00

Cherry-pick HTTP(S) toxme lookups from #2753

Implementation by @Diadlo
This commit is contained in:
tux3 2016-01-15 17:56:51 +01:00
parent e1305f1f6f
commit a141284e10
No known key found for this signature in database
GPG Key ID: 7E086DD661263264
3 changed files with 225 additions and 47 deletions

View File

@ -17,7 +17,6 @@
along with qTox. If not, see <http://www.gnu.org/licenses/>.
*/
#include "toxme.h"
#include "src/core/core.h"
#include <QtDebug>
@ -29,25 +28,69 @@
#include <string>
#include <ctime>
const QString Toxme::apiUrl{"https://toxme.se/api"};
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)
QByteArray Toxme::makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error)
{
QNetworkAccessManager netman;
QNetworkRequest request{apiUrl};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (error)
return QByteArray();
QNetworkAccessManager netman;
QNetworkRequest request{url};
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply* reply = netman.post(request,json.toUtf8());
while (!reply->isFinished())
while (reply->isRunning()) {
error = reply->error();
if (error)
break;
reply->waitForReadyRead(100);
qApp->processEvents();
}
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();
if (keypair.first.isEmpty() || keypair.second.isEmpty())
@ -56,6 +99,11 @@ QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
return QByteArray();
}
QNetworkReply::NetworkError error = QNetworkReply::NoError;
QByteArray key = getServerPubkey(url, error);
if (error != QNetworkReply::NoError)
return QByteArray();
QByteArray nonce(crypto_box_NONCEBYTES, 0);
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();
unsigned char* payloadEnc = new unsigned char[cypherlen];
crypto_box_easy(payloadEnc,(uint8_t*)payloadData.data(),payloadData.size(),
(uint8_t*)nonce.data(),pinnedPk,(uint8_t*)keypair.second.data());
int cryptResult = crypto_box_easy(payloadEnc,(uint8_t*)payloadData.data(),payloadData.size(),
(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);
delete[] payloadEnc;
@ -78,88 +131,196 @@ QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
ToxId Toxme::lookup(QString address)
{
// JSON injection ?
address = address.trimmed();
address.replace('\\',"\\\\");
address.replace('"',"\"");
ToxId id;
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);
if (index == -1)
return id;
return ToxId();
response = response.mid(index+pattern.size());
const int idStart = response.indexOf('"');
if (idStart == -1)
return id;
return ToxId();
response = response.mid(idStart+1);
const int idEnd = response.indexOf('"');
if (idEnd == -1)
return id;
return ToxId();
response.truncate(idEnd);
id = ToxId(response);
return id;
return ToxId(response);
}
int Toxme::extractError(QString json)
Toxme::ExecCode Toxme::extractError(QString json)
{
static const QByteArray pattern{"c\":"};
if (json.isEmpty())
return ServerError;
json = json.remove(' ');
const int index = json.indexOf(pattern);
if (index == -1)
return INT_MIN;
const int start = json.indexOf(pattern);
if (start == -1)
return ServerError;
json = json.mid(index+pattern.size());
const int end = json.indexOf('}');
json = json.mid(start+pattern.size());
int end = json.indexOf(",");
if (end == -1)
return INT_MIN;
{
end = json.indexOf("}");
if (end == -1)
return IncorrectResponce;
}
json.truncate(end);
bool ok;
int r = json.toInt(&ok);
if (!ok)
return INT_MIN;
return IncorrectResponce;
return r;
return ExecCode(r);
}
bool Toxme::createAddress(ToxId id, QString address,
bool keepPrivate, QString bio)
QString Toxme::createAddress(ExecCode &code, QString server, ToxId id, QString address,
bool keepPrivate, QString bio)
{
int privacy = keepPrivate ? 0 : 2;
// JSON injection ?
bio.replace('\\',"\\\\");
bio.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()+"\","
"\"name\":\""+address+"\","
"\"privacy\":"+QString().setNum(privacy)+","
"\"bio\":\""+bio+"\","
"\"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)+"\","
"\"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);
}
}

View File

@ -22,7 +22,9 @@
#define TOXME_H
#include <QString>
#include <QMap>
#include <QMutex>
#include <QNetworkReply>
#include <memory>
#include "src/core/toxid.h"
@ -34,25 +36,39 @@ class QNetworkAccessManager;
class Toxme
{
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
static ToxId lookup(QString address);
/// Creates a new toxme.se address associated with a Tox ID.
/// 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.
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());
/// 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:
Toxme()=delete;
static QByteArray makeJsonRequest(QString json);
static QByteArray prepareEncryptedJson(int action, QString payload);
static int extractError(QString json);
static QByteArray makeJsonRequest(QString url, QString json, QNetworkReply::NetworkError &error);
static QByteArray prepareEncryptedJson(QString url, int action, QString payload);
static QByteArray getServerPubkey(QString url, QNetworkReply::NetworkError &error);
static QString getPass(QString json, ExecCode &code);
static ExecCode extractError(QString json);
private:
static const QString apiUrl;
static const unsigned char pinnedPk[];
static const QMap<QString, QString> pubkeyUrls;
static const QMap<QString, QString> apiUrls;
};
#endif // TOXME_H

View File

@ -34,6 +34,7 @@
#include "src/widget/gui.h"
#include "src/widget/translator.h"
#include "src/widget/contentlayout.h"
#include "src/net/toxme.h"
#include <QWindow>
AddFriendForm::AddFriendForm()