ckb-next  v0.2.8 at branch master
ckb-next driver for corsair devices
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
KbFirmware Class Reference

#include <src/ckb/kbfirmware.h>

+ Inheritance diagram for KbFirmware:
+ Collaboration diagram for KbFirmware:

Data Structures

struct  FW
 

Signals

void downloaded ()
 

Static Public Member Functions

static bool checkUpdates ()
 
static bool hasDownloaded ()
 
static float versionForBoard (const QString &features, bool waitForComplete=false)
 
static QByteArray dataForBoard (const QString &features)
 

Data Fields

QNetworkAccessManager * networkManager
 

Private Types

enum  { UNKNOWN = -1, NO, YES }
 

Private Slots

void processDownload (QNetworkReply *reply)
 
void downloadFinished ()
 

Private Member Functions

 KbFirmware ()
 
 ~KbFirmware ()
 
bool _checkUpdates ()
 
float _latestForBoard (const QString &features, bool waitForComplete)
 
QByteArray _fileForBoard (const QString &features)
 

Private Attributes

quint64 lastCheck
 
quint64 lastFinished
 
QMap< QString, FWfwTable
 
QByteArray fwTableHash
 
QNetworkReply * tableDownload
 
enum KbFirmware:: { ... }  hasGPG
 

Static Private Attributes

static KbFirmware instance
 

Detailed Description

Definition at line 10 of file kbfirmware.h.

Member Enumeration Documentation

anonymous enum
private
Enumerator
UNKNOWN 
NO 
YES 

Definition at line 52 of file kbfirmware.h.

52 { UNKNOWN = -1, NO, YES } hasGPG :2;
enum KbFirmware::@1 hasGPG

Constructor & Destructor Documentation

KbFirmware::KbFirmware ( )
private

Definition at line 14 of file kbfirmware.cpp.

References networkManager.

14  :
16 {
17  // Disable bearer polling. This corrects an issue with latency spikes when
18  // using WiFi. The problem and workaround are described here:
19  // https://lostdomain.org/2017/06/17/qt-qnetworkaccessmanager-causing-latency-spikes-on-wifi/
20  qputenv("QT_BEARER_POLL_TIMEOUT", QByteArray::number(-1));
21  networkManager = new QNetworkAccessManager();
22 }
QNetworkAccessManager * networkManager
Definition: kbfirmware.h:28
quint64 lastFinished
Definition: kbfirmware.h:35
quint64 lastCheck
Definition: kbfirmware.h:35
enum KbFirmware::@1 hasGPG
QNetworkReply * tableDownload
Definition: kbfirmware.h:49
KbFirmware::~KbFirmware ( )
private

Definition at line 24 of file kbfirmware.cpp.

References networkManager.

24  {
25  delete networkManager;
26 }
QNetworkAccessManager * networkManager
Definition: kbfirmware.h:28

Member Function Documentation

bool KbFirmware::_checkUpdates ( )
private

Definition at line 28 of file kbfirmware.cpp.

References AUTO_CHECK_TIME, downloadFinished(), lastCheck, networkManager, and tableDownload.

Referenced by checkUpdates().

28  {
29  quint64 now = QDateTime::currentMSecsSinceEpoch();
30  if(now < lastCheck + AUTO_CHECK_TIME)
31  return false;
32  // First location is for debugging only.
33  // tableDownload = networkManager->get(QNetworkRequest(QUrl("https://raw.githubusercontent.com/frickler24/ckb-next/issues-26-Firmware-Incident/FIRMWARE")));
34  // This one is the production one.
35  tableDownload = networkManager->get(QNetworkRequest(QUrl("https://raw.githubusercontent.com/mattanger/ckb-next/master/FIRMWARE")));
36  connect(tableDownload, SIGNAL(finished()), this, SLOT(downloadFinished()));
37  lastCheck = now;
38  return true;
39 }
QNetworkAccessManager * networkManager
Definition: kbfirmware.h:28
static const quint64 AUTO_CHECK_TIME
Definition: kbfirmware.cpp:9
quint64 lastCheck
Definition: kbfirmware.h:35
QNetworkReply * tableDownload
Definition: kbfirmware.h:49
void downloadFinished()
Definition: kbfirmware.cpp:141

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

QByteArray KbFirmware::_fileForBoard ( const QString &  features)
private

Definition at line 180 of file kbfirmware.cpp.

