- The first part

- Added CodeDocs config file for customization
- Fixing LGTM alerts
- LGTM bug fixed again
- added token option to hyperion-remote
- fix DBManager::getDB()
- next bugfix
- correct broken signal from SettingManager to Hyperion
- Token list is created after the schema is fetched

Signed-off-by: Paulchen-Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
Paulchen-Panther
2019-07-12 16:54:26 +02:00
parent 4fc745e748
commit ea796160af
72 changed files with 2546 additions and 485 deletions

View File

@@ -12,6 +12,7 @@
#include <QString>
class JsonCB;
class AuthManager;
class JsonAPI : public QObject
{
@@ -24,16 +25,17 @@ public:
/// @param peerAddress provide the Address of the peer
/// @param log The Logger class of the creator
/// @param parent Parent QObject
/// @param localConnection True when the sender has origin home network
/// @param noListener if true, this instance won't listen for hyperion push events
///
JsonAPI(QString peerAddress, Logger* log, QObject* parent, bool noListener = false);
JsonAPI(QString peerAddress, Logger* log, const bool& localConnection, QObject* parent, bool noListener = false);
///
/// Handle an incoming JSON message
///
/// @param message the incoming message as string
///
void handleMessage(const QString & message);
void handleMessage(const QString & message, const QString& httpAuthHeader = "");
public slots:
///
@@ -48,6 +50,24 @@ public slots:
/// process and push new log messages from logger (if enabled)
void incommingLogMessage(const Logger::T_LOG_MESSAGE&);
private slots:
///
/// @brief Handle emits from AuthManager of new request, just _userAuthorized sessions are allowed to handle them
/// @param id The id of the request
/// @param The comment which needs to be accepted
///
void handlePendingTokenRequest(const QString& id, const QString& comment);
///
/// @brief Handle emits from AuthManager of accepted/denied/timeouts token request, just if QObject matches with this instance we are allowed to send response.
/// @param success If true the request was accepted else false and no token was created
/// @param caller The origin caller instance who requested this token
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void handleTokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
signals:
///
/// Signal emits with the reply message provided with handleMessage()
@@ -60,6 +80,16 @@ signals:
void forwardJsonMessage(QJsonObject);
private:
/// Auth management pointer
AuthManager* _authManager;
/// Reflect auth status of this client
bool _authorized;
bool _userAuthorized;
/// Reflect auth required
bool _apiAuthRequired;
// true if further callbacks are forbidden (http)
bool _noListener;
@@ -214,6 +244,21 @@ private:
///
void handleVideoModeCommand(const QJsonObject & message, const QString &command, const int tan);
/// Handle an incoming JSON plugin message
///
/// @param message the incoming message
///
void handleAuthorizeCommand(const QJsonObject & message, const QString &command, const int tan);
///
/// Handle HTTP on-the-fly token authorization
/// @param command The command
/// @param tan The tan
/// @param token The token to verify
/// @return True on succcess else false (pushes failed client feedback)
///
const bool handleHTTPAuth(const QString& command, const int& tan, const QString& token);
///
/// Handle an incoming JSON Clearall message
///

226
include/db/AuthTable.h Normal file
View File

@@ -0,0 +1,226 @@
#pragma once
// hyperion
#include <db/DBManager.h>
#include <QCryptographicHash>
// qt
#include <QDateTime>
#include <QUuid>
///
/// @brief Authentication table interface
///
class AuthTable : public DBManager
{
public:
/// construct wrapper with auth table
AuthTable(const QString& rootPath, QObject* parent = nullptr)
: DBManager(parent)
{
// Init Hyperion database usage
setRootPath(rootPath);
setDB("hyperion");
// init Auth table
setTable("auth");
// create table columns
createTable(QStringList()<<"user TEXT"<<"password BLOB"<<"token BLOB"<<"salt BLOB"<<"comment TEXT"<<"id TEXT"<<"created_at TEXT"<<"last_use TEXT");
};
~AuthTable(){};
///
/// @brief Create a user record, if called on a existing user the auth is recreated
/// @param[in] user The username
/// @param[in] pw The password
/// @return true on success else false
///
inline bool createUser(const QString& user, const QString& pw)
{
// new salt
QByteArray salt = QCryptographicHash::hash(QUuid::createUuid().toByteArray(), QCryptographicHash::Sha512).toHex();
QVariantMap map;
map["user"] = user;
map["salt"] = salt;
map["password"] = hashPasswordWithSalt(pw,salt);
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
VectorPair cond;
cond.append(CPair("user",user));
return createRecord(cond, map);
}
///
/// @brief Test if user record exists
/// @param[in] user The user id
/// @return true on success else false
///
inline bool userExist(const QString& user)
{
VectorPair cond;
cond.append(CPair("user",user));
return recordExists(cond);
}
///
/// @brief Test if a user is authorized for access with given pw.
/// @param user The user name
/// @param pw The password
/// @return True on success else false
///
inline bool isUserAuthorized(const QString& user, const QString& pw)
{
if(userExist(user) && (calcPasswordHashOfUser(user, pw) == getPasswordHashOfUser(user)))
{
updateUserUsed(user);
return true;
}
return false;
}
///
/// @brief Update 'last_use' column entry for the corresponding user
/// @param[in] user The user to search for
///
inline void updateUserUsed(const QString& user)
{
QVariantMap map;
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
VectorPair cond;
cond.append(CPair("user", user));
updateRecord(cond, map);
}
///
/// @brief Test if token record exists, updates last_use on success
/// @param[in] token The token id
/// @return true on success else false
///
inline bool tokenExist(const QString& token)
{
QVariantMap map;
map["last_use"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
VectorPair cond;
cond.append(CPair("token", hashToken(token)));
if(recordExists(cond))
{
// update it
createRecord(cond,map);
return true;
}
return false;
}
///
/// @brief Create a new token record with comment
/// @param[in] token The token id as plaintext
/// @param[in] comment The comment for the token (eg a human readable identifier)
/// @param[in] id The id for the token
/// @return true on success else false
///
inline bool createToken(const QString& token, const QString& comment, const QString& id)
{
QVariantMap map;
map["comment"] = comment;
map["id"] = idExist(id) ? QUuid::createUuid().toString().remove("{").remove("}").left(5) : id;
map["created_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
VectorPair cond;
cond.append(CPair("token", hashToken(token)));
return createRecord(cond, map);
}
///
/// @brief Delete token record by id
/// @param[in] id The token id
/// @return true on success else false
///
inline bool deleteToken(const QString& id)
{
VectorPair cond;
cond.append(CPair("id", id));
return deleteRecord(cond);
}
///
/// @brief Get all 'comment', 'last_use' and 'id' column entries
/// @return A vector of all lists
///
inline const QVector<QVariantMap> getTokenList()
{
QVector<QVariantMap> results;
getRecords(results, QStringList() << "comment" << "id" << "last_use");
return results;
}
///
/// @brief Test if id exists
/// @param[in] id The id
/// @return true on success else false
///
inline bool idExist(const QString& id)
{
VectorPair cond;
cond.append(CPair("id", id));
return recordExists(cond);
}
///
/// @brief Get the passwort hash of a user from db
/// @param user The user name
/// @return password as hash
///
inline const QByteArray getPasswordHashOfUser(const QString& user)
{
QVariantMap results;
VectorPair cond;
cond.append(CPair("user", user));
getRecord(cond, results, QStringList()<<"password");
return results["password"].toByteArray();
}
///
/// @brief Calc the password hash of a user based on user name and password
/// @param user The user name
/// @param pw The password
/// @return The calced password hash
///
inline const QByteArray calcPasswordHashOfUser(const QString& user, const QString& pw)
{
// get salt
QVariantMap results;
VectorPair cond;
cond.append(CPair("user", user));
getRecord(cond, results, QStringList()<<"salt");
// calc
return hashPasswordWithSalt(pw,results["salt"].toByteArray());
}
///
/// @brief Create a password hash of plaintex password + salt
/// @param pw The plaintext password
/// @param salt The salt
/// @return The password hash with salt
///
inline const QByteArray hashPasswordWithSalt(const QString& pw, const QByteArray& salt)
{
return QCryptographicHash::hash(pw.toUtf8().append(salt), QCryptographicHash::Sha512).toHex();
}
///
/// @brief Create a token hash
/// @param token The plaintext token
/// @return The token hash
///
inline const QByteArray hashToken(const QString& token)
{
return QCryptographicHash::hash(token.toUtf8(), QCryptographicHash::Sha512).toHex();
}
};

130
include/db/DBManager.h Normal file
View File

@@ -0,0 +1,130 @@
#pragma once
#include <utils/Logger.h>
#include <QMap>
#include <QVariant>
#include <QPair>
#include <QVector>
class QSqlDatabase;
class QSqlQuery;
typedef QPair<QString,QVariant> CPair;
typedef QVector<CPair> VectorPair;
///
/// @brief Database interface for SQLite3.
/// Inherit this class to create component specific methods based on this interface
/// Usage: setTable(name) once before you use read/write actions
/// To use another database use setDb(newDB) (defaults to "hyperion")
///
/// Incompatible functions with SQlite3:
/// QSqlQuery::size() returns always -1
///
class DBManager : public QObject
{
Q_OBJECT
public:
DBManager(QObject* parent = nullptr);
~DBManager();
/// set root path
void setRootPath(const QString& rootPath);
/// define the database to work with
void setDB(const QString& dbn);
/// set a table to work with
void setTable(const QString& table);
/// get current database object set with setDB()
QSqlDatabase getDB() const;
///
/// @brief Create a table (if required) with the given columns. Older tables will be updated accordingly with missing columns
/// Does not delete or migrate old columns
/// @param[in] columns The columns of the table. Requires at least one entry!
/// @return True on success else false
///
bool createTable(QStringList& columns) const;
///
/// @brief Create a column if the column already exists returns false and logs error
/// @param[in] column The column of the table
/// @return True on success else false
///
bool createColumn(const QString& column) const;
///
/// @brief Check if at least one record exists in table with the conditions
/// @param[in] conditions The search conditions (WHERE)
/// @return True on success else false
///
bool recordExists(const VectorPair& conditions) const;
///
/// @brief Create a new record in table when the conditions find no existing entry. Add additional key:value pairs in columns
/// DO NOT repeat column keys between 'conditions' and 'columns' as they will be merged on creation
/// @param[in] conditions conditions to search for, as a record may exist and should be updated instead (WHERE)
/// @param[in] columns columns to create or update (optional)
/// @return True on success else false
///
bool createRecord(const VectorPair& conditions, const QVariantMap& columns = QVariantMap()) const;
///
/// @brief Update a record with conditions and additional key:value pairs in columns
/// @param[in] conditions conditions which rows should be updated (WHERE)
/// @param[in] columns columns to update
/// @return True on success else false
///
bool updateRecord(const VectorPair& conditions, const QVariantMap& columns) const;
///
/// @brief Get data of a single record, multiple records are not supported
/// @param[in] conditions condition to search for (WHERE)
/// @param[out] results results of query
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
/// @return True on success else false
///
bool getRecord(const VectorPair& conditions, QVariantMap& results, const QStringList& tColumns = QStringList()) const;
///
/// @brief Get data of multiple records, you need to specify the columns. This search is without conditions. Good to grab all data from db
/// @param[in] conditions condition to search for (WHERE)
/// @param[out] results results of query
/// @param[in] tColumns target columns to search in (optional) if not provided returns all columns
/// @return True on success else false
///
bool getRecords(QVector<QVariantMap>& results, const QStringList& tColumns = QStringList()) const;
///
/// @brief Delete a record determined by conditions
/// @param[in] conditions conditions of the row to delete it (WHERE)
/// @return True on success; on error or not found false
///
bool deleteRecord(const VectorPair& conditions) const;
///
/// @brief Check if table exists in current database
/// @param[in] table The name of the table
/// @return True on success else false
///
bool tableExists(const QString& table) const;
///
/// @brief Delete a table, fails silent (return true) if table is not found
/// @param[in] table The name of the table
/// @return True on success else false
///
bool deleteTable(const QString& table) const;
private:
Logger* _log;
/// databse connection & file name, defaults to hyperion
QString _dbn = "hyperion";
/// table in database
QString _table;
/// addBindValue to query given by QVariantList
void doAddBindValue(QSqlQuery& query, const QVariantList& variants) const;
};

114
include/db/SettingsTable.h Normal file
View File

@@ -0,0 +1,114 @@
#pragma once
// hyperion
#include <db/DBManager.h>
// qt
#include <QDateTime>
#include <QJsonDocument>
///
/// @brief settings table db interface
///
class SettingsTable : public DBManager
{
public:
/// construct wrapper with settings table
SettingsTable(const quint8& instance, QObject* parent = nullptr)
: DBManager(parent)
, _hyperion_inst(instance)
{
setTable("settings");
// create table columns
createTable(QStringList()<<"type TEXT"<<"config TEXT"<<"hyperion_inst INTEGER"<<"updated_at TEXT");
};
~SettingsTable(){};
///
/// @brief Create or update a settings record
/// @param[in] type type of setting
/// @param[in] config The configuration data
/// @return true on success else false
///
inline bool createSettingsRecord(const QString& type, const QString& config) const
{
QVariantMap map;
map["config"] = config;
map["updated_at"] = QDateTime::currentDateTimeUtc().toString(Qt::ISODate);
VectorPair cond;
cond.append(CPair("type",type));
// when a setting is not global we are searching also for the instance
if(!isSettingGlobal(type))
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
return createRecord(cond, map);
}
///
/// @brief Test if record exist, type can be global setting or local (instance)
/// @param[in] type type of setting
/// @param[in] hyperion_inst The instance of hyperion assigned (might be empty)
/// @return true on success else false
///
inline bool recordExist(const QString& type) const
{
VectorPair cond;
cond.append(CPair("type",type));
// when a setting is not global we are searching also for the instance
if(!isSettingGlobal(type))
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
return recordExists(cond);
}
///
/// @brief Get 'config' column of settings entry as QJsonDocument
/// @param[in] type The settings type
/// @return The QJsonDocument
///
inline QJsonDocument getSettingsRecord(const QString& type) const
{
QVariantMap results;
VectorPair cond;
cond.append(CPair("type",type));
// when a setting is not global we are searching also for the instance
if(!isSettingGlobal(type))
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
getRecord(cond, results, QStringList("config"));
return QJsonDocument::fromJson(results["config"].toByteArray());
}
///
/// @brief Get 'config' column of settings entry as QString
/// @param[in] type The settings type
/// @return The QString
///
inline QString getSettingsRecordString(const QString& type) const
{
QVariantMap results;
VectorPair cond;
cond.append(CPair("type",type));
// when a setting is not global we are searching also for the instance
if(!isSettingGlobal(type))
cond.append(CPair("AND hyperion_inst",_hyperion_inst));
getRecord(cond, results, QStringList("config"));
return results["config"].toString();
}
inline bool isSettingGlobal(const QString& type) const
{
// list of global settings
QStringList list;
// server port services
list << "jsonServer" << "protoServer" << "flatbufServer" << "udpListener" << "webConfig" << "network"
// capture
<< "framegrabber" << "grabberV4L2"
// other
<< "logger";
return list.contains(type);
}
private:
const quint8 _hyperion_inst;
};

View File

@@ -9,6 +9,8 @@
class QTcpServer;
class FlatBufferClient;
class NetOrigin;
///
/// @brief A TcpServer to receive images of different formats with Google Flatbuffer
@@ -56,6 +58,7 @@ private:
private:
QTcpServer* _server;
NetOrigin* _netOrigin;
Logger* _log;
int _timeout;
quint16 _port;

View File

@@ -0,0 +1,160 @@
#pragma once
#include <utils/Logger.h>
#include <utils/settings.h>
//qt
#include <QMap>
class AuthTable;
class QTimer;
///
/// @brief Manage the authorization of user and tokens. This class is created once as part of the HyperionDaemon
/// To work with the global instance use AuthManager::getInstance()
///
class AuthManager : public QObject
{
Q_OBJECT
private:
friend class HyperionDaemon;
/// constructor is private, can be called from HyperionDaemon
AuthManager(const QString& rootPath, QObject* parent = 0);
public:
struct AuthDefinition{
QString id;
QString comment;
QObject* caller;
uint64_t timeoutTime;
QString token;
QString lastUse;
};
///
/// @brief Get all available token entries
///
const QVector<AuthDefinition> getTokenList();
///
/// @brief Check authorization is required according to the user setting
/// @return True if authorization required else false
///
const bool & isAuthRequired();
///
/// @brief Check if authorization is required for local network connections
/// @return True if authorization required else false
///
const bool & isLocalAuthRequired();
///
/// @brief Create a new token and skip the usual chain
/// @param comment The comment that should be used for
/// @return The new Auth definition
///
const AuthDefinition createToken(const QString& comment);
///
/// @brief Check if user is authorized
/// @param user The username
/// @param pw The password
/// @return True if authorized else false
///
const bool isUserAuthorized(const QString& user, const QString& pw);
///
/// @brief Check if token is authorized
/// @param token The token
/// @return True if authorized else false
///
const bool isTokenAuthorized(const QString& token);
///
/// @brief Generate a new pending token request with the provided comment and id as identifier helper
/// @param caller The QObject of the caller to deliver the reply
/// @param comment The comment as ident helper
/// @param id The id created by the caller
///
void setNewTokenRequest(QObject* caller, const QString& comment, const QString& id);
///
/// @brief Accept a token request by id, generate token and inform token caller
/// @param id The id of the request
/// @return True on success, false if not found
///
const bool acceptTokenRequest(const QString& id);
///
/// @brief Deny a token request by id, inform the requester
/// @param id The id of the request
/// @return True on success, false if not found
///
const bool denyTokenRequest(const QString& id);
///
/// @brief Get pending requests
/// @return All pending requests
///
const QMap<QString, AuthDefinition> getPendingRequests();
///
/// @brief Delete a token by id
/// @param id The token id
/// @return True on success else false (or not found)
///
const bool deleteToken(const QString& id);
/// Pointer of this instance
static AuthManager* manager;
/// Get Pointer of this instance
static AuthManager* getInstance() { return manager; };
public slots:
///
/// @brief Handle settings update from Hyperion Settingsmanager emit
/// @param type settings type from enum
/// @param config configuration object
///
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
signals:
///
/// @brief Emits whenever a new token Request has been created along with the id and comment
/// @param id The id of the request
/// @param comment The comment of the request
///
void newPendingTokenRequest(const QString& id, const QString& comment);
///
/// @brief Emits when the user has accepted or denied a token
/// @param success If true the request was accepted else false and no token will be available
/// @param caller The origin caller instance who requested this token
/// @param token The new token that is now valid
/// @param comment The comment that was part of the request
/// @param id The id that was part of the request
///
void tokenResponse(const bool& success, QObject* caller, const QString& token, const QString& comment, const QString& id);
private:
/// Database interface for auth table
AuthTable* _authTable;
/// All pending requests
QMap<QString,AuthDefinition> _pendingRequests;
/// Reflect state of global auth
bool _authRequired;
/// Reflect state of local auth
bool _localAuthRequired;
/// Timer for counting against pendingRequest timeouts
QTimer* _timer;
private slots:
///
/// @brief Check timeout of pending requests
///
void checkTimeout();
};

View File

@@ -7,6 +7,7 @@
#include <QJsonObject>
class Hyperion;
class SettingsTable;
///
/// @brief Manage the settings read write from/to config file, on settings changed will emit a signal to update components accordingly
@@ -17,43 +18,38 @@ class SettingsManager : public QObject
public:
///
/// @brief Construct a settings manager and assign a hyperion instance
/// @params hyperion The parent hyperion instance
/// @params instance Instance number of Hyperion
/// @params instance Instance number of Hyperion
/// @params configFile The config file
/// @params hyperion The parent hyperion instance
///
SettingsManager(Hyperion* hyperion, const quint8& instance, const QString& configFile);
///
/// @brief Construct a settings manager for HyperionDaemon
///
SettingsManager(const quint8& instance, const QString& configFile);
~SettingsManager();
SettingsManager(const quint8& instance, const QString& configFile, Hyperion* hyperion = nullptr);
///
/// @brief Save a complete json config
/// @param config The entire config object
/// @param correct If true will correct json against schema before save
/// @return True on success else false
/// @return True on success else false
///
bool saveSettings(QJsonObject config, const bool& correct = false);
///
/// @brief get a single setting json from config
/// @param type The settings::type from enum
/// @return The requested json data as QJsonDocument
/// @param type The settings::type from enum
/// @return The requested json data as QJsonDocument
///
const QJsonDocument getSetting(const settings::type& type);
///
/// @brief get the full settings object of this instance (with global settings)
/// @return The requested json
/// @return The requested json
///
const QJsonObject & getSettings() { return _qconfig; };
signals:
///
/// @brief Emits whenever a config part changed.
/// @param type The settings type from enum
/// @param data The data as QJsonDocument
/// @param type The settings type from enum
/// @param data The data as QJsonDocument
///
void settingsChanged(const settings::type& type, const QJsonDocument& data);
@@ -64,6 +60,9 @@ private:
/// Logger instance
Logger* _log;
/// instance of database table interface
SettingsTable* _sTable;
/// the schema
static QJsonObject schemaJson;

View File

@@ -9,6 +9,7 @@
class QTcpServer;
class ProtoClientConnection;
class NetOrigin;
///
/// @brief This class creates a TCP server which accepts connections wich can then send
@@ -58,6 +59,7 @@ private:
private:
QTcpServer* _server;
NetOrigin* _netOrigin;
Logger* _log;
int _timeout;
quint16 _port;

View File

@@ -18,6 +18,7 @@
class BonjourServiceRegister;
class QUdpSocket;
class NetOrigin;
///
/// This class creates a UDP server which accepts connections from boblight clients.
@@ -106,4 +107,7 @@ private:
QHostAddress _listenAddress;
uint16_t _listenPort;
QAbstractSocket::BindFlag _bondage;
/// Check Network Origin
NetOrigin* _netOrigin;
};

54
include/utils/NetOrigin.h Normal file
View File

@@ -0,0 +1,54 @@
#pragma once
// qt
#include <QHostAddress>
#include <QJsonArray>
// utils
#include <utils/Logger.h>
#include <utils/settings.h>
///
/// @brief Checks the origin ip addresses for access allowed
///
class NetOrigin : public QObject
{
Q_OBJECT
private:
friend class HyperionDaemon;
NetOrigin(QObject* parent = 0, Logger* log = Logger::getInstance("NETWORK"));
public:
///
/// @brief Check if address is allowed to connect. The local address is the network adapter ip this connection comes from
/// @param address The peer address
/// @param local The local address of the socket (Differs based on NetworkAdapter IP or localhost)
/// @return True when allowed, else false
///
bool accessAllowed(const QHostAddress& address, const QHostAddress& local);
///
/// @brief Check if address is in subnet of local
/// @return True or false
///
bool isLocalAddress(const QHostAddress& address, const QHostAddress& local);
static NetOrigin* getInstance(){ return instance; };
static NetOrigin* instance;
private slots:
///
/// @brief Handle settings update from SettingsManager
/// @param type settingyType from enum
/// @param config configuration object
///
void handleSettingsUpdate(const settings::type& type, const QJsonDocument& config);
private:
Logger* _log;
/// True when internet access is allowed
bool _internetAccessAllowed;
/// Whitelisted ip addresses
QList<QHostAddress> _ipWhitelist;
};

View File

@@ -29,7 +29,7 @@ public:
quint16 getPort() { return _port; };
/// check if server has been inited
bool isInited() { return _inited; };
const bool isInited() { return _inited; };
///
/// @brief Set a new description, if empty the description is NotFound for clients

View File

@@ -1,70 +0,0 @@
#pragma once
#include <utils/Logger.h>
#include "webserver/WebSocketUtils.h"
#include <QJsonObject>
class QTcpSocket;
class JsonAPI;
class WebSocketClient : public QObject {
Q_OBJECT
public:
WebSocketClient(QByteArray socketKey, QTcpSocket* sock, QObject* parent);
struct WebSocketHeader
{
bool fin;
quint8 opCode;
bool masked;
quint64 payloadLength;
char key[4];
};
private:
QTcpSocket* _socket;
QByteArray _secWebSocketKey;
Logger* _log;
JsonAPI* _jsonAPI;
void getWsFrameHeader(WebSocketHeader* header);
void sendClose(int status, QString reason = "");
// void handleBinaryMessage(QByteArray &data);
qint64 sendMessage_Raw(const char* data, quint64 size);
qint64 sendMessage_Raw(QByteArray &data);
QByteArray makeFrameHeader(quint8 opCode, quint64 payloadLength, bool lastFrame);
/// The buffer used for reading data from the socket
QByteArray _receiveBuffer;
/// buffer for websockets multi frame receive
QByteArray _wsReceiveBuffer;
quint8 _maskKey[4];
bool _onContinuation = false;
// true when data is missing for parsing
bool _notEnoughData = false;
// websocket header store
WebSocketHeader _wsh;
// masks for fields in the basic header
static uint8_t const BHB0_OPCODE = 0x0F;
static uint8_t const BHB0_RSV3 = 0x10;
static uint8_t const BHB0_RSV2 = 0x20;
static uint8_t const BHB0_RSV1 = 0x40;
static uint8_t const BHB0_FIN = 0x80;
static uint8_t const BHB1_PAYLOAD = 0x7F;
static uint8_t const BHB1_MASK = 0x80;
static uint8_t const payload_size_code_16bit = 0x7E; // 126
static uint8_t const payload_size_code_64bit = 0x7F; // 127
static const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //maximum size of a frame when sending a message
private slots:
void handleWebSocketFrame(void);
qint64 sendMessage(QJsonObject obj);
};

View File

@@ -1,68 +0,0 @@
#pragma once
/// Constants and utility functions related to WebSocket opcodes
/**
* WebSocket Opcodes are 4 bits. See RFC6455 section 5.2.
*/
namespace OPCODE {
enum value {
CONTINUATION = 0x0,
TEXT = 0x1,
BINARY = 0x2,
RSV3 = 0x3,
RSV4 = 0x4,
RSV5 = 0x5,
RSV6 = 0x6,
RSV7 = 0x7,
CLOSE = 0x8,
PING = 0x9,
PONG = 0xA,
CONTROL_RSVB = 0xB,
CONTROL_RSVC = 0xC,
CONTROL_RSVD = 0xD,
CONTROL_RSVE = 0xE,
CONTROL_RSVF = 0xF
};
/// Check if an opcode is reserved
/**
* @param v The opcode to test.
* @return Whether or not the opcode is reserved.
*/
inline bool reserved(value v) {
return (v >= RSV3 && v <= RSV7) || (v >= CONTROL_RSVB && v <= CONTROL_RSVF);
}
/// Check if an opcode is invalid
/**
* Invalid opcodes are negative or require greater than 4 bits to store.
*
* @param v The opcode to test.
* @return Whether or not the opcode is invalid.
*/
inline bool invalid(value v) {
return (v > 0xF || v < 0);
}
/// Check if an opcode is for a control frame
/**
* @param v The opcode to test.
* @return Whether or not the opcode is a control opcode.
*/
inline bool is_control(value v) {
return v >= 0x8;
}
}
namespace CLOSECODE {
enum value {
NORMAL = 1000,
AWAY = 1001,
TERM = 1002,
INV_TYPE = 1003,
INV_DATA = 1007,
VIOLATION = 1008,
BIG_MSG = 1009,
UNEXPECTED= 1011
};
}