# include <cassert>
# include <iostream>
# include <QFile>
# include <QDebug>
# include <QVBoxLayout>
# include <QLabel>
# include <QLineEdit>
# include <QJsonDocument>
# include <QJsonObject>
# include <QNetworkAccessManager>
# include <QNetworkRequest>
# include <QCryptographicHash>
# include <openssl/rsa.h>
# include <openssl/bio.h>
# include <openssl/pem.h>
# 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 " <b> " + s + " </b> " ;
}
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 : 2 px solid white ;
}
[ class = " outside " ] {
border - radius : 20 px ;
border : 2 px solid white ;
padding : 10 px ;
}
QLabel {
font - size : 70 px ;
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 ) ;
}