References QuaZip::csInsensitive, KbFirmware::FW::fileName, fwTable, KbFirmware::FW::hash, QuaZip::mdUnzip, networkManager, QuaZip::open(), QuaZipFile::open(), quit(), QuaZip::setCurrentFile(), tableName(), and KbFirmware::FW::url.

Referenced by dataForBoard().

180  {
181  QString name = tableName(features);
182  FW info = fwTable.value(name);
183  if(info.hash.isEmpty())
184  return "";
185  // Download zip from URL. Wait for it to finish.
186  QNetworkReply* reply = networkManager->get(QNetworkRequest(QUrl(info.url)));
187  QEventLoop loop(this);
188  connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
189  loop.exec();
190  // Download finished, process data
191  if(reply->error() != QNetworkReply::NoError)
192  return "";
193  QByteArray zipData = reply->readAll();
194  QBuffer buffer(&zipData);
195  // Open zip archive
196  QuaZip zip(&buffer);
197  if(!zip.open(QuaZip::mdUnzip))
198  return "";
199  // Find the desired file
200  if(!zip.setCurrentFile(info.fileName, QuaZip::csInsensitive))
201  return "";
202  QuaZipFile binFile(&zip);
203  if(!binFile.open(QIODevice::ReadOnly))
204  return "";
205  QByteArray binary = binFile.readAll();
206  // Check the hash
207  if(QCryptographicHash::hash(binary, QCryptographicHash::Sha256) != info.hash)
208  return "";
209  return binary;
210 }
QNetworkAccessManager * networkManager
Definition: kbfirmware.h:28
static void quit()
quit Stop working the daemon. function is called if the daemon received a sigterm In this case...
Definition: main.c:30
ZIP file is open for reading files inside it.
Definition: quazip.h:96
QMap< QString, FW > fwTable
Definition: kbfirmware.h:44
static QString tableName(const QString &features)
Definition: kbfirmware.cpp:150
ZIP archive.
Definition: quazip.h:84
A file inside ZIP archive.
Definition: quazipfile.h:74
Case insensitive.
Definition: quazip.h:117

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

float KbFirmware::_latestForBoard ( const QString &  features,
bool  waitForComplete 
)
private

Definition at line 162 of file kbfirmware.cpp.

References checkUpdates(), KbManager::ckbDaemonVersionF(), KbManager::ckbGuiVersionF(), KbFirmware::FW::ckbVersion, downloaded(), fwTable, KbFirmware::FW::fwVersion, KbFirmware::FW::hash, quit(), tableDownload, and tableName().

Referenced by versionForBoard().

162  {
163  if((tableDownload || checkUpdates()) && waitForComplete){
164  // If waiting is desired, enter an event loop and stay here until the download is finished
165  QEventLoop loop(this);
166  connect(this, SIGNAL(downloaded()), &loop, SLOT(quit()));
167  loop.exec();
168  }
169  // Find this board
170  QString name = tableName(features);
171  FW info = fwTable.value(name);
172  if(info.hash.isEmpty())
173  return 0.f;
174  // Don't return the new version if the current ckb doesn't support it
175  if(info.ckbVersion > KbManager::ckbGuiVersionF() || info.ckbVersion > KbManager::ckbDaemonVersionF())
176  return -1.f;
177  return info.fwVersion;
178 }
static void quit()
quit Stop working the daemon. function is called if the daemon received a sigterm In this case...
Definition: main.c:30
static float ckbDaemonVersionF()
Definition: kbmanager.h:31
static float ckbGuiVersionF()
Definition: kbmanager.h:30
QMap< QString, FW > fwTable
Definition: kbfirmware.h:44
static bool checkUpdates()
Definition: kbfirmware.h:15
static QString tableName(const QString &features)
Definition: kbfirmware.cpp:150
QNetworkReply * tableDownload
Definition: kbfirmware.h:49

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static bool KbFirmware::checkUpdates ( )
inlinestatic

Definition at line 15 of file kbfirmware.h.

References _checkUpdates(), and instance.

Referenced by _latestForBoard(), and MainWindow::timerTick().

15 { return instance._checkUpdates(); }
static KbFirmware instance
Definition: kbfirmware.h:58
bool _checkUpdates()
Definition: kbfirmware.cpp:28

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static QByteArray KbFirmware::dataForBoard ( const QString &  features)
inlinestatic

Definition at line 25 of file kbfirmware.h.

References _fileForBoard(), and instance.

Referenced by FwUpgradeDialog::exec().

