2015-01-30 00:36:22 +08:00
|
|
|
#include "toxme.h"
|
2015-02-20 07:47:13 +08:00
|
|
|
#include "core.h"
|
2015-01-30 00:36:22 +08:00
|
|
|
#include <QNetworkAccessManager>
|
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <sodium/crypto_box.h>
|
2015-02-20 07:47:13 +08:00
|
|
|
#include <sodium/randombytes.h>
|
2015-01-30 00:36:22 +08:00
|
|
|
#include <string>
|
2015-02-25 20:16:45 +08:00
|
|
|
#include <ctime>
|
2015-01-30 00:36:22 +08:00
|
|
|
|
|
|
|
const QString Toxme::apiUrl{"https://toxme.se/api"};
|
|
|
|
|
2015-02-20 07:47:13 +08:00
|
|
|
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};
|
2015-01-30 00:36:22 +08:00
|
|
|
|
|
|
|
QByteArray Toxme::makeJsonRequest(QString json)
|
|
|
|
{
|
|
|
|
QNetworkAccessManager netman;
|
|
|
|
QNetworkRequest request{apiUrl};
|
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
|
|
|
|
|
|
|
QNetworkReply* reply = netman.post(request,json.toUtf8());
|
|
|
|
while (!reply->isFinished())
|
|
|
|
qApp->processEvents();
|
|
|
|
|
|
|
|
return reply->readAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
QByteArray Toxme::prepareEncryptedJson(int action, QString payload)
|
|
|
|
{
|
2015-02-20 07:47:13 +08:00
|
|
|
QPair<QByteArray, QByteArray> keypair = Core::getInstance()->getKeypair();
|
|
|
|
if (keypair.first.isEmpty() || keypair.second.isEmpty())
|
|
|
|
{
|
|
|
|
qWarning() << "Toxme::prepareEncryptedJson: Couldn't get our keypair, aborting";
|
|
|
|
return QByteArray();
|
|
|
|
}
|
2015-01-30 00:36:22 +08:00
|
|
|
|
2015-02-20 07:47:13 +08:00
|
|
|
QByteArray nonce(crypto_box_NONCEBYTES, 0);
|
|
|
|
randombytes((uint8_t*)nonce.data(), crypto_box_NONCEBYTES);
|
2015-01-30 00:36:22 +08:00
|
|
|
|
|
|
|
QByteArray payloadData = payload.toUtf8();
|
2015-02-20 07:47:13 +08:00
|
|
|
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());
|
|
|
|
QByteArray payloadEncData(reinterpret_cast<char*>(payloadEnc), cypherlen);
|
2015-01-30 00:36:22 +08:00
|
|
|
delete[] payloadEnc;
|
|
|
|
|
|
|
|
const QString json{"{\"action\":"+QString().setNum(action)+","
|
2015-02-20 07:47:13 +08:00
|
|
|
"\"public_key\":\""+keypair.first.toHex()+"\","
|
|
|
|
"\"encrypted\":\""+payloadEncData.toBase64()+"\","
|
|
|
|
"\"nonce\":\""+nonce.toBase64()+"\"}"};
|
2015-01-30 00:36:22 +08:00
|
|
|
return json.toUtf8();
|
|
|
|
}
|
|
|
|
|
|
|
|
ToxID Toxme::lookup(QString address)
|
|
|
|
{
|
|
|
|
// JSON injection ?
|
|
|
|
address.replace('\\',"\\\\");
|
|
|
|
address.replace('"',"\"");
|
|
|
|
|
|
|
|
ToxID id;
|
|
|
|
const QString json{"{\"action\":3,\"name\":\""+address+"\"}"};
|
|
|
|
static const QByteArray pattern{"public_key\""};
|
|
|
|
|
|
|
|
QByteArray response = makeJsonRequest(json);
|
|
|
|
const int index = response.indexOf(pattern);
|
|
|
|
if (index == -1)
|
|
|
|
return id;
|
|
|
|
response = response.mid(index+pattern.size());
|
|
|
|
|
|
|
|
const int idStart = response.indexOf('"');
|
|
|
|
if (idStart == -1)
|
|
|
|
return id;
|
|
|
|
response = response.mid(idStart+1);
|
|
|
|
|
|
|
|
const int idEnd = response.indexOf('"');
|
|
|
|
if (idEnd == -1)
|
|
|
|
return id;
|
|
|
|
response.truncate(idEnd);
|
|
|
|
|
|
|
|
id = ToxID::fromString(response);
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2015-02-20 07:47:13 +08:00
|
|
|
int Toxme::extractError(QString json)
|
|
|
|
{
|
|
|
|
static const QByteArray pattern{"c\":"};
|
|
|
|
|
|
|
|
json = json.remove(' ');
|
|
|
|
const int index = json.indexOf(pattern);
|
|
|
|
if (index == -1)
|
|
|
|
return INT_MIN;
|
|
|
|
json = json.mid(index+pattern.size());
|
|
|
|
|
|
|
|
const int end = json.indexOf('}');
|
|
|
|
if (end == -1)
|
|
|
|
return INT_MIN;
|
|
|
|
json.truncate(end);
|
|
|
|
|
|
|
|
bool ok;
|
|
|
|
int r = json.toInt(&ok);
|
|
|
|
if (!ok)
|
|
|
|
return INT_MIN;
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2015-01-30 00:36:22 +08:00
|
|
|
bool Toxme::createAddress(ToxID id, QString address,
|
|
|
|
bool keepPrivate, QString bio)
|
|
|
|
{
|
|
|
|
int privacy = keepPrivate ? 0 : 2;
|
|
|
|
// JSON injection ?
|
|
|
|
bio.replace('\\',"\\\\");
|
|
|
|
bio.replace('"',"\"");
|
|
|
|
address.replace('\\',"\\\\");
|
|
|
|
address.replace('"',"\"");
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
2015-02-20 07:47:13 +08:00
|
|
|
return (extractError(response) == 0);
|
2015-01-30 00:36:22 +08:00
|
|
|
}
|
2015-02-20 08:09:32 +08:00
|
|
|
|
|
|
|
bool Toxme::deleteAddress(ToxID id)
|
|
|
|
{
|
|
|
|
const QString payload{"{\"public_key\":\""+id.toString().left(64)+"\","
|
|
|
|
"\"timestamp\":"+QString().setNum(time(0))+"}"};
|
|
|
|
|
|
|
|
QByteArray response = makeJsonRequest(prepareEncryptedJson(2,payload));
|
|
|
|
|
|
|
|
return (extractError(response) == 0);
|
|
|
|
}
|