#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "drive_stats.hpp" #include "common/params.h" #include "common/utilpp.h" double MILE_TO_KM = 1.60934; #if defined(QCOM) || defined(QCOM2) const std::string private_key_path = "/persist/comma/id_rsa"; #else const std::string private_key_path = util::getenv_default("HOME", "/.comma/persist/comma/id_rsa", "/persist/comma/id_rsa"); #endif QByteArray rsa_sign(QByteArray data){ auto file = QFile(private_key_path.c_str()); bool r = file.open(QIODevice::ReadOnly); assert(r); auto key = file.readAll(); BIO *mem = BIO_new_mem_buf(key.data(), key.size()); assert(mem); RSA *rsa_private = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL); assert(rsa_private); auto sig = QByteArray(); sig.resize(RSA_size(rsa_private)); unsigned int sig_len; int ret = RSA_sign(NID_sha256, (unsigned char*)data.data(), data.size(), (unsigned char*)sig.data(), &sig_len, rsa_private); assert(ret == 1); assert(sig_len == sig.size()); BIO_free(mem); RSA_free(rsa_private); return sig; } QString create_jwt(QString dongle_id, int expiry=3600){ QJsonObject header; header.insert("alg", "RS256"); header.insert("typ", "JWT"); auto t = QDateTime::currentSecsSinceEpoch(); QJsonObject payload; payload.insert("identity", dongle_id); payload.insert("nbf", t); payload.insert("iat", t); payload.insert("exp", t + expiry); QString jwt = QJsonDocument(header).toJson(QJsonDocument::Compact).toBase64() + '.' + QJsonDocument(payload).toJson(QJsonDocument::Compact).toBase64(); auto hash = QCryptographicHash::hash(jwt.toUtf8(), QCryptographicHash::Sha256); auto sig = rsa_sign(hash); jwt += '.' + sig.toBase64(); return jwt; } QString bold(QString s) { return "" + s + ""; } QWidget *widget(QLayout *l){ QWidget *q = new QWidget(); q->setLayout(l); return q; } QWidget *build_stat(QString name, int stat){ QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(new QLabel(bold(QString("%1").arg(stat))), 1, Qt::AlignCenter); layout->addWidget(new QLabel(name),1, Qt::AlignCenter); return widget(layout); } void DriveStats::replyFinished(QNetworkReply *l){ QString answer = l->readAll(); answer.chop(1); QJsonDocument doc = QJsonDocument::fromJson(answer.toUtf8()); if (doc.isNull()) { qDebug() << "JSON Parse failed"; } QString IsMetric = QString::fromStdString(Params().get("IsMetric")); bool metric = (IsMetric =="1"); QJsonObject json = doc.object(); auto all = json["all"].toObject(); auto week = json["week"].toObject(); QGridLayout *gl = new QGridLayout(); int all_distance = all["distance"].toDouble()*(metric ? MILE_TO_KM : 1); gl->addWidget(new QLabel(bold("ALL TIME")), 0, 0, 1, 3); gl->addWidget(build_stat("DRIVES", all["routes"].toDouble()), 1, 0, 3, 1); gl->addWidget(build_stat(metric ? "KM" : "MILES", all_distance), 1, 1, 3, 1); gl->addWidget(build_stat("HOURS", all["minutes"].toDouble() / 60), 1, 2, 3, 1); QFrame *lineA = new QFrame; lineA->setFrameShape(QFrame::HLine); lineA->setFrameShadow(QFrame::Sunken); lineA->setProperty("class", "line"); gl->addWidget(lineA, 5, 0, 1, 3); int week_distance = week["distance"].toDouble()*(metric ? MILE_TO_KM : 1); gl->addWidget(new QLabel(bold("PAST WEEK")), 6, 0, 1, 3); gl->addWidget(build_stat("DRIVES", week["routes"].toDouble()), 7, 0, 3, 1); gl->addWidget(build_stat(metric ? "KM" : "MILES", week_distance), 7, 1, 3, 1); gl->addWidget(build_stat("HOURS", week["minutes"].toDouble() / 60), 7, 2, 3, 1); f->setLayout(gl); f->setStyleSheet(R"( [class="line"] { border: 2px solid white; } [class="outside"] { border-radius: 20px; border: 2px solid white; padding: 10px; } QLabel { font-size: 70px; font-weight: 200; } )"); } DriveStats::DriveStats(QWidget *parent) : QWidget(parent) { f = new QFrame; f->setProperty("class", "outside"); QVBoxLayout *v = new QVBoxLayout; v->addWidget(f); setLayout(v); QString dongle_id = QString::fromStdString(Params().get("DongleId")); QString token = create_jwt(dongle_id); QNetworkAccessManager *manager = new QNetworkAccessManager(this); connect(manager, &QNetworkAccessManager::finished, this, &DriveStats::replyFinished); QNetworkRequest request; request.setUrl(QUrl("https://api.commadotai.com/v1.1/devices/" + dongle_id + "/stats")); request.setRawHeader("Authorization", ("JWT "+token).toUtf8()); manager->get(request); }