25 { return instance._fileForBoard(features); }
static KbFirmware instance
Definition: kbfirmware.h:58
QByteArray _fileForBoard(const QString &features)
Definition: kbfirmware.cpp:180

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void KbFirmware::downloaded ( )
signal

Definition at line 148 of file moc_kbfirmware.cpp.

Referenced by _latestForBoard(), and downloadFinished().

149 {
150  QMetaObject::activate(this, &staticMetaObject, 0, Q_NULLPTR);
151 }

+ Here is the caller graph for this function:

void KbFirmware::downloadFinished ( )
privateslot

Definition at line 141 of file kbfirmware.cpp.

References downloaded(), processDownload(), and tableDownload.

Referenced by _checkUpdates().

141  {
142  if(!tableDownload)
143  return;
145  tableDownload->deleteLater();
146  tableDownload = 0;
147  emit downloaded();
148 }
void processDownload(QNetworkReply *reply)
Definition: kbfirmware.cpp:41
QNetworkReply * tableDownload
Definition: kbfirmware.h:49

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static bool KbFirmware::hasDownloaded ( )
inlinestatic

Definition at line 18 of file kbfirmware.h.

References instance, and lastFinished.

Referenced by KbWidget::on_fwUpdButton_clicked(), and KbWidget::updateFwButton().

18 { return instance.lastFinished != 0; }
static KbFirmware instance
Definition: kbfirmware.h:58
quint64 lastFinished
Definition: kbfirmware.h:35

+ Here is the caller graph for this function:

void KbFirmware::processDownload ( QNetworkReply *  reply)
privateslot

Definition at line 41 of file kbfirmware.cpp.

References KbFirmware::FW::ckbVersion, KbFirmware::FW::fileName, fwTable, fwTableHash, KbFirmware::FW::fwVersion, hasGPG, KbFirmware::FW::hash, lastCheck, lastFinished, NO, KbManager::parseVersionString(), UNKNOWN, KbFirmware::FW::url, and YES.

Referenced by downloadFinished().

