mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Replaced qhttpserver with libmicrohttp.
The qhttpserver seems to be riddled with memory leaks and was continuously crashing. I don't know Qt well enough to fix it so I have replaced it with libmicrohttp. This is not nearly as elegant but it is much more stable.
This commit is contained in:
parent
2cd6787141
commit
b27ba03d42
@ -172,6 +172,8 @@ if(NOT (${GCRYPT_VERSION_STRING} VERSION_LESS "1.6.0"))
|
||||
set(GCRYPT_HAS_SALSA20 1)
|
||||
endif()
|
||||
|
||||
find_package(LibMicroHTTPD REQUIRED)
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
|
9
cmake/FindLibMicroHTTPD.cmake
Normal file
9
cmake/FindLibMicroHTTPD.cmake
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
find_path(MHD_INCLUDE_DIR microhttpd.h)
|
||||
|
||||
find_library(MHD_LIBRARIES microhttpd)
|
||||
|
||||
mark_as_advanced(MHD_LIBRARIES MHD_INCLUDE_DIR)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LibMicroHTTPD DEFAULT_MSG MHD_LIBRARIES MHD_INCLUDE_DIR)
|
@ -237,7 +237,6 @@ add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
||||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
||||
|
||||
add_subdirectory(gui/qocoa)
|
||||
add_subdirectory(http/qhttpserver)
|
||||
add_subdirectory(http/qjson)
|
||||
|
||||
add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE})
|
||||
@ -245,7 +244,7 @@ target_link_libraries(${PROGNAME}
|
||||
keepassx_core
|
||||
Qocoa
|
||||
qjson
|
||||
qhttpserver
|
||||
${MHD_LIBRARIES}
|
||||
${QT_QTCORE_LIBRARY}
|
||||
${QT_QTGUI_LIBRARY}
|
||||
${QT_QTNETWORK_LIBRARY}
|
||||
|
@ -42,17 +42,19 @@
|
||||
|
||||
class HttpPlugin: public ISettingsPage {
|
||||
public:
|
||||
HttpPlugin(DatabaseTabWidget * tabWidget): m_service(new Service(tabWidget)) {
|
||||
HttpPlugin(DatabaseTabWidget * tabWidget) {
|
||||
m_service = new Service(tabWidget);
|
||||
}
|
||||
virtual ~HttpPlugin() {
|
||||
//delete m_service;
|
||||
}
|
||||
virtual QString name() {
|
||||
return QObject::tr("Http");
|
||||
}
|
||||
virtual QWidget * createWidget() {
|
||||
OptionDialog * dlg = new OptionDialog();
|
||||
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service.data(), SLOT(removeSharedEncryptionKeys()));
|
||||
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service.data(), SLOT(removeStoredPermissions()));
|
||||
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys()));
|
||||
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions()));
|
||||
return dlg;
|
||||
}
|
||||
virtual void loadSettings(QWidget * widget) {
|
||||
@ -66,7 +68,7 @@ public:
|
||||
m_service->stop();
|
||||
}
|
||||
private:
|
||||
QScopedPointer<Service> m_service;
|
||||
Service *m_service;
|
||||
};
|
||||
|
||||
const QString MainWindow::BaseWindowTitle = "KeePassX";
|
||||
|
@ -12,37 +12,17 @@
|
||||
*/
|
||||
|
||||
#include "Server.h"
|
||||
#include "qhttpserver/qhttpserver.h"
|
||||
#include "qhttpserver/qhttprequest.h"
|
||||
#include "qhttpserver/qhttpresponse.h"
|
||||
#include <microhttpd.h>
|
||||
#include "Protocol.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/QCryptographicHash>
|
||||
#include <QtGui/QMessageBox>
|
||||
#include <QEventLoop>
|
||||
|
||||
using namespace KeepassHttpProtocol;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/// Request
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RequestHandler::RequestHandler(QHttpRequest *request, QHttpResponse *response): m_request(request), m_response(response)
|
||||
{
|
||||
m_request->storeBody();
|
||||
connect(m_request, SIGNAL(end()), this, SLOT(onEnd()));
|
||||
connect(m_response, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||
}
|
||||
|
||||
RequestHandler::~RequestHandler()
|
||||
{
|
||||
delete m_request;
|
||||
}
|
||||
|
||||
void RequestHandler::onEnd()
|
||||
{
|
||||
Q_EMIT requestComplete(m_request, m_response);
|
||||
}
|
||||
using namespace KeepassHttpProtocol;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -51,71 +31,14 @@ void RequestHandler::onEnd()
|
||||
|
||||
Server::Server(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_httpServer(new QHttpServer(this)),
|
||||
m_started(false)
|
||||
{
|
||||
connect(m_httpServer, SIGNAL(newRequest(QHttpRequest*,QHttpResponse*)), this, SLOT(handleRequest(QHttpRequest*,QHttpResponse*)));
|
||||
connect(this, SIGNAL(emitRequest(const QByteArray, QByteArray*)),
|
||||
this, SLOT(handleRequest(const QByteArray, QByteArray*)));
|
||||
connect(this, SIGNAL(emitOpenDatabase(bool*)),
|
||||
this, SLOT(handleOpenDatabase(bool*)));
|
||||
}
|
||||
|
||||
void Server::start()
|
||||
{
|
||||
if (m_started)
|
||||
return;
|
||||
|
||||
static const int PORT = 19455;
|
||||
m_started = m_httpServer->listen(QHostAddress::LocalHost, PORT)
|
||||
|| m_httpServer->listen(QHostAddress::LocalHostIPv6, PORT);
|
||||
}
|
||||
|
||||
void Server::stop()
|
||||
{
|
||||
if (!m_started)
|
||||
return;
|
||||
m_httpServer->close();
|
||||
m_started = false;
|
||||
}
|
||||
|
||||
void Server::handleRequest(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
RequestHandler * h = new RequestHandler(request, response);
|
||||
connect(h, SIGNAL(requestComplete(QHttpRequest*,QHttpResponse*)), this, SLOT(handleRequestComplete(QHttpRequest*,QHttpResponse*)));
|
||||
}
|
||||
|
||||
void Server::handleRequestComplete(QHttpRequest *request, QHttpResponse *response)
|
||||
{
|
||||
Request r;
|
||||
if (!isDatabaseOpened() && !openDatabase()) {
|
||||
response->writeHead(QHttpResponse::STATUS_SERVICE_UNAVAILABLE);
|
||||
response->end();
|
||||
} else if (request->header("content-type").compare("application/json", Qt::CaseInsensitive) == 0 &&
|
||||
r.fromJson(request->body())) {
|
||||
|
||||
QByteArray hash = QCryptographicHash::hash((getDatabaseRootUuid() + getDatabaseRecycleBinUuid()).toUtf8(),
|
||||
QCryptographicHash::Sha1).toHex();
|
||||
|
||||
Response protocolResp(r, QString::fromAscii(hash));
|
||||
switch(r.requestType()) {
|
||||
case INVALID: break;
|
||||
case TEST_ASSOCIATE: testAssociate(r, &protocolResp); break;
|
||||
case ASSOCIATE: associate(r, &protocolResp); break;
|
||||
case GET_LOGINS: getLogins(r, &protocolResp); break;
|
||||
case GET_LOGINS_COUNT: getLoginsCount(r, &protocolResp); break;
|
||||
case GET_ALL_LOGINS: getAllLogins(r, &protocolResp); break;
|
||||
case SET_LOGIN: setLogin(r, &protocolResp); break;
|
||||
case GENERATE_PASSWORD: generatePassword(r, &protocolResp); break;
|
||||
}
|
||||
|
||||
QByteArray s = protocolResp.toJson().toUtf8();
|
||||
response->setHeader("Content-Type", "application/json");
|
||||
response->setHeader("Content-Length", QString::number(s.size()));
|
||||
response->writeHead(QHttpResponse::STATUS_OK);
|
||||
response->write(s);
|
||||
response->end();
|
||||
} else {
|
||||
response->writeHead(QHttpResponse::STATUS_BAD_REQUEST);
|
||||
response->end();
|
||||
}
|
||||
}
|
||||
|
||||
void Server::testAssociate(const Request& r, Response * protocolResp)
|
||||
{
|
||||
@ -202,6 +125,39 @@ void Server::setLogin(const Request &r, Response *protocolResp)
|
||||
protocolResp->setVerifier(key);
|
||||
}
|
||||
|
||||
|
||||
int Server::send_response(struct MHD_Connection *connection, const char *page)
|
||||
{
|
||||
int ret;
|
||||
struct MHD_Response *response;
|
||||
|
||||
response = MHD_create_response_from_buffer(
|
||||
strlen(page), static_cast<void*>(const_cast<char*>(page)),
|
||||
MHD_RESPMEM_PERSISTENT);
|
||||
if (!response) return MHD_NO;
|
||||
|
||||
MHD_add_response_header (response, "Content-Type", "application/json");
|
||||
ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
|
||||
MHD_destroy_response (response);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int Server::send_unavailable(struct MHD_Connection *connection)
|
||||
{
|
||||
int ret;
|
||||
struct MHD_Response *response;
|
||||
|
||||
response = MHD_create_response_from_buffer(0, NULL, MHD_RESPMEM_PERSISTENT);
|
||||
if (!response) return MHD_NO;
|
||||
|
||||
ret = MHD_queue_response (connection, MHD_HTTP_SERVICE_UNAVAILABLE, response);
|
||||
MHD_destroy_response (response);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Server::generatePassword(const Request &r, Response *protocolResp)
|
||||
{
|
||||
QString key = getKey(r.id());
|
||||
@ -217,3 +173,153 @@ void Server::generatePassword(const Request &r, Response *protocolResp)
|
||||
|
||||
memset(password.data(), 0, password.length());
|
||||
}
|
||||
|
||||
|
||||
int Server::request_handler_wrapper(void *me, struct MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size, void **con_cls)
|
||||
{
|
||||
Server *myself = static_cast<Server*>(me);
|
||||
|
||||
if (myself)
|
||||
return myself->request_handler(connection, url, method, version,
|
||||
upload_data, upload_data_size, con_cls);
|
||||
else
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
|
||||
void Server::handleRequest(const QByteArray in, QByteArray *out)
|
||||
{
|
||||
*out = QByteArray();
|
||||
|
||||
Request r;
|
||||
if (!r.fromJson(in))
|
||||
return;
|
||||
|
||||
QByteArray hash = QCryptographicHash::hash(
|
||||
(getDatabaseRootUuid() + getDatabaseRecycleBinUuid()).toUtf8(),
|
||||
QCryptographicHash::Sha1).toHex();
|
||||
|
||||
Response protocolResp(r, QString::fromAscii(hash));
|
||||
switch(r.requestType()) {
|
||||
case INVALID: break;
|
||||
case TEST_ASSOCIATE: testAssociate(r, &protocolResp); break;
|
||||
case ASSOCIATE: associate(r, &protocolResp); break;
|
||||
case GENERATE_PASSWORD: generatePassword(r, &protocolResp); break;
|
||||
case GET_LOGINS: getLogins(r, &protocolResp); break;
|
||||
case GET_LOGINS_COUNT: getLoginsCount(r, &protocolResp); break;
|
||||
case GET_ALL_LOGINS: getAllLogins(r, &protocolResp); break;
|
||||
case SET_LOGIN: setLogin(r, &protocolResp); break;
|
||||
}
|
||||
|
||||
*out = protocolResp.toJson().toUtf8();
|
||||
Q_EMIT donewrk();
|
||||
}
|
||||
|
||||
|
||||
void Server::handleOpenDatabase(bool *success)
|
||||
{
|
||||
*success = openDatabase();
|
||||
Q_EMIT donewrk();
|
||||
}
|
||||
|
||||
|
||||
int Server::request_handler(struct MHD_Connection *connection,
|
||||
const char *, const char *method, const char *,
|
||||
const char *upload_data, size_t *upload_data_size, void **con_cls)
|
||||
{
|
||||
struct Server::connection_info_struct *con_info =
|
||||
static_cast<struct Server::connection_info_struct*>(*con_cls);
|
||||
|
||||
if (!isDatabaseOpened()) {
|
||||
bool success;
|
||||
QEventLoop loop1;
|
||||
loop1.connect(this, SIGNAL(donewrk()), SLOT(quit()));
|
||||
Q_EMIT emitOpenDatabase(&success);
|
||||
loop1.exec();
|
||||
|
||||
if (!success)
|
||||
return send_unavailable(connection);
|
||||
}
|
||||
|
||||
if (con_info == NULL) {
|
||||
*con_cls = calloc(1, sizeof(*con_info));
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
if (strcmp (method, MHD_HTTP_METHOD_POST) != 0)
|
||||
return MHD_NO;
|
||||
|
||||
if (*upload_data_size == 0) {
|
||||
if (con_info && con_info->response)
|
||||
return send_response(connection, con_info->response);
|
||||
else
|
||||
return MHD_NO;
|
||||
}
|
||||
|
||||
QString type = MHD_lookup_connection_value(connection,
|
||||
MHD_HEADER_KIND, "Content-Type");
|
||||
if (!type.contains("application/json", Qt::CaseInsensitive))
|
||||
return MHD_NO;
|
||||
|
||||
// Now process the POST request
|
||||
|
||||
QByteArray post = QByteArray(upload_data, *upload_data_size);
|
||||
|
||||
QByteArray s;
|
||||
QEventLoop loop;
|
||||
loop.connect(this, SIGNAL(donewrk()), SLOT(quit()));
|
||||
Q_EMIT emitRequest(post, &s);
|
||||
loop.exec();
|
||||
|
||||
if (s.size() == 0)
|
||||
return MHD_NO;
|
||||
|
||||
con_info->response = static_cast<char*>(calloc(1, s.size()+1));
|
||||
memcpy(con_info->response, s.data(), s.size());
|
||||
|
||||
*upload_data_size = 0;
|
||||
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
|
||||
void Server::request_completed(void *, struct MHD_Connection *,
|
||||
void **con_cls, enum MHD_RequestTerminationCode)
|
||||
{
|
||||
struct Server::connection_info_struct *con_info =
|
||||
static_cast<struct Server::connection_info_struct*>(*con_cls);
|
||||
|
||||
if (con_info == NULL)
|
||||
return;
|
||||
|
||||
if (con_info->response) free(con_info->response);
|
||||
free(con_info);
|
||||
*con_cls = NULL;
|
||||
}
|
||||
|
||||
|
||||
void Server::start(void)
|
||||
{
|
||||
if (m_started) return;
|
||||
|
||||
static const int PORT = 19455;
|
||||
|
||||
daemon = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, PORT, NULL, NULL,
|
||||
&this->request_handler_wrapper, this,
|
||||
MHD_OPTION_NOTIFY_COMPLETED,
|
||||
this->request_completed, NULL,
|
||||
MHD_OPTION_END);
|
||||
m_started = true;
|
||||
}
|
||||
|
||||
|
||||
void Server::stop(void)
|
||||
{
|
||||
if (!m_started)
|
||||
return;
|
||||
|
||||
MHD_stop_daemon(daemon);
|
||||
m_started = false;
|
||||
}
|
||||
|
@ -16,10 +16,8 @@
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QList>
|
||||
#include <microhttpd.h>
|
||||
|
||||
class QHttpServer;
|
||||
class QHttpRequest;
|
||||
class QHttpResponse;
|
||||
|
||||
namespace KeepassHttpProtocol {
|
||||
|
||||
@ -27,24 +25,6 @@ class Request;
|
||||
class Response;
|
||||
class Entry;
|
||||
|
||||
class RequestHandler: public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
RequestHandler(QHttpRequest *request, QHttpResponse *response);
|
||||
~RequestHandler();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onEnd();
|
||||
|
||||
Q_SIGNALS:
|
||||
void requestComplete(QHttpRequest *request, QHttpResponse *response);
|
||||
|
||||
private:
|
||||
QHttpRequest * m_request;
|
||||
QHttpResponse *m_response;
|
||||
};
|
||||
|
||||
class Server : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
@ -70,8 +50,13 @@ public Q_SLOTS:
|
||||
void stop();
|
||||
|
||||
private Q_SLOTS:
|
||||
void handleRequest(QHttpRequest * request, QHttpResponse* response);
|
||||
void handleRequestComplete(QHttpRequest * request, QHttpResponse* response);
|
||||
void handleRequest(const QByteArray in, QByteArray *out);
|
||||
void handleOpenDatabase(bool *success);
|
||||
|
||||
Q_SIGNALS:
|
||||
void emitRequest(const QByteArray in, QByteArray *out);
|
||||
void emitOpenDatabase(bool *success);
|
||||
void donewrk();
|
||||
|
||||
private:
|
||||
void testAssociate(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
|
||||
@ -82,8 +67,25 @@ private:
|
||||
void setLogin(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
|
||||
void generatePassword(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp);
|
||||
|
||||
QHttpServer * const m_httpServer;
|
||||
static int request_handler_wrapper(void *me,
|
||||
struct MHD_Connection *connection,
|
||||
const char *url, const char *method, const char *version,
|
||||
const char *upload_data, size_t *upload_data_size, void **con_cls);
|
||||
static void request_completed(void *, struct MHD_Connection *,
|
||||
void **con_cls, enum MHD_RequestTerminationCode);
|
||||
|
||||
int request_handler(struct MHD_Connection *connection,
|
||||
const char *, const char *method, const char *,
|
||||
const char *upload_data, size_t *upload_data_size, void **con_cls);
|
||||
int send_response(struct MHD_Connection *connection, const char *page);
|
||||
int send_unavailable(struct MHD_Connection *connection);
|
||||
|
||||
bool m_started;
|
||||
struct MHD_Daemon *daemon;
|
||||
|
||||
struct connection_info_struct {
|
||||
char *response;
|
||||
};
|
||||
};
|
||||
|
||||
} /*namespace KeepassHttpProtocol*/
|
||||
|
Loading…
Reference in New Issue
Block a user