diff --git a/CMakeLists.txt b/CMakeLists.txt index a45933562..188873b95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,22 +45,14 @@ set(WITH_XC_ALL OFF CACHE BOOLEAN "Build in all available plugins") option(WITH_XC_AUTOTYPE "Include Auto-Type." ON) option(WITH_XC_NETWORKING "Include networking code (e.g. for downlading website icons)." OFF) option(WITH_XC_BROWSER "Include browser integration with keepassxc-browser." OFF) -option(WITH_XC_HTTP "Include KeePassHTTP-compatible browser integration (deprecated, implies WITH_NETWORKING)." OFF) option(WITH_XC_YUBIKEY "Include YubiKey support." OFF) option(WITH_XC_SSHAGENT "Include SSH agent support." OFF) -if(WITH_XC_HTTP) - message(WARNING "KeePassHTTP support has been deprecated and will be removed in a future version. Please use WITH_XC_BROWSER instead!\n" - "For enabling / disabling network access code, WITH_XC_HTTP has been replaced by WITH_XC_NETWORKING.") - set(WITH_XC_NETWORKING ON CACHE BOOL "Include networking code (e.g. for downlading website icons)." FORCE) -endif() - if(WITH_XC_ALL) # Enable all options set(WITH_XC_AUTOTYPE ON) set(WITH_XC_NETWORKING ON) set(WITH_XC_BROWSER ON) - set(WITH_XC_HTTP ON) # Deprecated set(WITH_XC_YUBIKEY ON) set(WITH_XC_SSHAGENT ON) endif() diff --git a/COPYING b/COPYING index 195650551..8095ef067 100644 --- a/COPYING +++ b/COPYING @@ -226,10 +226,6 @@ Files: src/zxcvbn/zxcvbn.* Copyright: 2015-2017, Tony Evans License: MIT -Files: src/http/qhttp/* -Copyright: 2014, Amir Zamani -License: MIT - Files: src/gui/KMessageWidget.h src/gui/KMessageWidget.cpp Copyright: 2011 Aurélien Gâteau diff --git a/INSTALL.md b/INSTALL.md index 41a948b57..1a23397d3 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -93,10 +93,12 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct ``` -DWITH_XC_AUTOTYPE=[ON|OFF] Enable/Disable Auto-Type (default: ON) - -DWITH_XC_HTTP=[ON|OFF] Enable/Disable KeePassHTTP and custom icon downloads (default: OFF) -DWITH_XC_YUBIKEY=[ON|OFF] Enable/Disable YubiKey HMAC-SHA1 authentication support (default: OFF) -DWITH_XC_BROWSER=[ON|OFF] Enable/Disable KeePassXC-Browser extension support (default: OFF) - + -DWITH_XC_NETWORKING=[ON|OFF] Enable/Disable Networking support (favicon download) (default: OFF) + + -DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (default: OFF) + -DWITH_TESTS=[ON|OFF] Enable/Disable building of unit tests (default: ON) -DWITH_GUI_TESTS=[ON|OFF] Enable/Disable building of GUI tests (default: OFF) -DWITH_DEV_BUILD=[ON|OFF] Enable/Disable deprecated method warnings (default: OFF) diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md index b4b2d38ca..8b694888e 100644 --- a/docs/QUICKSTART.md +++ b/docs/QUICKSTART.md @@ -24,9 +24,10 @@ for all your websites, programs, etc. ## Setting up Browser Integration with KeePassXC * *Within KeePassXC*, go to **Tools->Settings** (on macOS, go to **KeePassXC->Preferences**.) -* In **Browser Integration**, check **Enable KeePassHTTP server** +* In **Browser Integration**, check **Enable KeePassXC browser integration** +* Right below that, click the checkbox for the browser(s) you use Leave the other options at their defaults. -* *In your default web browser,* install the KeePassHTTP-Connector extension/add-on. Instructions for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepasshttp-connector/?src=api) or [Chrome](https://chrome.google.com/webstore/detail/keepasshttp-connector/dafgdjggglmmknipkhngniifhplpcldb?utm_source=chrome-app-launcher-info-dialog) +* *In your default web browser,* install the KeePassXC Browser extension/add-on. Instructions for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) or [Chrome](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) * Click the KeePassXC icon in the upper-right corner. You'll see the dialog below. * Click the blue Connect button to make the browser extension connect to the KeePassXC application. KeePassXC Connect dialog diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b3c7a650f..747848c5e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -197,11 +197,9 @@ set(keepassx_SOURCES_MAINEXE add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing") add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)") add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser") -add_feature_info(KeePassHTTP WITH_XC_HTTP "Browser integration compatible with ChromeIPass and PassIFox (deprecated, implies Networking)") add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent") add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response") -add_subdirectory(http) if(WITH_XC_NETWORKING) find_package(CURL REQUIRED) endif() @@ -254,7 +252,6 @@ add_library(keepassx_core STATIC ${keepassx_SOURCES}) set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE) target_link_libraries(keepassx_core autotype - ${keepassxchttp_LIB} ${keepassxcbrowser_LIB} ${sshagent_LIB} Qt5::Core diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index 9f3952b04..8bbea9eae 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -15,7 +15,6 @@ #cmakedefine WITH_XC_AUTOTYPE #cmakedefine WITH_XC_NETWORKING #cmakedefine WITH_XC_BROWSER -#cmakedefine WITH_XC_HTTP #cmakedefine WITH_XC_YUBIKEY #cmakedefine WITH_XC_SSHAGENT diff --git a/src/gui/AboutDialog.cpp b/src/gui/AboutDialog.cpp index 10f42cd79..73d8ec553 100644 --- a/src/gui/AboutDialog.cpp +++ b/src/gui/AboutDialog.cpp @@ -87,9 +87,6 @@ AboutDialog::AboutDialog(QWidget* parent) #ifdef WITH_XC_BROWSER extensions += "\n- " + tr("Browser Integration"); #endif -#ifdef WITH_XC_HTTP - extensions += "\n- " + tr("Legacy Browser Integration (KeePassHTTP)"); -#endif #ifdef WITH_XC_SSHAGENT extensions += "\n- " + tr("SSH Agent"); #endif diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 1465cf872..8549d8f82 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -39,12 +39,6 @@ #include "gui/MessageBox.h" #include "gui/SearchWidget.h" -#ifdef WITH_XC_HTTP -#include "http/Service.h" -#include "http/HttpSettings.h" -#include "http/OptionDialog.h" -#endif - #ifdef WITH_XC_SSHAGENT #include "sshagent/AgentSettingsPage.h" #include "sshagent/SSHAgent.h" @@ -65,53 +59,6 @@ #include "gui/SettingsWidget.h" #include "gui/PasswordGeneratorWidget.h" -#ifdef WITH_XC_HTTP -class HttpPlugin: public ISettingsPage -{ -public: - HttpPlugin(DatabaseTabWidget* tabWidget) - { - m_service = new Service(tabWidget); - } - - ~HttpPlugin() = default; - - QString name() override - { - return QObject::tr("Legacy Browser Integration"); - } - - QIcon icon() override - { - return FilePath::instance()->icon("apps", "internet-web-browser"); - } - - QWidget * createWidget() override - { - OptionDialog* dlg = new OptionDialog(); - QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys())); - QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions())); - return dlg; - } - - void loadSettings(QWidget* widget) override - { - qobject_cast(widget)->loadSettings(); - } - - void saveSettings(QWidget* widget) override - { - qobject_cast(widget)->saveSettings(); - if (HttpSettings::isEnabled()) - m_service->start(); - else - m_service->stop(); - } -private: - Service* m_service; -}; -#endif - #ifdef WITH_XC_BROWSER class BrowserPlugin: public ISettingsPage { @@ -193,9 +140,6 @@ MainWindow::MainWindow() #ifdef WITH_XC_BROWSER m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget)); #endif -#ifdef WITH_XC_HTTP - m_ui->settingsWidget->addSettingsPage(new HttpPlugin(m_ui->tabWidget)); -#endif #ifdef WITH_XC_SSHAGENT SSHAgent::init(this); connect(SSHAgent::instance(), SIGNAL(error(QString)), this, SLOT(showErrorMessage(QString))); @@ -419,21 +363,12 @@ MainWindow::MainWindow() m_ui->globalMessageWidget->showMessage( tr("Access error for config file %1").arg(config()->getFileName()), MessageWidget::Error); } -#ifdef WITH_XC_HTTP - if (config()->get("Http/Enabled", false).toBool() && config()->get("Http/DeprecationNoticeShown", 0).toInt() < 3) { - // show message after global widget dismissed all messages - connect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), this, SLOT(showKeePassHTTPDeprecationNotice())); - } -#endif #ifndef KEEPASSXC_BUILD_TYPE_RELEASE m_ui->globalMessageWidget->showMessage(tr("WARNING: You are using an unstable build of KeePassXC!\n" "There is a high risk of corruption, maintain a backup of your databases.\n" "This version is not meant for production use."), MessageWidget::Warning, -1); -#else - // Show the HTTP deprecation message if enabled above - emit m_ui->globalMessageWidget->hideAnimationFinished(); #endif } @@ -441,20 +376,6 @@ MainWindow::~MainWindow() { } -void MainWindow::showKeePassHTTPDeprecationNotice() -{ - int warningNum = config()->get("Http/DeprecationNoticeShown", 0).toInt(); - displayGlobalMessage(tr("

It looks like you are using KeePassHTTP for browser integration. " - "This feature has been deprecated and will be removed in the future.
" - "Please switch to KeePassXC-Browser instead! For help with migration, " - "visit our " - "migration guide (warning %1 of 3).

").arg(warningNum + 1), - MessageWidget::Warning, true, -1); - - config()->set("Http/DeprecationNoticeShown", warningNum + 1); - disconnect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), this, SLOT(showKeePassHTTPDeprecationNotice())); -} - void MainWindow::showErrorMessage(const QString& message) { m_ui->globalMessageWidget->showMessage(message, MessageWidget::Error); diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 20e548910..5ce695234 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -103,7 +103,6 @@ private slots: void repairDatabase(); void hideTabMessage(); void handleScreenLock(); - void showKeePassHTTPDeprecationNotice(); void showErrorMessage(const QString& message); private: diff --git a/src/http/AccessControlDialog.cpp b/src/http/AccessControlDialog.cpp deleted file mode 100644 index 30ab3f21c..000000000 --- a/src/http/AccessControlDialog.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "AccessControlDialog.h" -#include "ui_AccessControlDialog.h" -#include "core/Entry.h" - -AccessControlDialog::AccessControlDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::AccessControlDialog()) -{ - this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - - ui->setupUi(this); - connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); - connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); -} - -AccessControlDialog::~AccessControlDialog() -{ -} - -void AccessControlDialog::setUrl(const QString &url) -{ - ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" - "Please select whether you want to allow access.")).arg(QUrl(url).host())); -} - -void AccessControlDialog::setItems(const QList &items) -{ - for (Entry* entry: items) { - QString title = entry->resolveMultiplePlaceholders(entry->title()); - QString username = entry->resolveMultiplePlaceholders(entry->username()); - ui->itemsList->addItem(title + " - " + username); - } -} - -bool AccessControlDialog::remember() const -{ - return ui->rememberDecisionCheckBox->isChecked(); -} - -void AccessControlDialog::setRemember(bool r) -{ - ui->rememberDecisionCheckBox->setChecked(r); -} diff --git a/src/http/AccessControlDialog.h b/src/http/AccessControlDialog.h deleted file mode 100644 index 76392eff1..000000000 --- a/src/http/AccessControlDialog.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef ACCESSCONTROLDIALOG_H -#define ACCESSCONTROLDIALOG_H - -#include -#include - -class Entry; - -namespace Ui { -class AccessControlDialog; -} - -class AccessControlDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AccessControlDialog(QWidget *parent = nullptr); - ~AccessControlDialog(); - - void setUrl(const QString & url); - void setItems(const QList & items); - bool remember() const; - void setRemember(bool r); - -private: - QScopedPointer ui; -}; - -#endif // ACCESSCONTROLDIALOG_H diff --git a/src/http/AccessControlDialog.ui b/src/http/AccessControlDialog.ui deleted file mode 100644 index 37d477eee..000000000 --- a/src/http/AccessControlDialog.ui +++ /dev/null @@ -1,69 +0,0 @@ - - - AccessControlDialog - - - - 0 - 0 - 400 - 221 - - - - KeePassXC HTTP Confirm Access - - - - - - - - - - - - - - - - Remember this decision - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Allow - - - - - - - Deny - - - - - - - - - - diff --git a/src/http/CMakeLists.txt b/src/http/CMakeLists.txt deleted file mode 100644 index 962f78c36..000000000 --- a/src/http/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -if(WITH_XC_HTTP) - add_subdirectory(qhttp) - - include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - - set(keepasshttp_SOURCES - AccessControlDialog.cpp - EntryConfig.cpp - HttpPasswordGeneratorWidget.cpp - HttpSettings.cpp - OptionDialog.cpp - Protocol.cpp - Server.cpp - Service.cpp - ) - - add_library(keepasshttp STATIC ${keepasshttp_SOURCES}) - target_link_libraries(keepasshttp PUBLIC qhttp Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network) - - set(keepassxchttp_LIB keepasshttp PARENT_SCOPE) -endif() diff --git a/src/http/EntryConfig.cpp b/src/http/EntryConfig.cpp deleted file mode 100644 index 309afafac..000000000 --- a/src/http/EntryConfig.cpp +++ /dev/null @@ -1,107 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "EntryConfig.h" -#include -#include "core/Entry.h" -#include "core/EntryAttributes.h" -#include "http/Protocol.h" - -static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; //TODO: duplicated string (also in Service.cpp) - -EntryConfig::EntryConfig(QObject *parent) : - QObject(parent) -{ -} - -QStringList EntryConfig::allowedHosts() const -{ - return m_allowedHosts.toList(); -} - -void EntryConfig::setAllowedHosts(const QStringList &allowedHosts) -{ - m_allowedHosts = allowedHosts.toSet(); -} - -QStringList EntryConfig::deniedHosts() const -{ - return m_deniedHosts.toList(); -} - -void EntryConfig::setDeniedHosts(const QStringList &deniedHosts) -{ - m_deniedHosts = deniedHosts.toSet(); -} - -bool EntryConfig::isAllowed(const QString &host) -{ - return m_allowedHosts.contains(host); -} - -void EntryConfig::allow(const QString &host) -{ - m_allowedHosts.insert(host); - m_deniedHosts.remove(host); -} - -bool EntryConfig::isDenied(const QString &host) -{ - return m_deniedHosts.contains(host); -} - -void EntryConfig::deny(const QString &host) -{ - m_deniedHosts.insert(host); - m_allowedHosts.remove(host); -} - -QString EntryConfig::realm() const -{ - return m_realm; -} - -void EntryConfig::setRealm(const QString &realm) -{ - m_realm = realm; -} - -bool EntryConfig::load(const Entry *entry) -{ - QString s = entry->attributes()->value(KEEPASSHTTP_NAME); - if (s.isEmpty()) - return false; - - QJsonDocument doc = QJsonDocument::fromJson(s.toUtf8()); - if (doc.isNull()) - return false; - - QVariantMap map = doc.object().toVariantMap(); - for(QVariantMap::iterator iter = map.begin(); iter != map.end(); ++iter) { - setProperty(iter.key().toLatin1(), iter.value()); - } - return true; -} - -void EntryConfig::save(Entry *entry) -{ - QVariantMap v = qobject2qvariant(this); - QJsonObject o = QJsonObject::fromVariantMap(v); - QByteArray json = QJsonDocument(o).toJson(QJsonDocument::Compact); - entry->attributes()->set(KEEPASSHTTP_NAME, json); -} diff --git a/src/http/EntryConfig.h b/src/http/EntryConfig.h deleted file mode 100644 index d5e9876ee..000000000 --- a/src/http/EntryConfig.h +++ /dev/null @@ -1,59 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef ENTRYCONFIG_H -#define ENTRYCONFIG_H - -#include -#include -#include -#include - -class Entry; - -class EntryConfig : public QObject -{ - Q_OBJECT - Q_PROPERTY(QStringList Allow READ allowedHosts WRITE setAllowedHosts) - Q_PROPERTY(QStringList Deny READ deniedHosts WRITE setDeniedHosts ) - Q_PROPERTY(QString Realm READ realm WRITE setRealm ) - -public: - EntryConfig(QObject * object = 0); - - bool load(const Entry * entry); - void save(Entry * entry); - bool isAllowed(const QString & host); - void allow(const QString & host); - bool isDenied(const QString & host); - void deny(const QString & host); - QString realm() const; - void setRealm(const QString &realm); - -private: - QStringList allowedHosts() const; - void setAllowedHosts(const QStringList &allowedHosts); - QStringList deniedHosts() const; - void setDeniedHosts(const QStringList &deniedHosts); - - QSet m_allowedHosts; - QSet m_deniedHosts; - QString m_realm; -}; - -#endif // ENTRYCONFIG_H diff --git a/src/http/HttpPasswordGeneratorWidget.cpp b/src/http/HttpPasswordGeneratorWidget.cpp deleted file mode 100644 index f08bc3d36..000000000 --- a/src/http/HttpPasswordGeneratorWidget.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "HttpPasswordGeneratorWidget.h" -#include "ui_HttpPasswordGeneratorWidget.h" - -#include - -#include "core/Config.h" -#include "core/PasswordGenerator.h" -#include "core/FilePath.h" - -HttpPasswordGeneratorWidget::HttpPasswordGeneratorWidget(QWidget* parent) - : QWidget(parent) - , m_updatingSpinBox(false) - , m_generator(new PasswordGenerator()) - , m_ui(new Ui::HttpPasswordGeneratorWidget()) -{ - m_ui->setupUi(this); - - connect(m_ui->sliderLength, SIGNAL(valueChanged(int)), SLOT(sliderMoved())); - connect(m_ui->spinBoxLength, SIGNAL(valueChanged(int)), SLOT(spinBoxChanged())); - - connect(m_ui->optionButtons, SIGNAL(buttonClicked(int)), SLOT(updateGenerator())); - - loadSettings(); - reset(); -} - -HttpPasswordGeneratorWidget::~HttpPasswordGeneratorWidget() -{ -} - -void HttpPasswordGeneratorWidget::loadSettings() -{ - m_ui->checkBoxLower->setChecked(config()->get("Http/generator/LowerCase", PasswordGenerator::DefaultLower).toBool()); - m_ui->checkBoxUpper->setChecked(config()->get("Http/generator/UpperCase", PasswordGenerator::DefaultUpper).toBool()); - m_ui->checkBoxNumbers->setChecked(config()->get("Http/generator/Numbers", PasswordGenerator::DefaultNumbers).toBool()); - m_ui->checkBoxSpecialChars->setChecked(config()->get("Http/generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool()); - m_ui->checkBoxSpecialChars->setChecked(config()->get("Http/generator/EASCII", PasswordGenerator::DefaultEASCII).toBool()); - - m_ui->checkBoxExcludeAlike->setChecked(config()->get("Http/generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool()); - m_ui->checkBoxEnsureEvery->setChecked(config()->get("Http/generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool()); - - m_ui->spinBoxLength->setValue(config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt()); -} - -void HttpPasswordGeneratorWidget::saveSettings() -{ - config()->set("Http/generator/LowerCase", m_ui->checkBoxLower->isChecked()); - config()->set("Http/generator/UpperCase", m_ui->checkBoxUpper->isChecked()); - config()->set("Http/generator/Numbers", m_ui->checkBoxNumbers->isChecked()); - config()->set("Http/generator/SpecialChars", m_ui->checkBoxSpecialChars->isChecked()); - config()->set("Http/generator/EASCII", m_ui->checkBoxExtASCII->isChecked()); - - config()->set("Http/generator/ExcludeAlike", m_ui->checkBoxExcludeAlike->isChecked()); - config()->set("Http/generator/EnsureEvery", m_ui->checkBoxEnsureEvery->isChecked()); - - config()->set("Http/generator/Length", m_ui->spinBoxLength->value()); -} - -void HttpPasswordGeneratorWidget::reset() -{ - updateGenerator(); -} - -void HttpPasswordGeneratorWidget::sliderMoved() -{ - if (m_updatingSpinBox) { - return; - } - - m_ui->spinBoxLength->setValue(m_ui->sliderLength->value()); - - updateGenerator(); -} - -void HttpPasswordGeneratorWidget::spinBoxChanged() -{ - // Interlock so that we don't update twice - this causes issues as the spinbox can go higher than slider - m_updatingSpinBox = true; - - m_ui->sliderLength->setValue(m_ui->spinBoxLength->value()); - - m_updatingSpinBox = false; - - updateGenerator(); -} - -PasswordGenerator::CharClasses HttpPasswordGeneratorWidget::charClasses() -{ - PasswordGenerator::CharClasses classes; - - if (m_ui->checkBoxLower->isChecked()) { - classes |= PasswordGenerator::LowerLetters; - } - - if (m_ui->checkBoxUpper->isChecked()) { - classes |= PasswordGenerator::UpperLetters; - } - - if (m_ui->checkBoxNumbers->isChecked()) { - classes |= PasswordGenerator::Numbers; - } - - if (m_ui->checkBoxSpecialChars->isChecked()) { - classes |= PasswordGenerator::SpecialCharacters; - } - - if (m_ui->checkBoxExtASCII->isChecked()) { - classes |= PasswordGenerator::EASCII; - } - - return classes; -} - -PasswordGenerator::GeneratorFlags HttpPasswordGeneratorWidget::generatorFlags() -{ - PasswordGenerator::GeneratorFlags flags; - - if (m_ui->checkBoxExcludeAlike->isChecked()) { - flags |= PasswordGenerator::ExcludeLookAlike; - } - - if (m_ui->checkBoxEnsureEvery->isChecked()) { - flags |= PasswordGenerator::CharFromEveryGroup; - } - - return flags; -} - -void HttpPasswordGeneratorWidget::updateGenerator() -{ - m_generator->setLength(m_ui->spinBoxLength->value()); - m_generator->setCharClasses(charClasses()); - m_generator->setFlags(generatorFlags()); -} diff --git a/src/http/HttpPasswordGeneratorWidget.h b/src/http/HttpPasswordGeneratorWidget.h deleted file mode 100644 index 8ef6091b6..000000000 --- a/src/http/HttpPasswordGeneratorWidget.h +++ /dev/null @@ -1,58 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef KEEPASSX_HTTPPASSWORDGENERATORWIDGET_H -#define KEEPASSX_HTTPPASSWORDGENERATORWIDGET_H - -#include -#include - -#include "core/PasswordGenerator.h" - -namespace Ui { - class HttpPasswordGeneratorWidget; -} - -class HttpPasswordGeneratorWidget : public QWidget -{ - Q_OBJECT - -public: - explicit HttpPasswordGeneratorWidget(QWidget* parent = nullptr); - ~HttpPasswordGeneratorWidget(); - void loadSettings(); - void saveSettings(); - void reset(); - -private slots: - void sliderMoved(); - void spinBoxChanged(); - - void updateGenerator(); - -private: - bool m_updatingSpinBox; - - PasswordGenerator::CharClasses charClasses(); - PasswordGenerator::GeneratorFlags generatorFlags(); - - const QScopedPointer m_generator; - const QScopedPointer m_ui; -}; - -#endif // KEEPASSX_PASSWORDGENERATORWIDGET_H diff --git a/src/http/HttpPasswordGeneratorWidget.ui b/src/http/HttpPasswordGeneratorWidget.ui deleted file mode 100644 index 71df88730..000000000 --- a/src/http/HttpPasswordGeneratorWidget.ui +++ /dev/null @@ -1,220 +0,0 @@ - - - HttpPasswordGeneratorWidget - - - - 0 - 0 - 500 - 181 - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Length: - - - - - - - - - 1 - - - 64 - - - Qt::Horizontal - - - QSlider::TicksBelow - - - 8 - - - - - - - 1 - - - 999 - - - - - - - - - - - Character Types - - - - - - - - Upper Case Letters - - - A-Z - - - true - - - optionButtons - - - - - - - Lower Case Letters - - - a-z - - - true - - - optionButtons - - - - - - - Numbers - - - 0-9 - - - true - - - optionButtons - - - - - - - Special Characters - - - /*_& ... - - - true - - - optionButtons - - - - - - - Extended ASCII - - - Extended ASCII - - - true - - - optionButtons - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Exclude look-alike characters - - - optionButtons - - - - - - - Ensure that the password contains characters from every group - - - optionButtons - - - - - - - - - - sliderLength - spinBoxLength - checkBoxUpper - checkBoxLower - checkBoxNumbers - checkBoxSpecialChars - checkBoxExcludeAlike - checkBoxEnsureEvery - - - - - - - false - - - - diff --git a/src/http/HttpSettings.cpp b/src/http/HttpSettings.cpp deleted file mode 100644 index 7ff0dbaa4..000000000 --- a/src/http/HttpSettings.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "HttpSettings.h" -#include "core/Config.h" - -PasswordGenerator HttpSettings::m_generator; - -bool HttpSettings::isEnabled() -{ - return config()->get("Http/Enabled", false).toBool(); -} - -void HttpSettings::setEnabled(bool enabled) -{ - config()->set("Http/Enabled", enabled); -} - -bool HttpSettings::showNotification() -{ - return config()->get("Http/ShowNotification", true).toBool(); -} - -void HttpSettings::setShowNotification(bool showNotification) -{ - config()->set("Http/ShowNotification", showNotification); -} - -bool HttpSettings::bestMatchOnly() -{ - return config()->get("Http/BestMatchOnly", false).toBool(); -} - -void HttpSettings::setBestMatchOnly(bool bestMatchOnly) -{ - config()->set("Http/BestMatchOnly", bestMatchOnly); -} - -bool HttpSettings::unlockDatabase() -{ - return config()->get("Http/UnlockDatabase", true).toBool(); -} - -void HttpSettings::setUnlockDatabase(bool unlockDatabase) -{ - config()->set("Http/UnlockDatabase", unlockDatabase); -} - -bool HttpSettings::matchUrlScheme() -{ - return config()->get("Http/MatchUrlScheme", true).toBool(); -} - -void HttpSettings::setMatchUrlScheme(bool matchUrlScheme) -{ - config()->set("Http/MatchUrlScheme", matchUrlScheme); -} - -bool HttpSettings::sortByUsername() -{ - return config()->get("Http/SortByUsername", false).toBool(); -} - -void HttpSettings::setSortByUsername(bool sortByUsername) -{ - config()->set("Http/SortByUsername", sortByUsername); -} - -bool HttpSettings::sortByTitle() -{ - return !sortByUsername(); -} - -void HttpSettings::setSortByTitle(bool sortByUsertitle) -{ - setSortByUsername(!sortByUsertitle); -} - -bool HttpSettings::alwaysAllowAccess() -{ - return config()->get("Http/AlwaysAllowAccess", false).toBool(); -} - -void HttpSettings::setAlwaysAllowAccess(bool alwaysAllowAccess) -{ - config()->set("Http/AlwaysAllowAccess", alwaysAllowAccess); -} - -bool HttpSettings::alwaysAllowUpdate() -{ - return config()->get("Http/AlwaysAllowUpdate", false).toBool(); -} - -void HttpSettings::setAlwaysAllowUpdate(bool alwaysAllowUpdate) -{ - config()->set("Http/AlwaysAllowUpdate", alwaysAllowUpdate); -} - -bool HttpSettings::searchInAllDatabases() -{ - return config()->get("Http/SearchInAllDatabases", false).toBool(); -} - -void HttpSettings::setSearchInAllDatabases(bool searchInAllDatabases) -{ - config()->set("Http/SearchInAllDatabases", searchInAllDatabases); -} - -bool HttpSettings::supportKphFields() -{ - return config()->get("Http/SupportKphFields", true).toBool(); -} - -void HttpSettings::setSupportKphFields(bool supportKphFields) -{ - config()->set("Http/SupportKphFields", supportKphFields); -} - -int HttpSettings::httpPort() -{ - static const int PORT = 19455; - - return config()->get("Http/Port", PORT).toInt(); -} - -void HttpSettings::setHttpPort(int port) -{ - config()->set("Http/Port", port); -} - -bool HttpSettings::passwordUseNumbers() -{ - return config()->get("Http/generator/Numbers", PasswordGenerator::DefaultNumbers).toBool(); -} - -void HttpSettings::setPasswordUseNumbers(bool useNumbers) -{ - config()->set("Http/generator/Numbers", useNumbers); -} - -bool HttpSettings::passwordUseLowercase() -{ - return config()->get("Http/generator/LowerCase", PasswordGenerator::DefaultLower).toBool(); -} - -void HttpSettings::setPasswordUseLowercase(bool useLowercase) -{ - config()->set("Http/generator/LowerCase", useLowercase); -} - -bool HttpSettings::passwordUseUppercase() -{ - return config()->get("Http/generator/UpperCase", PasswordGenerator::DefaultUpper).toBool(); -} - -void HttpSettings::setPasswordUseUppercase(bool useUppercase) -{ - config()->set("Http/generator/UpperCase", useUppercase); -} - -bool HttpSettings::passwordUseSpecial() -{ - return config()->get("Http/generator/SpecialChars", PasswordGenerator::DefaultSpecial).toBool(); -} - -void HttpSettings::setPasswordUseSpecial(bool useSpecial) -{ - config()->set("Http/generator/SpecialChars", useSpecial); -} - -bool HttpSettings::passwordUseEASCII() -{ - return config()->get("Http/generator/EASCII", PasswordGenerator::DefaultEASCII).toBool(); -} - -void HttpSettings::setPasswordUseEASCII(bool useExtended) -{ - config()->set("Http/generator/EASCII", useExtended); -} - -bool HttpSettings::passwordEveryGroup() -{ - return config()->get("Http/generator/EnsureEvery", PasswordGenerator::DefaultFromEveryGroup).toBool(); -} - -void HttpSettings::setPasswordEveryGroup(bool everyGroup) -{ - config()->get("Http/generator/EnsureEvery", everyGroup); -} - -bool HttpSettings::passwordExcludeAlike() -{ - return config()->get("Http/generator/ExcludeAlike", PasswordGenerator::DefaultLookAlike).toBool(); -} - -void HttpSettings::setPasswordExcludeAlike(bool excludeAlike) -{ - config()->set("Http/generator/ExcludeAlike", excludeAlike); -} - -int HttpSettings::passwordLength() -{ - return config()->get("Http/generator/Length", PasswordGenerator::DefaultLength).toInt(); -} - -void HttpSettings::setPasswordLength(int length) -{ - config()->set("Http/generator/Length", length); - m_generator.setLength(length); -} - -PasswordGenerator::CharClasses HttpSettings::passwordCharClasses() -{ - PasswordGenerator::CharClasses classes; - if (passwordUseLowercase()) { - classes |= PasswordGenerator::LowerLetters; - } - if (passwordUseUppercase()) { - classes |= PasswordGenerator::UpperLetters; - } - if (passwordUseNumbers()) { - classes |= PasswordGenerator::Numbers; - } - if (passwordUseSpecial()) { - classes |= PasswordGenerator::SpecialCharacters; - } - if (passwordUseEASCII()) { - classes |= PasswordGenerator::EASCII; - } - return classes; -} - -PasswordGenerator::GeneratorFlags HttpSettings::passwordGeneratorFlags() -{ - PasswordGenerator::GeneratorFlags flags; - if (passwordExcludeAlike()) - flags |= PasswordGenerator::ExcludeLookAlike; - if (passwordEveryGroup()) - flags |= PasswordGenerator::CharFromEveryGroup; - return flags; -} - -QString HttpSettings::generatePassword() -{ - m_generator.setLength(passwordLength()); - m_generator.setCharClasses(passwordCharClasses()); - m_generator.setFlags(passwordGeneratorFlags()); - - return m_generator.generatePassword(); -} - -int HttpSettings::getbits() -{ - return m_generator.getbits(); -} diff --git a/src/http/HttpSettings.h b/src/http/HttpSettings.h deleted file mode 100644 index 63c2963cf..000000000 --- a/src/http/HttpSettings.h +++ /dev/null @@ -1,78 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef HTTPSETTINGS_H -#define HTTPSETTINGS_H - -#include "core/PasswordGenerator.h" - -class HttpSettings -{ -public: - static bool isEnabled(); - static void setEnabled(bool enabled); - - static bool showNotification(); //TODO!! - static void setShowNotification(bool showNotification); - static bool bestMatchOnly(); //TODO!! - static void setBestMatchOnly(bool bestMatchOnly); - static bool unlockDatabase(); //TODO!! - static void setUnlockDatabase(bool unlockDatabase); - static bool matchUrlScheme(); - static void setMatchUrlScheme(bool matchUrlScheme); - static bool sortByUsername(); - static void setSortByUsername(bool sortByUsername = true); - static bool sortByTitle(); - static void setSortByTitle(bool sortByUsertitle = true); - static bool alwaysAllowAccess(); - static void setAlwaysAllowAccess(bool alwaysAllowAccess); - static bool alwaysAllowUpdate(); - static void setAlwaysAllowUpdate(bool alwaysAllowUpdate); - static bool searchInAllDatabases();//TODO!! - static void setSearchInAllDatabases(bool searchInAllDatabases); - static bool supportKphFields(); - static void setSupportKphFields(bool supportKphFields); - static int httpPort(); - static void setHttpPort(int port); - - static bool passwordUseNumbers(); - static void setPasswordUseNumbers(bool useNumbers); - static bool passwordUseLowercase(); - static void setPasswordUseLowercase(bool useLowercase); - static bool passwordUseUppercase(); - static void setPasswordUseUppercase(bool useUppercase); - static bool passwordUseSpecial(); - static void setPasswordUseSpecial(bool useSpecial); - static bool passwordUseEASCII(); - static void setPasswordUseEASCII(bool useExtended); - static bool passwordEveryGroup(); - static void setPasswordEveryGroup(bool everyGroup); - static bool passwordExcludeAlike(); - static void setPasswordExcludeAlike(bool excludeAlike); - static int passwordLength(); - static void setPasswordLength(int length); - static PasswordGenerator::CharClasses passwordCharClasses(); - static PasswordGenerator::GeneratorFlags passwordGeneratorFlags(); - static QString generatePassword(); - static int getbits(); - -private: - static PasswordGenerator m_generator; -}; - -#endif // HTTPSETTINGS_H diff --git a/src/http/OptionDialog.cpp b/src/http/OptionDialog.cpp deleted file mode 100644 index dd5a51f73..000000000 --- a/src/http/OptionDialog.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "OptionDialog.h" -#include "ui_OptionDialog.h" -#include "HttpSettings.h" - -#include "core/FilePath.h" - -#include - -OptionDialog::OptionDialog(QWidget *parent) : - QWidget(parent), - m_ui(new Ui::OptionDialog()) -{ - m_ui->setupUi(this); - connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SIGNAL(removeSharedEncryptionKeys())); - connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SIGNAL(removeStoredPermissions())); - - m_ui->warningWidget->showMessage(tr("Warning: The following options can be dangerous!"), MessageWidget::Warning); - m_ui->warningWidget->setCloseButtonVisible(false); - m_ui->warningWidget->setAutoHideTimeout(MessageWidget::DisableAutoHide); - - m_ui->tabWidget->setEnabled(m_ui->enableHttpServer->isChecked()); - connect(m_ui->enableHttpServer, SIGNAL(toggled(bool)), m_ui->tabWidget, SLOT(setEnabled(bool))); - - m_ui->deprecationNotice->showMessage(tr("

KeePassHTTP has been deprecated and will be removed in the future.
" - "Please switch to KeePassXC-Browser instead! For help with migration, visit " - "our " - "migration guide.

"), MessageWidget::Warning); - m_ui->deprecationNotice->setCloseButtonVisible(false); - m_ui->deprecationNotice->setAutoHideTimeout(-1); - connect(m_ui->deprecationNotice, &MessageWidget::linkActivated, &MessageWidget::openHttpUrl); -} - -OptionDialog::~OptionDialog() -{ -} - -void OptionDialog::loadSettings() -{ - HttpSettings settings; - m_ui->enableHttpServer->setChecked(settings.isEnabled()); - - m_ui->showNotification->setChecked(settings.showNotification()); - m_ui->bestMatchOnly->setChecked(settings.bestMatchOnly()); - m_ui->unlockDatabase->setChecked(settings.unlockDatabase()); - m_ui->matchUrlScheme->setChecked(settings.matchUrlScheme()); - if (settings.sortByUsername()) - m_ui->sortByUsername->setChecked(true); - else - m_ui->sortByTitle->setChecked(true); - m_ui->httpPort->setText(QString::number(settings.httpPort())); - - m_ui->alwaysAllowAccess->setChecked(settings.alwaysAllowAccess()); - m_ui->alwaysAllowUpdate->setChecked(settings.alwaysAllowUpdate()); - m_ui->searchInAllDatabases->setChecked(settings.searchInAllDatabases()); - m_ui->supportKphFields->setChecked(settings.supportKphFields()); - - m_ui->passwordGenerator->loadSettings(); -} - -void OptionDialog::saveSettings() -{ - HttpSettings settings; - settings.setEnabled(m_ui->enableHttpServer->isChecked()); - - settings.setShowNotification(m_ui->showNotification->isChecked()); - settings.setBestMatchOnly(m_ui->bestMatchOnly->isChecked()); - settings.setUnlockDatabase(m_ui->unlockDatabase->isChecked()); - settings.setMatchUrlScheme(m_ui->matchUrlScheme->isChecked()); - settings.setSortByUsername(m_ui->sortByUsername->isChecked()); - - int port = m_ui->httpPort->text().toInt(); - if (port < 1024) { - QMessageBox::warning(this, tr("Cannot bind to privileged ports"), - tr("Cannot bind to privileged ports below 1024!\nUsing default port 19455.")); - port = 19455; - } - settings.setHttpPort(port); - settings.setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked()); - settings.setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked()); - settings.setSearchInAllDatabases(m_ui->searchInAllDatabases->isChecked()); - settings.setSupportKphFields(m_ui->supportKphFields->isChecked()); - - m_ui->passwordGenerator->saveSettings(); -} diff --git a/src/http/OptionDialog.h b/src/http/OptionDialog.h deleted file mode 100644 index 6139f929b..000000000 --- a/src/http/OptionDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef OPTIONDIALOG_H -#define OPTIONDIALOG_H - -#include -#include - -namespace Ui { -class OptionDialog; -} - -class OptionDialog : public QWidget -{ - Q_OBJECT - -public: - explicit OptionDialog(QWidget *parent = nullptr); - ~OptionDialog(); - -public slots: - void loadSettings(); - void saveSettings(); - -signals: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - -private: - QScopedPointer m_ui; -}; - -#endif // OPTIONDIALOG_H diff --git a/src/http/OptionDialog.ui b/src/http/OptionDialog.ui deleted file mode 100644 index 472f9f62a..000000000 --- a/src/http/OptionDialog.ui +++ /dev/null @@ -1,301 +0,0 @@ - - - OptionDialog - - - - 0 - 0 - 803 - 433 - - - - Dialog - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - This is required for accessing your databases from ChromeIPass or PassIFox - - - Enable KeePassHTTP server - - - - - - - - - - 0 - - - - General - - - - - - Sh&ow a notification when credentials are requested - - - true - - - - - - - Only returns the best matches for a specific URL instead of all entries for the whole domain. - - - &Return only best matching entries - - - - - - - Re&quest to unlock the database if it is locked - - - true - - - - - - - Only entries with the same scheme (http://, https://, ftp://, ...) are returned. - - - &Match URL schemes - - - - - - - Sort matching entries by &username - - - - - - - Sort &matching entries by title - - - - - - - R&emove all shared encryption keys from active database - - - - - - - Re&move all stored permissions from entries in active database - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Password Generator - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - Advanced - - - - - - - 0 - 0 - - - - - - - - Always allow &access to entries - - - - - - - Always allow &updating entries - - - - - - - Only the selected database has to be connected with a client. - - - Searc&h in all opened databases for matching entries - - - - - - - Automatically creating or updating string fields is not supported. - - - &Return advanced string fields which start with "KPH: " - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - - - - 0 - 0 - - - - HTTP Port: - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - d0000 - - - Default port: 19455 - - - - - - - KeePassXC will listen to this port on 127.0.0.1 - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - HttpPasswordGeneratorWidget - QWidget -
http/HttpPasswordGeneratorWidget.h
- 1 -
- - MessageWidget - QWidget -
gui/MessageWidget.h
- 1 -
-
- - -
diff --git a/src/http/Protocol.cpp b/src/http/Protocol.cpp deleted file mode 100644 index d6d5557a1..000000000 --- a/src/http/Protocol.cpp +++ /dev/null @@ -1,557 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include "Protocol.h" -#include - -#include "crypto/Random.h" -#include "crypto/SymmetricCipher.h" -#include "crypto/SymmetricCipherGcrypt.h" -#include "core/Global.h" - -namespace KeepassHttpProtocol -{ -static const char * const STR_GET_LOGINS = "get-logins"; -static const char * const STR_GET_LOGINS_COUNT = "get-logins-count"; -static const char * const STR_GET_ALL_LOGINS = "get-all-logins"; -static const char * const STR_SET_LOGIN = "set-login"; -static const char * const STR_ASSOCIATE = "associate"; -static const char * const STR_TEST_ASSOCIATE = "test-associate"; -static const char * const STR_GENERATE_PASSWORD = "generate-password"; -static const char * const STR_VERSION = "1.8.4.2"; - -}/*namespace KeepassHttpProtocol*/ - -using namespace KeepassHttpProtocol; - -QVariantMap qobject2qvariant( const QObject* object, const QStringList& ignoredProperties ) -{ - QVariantMap result; - const QMetaObject *metaobject = object->metaObject(); - int count = metaobject->propertyCount(); - for (int i=0; iproperty(i); - const char *name = metaproperty.name(); - - if (ignoredProperties.contains(QLatin1String(name)) || (!metaproperty.isReadable())) - continue; - - QVariant value = object->property(name); - result[QLatin1String(name)] = value; - } - return result; -} - -static QHash createStringHash() -{ - QHash hash; - hash.insert(STR_GET_LOGINS, GET_LOGINS); - hash.insert(STR_GET_LOGINS_COUNT, GET_LOGINS_COUNT); - hash.insert(STR_GET_ALL_LOGINS, GET_ALL_LOGINS); - hash.insert(STR_SET_LOGIN, SET_LOGIN); - hash.insert(STR_ASSOCIATE, ASSOCIATE); - hash.insert(STR_TEST_ASSOCIATE, TEST_ASSOCIATE); - hash.insert(STR_GENERATE_PASSWORD,GENERATE_PASSWORD); - return hash; -} - -static RequestType parseRequest(const QString &str) -{ - static const QHash REQUEST_STRINGS = createStringHash(); - return REQUEST_STRINGS.value(str, INVALID); -} - -static QByteArray decode64(QString s) -{ - return QByteArray::fromBase64(s.toLatin1()); -} - -static QString encode64(QByteArray b) -{ - return QString::fromLatin1(b.toBase64()); -} - -static QByteArray decrypt2(const QByteArray & data, SymmetricCipherGcrypt & cipher) -{ - //Ensure we get full blocks only - if (data.length() <= 0 || data.length() % cipher.blockSize()) - return QByteArray(); - - //Decrypt - cipher.reset(); - bool ok; - QByteArray buffer = cipher.process(data, &ok); - - //Remove PKCS#7 padding - buffer.chop(buffer.at(buffer.length()-1)); - return buffer; -} - -static QString decrypt(const QString &data, SymmetricCipherGcrypt & cipher) -{ - return QString::fromUtf8(decrypt2(decode64(data), cipher)); -} - -static QByteArray encrypt2(const QByteArray & data, SymmetricCipherGcrypt & cipher) -{ - //Add PKCS#7 padding - const int blockSize = cipher.blockSize(); - const int paddingSize = blockSize - data.size() % blockSize; - - //Encrypt - QByteArray buffer = data + QByteArray(paddingSize, paddingSize); - cipher.reset(); - Q_UNUSED(cipher.processInPlace(buffer)); - return buffer; -} - -static QString encrypt(const QString & data, SymmetricCipherGcrypt & cipher) -{ - return encode64(encrypt2(data.toUtf8(), cipher)); -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// Request -//////////////////////////////////////////////////////////////////////////////////////////////////// - -Request::Request(): - m_requestType(INVALID), - m_cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt) -{ - m_cipher.init(); -} - -QString Request::nonce() const -{ - return m_nonce; -} - -void Request::setNonce(const QString &nonce) -{ - m_nonce = nonce; -} - -QString Request::verifier() const -{ - return m_verifier; -} - -void Request::setVerifier(const QString &verifier) -{ - m_verifier = verifier; -} - -QString Request::id() const -{ - return m_id; -} - -void Request::setId(const QString &id) -{ - m_id = id; -} - -QString Request::key() const -{ - return m_key; -} - -void Request::setKey(const QString &key) -{ - m_key = key; -} - -QString Request::submitUrl() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_submitUrl, m_cipher); -} - -void Request::setSubmitUrl(const QString &submitUrl) -{ - m_submitUrl = submitUrl; -} - -QString Request::url() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_url, m_cipher); -} - -void Request::setUrl(const QString &url) -{ - m_url = url; -} - -QString Request::realm() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_realm, m_cipher); -} - -void Request::setRealm(const QString &realm) -{ - m_realm = realm; -} - -QString Request::login() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_login, m_cipher); -} - -void Request::setLogin(const QString &login) -{ - m_login = login; -} - -QString Request::uuid() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_uuid, m_cipher); -} - -void Request::setUuid(const QString &uuid) -{ - m_uuid = uuid; -} - -QString Request::password() const -{ - //Q_ASSERT(m_cipher.isValid()); - return decrypt(m_password, m_cipher); -} - -void Request::setPassword(const QString &password) -{ - m_password = password; -} - -bool Request::sortSelection() const -{ - return m_sortSelection; -} - -void Request::setSortSelection(bool sortSelection) -{ - m_sortSelection = sortSelection; -} - -KeepassHttpProtocol::RequestType Request::requestType() const -{ - return parseRequest(m_requestType); -} - -QString Request::requestTypeStr() const -{ - return m_requestType; -} - -void Request::setRequestType(const QString &requestType) -{ - m_requestType = requestType; -} - -bool Request::CheckVerifier(const QString &key) const -{ - //Q_ASSERT(!m_cipher.isValid()); - - m_cipher.setKey(decode64(key)); - m_cipher.setIv(decode64(m_nonce)); - - return decrypt(m_verifier, m_cipher) == m_nonce; -} - -bool Request::fromJson(QString text) -{ - QJsonDocument doc = QJsonDocument::fromJson(text.toUtf8()); - if (doc.isNull()) - return false; - - m_requestType.clear(); - QVariantMap map = doc.object().toVariantMap(); - for(QVariantMap::iterator iter = map.begin(); iter != map.end(); ++iter) { - setProperty(iter.key().toLatin1(), iter.value()); - } - - return requestType() != INVALID; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// Response -//////////////////////////////////////////////////////////////////////////////////////////////////// - -Response::Response(const Request &request, QString hash): - m_requestType(request.requestTypeStr()), - m_success(false), - m_count(-1), - m_version(STR_VERSION), - m_hash(hash), - m_cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt) -{ - m_cipher.init(); -} - -void Response::setVerifier(QString key) -{ - //Q_ASSERT(!m_cipher.isValid()); - - m_cipher.setKey(decode64(key)); - - //Generate new IV - const QByteArray iv = randomGen()->randomArray(m_cipher.blockSize()); - m_cipher.setIv(iv); - m_nonce = encode64(iv); - - //Encrypt - m_verifier = encrypt(m_nonce, m_cipher); -} - -QString Response::toJson() -{ - QJsonObject json; - - int count = metaObject()->propertyCount(); - for (int i=0; iproperty(i); - const char *name = metaproperty.name(); - json.insert(QString(name), QJsonValue::fromVariant(this->property(name))); - } - - QJsonDocument doc(json); - return doc.toJson(QJsonDocument::Compact); -} - -KeepassHttpProtocol::RequestType Response::requestType() const -{ - return parseRequest(m_requestType); -} - -QString Response::requestTypeStr() const -{ - return m_requestType; -} - -QString Response::verifier() const -{ - return m_verifier; -} - -QString Response::nonce() const -{ - return m_nonce; -} - -QVariant Response::count() const -{ - return m_count < 0 ? QVariant() : QVariant(m_count); -} - -void Response::setCount(int count) -{ - m_count = count; -} - -QVariant Response::getEntries() const -{ - if (m_count < 0 || m_entries.isEmpty()) - return QVariant(); - - QList res; - res.reserve(m_entries.size()); - for (const Entry& entry: asConst(m_entries)) { - res.append(qobject2qvariant(&entry)); - } - return res; -} - -void Response::setEntries(const QList &entries) -{ - //Q_ASSERT(m_cipher.isValid()); - - m_count = entries.count(); - - QList encryptedEntries; - encryptedEntries.reserve(m_count); - for (const Entry& entry: entries) { - Entry encryptedEntry(encrypt(entry.name(), m_cipher), - encrypt(entry.login(), m_cipher), - entry.password().isNull() ? QString() : encrypt(entry.password(), m_cipher), - encrypt(entry.uuid(), m_cipher)); - const auto stringFields = entry.stringFields(); - for (const StringField& field: stringFields) { - encryptedEntry.addStringField(encrypt(field.key(), m_cipher), - encrypt(field.value(), m_cipher)); - } - encryptedEntries << encryptedEntry; - } - m_entries = encryptedEntries; -} - -QString Response::hash() const -{ - return m_hash; -} - -QString Response::version() const -{ - return m_version; -} - -QString Response::id() const -{ - return m_id; -} - -void Response::setId(const QString &id) -{ - m_id = id; -} - -bool Response::success() const -{ - return m_success; -} - -void Response::setSuccess() -{ - m_success = true; -} - -QString Response::error() const -{ - return m_error; -} - -void Response::setError(const QString &error) -{ - m_success = false; - m_error = error; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// Entry -//////////////////////////////////////////////////////////////////////////////////////////////////// - -Entry::Entry() -{} - -Entry::Entry(QString name, QString login, QString password, QString uuid): - m_login(login), - m_password(password), - m_uuid(uuid), - m_name(name) -{} - -Entry::Entry(const Entry & other): - QObject(), - m_login(other.m_login), - m_password(other.m_password), - m_uuid(other.m_uuid), - m_name(other.m_name), - m_stringFields(other.m_stringFields) -{} - -Entry & Entry::operator=(const Entry & other) -{ - m_login = other.m_login; - m_password = other.m_password; - m_uuid = other.m_uuid; - m_name = other.m_name; - m_stringFields = other.m_stringFields; - return *this; -} - -QString Entry::login() const -{ - return m_login; -} - -QString Entry::name() const -{ - return m_name; -} - -QString Entry::uuid() const -{ - return m_uuid; -} - -QString Entry::password() const -{ - return m_password; -} - -QList Entry::stringFields() const -{ - return m_stringFields; -} - -void Entry::addStringField(const QString &key, const QString &value) -{ - m_stringFields.append(StringField(key, value)); -} - -QVariant Entry::getStringFields() const -{ - if (m_stringFields.isEmpty()) - return QVariant(); - - QList res; - res.reserve(m_stringFields.size()); - for (const StringField& stringfield: asConst(m_stringFields)) { - res.append(qobject2qvariant(&stringfield)); - } - return res; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// StringField -//////////////////////////////////////////////////////////////////////////////////////////////////// - -StringField::StringField() -{} - -StringField::StringField(const QString &key, const QString &value): - m_key(key), m_value(value) -{} - -StringField::StringField(const StringField &other): - QObject(NULL), m_key(other.m_key), m_value(other.m_value) -{} - -StringField &StringField::operator =(const StringField &other) -{ - m_key = other.m_key; - m_value = other.m_value; - return *this; -} - -QString StringField::key() const -{ - return m_key; -} - -QString StringField::value() const -{ - return m_value; -} diff --git a/src/http/Protocol.h b/src/http/Protocol.h deleted file mode 100644 index ff48fe58c..000000000 --- a/src/http/Protocol.h +++ /dev/null @@ -1,212 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef RESPONSE_H -#define RESPONSE_H - -#include -#include "crypto/SymmetricCipherGcrypt.h" - -QVariantMap qobject2qvariant( const QObject* object, const QStringList& ignoredProperties = QStringList(QString(QLatin1String("objectName"))) ); - -namespace KeepassHttpProtocol { - -enum RequestType { - INVALID = -1, - GET_LOGINS, - GET_LOGINS_COUNT, - GET_ALL_LOGINS, - SET_LOGIN, - ASSOCIATE, - TEST_ASSOCIATE, - GENERATE_PASSWORD -}; - -//TODO: use QByteArray whenever possible? - -class Request : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString RequestType READ requestTypeStr WRITE setRequestType ) - Q_PROPERTY(bool SortSelection READ sortSelection WRITE setSortSelection) - Q_PROPERTY(QString Login READ login WRITE setLogin ) - Q_PROPERTY(QString Password READ password WRITE setPassword ) - Q_PROPERTY(QString Uuid READ uuid WRITE setUuid ) - Q_PROPERTY(QString Url READ url WRITE setUrl ) - Q_PROPERTY(QString SubmitUrl READ submitUrl WRITE setSubmitUrl ) - Q_PROPERTY(QString Key READ key WRITE setKey ) - Q_PROPERTY(QString Id READ id WRITE setId ) - Q_PROPERTY(QString Verifier READ verifier WRITE setVerifier ) - Q_PROPERTY(QString Nonce READ nonce WRITE setNonce ) - Q_PROPERTY(QString Realm READ realm WRITE setRealm ) - -public: - Request(); - bool fromJson(QString text); - - KeepassHttpProtocol::RequestType requestType() const; - QString requestTypeStr() const; - bool sortSelection() const; - QString login() const; - QString password() const; - QString uuid() const; - QString url() const; - QString submitUrl() const; - QString key() const; - QString id() const; - QString verifier() const; - QString nonce() const; - QString realm() const; - bool CheckVerifier(const QString & key) const; - -private: - void setRequestType(const QString &requestType); - void setSortSelection(bool sortSelection); - void setLogin(const QString &login); - void setPassword(const QString &password); - void setUuid(const QString &uuid); - void setUrl(const QString &url); - void setSubmitUrl(const QString &submitUrl); - void setKey(const QString &key); - void setId(const QString &id); - void setVerifier(const QString &verifier); - void setNonce(const QString &nonce); - void setRealm(const QString &realm); - - QString m_requestType; - bool m_sortSelection; - QString m_login; - QString m_password; - QString m_uuid; - QString m_url; - QString m_submitUrl; - QString m_key; - QString m_id; - QString m_verifier; - QString m_nonce; - QString m_realm; - mutable SymmetricCipherGcrypt m_cipher; -}; - -class StringField : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString Key READ key ) - Q_PROPERTY(QString Value READ value) - -public: - StringField(); - StringField(const QString& key, const QString& value); - StringField(const StringField & other); - StringField &operator =(const StringField &other); - - QString key() const; - QString value() const; - -private: - QString m_key; - QString m_value; -}; - -class Entry : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString Login READ login ) - Q_PROPERTY(QString Password READ password ) - Q_PROPERTY(QString Uuid READ uuid ) - Q_PROPERTY(QString Name READ name ) - Q_PROPERTY(QVariant StringFields READ getStringFields) - -public: - Entry(); - Entry(QString name, QString login, QString password, QString uuid); - Entry(const Entry & other); - Entry &operator =(const Entry &other); - - QString login() const; - QString password() const; - QString uuid() const; - QString name() const; - QList stringFields() const; - void addStringField(const QString& key, const QString& value); - -private: - QVariant getStringFields() const; - - QString m_login; - QString m_password; - QString m_uuid; - QString m_name; - QList m_stringFields; -}; - -class Response : public QObject -{ - Q_OBJECT - Q_PROPERTY(QString RequestType READ requestTypeStr) - Q_PROPERTY(QString Error READ error ) - Q_PROPERTY(bool Success READ success ) - Q_PROPERTY(QString Id READ id ) - Q_PROPERTY(QString Version READ version ) - Q_PROPERTY(QString Hash READ hash ) - Q_PROPERTY(QVariant Count READ count ) - Q_PROPERTY(QVariant Entries READ getEntries ) - Q_PROPERTY(QString Nonce READ nonce ) - Q_PROPERTY(QString Verifier READ verifier ) - -public: - Response(const Request &request, QString hash); - - KeepassHttpProtocol::RequestType requestType() const; - QString error() const; - void setError(const QString &error = QString()); - bool success() const; - void setSuccess(); - QString id() const; - void setId(const QString &id); - QString version() const; - QString hash() const; - QVariant count() const; - void setCount(int count); - QVariant getEntries() const; - void setEntries(const QList &entries); - QString nonce() const; - QString verifier() const; - void setVerifier(QString key); - - QString toJson(); - -private: - QString requestTypeStr() const; - - QString m_requestType; - QString m_error; - bool m_success; - QString m_id; - int m_count; - QString m_version; - QString m_hash; - QList m_entries; - QString m_nonce; - QString m_verifier; - mutable SymmetricCipherGcrypt m_cipher; -}; - -}/*namespace KeepassHttpProtocol*/ - -#endif // RESPONSE_H diff --git a/src/http/Server.cpp b/src/http/Server.cpp deleted file mode 100644 index 08c5b20bf..000000000 --- a/src/http/Server.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include -#include -#include -#include - -#include "qhttp/qhttpserver.hpp" -#include "qhttp/qhttpserverresponse.hpp" -#include "qhttp/qhttpserverrequest.hpp" - -#include "Server.h" -#include "Protocol.h" -#include "HttpSettings.h" -#include "crypto/Crypto.h" - -using namespace KeepassHttpProtocol; -using namespace qhttp::server; - -Server::Server(QObject *parent) : - QObject(parent), - m_started(false), - m_server(nullptr) -{ - -} - -void Server::testAssociate(const Request& r, Response * protocolResp) -{ - if (r.id().isEmpty()) - return; //ping - - QString key = getKey(r.id()); - if (key.isEmpty() || !r.CheckVerifier(key)) - return; - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); -} - -void Server::associate(const Request& r, Response * protocolResp) -{ - if (!r.CheckVerifier(r.key())) - return; - - QString id = storeKey(r.key()); - if (id.isEmpty()) - return; - - protocolResp->setSuccess(); - protocolResp->setId(id); - protocolResp->setVerifier(r.key()); -} - -void Server::getLogins(const Request &r, Response *protocolResp) -{ - QString key = getKey(r.id()); - if (!r.CheckVerifier(key)) - return; - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); - QList entries = findMatchingEntries(r.id(), r.url(), r.submitUrl(), r.realm()); //TODO: filtering, request confirmation [in db adaptation layer?] - if (r.sortSelection()) { - //TODO: sorting (in db adaptation layer? here?) - } - protocolResp->setEntries(entries); -} - -void Server::getLoginsCount(const Request &r, Response *protocolResp) -{ - QString key = getKey(r.id()); - if (!r.CheckVerifier(key)) - return; - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); - protocolResp->setCount(countMatchingEntries(r.id(), r.url(), r.submitUrl(), r.realm())); -} - -void Server::getAllLogins(const Request &r, Response *protocolResp) -{ - QString key = getKey(r.id()); - if (!r.CheckVerifier(key)) - return; - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); - protocolResp->setEntries(searchAllEntries(r.id())); //TODO: ensure there is no password --> change API? -} - -void Server::setLogin(const Request &r, Response *protocolResp) -{ - QString key = getKey(r.id()); - if (!r.CheckVerifier(key)) - return; - - QString uuid = r.uuid(); - if (uuid.isEmpty()) - addEntry(r.id(), r.login(), r.password(), r.url(), r.submitUrl(), r.realm()); - else - updateEntry(r.id(), r.uuid(), r.login(), r.password(), r.url()); - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); -} - -void Server::generatePassword(const Request &r, Response *protocolResp) -{ - QString key = getKey(r.id()); - if (!r.CheckVerifier(key)) - return; - - QString password = generatePassword(); - QString bits = QString::number(HttpSettings::getbits()); - - protocolResp->setSuccess(); - protocolResp->setId(r.id()); - protocolResp->setVerifier(key); - protocolResp->setEntries(QList() << Entry("generate-password", bits, password, "generate-password")); - - int size = password.capacity(); - volatile auto* mem = reinterpret_cast(password.data()); - while (size--) { - *mem++ = 0; - } -} - -void Server::handleRequest(const QByteArray& data, QHttpResponse* response) -{ - Request r; - if (!r.fromJson(data)) - return; - - QByteArray hash = QCryptographicHash::hash( - (getDatabaseRootUuid() + getDatabaseRecycleBinUuid()).toUtf8(), - QCryptographicHash::Sha1).toHex(); - - Response protocolResp(r, QString::fromLatin1(hash)); - switch(r.requestType()) { - case INVALID: 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 ASSOCIATE: associate(r, &protocolResp); break; - case TEST_ASSOCIATE: testAssociate(r, &protocolResp); break; - case GENERATE_PASSWORD: generatePassword(r, &protocolResp); break; - } - - QString out = protocolResp.toJson().toUtf8(); - - // THIS IS A FAKE HACK!!! - // the real "error" is a misbehavior in the QJSON qobject2qvariant method - // 1. getLogins returns an empty QList into protocolResp->m_entries - // in toJson() function the following happened: - // 2. QJson::QObjectHelper::qobject2qvariant marks m_entries as invalid !!! - // 3. QJson::Serializer write out Entries as null instead of empty list - //(4. ChromeIPass tries to access Entries.length and fails with null pointer exception) - // the fake workaround replaces the (wrong) "Entries":null with "Entries:[] to give - // chromeIPass (and passIFox) en empty list - int pos1 = out.indexOf("\"Count\":0,"); - int pos2 = out.indexOf("\"Entries\":null,"); - if (pos1 != -1 && pos2 != -1) { - out.replace(pos2, 15, "\"Entries\":[],"); - } - - response->setStatusCode(qhttp::ESTATUS_OK); - response->addHeader("Content-Type", "application/json"); - response->end(out.toUtf8()); -} - -void Server::start(void) -{ - if (m_started) - return; - - // local loopback hardcoded, since KeePassHTTP handshake - // is not safe against interception - QHostAddress address("127.0.0.1"); - int port = HttpSettings::httpPort(); - - m_server = new QHttpServer(this); - m_server->listen(address, port); - connect(m_server, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)), this, SLOT(onNewRequest(QHttpRequest*, QHttpResponse*))); - - m_started = true; -} - -void Server::stop(void) -{ - if (!m_started) - return; - - m_server->stopListening(); - m_server->deleteLater(); - m_started = false; -} - -void Server::onNewRequest(QHttpRequest* request, QHttpResponse* response) -{ - if (!isDatabaseOpened()) { - if (!openDatabase()) { - response->setStatusCode(qhttp::ESTATUS_SERVICE_UNAVAILABLE); - response->end(); - return; - } - } - - request->collectData(1024); - - request->onEnd([=]() { - this->handleRequest(request->collectedData(), response); - }); -} diff --git a/src/http/Server.h b/src/http/Server.h deleted file mode 100644 index 08cdfa24a..000000000 --- a/src/http/Server.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef SERVER_H -#define SERVER_H - -#include -#include - -namespace qhttp { - namespace server { - class QHttpServer; - class QHttpRequest; - class QHttpResponse; - } -} - -namespace KeepassHttpProtocol { - -using namespace qhttp::server; - -class Request; -class Response; -class Entry; - -class Server : public QObject -{ - Q_OBJECT -public: - explicit Server(QObject *parent = 0); - - virtual bool isDatabaseOpened() const = 0; - virtual bool openDatabase() = 0; - virtual QString getDatabaseRootUuid() = 0; - virtual QString getDatabaseRecycleBinUuid() = 0; - virtual QString getKey(const QString &id) = 0; - virtual QString storeKey(const QString &key) = 0; - virtual QList findMatchingEntries(const QString &id, const QString &url, const QString & submitUrl, const QString & realm) = 0; - virtual int countMatchingEntries(const QString &id, const QString &url, const QString & submitUrl, const QString & realm) = 0; - virtual QList searchAllEntries(const QString &id) = 0; - virtual void addEntry(const QString &id, const QString &login, const QString &password, const QString &url, const QString &submitUrl, const QString &realm) = 0; - virtual void updateEntry(const QString &id, const QString &uuid, const QString &login, const QString &password, const QString &url) = 0; - virtual QString generatePassword() = 0; - -public slots: - void start(); - void stop(); - -private slots: - void onNewRequest(QHttpRequest* request, QHttpResponse* response); - void handleRequest(const QByteArray& data, QHttpResponse* response); - -private: - void testAssociate(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void associate(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void getLogins(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void getLoginsCount(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void getAllLogins(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void setLogin(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - void generatePassword(const KeepassHttpProtocol::Request &r, KeepassHttpProtocol::Response *protocolResp); - - bool m_started; - - QHttpServer* m_server; -}; - -} /*namespace KeepassHttpProtocol*/ - -#endif // SERVER_H diff --git a/src/http/Service.cpp b/src/http/Service.cpp deleted file mode 100644 index 7f74a3ae8..000000000 --- a/src/http/Service.cpp +++ /dev/null @@ -1,603 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#include -#include -#include - -#include "Service.h" -#include "Protocol.h" -#include "EntryConfig.h" -#include "AccessControlDialog.h" -#include "HttpSettings.h" - -#include "core/Database.h" -#include "core/Entry.h" -#include "core/Global.h" -#include "core/Group.h" -#include "core/EntrySearcher.h" -#include "core/Metadata.h" -#include "core/Uuid.h" -#include "core/PasswordGenerator.h" - -#include - -static const unsigned char KEEPASSHTTP_UUID_DATA[] = { - 0x34, 0x69, 0x7a, 0x40, 0x8a, 0x5b, 0x41, 0xc0, - 0x9f, 0x36, 0x89, 0x7d, 0x62, 0x3e, 0xcb, 0x31 -}; -static const Uuid KEEPASSHTTP_UUID = Uuid(QByteArray::fromRawData(reinterpret_cast(KEEPASSHTTP_UUID_DATA), sizeof(KEEPASSHTTP_UUID_DATA))); -static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; -static const char ASSOCIATE_KEY_PREFIX[] = "AES Key: "; -static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords"; //Group where new KeePassHttp password are stored -static int KEEPASSHTTP_DEFAULT_ICON = 1; -//private const int DEFAULT_NOTIFICATION_TIME = 5000; - -Service::Service(DatabaseTabWidget* parent) : - KeepassHttpProtocol::Server(parent), - m_dbTabWidget(parent) -{ - if (HttpSettings::isEnabled()) - start(); -} - -Entry* Service::getConfigEntry(bool create) -{ - if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database * db = dbWidget->database()) { - Entry* entry = db->resolveEntry(KEEPASSHTTP_UUID); - if (!entry && create) { - entry = new Entry(); - entry->setTitle(QLatin1String(KEEPASSHTTP_NAME)); - entry->setUuid(KEEPASSHTTP_UUID); - entry->setAutoTypeEnabled(false); - entry->setGroup(db->rootGroup()); - } else if (entry && entry->group() == db->metadata()->recycleBin()) { - if (create) - entry->setGroup(db->rootGroup()); - else - entry = NULL; - } - return entry; - } - return NULL; -} - -bool Service::isDatabaseOpened() const -{ - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) - switch(dbWidget->currentMode()) { - case DatabaseWidget::None: - case DatabaseWidget::LockedMode: - break; - - case DatabaseWidget::ViewMode: - case DatabaseWidget::EditMode: - return true; - default: - break; - } - return false; -} - -bool Service::openDatabase() -{ - if (!HttpSettings::unlockDatabase()) - return false; - if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) { - switch(dbWidget->currentMode()) { - case DatabaseWidget::None: - case DatabaseWidget::LockedMode: - break; - - case DatabaseWidget::ViewMode: - case DatabaseWidget::EditMode: - return true; - default: - break; - } - } - //if (HttpSettings::showNotification() - // && !ShowNotification(QString("%0: %1 is requesting access, click to allow or deny") - // .arg(id).arg(submitHost.isEmpty() ? host : submithost)); - // return false; - m_dbTabWidget->activateWindow(); - //Wait a bit for DB to be open... (w/ asynchronous reply?) - return false; -} - -QString Service::getDatabaseRootUuid() -{ - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database* db = dbWidget->database()) - if (Group* rootGroup = db->rootGroup()) - return rootGroup->uuid().toHex(); - return QString(); -} - -QString Service::getDatabaseRecycleBinUuid() -{ - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database* db = dbWidget->database()) - if (Group* recycleBin = db->metadata()->recycleBin()) - return recycleBin->uuid().toHex(); - return QString(); -} - -QString Service::getKey(const QString &id) -{ - if (Entry* config = getConfigEntry()) - return config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id); - return QString(); -} - -QString Service::storeKey(const QString &key) -{ - QString id; - if (Entry* config = getConfigEntry(true)) { - - //ShowNotification("New key association requested") - - do { - bool ok; - //Indicate who wants to associate, and request user to enter the 'name' of association key - id = QInputDialog::getText(0, - tr("KeePassXC: New key association request"), - tr("You have received an association " - "request for the above key.\n" - "If you would like to allow it access " - "to your KeePassXC database\n" - "give it a unique name to identify and accept it."), - QLineEdit::Normal, QString(), &ok); - if (!ok || id.isEmpty()) - return QString(); - - //Warn if association key already exists - } while(config->attributes()->contains(QLatin1String(ASSOCIATE_KEY_PREFIX) + id) && - QMessageBox::warning(0, tr("KeePassXC: Overwrite existing key?"), - tr("A shared encryption-key with the name \"%1\" already exists.\nDo you want to overwrite it?").arg(id), - QMessageBox::Yes | QMessageBox::No) == QMessageBox::No); - - config->attributes()->set(QLatin1String(ASSOCIATE_KEY_PREFIX) + id, key, true); - } - return id; -} - -bool Service::matchUrlScheme(const QString & url) -{ - QString str = url.left(8).toLower(); - return str.startsWith("http://") || - str.startsWith("https://") || - str.startsWith("ftp://") || - str.startsWith("ftps://"); -} - -bool Service::removeFirstDomain(QString & hostname) -{ - int pos = hostname.indexOf("."); - if (pos < 0) - return false; - hostname = hostname.mid(pos + 1); - return !hostname.isEmpty(); -} - -QList Service::searchEntries(Database* db, const QString& hostname) -{ - QList entries; - if (Group* rootGroup = db->rootGroup()) { - const auto results = EntrySearcher().search(hostname, rootGroup, Qt::CaseInsensitive); - for (Entry* entry: results) { - QString title = entry->title(); - QString url = entry->webUrl(); - - //Filter to match hostname in Title and Url fields - if ( (!title.isEmpty() && hostname.contains(title)) - || (!url.isEmpty() && hostname.contains(url)) - || (matchUrlScheme(title) && hostname.endsWith(QUrl(title).host())) - || (matchUrlScheme(url) && hostname.endsWith(QUrl(url).host())) ) - entries.append(entry); - } - } - return entries; -} - -QList Service::searchEntries(const QString& text) -{ - //Get the list of databases to search - QList databases; - if (HttpSettings::searchInAllDatabases()) { - for (int i = 0; i < m_dbTabWidget->count(); i++) - if (DatabaseWidget* dbWidget = qobject_cast(m_dbTabWidget->widget(i))) - if (Database* db = dbWidget->database()) - databases << db; - } - else if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) { - if (Database* db = dbWidget->database()) - databases << db; - } - - //Search entries matching the hostname - QString hostname = QUrl(text).host(); - QList entries; - do { - for (Database* db: asConst(databases)) { - entries << searchEntries(db, hostname); - } - } while(entries.isEmpty() && removeFirstDomain(hostname)); - - return entries; -} - -Service::Access Service::checkAccess(const Entry *entry, const QString & host, const QString & submitHost, const QString & realm) -{ - EntryConfig config; - if (!config.load(entry)) - return Unknown; //not configured - if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) - return Allowed; //allowed - if ((config.isDenied(host)) || (!submitHost.isEmpty() && config.isDenied(submitHost))) - return Denied; //denied - if (!realm.isEmpty() && config.realm() != realm) - return Denied; - return Unknown; //not configured for this host -} - -KeepassHttpProtocol::Entry Service::prepareEntry(const Entry* entry) -{ - KeepassHttpProtocol::Entry res(entry->resolveMultiplePlaceholders(entry->title()), - entry->resolveMultiplePlaceholders(entry->username()), - entry->resolveMultiplePlaceholders(entry->password()), - entry->uuid().toHex()); - if (HttpSettings::supportKphFields()) { - const EntryAttributes * attr = entry->attributes(); - const auto keys = attr->keys(); - for (const QString& key: keys) { - if (key.startsWith(QLatin1String("KPH: "))) { - res.addStringField(key, entry->resolveMultiplePlaceholders(attr->value(key))); - } - } - } - return res; -} - -int Service::sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const -{ - QUrl url(entry->url()); - if (url.scheme().isEmpty()) - url.setScheme("http"); - const QString entryURL = url.toString(QUrl::StripTrailingSlash); - const QString baseEntryURL = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment); - - if (submitUrl == entryURL) - return 100; - if (submitUrl.startsWith(entryURL) && entryURL != host && baseSubmitUrl != entryURL) - return 90; - if (submitUrl.startsWith(baseEntryURL) && entryURL != host && baseSubmitUrl != baseEntryURL) - return 80; - if (entryURL == host) - return 70; - if (entryURL == baseSubmitUrl) - return 60; - if (entryURL.startsWith(submitUrl)) - return 50; - if (entryURL.startsWith(baseSubmitUrl) && baseSubmitUrl != host) - return 40; - if (submitUrl.startsWith(entryURL)) - return 30; - if (submitUrl.startsWith(baseEntryURL)) - return 20; - if (entryURL.startsWith(host)) - return 10; - if (host.startsWith(entryURL)) - return 5; - return 0; -} - -class Service::SortEntries -{ -public: - SortEntries(const QHash& priorities, const QString & field): - m_priorities(priorities), m_field(field) - {} - - bool operator()(const Entry* left, const Entry* right) const - { - int res = m_priorities.value(left) - m_priorities.value(right); - if (res == 0) - return QString::localeAwareCompare(left->attributes()->value(m_field), right->attributes()->value(m_field)) < 0; - return res < 0; - } - -private: - const QHash& m_priorities; - const QString m_field; -}; - -QList Service::findMatchingEntries(const QString& /*id*/, const QString& url, const QString& submitUrl, const QString& realm) -{ - const bool alwaysAllowAccess = HttpSettings::alwaysAllowAccess(); - const QString host = QUrl(url).host(); - const QString submitHost = QUrl(submitUrl).host(); - - //Check entries for authorization - QList pwEntriesToConfirm; - QList pwEntries; - const auto entries = searchEntries(url); - for (Entry* entry: entries) { - switch(checkAccess(entry, host, submitHost, realm)) { - case Denied: - continue; - - case Unknown: - if (alwaysAllowAccess) - pwEntries.append(entry); - else - pwEntriesToConfirm.append(entry); - break; - - case Allowed: - pwEntries.append(entry); - break; - } - } - - //If unsure, ask user for confirmation - //if (!pwEntriesToConfirm.isEmpty() - // && HttpSettings::showNotification() - // && !ShowNotification(QString("%0: %1 is requesting access, click to allow or deny") - // .arg(id).arg(submitHost.isEmpty() ? host : submithost)); - // pwEntriesToConfirm.clear(); //timeout --> do not request confirmation - - if (!pwEntriesToConfirm.isEmpty()) { - - AccessControlDialog dlg; - dlg.setUrl(url); - dlg.setItems(pwEntriesToConfirm); - //dlg.setRemember(); //TODO: setting! - - int res = dlg.exec(); - if (dlg.remember()) { - for (Entry* entry: asConst(pwEntriesToConfirm)) { - EntryConfig config; - config.load(entry); - if (res == QDialog::Accepted) { - config.allow(host); - if (!submitHost.isEmpty() && host != submitHost) - config.allow(submitHost); - } else if (res == QDialog::Rejected) { - config.deny(host); - if (!submitHost.isEmpty() && host != submitHost) - config.deny(submitHost); - } - if (!realm.isEmpty()) - config.setRealm(realm); - config.save(entry); - } - } - if (res == QDialog::Accepted) - pwEntries.append(pwEntriesToConfirm); - } - - //Sort results - const bool sortSelection = true; - if (sortSelection) { - QUrl url(submitUrl); - if (url.scheme().isEmpty()) - url.setScheme("http"); - const QString submitUrl = url.toString(QUrl::StripTrailingSlash); - const QString baseSubmitURL = url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment); - - //Cache priorities - QHash priorities; - priorities.reserve(pwEntries.size()); - for (const Entry* entry: asConst(pwEntries)) { - priorities.insert(entry, sortPriority(entry, host, submitUrl, baseSubmitURL)); - } - - //Sort by priorities - std::sort(pwEntries.begin(), pwEntries.end(), SortEntries(priorities, HttpSettings::sortByTitle() ? "Title" : "UserName")); - } - - //Fill the list - QList result; - result.reserve(pwEntries.count()); - for (Entry* entry: asConst(pwEntries)) { - result << prepareEntry(entry); - } - return result; -} - -int Service::countMatchingEntries(const QString &, const QString &url, const QString &, const QString &) -{ - return searchEntries(url).count(); -} - -QList Service::searchAllEntries(const QString &) -{ - QList result; - if (DatabaseWidget* dbWidget = m_dbTabWidget->currentDatabaseWidget()) { - if (Database* db = dbWidget->database()) { - if (Group* rootGroup = db->rootGroup()) { - const auto entries = rootGroup->entriesRecursive(); - for (Entry* entry: entries) { - if (!entry->url().isEmpty() || QUrl(entry->title()).isValid()) { - result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), - QString(), entry->uuid().toHex()); - } - } - } - } - } - return result; -} - -Group * Service::findCreateAddEntryGroup() -{ - if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database * db = dbWidget->database()) - if (Group * rootGroup = db->rootGroup()) { - //TODO: setting to decide where new keys are created - const QString groupName = QLatin1String(KEEPASSHTTP_GROUP_NAME); - - const auto groups = rootGroup->groupsRecursive(true); - for (const Group * g: groups) { - if (g->name() == groupName) { - return db->resolveGroup(g->uuid()); - } - } - - Group * group; - group = new Group(); - group->setUuid(Uuid::random()); - group->setName(groupName); - group->setIcon(KEEPASSHTTP_DEFAULT_ICON); - group->setParent(rootGroup); - return group; - } - return NULL; -} - -void Service::addEntry(const QString &, const QString &login, const QString &password, const QString &url, const QString &submitUrl, const QString &realm) -{ - if (Group * group = findCreateAddEntryGroup()) { - Entry * entry = new Entry(); - entry->setUuid(Uuid::random()); - entry->setTitle(QUrl(url).host()); - entry->setUrl(url); - entry->setIcon(KEEPASSHTTP_DEFAULT_ICON); - entry->setUsername(login); - entry->setPassword(password); - entry->setGroup(group); - - const QString host = QUrl(url).host(); - const QString submitHost = QUrl(submitUrl).host(); - EntryConfig config; - config.allow(host); - if (!submitHost.isEmpty()) - config.allow(submitHost); - if (!realm.isEmpty()) - config.setRealm(realm); - config.save(entry); - } -} - -void Service::updateEntry(const QString &, const QString &uuid, const QString &login, const QString &password, const QString &url) -{ - if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget()) - if (Database * db = dbWidget->database()) - if (Entry * entry = db->resolveEntry(Uuid::fromHex(uuid))) { - QString u = entry->username(); - if (u != login || entry->password() != password) { - //ShowNotification(QString("%0: You have an entry change prompt waiting, click to activate").arg(requestId)); - if ( HttpSettings::alwaysAllowUpdate() - || QMessageBox::warning(0, tr("KeePassXC: Update Entry"), - tr("Do you want to update the information in %1 - %2?") - .arg(QUrl(url).host().toHtmlEscaped()).arg(u.toHtmlEscaped()), - QMessageBox::Yes|QMessageBox::No) == QMessageBox::Yes ) { - entry->beginUpdate(); - entry->setUsername(login); - entry->setPassword(password); - entry->endUpdate(); - } - } - } -} - -QString Service::generatePassword() -{ - return HttpSettings::generatePassword(); -} - -void Service::removeSharedEncryptionKeys() -{ - if (!isDatabaseOpened()) { - QMessageBox::critical(0, tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - } else if (Entry* entry = getConfigEntry()) { - QStringList keysToRemove; - const auto keys = entry->attributes()->keys(); - for (const QString& key: keys) { - if (key.startsWith(ASSOCIATE_KEY_PREFIX)) { - keysToRemove << key; - } - } - - if(keysToRemove.count()) { - entry->beginUpdate(); - for (const QString& key: asConst(keysToRemove)) { - entry->attributes()->remove(key); - } - entry->endUpdate(); - - const int count = keysToRemove.count(); - QMessageBox::information(0, tr("KeePassXC: Removed keys from database"), - tr("Successfully removed %n encryption-key(s) from KeePassX/Http Settings.", "", count), - QMessageBox::Ok); - } else { - QMessageBox::information(0, tr("KeePassXC: No keys found"), - tr("No shared encryption-keys found in KeePassHttp Settings."), - QMessageBox::Ok); - } - } else { - QMessageBox::information(0, tr("KeePassXC: Settings not available!"), - tr("The active database does not contain an entry of KeePassHttp Settings."), - QMessageBox::Ok); - } -} - -void Service::removeStoredPermissions() -{ - if (!isDatabaseOpened()) { - QMessageBox::critical(0, tr("KeePassXC: Database locked!"), - tr("The active database is locked!\n" - "Please unlock the selected database or choose another one which is unlocked."), - QMessageBox::Ok); - } else { - Database * db = m_dbTabWidget->currentDatabaseWidget()->database(); - QList entries = db->rootGroup()->entriesRecursive(); - - QProgressDialog progress(tr("Removing stored permissions..."), tr("Abort"), 0, entries.count()); - progress.setWindowModality(Qt::WindowModal); - - uint counter = 0; - for (Entry* entry: asConst(entries)) { - if (progress.wasCanceled()) - return; - if (entry->attributes()->contains(KEEPASSHTTP_NAME)) { - entry->beginUpdate(); - entry->attributes()->remove(KEEPASSHTTP_NAME); - entry->endUpdate(); - counter ++; - } - progress.setValue(progress.value() + 1); - } - progress.reset(); - - if (counter > 0) { - QMessageBox::information(0, tr("KeePassXC: Removed permissions"), - tr("Successfully removed permissions from %n entries.", "", counter), - QMessageBox::Ok); - } else { - QMessageBox::information(0, tr("KeePassXC: No entry with permissions found!"), - tr("The active database does not contain an entry with permissions."), - QMessageBox::Ok); - } - } -} diff --git a/src/http/Service.h b/src/http/Service.h deleted file mode 100644 index d60d884bb..000000000 --- a/src/http/Service.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -* Copyright (C) 2013 Francois Ferrand -* Copyright (C) 2017 KeePassXC Team -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 or (at your option) -* version 3 of the License. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see . -*/ - -#ifndef SERVICE_H -#define SERVICE_H - -#include -#include "gui/DatabaseTabWidget.h" -#include "Server.h" - -class Service : public KeepassHttpProtocol::Server -{ - Q_OBJECT - -public: - explicit Service(DatabaseTabWidget* parent = 0); - - virtual bool isDatabaseOpened() const; - virtual bool openDatabase(); - virtual QString getDatabaseRootUuid(); - virtual QString getDatabaseRecycleBinUuid(); - virtual QString getKey(const QString& id); - virtual QString storeKey(const QString& key); - virtual QList findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm); - virtual int countMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm); - virtual QList searchAllEntries(const QString& id); - virtual void addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm); - virtual void updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, const QString& url); - virtual QString generatePassword(); - -public slots: - void removeSharedEncryptionKeys(); - void removeStoredPermissions(); - -private: - enum Access { Denied, Unknown, Allowed}; - Entry* getConfigEntry(bool create = false); - bool matchUrlScheme(const QString& url); - Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); - bool removeFirstDomain(QString& hostname); - Group *findCreateAddEntryGroup(); - class SortEntries; - int sortPriority(const Entry *entry, const QString &host, const QString &submitUrl, const QString &baseSubmitUrl) const; - KeepassHttpProtocol::Entry prepareEntry(const Entry* entry); - QList searchEntries(Database* db, const QString& hostname); - QList searchEntries(const QString& text); - - DatabaseTabWidget * const m_dbTabWidget; -}; - -#endif // SERVICE_H diff --git a/src/http/qhttp/CMakeLists.txt b/src/http/qhttp/CMakeLists.txt deleted file mode 100644 index 3b4965b31..000000000 --- a/src/http/qhttp/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -project(qhttp) - -set(qhttp_SOURCES - qhttpabstracts.cpp - qhttpserverconnection.cpp - qhttpserverrequest.cpp - qhttpserverresponse.cpp - qhttpserver.cpp - qhttpclientrequest.cpp - qhttpclientresponse.cpp - qhttpclient.cpp - http-parser/http_parser.c -) - -add_library(qhttp STATIC ${qhttp_SOURCES}) -target_compile_definitions(qhttp PUBLIC QHTTP_MEMORY_LOG=0 QHTTP_EXPORT) -target_include_directories(qhttp PRIVATE .) -target_link_libraries(qhttp Qt5::Core Qt5::Network) \ No newline at end of file diff --git a/src/http/qhttp/LICENSE b/src/http/qhttp/LICENSE deleted file mode 100644 index a7d9ae479..000000000 --- a/src/http/qhttp/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Amir Zamani - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - diff --git a/src/http/qhttp/README.md b/src/http/qhttp/README.md deleted file mode 100644 index a359c2ff5..000000000 --- a/src/http/qhttp/README.md +++ /dev/null @@ -1,229 +0,0 @@ -# QHttp - - -### Table of contents -- [About](#about) -- [Sample codes](#sample-codes) -- [Features](#features) -- [Setup](#setup) -- [Multi-threading](#multi-threading) -- [Source tree](#source-tree) -- [Disclaimer](#disclaimer) -- [License](#license) - -## About -[TOC](#table-of-contents) - -`QHttp` is a lightweight, asynchronous and fast HTTP library, containing both server and client side classes for managing connections, parsing and building HTTP requests and responses. this project is inspired by [nikhilm/qhttpserver](https://github.com/nikhilm/qhttpserver) effort to implement a Qt HTTP server. `QHttp` pushes the idea further by implementing client classes and better memory management, a lot more Node.js-like API, ... - -* the fantastic [nodejs/http-parser](https://github.com/nodejs/http-parser) is the core parser of HTTP requests (server mode) and responses (client mode). - -* By using `std::function` and `c++11 lambda`, the API is intentionally similar to the [Node.js' http module](http://nodejs.org/api/http.html). Asynchronous and non-blocking HTTP programming is quite easy with `QHttp`. have a look at [sample codes](#sample-codes). - -* the objective of `QHttp` is being light weight with a simple API for Qt developers to implement RESTful web services in private (internal) zones. [more](#disclaimer) - - - -## Sample codes -[TOC](#table-of-contents) - -a HelloWorld **HTTP server** by `QHttp` looks like: -``` cpp -int main(int argc, char** argv) { - QCoreApplication app(argc, argv); - - using namespace qhttp::server; - QHttpServer server(&app); - // listening on 0.0.0.0:8080 - server.listen(QHostAddress::Any, 8080, [](QHttpRequest* req, QHttpResponse* res) { - - res->setStatusCode(qhttp::ESTATUS_OK); // http status 200 - //res->addHeader("connection", "close"); // optional, it's the default header - res->end("Hello World!\n"); // the response body data - // by "connection: close", the req and res objects will be deleted automatically. - }); - - if ( !server.isListening() ) { - fprintf(stderr, "failed. can not listen at port 8080!\n"); - return -1; - } - - return app.exec(); -} -``` - -to request weather information by **HTTP client**: -```cpp -int main(int argc, char** argv) { - QCoreApplication app(argc, argv); - - using namespace qhttp::client; - QHttpClient client(&app); - QByteArray httpBody; - - QUrl weatherUrl("http://api.openweathermap.org/data/2.5/weather?q=tehran,ir&units=metric&mode=xml"); - - client.request(qhttp::EHTTP_GET, weatherUrl, [&httpBody](QHttpResponse* res) { - // response handler, called when the HTTP headers of the response are ready - - // gather HTTP response data - res->onData([&httpBody](const QByteArray& chunk) { - httpBody.append(chunk); - }); - - // called when all data in HTTP response have been read. - res->onEnd([&httpBody]() { - // print the XML body of the response - puts("\n[incoming response:]"); - puts(httpBody.constData()); - puts("\n\n"); - - QCoreApplication::instance()->quit(); - }); - - // just for fun! print incoming headers: - puts("\n[Headers:]"); - const qhttp::THeaderHash& hs = res->headers(); - for ( auto cit = hs.constBegin(); cit != hs.constEnd(); cit++) { - printf("%s : %s\n", cit.key().constData(), cit.value().constData()); - } - }); - - // set a timeout for making the request - client.setConnectingTimeOut(10000, []{ - qDebug("connecting to HTTP server timed out!"); - QCoreApplication::quit(); - }); - - - return app.exec(); -} -``` - - -## Features -[TOC](#table-of-contents) - -* the only dependencies are: [Qt 5](http://qt-project.org/downloads), [c++11](http://en.wikipedia.org/wiki/C%2B%2B11) and [joyent/http-parser](https://github.com/joyent/http-parser) - -* both TCP and UNIX (local) sockets are supported as backend. - -* separate `namespace`s for server and client classes. - -* HTTP server classes: [QHttpServer](./src/qhttpserver.hpp), [QHttpConnection](./src/qhttpserverconnection.hpp), [QHttpRequest](./src/qhttpserverrequest.hpp) and [QHttpResponse](./src/qhttpserverresponse.hpp). - -* HTTP client classes: [QHttpClient](./src/qhttpclient.hpp), [QHttpRequest](./src/qhttpclientrequest.hpp) and [QHttpResponse](./src/qhttpclientresponse.hpp). - -* **automatic memory management** of objects. Instances of connections, requests and replies will be deleted automatically when socket drops or disconnected. - -* **PIMPL** (Private classes) to achieve better ABI compatibility and cleaner API. - -* **Asynchronous** and **non-blocking**. You can handle thousands of concurrent HTTP connections efficiently by a single thread, although a multi-threaded HTTP server is easy to implement. - -* **high throughput**, I have tried the `QHttp` and [gason++](https://github.com/azadkuh/gason--) to implement a REST/Json web service on an Ubuntu VPS (dual core + 512MB ram) with more than **5800** connections per second (stress test). On a MacBook Pro (i5 4258U 4cores with HT + 8096MB ram), `QHttp` easily reaches to more than **11700** connections / second. Generally `QHttp` is **1.5x ~ 3x** faster than `Node.js` depending on your machine / OS. check [benchmark app](./example/benchmard/README.md) to measure your system. - -* Tested under **Linux** (Ubuntu 12.04 LTS, 14.04 LTS, g++) and **OS X** (10.9/10.10/10.11, clang). Easily portable where ever Qt 5 works. (tested by some users on Windows7/msvc2013 and Windows8.1/msvc2015) - - -## Setup -[TOC](#table-of-contents) - -instructions: -```bash -# first clone this repository: -$> git clone --depth=1 https://github.com/azadkuh/qhttp.git -b master -$> cd qhttp - -# prepare dependencies: -$> ./update-dependencies.sh - -# now build the library and the examples -$> qmake qhttp.pro -$> make -j 8 -``` - -## Multi-threading -[TOC](#table-of-contents) - -As `QHttp` is **asynchronous** and **non-blocking**, your app can handle thousands of concurrent HTTP connections by a single thread. - -in some rare scenarios you may want to use multiple handler threads (although it's not the best solution): - -* there are some blocking APIs (QSql, system calls, ...) in your connection handler (adopting asynchronous layer over the blocking API is a better approach). - -* the hardware has lots of free cores and the measurement shows that the load on the main `QHttp` thread is close to highest limit. There you can spawn some other handler threads. - - -[benchmark example](./example/benchmark/README.md) shows how to implement a single or multi threaded HTTP app (both server and client). This example uses worker `QThread` and `QObject::moveToThread()` for worker objects. see also: [Subclassing no longer recommended way of using QThread](http://qt-project.org/doc/note_revisions/5/8/view). - -**Note**: -> moving objects between threads is an expensive job, more ever the locking/unlocking mechanism, creating or stopping threads, ... cost even more! so using multiple threads in an application is not guaranteed to get better performance, but it's guaranteed to add more complexity, nasty bugs and headache! - -see why other top performer networking libraries as ZeroMQ are concurrent but not multi-threaded by default: - -* [ZeroMQ : Multithreading Magic](http://zeromq.org/blog:multithreading-magic) -* [Node.js : about](http://nodejs.org/about/) - - -## Source tree -[TOC](#table-of-contents) - - -* **`3rdparty/`**: -will contain `http-parser` source tree as the only dependency. -this directory is created by setup. see also: [setup](#setup). - -* **`example/`**: -contains some sample applications representing the `QHttp` usage: - * **`helloworld/`**: - the HelloWorld example of `QHttp`, both server + client are represented. - see: [README@helloworld](./example/helloworld/README.md) - - * **`basic-server/`**: - a basic HTTP server shows how to collect the request body, and respond to the clients. - see: [README@basic-server](./example/basic-server/README.md) - - - * **`benchmark/`**: - a simple utility to measure the throughput (requests per second) of `QHttp` as a REST/Json server. this app provides both the server and attacking clients. - see: [README@benchmark](./example/benchmark/README.md) - - * **`nodejs/`**: - Node.js implementation of `benchmark/` in server mode. Provided for benchmarking `QHttp` with `Node.js` as a RESTFul service app. - see: [README@nodejs](./example/nodejs/README.md) - - -* **`src/`**: -holds the source code of `QHttp`. server classes are prefixed by `qhttpserver*` and client classes by `qhttpclient*`. - * **`private/`**: - Private classes of the library. see: [d-pointers](https://qt-project.org/wiki/Dpointer). - -* **`tmp/`**: -a temporary directory which is created while `make`ing the library and holds all the `.o`, `moc files`, etc. - -* **`xbin/`**: -all the executable and binaries will be placed on this folder by `make`. - - - - -## Disclaimer -[TOC](#table-of-contents) - -* Implementing a lightweight and simple HTTP server/client in Qt with Node.js like API, is the main purpose of `QHttp`. - -* There are lots of features in a full blown HTTP server which are out of scope of this small library, although those can be added on top of `QHttp`. - -* The client classes are by no mean designed as a `QNetworkAccessManager` replacement. `QHttpClient` is simpler and lighter, for serious scenarios just use `QNetworkAccessManager`. - -* I'm a busy person. - - -> If you have any ideas, critiques, suggestions or whatever you want to call it, please open an issue. I'll be happy to hear from you what you'd see in this lib. I think about all suggestions, and I try to add those that make sense. - - -## License -[TOC](#table-of-contents) - -Distributed under the MIT license. Copyright (c) 2014, Amir Zamani. - diff --git a/src/http/qhttp/http-parser/LICENSE-MIT b/src/http/qhttp/http-parser/LICENSE-MIT deleted file mode 100644 index 58010b388..000000000 --- a/src/http/qhttp/http-parser/LICENSE-MIT +++ /dev/null @@ -1,23 +0,0 @@ -http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright -Igor Sysoev. - -Additional changes are licensed under the same terms as NGINX and -copyright Joyent, Inc. and other Node contributors. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. diff --git a/src/http/qhttp/http-parser/README.md b/src/http/qhttp/http-parser/README.md deleted file mode 100644 index 439b30998..000000000 --- a/src/http/qhttp/http-parser/README.md +++ /dev/null @@ -1,246 +0,0 @@ -HTTP Parser -=========== - -[![Build Status](https://api.travis-ci.org/nodejs/http-parser.svg?branch=master)](https://travis-ci.org/nodejs/http-parser) - -This is a parser for HTTP messages written in C. It parses both requests and -responses. The parser is designed to be used in performance HTTP -applications. It does not make any syscalls nor allocations, it does not -buffer data, it can be interrupted at anytime. Depending on your -architecture, it only requires about 40 bytes of data per message -stream (in a web server that is per connection). - -Features: - - * No dependencies - * Handles persistent streams (keep-alive). - * Decodes chunked encoding. - * Upgrade support - * Defends against buffer overflow attacks. - -The parser extracts the following information from HTTP messages: - - * Header fields and values - * Content-Length - * Request method - * Response status code - * Transfer-Encoding - * HTTP version - * Request URL - * Message body - - -Usage ------ - -One `http_parser` object is used per TCP connection. Initialize the struct -using `http_parser_init()` and set the callbacks. That might look something -like this for a request parser: -```c -http_parser_settings settings; -settings.on_url = my_url_callback; -settings.on_header_field = my_header_field_callback; -/* ... */ - -http_parser *parser = malloc(sizeof(http_parser)); -http_parser_init(parser, HTTP_REQUEST); -parser->data = my_socket; -``` - -When data is received on the socket execute the parser and check for errors. - -```c -size_t len = 80*1024, nparsed; -char buf[len]; -ssize_t recved; - -recved = recv(fd, buf, len, 0); - -if (recved < 0) { - /* Handle error. */ -} - -/* Start up / continue the parser. - * Note we pass recved==0 to signal that EOF has been received. - */ -nparsed = http_parser_execute(parser, &settings, buf, recved); - -if (parser->upgrade) { - /* handle new protocol */ -} else if (nparsed != recved) { - /* Handle error. Usually just close the connection. */ -} -``` - -HTTP needs to know where the end of the stream is. For example, sometimes -servers send responses without Content-Length and expect the client to -consume input (for the body) until EOF. To tell http_parser about EOF, give -`0` as the fourth parameter to `http_parser_execute()`. Callbacks and errors -can still be encountered during an EOF, so one must still be prepared -to receive them. - -Scalar valued message information such as `status_code`, `method`, and the -HTTP version are stored in the parser structure. This data is only -temporally stored in `http_parser` and gets reset on each new message. If -this information is needed later, copy it out of the structure during the -`headers_complete` callback. - -The parser decodes the transfer-encoding for both requests and responses -transparently. That is, a chunked encoding is decoded before being sent to -the on_body callback. - - -The Special Problem of Upgrade ------------------------------- - -HTTP supports upgrading the connection to a different protocol. An -increasingly common example of this is the WebSocket protocol which sends -a request like - - GET /demo HTTP/1.1 - Upgrade: WebSocket - Connection: Upgrade - Host: example.com - Origin: http://example.com - WebSocket-Protocol: sample - -followed by non-HTTP data. - -(See [RFC6455](https://tools.ietf.org/html/rfc6455) for more information the -WebSocket protocol.) - -To support this, the parser will treat this as a normal HTTP message without a -body, issuing both on_headers_complete and on_message_complete callbacks. However -http_parser_execute() will stop parsing at the end of the headers and return. - -The user is expected to check if `parser->upgrade` has been set to 1 after -`http_parser_execute()` returns. Non-HTTP data begins at the buffer supplied -offset by the return value of `http_parser_execute()`. - - -Callbacks ---------- - -During the `http_parser_execute()` call, the callbacks set in -`http_parser_settings` will be executed. The parser maintains state and -never looks behind, so buffering the data is not necessary. If you need to -save certain data for later usage, you can do that from the callbacks. - -There are two types of callbacks: - -* notification `typedef int (*http_cb) (http_parser*);` - Callbacks: on_message_begin, on_headers_complete, on_message_complete. -* data `typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);` - Callbacks: (requests only) on_url, - (common) on_header_field, on_header_value, on_body; - -Callbacks must return 0 on success. Returning a non-zero value indicates -error to the parser, making it exit immediately. - -For cases where it is necessary to pass local information to/from a callback, -the `http_parser` object's `data` field can be used. -An example of such a case is when using threads to handle a socket connection, -parse a request, and then give a response over that socket. By instantiation -of a thread-local struct containing relevant data (e.g. accepted socket, -allocated memory for callbacks to write into, etc), a parser's callbacks are -able to communicate data between the scope of the thread and the scope of the -callback in a threadsafe manner. This allows http-parser to be used in -multi-threaded contexts. - -Example: -```c - typedef struct { - socket_t sock; - void* buffer; - int buf_len; - } custom_data_t; - - -int my_url_callback(http_parser* parser, const char *at, size_t length) { - /* access to thread local custom_data_t struct. - Use this access save parsed data for later use into thread local - buffer, or communicate over socket - */ - parser->data; - ... - return 0; -} - -... - -void http_parser_thread(socket_t sock) { - int nparsed = 0; - /* allocate memory for user data */ - custom_data_t *my_data = malloc(sizeof(custom_data_t)); - - /* some information for use by callbacks. - * achieves thread -> callback information flow */ - my_data->sock = sock; - - /* instantiate a thread-local parser */ - http_parser *parser = malloc(sizeof(http_parser)); - http_parser_init(parser, HTTP_REQUEST); /* initialise parser */ - /* this custom data reference is accessible through the reference to the - parser supplied to callback functions */ - parser->data = my_data; - - http_parser_settings settings; /* set up callbacks */ - settings.on_url = my_url_callback; - - /* execute parser */ - nparsed = http_parser_execute(parser, &settings, buf, recved); - - ... - /* parsed information copied from callback. - can now perform action on data copied into thread-local memory from callbacks. - achieves callback -> thread information flow */ - my_data->buffer; - ... -} - -``` - -In case you parse HTTP message in chunks (i.e. `read()` request line -from socket, parse, read half headers, parse, etc) your data callbacks -may be called more than once. Http-parser guarantees that data pointer is only -valid for the lifetime of callback. You can also `read()` into a heap allocated -buffer to avoid copying memory around if this fits your application. - -Reading headers may be a tricky task if you read/parse headers partially. -Basically, you need to remember whether last header callback was field or value -and apply the following logic: - - (on_header_field and on_header_value shortened to on_h_*) - ------------------------ ------------ -------------------------------------------- - | State (prev. callback) | Callback | Description/action | - ------------------------ ------------ -------------------------------------------- - | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | - | | | into it | - ------------------------ ------------ -------------------------------------------- - | value | on_h_field | New header started. | - | | | Copy current name,value buffers to headers | - | | | list and allocate new buffer for new name | - ------------------------ ------------ -------------------------------------------- - | field | on_h_field | Previous name continues. Reallocate name | - | | | buffer and append callback data to it | - ------------------------ ------------ -------------------------------------------- - | field | on_h_value | Value for current header started. Allocate | - | | | new buffer and copy callback data to it | - ------------------------ ------------ -------------------------------------------- - | value | on_h_value | Value continues. Reallocate value buffer | - | | | and append callback data to it | - ------------------------ ------------ -------------------------------------------- - - -Parsing URLs ------------- - -A simplistic zero-copy URL parser is provided as `http_parser_parse_url()`. -Users of this library may wish to use it to parse URLs constructed from -consecutive `on_url` callbacks. - -See examples of reading in headers: - -* [partial example](http://gist.github.com/155877) in C -* [from http-parser tests](http://github.com/joyent/http-parser/blob/37a0ff8/test.c#L403) in C -* [from Node library](http://github.com/joyent/node/blob/842eaf4/src/http.js#L284) in Javascript diff --git a/src/http/qhttp/http-parser/http_parser.c b/src/http/qhttp/http-parser/http_parser.c deleted file mode 100644 index ab48d0cb5..000000000 --- a/src/http/qhttp/http-parser/http_parser.c +++ /dev/null @@ -1,2476 +0,0 @@ -/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev - * - * Additional changes are licensed under the same terms as NGINX and - * copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#include "http_parser.h" -#include -#include -#include -#include -#include -#include - -#ifndef ULLONG_MAX -# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ -#endif - -#ifndef MIN -# define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef ARRAY_SIZE -# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) -#endif - -#ifndef BIT_AT -# define BIT_AT(a, i) \ - (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ - (1 << ((unsigned int) (i) & 7)))) -#endif - -#ifndef ELEM_AT -# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) -#endif - -#define SET_ERRNO(e) \ -do { \ - parser->http_errno = (e); \ -} while(0) - -#define CURRENT_STATE() p_state -#define UPDATE_STATE(V) p_state = (enum state) (V); -#define RETURN(V) \ -do { \ - parser->state = CURRENT_STATE(); \ - return (V); \ -} while (0); -#define REEXECUTE() \ - goto reexecute; \ - - -#ifdef __GNUC__ -# define LIKELY(X) __builtin_expect(!!(X), 1) -# define UNLIKELY(X) __builtin_expect(!!(X), 0) -#else -# define LIKELY(X) (X) -# define UNLIKELY(X) (X) -#endif - - -/* Run the notify callback FOR, returning ER if it fails */ -#define CALLBACK_NOTIFY_(FOR, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != settings->on_##FOR(parser))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ -} while (0) - -/* Run the notify callback FOR and consume the current byte */ -#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) - -/* Run the notify callback FOR and don't consume the current byte */ -#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) - -/* Run data callback FOR with LEN bytes, returning ER if it fails */ -#define CALLBACK_DATA_(FOR, LEN, ER) \ -do { \ - assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ - \ - if (FOR##_mark) { \ - if (LIKELY(settings->on_##FOR)) { \ - parser->state = CURRENT_STATE(); \ - if (UNLIKELY(0 != \ - settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \ - SET_ERRNO(HPE_CB_##FOR); \ - } \ - UPDATE_STATE(parser->state); \ - \ - /* We either errored above or got paused; get out */ \ - if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \ - return (ER); \ - } \ - } \ - FOR##_mark = NULL; \ - } \ -} while (0) - -/* Run the data callback FOR and consume the current byte */ -#define CALLBACK_DATA(FOR) \ - CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) - -/* Run the data callback FOR and don't consume the current byte */ -#define CALLBACK_DATA_NOADVANCE(FOR) \ - CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) - -/* Set the mark FOR; non-destructive if mark is already set */ -#define MARK(FOR) \ -do { \ - if (!FOR##_mark) { \ - FOR##_mark = p; \ - } \ -} while (0) - -/* Don't allow the total size of the HTTP headers (including the status - * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect - * embedders against denial-of-service attacks where the attacker feeds - * us a never-ending header that the embedder keeps buffering. - * - * This check is arguably the responsibility of embedders but we're doing - * it on the embedder's behalf because most won't bother and this way we - * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger - * than any reasonable request or response so this should never affect - * day-to-day operation. - */ -#define COUNT_HEADER_SIZE(V) \ -do { \ - parser->nread += (V); \ - if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \ - SET_ERRNO(HPE_HEADER_OVERFLOW); \ - goto error; \ - } \ -} while (0) - - -#define PROXY_CONNECTION "proxy-connection" -#define CONNECTION "connection" -#define CONTENT_LENGTH "content-length" -#define TRANSFER_ENCODING "transfer-encoding" -#define UPGRADE "upgrade" -#define CHUNKED "chunked" -#define KEEP_ALIVE "keep-alive" -#define CLOSE "close" - - -static const char *method_strings[] = - { -#define XX(num, name, string) #string, - HTTP_METHOD_MAP(XX) -#undef XX - }; - - -/* Tokens as defined by rfc 2616. Also lowercases them. - * token = 1* - * separators = "(" | ")" | "<" | ">" | "@" - * | "," | ";" | ":" | "\" | <"> - * | "/" | "[" | "]" | "?" | "=" - * | "{" | "}" | SP | HT - */ -static const char tokens[256] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0, 0, 0, 0, 0, 0, 0, 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0, '!', 0, '#', '$', '%', '&', '\'', -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 0, 0, '*', '+', 0, '-', '.', 0, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - '0', '1', '2', '3', '4', '5', '6', '7', -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - '8', '9', 0, 0, 0, 0, 0, 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 'x', 'y', 'z', 0, 0, 0, '^', '_', -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 'x', 'y', 'z', 0, '|', 0, '~', 0 }; - - -static const int8_t unhex[256] = - {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 - ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 - ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 - }; - - -#if HTTP_PARSER_STRICT -# define T(v) 0 -#else -# define T(v) v -#endif - - -static const uint8_t normal_url_char[32] = { -/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ - 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, -/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ - 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, -/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ - 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, -/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, -/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, -/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ - 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; - -#undef T - -enum state - { s_dead = 1 /* important that this is > 0 */ - - , s_start_req_or_res - , s_res_or_resp_H - , s_start_res - , s_res_H - , s_res_HT - , s_res_HTT - , s_res_HTTP - , s_res_first_http_major - , s_res_http_major - , s_res_first_http_minor - , s_res_http_minor - , s_res_first_status_code - , s_res_status_code - , s_res_status_start - , s_res_status - , s_res_line_almost_done - - , s_start_req - - , s_req_method - , s_req_spaces_before_url - , s_req_schema - , s_req_schema_slash - , s_req_schema_slash_slash - , s_req_server_start - , s_req_server - , s_req_server_with_at - , s_req_path - , s_req_query_string_start - , s_req_query_string - , s_req_fragment_start - , s_req_fragment - , s_req_http_start - , s_req_http_H - , s_req_http_HT - , s_req_http_HTT - , s_req_http_HTTP - , s_req_first_http_major - , s_req_http_major - , s_req_first_http_minor - , s_req_http_minor - , s_req_line_almost_done - - , s_header_field_start - , s_header_field - , s_header_value_discard_ws - , s_header_value_discard_ws_almost_done - , s_header_value_discard_lws - , s_header_value_start - , s_header_value - , s_header_value_lws - - , s_header_almost_done - - , s_chunk_size_start - , s_chunk_size - , s_chunk_parameters - , s_chunk_size_almost_done - - , s_headers_almost_done - , s_headers_done - - /* Important: 's_headers_done' must be the last 'header' state. All - * states beyond this must be 'body' states. It is used for overflow - * checking. See the PARSING_HEADER() macro. - */ - - , s_chunk_data - , s_chunk_data_almost_done - , s_chunk_data_done - - , s_body_identity - , s_body_identity_eof - - , s_message_done - }; - - -#define PARSING_HEADER(state) (state <= s_headers_done) - - -enum header_states - { h_general = 0 - , h_C - , h_CO - , h_CON - - , h_matching_connection - , h_matching_proxy_connection - , h_matching_content_length - , h_matching_transfer_encoding - , h_matching_upgrade - - , h_connection - , h_content_length - , h_transfer_encoding - , h_upgrade - - , h_matching_transfer_encoding_chunked - , h_matching_connection_token_start - , h_matching_connection_keep_alive - , h_matching_connection_close - , h_matching_connection_upgrade - , h_matching_connection_token - - , h_transfer_encoding_chunked - , h_connection_keep_alive - , h_connection_close - , h_connection_upgrade - }; - -enum http_host_state - { - s_http_host_dead = 1 - , s_http_userinfo_start - , s_http_userinfo - , s_http_host_start - , s_http_host_v6_start - , s_http_host - , s_http_host_v6 - , s_http_host_v6_end - , s_http_host_v6_zone_start - , s_http_host_v6_zone - , s_http_host_port_start - , s_http_host_port -}; - -/* Macros for character classes; depends on strict-mode */ -#define CR '\r' -#define LF '\n' -#define LOWER(c) (unsigned char)(c | 0x20) -#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') -#define IS_NUM(c) ((c) >= '0' && (c) <= '9') -#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) -#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) -#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ - (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ - (c) == ')') -#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ - (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ - (c) == '$' || (c) == ',') - -#define STRICT_TOKEN(c) (tokens[(unsigned char)c]) - -#if HTTP_PARSER_STRICT -#define TOKEN(c) (tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) -#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') -#else -#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) -#define IS_URL_CHAR(c) \ - (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) -#define IS_HOST_CHAR(c) \ - (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') -#endif - -/** - * Verify that a char is a valid visible (printable) US-ASCII - * character or %x80-FF - **/ -#define IS_HEADER_CHAR(ch) \ - (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127)) - -#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) - - -#if HTTP_PARSER_STRICT -# define STRICT_CHECK(cond) \ -do { \ - if (cond) { \ - SET_ERRNO(HPE_STRICT); \ - goto error; \ - } \ -} while (0) -# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) -#else -# define STRICT_CHECK(cond) -# define NEW_MESSAGE() start_state -#endif - - -/* Map errno values to strings for human-readable output */ -#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, -static struct { - const char *name; - const char *description; -} http_strerror_tab[] = { - HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) -}; -#undef HTTP_STRERROR_GEN - -int http_message_needs_eof(const http_parser *parser); - -/* Our URL parser. - * - * This is designed to be shared by http_parser_execute() for URL validation, - * hence it has a state transition + byte-for-byte interface. In addition, it - * is meant to be embedded in http_parser_parse_url(), which does the dirty - * work of turning state transitions URL components for its API. - * - * This function should only be invoked with non-space characters. It is - * assumed that the caller cares about (and can detect) the transition between - * URL and non-URL states by looking for these. - */ -static enum state -parse_url_char(enum state s, const char ch) -{ - if (ch == ' ' || ch == '\r' || ch == '\n') { - return s_dead; - } - -#if HTTP_PARSER_STRICT - if (ch == '\t' || ch == '\f') { - return s_dead; - } -#endif - - switch (s) { - case s_req_spaces_before_url: - /* Proxied requests are followed by scheme of an absolute URI (alpha). - * All methods except CONNECT are followed by '/' or '*'. - */ - - if (ch == '/' || ch == '*') { - return s_req_path; - } - - if (IS_ALPHA(ch)) { - return s_req_schema; - } - - break; - - case s_req_schema: - if (IS_ALPHA(ch)) { - return s; - } - - if (ch == ':') { - return s_req_schema_slash; - } - - break; - - case s_req_schema_slash: - if (ch == '/') { - return s_req_schema_slash_slash; - } - - break; - - case s_req_schema_slash_slash: - if (ch == '/') { - return s_req_server_start; - } - - break; - - case s_req_server_with_at: - if (ch == '@') { - return s_dead; - } - - /* FALLTHROUGH */ - case s_req_server_start: - case s_req_server: - if (ch == '/') { - return s_req_path; - } - - if (ch == '?') { - return s_req_query_string_start; - } - - if (ch == '@') { - return s_req_server_with_at; - } - - if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { - return s_req_server; - } - - break; - - case s_req_path: - if (IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - return s_req_query_string_start; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_query_string_start: - case s_req_query_string: - if (IS_URL_CHAR(ch)) { - return s_req_query_string; - } - - switch (ch) { - case '?': - /* allow extra '?' in query string */ - return s_req_query_string; - - case '#': - return s_req_fragment_start; - } - - break; - - case s_req_fragment_start: - if (IS_URL_CHAR(ch)) { - return s_req_fragment; - } - - switch (ch) { - case '?': - return s_req_fragment; - - case '#': - return s; - } - - break; - - case s_req_fragment: - if (IS_URL_CHAR(ch)) { - return s; - } - - switch (ch) { - case '?': - case '#': - return s; - } - - break; - - default: - break; - } - - /* We should never fall out of the switch above unless there's an error */ - return s_dead; -} - -size_t http_parser_execute (http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len) -{ - char c, ch; - int8_t unhex_val; - const char *p = data; - const char *header_field_mark = 0; - const char *header_value_mark = 0; - const char *url_mark = 0; - const char *body_mark = 0; - const char *status_mark = 0; - enum state p_state = (enum state) parser->state; - const unsigned int lenient = parser->lenient_http_headers; - - /* We're in an error state. Don't bother doing anything. */ - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - return 0; - } - - if (len == 0) { - switch (CURRENT_STATE()) { - case s_body_identity_eof: - /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if - * we got paused. - */ - CALLBACK_NOTIFY_NOADVANCE(message_complete); - return 0; - - case s_dead: - case s_start_req_or_res: - case s_start_res: - case s_start_req: - return 0; - - default: - SET_ERRNO(HPE_INVALID_EOF_STATE); - return 1; - } - } - - - if (CURRENT_STATE() == s_header_field) - header_field_mark = data; - if (CURRENT_STATE() == s_header_value) - header_value_mark = data; - switch (CURRENT_STATE()) { - case s_req_path: - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_server: - case s_req_server_with_at: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - url_mark = data; - break; - case s_res_status: - status_mark = data; - break; - default: - break; - } - - for (p=data; p != data + len; p++) { - ch = *p; - - if (PARSING_HEADER(CURRENT_STATE())) - COUNT_HEADER_SIZE(1); - -reexecute: - switch (CURRENT_STATE()) { - - case s_dead: - /* this state is used after a 'Connection: close' message - * the parser will error out if it reads another message - */ - if (LIKELY(ch == CR || ch == LF)) - break; - - SET_ERRNO(HPE_CLOSED_CONNECTION); - goto error; - - case s_start_req_or_res: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - if (ch == 'H') { - UPDATE_STATE(s_res_or_resp_H); - - CALLBACK_NOTIFY(message_begin); - } else { - parser->type = HTTP_REQUEST; - UPDATE_STATE(s_start_req); - REEXECUTE(); - } - - break; - } - - case s_res_or_resp_H: - if (ch == 'T') { - parser->type = HTTP_RESPONSE; - UPDATE_STATE(s_res_HT); - } else { - if (UNLIKELY(ch != 'E')) { - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - - parser->type = HTTP_REQUEST; - parser->method = HTTP_HEAD; - parser->index = 2; - UPDATE_STATE(s_req_method); - } - break; - - case s_start_res: - { - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - switch (ch) { - case 'H': - UPDATE_STATE(s_res_H); - break; - - case CR: - case LF: - break; - - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - - CALLBACK_NOTIFY(message_begin); - break; - } - - case s_res_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HT); - break; - - case s_res_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_res_HTT); - break; - - case s_res_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_res_HTTP); - break; - - case s_res_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_res_first_http_major); - break; - - case s_res_first_http_major: - if (UNLIKELY(ch < '0' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; - UPDATE_STATE(s_res_http_major); - break; - - /* major HTTP version or dot */ - case s_res_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_res_first_http_minor); - break; - } - - if (!IS_NUM(ch)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* first digit of minor HTTP version */ - case s_res_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor = ch - '0'; - UPDATE_STATE(s_res_http_minor); - break; - - /* minor HTTP version or end of request line */ - case s_res_http_minor: - { - if (ch == ' ') { - UPDATE_STATE(s_res_first_status_code); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - case s_res_first_status_code: - { - if (!IS_NUM(ch)) { - if (ch == ' ') { - break; - } - - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - parser->status_code = ch - '0'; - UPDATE_STATE(s_res_status_code); - break; - } - - case s_res_status_code: - { - if (!IS_NUM(ch)) { - switch (ch) { - case ' ': - UPDATE_STATE(s_res_status_start); - break; - case CR: - UPDATE_STATE(s_res_line_almost_done); - break; - case LF: - UPDATE_STATE(s_header_field_start); - break; - default: - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - break; - } - - parser->status_code *= 10; - parser->status_code += ch - '0'; - - if (UNLIKELY(parser->status_code > 999)) { - SET_ERRNO(HPE_INVALID_STATUS); - goto error; - } - - break; - } - - case s_res_status_start: - { - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - - MARK(status); - UPDATE_STATE(s_res_status); - parser->index = 0; - break; - } - - case s_res_status: - if (ch == CR) { - UPDATE_STATE(s_res_line_almost_done); - CALLBACK_DATA(status); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA(status); - break; - } - - break; - - case s_res_line_almost_done: - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_field_start); - break; - - case s_start_req: - { - if (ch == CR || ch == LF) - break; - parser->flags = 0; - parser->content_length = ULLONG_MAX; - - if (UNLIKELY(!IS_ALPHA(ch))) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - parser->method = (enum http_method) 0; - parser->index = 1; - switch (ch) { - case 'A': parser->method = HTTP_ACL; break; - case 'B': parser->method = HTTP_BIND; break; - case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; - case 'D': parser->method = HTTP_DELETE; break; - case 'G': parser->method = HTTP_GET; break; - case 'H': parser->method = HTTP_HEAD; break; - case 'L': parser->method = HTTP_LOCK; /* or LINK */ break; - case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break; - case 'N': parser->method = HTTP_NOTIFY; break; - case 'O': parser->method = HTTP_OPTIONS; break; - case 'P': parser->method = HTTP_POST; - /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ - break; - case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break; - case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; - case 'T': parser->method = HTTP_TRACE; break; - case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break; - default: - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - UPDATE_STATE(s_req_method); - - CALLBACK_NOTIFY(message_begin); - - break; - } - - case s_req_method: - { - const char *matcher; - if (UNLIKELY(ch == '\0')) { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - matcher = method_strings[parser->method]; - if (ch == ' ' && matcher[parser->index] == '\0') { - UPDATE_STATE(s_req_spaces_before_url); - } else if (ch == matcher[parser->index]) { - ; /* nada */ - } else if (IS_ALPHA(ch)) { - - switch (parser->method << 16 | parser->index << 8 | ch) { -#define XX(meth, pos, ch, new_meth) \ - case (HTTP_##meth << 16 | pos << 8 | ch): \ - parser->method = HTTP_##new_meth; break; - - XX(POST, 1, 'U', PUT) - XX(POST, 1, 'A', PATCH) - XX(CONNECT, 1, 'H', CHECKOUT) - XX(CONNECT, 2, 'P', COPY) - XX(MKCOL, 1, 'O', MOVE) - XX(MKCOL, 1, 'E', MERGE) - XX(MKCOL, 2, 'A', MKACTIVITY) - XX(MKCOL, 3, 'A', MKCALENDAR) - XX(SUBSCRIBE, 1, 'E', SEARCH) - XX(REPORT, 2, 'B', REBIND) - XX(POST, 1, 'R', PROPFIND) - XX(PROPFIND, 4, 'P', PROPPATCH) - XX(PUT, 2, 'R', PURGE) - XX(LOCK, 1, 'I', LINK) - XX(UNLOCK, 2, 'S', UNSUBSCRIBE) - XX(UNLOCK, 2, 'B', UNBIND) - XX(UNLOCK, 3, 'I', UNLINK) -#undef XX - - default: - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - } else if (ch == '-' && - parser->index == 1 && - parser->method == HTTP_MKCOL) { - parser->method = HTTP_MSEARCH; - } else { - SET_ERRNO(HPE_INVALID_METHOD); - goto error; - } - - ++parser->index; - break; - } - - case s_req_spaces_before_url: - { - if (ch == ' ') break; - - MARK(url); - if (parser->method == HTTP_CONNECT) { - UPDATE_STATE(s_req_server_start); - } - - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - - break; - } - - case s_req_schema: - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - { - switch (ch) { - /* No whitespace allowed here */ - case ' ': - case CR: - case LF: - SET_ERRNO(HPE_INVALID_URL); - goto error; - default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - } - - break; - } - - case s_req_server: - case s_req_server_with_at: - case s_req_path: - case s_req_query_string_start: - case s_req_query_string: - case s_req_fragment_start: - case s_req_fragment: - { - switch (ch) { - case ' ': - UPDATE_STATE(s_req_http_start); - CALLBACK_DATA(url); - break; - case CR: - case LF: - parser->http_major = 0; - parser->http_minor = 9; - UPDATE_STATE((ch == CR) ? - s_req_line_almost_done : - s_header_field_start); - CALLBACK_DATA(url); - break; - default: - UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch)); - if (UNLIKELY(CURRENT_STATE() == s_dead)) { - SET_ERRNO(HPE_INVALID_URL); - goto error; - } - } - break; - } - - case s_req_http_start: - switch (ch) { - case 'H': - UPDATE_STATE(s_req_http_H); - break; - case ' ': - break; - default: - SET_ERRNO(HPE_INVALID_CONSTANT); - goto error; - } - break; - - case s_req_http_H: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HT); - break; - - case s_req_http_HT: - STRICT_CHECK(ch != 'T'); - UPDATE_STATE(s_req_http_HTT); - break; - - case s_req_http_HTT: - STRICT_CHECK(ch != 'P'); - UPDATE_STATE(s_req_http_HTTP); - break; - - case s_req_http_HTTP: - STRICT_CHECK(ch != '/'); - UPDATE_STATE(s_req_first_http_major); - break; - - /* first digit of major HTTP version */ - case s_req_first_http_major: - if (UNLIKELY(ch < '1' || ch > '9')) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major = ch - '0'; - UPDATE_STATE(s_req_http_major); - break; - - /* major HTTP version or dot */ - case s_req_http_major: - { - if (ch == '.') { - UPDATE_STATE(s_req_first_http_minor); - break; - } - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_major *= 10; - parser->http_major += ch - '0'; - - if (UNLIKELY(parser->http_major > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* first digit of minor HTTP version */ - case s_req_first_http_minor: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor = ch - '0'; - UPDATE_STATE(s_req_http_minor); - break; - - /* minor HTTP version or end of request line */ - case s_req_http_minor: - { - if (ch == CR) { - UPDATE_STATE(s_req_line_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_field_start); - break; - } - - /* XXX allow spaces after digit? */ - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - parser->http_minor *= 10; - parser->http_minor += ch - '0'; - - if (UNLIKELY(parser->http_minor > 999)) { - SET_ERRNO(HPE_INVALID_VERSION); - goto error; - } - - break; - } - - /* end of request line */ - case s_req_line_almost_done: - { - if (UNLIKELY(ch != LF)) { - SET_ERRNO(HPE_LF_EXPECTED); - goto error; - } - - UPDATE_STATE(s_header_field_start); - break; - } - - case s_header_field_start: - { - if (ch == CR) { - UPDATE_STATE(s_headers_almost_done); - break; - } - - if (ch == LF) { - /* they might be just sending \n instead of \r\n so this would be - * the second \n to denote the end of headers*/ - UPDATE_STATE(s_headers_almost_done); - REEXECUTE(); - } - - c = TOKEN(ch); - - if (UNLIKELY(!c)) { - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } - - MARK(header_field); - - parser->index = 0; - UPDATE_STATE(s_header_field); - - switch (c) { - case 'c': - parser->header_state = h_C; - break; - - case 'p': - parser->header_state = h_matching_proxy_connection; - break; - - case 't': - parser->header_state = h_matching_transfer_encoding; - break; - - case 'u': - parser->header_state = h_matching_upgrade; - break; - - default: - parser->header_state = h_general; - break; - } - break; - } - - case s_header_field: - { - const char* start = p; - for (; p != data + len; p++) { - ch = *p; - c = TOKEN(ch); - - if (!c) - break; - - switch (parser->header_state) { - case h_general: - break; - - case h_C: - parser->index++; - parser->header_state = (c == 'o' ? h_CO : h_general); - break; - - case h_CO: - parser->index++; - parser->header_state = (c == 'n' ? h_CON : h_general); - break; - - case h_CON: - parser->index++; - switch (c) { - case 'n': - parser->header_state = h_matching_connection; - break; - case 't': - parser->header_state = h_matching_content_length; - break; - default: - parser->header_state = h_general; - break; - } - break; - - /* connection */ - - case h_matching_connection: - parser->index++; - if (parser->index > sizeof(CONNECTION)-1 - || c != CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* proxy-connection */ - - case h_matching_proxy_connection: - parser->index++; - if (parser->index > sizeof(PROXY_CONNECTION)-1 - || c != PROXY_CONNECTION[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { - parser->header_state = h_connection; - } - break; - - /* content-length */ - - case h_matching_content_length: - parser->index++; - if (parser->index > sizeof(CONTENT_LENGTH)-1 - || c != CONTENT_LENGTH[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { - parser->header_state = h_content_length; - } - break; - - /* transfer-encoding */ - - case h_matching_transfer_encoding: - parser->index++; - if (parser->index > sizeof(TRANSFER_ENCODING)-1 - || c != TRANSFER_ENCODING[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { - parser->header_state = h_transfer_encoding; - } - break; - - /* upgrade */ - - case h_matching_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE)-1 - || c != UPGRADE[parser->index]) { - parser->header_state = h_general; - } else if (parser->index == sizeof(UPGRADE)-2) { - parser->header_state = h_upgrade; - } - break; - - case h_connection: - case h_content_length: - case h_transfer_encoding: - case h_upgrade: - if (ch != ' ') parser->header_state = h_general; - break; - - default: - assert(0 && "Unknown header_state"); - break; - } - } - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) { - --p; - break; - } - - if (ch == ':') { - UPDATE_STATE(s_header_value_discard_ws); - CALLBACK_DATA(header_field); - break; - } - - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } - - case s_header_value_discard_ws: - if (ch == ' ' || ch == '\t') break; - - if (ch == CR) { - UPDATE_STATE(s_header_value_discard_ws_almost_done); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_value_discard_lws); - break; - } - - /* FALLTHROUGH */ - - case s_header_value_start: - { - MARK(header_value); - - UPDATE_STATE(s_header_value); - parser->index = 0; - - c = LOWER(ch); - - switch (parser->header_state) { - case h_upgrade: - parser->flags |= F_UPGRADE; - parser->header_state = h_general; - break; - - case h_transfer_encoding: - /* looking for 'Transfer-Encoding: chunked' */ - if ('c' == c) { - parser->header_state = h_matching_transfer_encoding_chunked; - } else { - parser->header_state = h_general; - } - break; - - case h_content_length: - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; - } - - if (parser->flags & F_CONTENTLENGTH) { - SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); - goto error; - } - - parser->flags |= F_CONTENTLENGTH; - parser->content_length = ch - '0'; - break; - - case h_connection: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - parser->header_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - parser->header_state = h_matching_connection_close; - } else if (c == 'u') { - parser->header_state = h_matching_connection_upgrade; - } else { - parser->header_state = h_matching_connection_token; - } - break; - - /* Multi-value `Connection` header */ - case h_matching_connection_token_start: - break; - - default: - parser->header_state = h_general; - break; - } - break; - } - - case s_header_value: - { - const char* start = p; - enum header_states h_state = (enum header_states) parser->header_state; - for (; p != data + len; p++) { - ch = *p; - if (ch == CR) { - UPDATE_STATE(s_header_almost_done); - parser->header_state = h_state; - CALLBACK_DATA(header_value); - break; - } - - if (ch == LF) { - UPDATE_STATE(s_header_almost_done); - COUNT_HEADER_SIZE(p - start); - parser->header_state = h_state; - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } - - if (!lenient && !IS_HEADER_CHAR(ch)) { - SET_ERRNO(HPE_INVALID_HEADER_TOKEN); - goto error; - } - - c = LOWER(ch); - - switch (h_state) { - case h_general: - { - const char* p_cr; - const char* p_lf; - size_t limit = data + len - p; - - limit = MIN(limit, HTTP_MAX_HEADER_SIZE); - - p_cr = (const char*) memchr(p, CR, limit); - p_lf = (const char*) memchr(p, LF, limit); - if (p_cr != NULL) { - if (p_lf != NULL && p_cr >= p_lf) - p = p_lf; - else - p = p_cr; - } else if (UNLIKELY(p_lf != NULL)) { - p = p_lf; - } else { - p = data + len; - } - --p; - - break; - } - - case h_connection: - case h_transfer_encoding: - assert(0 && "Shouldn't get here."); - break; - - case h_content_length: - { - uint64_t t; - - if (ch == ' ') break; - - if (UNLIKELY(!IS_NUM(ch))) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } - - t = parser->content_length; - t *= 10; - t += ch - '0'; - - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - parser->header_state = h_state; - goto error; - } - - parser->content_length = t; - break; - } - - /* Transfer-Encoding: chunked */ - case h_matching_transfer_encoding_chunked: - parser->index++; - if (parser->index > sizeof(CHUNKED)-1 - || c != CHUNKED[parser->index]) { - h_state = h_general; - } else if (parser->index == sizeof(CHUNKED)-2) { - h_state = h_transfer_encoding_chunked; - } - break; - - case h_matching_connection_token_start: - /* looking for 'Connection: keep-alive' */ - if (c == 'k') { - h_state = h_matching_connection_keep_alive; - /* looking for 'Connection: close' */ - } else if (c == 'c') { - h_state = h_matching_connection_close; - } else if (c == 'u') { - h_state = h_matching_connection_upgrade; - } else if (STRICT_TOKEN(c)) { - h_state = h_matching_connection_token; - } else if (c == ' ' || c == '\t') { - /* Skip lws */ - } else { - h_state = h_general; - } - break; - - /* looking for 'Connection: keep-alive' */ - case h_matching_connection_keep_alive: - parser->index++; - if (parser->index > sizeof(KEEP_ALIVE)-1 - || c != KEEP_ALIVE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(KEEP_ALIVE)-2) { - h_state = h_connection_keep_alive; - } - break; - - /* looking for 'Connection: close' */ - case h_matching_connection_close: - parser->index++; - if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(CLOSE)-2) { - h_state = h_connection_close; - } - break; - - /* looking for 'Connection: upgrade' */ - case h_matching_connection_upgrade: - parser->index++; - if (parser->index > sizeof(UPGRADE) - 1 || - c != UPGRADE[parser->index]) { - h_state = h_matching_connection_token; - } else if (parser->index == sizeof(UPGRADE)-2) { - h_state = h_connection_upgrade; - } - break; - - case h_matching_connection_token: - if (ch == ',') { - h_state = h_matching_connection_token_start; - parser->index = 0; - } - break; - - case h_transfer_encoding_chunked: - if (ch != ' ') h_state = h_general; - break; - - case h_connection_keep_alive: - case h_connection_close: - case h_connection_upgrade: - if (ch == ',') { - if (h_state == h_connection_keep_alive) { - parser->flags |= F_CONNECTION_KEEP_ALIVE; - } else if (h_state == h_connection_close) { - parser->flags |= F_CONNECTION_CLOSE; - } else if (h_state == h_connection_upgrade) { - parser->flags |= F_CONNECTION_UPGRADE; - } - h_state = h_matching_connection_token_start; - parser->index = 0; - } else if (ch != ' ') { - h_state = h_matching_connection_token; - } - break; - - default: - UPDATE_STATE(s_header_value); - h_state = h_general; - break; - } - } - parser->header_state = h_state; - - COUNT_HEADER_SIZE(p - start); - - if (p == data + len) - --p; - break; - } - - case s_header_almost_done: - { - if (UNLIKELY(ch != LF)) { - SET_ERRNO(HPE_LF_EXPECTED); - goto error; - } - - UPDATE_STATE(s_header_value_lws); - break; - } - - case s_header_value_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_start); - REEXECUTE(); - } - - /* finished the header */ - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - default: - break; - } - - UPDATE_STATE(s_header_field_start); - REEXECUTE(); - } - - case s_header_value_discard_ws_almost_done: - { - STRICT_CHECK(ch != LF); - UPDATE_STATE(s_header_value_discard_lws); - break; - } - - case s_header_value_discard_lws: - { - if (ch == ' ' || ch == '\t') { - UPDATE_STATE(s_header_value_discard_ws); - break; - } else { - switch (parser->header_state) { - case h_connection_keep_alive: - parser->flags |= F_CONNECTION_KEEP_ALIVE; - break; - case h_connection_close: - parser->flags |= F_CONNECTION_CLOSE; - break; - case h_connection_upgrade: - parser->flags |= F_CONNECTION_UPGRADE; - break; - case h_transfer_encoding_chunked: - parser->flags |= F_CHUNKED; - break; - default: - break; - } - - /* header value was empty */ - MARK(header_value); - UPDATE_STATE(s_header_field_start); - CALLBACK_DATA_NOADVANCE(header_value); - REEXECUTE(); - } - } - - case s_headers_almost_done: - { - STRICT_CHECK(ch != LF); - - if (parser->flags & F_TRAILING) { - /* End of a chunked request */ - UPDATE_STATE(s_message_done); - CALLBACK_NOTIFY_NOADVANCE(chunk_complete); - REEXECUTE(); - } - - /* Cannot use chunked encoding and a content-length header together - per the HTTP specification. */ - if ((parser->flags & F_CHUNKED) && - (parser->flags & F_CONTENTLENGTH)) { - SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH); - goto error; - } - - UPDATE_STATE(s_headers_done); - - /* Set this here so that on_headers_complete() callbacks can see it */ - parser->upgrade = - ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) == - (F_UPGRADE | F_CONNECTION_UPGRADE) || - parser->method == HTTP_CONNECT); - - /* Here we call the headers_complete callback. This is somewhat - * different than other callbacks because if the user returns 1, we - * will interpret that as saying that this message has no body. This - * is needed for the annoying case of recieving a response to a HEAD - * request. - * - * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so - * we have to simulate it by handling a change in errno below. - */ - if (settings->on_headers_complete) { - switch (settings->on_headers_complete(parser)) { - case 0: - break; - - case 2: - parser->upgrade = 1; -#if __GNUC__ >= 7 - __attribute__ ((fallthrough)); -#endif - - case 1: - parser->flags |= F_SKIPBODY; - break; - - default: - SET_ERRNO(HPE_CB_headers_complete); - RETURN(p - data); /* Error */ - } - } - - if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { - RETURN(p - data); - } - - REEXECUTE(); - } - - case s_headers_done: - { - int hasBody; - STRICT_CHECK(ch != LF); - - parser->nread = 0; - - hasBody = parser->flags & F_CHUNKED || - (parser->content_length > 0 && parser->content_length != ULLONG_MAX); - if (parser->upgrade && (parser->method == HTTP_CONNECT || - (parser->flags & F_SKIPBODY) || !hasBody)) { - /* Exit, the rest of the message is in a different protocol. */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - RETURN((p - data) + 1); - } - - if (parser->flags & F_SKIPBODY) { - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->flags & F_CHUNKED) { - /* chunked encoding - ignore Content-Length header */ - UPDATE_STATE(s_chunk_size_start); - } else { - if (parser->content_length == 0) { - /* Content-Length header given but zero: Content-Length: 0\r\n */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else if (parser->content_length != ULLONG_MAX) { - /* Content-Length header given and non-zero */ - UPDATE_STATE(s_body_identity); - } else { - if (!http_message_needs_eof(parser)) { - /* Assume content-length 0 - read the next */ - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - } else { - /* Read body until EOF */ - UPDATE_STATE(s_body_identity_eof); - } - } - } - - break; - } - - case s_body_identity: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); - - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); - - /* The difference between advancing content_length and p is because - * the latter will automaticaly advance on the next loop iteration. - * Further, if content_length ends up at 0, we want to see the last - * byte again for our message complete callback. - */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; - - if (parser->content_length == 0) { - UPDATE_STATE(s_message_done); - - /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. - * - * The alternative to doing this is to wait for the next byte to - * trigger the data callback, just as in every other case. The - * problem with this is that this makes it difficult for the test - * harness to distinguish between complete-on-EOF and - * complete-on-length. It's not clear that this distinction is - * important for applications, but let's keep it for now. - */ - CALLBACK_DATA_(body, p - body_mark + 1, p - data); - REEXECUTE(); - } - - break; - } - - /* read until EOF */ - case s_body_identity_eof: - MARK(body); - p = data + len - 1; - - break; - - case s_message_done: - UPDATE_STATE(NEW_MESSAGE()); - CALLBACK_NOTIFY(message_complete); - if (parser->upgrade) { - /* Exit, the rest of the message is in a different protocol. */ - RETURN((p - data) + 1); - } - break; - - case s_chunk_size_start: - { - assert(parser->nread == 1); - assert(parser->flags & F_CHUNKED); - - unhex_val = unhex[(unsigned char)ch]; - if (UNLIKELY(unhex_val == -1)) { - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; - } - - parser->content_length = unhex_val; - UPDATE_STATE(s_chunk_size); - break; - } - - case s_chunk_size: - { - uint64_t t; - - assert(parser->flags & F_CHUNKED); - - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; - } - - unhex_val = unhex[(unsigned char)ch]; - - if (unhex_val == -1) { - if (ch == ';' || ch == ' ') { - UPDATE_STATE(s_chunk_parameters); - break; - } - - SET_ERRNO(HPE_INVALID_CHUNK_SIZE); - goto error; - } - - t = parser->content_length; - t *= 16; - t += unhex_val; - - /* Overflow? Test against a conservative limit for simplicity. */ - if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) { - SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); - goto error; - } - - parser->content_length = t; - break; - } - - case s_chunk_parameters: - { - assert(parser->flags & F_CHUNKED); - /* just ignore this shit. TODO check for overflow */ - if (ch == CR) { - UPDATE_STATE(s_chunk_size_almost_done); - break; - } - break; - } - - case s_chunk_size_almost_done: - { - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); - - parser->nread = 0; - - if (parser->content_length == 0) { - parser->flags |= F_TRAILING; - UPDATE_STATE(s_header_field_start); - } else { - UPDATE_STATE(s_chunk_data); - } - CALLBACK_NOTIFY(chunk_header); - break; - } - - case s_chunk_data: - { - uint64_t to_read = MIN(parser->content_length, - (uint64_t) ((data + len) - p)); - - assert(parser->flags & F_CHUNKED); - assert(parser->content_length != 0 - && parser->content_length != ULLONG_MAX); - - /* See the explanation in s_body_identity for why the content - * length and data pointers are managed this way. - */ - MARK(body); - parser->content_length -= to_read; - p += to_read - 1; - - if (parser->content_length == 0) { - UPDATE_STATE(s_chunk_data_almost_done); - } - - break; - } - - case s_chunk_data_almost_done: - assert(parser->flags & F_CHUNKED); - assert(parser->content_length == 0); - STRICT_CHECK(ch != CR); - UPDATE_STATE(s_chunk_data_done); - CALLBACK_DATA(body); - break; - - case s_chunk_data_done: - assert(parser->flags & F_CHUNKED); - STRICT_CHECK(ch != LF); - parser->nread = 0; - UPDATE_STATE(s_chunk_size_start); - CALLBACK_NOTIFY(chunk_complete); - break; - - default: - assert(0 && "unhandled state"); - SET_ERRNO(HPE_INVALID_INTERNAL_STATE); - goto error; - } - } - - /* Run callbacks for any marks that we have leftover after we ran our of - * bytes. There should be at most one of these set, so it's OK to invoke - * them in series (unset marks will not result in callbacks). - * - * We use the NOADVANCE() variety of callbacks here because 'p' has already - * overflowed 'data' and this allows us to correct for the off-by-one that - * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' - * value that's in-bounds). - */ - - assert(((header_field_mark ? 1 : 0) + - (header_value_mark ? 1 : 0) + - (url_mark ? 1 : 0) + - (body_mark ? 1 : 0) + - (status_mark ? 1 : 0)) <= 1); - - CALLBACK_DATA_NOADVANCE(header_field); - CALLBACK_DATA_NOADVANCE(header_value); - CALLBACK_DATA_NOADVANCE(url); - CALLBACK_DATA_NOADVANCE(body); - CALLBACK_DATA_NOADVANCE(status); - - RETURN(len); - -error: - if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { - SET_ERRNO(HPE_UNKNOWN); - } - - RETURN(p - data); -} - - -/* Does the parser need to see an EOF to find the end of the message? */ -int -http_message_needs_eof (const http_parser *parser) -{ - if (parser->type == HTTP_REQUEST) { - return 0; - } - - /* See RFC 2616 section 4.4 */ - if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ - parser->status_code == 204 || /* No Content */ - parser->status_code == 304 || /* Not Modified */ - parser->flags & F_SKIPBODY) { /* response to a HEAD request */ - return 0; - } - - if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { - return 0; - } - - return 1; -} - - -int -http_should_keep_alive (const http_parser *parser) -{ - if (parser->http_major > 0 && parser->http_minor > 0) { - /* HTTP/1.1 */ - if (parser->flags & F_CONNECTION_CLOSE) { - return 0; - } - } else { - /* HTTP/1.0 or earlier */ - if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { - return 0; - } - } - - return !http_message_needs_eof(parser); -} - - -const char * -http_method_str (enum http_method m) -{ - return ELEM_AT(method_strings, m, ""); -} - - -void -http_parser_init (http_parser *parser, enum http_parser_type t) -{ - void *data = parser->data; /* preserve application data */ - memset(parser, 0, sizeof(*parser)); - parser->data = data; - parser->type = t; - parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); - parser->http_errno = HPE_OK; -} - -void -http_parser_settings_init(http_parser_settings *settings) -{ - memset(settings, 0, sizeof(*settings)); -} - -const char * -http_errno_name(enum http_errno err) { - assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); - return http_strerror_tab[err].name; -} - -const char * -http_errno_description(enum http_errno err) { - assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab)); - return http_strerror_tab[err].description; -} - -static enum http_host_state -http_parse_host_char(enum http_host_state s, const char ch) { - switch(s) { - case s_http_userinfo: - case s_http_userinfo_start: - if (ch == '@') { - return s_http_host_start; - } - - if (IS_USERINFO_CHAR(ch)) { - return s_http_userinfo; - } - break; - - case s_http_host_start: - if (ch == '[') { - return s_http_host_v6_start; - } - - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } - - break; - - case s_http_host: - if (IS_HOST_CHAR(ch)) { - return s_http_host; - } - - /* FALLTHROUGH */ - case s_http_host_v6_end: - if (ch == ':') { - return s_http_host_port_start; - } - - break; - - case s_http_host_v6: - if (ch == ']') { - return s_http_host_v6_end; - } - - /* FALLTHROUGH */ - case s_http_host_v6_start: - if (IS_HEX(ch) || ch == ':' || ch == '.') { - return s_http_host_v6; - } - - if (s == s_http_host_v6 && ch == '%') { - return s_http_host_v6_zone_start; - } - break; - - case s_http_host_v6_zone: - if (ch == ']') { - return s_http_host_v6_end; - } - - /* FALLTHROUGH */ - case s_http_host_v6_zone_start: - /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */ - if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' || - ch == '~') { - return s_http_host_v6_zone; - } - break; - - case s_http_host_port: - case s_http_host_port_start: - if (IS_NUM(ch)) { - return s_http_host_port; - } - - break; - - default: - break; - } - return s_http_host_dead; -} - -static int -http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { - enum http_host_state s; - - const char *p; - size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; - - assert(u->field_set & (1 << UF_HOST)); - - u->field_data[UF_HOST].len = 0; - - s = found_at ? s_http_userinfo_start : s_http_host_start; - - for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { - enum http_host_state new_s = http_parse_host_char(s, *p); - - if (new_s == s_http_host_dead) { - return 1; - } - - switch(new_s) { - case s_http_host: - if (s != s_http_host) { - u->field_data[UF_HOST].off = p - buf; - } - u->field_data[UF_HOST].len++; - break; - - case s_http_host_v6: - if (s != s_http_host_v6) { - u->field_data[UF_HOST].off = p - buf; - } - u->field_data[UF_HOST].len++; - break; - - case s_http_host_v6_zone_start: - case s_http_host_v6_zone: - u->field_data[UF_HOST].len++; - break; - - case s_http_host_port: - if (s != s_http_host_port) { - u->field_data[UF_PORT].off = p - buf; - u->field_data[UF_PORT].len = 0; - u->field_set |= (1 << UF_PORT); - } - u->field_data[UF_PORT].len++; - break; - - case s_http_userinfo: - if (s != s_http_userinfo) { - u->field_data[UF_USERINFO].off = p - buf ; - u->field_data[UF_USERINFO].len = 0; - u->field_set |= (1 << UF_USERINFO); - } - u->field_data[UF_USERINFO].len++; - break; - - default: - break; - } - s = new_s; - } - - /* Make sure we don't end somewhere unexpected */ - switch (s) { - case s_http_host_start: - case s_http_host_v6_start: - case s_http_host_v6: - case s_http_host_v6_zone_start: - case s_http_host_v6_zone: - case s_http_host_port_start: - case s_http_userinfo: - case s_http_userinfo_start: - return 1; - default: - break; - } - - return 0; -} - -void -http_parser_url_init(struct http_parser_url *u) { - memset(u, 0, sizeof(*u)); -} - -int -http_parser_parse_url(const char *buf, size_t buflen, int is_connect, - struct http_parser_url *u) -{ - enum state s; - const char *p; - enum http_parser_url_fields uf, old_uf; - int found_at = 0; - - u->port = u->field_set = 0; - s = is_connect ? s_req_server_start : s_req_spaces_before_url; - old_uf = UF_MAX; - - for (p = buf; p < buf + buflen; p++) { - s = parse_url_char(s, *p); - - /* Figure out the next field that we're operating on */ - switch (s) { - case s_dead: - return 1; - - /* Skip delimeters */ - case s_req_schema_slash: - case s_req_schema_slash_slash: - case s_req_server_start: - case s_req_query_string_start: - case s_req_fragment_start: - continue; - - case s_req_schema: - uf = UF_SCHEMA; - break; - - case s_req_server_with_at: - found_at = 1; -#if __GNUC__ >= 7 - __attribute__ ((fallthrough)); -#endif - - /* FALLTROUGH */ - case s_req_server: - uf = UF_HOST; - break; - - case s_req_path: - uf = UF_PATH; - break; - - case s_req_query_string: - uf = UF_QUERY; - break; - - case s_req_fragment: - uf = UF_FRAGMENT; - break; - - default: - assert(!"Unexpected state"); - return 1; - } - - /* Nothing's changed; soldier on */ - if (uf == old_uf) { - u->field_data[uf].len++; - continue; - } - - u->field_data[uf].off = p - buf; - u->field_data[uf].len = 1; - - u->field_set |= (1 << uf); - old_uf = uf; - } - - /* host must be present if there is a schema */ - /* parsing http:///toto will fail */ - if ((u->field_set & (1 << UF_SCHEMA)) && - (u->field_set & (1 << UF_HOST)) == 0) { - return 1; - } - - if (u->field_set & (1 << UF_HOST)) { - if (http_parse_host(buf, u, found_at) != 0) { - return 1; - } - } - - /* CONNECT requests can only contain "hostname:port" */ - if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { - return 1; - } - - if (u->field_set & (1 << UF_PORT)) { - /* Don't bother with endp; we've already validated the string */ - unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); - - /* Ports have a max value of 2^16 */ - if (v > 0xffff) { - return 1; - } - - u->port = (uint16_t) v; - } - - return 0; -} - -void -http_parser_pause(http_parser *parser, int paused) { - /* Users should only be pausing/unpausing a parser that is not in an error - * state. In non-debug builds, there's not much that we can do about this - * other than ignore it. - */ - if (HTTP_PARSER_ERRNO(parser) == HPE_OK || - HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { - SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); - } else { - assert(0 && "Attempting to pause parser in error state"); - } -} - -int -http_body_is_final(const struct http_parser *parser) { - return parser->state == s_message_done; -} - -unsigned long -http_parser_version(void) { - return HTTP_PARSER_VERSION_MAJOR * 0x10000 | - HTTP_PARSER_VERSION_MINOR * 0x00100 | - HTTP_PARSER_VERSION_PATCH * 0x00001; -} diff --git a/src/http/qhttp/http-parser/http_parser.h b/src/http/qhttp/http-parser/http_parser.h deleted file mode 100644 index 005db96ec..000000000 --- a/src/http/qhttp/http-parser/http_parser.h +++ /dev/null @@ -1,432 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ -#ifndef http_parser_h -#define http_parser_h -#ifdef __cplusplus -extern "C" { -#endif - -/* Also update SONAME in the Makefile whenever you change these. */ -#define HTTP_PARSER_VERSION_MAJOR 2 -#define HTTP_PARSER_VERSION_MINOR 7 -#define HTTP_PARSER_VERSION_PATCH 1 - -#include -#if defined(_WIN32) && !defined(__MINGW32__) && \ - (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__) -#include -#include -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#else -#include -#endif - -/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run - * faster - */ -#ifndef HTTP_PARSER_STRICT -# define HTTP_PARSER_STRICT 1 -#endif - -/* Maximium header size allowed. If the macro is not defined - * before including this header then the default is used. To - * change the maximum header size, define the macro in the build - * environment (e.g. -DHTTP_MAX_HEADER_SIZE=). To remove - * the effective limit on the size of the header, define the macro - * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff) - */ -#ifndef HTTP_MAX_HEADER_SIZE -# define HTTP_MAX_HEADER_SIZE (80*1024) -#endif - -typedef struct http_parser http_parser; -typedef struct http_parser_settings http_parser_settings; - - -/* Callbacks should return non-zero to indicate an error. The parser will - * then halt execution. - * - * The one exception is on_headers_complete. In a HTTP_RESPONSE parser - * returning '1' from on_headers_complete will tell the parser that it - * should not expect a body. This is used when receiving a response to a - * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: - * chunked' headers that indicate the presence of a body. - * - * Returning `2` from on_headers_complete will tell parser that it should not - * expect neither a body nor any futher responses on this connection. This is - * useful for handling responses to a CONNECT request which may not contain - * `Upgrade` or `Connection: upgrade` headers. - * - * http_data_cb does not return data chunks. It will be called arbitrarily - * many times for each string. E.G. you might get 10 callbacks for "on_url" - * each providing just a few characters more data. - */ -typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); -typedef int (*http_cb) (http_parser*); - - -/* Status Codes */ -#define HTTPPARSER_HTTP_STATUS_MAP(XX) \ - XX(100, CONTINUE, Continue) \ - XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \ - XX(102, PROCESSING, Processing) \ - XX(200, OK, OK) \ - XX(201, CREATED, Created) \ - XX(202, ACCEPTED, Accepted) \ - XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \ - XX(204, NO_CONTENT, No Content) \ - XX(205, RESET_CONTENT, Reset Content) \ - XX(206, PARTIAL_CONTENT, Partial Content) \ - XX(207, MULTI_STATUS, Multi-Status) \ - XX(208, ALREADY_REPORTED, Already Reported) \ - XX(226, IM_USED, IM Used) \ - XX(300, MULTIPLE_CHOICES, Multiple Choices) \ - XX(301, MOVED_PERMANENTLY, Moved Permanently) \ - XX(302, FOUND, Found) \ - XX(303, SEE_OTHER, See Other) \ - XX(304, NOT_MODIFIED, Not Modified) \ - XX(305, USE_PROXY, Use Proxy) \ - XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \ - XX(308, PERMANENT_REDIRECT, Permanent Redirect) \ - XX(400, BAD_REQUEST, Bad Request) \ - XX(401, UNAUTHORIZED, Unauthorized) \ - XX(402, PAYMENT_REQUIRED, Payment Required) \ - XX(403, FORBIDDEN, Forbidden) \ - XX(404, NOT_FOUND, Not Found) \ - XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \ - XX(406, NOT_ACCEPTABLE, Not Acceptable) \ - XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \ - XX(408, REQUEST_TIMEOUT, Request Timeout) \ - XX(409, CONFLICT, Conflict) \ - XX(410, GONE, Gone) \ - XX(411, LENGTH_REQUIRED, Length Required) \ - XX(412, PRECONDITION_FAILED, Precondition Failed) \ - XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \ - XX(414, URI_TOO_LONG, URI Too Long) \ - XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \ - XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \ - XX(417, EXPECTATION_FAILED, Expectation Failed) \ - XX(421, MISDIRECTED_REQUEST, Misdirected Request) \ - XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \ - XX(423, LOCKED, Locked) \ - XX(424, FAILED_DEPENDENCY, Failed Dependency) \ - XX(426, UPGRADE_REQUIRED, Upgrade Required) \ - XX(428, PRECONDITION_REQUIRED, Precondition Required) \ - XX(429, TOO_MANY_REQUESTS, Too Many Requests) \ - XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \ - XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \ - XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \ - XX(501, NOT_IMPLEMENTED, Not Implemented) \ - XX(502, BAD_GATEWAY, Bad Gateway) \ - XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \ - XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \ - XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \ - XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \ - XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \ - XX(508, LOOP_DETECTED, Loop Detected) \ - XX(510, NOT_EXTENDED, Not Extended) \ - XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) - -enum http_status - { -#define XX(num, name, string) HTTP_STATUS_##name = num, - HTTPPARSER_HTTP_STATUS_MAP(XX) -#undef XX - }; - - -/* Request Methods */ -#define HTTP_METHOD_MAP(XX) \ - XX(0, DELETE, DELETE) \ - XX(1, GET, GET) \ - XX(2, HEAD, HEAD) \ - XX(3, POST, POST) \ - XX(4, PUT, PUT) \ - /* pathological */ \ - XX(5, CONNECT, CONNECT) \ - XX(6, OPTIONS, OPTIONS) \ - XX(7, TRACE, TRACE) \ - /* WebDAV */ \ - XX(8, COPY, COPY) \ - XX(9, LOCK, LOCK) \ - XX(10, MKCOL, MKCOL) \ - XX(11, MOVE, MOVE) \ - XX(12, PROPFIND, PROPFIND) \ - XX(13, PROPPATCH, PROPPATCH) \ - XX(14, SEARCH, SEARCH) \ - XX(15, UNLOCK, UNLOCK) \ - XX(16, BIND, BIND) \ - XX(17, REBIND, REBIND) \ - XX(18, UNBIND, UNBIND) \ - XX(19, ACL, ACL) \ - /* subversion */ \ - XX(20, REPORT, REPORT) \ - XX(21, MKACTIVITY, MKACTIVITY) \ - XX(22, CHECKOUT, CHECKOUT) \ - XX(23, MERGE, MERGE) \ - /* upnp */ \ - XX(24, MSEARCH, M-SEARCH) \ - XX(25, NOTIFY, NOTIFY) \ - XX(26, SUBSCRIBE, SUBSCRIBE) \ - XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \ - /* RFC-5789 */ \ - XX(28, PATCH, PATCH) \ - XX(29, PURGE, PURGE) \ - /* CalDAV */ \ - XX(30, MKCALENDAR, MKCALENDAR) \ - /* RFC-2068, section 19.6.1.2 */ \ - XX(31, LINK, LINK) \ - XX(32, UNLINK, UNLINK) \ - -enum http_method - { -#define XX(num, name, string) HTTP_##name = num, - HTTP_METHOD_MAP(XX) -#undef XX - }; - - -enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; - - -/* Flag values for http_parser.flags field */ -enum flags - { F_CHUNKED = 1 << 0 - , F_CONNECTION_KEEP_ALIVE = 1 << 1 - , F_CONNECTION_CLOSE = 1 << 2 - , F_CONNECTION_UPGRADE = 1 << 3 - , F_TRAILING = 1 << 4 - , F_UPGRADE = 1 << 5 - , F_SKIPBODY = 1 << 6 - , F_CONTENTLENGTH = 1 << 7 - }; - - -/* Map for errno-related constants - * - * The provided argument should be a macro that takes 2 arguments. - */ -#define HTTP_ERRNO_MAP(XX) \ - /* No error */ \ - XX(OK, "success") \ - \ - /* Callback-related errors */ \ - XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_url, "the on_url callback failed") \ - XX(CB_header_field, "the on_header_field callback failed") \ - XX(CB_header_value, "the on_header_value callback failed") \ - XX(CB_headers_complete, "the on_headers_complete callback failed") \ - XX(CB_body, "the on_body callback failed") \ - XX(CB_message_complete, "the on_message_complete callback failed") \ - XX(CB_status, "the on_status callback failed") \ - XX(CB_chunk_header, "the on_chunk_header callback failed") \ - XX(CB_chunk_complete, "the on_chunk_complete callback failed") \ - \ - /* Parsing-related errors */ \ - XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ - XX(HEADER_OVERFLOW, \ - "too many header bytes seen; overflow detected") \ - XX(CLOSED_CONNECTION, \ - "data received after completed connection: close message") \ - XX(INVALID_VERSION, "invalid HTTP version") \ - XX(INVALID_STATUS, "invalid HTTP status code") \ - XX(INVALID_METHOD, "invalid HTTP method") \ - XX(INVALID_URL, "invalid URL") \ - XX(INVALID_HOST, "invalid host") \ - XX(INVALID_PORT, "invalid port") \ - XX(INVALID_PATH, "invalid path") \ - XX(INVALID_QUERY_STRING, "invalid query string") \ - XX(INVALID_FRAGMENT, "invalid fragment") \ - XX(LF_EXPECTED, "LF character expected") \ - XX(INVALID_HEADER_TOKEN, "invalid character in header") \ - XX(INVALID_CONTENT_LENGTH, \ - "invalid character in content-length header") \ - XX(UNEXPECTED_CONTENT_LENGTH, \ - "unexpected content-length header") \ - XX(INVALID_CHUNK_SIZE, \ - "invalid character in chunk size header") \ - XX(INVALID_CONSTANT, "invalid constant string") \ - XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ - XX(STRICT, "strict mode assertion failed") \ - XX(PAUSED, "parser is paused") \ - XX(UNKNOWN, "an unknown error occurred") - - -/* Define HPE_* values for each errno value above */ -#define HTTP_ERRNO_GEN(n, s) HPE_##n, -enum http_errno { - HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) -}; -#undef HTTP_ERRNO_GEN - - -/* Get an http_errno value from an http_parser */ -#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) - - -struct http_parser { - /** PRIVATE **/ - unsigned int type : 2; /* enum http_parser_type */ - unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */ - unsigned int state : 7; /* enum state from http_parser.c */ - unsigned int header_state : 7; /* enum header_state from http_parser.c */ - unsigned int index : 7; /* index into current matcher */ - unsigned int lenient_http_headers : 1; - - uint32_t nread; /* # bytes read in various scenarios */ - uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ - - /** READ-ONLY **/ - unsigned short http_major; - unsigned short http_minor; - unsigned int status_code : 16; /* responses only */ - unsigned int method : 8; /* requests only */ - unsigned int http_errno : 7; - - /* 1 = Upgrade header was present and the parser has exited because of that. - * 0 = No upgrade header present. - * Should be checked when http_parser_execute() returns in addition to - * error checking. - */ - unsigned int upgrade : 1; - - /** PUBLIC **/ - void *data; /* A pointer to get hook to the "connection" or "socket" object */ -}; - - -struct http_parser_settings { - http_cb on_message_begin; - http_data_cb on_url; - http_data_cb on_status; - http_data_cb on_header_field; - http_data_cb on_header_value; - http_cb on_headers_complete; - http_data_cb on_body; - http_cb on_message_complete; - /* When on_chunk_header is called, the current chunk length is stored - * in parser->content_length. - */ - http_cb on_chunk_header; - http_cb on_chunk_complete; -}; - - -enum http_parser_url_fields - { UF_SCHEMA = 0 - , UF_HOST = 1 - , UF_PORT = 2 - , UF_PATH = 3 - , UF_QUERY = 4 - , UF_FRAGMENT = 5 - , UF_USERINFO = 6 - , UF_MAX = 7 - }; - - -/* Result structure for http_parser_parse_url(). - * - * Callers should index into field_data[] with UF_* values iff field_set - * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and - * because we probably have padding left over), we convert any port to - * a uint16_t. - */ -struct http_parser_url { - uint16_t field_set; /* Bitmask of (1 << UF_*) values */ - uint16_t port; /* Converted UF_PORT string */ - - struct { - uint16_t off; /* Offset into buffer in which field starts */ - uint16_t len; /* Length of run in buffer */ - } field_data[UF_MAX]; -}; - - -/* Returns the library version. Bits 16-23 contain the major version number, - * bits 8-15 the minor version number and bits 0-7 the patch level. - * Usage example: - * - * unsigned long version = http_parser_version(); - * unsigned major = (version >> 16) & 255; - * unsigned minor = (version >> 8) & 255; - * unsigned patch = version & 255; - * printf("http_parser v%u.%u.%u\n", major, minor, patch); - */ -unsigned long http_parser_version(void); - -void http_parser_init(http_parser *parser, enum http_parser_type type); - - -/* Initialize http_parser_settings members to 0 - */ -void http_parser_settings_init(http_parser_settings *settings); - - -/* Executes the parser. Returns number of parsed bytes. Sets - * `parser->http_errno` on error. */ -size_t http_parser_execute(http_parser *parser, - const http_parser_settings *settings, - const char *data, - size_t len); - - -/* If http_should_keep_alive() in the on_headers_complete or - * on_message_complete callback returns 0, then this should be - * the last message on the connection. - * If you are the server, respond with the "Connection: close" header. - * If you are the client, close the connection. - */ -int http_should_keep_alive(const http_parser *parser); - -/* Returns a string version of the HTTP method. */ -const char *http_method_str(enum http_method m); - -/* Return a string name of the given error */ -const char *http_errno_name(enum http_errno err); - -/* Return a string description of the given error */ -const char *http_errno_description(enum http_errno err); - -/* Initialize all http_parser_url members to 0 */ -void http_parser_url_init(struct http_parser_url *u); - -/* Parse a URL; return nonzero on failure */ -int http_parser_parse_url(const char *buf, size_t buflen, - int is_connect, - struct http_parser_url *u); - -/* Pause or un-pause the parser; a nonzero value pauses */ -void http_parser_pause(http_parser *parser, int paused); - -/* Checks if this is the final chunk of the body. */ -int http_body_is_final(const http_parser *parser); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/http/qhttp/private/httpparser.hxx b/src/http/qhttp/private/httpparser.hxx deleted file mode 100644 index 8dfea18e5..000000000 --- a/src/http/qhttp/private/httpparser.hxx +++ /dev/null @@ -1,119 +0,0 @@ -/** @file httpparser.hxx - * - * @copyright (C) 2016 - * @date 2016.05.26 - * @version 1.0.0 - * @author amir zamani - * - */ - -#ifndef __QHTTP_HTTPPARSER_HXX__ -#define __QHTTP_HTTPPARSER_HXX__ - -#include "qhttpbase.hpp" -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace details { -/////////////////////////////////////////////////////////////////////////////// - - -/// base HttpParser based on joyent http_parser -template -class HttpParser -{ -public: - explicit HttpParser(http_parser_type type) { - // create http_parser object - iparser.data = static_cast(this); - http_parser_init(&iparser, type); - - memset(&iparserSettings, 0, sizeof(http_parser_settings)); - iparserSettings.on_message_begin = onMessageBegin; - iparserSettings.on_url = onUrl; - iparserSettings.on_status = onStatus; - iparserSettings.on_header_field = onHeaderField; - iparserSettings.on_header_value = onHeaderValue; - iparserSettings.on_headers_complete = onHeadersComplete; - iparserSettings.on_body = onBody; - iparserSettings.on_message_complete = onMessageComplete; - } - - size_t parse(const char* data, size_t length) { - return http_parser_execute(&iparser, - &iparserSettings, - data, - length); - } - -public: // callback functions for http_parser_settings - static int onMessageBegin(http_parser* p) { - return me(p)->messageBegin(p); - } - - static int onUrl(http_parser* p, const char* at, size_t length) { - return me(p)->url(p, at, length); - } - - static int onStatus(http_parser* p, const char* at, size_t length) { - return me(p)->status(p, at, length); - } - - static int onHeaderField(http_parser* p, const char* at, size_t length) { - return me(p)->headerField(p, at, length); - } - - static int onHeaderValue(http_parser* p, const char* at, size_t length) { - return me(p)->headerValue(p, at, length); - } - - static int onHeadersComplete(http_parser* p) { - return me(p)->headersComplete(p); - } - - static int onBody(http_parser* p, const char* at, size_t length) { - return me(p)->body(p, at, length); - } - - static int onMessageComplete(http_parser* p) { - return me(p)->messageComplete(p); - } - - -protected: - // The ones we are reading in from the parser - QByteArray itempHeaderField; - QByteArray itempHeaderValue; - // if connection has a timeout, these fields will be used - quint32 itimeOut = 0; - QBasicTimer itimer; - // uniform socket object - QSocket isocket; - // if connection should persist - bool ikeepAlive = false; - - // joyent http_parser - http_parser iparser; - http_parser_settings iparserSettings; - - static TImpl* me(http_parser* p) { - return static_cast(p->data); - } -}; // - -/// basic request parser (server) -template -struct HttpRequestParser : public HttpParser { - HttpRequestParser() : HttpParser(HTTP_REQUEST) {} -}; - -/// basic response parser (clinet) -template -struct HttpResponseParser : public HttpParser { - HttpResponseParser() : HttpParser(HTTP_RESPONSE) {} -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace details -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // __QHTTP_HTTPPARSER_HXX__ diff --git a/src/http/qhttp/private/httpreader.hxx b/src/http/qhttp/private/httpreader.hxx deleted file mode 100644 index 338ed2a2f..000000000 --- a/src/http/qhttp/private/httpreader.hxx +++ /dev/null @@ -1,78 +0,0 @@ -/** @file httpreader.hxx - * - * @copyright (C) 2016 - * @date 2016.05.26 - * @version 1.0.0 - * @author amir zamani - * - */ - -#ifndef __QHTTP_HTTPREADER_HXX__ -#define __QHTTP_HTTPREADER_HXX__ - -#include "qhttpbase.hpp" -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace details { -/////////////////////////////////////////////////////////////////////////////// - -// usage in client::QHttpResponse, server::QHttpRequest -template -class HttpReader : public TBase -{ -public: - enum TReadState { - EEmpty, - EPartial, - EComplete, - ESent - }; - -public: - void collectData(int atMost) { - icollectRequired = true; - icollectCapacity = atMost; - icollectedData.clear(); - if ( atMost > 0 ) - icollectedData.reserve(atMost); - } - - bool append(const char* data, size_t length) { - if ( !icollectRequired ) // not allowed to collect data - return false; - - int newLength = icollectedData.length() + static_cast(length); - - if ( icollectCapacity > 0 && newLength > icollectCapacity ) - return false; // the capacity is full - - icollectedData.append(data, length); - return true; - } - - // call cb if the message is not finalized yet - template - void finalizeSending(Func cb) { - if ( ireadState != EComplete ) { - ireadState = EComplete; - isuccessful = true; - - cb(); - } - } - -public: - bool isuccessful = false; - TReadState ireadState = EEmpty; - - /// shall I collect incoming body data by myself? - bool icollectRequired = false; - int icollectCapacity = 0; - QByteArray icollectedData; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace details -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // __QHTTP_HTTPREADER_HXX__ diff --git a/src/http/qhttp/private/httpwriter.hxx b/src/http/qhttp/private/httpwriter.hxx deleted file mode 100644 index 2e9bedfe4..000000000 --- a/src/http/qhttp/private/httpwriter.hxx +++ /dev/null @@ -1,112 +0,0 @@ -/** @file httpwriter.hxx - * - * @copyright (C) 2016 - * @date 2016.05.26 - * @version 1.0.0 - * @author amir zamani - * - */ - -#ifndef __QHTTP_HTTPWRITER_HXX__ -#define __QHTTP_HTTPWRITER_HXX__ - -#include "qhttpbase.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace details { -/////////////////////////////////////////////////////////////////////////////// - -// usage in client::QHttpRequest, server::QHttpResponse -template -class HttpWriter : public TBase -{ -public: - bool addHeader(const QByteArray &field, const QByteArray &value) { - if ( ifinished ) - return false; - - TBase::iheaders.insert(field.toLower(), value); - return true; - } - - bool writeHeader(const QByteArray& field, const QByteArray& value) { - if ( ifinished ) - return false; - - QByteArray buffer = QByteArray(field) - .append(": ") - .append(value) - .append("\r\n"); - - isocket.writeRaw(buffer); - return true; - } - - bool writeData(const QByteArray& data) { - if ( ifinished ) - return false; - - ensureWritingHeaders(); - isocket.writeRaw(data); - return true; - } - - bool endPacket(const QByteArray& data) { - if ( !writeData(data) ) - return false; - - isocket.flush(); - ifinished = true; - return true; - } - - void ensureWritingHeaders() { - if ( ifinished || iheaderWritten ) - return; - - TImpl* me = static_cast(this); - isocket.writeRaw(me->makeTitle()); - writeHeaders(); - - iheaderWritten = true; - } - - void writeHeaders(bool doFlush = false) { - if ( ifinished || iheaderWritten ) - return; - - if ( TBase::iheaders.keyHasValue("connection", "keep-alive") ) - ikeepAlive = true; - else - TBase::iheaders.insert("connection", "close"); - - TImpl* me = static_cast(this); - me->prepareHeadersToWrite(); - - for ( auto cit = TBase::iheaders.constBegin(); cit != TBase::iheaders.constEnd(); cit++ ) { - const QByteArray& field = cit.key(); - const QByteArray& value = cit.value(); - - writeHeader(field, value); - } - - isocket.writeRaw("\r\n"); - if ( doFlush ) - isocket.flush(); - } - -public: - QSocket isocket; - - bool ifinished = false; - bool iheaderWritten = false; - bool ikeepAlive = false; -}; - - -/////////////////////////////////////////////////////////////////////////////// -} // namespace details -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // __QHTTP_HTTPWRITER_HXX__ diff --git a/src/http/qhttp/private/qhttpbase.hpp b/src/http/qhttp/private/qhttpbase.hpp deleted file mode 100644 index 81bae4f5c..000000000 --- a/src/http/qhttp/private/qhttpbase.hpp +++ /dev/null @@ -1,52 +0,0 @@ -/** base classes for private implementations. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPBASE_HPP -#define QHTTPBASE_HPP - -#include "qhttpfwd.hpp" - -#include "qsocket.hpp" -#include -#include - -#include "http-parser/http_parser.h" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace details { -/////////////////////////////////////////////////////////////////////////////// - -struct HttpBase -{ - QString iversion; - THeaderHash iheaders; -}; // struct HttpBase - -/////////////////////////////////////////////////////////////////////////////// - -struct HttpRequestBase : public HttpBase -{ - QUrl iurl; - THttpMethod imethod; -}; // HttpRequestBase - -/////////////////////////////////////////////////////////////////////////////// - -struct HttpResponseBase : public HttpBase -{ - TStatusCode istatus = ESTATUS_BAD_REQUEST; - - HttpResponseBase() { iversion = "1.1"; } -}; // HttpResponseBase - -/////////////////////////////////////////////////////////////////////////////// -} // namespace details -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPBASE_HPP diff --git a/src/http/qhttp/private/qhttpclient_private.hpp b/src/http/qhttp/private/qhttpclient_private.hpp deleted file mode 100644 index 9c6cd0989..000000000 --- a/src/http/qhttp/private/qhttpclient_private.hpp +++ /dev/null @@ -1,222 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_PRIVATE_HPP -#define QHTTPCLIENT_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpclient.hpp" -#include "httpparser.hxx" -#include "qhttpclientrequest_private.hpp" -#include "qhttpclientresponse_private.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// - -class QHttpClientPrivate : - public details::HttpResponseParser -{ - Q_DECLARE_PUBLIC(QHttpClient) - -public: - explicit QHttpClientPrivate(QHttpClient* q) : q_ptr(q) { - QObject::connect( - q_func(), &QHttpClient::disconnected, - [this](){ release(); } - ); - - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpClientPrivate() { - QHTTP_LINE_DEEPLOG - } - - void release() { - // if socket drops and http_parser can not call messageComplete, - // dispatch the ilastResponse - finalizeConnection(); - - isocket.disconnectAllQtConnections(); - isocket.release(); - - if ( ilastRequest ) { - ilastRequest->deleteLater(); - ilastRequest = nullptr; - } - - if ( ilastResponse ) { - ilastResponse->deleteLater(); - ilastResponse = nullptr; - } - - // must be called! or the later http_parser_execute() may fail - http_parser_init(&iparser, HTTP_RESPONSE); - } - - void initializeSocket() { - // no need to reconnect. do nothing and simply return - if ( ikeepAlive ) - return; - - // close previous connection now! - // instead being called by emitted disconnected signal - release(); - - ikeepAlive = false; - - // create a tcp connection - if ( isocket.ibackendType == ETcpSocket ) { - initTcpSocket(); - - } else if ( isocket.ibackendType == ESslSocket ) { - initSslSocket(); - - } else if ( isocket.ibackendType == ELocalSocket ) { - initLocalSocket(); - } - } - -public: - int messageBegin(http_parser* parser); - int url(http_parser*, const char*, size_t) { - return 0; // not used in parsing incoming response. - } - int status(http_parser* parser, const char* at, size_t length) ; - int headerField(http_parser* parser, const char* at, size_t length); - int headerValue(http_parser* parser, const char* at, size_t length); - int headersComplete(http_parser* parser); - int body(http_parser* parser, const char* at, size_t length); - int messageComplete(http_parser* parser); - -protected: - void onConnected() { - iconnectingTimer.stop(); - - if ( itimeOut > 0 ) - itimer.start(itimeOut, Qt::CoarseTimer, q_func()); - - if ( ireqHandler ) - ireqHandler(ilastRequest); - else - q_func()->onRequestReady(ilastRequest); - } - - void onReadyRead() { - while ( isocket.bytesAvailable() > 0 ) { - char buffer[4097] = {0}; - size_t readLength = static_cast(isocket.readRaw(buffer, 4096)); - - parse(buffer, readLength); - } - } - - void finalizeConnection() { - if ( ilastResponse == nullptr ) - return; - - ilastResponse->d_func()->finalizeSending([this]{ - emit ilastResponse->end(); - }); - } - -private: - void initTcpSocket() { - QTcpSocket* sok = new QTcpSocket(q_func()); - isocket.itcpSocket = sok; - - QObject::connect( - sok, &QTcpSocket::connected, - [this](){ onConnected(); } - ); - QObject::connect( - sok, &QTcpSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QTcpSocket::bytesWritten, - [this](qint64){ - const auto& ts = isocket.itcpSocket; - if ( ts->bytesToWrite() == 0 && ilastRequest ) - emit ilastRequest->allBytesWritten(); - }); - QObject::connect( - sok, &QTcpSocket::disconnected, - q_func(), &QHttpClient::disconnected - ); - } - - void initSslSocket() { - QSslSocket* sok = new QSslSocket(q_func()); - isocket.itcpSocket = sok; - - QObject::connect( - sok, &QSslSocket::encrypted, - [this](){ onConnected(); } - ); - QObject::connect( - sok, &QSslSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QSslSocket::bytesWritten, - [this](qint64){ - const auto& ts = isocket.itcpSocket; - if ( ts->bytesToWrite() == 0 && ilastRequest ) - emit ilastRequest->allBytesWritten(); - }); - QObject::connect( - sok, &QSslSocket::disconnected, - q_func(), &QHttpClient::disconnected - ); - } - - void initLocalSocket() { - QLocalSocket* sok = new QLocalSocket(q_func()); - isocket.ilocalSocket = sok; - - QObject::connect( - sok, &QLocalSocket::connected, - [this](){ onConnected(); } - ); - QObject::connect( - sok, &QLocalSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QLocalSocket::bytesWritten, - [this](qint64){ - const auto* ls = isocket.ilocalSocket; - if ( ls->bytesToWrite() == 0 && ilastRequest ) - emit ilastRequest->allBytesWritten(); - }); - QObject::connect( - sok, &QLocalSocket::disconnected, - q_func(), &QHttpClient::disconnected - ); - } - -protected: - QHttpClient* const q_ptr; - - QHttpRequest* ilastRequest = nullptr; - QHttpResponse* ilastResponse = nullptr; - TRequstHandler ireqHandler; - TResponseHandler irespHandler; - - QBasicTimer iconnectingTimer; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// - -#endif // QHTTPCLIENT_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpclientrequest_private.hpp b/src/http/qhttp/private/qhttpclientrequest_private.hpp deleted file mode 100644 index 703fcc40b..000000000 --- a/src/http/qhttp/private/qhttpclientrequest_private.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_REQUEST_PRIVATE_HPP -#define QHTTPCLIENT_REQUEST_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// -#include "httpwriter.hxx" -#include "qhttpclient.hpp" -#include "qhttpclientrequest.hpp" - -#include - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -class QHttpRequestPrivate : - public details::HttpWriter -{ - Q_DECLARE_PUBLIC(QHttpRequest) - -public: - explicit QHttpRequestPrivate(QHttpClient* cli, QHttpRequest* q) : q_ptr(q), iclient(cli) { - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpRequestPrivate() { - QHTTP_LINE_DEEPLOG - } - - void initialize() { - iversion = "1.1"; - - isocket.ibackendType = iclient->backendType(); - isocket.itcpSocket = iclient->tcpSocket(); - isocket.ilocalSocket = iclient->localSocket(); - } - - QByteArray makeTitle(); - - void prepareHeadersToWrite(); - -protected: - QHttpRequest* const q_ptr; - QHttpClient* const iclient; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPCLIENT_REQUEST_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpclientresponse_private.hpp b/src/http/qhttp/private/qhttpclientresponse_private.hpp deleted file mode 100644 index 4c216f1d0..000000000 --- a/src/http/qhttp/private/qhttpclientresponse_private.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_RESPONSE_PRIVATE_HPP -#define QHTTPCLIENT_RESPONSE_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "httpreader.hxx" -#include "qhttpclient.hpp" -#include "qhttpclientresponse.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -class QHttpResponsePrivate : - public details::HttpReader -{ - Q_DECLARE_PUBLIC(QHttpResponse) - QHttpResponse* const q_ptr; - -public: - explicit QHttpResponsePrivate(QHttpClient* cli, QHttpResponse* q) - : q_ptr(q), iclient(cli) { - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpResponsePrivate() { - QHTTP_LINE_DEEPLOG - } - - void initialize() { - } - -public: - QString icustomStatusMessage; - -protected: - QHttpClient* const iclient; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPCLIENT_RESPONSE_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpserver_private.hpp b/src/http/qhttp/private/qhttpserver_private.hpp deleted file mode 100644 index e7c081af4..000000000 --- a/src/http/qhttp/private/qhttpserver_private.hpp +++ /dev/null @@ -1,90 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_PRIVATE_HPP -#define QHTTPSERVER_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpserver.hpp" -#include "qhttpserverconnection.hpp" -#include "qhttpserverrequest.hpp" -#include "qhttpserverresponse.hpp" - -#include -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// - -class QHttpServerPrivate -{ -public: - template - class BackendServer : public TServer - { - public: - QHttpServer* iserver; - - explicit BackendServer(QHttpServer* s) : TServer(s), iserver(s) { - } - - protected: - // if it's a QTcpServer - virtual void incomingConnection(qintptr socketDescriptor) { - iserver->incomingConnection(socketDescriptor); - } - - // if it's a QLocalServer - virtual void incomingConnection(quintptr socketDescriptor) { - iserver->incomingConnection(static_cast(socketDescriptor)); - } - }; - - using TTcpServer = QScopedPointer>; - using TLocalServer = QScopedPointer>; - -public: - quint32 itimeOut = 0; - TServerHandler ihandler = nullptr; - - TBackend ibackend = ETcpSocket; - - TTcpServer itcpServer; - TLocalServer ilocalServer; - -public: - explicit QHttpServerPrivate() { - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpServerPrivate() { - QHTTP_LINE_DEEPLOG - } - - void initialize(TBackend backend, QHttpServer* parent) { - ibackend = backend; - - if ( ibackend == ETcpSocket || ibackend == ESslSocket ) { - itcpServer.reset( new BackendServer(parent) ); - ilocalServer.reset( nullptr ); - - } else if ( ibackend == ELocalSocket ) { - itcpServer.reset( nullptr ); - ilocalServer.reset( new BackendServer(parent) ); - } - } - -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// - -#endif // QHTTPSERVER_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpserverconnection_private.hpp b/src/http/qhttp/private/qhttpserverconnection_private.hpp deleted file mode 100644 index 53b349fa9..000000000 --- a/src/http/qhttp/private/qhttpserverconnection_private.hpp +++ /dev/null @@ -1,201 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_CONNECTION_PRIVATE_HPP -#define QHTTPSERVER_CONNECTION_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpserverconnection.hpp" -#include "httpparser.hxx" -#include "qhttpserverrequest.hpp" -#include "qhttpserverresponse.hpp" - -#include "private/qhttpserverrequest_private.hpp" -#include "private/qhttpserverresponse_private.hpp" - -#include -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -class QHttpConnectionPrivate : - public details::HttpRequestParser -{ - Q_DECLARE_PUBLIC(QHttpConnection) - -public: - explicit QHttpConnectionPrivate(QHttpConnection* q) : q_ptr(q) { - - QObject::connect( - q_func(), &QHttpConnection::disconnected, - [this](){ release(); } - ); - - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpConnectionPrivate() { - QHTTP_LINE_DEEPLOG - } - - void createSocket(qintptr sokDesc, TBackend bend) { - isocket.ibackendType = bend; - - if ( bend == ETcpSocket ) { - initTcpSocket(sokDesc); - - } else if ( bend == ESslSocket) { - initSslSocket(sokDesc); - - } else if ( bend == ELocalSocket ) { - initLocalSocket(sokDesc); - } - } - - void release() { - // if socket drops and http_parser can not call - // messageComplete, dispatch the ilastRequest - finalizeConnection(); - - isocket.disconnectAllQtConnections(); - isocket.release(); - - if ( ilastRequest ) { - ilastRequest->deleteLater(); - ilastRequest = nullptr; - } - - if ( ilastResponse ) { - ilastResponse->deleteLater(); - ilastResponse = nullptr; - } - - q_func()->deleteLater(); - } - -public: - void onReadyRead() { - while ( isocket.bytesAvailable() > 0 ) { - char buffer[4097] = {0}; - size_t readLength = static_cast(isocket.readRaw(buffer, 4096)); - - parse(buffer, readLength); - } - } - - void finalizeConnection() { - if ( ilastRequest == nullptr ) - return; - - ilastRequest->d_func()->finalizeSending([this]{ - emit ilastRequest->end(); - }); - } - -public: - int messageBegin(http_parser* parser); - int url(http_parser* parser, const char* at, size_t length); - int status(http_parser*, const char*, size_t) { - return 0; // not used in parsing incoming request. - } - int headerField(http_parser* parser, const char* at, size_t length); - int headerValue(http_parser* parser, const char* at, size_t length); - int headersComplete(http_parser* parser); - int body(http_parser* parser, const char* at, size_t length); - int messageComplete(http_parser* parser); - -private: - void initTcpSocket(qintptr sokDesc) { - QTcpSocket* sok = new QTcpSocket( q_func() ); - isocket.itcpSocket = sok; - sok->setSocketDescriptor(sokDesc); - - QObject::connect( - sok, &QTcpSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QTcpSocket::bytesWritten, - [this](){ - auto btw = isocket.itcpSocket->bytesToWrite(); - if ( btw == 0 && ilastResponse ) - emit ilastResponse->allBytesWritten(); - }); - QObject::connect( - sok, &QTcpSocket::disconnected, - q_func(), &QHttpConnection::disconnected, - Qt::QueuedConnection - ); - } - - void initSslSocket(qintptr sokDesc) { - QSslSocket* sok = new QSslSocket( q_func() ); - isocket.itcpSocket = sok; - sok->setSocketDescriptor(sokDesc); - - QObject::connect( - sok, &QSslSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QSslSocket::bytesWritten, - [this](){ - auto btw = isocket.itcpSocket->bytesToWrite(); - if ( btw == 0 && ilastResponse ) - emit ilastResponse->allBytesWritten(); - }); - QObject::connect( - sok, &QSslSocket::disconnected, - q_func(), &QHttpConnection::disconnected, - Qt::QueuedConnection - ); - } - - void initLocalSocket(qintptr sokDesc) { - QLocalSocket* sok = new QLocalSocket( q_func() ); - isocket.ilocalSocket = sok; - sok->setSocketDescriptor(sokDesc); - - QObject::connect( - sok, &QLocalSocket::readyRead, - [this](){ onReadyRead(); } - ); - QObject::connect( - sok, &QLocalSocket::bytesWritten, - [this](){ - auto btw = isocket.ilocalSocket->bytesToWrite(); - if ( btw == 0 && ilastResponse ) - emit ilastResponse->allBytesWritten(); - }); - QObject::connect( - sok, &QLocalSocket::disconnected, - q_func(), &QHttpConnection::disconnected, - Qt::QueuedConnection - ); - } - -protected: - QHttpConnection* const q_ptr; - - QByteArray itempUrl; - - // Since there can only be one request/response pair per connection at any - // time even with pipelining. - QHttpRequest* ilastRequest = nullptr; - QHttpResponse* ilastResponse = nullptr; - - TServerHandler ihandler = nullptr; - -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPSERVER_CONNECTION_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpserverrequest_private.hpp b/src/http/qhttp/private/qhttpserverrequest_private.hpp deleted file mode 100644 index c101d0b1f..000000000 --- a/src/http/qhttp/private/qhttpserverrequest_private.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_REQUEST_PRIVATE_HPP -#define QHTTPSERVER_REQUEST_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "httpreader.hxx" -#include "qhttpserverrequest.hpp" -#include "qhttpserverconnection.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -class QHttpRequestPrivate : - public details::HttpReader -{ -protected: - Q_DECLARE_PUBLIC(QHttpRequest) - QHttpRequest* const q_ptr; - -public: - explicit QHttpRequestPrivate(QHttpConnection* conn, QHttpRequest* q) : q_ptr(q), iconnection(conn) { - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpRequestPrivate() { - QHTTP_LINE_DEEPLOG - } - - void initialize() { - } - -public: - QString iremoteAddress; - quint16 iremotePort = 0; - - QHttpConnection* const iconnection = nullptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPSERVER_REQUEST_PRIVATE_HPP diff --git a/src/http/qhttp/private/qhttpserverresponse_private.hpp b/src/http/qhttp/private/qhttpserverresponse_private.hpp deleted file mode 100644 index 6c1cd4d94..000000000 --- a/src/http/qhttp/private/qhttpserverresponse_private.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/** private imeplementation. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_RESPONSE_PRIVATE_HPP -#define QHTTPSERVER_RESPONSE_PRIVATE_HPP -/////////////////////////////////////////////////////////////////////////////// -#include "httpwriter.hxx" -#include "qhttpserverresponse.hpp" -#include "qhttpserver.hpp" -#include "qhttpserverconnection.hpp" - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -class QHttpResponsePrivate : - public details::HttpWriter -{ - Q_DECLARE_PUBLIC(QHttpResponse) - -public: - explicit QHttpResponsePrivate(QHttpConnection* conn, QHttpResponse* q) - : q_ptr(q), iconnection(conn) { - QHTTP_LINE_DEEPLOG - } - - virtual ~QHttpResponsePrivate() { - QHTTP_LINE_DEEPLOG - } - - void initialize() { - isocket.ibackendType = iconnection->backendType(); - isocket.ilocalSocket = iconnection->localSocket(); - isocket.itcpSocket = iconnection->tcpSocket(); - - QObject::connect(iconnection, &QHttpConnection::disconnected, - q_func(), &QHttpResponse::deleteLater); - } - - QByteArray makeTitle(); - - void prepareHeadersToWrite(); - -protected: - QHttpResponse* const q_ptr; - QHttpConnection* const iconnection; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPSERVER_RESPONSE_PRIVATE_HPP diff --git a/src/http/qhttp/private/qsocket.hpp b/src/http/qhttp/private/qsocket.hpp deleted file mode 100644 index 06b35c550..000000000 --- a/src/http/qhttp/private/qsocket.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/** @file qsocket.hpp - * - * @copyright (C) 2016 - * @date 2016.05.26 - * @version 1.0.0 - * @author amir zamani - * - */ - -#ifndef __QHTTP_SOCKET_HPP__ -#define __QHTTP_SOCKET_HPP__ - -#include "qhttpfwd.hpp" - -#include -#include -#include -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace details { -/////////////////////////////////////////////////////////////////////////////// - -/** an adapter for different socket types. - * the main purpose of QHttp was to create a small HTTP server with ability to - * support UNIX sockets (QLocalSocket) - */ -class QSocket -{ -public: - void close() { - if ( itcpSocket ) - itcpSocket->close(); - - if ( ilocalSocket ) - ilocalSocket->close(); - } - - void release() { - close(); - if ( itcpSocket ) - itcpSocket->deleteLater(); - - if ( ilocalSocket ) - ilocalSocket->deleteLater(); - - itcpSocket = nullptr; - ilocalSocket = nullptr; - } - - void flush() { - if ( itcpSocket ) - itcpSocket->flush(); - - else if ( ilocalSocket ) - ilocalSocket->flush(); - } - - bool isOpen() const { - if ( ibackendType == ETcpSocket && itcpSocket ) - return itcpSocket->isOpen() - && itcpSocket->state() == QTcpSocket::ConnectedState; - - else if ( ibackendType == ELocalSocket && ilocalSocket ) - return ilocalSocket->isOpen() - && ilocalSocket->state() == QLocalSocket::ConnectedState; - - return false; - } - - void connectTo(const QUrl& url) { - if ( ilocalSocket ) - ilocalSocket->connectToServer(url.path()); - } - - void connectTo(const QString& host, quint16 port) { - if ( itcpSocket ) { - if ( ibackendType == ESslSocket ) - static_cast(itcpSocket)->connectToHostEncrypted(host, port); - else - itcpSocket->connectToHost(host, port); - } - } - - qint64 readRaw(char* buffer, int maxlen) { - if ( itcpSocket ) - return itcpSocket->read(buffer, maxlen); - - else if ( ilocalSocket ) - return ilocalSocket->read(buffer, maxlen); - - return 0; - } - - void writeRaw(const QByteArray& data) { - if ( itcpSocket ) - itcpSocket->write(data); - - else if ( ilocalSocket ) - ilocalSocket->write(data); - } - - qint64 bytesAvailable() { - if ( itcpSocket ) - return itcpSocket->bytesAvailable(); - - else if ( ilocalSocket ) - return ilocalSocket->bytesAvailable(); - - return 0; - } - - void disconnectAllQtConnections() { - if ( itcpSocket ) - QObject::disconnect(itcpSocket, 0, 0, 0); - - if ( ilocalSocket ) - QObject::disconnect(ilocalSocket, 0, 0, 0); - } - -public: - TBackend ibackendType = ETcpSocket; - QTcpSocket* itcpSocket = nullptr; - QLocalSocket* ilocalSocket = nullptr; -}; // class QSocket - -/////////////////////////////////////////////////////////////////////////////// -} // namespace details -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // __QHTTP_SOCKET_HPP__ diff --git a/src/http/qhttp/qhttpabstracts.cpp b/src/http/qhttp/qhttpabstracts.cpp deleted file mode 100644 index a29a90b9f..000000000 --- a/src/http/qhttp/qhttpabstracts.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include "qhttpabstracts.hpp" -#include "http-parser/http_parser.h" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -/////////////////////////////////////////////////////////////////////////////// -#if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) -# error "to compile QHttp classes, Qt 5.0 or later is needed." -#endif - -#define QHTTPABSTRACTS_HTTP_STATUS_MAP(XX) \ - XX(100, "Continue") \ - XX(101, "Switching Protocols") \ - /* RFC 2518) obsoleted by RFC 4918 */ \ - XX(102, "Processing") \ - XX(200, "OK") \ - XX(201, "Created") \ - XX(202, "Accepted") \ - XX(203, "Non-Authoritative Information") \ - XX(204, "No Content") \ - XX(205, "Reset Content") \ - XX(206, "Partial Content") \ - /* RFC 4918 */ \ - XX(207, "Multi-Status") \ - XX(300, "Multiple Choices") \ - XX(301, "Moved Permanently") \ - XX(302, "Moved Temporarily") \ - XX(303, "See Other") \ - XX(304, "Not Modified") \ - XX(305, "Use Proxy") \ - XX(307, "Temporary Redirect") \ - XX(400, "Bad Request") \ - XX(401, "Unauthorized") \ - XX(402, "Payment Required") \ - XX(403, "Forbidden") \ - XX(404, "Not Found") \ - XX(405, "Method Not Allowed") \ - XX(406, "Not Acceptable") \ - XX(407, "Proxy Authentication Required") \ - XX(408, "Request Time-out") \ - XX(409, "Conflict") \ - XX(410, "Gone") \ - XX(411, "Length Required") \ - XX(412, "Precondition Failed") \ - XX(413, "Request Entity Too Large") \ - XX(414, "Request-URI Too Large") \ - XX(415, "Unsupported Media Type") \ - XX(416, "Requested Range Not Satisfiable") \ - XX(417, "Expectation Failed") \ - /* RFC 2324 */ \ - XX(418, "I\"m a teapot") \ - /* RFC 4918 */ \ - XX(422, "Unprocessable Entity") \ - /* RFC 4918 */ \ - XX(423, "Locked") \ - /* RFC 4918 */ \ - XX(424, "Failed Dependency") \ - /* RFC 4918 */ \ - XX(425, "Unordered Collection") \ - /* RFC 2817 */ \ - XX(426, "Upgrade Required") \ - XX(500, "Internal Server Error") \ - XX(501, "Not Implemented") \ - XX(502, "Bad Gateway") \ - XX(503, "Service Unavailable") \ - XX(504, "Gateway Time-out") \ - XX(505, "HTTP Version not supported") \ - /* RFC 2295 */ \ - XX(506, "Variant Also Negotiates") \ - /* RFC 4918 */ \ - XX(507, "Insufficient Storage") \ - XX(509, "Bandwidth Limit Exceeded") \ - /* RFC 2774 */ \ - XX(510, "Not Extended") - -#define PATCH_STATUS_CODES(n,s) {n, s}, -static struct { - int code; - const char* message; -} g_status_codes[] { - QHTTPABSTRACTS_HTTP_STATUS_MAP(PATCH_STATUS_CODES) -}; -#undef PATCH_STATUS_CODES - -/////////////////////////////////////////////////////////////////////////////// - -const char* -Stringify::toString(TStatusCode code) { - size_t count = sizeof(g_status_codes) / sizeof(g_status_codes[0]); - for ( size_t i = 0; i < count; i++ ) { - if ( g_status_codes[i].code == code ) - return g_status_codes[i].message; - } - - return nullptr; -} - -const char* -Stringify::toString(THttpMethod method) { - return http_method_str(static_cast(method)); -} - -/////////////////////////////////////////////////////////////////////////////// - -QHttpAbstractInput::QHttpAbstractInput(QObject* parent) : QObject(parent) { -} - -QHttpAbstractOutput::QHttpAbstractOutput(QObject *parent) : QObject(parent) { -} - - -/////////////////////////////////////////////////////////////////////////////// -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpabstracts.hpp b/src/http/qhttp/qhttpabstracts.hpp deleted file mode 100644 index 005b9ce5d..000000000 --- a/src/http/qhttp/qhttpabstracts.hpp +++ /dev/null @@ -1,190 +0,0 @@ -/** interfaces of QHttp' incomming and outgoing classes. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPABSTRACTS_HPP -#define QHTTPABSTRACTS_HPP - -/////////////////////////////////////////////////////////////////////////////// -#include "qhttpfwd.hpp" - -#include -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -/////////////////////////////////////////////////////////////////////////////// - -/** a utility class to give the string representation of qhttp types. */ -class QHTTP_API Stringify -{ -public: - /** returns the standard message for an HTTP status code. */ - static const char* toString(TStatusCode); - - /** returns the standars name of an HTTP method. */ - static const char* toString(THttpMethod); -}; - -/////////////////////////////////////////////////////////////////////////////// - -/** an interface for input (incoming) HTTP packets. - * server::QHttpRequest or client::QHttpResponse inherit from this class. */ -class QHTTP_API QHttpAbstractInput : public QObject -{ - Q_OBJECT - -public: - /** Return all the headers in the incomming packet. - * This returns a reference. If you want to store headers - * somewhere else, where the request may be deleted, - * make sure you store them as a copy. - * @note All header names are lowercase . */ - virtual const THeaderHash& headers() const=0; - - /** The HTTP version of the packet. - * @return A string in the form of "x.x" */ - virtual const QString& httpVersion() const=0; - - /** If this packet was successfully received. - * Set before end() has been emitted, stating whether - * the message was properly received. This is false - * until the receiving the full request has completed. */ - virtual bool isSuccessful() const=0; - -signals: - /** Emitted when new body data has been received. - * @param data Received data. - * @note This may be emitted zero or more times depending on the transfer type. - * @see onData(); - */ - void data(QByteArray data); - - /** Emitted when the incomming packet has been fully received. - * @note The no more data() signals will be emitted after this. - * @see onEnd(); - */ - void end(); - -public: - /** optionally set a handler for data() signal. - * @param dataHandler a std::function or lambda handler to receive incoming data. - * @note if you set this handler, the data() signal won't be emitted anymore. - */ - template - void onData(Func f) { - QObject::connect(this, &QHttpAbstractInput::data, f); - } - - - /** optionally set a handler for end() signal. - * @param endHandler a std::function or lambda handler to receive end notification. - * @note if you set this handler, the end() signal won't be emitted anymore. - */ - template - void onEnd(Func f) { - QObject::connect(this, &QHttpAbstractInput::end, f); - } - -public: - /** tries to collect all the incoming data internally. - * @note if you call this method, data() signal won't be emitted and - * onData() will have no effect. - * - * @param atMost maximum acceptable incoming data. if the incoming data - * exceeds this value, the connection won't read any more data and - * end() signal will be emitted. - * default value (-1) means read data as "content-length" or unlimited if - * the body size is unknown. - */ - virtual void collectData(int atMost = -1) =0; - - /** returns the collected data requested by collectData(). */ - virtual const QByteArray& collectedData()const =0; - - -public: - virtual ~QHttpAbstractInput() = default; - - explicit QHttpAbstractInput(QObject* parent); - - Q_DISABLE_COPY(QHttpAbstractInput) -}; - -/////////////////////////////////////////////////////////////////////////////// - -/** an interface for output (outgoing) HTTP packets. - * server::QHttpResponse or client::QHttpRequest inherit from this class. */ -class QHTTP_API QHttpAbstractOutput : public QObject -{ - Q_OBJECT - -public: - /** changes the HTTP version string ex: "1.1" or "1.0". - * version is "1.1" set by default. */ - virtual void setVersion(const QString& versionString)=0; - - /** helper function. @sa addHeader */ - template - void addHeaderValue(const QByteArray &field, T value); - - /** adds an HTTP header to the packet. - * @note this method does not actually write anything to socket, just prepares the headers(). */ - virtual void addHeader(const QByteArray& field, const QByteArray& value)=0; - - /** returns all the headers that already been set. */ - virtual THeaderHash& headers()=0; - - /** writes a block of data into the HTTP packet. - * @note headers are written (flushed) before any data. - * @warning after calling this method add a new header, set staus code, set Url have no effect! */ - virtual void write(const QByteArray &data)=0; - - /** ends (finishes) the outgoing packet by calling write(). - * headers and data will be flushed to the underlying socket. - * - * @sa write() */ - virtual void end(const QByteArray &data = QByteArray())=0; - -signals: - /** Emitted when all the data has been sent. - * this signal indicates that the underlaying socket has transmitted all - * of it's buffered data. */ - void allBytesWritten(); - - /** Emitted when the packet is finished and reports if it was the last packet. - * if it was the last packet (google for "Connection: keep-alive / close") - * the http connection (socket) will be closed automatically. */ - void done(bool wasTheLastPacket); - -public: - virtual ~QHttpAbstractOutput() = default; - -protected: - explicit QHttpAbstractOutput(QObject* parent); - - Q_DISABLE_COPY(QHttpAbstractOutput) -}; - -template<> inline void -QHttpAbstractOutput::addHeaderValue(const QByteArray &field, int value) { - addHeader(field, QString::number(value).toLatin1()); -} - -template<> inline void -QHttpAbstractOutput::addHeaderValue(const QByteArray &field, size_t value) { - addHeader(field, QString::number(value).toLatin1()); -} - -template<> inline void -QHttpAbstractOutput::addHeaderValue(const QByteArray &field, QString value) { - addHeader(field, value.toUtf8()); -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // QHTTPABSTRACTS_HPP diff --git a/src/http/qhttp/qhttpclient.cpp b/src/http/qhttp/qhttpclient.cpp deleted file mode 100644 index b77645ecf..000000000 --- a/src/http/qhttp/qhttpclient.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "private/qhttpclient_private.hpp" - -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -QHttpClient::QHttpClient(QObject *parent) - : QObject(parent), d_ptr(new QHttpClientPrivate(this)) { - QHTTP_LINE_LOG -} - -QHttpClient::QHttpClient(QHttpClientPrivate &dd, QObject *parent) - : QObject(parent), d_ptr(&dd) { - QHTTP_LINE_LOG -} - -QHttpClient::~QHttpClient() { - QHTTP_LINE_LOG -} - -quint32 -QHttpClient::timeOut() const { - return d_func()->itimeOut; -} - -void -QHttpClient::setTimeOut(quint32 t) { - d_func()->itimeOut = t; -} - -bool -QHttpClient::isOpen() const { - return d_func()->isocket.isOpen(); -} - -void -QHttpClient::killConnection() { - Q_D(QHttpClient); - - d->iconnectingTimer.stop(); - d->itimer.stop(); - d->isocket.close(); -} - -TBackend -QHttpClient::backendType() const { - return d_func()->isocket.ibackendType; -} - -QTcpSocket* -QHttpClient::tcpSocket() const { - return d_func()->isocket.itcpSocket; -} - -QLocalSocket* -QHttpClient::localSocket() const { - return d_func()->isocket.ilocalSocket; -} - -void -QHttpClient::setConnectingTimeOut(quint32 timeout) { - Q_D(QHttpClient); - - if ( timeout == 0 ) { - d->iconnectingTimer.stop(); - - } else { - d->iconnectingTimer.start(timeout, - Qt::CoarseTimer, - this - ); - } -} - -bool -QHttpClient::request(THttpMethod method, QUrl url, - const TRequstHandler &reqHandler, - const TResponseHandler &resHandler) { - Q_D(QHttpClient); - - d->ireqHandler = nullptr; - d->irespHandler = nullptr; - - // if url is a local file (UNIX socket) the host could be empty! - if ( !url.isValid() || url.isEmpty() /*|| url.host().isEmpty()*/ ) - return false; - - // process handlers - if ( resHandler ) { - d->irespHandler = resHandler; - - if ( reqHandler ) - d->ireqHandler = reqHandler; - else - d->ireqHandler = [](QHttpRequest* req) ->void { - req->addHeader("connection", "close"); - req->end(); - }; - } - - auto requestCreator = [this, method, url]() { - // create request object - if ( d_ptr->ilastRequest ) - d_ptr->ilastRequest->deleteLater(); - - d_ptr->ilastRequest = new QHttpRequest(this); - QObject::connect(d_ptr->ilastRequest, &QHttpRequest::done, [this](bool wasTheLastPacket){ - d_ptr->ikeepAlive = !wasTheLastPacket; - }); - - d_ptr->ilastRequest->d_ptr->imethod = method; - d_ptr->ilastRequest->d_ptr->iurl = url; - }; - - // connecting to host/server must be the last thing. (after all function handlers and ...) - // check for type - if ( url.scheme().toLower() == QLatin1String("file") ) { - d->isocket.ibackendType = ELocalSocket; - d->initializeSocket(); - - requestCreator(); - - if ( d->isocket.isOpen() ) - d->onConnected(); - else - d->isocket.connectTo(url); - - } else { - bool ssl = url.scheme() == "https"; - - d->isocket.ibackendType = ssl ? ESslSocket : ETcpSocket; - d->initializeSocket(); - - requestCreator(); - - if ( d->isocket.isOpen() ) - d->onConnected(); - else - d->isocket.connectTo(url.host(), url.port(ssl ? 443 : 80)); - } - - return true; -} - -void -QHttpClient::timerEvent(QTimerEvent *e) { - Q_D(QHttpClient); - - if ( e->timerId() == d->itimer.timerId() ) { - killConnection(); - - } else if ( e->timerId() == d->iconnectingTimer.timerId() ) { - d->iconnectingTimer.stop(); - emit connectingTimeOut(); - } -} - -void -QHttpClient::onRequestReady(QHttpRequest *req) { - emit httpConnected(req); -} - -void -QHttpClient::onResponseReady(QHttpResponse *res) { - emit newResponse(res); -} - -/////////////////////////////////////////////////////////////////////////////// - -// if server closes the connection, ends the response or by any other reason -// the socket disconnects, then the irequest and iresponse instances may have -// been deleted. In these situations reading more http body or emitting end() -// for incoming request are not possible: -// if ( ilastRequest == nullptr ) -// return 0; - - - -int -QHttpClientPrivate::messageBegin(http_parser*) { - itempHeaderField.clear(); - itempHeaderValue.clear(); - - return 0; -} - -int -QHttpClientPrivate::status(http_parser* parser, const char* at, size_t length) { - if ( ilastResponse ) - ilastResponse->deleteLater(); - - ilastResponse = new QHttpResponse(q_func()); - ilastResponse->d_func()->istatus = static_cast(parser->status_code); - ilastResponse->d_func()->iversion = QString("%1.%2") - .arg(parser->http_major) - .arg(parser->http_minor); - ilastResponse->d_func()->icustomStatusMessage = QString::fromUtf8(at, length); - - return 0; -} - -int -QHttpClientPrivate::headerField(http_parser*, const char* at, size_t length) { - if ( ilastResponse == nullptr ) - return 0; - - // insert the header we parsed previously - // into the header map - if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) { - // header names are always lower-cased - ilastResponse->d_func()->iheaders.insert( - itempHeaderField.toLower(), - itempHeaderValue.toLower() - ); - // clear header value. this sets up a nice - // feedback loop where the next time - // HeaderValue is called, it can simply append - itempHeaderField.clear(); - itempHeaderValue.clear(); - } - - itempHeaderField.append(at, length); - return 0; -} - -int -QHttpClientPrivate::headerValue(http_parser*, const char* at, size_t length) { - - itempHeaderValue.append(at, length); - return 0; -} - -int -QHttpClientPrivate::headersComplete(http_parser*) { - if ( ilastResponse == nullptr ) - return 0; - - // Insert last remaining header - ilastResponse->d_func()->iheaders.insert( - itempHeaderField.toLower(), - itempHeaderValue.toLower() - ); - - if ( irespHandler ) - irespHandler(ilastResponse); - else - q_func()->onResponseReady(ilastResponse); - - return 0; -} - -int -QHttpClientPrivate::body(http_parser*, const char* at, size_t length) { - if ( ilastResponse == nullptr ) - return 0; - - ilastResponse->d_func()->ireadState = QHttpResponsePrivate::EPartial; - - if ( ilastResponse->d_func()->icollectRequired ) { - if ( !ilastResponse->d_func()->append(at, length) ) { - // forcefully dispatch the ilastResponse - finalizeConnection(); - } - - return 0; - } - - emit ilastResponse->data(QByteArray(at, length)); - return 0; -} - -int -QHttpClientPrivate::messageComplete(http_parser*) { - if ( ilastResponse == nullptr ) - return 0; - - // response is done - finalizeConnection(); - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpclient.hpp b/src/http/qhttp/qhttpclient.hpp deleted file mode 100644 index f73e7b7fc..000000000 --- a/src/http/qhttp/qhttpclient.hpp +++ /dev/null @@ -1,179 +0,0 @@ -/** HTTP client class. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_HPP -#define QHTTPCLIENT_HPP - -/////////////////////////////////////////////////////////////////////////////// -#include "qhttpfwd.hpp" - -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -using TRequstHandler = std::function; -using TResponseHandler = std::function; - -/** a simple and async HTTP client class which sends a request to an HTTP server and parses the - * corresponding response. - * This class internally handles the memory management and life cycle of QHttpRequest and - * QHttpResponse instances. you do not have to manually delete or keep their pointers. - * in fact the QHttpRequest and QHttpResponse object will be deleted when the internal socket - * disconnects. - */ -class QHTTP_API QHttpClient : public QObject -{ - Q_OBJECT - - Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut) - -public: - explicit QHttpClient(QObject *parent = nullptr); - - virtual ~QHttpClient(); - - /** tries to connect to a HTTP server. - * when the connection is made, the reqHandler will be called - * and when the response is ready, resHandler will be called. - * @note httpConnected() and newResponse() won't be emitted. - * - * @param method an HTTP method, ex: GET, POST, ... - * @param url specifies server's address, port and optional path and query strings. - * if url starts with socket:// the request will be made on QLocalSocket, otherwise - * normal QTcpSocket will be used. - * @param resHandler response handler (a lambda, std::function object, ...) - * @return true if the url is valid or false (no connection will be made). - */ - bool request(THttpMethod method, QUrl url, - const TRequstHandler& reqHandler, - const TResponseHandler& resHandler); - - /** tries to connect to a HTTP server. - * when the connection is made, a default request handler is called automatically ( - * simply calls req->end()) and when the response is ready, resHandler will be called. - * @note httpConnected() and newResponse() won't be emitted. - * - * @param method an HTTP method, ex: GET, POST, ... - * @param url specifies server's address, port and optional path and query strings. - * @param resHandler response handler (a lambda, std::function object, ...) - * @return true if the url is valid or false (no connection will be made). - */ - inline bool request(THttpMethod method, QUrl url, const TResponseHandler& resHandler) { - return request(method, url, nullptr, resHandler); - } - - /** tries to connect to a HTTP server. - * when the connection is made, creates and emits a QHttpRequest instance - * by @sa httpConnected(QHttpRequest*). - * @note both httpConnected() and newResponse() may be emitted. - * - * @param method an HTTP method, ex: GET, POST, ... - * @param url specifies server's address, port and optional path and query strings. - * @return true if the url is valid or false (no connection will be made). - */ - inline bool request(THttpMethod method, QUrl url) { - return request(method, url, nullptr, nullptr); - } - - /** checks if the connetion to the server is open. */ - bool isOpen() const; - - /** forcefully close the connection. */ - void killConnection(); - - - /** returns time-out value [mSec] for ESTABLISHED connections (sockets). - * @sa setTimeOut(). */ - quint32 timeOut()const; - - /** set time-out for ESTABLISHED connections in miliseconds [mSec]. - * each (already opened) connection will be forcefully closed after this timeout. - * a zero (0) value disables timer for new connections. */ - void setTimeOut(quint32); - - /** set a time-out [mSec] for making a new connection (make a request). - * if connecting to server takes more than this time-out value, - * the @sa timedOut(quint32) signal will be emitted and connection will be killed. - * 0 (default) timeout value means to disable this timer. - */ - void setConnectingTimeOut(quint32); - - template - void setConnectingTimeOut(quint32 timeout, Handler&& func) { - setConnectingTimeOut(timeout); - QObject::connect(this, &QHttpClient::connectingTimeOut, - std::forward(func) - ); - } - - /** returns the backend type of this client. */ - TBackend backendType() const; - - /** returns tcp socket of the connection if backend() == ETcpSocket. */ - QTcpSocket* tcpSocket() const; - - /** returns local socket of the connection if backend() == ELocalSocket. */ - QLocalSocket* localSocket() const; - -signals: - /** emitted when a new HTTP connection to the server is established. - * if you overload onRequestReady this signal won't be emitted. - * @sa onRequestReady - * @sa QHttpRequest - */ - void httpConnected(QHttpRequest* req); - - /** emitted when a new response is received from the server. - * if you overload onResponseReady this signal won't be emitted. - * @sa onResponseReady - * @sa QHttpResponse - */ - void newResponse(QHttpResponse* res); - - /** emitted when the HTTP connection drops or being disconnected. */ - void disconnected(); - - /// emitted when fails to connect to a HTTP server. @sa setConnectingTimeOut() - void connectingTimeOut(); - - -protected: - /** called when a new HTTP connection is established. - * you can overload this method, the default implementaion only emits connected(). - * @param req use this request instance for assinging the - * request headers and sending optional body. - * @see httpConnected(QHttpRequest*) - */ - virtual void onRequestReady(QHttpRequest* req); - - /** called when a new response is received from the server. - * you can overload this method, the default implementaion only emits newResponse(). - * @param res use this instance for reading incoming response. - * @see newResponse(QHttpResponse*) - */ - virtual void onResponseReady(QHttpResponse* res); - -protected: - explicit QHttpClient(QHttpClientPrivate&, QObject*); - - void timerEvent(QTimerEvent*) override; - - Q_DECLARE_PRIVATE(QHttpClient) - Q_DISABLE_COPY(QHttpClient) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPCLIENT_HPP diff --git a/src/http/qhttp/qhttpclientrequest.cpp b/src/http/qhttp/qhttpclientrequest.cpp deleted file mode 100644 index 6d7cd2341..000000000 --- a/src/http/qhttp/qhttpclientrequest.cpp +++ /dev/null @@ -1,97 +0,0 @@ -#include "private/qhttpclientrequest_private.hpp" -#include "qhttpclient.hpp" -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -QHttpRequest::QHttpRequest(QHttpClient* cli) - : QHttpAbstractOutput(cli) , d_ptr(new QHttpRequestPrivate(cli, this)) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpRequest::QHttpRequest(QHttpRequestPrivate& dd, QHttpClient* cli) - : QHttpAbstractOutput(cli) , d_ptr(&dd) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpRequest::~QHttpRequest() { - QHTTP_LINE_LOG -} - -void -QHttpRequest::setVersion(const QString &versionString) { - d_func()->iversion = versionString; -} - -void -QHttpRequest::addHeader(const QByteArray &field, const QByteArray &value) { - d_func()->addHeader(field, value); -} - -THeaderHash& -QHttpRequest::headers() { - return d_func()->iheaders; -} - -void -QHttpRequest::write(const QByteArray &data) { - d_func()->writeData(data); -} - -void -QHttpRequest::end(const QByteArray &data) { - Q_D(QHttpRequest); - - if ( d->endPacket(data) ) - emit done(!d->ikeepAlive); -} - -QHttpClient* -QHttpRequest::connection() const { - return d_func()->iclient; -} - -/////////////////////////////////////////////////////////////////////////////// -QByteArray -QHttpRequestPrivate::makeTitle() { - - QByteArray title; - title.reserve(512); - title.append(qhttp::Stringify::toString(imethod)) - .append(" "); - - QByteArray path = iurl.path(QUrl::FullyEncoded).toLatin1(); - if ( path.size() == 0 ) - path = "/"; - title.append(path); - - if ( iurl.hasQuery() ) - title.append("?").append(iurl.query(QUrl::FullyEncoded).toLatin1()); - - - title.append(" HTTP/") - .append(iversion.toLatin1()) - .append("\r\n"); - - return title; -} - -void -QHttpRequestPrivate::prepareHeadersToWrite() { - - if ( !iheaders.contains("host") ) { - QString portStr = ( -1 != iurl.port() ) ? - QString(":%1").arg(iurl.port()) : ""; - - iheaders.insert("host", - QString("%1%2").arg(iurl.host()).arg(portStr).toLatin1() - ); - } -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpclientrequest.hpp b/src/http/qhttp/qhttpclientrequest.hpp deleted file mode 100644 index 6a8750621..000000000 --- a/src/http/qhttp/qhttpclientrequest.hpp +++ /dev/null @@ -1,63 +0,0 @@ -/** HTTP request from a client. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_REQUEST_HPP -#define QHTTPCLIENT_REQUEST_HPP - -/////////////////////////////////////////////////////////////////////////////// -#include "qhttpabstracts.hpp" -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -/** a class for building a new HTTP request. - * the life cycle of this class and the memory management is handled by QHttpClient. - * @sa QHttpClient - */ -class QHTTP_API QHttpRequest : public QHttpAbstractOutput -{ - Q_OBJECT - -public: - virtual ~QHttpRequest(); - -public: // QHttpAbstractOutput methods: - /** @see QHttpAbstractOutput::setVersion(). */ - void setVersion(const QString& versionString) override; - - /** @see QHttpAbstractOutput::addHeader(). */ - void addHeader(const QByteArray& field, const QByteArray& value) override; - - /** @see QHttpAbstractOutput::headers(). */ - THeaderHash& headers() override; - - /** @see QHttpAbstractOutput::write(). */ - void write(const QByteArray &data) override; - - /** @see QHttpAbstractOutput::end(). */ - void end(const QByteArray &data = QByteArray()) override; - -public: - /** returns parent QHttpClient object. */ - QHttpClient* connection() const; - -protected: - explicit QHttpRequest(QHttpClient*); - explicit QHttpRequest(QHttpRequestPrivate&, QHttpClient*); - friend class QHttpClient; - - Q_DECLARE_PRIVATE(QHttpRequest) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPCLIENT_REQUEST_HPP diff --git a/src/http/qhttp/qhttpclientresponse.cpp b/src/http/qhttp/qhttpclientresponse.cpp deleted file mode 100644 index 182d2b0fc..000000000 --- a/src/http/qhttp/qhttpclientresponse.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "private/qhttpclientresponse_private.hpp" -#include "qhttpclient.hpp" -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -QHttpResponse::QHttpResponse(QHttpClient *cli) - : QHttpAbstractInput(cli), d_ptr(new QHttpResponsePrivate(cli, this)) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpResponse::QHttpResponse(QHttpResponsePrivate &dd, QHttpClient *cli) - : QHttpAbstractInput(cli), d_ptr(&dd) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpResponse::~QHttpResponse() { - QHTTP_LINE_LOG -} - -TStatusCode -QHttpResponse::status() const { - return d_func()->istatus; -} - -const QString& -QHttpResponse::statusString() const { - return d_func()->icustomStatusMessage; -} - -const QString& -QHttpResponse::httpVersion() const { - return d_func()->iversion; -} - -const THeaderHash& -QHttpResponse::headers() const { - return d_func()->iheaders; -} - -bool -QHttpResponse::isSuccessful() const { - return d_func()->isuccessful; -} - -void -QHttpResponse::collectData(int atMost) { - d_func()->collectData(atMost); -} - -const QByteArray& -QHttpResponse::collectedData() const { - return d_func()->icollectedData; -} - -QHttpClient* -QHttpResponse::connection() const { - return d_func()->iclient; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpclientresponse.hpp b/src/http/qhttp/qhttpclientresponse.hpp deleted file mode 100644 index 37024f0c4..000000000 --- a/src/http/qhttp/qhttpclientresponse.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/** HTTP response received by client. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPCLIENT_RESPONSE_HPP -#define QHTTPCLIENT_RESPONSE_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpabstracts.hpp" - -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace client { -/////////////////////////////////////////////////////////////////////////////// -/** a class for reading incoming HTTP response from a server. - * the life cycle of this class and the memory management is handled by QHttpClient. - * @sa QHttpClient - */ -class QHTTP_API QHttpResponse : public QHttpAbstractInput -{ - Q_OBJECT - -public: - virtual ~QHttpResponse(); - -public: // QHttpAbstractInput methods: - /** @see QHttpAbstractInput::headers(). */ - const THeaderHash& headers() const override; - - /** @see QHttpAbstractInput::httpVersion(). */ - const QString& httpVersion() const override; - - /** @see QHttpAbstractInput::isSuccessful(). */ - bool isSuccessful() const override; - - /** @see QHttpAbstractInput::collectData(). */ - void collectData(int atMost = -1) override; - - /** @see QHttpAbstractInput::collectedData(). */ - const QByteArray& collectedData()const override; - - -public: - /** The status code of this response. */ - TStatusCode status() const ; - - /** The server status message as string. - * may be slightly different than: @code qhttp::Stringify::toString(status()); @endcode - * depending on implementation of HTTP server. */ - const QString& statusString() const; - - /** returns parent QHttpClient object. */ - QHttpClient* connection() const; - -protected: - explicit QHttpResponse(QHttpClient*); - explicit QHttpResponse(QHttpResponsePrivate&, QHttpClient*); - friend class QHttpClientPrivate; - - Q_DECLARE_PRIVATE(QHttpResponse) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPCLIENT_RESPONSE_HPP diff --git a/src/http/qhttp/qhttpfwd.hpp b/src/http/qhttp/qhttpfwd.hpp deleted file mode 100644 index d6f38db8b..000000000 --- a/src/http/qhttp/qhttpfwd.hpp +++ /dev/null @@ -1,216 +0,0 @@ -/** forward declarations and general definitions of the QHttp. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPFWD_HPP -#define QHTTPFWD_HPP -/////////////////////////////////////////////////////////////////////////////// -#include -#include -#include - -#include -/////////////////////////////////////////////////////////////////////////////// -// Qt -class QTcpServer; -class QTcpSocket; -class QLocalServer; -class QLocalSocket; - -// http_parser -struct http_parser_settings; -struct http_parser; - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -/////////////////////////////////////////////////////////////////////////////// - -/** A map of request or response headers. */ -class THeaderHash : public QHash -{ -public: - /** checks for a header item, regardless of the case of the characters. */ - bool has(const QByteArray& key) const { - return contains(key.toLower()); - } - - /** checks if a header has the specified value ignoring the case of the characters. */ - bool keyHasValue(const QByteArray& key, const QByteArray& value) const { - if ( !contains(key) ) - return false; - - const QByteArray& v = QHash::value(key); - return qstrnicmp(value.constData(), v.constData(), v.size()) == 0; - } -}; - -/// QHash/QMap iterators are incompatibility with range for -template -void for_each(Iterator first, Iterator last, Func&& f) { - while ( first != last ) { - f( first ); - ++first; - } -} - - -/** Request method enumeration. - * @note Taken from http_parser.h */ -enum THttpMethod { - EHTTP_DELETE = 0, ///< DELETE - EHTTP_GET = 1, ///< GET - EHTTP_HEAD = 2, ///< HEAD - EHTTP_POST = 3, ///< POST - EHTTP_PUT = 4, ///< PUT - /* pathological */ - EHTTP_CONNECT = 5, ///< CONNECT - EHTTP_OPTIONS = 6, ///< OPTIONS - EHTTP_TRACE = 7, ///< TRACE - /* webdav */ - EHTTP_COPY = 8, ///< COPY - EHTTP_LOCK = 9, ///< LOCK - EHTTP_MKCOL = 10, ///< MKCOL - EHTTP_MOVE = 11, ///< MOVE - EHTTP_PROPFIND = 12, ///< PROPFIND - EHTTP_PROPPATCH = 13, ///< PROPPATCH - EHTTP_SEARCH = 14, ///< SEARCH - EHTTP_UNLOCK = 15, ///< UNLOCK - EHTTP_BIND = 16, ///< BIND - EHTTP_REBIND = 17, ///< REBIND - EHTTP_UNBIND = 18, ///< UNBIND - EHTTP_ACL = 19, ///< ACL - /* subversion */ - EHTTP_REPORT = 20, ///< REPORT - EHTTP_MKACTIVITY = 21, ///< MKACTIVITY - EHTTP_CHECKOUT = 22, ///< CHECKOUT - EHTTP_MERGE = 23, ///< MERGE - /* upnp */ - EHTTP_MSEARCH = 24, ///< M-SEARCH - EHTTP_NOTIFY = 25, ///< NOTIFY - EHTTP_SUBSCRIBE = 26, ///< SUBSCRIBE - EHTTP_UNSUBSCRIBE = 27, ///< UNSUBSCRIBE - /* RFC-5789 */ - EHTTP_PATCH = 28, ///< PATCH - EHTTP_PURGE = 29, ///< PURGE - /* CalDAV */ - EHTTP_MKCALENDAR = 30, ///< MKCALENDAR - /* RFC-2068, section 19.6.1.2 */ - EHTTP_LINK = 31, ///< LINK - EHTTP_UNLINK = 32, ///< UNLINK -}; - -/** HTTP status codes. */ -enum TStatusCode { - ESTATUS_CONTINUE = 100, - ESTATUS_SWITCH_PROTOCOLS = 101, - ESTATUS_OK = 200, - ESTATUS_CREATED = 201, - ESTATUS_ACCEPTED = 202, - ESTATUS_NON_AUTHORITATIVE_INFORMATION = 203, - ESTATUS_NO_CONTENT = 204, - ESTATUS_RESET_CONTENT = 205, - ESTATUS_PARTIAL_CONTENT = 206, - ESTATUS_MULTI_STATUS = 207, - ESTATUS_MULTIPLE_CHOICES = 300, - ESTATUS_MOVED_PERMANENTLY = 301, - ESTATUS_FOUND = 302, - ESTATUS_SEE_OTHER = 303, - ESTATUS_NOT_MODIFIED = 304, - ESTATUS_USE_PROXY = 305, - ESTATUS_TEMPORARY_REDIRECT = 307, - ESTATUS_BAD_REQUEST = 400, - ESTATUS_UNAUTHORIZED = 401, - ESTATUS_PAYMENT_REQUIRED = 402, - ESTATUS_FORBIDDEN = 403, - ESTATUS_NOT_FOUND = 404, - ESTATUS_METHOD_NOT_ALLOWED = 405, - ESTATUS_NOT_ACCEPTABLE = 406, - ESTATUS_PROXY_AUTHENTICATION_REQUIRED = 407, - ESTATUS_REQUEST_TIMEOUT = 408, - ESTATUS_CONFLICT = 409, - ESTATUS_GONE = 410, - ESTATUS_LENGTH_REQUIRED = 411, - ESTATUS_PRECONDITION_FAILED = 412, - ESTATUS_REQUEST_ENTITY_TOO_LARGE = 413, - ESTATUS_REQUEST_URI_TOO_LONG = 414, - ESTATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415, - ESTATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416, - ESTATUS_EXPECTATION_FAILED = 417, - ESTATUS_INTERNAL_SERVER_ERROR = 500, - ESTATUS_NOT_IMPLEMENTED = 501, - ESTATUS_BAD_GATEWAY = 502, - ESTATUS_SERVICE_UNAVAILABLE = 503, - ESTATUS_GATEWAY_TIMEOUT = 504, - ESTATUS_HTTP_VERSION_NOT_SUPPORTED = 505 -}; - -/** The backend of QHttp library. */ -enum TBackend { - ETcpSocket = 0, ///< client / server work on top of TCP/IP stack. (default) - ESslSocket = 1, ///< client / server work on SSL/TLS tcp stack. - ELocalSocket = 2 ///< client / server work on local socket (unix socket). -}; - -/////////////////////////////////////////////////////////////////////////////// -namespace server { -/////////////////////////////////////////////////////////////////////////////// -class QHttpServer; -class QHttpConnection; -class QHttpRequest; -class QHttpResponse; - -// Privte classes -class QHttpServerPrivate; -class QHttpConnectionPrivate; -class QHttpRequestPrivate; -class QHttpResponsePrivate; - -using TServerHandler = std::function; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -/////////////////////////////////////////////////////////////////////////////// -namespace client { -/////////////////////////////////////////////////////////////////////////////// -class QHttpClient; -class QHttpRequest; -class QHttpResponse; - -// Private classes -class QHttpClientPrivate; -class QHttpRequestPrivate; -class QHttpResponsePrivate; -/////////////////////////////////////////////////////////////////////////////// -} // namespace client -/////////////////////////////////////////////////////////////////////////////// -#ifdef Q_OS_WIN -# if defined(QHTTP_EXPORT) -# define QHTTP_API __declspec(dllexport) -# else -# define QHTTP_API __declspec(dllimport) -# endif -#else -# define QHTTP_API -#endif - - -#if QHTTP_MEMORY_LOG > 0 -# define QHTTP_LINE_LOG fprintf(stderr, "%s(): obj = %p @ %s[%d]\n",\ - __FUNCTION__, this, __FILE__, __LINE__); -#else -# define QHTTP_LINE_LOG -#endif - -#if QHTTP_MEMORY_LOG > 1 -# define QHTTP_LINE_DEEPLOG QHTTP_LINE_LOG -#else -# define QHTTP_LINE_DEEPLOG -#endif -/////////////////////////////////////////////////////////////////////////////// -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPFWD_HPP diff --git a/src/http/qhttp/qhttpserver.cpp b/src/http/qhttp/qhttpserver.cpp deleted file mode 100644 index a9c6a082c..000000000 --- a/src/http/qhttp/qhttpserver.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "private/qhttpserver_private.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// - -QHttpServer::QHttpServer(QObject *parent) - : QObject(parent), d_ptr(new QHttpServerPrivate) { -} - -QHttpServer::QHttpServer(QHttpServerPrivate &dd, QObject *parent) - : QObject(parent), d_ptr(&dd) { -} - -QHttpServer::~QHttpServer() { - stopListening(); -} - -bool -QHttpServer::listen(const QString &socketOrPort, const TServerHandler &handler) { - Q_D(QHttpServer); - - bool isNumber = false; - quint16 tcpPort = socketOrPort.toUShort(&isNumber); - if ( isNumber && tcpPort > 0 ) - return listen(QHostAddress::Any, tcpPort, handler); - - d->initialize(ELocalSocket, this); - d->ihandler = handler; - return d->ilocalServer->listen(socketOrPort); -} - -bool -QHttpServer::listen(const QHostAddress& address, quint16 port, const qhttp::server::TServerHandler& handler) { - Q_D(QHttpServer); - - d->initialize(d->ibackend, this); - d->ihandler = handler; - return d->itcpServer->listen(address, port); -} - -bool -QHttpServer::isListening() const { - const Q_D(QHttpServer); - - if ( ( d->ibackend == ETcpSocket || d->ibackend == ESslSocket ) && d->itcpServer ) - return d->itcpServer->isListening(); - - else if ( d->ibackend == ELocalSocket && d->ilocalServer ) - return d->ilocalServer->isListening(); - - return false; -} - -void -QHttpServer::stopListening() { - Q_D(QHttpServer); - - if ( d->itcpServer ) - d->itcpServer->close(); - - if ( d->ilocalServer ) { - d->ilocalServer->close(); - QLocalServer::removeServer( d->ilocalServer->fullServerName() ); - } -} - -quint32 -QHttpServer::timeOut() const { - return d_func()->itimeOut; -} - -void -QHttpServer::setTimeOut(quint32 newValue) { - d_func()->itimeOut = newValue; -} - -TBackend -QHttpServer::backendType() const { - return d_func()->ibackend; -} - -QTcpServer* -QHttpServer::tcpServer() const { - return d_func()->itcpServer.data(); -} - -QLocalServer* -QHttpServer::localServer() const { - return d_func()->ilocalServer.data(); -} - -void -QHttpServer::incomingConnection(qintptr handle) { - QHttpConnection* conn = new QHttpConnection(this); - conn->setSocketDescriptor(handle, backendType()); - conn->setTimeOut(d_func()->itimeOut); - - emit newConnection(conn); - - Q_D(QHttpServer); - if ( d->ihandler ) - QObject::connect(conn, &QHttpConnection::newRequest, d->ihandler); - else - incomingConnection(conn); -} - -void -QHttpServer::incomingConnection(QHttpConnection *connection) { - QObject::connect(connection, &QHttpConnection::newRequest, - this, &QHttpServer::newRequest); -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpserver.hpp b/src/http/qhttp/qhttpserver.hpp deleted file mode 100644 index 4e492e356..000000000 --- a/src/http/qhttp/qhttpserver.hpp +++ /dev/null @@ -1,131 +0,0 @@ -/** HTTP server class. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_HPP -#define QHTTPSERVER_HPP - -/////////////////////////////////////////////////////////////////////////////// -#include "qhttpfwd.hpp" - -#include -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// - -/** The QHttpServer class is a fast, async (non-blocking) HTTP server. */ -class QHTTP_API QHttpServer : public QObject -{ - Q_OBJECT - - Q_PROPERTY(quint32 timeOut READ timeOut WRITE setTimeOut) - -public: - /** construct a new HTTP Server. */ - explicit QHttpServer(QObject *parent = nullptr); - - virtual ~QHttpServer(); - - /** starts a TCP or Local (unix domain socket) server. - * if you provide a server handler, the newRequest() signal won't be emitted. - * - * @param socketOrPort could be a tcp port number as "8080" or a unix socket name as - * "/tmp/sample.socket" or "sample.socket". - * @param handler optional server handler (a lambda, std::function, ...) - * @return false if listening fails. - */ - bool listen(const QString& socketOrPort, - const TServerHandler& handler = nullptr); - - /** starts a TCP server on specified address and port. - * if you provide a server handler, the newRequest() signal won't be emitted. - * - * @param address listening address as QHostAddress::Any. - * @param port listening port. - * @param handler optional server handler (a lambda, std::function, ...) - * @return false if listening fails. - */ - bool listen(const QHostAddress& address, quint16 port, - const TServerHandler& handler = nullptr); - - /** @overload listen() */ - bool listen(quint16 port) { - return listen(QHostAddress::Any, port); - } - - /** returns true if server successfully listens. @sa listen() */ - bool isListening() const; - - /** closes the server and stops from listening. */ - void stopListening(); - - /** returns timeout value [mSec] for open connections (sockets). - * @sa setTimeOut(). */ - quint32 timeOut()const; - - /** set time-out for new open connections in miliseconds [mSec]. - * new incoming connections will be forcefully closed after this time out. - * a zero (0) value disables timer for new connections. */ - void setTimeOut(quint32); - - /** returns the QHttpServer's backend type. */ - TBackend backendType() const; - -signals: - /** emitted when a client makes a new request to the server if you do not override - * incomingConnection(QHttpConnection *connection); - * @sa incommingConnection(). */ - void newRequest(QHttpRequest *request, QHttpResponse *response); - - /** emitted when a new connection comes to the server if you do not override - * incomingConnection(QHttpConnection *connection); - * @sa incomingConnection(); */ - void newConnection(QHttpConnection* connection); - -protected: - /** returns the tcp server instance if the backend() == ETcpSocket. */ - QTcpServer* tcpServer() const; - - /** returns the local server instance if the backend() == ELocalSocket. */ - QLocalServer* localServer() const; - - - /** is called when server accepts a new connection. - * you can override this function for using a thread-pool or ... some other reasons. - * - * the default implementation just connects QHttpConnection::newRequest signal. - * @note if you override this method, the signal won't be emitted by QHttpServer. - * (perhaps, you do not need it anymore). - * - * @param connection New incoming connection. */ - virtual void incomingConnection(QHttpConnection* connection); - - /** overrides QTcpServer::incomingConnection() to make a new QHttpConnection. - * override this function if you like to create your derived QHttpConnection instances. - * - * @note if you override this method, incomingConnection(QHttpConnection*) or - * newRequest(QHttpRequest *, QHttpResponse *) signal won't be called. - * - * @see example/benchmark/server.cpp to see how to override. - */ - virtual void incomingConnection(qintptr handle); - -private: - explicit QHttpServer(QHttpServerPrivate&, QObject *parent); - - Q_DECLARE_PRIVATE(QHttpServer) - Q_DISABLE_COPY(QHttpServer) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPSERVER_HPP diff --git a/src/http/qhttp/qhttpserverconnection.cpp b/src/http/qhttp/qhttpserverconnection.cpp deleted file mode 100644 index 7345c83be..000000000 --- a/src/http/qhttp/qhttpserverconnection.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "private/qhttpserverconnection_private.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -QHttpConnection::QHttpConnection(QObject *parent) - : QObject(parent), d_ptr(new QHttpConnectionPrivate(this)) { - QHTTP_LINE_LOG -} - -QHttpConnection::QHttpConnection(QHttpConnectionPrivate& dd, QObject* parent) - : QObject(parent), d_ptr(&dd) { - QHTTP_LINE_LOG -} - -void -QHttpConnection::setSocketDescriptor(qintptr sokDescriptor, TBackend backendType) { - d_ptr->createSocket(sokDescriptor, backendType); -} - -QHttpConnection::~QHttpConnection() { - QHTTP_LINE_LOG -} - -void -QHttpConnection::setTimeOut(quint32 miliSeconds) { - if ( miliSeconds != 0 ) { - d_func()->itimeOut = miliSeconds; - d_func()->itimer.start(miliSeconds, Qt::CoarseTimer, this); - } -} - -void -QHttpConnection::killConnection() { - d_func()->isocket.close(); -} - -TBackend -QHttpConnection::backendType() const { - return d_func()->isocket.ibackendType; -} - -QTcpSocket* -QHttpConnection::tcpSocket() const { - return d_func()->isocket.itcpSocket; -} - -QLocalSocket* -QHttpConnection::localSocket() const { - return d_func()->isocket.ilocalSocket; -} - -void -QHttpConnection::onHandler(const TServerHandler &handler) { - d_func()->ihandler = handler; -} - -void -QHttpConnection::timerEvent(QTimerEvent *) { - killConnection(); -} - -/////////////////////////////////////////////////////////////////////////////// - -// if user closes the connection, ends the response or by any other reason the -// socket disconnects, then the irequest and iresponse instances may have -// been deleted. In these situations reading more http body or emitting end() -// for incoming request are not possible: -// if ( ilastRequest == nullptr ) -// return 0; - - -int -QHttpConnectionPrivate::messageBegin(http_parser*) { - itempUrl.clear(); - itempUrl.reserve(128); - - if ( ilastRequest ) - ilastRequest->deleteLater(); - - ilastRequest = new QHttpRequest(q_func()); - return 0; -} - -int -QHttpConnectionPrivate::url(http_parser*, const char* at, size_t length) { - Q_ASSERT(ilastRequest); - - itempUrl.append(at, length); - return 0; -} - -int -QHttpConnectionPrivate::headerField(http_parser*, const char* at, size_t length) { - if ( ilastRequest == nullptr ) - return 0; - - // insert the header we parsed previously - // into the header map - if ( !itempHeaderField.isEmpty() && !itempHeaderValue.isEmpty() ) { - // header names are always lower-cased - ilastRequest->d_func()->iheaders.insert( - itempHeaderField.toLower(), - itempHeaderValue.toLower() - ); - // clear header value. this sets up a nice - // feedback loop where the next time - // HeaderValue is called, it can simply append - itempHeaderField.clear(); - itempHeaderValue.clear(); - } - - itempHeaderField.append(at, length); - return 0; -} - -int -QHttpConnectionPrivate::headerValue(http_parser*, const char* at, size_t length) { - if ( ilastRequest == nullptr ) - return 0; - - itempHeaderValue.append(at, length); - return 0; -} - -int -QHttpConnectionPrivate::headersComplete(http_parser* parser) { - if ( ilastRequest == nullptr ) - return 0; - - ilastRequest->d_func()->iurl = QUrl(itempUrl); - - // set method - ilastRequest->d_func()->imethod = - static_cast(parser->method); - - // set version - ilastRequest->d_func()->iversion = QString("%1.%2") - .arg(parser->http_major) - .arg(parser->http_minor); - - // Insert last remaining header - ilastRequest->d_func()->iheaders.insert( - itempHeaderField.toLower(), - itempHeaderValue.toLower() - ); - - // set client information - if ( isocket.ibackendType == ETcpSocket || isocket.ibackendType == ESslSocket ) { - ilastRequest->d_func()->iremoteAddress = isocket.itcpSocket->peerAddress().toString(); - ilastRequest->d_func()->iremotePort = isocket.itcpSocket->peerPort(); - - } else if ( isocket.ibackendType == ELocalSocket ) { - ilastRequest->d_func()->iremoteAddress = isocket.ilocalSocket->fullServerName(); - ilastRequest->d_func()->iremotePort = 0; // not used in local sockets - } - - if ( ilastResponse ) - ilastResponse->deleteLater(); - ilastResponse = new QHttpResponse(q_func()); - - if ( parser->http_major < 1 || parser->http_minor < 1 ) - ilastResponse->d_func()->ikeepAlive = false; - - // close the connection if response was the last packet - QObject::connect(ilastResponse, &QHttpResponse::done, [this](bool wasTheLastPacket){ - ikeepAlive = !wasTheLastPacket; - if ( wasTheLastPacket ) { - isocket.flush(); - isocket.close(); - } - }); - - // we are good to go! - if ( ihandler ) - ihandler(ilastRequest, ilastResponse); - else - emit q_ptr->newRequest(ilastRequest, ilastResponse); - - return 0; -} - -int -QHttpConnectionPrivate::body(http_parser*, const char* at, size_t length) { - if ( ilastRequest == nullptr ) - return 0; - - ilastRequest->d_func()->ireadState = QHttpRequestPrivate::EPartial; - - if ( ilastRequest->d_func()->icollectRequired ) { - if ( !ilastRequest->d_func()->append(at, length) ) { - // forcefully dispatch the ilastRequest - finalizeConnection(); - } - - return 0; - } - - emit ilastRequest->data(QByteArray(at, length)); - return 0; -} - -int -QHttpConnectionPrivate::messageComplete(http_parser*) { - if ( ilastRequest == nullptr ) - return 0; - - // request is done - finalizeConnection(); - return 0; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpserverconnection.hpp b/src/http/qhttp/qhttpserverconnection.hpp deleted file mode 100644 index b908665cc..000000000 --- a/src/http/qhttp/qhttpserverconnection.hpp +++ /dev/null @@ -1,87 +0,0 @@ -/** HTTP connection class. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_CONNECTION_HPP -#define QHTTPSERVER_CONNECTION_HPP -/////////////////////////////////////////////////////////////////////////////// -#include "qhttpfwd.hpp" - -#include - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -/** a HTTP connection in the server. - * this class controls the HTTP connetion and handles life cycle and the memory management - * of QHttpRequest and QHttpResponse instances autoamtically. - */ -class QHTTP_API QHttpConnection : public QObject -{ - Q_OBJECT - -public: - virtual ~QHttpConnection(); - - /** set an optional timer event to close the connection. */ - void setTimeOut(quint32 miliSeconds); - - /** forcefully kills (closes) a connection. */ - void killConnection(); - - /** optionally set a handler for connection class. - * @note if you set this handler, the newRequest() signal won't be emitted. - */ - void onHandler(const TServerHandler& handler); - - /** returns the backend type of the connection. */ - TBackend backendType() const; - - /** returns connected socket if the backend() == ETcpSocket. */ - QTcpSocket* tcpSocket() const; - - /** returns connected socket if the backend() == ELocalSocket. */ - QLocalSocket* localSocket() const; - - /** creates a new QHttpConnection based on arguments. */ - static - QHttpConnection* create(qintptr sokDescriptor, TBackend backendType, QObject* parent) { - QHttpConnection* conn = new QHttpConnection(parent); - conn->setSocketDescriptor(sokDescriptor, backendType); - return conn; - } - -signals: - /** emitted when a pair of HTTP request and response are ready to interact. - * @param req incoming request by the client. - * @param res outgoing response to the client. - */ - void newRequest(QHttpRequest* req, QHttpResponse* res); - - /** emitted when the tcp/local socket, disconnects. */ - void disconnected(); - -protected: - explicit QHttpConnection(QObject *parent); - explicit QHttpConnection(QHttpConnectionPrivate&, QObject *); - - void setSocketDescriptor(qintptr sokDescriptor, TBackend backendType); - void timerEvent(QTimerEvent*) override; - - Q_DISABLE_COPY(QHttpConnection) - Q_DECLARE_PRIVATE(QHttpConnection) - QScopedPointer d_ptr; - - friend class QHttpServer; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // #define QHTTPSERVER_CONNECTION_HPP diff --git a/src/http/qhttp/qhttpserverrequest.cpp b/src/http/qhttp/qhttpserverrequest.cpp deleted file mode 100644 index 87787fa3a..000000000 --- a/src/http/qhttp/qhttpserverrequest.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "private/qhttpserverrequest_private.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -QHttpRequest::QHttpRequest(QHttpConnection *conn) - : QHttpAbstractInput(conn), d_ptr(new QHttpRequestPrivate(conn, this)) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpRequest::QHttpRequest(QHttpRequestPrivate &dd, QHttpConnection *conn) - : QHttpAbstractInput(conn), d_ptr(&dd) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpRequest::~QHttpRequest() { - QHTTP_LINE_LOG -} - -THttpMethod -QHttpRequest::method() const { - return d_func()->imethod; -} - -const QString -QHttpRequest::methodString() const { - return http_method_str(static_cast(d_func()->imethod)); -} - -const QUrl& -QHttpRequest::url() const { - return d_func()->iurl; -} - -const QString& -QHttpRequest::httpVersion() const { - return d_func()->iversion; -} - -const THeaderHash& -QHttpRequest::headers() const { - return d_func()->iheaders; -} - -const QString& -QHttpRequest::remoteAddress() const { - return d_func()->iremoteAddress; -} - -quint16 -QHttpRequest::remotePort() const { - return d_func()->iremotePort; -} - -bool -QHttpRequest::isSuccessful() const { - return d_func()->isuccessful; -} - -void -QHttpRequest::collectData(int atMost) { - d_func()->collectData(atMost); -} - -const QByteArray& -QHttpRequest::collectedData() const { - return d_func()->icollectedData; -} - -QHttpConnection* -QHttpRequest::connection() const { - return d_ptr->iconnection; -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpserverrequest.hpp b/src/http/qhttp/qhttpserverrequest.hpp deleted file mode 100644 index 4001c710c..000000000 --- a/src/http/qhttp/qhttpserverrequest.hpp +++ /dev/null @@ -1,82 +0,0 @@ -/** HTTP request which is received by the server. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_REQUEST_HPP -#define QHTTPSERVER_REQUEST_HPP -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpabstracts.hpp" - -#include -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -/** The QHttpRequest class represents the header and body data sent by the client. - * The class is read-only. - * @sa QHttpConnection - */ -class QHTTP_API QHttpRequest : public QHttpAbstractInput -{ - Q_OBJECT - -public: - virtual ~QHttpRequest(); - -public: // QHttpAbstractInput methods: - /** @see QHttpAbstractInput::headers(). */ - const THeaderHash& headers() const override; - - /** @see QHttpAbstractInput::httpVersion(). */ - const QString& httpVersion() const override; - - /** @see QHttpAbstractInput::isSuccessful(). */ - bool isSuccessful() const override; - - /** @see QHttpAbstractInput::collectData(). */ - void collectData(int atMost = -1) override; - - /** @see QHttpAbstractInput::collectedData(). */ - const QByteArray& collectedData()const override; - - -public: - /** The method used for the request. */ - THttpMethod method() const ; - - /** Returns the method string for the request. - * @note This will plainly transform the enum into a string HTTP_GET -> "HTTP_GET". */ - const QString methodString() const; - - /** The complete URL for the request. - * This includes the path and query string. @sa path(). */ - const QUrl& url() const; - - /** IP Address of the client in dotted decimal format. */ - const QString& remoteAddress() const; - - /** Outbound connection port for the client. */ - quint16 remotePort() const; - - /** returns the parent QHttpConnection object. */ - QHttpConnection* connection() const; - -protected: - explicit QHttpRequest(QHttpConnection*); - explicit QHttpRequest(QHttpRequestPrivate&, QHttpConnection*); - friend class QHttpConnectionPrivate; - - Q_DECLARE_PRIVATE(QHttpRequest) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPSERVER_REQUEST_HPP diff --git a/src/http/qhttp/qhttpserverresponse.cpp b/src/http/qhttp/qhttpserverresponse.cpp deleted file mode 100644 index 5e60746ee..000000000 --- a/src/http/qhttp/qhttpserverresponse.cpp +++ /dev/null @@ -1,90 +0,0 @@ -#include "private/qhttpserverresponse_private.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -QHttpResponse::QHttpResponse(QHttpConnection* conn) - : QHttpAbstractOutput(conn) , d_ptr(new QHttpResponsePrivate(conn, this)) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpResponse::QHttpResponse(QHttpResponsePrivate& dd, QHttpConnection* conn) - : QHttpAbstractOutput(conn) , d_ptr(&dd) { - d_ptr->initialize(); - QHTTP_LINE_LOG -} - -QHttpResponse::~QHttpResponse() { - QHTTP_LINE_LOG -} - -void -QHttpResponse::setStatusCode(TStatusCode code) { - d_func()->istatus = code; -} - -void -QHttpResponse::setVersion(const QString &versionString) { - d_func()->iversion = versionString; -} - -void -QHttpResponse::addHeader(const QByteArray &field, const QByteArray &value) { - d_func()->addHeader(field, value); -} - -THeaderHash& -QHttpResponse::headers() { - return d_func()->iheaders; -} - -void -QHttpResponse::write(const QByteArray &data) { - d_func()->writeData(data); -} - -void -QHttpResponse::end(const QByteArray &data) { - Q_D(QHttpResponse); - - if ( d->endPacket(data) ) - emit done(!d->ikeepAlive); -} - -QHttpConnection* -QHttpResponse::connection() const { - return d_func()->iconnection; -} - -/////////////////////////////////////////////////////////////////////////////// -QByteArray -QHttpResponsePrivate::makeTitle() { - - QString title = QString("HTTP/%1 %2 %3\r\n") - .arg(iversion) - .arg(istatus) - .arg(Stringify::toString(istatus)); - - return title.toLatin1(); -} - -void -QHttpResponsePrivate::prepareHeadersToWrite() { - - if ( !iheaders.contains("date") ) { - // Sun, 06 Nov 1994 08:49:37 GMT - RFC 822. Use QLocale::c() so english is used for month and - // day. - QString dateString = QLocale::c(). - toString(QDateTime::currentDateTimeUtc(), - "ddd, dd MMM yyyy hh:mm:ss") - .append(" GMT"); - addHeader("date", dateString.toLatin1()); - } -} - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// diff --git a/src/http/qhttp/qhttpserverresponse.hpp b/src/http/qhttp/qhttpserverresponse.hpp deleted file mode 100644 index d066712a8..000000000 --- a/src/http/qhttp/qhttpserverresponse.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/** HTTP response from a server. - * https://github.com/azadkuh/qhttp - * - * @author amir zamani - * @version 2.0.0 - * @date 2014-07-11 - */ - -#ifndef QHTTPSERVER_RESPONSE_HPP -#define QHTTPSERVER_RESPONSE_HPP - -/////////////////////////////////////////////////////////////////////////////// - -#include "qhttpabstracts.hpp" - -/////////////////////////////////////////////////////////////////////////////// -namespace qhttp { -namespace server { -/////////////////////////////////////////////////////////////////////////////// -/** The QHttpResponse class handles sending data back to the client as a response to a request. - * @sa QHttpConnection - */ -class QHTTP_API QHttpResponse : public QHttpAbstractOutput -{ - Q_OBJECT - -public: - virtual ~QHttpResponse(); - -public: - /** set the response HTTP status code. @sa TStatusCode. - * default value is ESTATUS_BAD_REQUEST. - * @sa write() - */ - void setStatusCode(TStatusCode code); - -public: // QHttpAbstractOutput methods: - /** @see QHttpAbstractOutput::setVersion(). */ - void setVersion(const QString& versionString) override; - - /** @see QHttpAbstractOutput::addHeader(). */ - void addHeader(const QByteArray& field, const QByteArray& value) override; - - /** @see QHttpAbstractOutput::headers(). */ - THeaderHash& headers() override; - - /** @see QHttpAbstractOutput::write(). */ - void write(const QByteArray &data) override; - - /** @see QHttpAbstractOutput::end(). */ - void end(const QByteArray &data = QByteArray()) override; - -public: - /** returns the parent QHttpConnection object. */ - QHttpConnection* connection() const; - -protected: - explicit QHttpResponse(QHttpConnection*); - explicit QHttpResponse(QHttpResponsePrivate&, QHttpConnection*); - friend class QHttpConnectionPrivate; - - Q_DECLARE_PRIVATE(QHttpResponse) - QScopedPointer d_ptr; -}; - -/////////////////////////////////////////////////////////////////////////////// -} // namespace server -} // namespace qhttp -/////////////////////////////////////////////////////////////////////////////// -#endif // define QHTTPSERVER_RESPONSE_HPP diff --git a/tests/config-keepassx-tests.h.cmake b/tests/config-keepassx-tests.h.cmake index 26204f7dd..7b5a14f51 100644 --- a/tests/config-keepassx-tests.h.cmake +++ b/tests/config-keepassx-tests.h.cmake @@ -5,7 +5,6 @@ #define KEEPASSX_TEST_DATA_DIR "${KEEPASSX_TEST_DATA_DIR}" -#cmakedefine WITH_XC_HTTP #cmakedefine WITH_XC_AUTOTYPE #cmakedefine WITH_XC_YUBIKEY