41  {
42  if(reply->error() != QNetworkReply::NoError)
43  return;
44  // Update last check
45  lastCheck = lastFinished = QDateTime::currentMSecsSinceEpoch();
46  QByteArray data = reply->readAll();
47  // Don't do anything if this is the same as the last version downloaded
48  QByteArray hash = QCryptographicHash::hash(data, QCryptographicHash::Sha256);
49  if(hash == fwTableHash)
50  return;
51  fwTableHash = hash;
52  if(hasGPG == UNKNOWN){
53  // Check for a GPG installation
54  QProcess gpg;
55  gpg.start("gpg", QStringList("--version"));
56  gpg.waitForFinished();
57  if(gpg.error() == QProcess::FailedToStart)
58  // No GPG install
59  hasGPG = NO;
60  else {
61  QString output = QString::fromUtf8(gpg.readAll());
62  // Must support RSA keys and SHA256
63  if(output.contains("RSA", Qt::CaseInsensitive) && output.contains("SHA256", Qt::CaseInsensitive))
64  hasGPG = YES;
65  else
66  hasGPG = NO;
67  }
68  if(!hasGPG)
69  qDebug() << "No GPG detected, signature verification disabled";
70  }
71  if(hasGPG){
72  // If GPG is available, check the signature on the file before proceeding.
73  QDir tmp = QDir::temp();
74  // Save file to a temporary path. Include PID to avoid conflicts
75  qint64 pid = QCoreApplication::applicationPid();
76  QString fwPath = tmp.absoluteFilePath(QString("ckb-%1-firmware").arg(pid));
77  QFile firmware(fwPath);
78  if(!firmware.open(QIODevice::WriteOnly)
79  || firmware.write(data) != data.length()){
80  qDebug() << "Failed to write firmware file to temporary location, aborting firmware check";
81  return;
82  }
83  firmware.close();
84  // Write GPG key
85  QString keyPath = tmp.absoluteFilePath(QString("ckb-%1-key.gpg").arg(pid));
86  if(!QFile::copy(":/bin/ckb-next-key.gpg", keyPath)){
87  firmware.remove();
88  qDebug() << "Failed to write GPG key to temporary location, aborting firmware check";
89  return;
90  }
91  // Check signature
92  QProcess gpg;
93  gpg.start("gpg", QStringList("--no-default-keyring") << "--keyring" << keyPath << "--verify" << fwPath);
94  gpg.waitForFinished();
95  // Clean up temp files
96  tmp.remove(fwPath);
97  tmp.remove(keyPath);
98  if(gpg.error() != QProcess::UnknownError || gpg.exitCode() != 0){
99  qDebug() << "GPG couldn't verify firmware signature:";
100  qDebug() << gpg.readAllStandardOutput();
101  qDebug() << gpg.readAllStandardError();
102  return;
103  }
104  // Signature good, proceed to update database
105  }
106  fwTable.clear();
107  QStringList lines = QString::fromUtf8(data).split("\n");
108  bool scan = false;
109  foreach(QString line, lines){
110  // Collapse whitespace
111  line.replace(QRegExp("\\s+"), " ").remove(QRegExp("^\\s")).remove(QRegExp("\\s$"));
112  // Skip empty or commented-out lines
113  if(line.length() == 0 || line.at(0) == '#')
114  continue;
115  // Don't read anything until the entries begin and don't read anything after they end
116  if(!scan){
117  if(line == "!BEGIN FW ENTRIES")
118  scan = true;
119  else
120  continue;
121  }
122  if(line == "!END FW ENTRIES")
123  break;
124  QStringList components = line.split(" ");
125  if(components.length() != 7)
126  continue;
127  // "VENDOR-PRODUCT"
128  QString device = components[0].toUpper() + "-" + components[1].toUpper();
129  FW fw;
130  fw.fwVersion = components[2].toFloat(); // Firmware blob version
131  fw.url = QUrl::fromPercentEncoding(components[3].toLatin1()); // URL to zip file
132  fw.ckbVersion = KbManager::parseVersionString(components[4]); // Minimum ckb version
133  fw.fileName = QUrl::fromPercentEncoding(components[5].toLatin1()); // Name of file inside zip
134  fw.hash = QByteArray::fromHex(components[6].toLatin1()); // SHA256 of file inside zip
135  // Update entry
136  fwTable[device] = fw;
137  }
138  qDebug() << "Downloaded new firmware list." << fwTable.count() << "entries found.";
139 }
static float parseVersionString(QString version)
Definition: kbmanager.cpp:48
QByteArray fwTableHash
Definition: kbfirmware.h:46
quint64 lastFinished
Definition: kbfirmware.h:35
quint64 lastCheck
Definition: kbfirmware.h:35
enum KbFirmware::@1 hasGPG
QMap< QString, FW > fwTable
Definition: kbfirmware.h:44

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

static float KbFirmware::versionForBoard ( const QString &  features,
bool  waitForComplete = false 
)
inlinestatic

Definition at line 22 of file kbfirmware.h.

References _latestForBoard(), and instance.

Referenced by MainWindow::checkFwUpdates(), KbWidget::on_fwUpdButton_clicked(), and KbWidget::updateFwButton().

22 { return instance._latestForBoard(features, waitForComplete); }
static KbFirmware instance
Definition: kbfirmware.h:58
float _latestForBoard(const QString &features, bool waitForComplete)
Definition: kbfirmware.cpp:162

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

Field Documentation

QMap<QString, FW> KbFirmware::fwTable
private

Definition at line 44 of file kbfirmware.h.

Referenced by _fileForBoard(), _latestForBoard(), and processDownload().

QByteArray KbFirmware::fwTableHash
private

Definition at line 46 of file kbfirmware.h.

Referenced by processDownload().

enum { ... } KbFirmware::hasGPG

Referenced by processDownload().

KbFirmware KbFirmware::instance
staticprivate

Definition at line 58 of file kbfirmware.h.

Referenced by checkUpdates(), dataForBoard(), hasDownloaded(), and versionForBoard().

quint64 KbFirmware::lastCheck
private

Definition at line 35 of file kbfirmware.h.

Referenced by _checkUpdates(), and processDownload().

quint64 KbFirmware::lastFinished
private

Definition at line 35 of file kbfirmware.h.

Referenced by hasDownloaded(), and processDownload().

QNetworkAccessManager* KbFirmware::networkManager

Definition at line 28 of file kbfirmware.h.

Referenced by _checkUpdates(), _fileForBoard(), KbFirmware(), and ~KbFirmware().

QNetworkReply* KbFirmware::tableDownload
private

Definition at line 49 of file kbfirmware.h.

Referenced by _checkUpdates(), _latestForBoard(), and downloadFinished().


The documentation for this class was generated from the following files: