mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-19 11:11:36 -05:00
Support KeyPassHttp protocol.
Allows using passIfox (firefox) and Chromeipass (chrome).
This commit is contained in:
parent
20f3f23576
commit
ea992bc3e6
@ -101,6 +101,9 @@ set(keepassx_SOURCES
|
|||||||
gui/group/EditGroupWidget.cpp
|
gui/group/EditGroupWidget.cpp
|
||||||
gui/group/GroupModel.cpp
|
gui/group/GroupModel.cpp
|
||||||
gui/group/GroupView.cpp
|
gui/group/GroupView.cpp
|
||||||
|
http/Protocol.cpp
|
||||||
|
http/Server.cpp
|
||||||
|
http/Service.cpp
|
||||||
keys/CompositeKey.cpp
|
keys/CompositeKey.cpp
|
||||||
keys/FileKey.cpp
|
keys/FileKey.cpp
|
||||||
keys/Key.h
|
keys/Key.h
|
||||||
@ -163,6 +166,9 @@ set(keepassx_MOC
|
|||||||
gui/group/EditGroupWidget.h
|
gui/group/EditGroupWidget.h
|
||||||
gui/group/GroupModel.h
|
gui/group/GroupModel.h
|
||||||
gui/group/GroupView.h
|
gui/group/GroupView.h
|
||||||
|
http/Protocol.h
|
||||||
|
http/Server.h
|
||||||
|
http/Service.h
|
||||||
keys/CompositeKey_p.h
|
keys/CompositeKey_p.h
|
||||||
streams/HashedBlockStream.h
|
streams/HashedBlockStream.h
|
||||||
streams/LayeredStream.h
|
streams/LayeredStream.h
|
||||||
@ -204,11 +210,17 @@ qt4_wrap_cpp(keepassx_SOURCES ${keepassx_MOC})
|
|||||||
add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
add_library(keepassx_core STATIC ${keepassx_SOURCES})
|
||||||
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
|
||||||
|
|
||||||
|
add_subdirectory(http/qhttpserver)
|
||||||
|
add_subdirectory(http/qjson)
|
||||||
|
|
||||||
add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE})
|
add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE})
|
||||||
target_link_libraries(${PROGNAME}
|
target_link_libraries(${PROGNAME}
|
||||||
keepassx_core
|
keepassx_core
|
||||||
|
qjson
|
||||||
|
qhttpserver
|
||||||
${QT_QTCORE_LIBRARY}
|
${QT_QTCORE_LIBRARY}
|
||||||
${QT_QTGUI_LIBRARY}
|
${QT_QTGUI_LIBRARY}
|
||||||
|
${QT_QTNETWORK_LIBRARY}
|
||||||
${GCRYPT_LIBRARIES}
|
${GCRYPT_LIBRARIES}
|
||||||
${ZLIB_LIBRARIES})
|
${ZLIB_LIBRARIES})
|
||||||
|
|
||||||
|
@ -89,6 +89,12 @@ Uuid Uuid::fromBase64(const QString& str)
|
|||||||
return Uuid(data);
|
return Uuid(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Uuid Uuid::fromHex(const QString& str)
|
||||||
|
{
|
||||||
|
QByteArray data = QByteArray::fromHex(str.toAscii());
|
||||||
|
return Uuid(data);
|
||||||
|
}
|
||||||
|
|
||||||
uint qHash(const Uuid& key)
|
uint qHash(const Uuid& key)
|
||||||
{
|
{
|
||||||
return qHash(key.toByteArray());
|
return qHash(key.toByteArray());
|
||||||
|
@ -37,6 +37,7 @@ public:
|
|||||||
bool operator!=(const Uuid& other) const;
|
bool operator!=(const Uuid& other) const;
|
||||||
static const int Length;
|
static const int Length;
|
||||||
static Uuid fromBase64(const QString& str);
|
static Uuid fromBase64(const QString& str);
|
||||||
|
static Uuid fromHex(const QString& str);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QByteArray m_data;
|
QByteArray m_data;
|
||||||
|
@ -20,13 +20,16 @@
|
|||||||
#include "crypto/SymmetricCipherGcrypt.h"
|
#include "crypto/SymmetricCipherGcrypt.h"
|
||||||
#include "crypto/SymmetricCipherSalsa20.h"
|
#include "crypto/SymmetricCipherSalsa20.h"
|
||||||
|
|
||||||
|
SymmetricCipher::SymmetricCipher()
|
||||||
|
: m_backend(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
|
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
|
||||||
: m_backend(createBackend(algo, mode, direction))
|
: m_backend(0)
|
||||||
{
|
{
|
||||||
m_backend->init();
|
init(algo, mode, direction, key, iv);
|
||||||
m_backend->setKey(key);
|
|
||||||
m_backend->setIv(iv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SymmetricCipher::~SymmetricCipher()
|
SymmetricCipher::~SymmetricCipher()
|
||||||
@ -50,12 +53,31 @@ SymmetricCipherBackend* SymmetricCipher::createBackend(SymmetricCipher::Algorith
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymmetricCipher::init(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
|
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_backend);
|
||||||
|
m_backend.reset(createBackend(algo, mode, direction));
|
||||||
|
m_backend->init();
|
||||||
|
m_backend->setKey(key);
|
||||||
|
if (!iv.isNull())
|
||||||
|
m_backend->setIv(iv);
|
||||||
|
}
|
||||||
|
|
||||||
void SymmetricCipher::reset()
|
void SymmetricCipher::reset()
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
m_backend->reset();
|
m_backend->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SymmetricCipher::setIv(const QByteArray &iv)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
|
m_backend->setIv(iv);
|
||||||
|
}
|
||||||
|
|
||||||
int SymmetricCipher::blockSize() const
|
int SymmetricCipher::blockSize() const
|
||||||
{
|
{
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
return m_backend->blockSize();
|
return m_backend->blockSize();
|
||||||
}
|
}
|
||||||
|
@ -47,31 +47,42 @@ public:
|
|||||||
Encrypt
|
Encrypt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SymmetricCipher();
|
||||||
SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv);
|
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv = QByteArray());
|
||||||
~SymmetricCipher();
|
~SymmetricCipher();
|
||||||
|
|
||||||
|
void init(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode, SymmetricCipher::Direction direction,
|
||||||
|
const QByteArray &key, const QByteArray &iv = QByteArray());
|
||||||
|
bool isValid() const {
|
||||||
|
return m_backend != 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline QByteArray process(const QByteArray& data) {
|
inline QByteArray process(const QByteArray& data) {
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
return m_backend->process(data);
|
return m_backend->process(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void processInPlace(QByteArray& data) {
|
inline void processInPlace(QByteArray& data) {
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
m_backend->processInPlace(data);
|
m_backend->processInPlace(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void processInPlace(QByteArray& data, quint64 rounds) {
|
inline void processInPlace(QByteArray& data, quint64 rounds) {
|
||||||
Q_ASSERT(rounds > 0);
|
Q_ASSERT(rounds > 0);
|
||||||
|
Q_ASSERT(m_backend);
|
||||||
m_backend->processInPlace(data, rounds);
|
m_backend->processInPlace(data, rounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
void setIv(const QByteArray& iv);
|
||||||
int blockSize() const;
|
int blockSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
|
||||||
SymmetricCipher::Direction direction);
|
SymmetricCipher::Direction direction);
|
||||||
|
|
||||||
const QScopedPointer<SymmetricCipherBackend> m_backend;
|
QScopedPointer<SymmetricCipherBackend> m_backend;
|
||||||
|
|
||||||
Q_DISABLE_COPY(SymmetricCipher)
|
Q_DISABLE_COPY(SymmetricCipher)
|
||||||
};
|
};
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
#include "gui/FileDialog.h"
|
#include "gui/FileDialog.h"
|
||||||
#include "gui/entry/EntryView.h"
|
#include "gui/entry/EntryView.h"
|
||||||
#include "gui/group/GroupView.h"
|
#include "gui/group/GroupView.h"
|
||||||
|
#include "http/Service.h"
|
||||||
|
|
||||||
DatabaseManagerStruct::DatabaseManagerStruct()
|
DatabaseManagerStruct::DatabaseManagerStruct()
|
||||||
: dbWidget(Q_NULLPTR)
|
: dbWidget(Q_NULLPTR)
|
||||||
@ -53,6 +54,8 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
|||||||
|
|
||||||
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int)));
|
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int)));
|
||||||
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
||||||
|
|
||||||
|
(new Service(this))->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
DatabaseTabWidget::~DatabaseTabWidget()
|
DatabaseTabWidget::~DatabaseTabWidget()
|
||||||
|
477
src/http/Protocol.cpp
Normal file
477
src/http/Protocol.cpp
Normal file
@ -0,0 +1,477 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Response.cpp
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Protocol.h"
|
||||||
|
|
||||||
|
#include <QtCore/QMetaProperty>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
#include "qjson/parser.h"
|
||||||
|
#include "qjson/qobjecthelper.h"
|
||||||
|
#include "qjson/serializer.h"
|
||||||
|
|
||||||
|
#include "crypto/Random.h"
|
||||||
|
#include "crypto/SymmetricCipher.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.5.0.0";
|
||||||
|
}/*namespace KeepassHttpProtocol*/
|
||||||
|
|
||||||
|
using namespace KeepassHttpProtocol;
|
||||||
|
|
||||||
|
static QHash<QString, RequestType> createStringHash()
|
||||||
|
{
|
||||||
|
QHash<QString, RequestType> 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<QString, RequestType> REQUEST_STRINGS = createStringHash();
|
||||||
|
return REQUEST_STRINGS.value(str, INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray decode64(QString s)
|
||||||
|
{
|
||||||
|
return QByteArray::fromBase64(s.toAscii());
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString encode64(QByteArray b)
|
||||||
|
{
|
||||||
|
return QString::fromAscii(b.toBase64());
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray decrypt2(const QByteArray & data, SymmetricCipher & cipher)
|
||||||
|
{
|
||||||
|
//Ensure we get full blocks only
|
||||||
|
if (data.length() <= 0 || data.length() % cipher.blockSize())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
//Decrypt
|
||||||
|
cipher.reset();
|
||||||
|
QByteArray buffer = cipher.process(data);
|
||||||
|
|
||||||
|
//Remove PKCS#7 padding
|
||||||
|
buffer.chop(buffer.at(buffer.length()-1));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString decrypt(const QString &data, SymmetricCipher &cipher)
|
||||||
|
{
|
||||||
|
return QString::fromUtf8(decrypt2(decode64(data), cipher));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QByteArray encrypt2(const QByteArray & data, SymmetricCipher & 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();
|
||||||
|
cipher.processInPlace(buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString encrypt(const QString & data, SymmetricCipher & cipher)
|
||||||
|
{
|
||||||
|
return encode64(encrypt2(data.toUtf8(), cipher));
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant qobject2qvariant(const QObject * object, const QStringList ignoredProperties = QStringList(QString(QLatin1String("objectName"))))
|
||||||
|
{
|
||||||
|
QVariantMap result;
|
||||||
|
const QMetaObject *metaobject = object->metaObject();
|
||||||
|
int count = metaobject->propertyCount();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
QMetaProperty metaproperty = metaobject->property(i);
|
||||||
|
const char *name = metaproperty.name();
|
||||||
|
|
||||||
|
if (ignoredProperties.contains(QLatin1String(name)) || (!metaproperty.isReadable()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
QVariant value = object->property(name);
|
||||||
|
if (!value.isNull() /*&& value.isValid()*/) //Do not add NULL or invalid fields
|
||||||
|
result[QLatin1String(name)] = value;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Request
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Request::Request(): m_requestType(INVALID)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
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.init(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt,
|
||||||
|
decode64(key), decode64(m_nonce));
|
||||||
|
return decrypt(m_verifier, m_cipher) == m_nonce;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::fromJson(QString text)
|
||||||
|
{
|
||||||
|
bool isok = false;
|
||||||
|
QVariant v = QJson::Parser().parse(text.toUtf8(), &isok);
|
||||||
|
if (!isok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
m_requestType.clear();
|
||||||
|
QJson::QObjectHelper::qvariant2qobject(v.toMap(), this);
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::setVerifier(QString key)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!m_cipher.isValid());
|
||||||
|
m_cipher.init(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt, decode64(key));
|
||||||
|
|
||||||
|
//Generate new IV
|
||||||
|
const QByteArray iv = Random::randomArray(m_cipher.blockSize());
|
||||||
|
m_cipher.setIv(iv);
|
||||||
|
m_nonce = encode64(iv);
|
||||||
|
|
||||||
|
//Encrypt
|
||||||
|
m_verifier = encrypt(m_nonce, m_cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Response::toJson()
|
||||||
|
{
|
||||||
|
QVariant result = qobject2qvariant(this);
|
||||||
|
|
||||||
|
QJson::Serializer s;
|
||||||
|
s.setIndentMode(QJson::IndentCompact);
|
||||||
|
return s.serialize(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
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<QVariant> res;
|
||||||
|
res.reserve(m_entries.size());
|
||||||
|
Q_FOREACH(const Entry &entry, m_entries)
|
||||||
|
res.append(qobject2qvariant(&entry));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Response::setEntries(const QList<Entry> &entries)
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_cipher.isValid());
|
||||||
|
|
||||||
|
m_count = entries.count();
|
||||||
|
|
||||||
|
QList<Entry> encryptedEntries;
|
||||||
|
encryptedEntries.reserve(m_count);
|
||||||
|
Q_FOREACH(const Entry &entry, entries) {
|
||||||
|
encryptedEntries << Entry(encrypt(entry.name(), m_cipher),
|
||||||
|
encrypt(entry.login(), m_cipher),
|
||||||
|
entry.password().isNull() ? QString() : encrypt(entry.password(), m_cipher),
|
||||||
|
encrypt(entry.uuid(), m_cipher));
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// ResponseEntry
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(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)
|
||||||
|
{}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
182
src/http/Protocol.h
Normal file
182
src/http/Protocol.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Response.h
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RESPONSE_H
|
||||||
|
#define RESPONSE_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QCryptographicHash>
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
#include "crypto/SymmetricCipher.h"
|
||||||
|
|
||||||
|
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 SymmetricCipher m_cipher;
|
||||||
|
};
|
||||||
|
|
||||||
|
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 )
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_login;
|
||||||
|
QString m_password;
|
||||||
|
QString m_uuid;
|
||||||
|
QString m_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
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<Entry> &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<Entry> m_entries;
|
||||||
|
QString m_nonce;
|
||||||
|
QString m_verifier;
|
||||||
|
SymmetricCipher m_cipher;
|
||||||
|
};
|
||||||
|
|
||||||
|
}/*namespace KeepassHttpProtocol*/
|
||||||
|
|
||||||
|
#endif // RESPONSE_H
|
217
src/http/Server.cpp
Normal file
217
src/http/Server.cpp
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Server.cpp
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Server.h"
|
||||||
|
#include "qhttpserver/qhttpserver.h"
|
||||||
|
#include "qhttpserver/qhttprequest.h"
|
||||||
|
#include "qhttpserver/qhttpresponse.h"
|
||||||
|
#include "Protocol.h"
|
||||||
|
#include "crypto/Crypto.h"
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QCryptographicHash>
|
||||||
|
#include <QtGui/QMessageBox>
|
||||||
|
|
||||||
|
using namespace KeepassHttpProtocol;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Request
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
RequestHandler::RequestHandler(QHttpRequest *request, QHttpResponse *response): m_request(request), m_response(response)
|
||||||
|
{
|
||||||
|
m_request->storeBody();
|
||||||
|
connect(m_request, SIGNAL(end()), this, SLOT(onEnd()));
|
||||||
|
connect(m_response, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestHandler::~RequestHandler()
|
||||||
|
{
|
||||||
|
delete m_request;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestHandler::onEnd()
|
||||||
|
{
|
||||||
|
Q_EMIT requestComplete(m_request, m_response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// Request
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
Server::Server(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_httpServer(new QHttpServer(this)),
|
||||||
|
m_started(false)
|
||||||
|
{
|
||||||
|
connect(m_httpServer, SIGNAL(newRequest(QHttpRequest*,QHttpResponse*)), this, SLOT(handleRequest(QHttpRequest*,QHttpResponse*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::start()
|
||||||
|
{
|
||||||
|
if (m_started)
|
||||||
|
return;
|
||||||
|
m_started = m_httpServer->listen(QHostAddress::LocalHost, 19455);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::stop()
|
||||||
|
{
|
||||||
|
if (!m_started)
|
||||||
|
return;
|
||||||
|
m_httpServer->close();
|
||||||
|
m_started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::handleRequest(QHttpRequest *request, QHttpResponse *response)
|
||||||
|
{
|
||||||
|
RequestHandler * h = new RequestHandler(request, response);
|
||||||
|
connect(h, SIGNAL(requestComplete(QHttpRequest*,QHttpResponse*)), this, SLOT(handleRequestComplete(QHttpRequest*,QHttpResponse*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::handleRequestComplete(QHttpRequest *request, QHttpResponse *response)
|
||||||
|
{
|
||||||
|
Request r;
|
||||||
|
if (!isDatabaseOpened() && !openDatabase()) {
|
||||||
|
response->writeHead(QHttpResponse::STATUS_SERVICE_UNAVAILABLE);
|
||||||
|
response->end();
|
||||||
|
} else if (request->header("content-type").compare("application/json", Qt::CaseInsensitive) == 0 &&
|
||||||
|
r.fromJson(request->body())) {
|
||||||
|
|
||||||
|
QByteArray hash = QCryptographicHash::hash((getDatabaseRootUuid() + getDatabaseRecycleBinUuid()).toUtf8(),
|
||||||
|
QCryptographicHash::Sha1).toHex();
|
||||||
|
|
||||||
|
Response protocolResp(r, QString::fromAscii(hash));
|
||||||
|
switch(r.requestType()) {
|
||||||
|
case INVALID: break;
|
||||||
|
case TEST_ASSOCIATE: testAssociate(r, &protocolResp); break;
|
||||||
|
case ASSOCIATE: associate(r, &protocolResp); break;
|
||||||
|
case GET_LOGINS: getLogins(r, &protocolResp); break;
|
||||||
|
case GET_LOGINS_COUNT: getLoginsCount(r, &protocolResp); break;
|
||||||
|
case GET_ALL_LOGINS: getAllLogins(r, &protocolResp); break;
|
||||||
|
case SET_LOGIN: setLogin(r, &protocolResp); break;
|
||||||
|
case GENERATE_PASSWORD: generatePassword(r, &protocolResp); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray s = protocolResp.toJson().toUtf8();
|
||||||
|
response->setHeader("Content-Type", "application/json");
|
||||||
|
response->setHeader("Content-Length", QString::number(s.size()));
|
||||||
|
response->writeHead(QHttpResponse::STATUS_OK);
|
||||||
|
response->write(s);
|
||||||
|
response->end();
|
||||||
|
} else {
|
||||||
|
response->writeHead(QHttpResponse::STATUS_BAD_REQUEST);
|
||||||
|
response->end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Server::testAssociate(const Request& r, Response * protocolResp)
|
||||||
|
{
|
||||||
|
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<Entry> 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;
|
||||||
|
|
||||||
|
// parms.SearchString = @"^[A-Za-z0-9:/-]+\.[A-Za-z0-9:/-]+$"; // match anything looking like a domain or url
|
||||||
|
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();
|
||||||
|
|
||||||
|
protocolResp->setSuccess();
|
||||||
|
protocolResp->setId(r.id());
|
||||||
|
protocolResp->setVerifier(key);
|
||||||
|
protocolResp->setEntries(QList<Entry>() << Entry("generate-password", "generate-password", password, "generate-password"));
|
||||||
|
|
||||||
|
memset(password.data(), 0, password.length());
|
||||||
|
}
|
91
src/http/Server.h
Normal file
91
src/http/Server.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Server.h
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SERVER_H
|
||||||
|
#define SERVER_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
|
||||||
|
class QHttpServer;
|
||||||
|
class QHttpRequest;
|
||||||
|
class QHttpResponse;
|
||||||
|
|
||||||
|
namespace KeepassHttpProtocol {
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class Response;
|
||||||
|
class Entry;
|
||||||
|
|
||||||
|
class RequestHandler: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RequestHandler(QHttpRequest *request, QHttpResponse *response);
|
||||||
|
~RequestHandler();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void onEnd();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void requestComplete(QHttpRequest *request, QHttpResponse *response);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHttpRequest * m_request;
|
||||||
|
QHttpResponse *m_response;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit Server(QObject *parent = 0);
|
||||||
|
|
||||||
|
//TODO: use QByteArray?
|
||||||
|
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<Entry> 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<Entry> 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 Q_SLOTS:
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void handleRequest(QHttpRequest * request, QHttpResponse* response);
|
||||||
|
void handleRequestComplete(QHttpRequest * request, 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);
|
||||||
|
|
||||||
|
QHttpServer * const m_httpServer;
|
||||||
|
bool m_started;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /*namespace KeepassHttpProtocol*/
|
||||||
|
|
||||||
|
#endif // SERVER_H
|
293
src/http/Service.cpp
Normal file
293
src/http/Service.cpp
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Service.cpp
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Service.h"
|
||||||
|
#include "Protocol.h"
|
||||||
|
#include "core/Database.h"
|
||||||
|
#include "core/Entry.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
#include "core/Metadata.h"
|
||||||
|
#include "core/Uuid.h"
|
||||||
|
#include "core/PasswordGenerator.h"
|
||||||
|
#include <QtGui/QInputDialog>
|
||||||
|
#include <QtGui/QMessageBox>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
Service::Service(DatabaseTabWidget *parent) :
|
||||||
|
KeepassHttpProtocol::Server(parent),
|
||||||
|
m_dbTabWidget(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static const 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(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
|
||||||
|
//private const int DEFAULT_NOTIFICATION_TIME = 5000;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Service::openDatabase()
|
||||||
|
{
|
||||||
|
if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget())
|
||||||
|
if (dbWidget->currentMode() == DatabaseWidget::LockedMode) {
|
||||||
|
//- show notification
|
||||||
|
//- open window
|
||||||
|
//- wait a few seconds for user to unlock...
|
||||||
|
}
|
||||||
|
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)) {
|
||||||
|
do {
|
||||||
|
bool ok;
|
||||||
|
//Indicate who wants to associate, and request user to enter the 'name' of association key
|
||||||
|
id = QInputDialog::getText(0, tr("KeyPassX/Http: New key association request"),
|
||||||
|
tr("You have received an association request for the above key. If you would like to "
|
||||||
|
"allow it access to your KeePassX database give it a unique name to identify and a"
|
||||||
|
"ccept it."),
|
||||||
|
QLineEdit::Normal, QString(), &ok);
|
||||||
|
if (!ok || id.isEmpty())
|
||||||
|
return QString();
|
||||||
|
//Warn if association key already exists
|
||||||
|
} while(!config->attributes()->value(QLatin1String(ASSOCIATE_KEY_PREFIX) + id).isEmpty() &&
|
||||||
|
QMessageBox(QMessageBox::Warning, tr("KeyPassX/Http: 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).exec() == 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<Entry*> Service::searchEntries(const QString &text)
|
||||||
|
{
|
||||||
|
QList<Entry*> entries;
|
||||||
|
|
||||||
|
//TODO: setting to search all databases [e.g. as long as the 'current' db is authentified
|
||||||
|
|
||||||
|
//Search entries matching the hostname
|
||||||
|
QString hostname = QUrl(text).host();
|
||||||
|
if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget())
|
||||||
|
if (Database * db = dbWidget->database())
|
||||||
|
if (Group * rootGroup = db->rootGroup())
|
||||||
|
do {
|
||||||
|
Q_FOREACH (Entry * entry, rootGroup->search(hostname, Qt::CaseInsensitive)) {
|
||||||
|
QString title = entry->title();
|
||||||
|
QString url = entry->url();
|
||||||
|
|
||||||
|
//Filter to match hostname in Title and Url fields
|
||||||
|
if ( hostname.contains(title)
|
||||||
|
|| hostname.contains(url)
|
||||||
|
|| (matchUrlScheme(title) && hostname.contains(QUrl(title).host()))
|
||||||
|
|| (matchUrlScheme(url) && hostname.contains(QUrl(url).host())) )
|
||||||
|
entries.append(entry);
|
||||||
|
}
|
||||||
|
} while(entries.isEmpty() && removeFirstDomain(hostname));
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<KeepassHttpProtocol::Entry> Service::findMatchingEntries(const QString &id, const QString &url, const QString &submitUrl, const QString &realm)
|
||||||
|
{
|
||||||
|
QList<KeepassHttpProtocol::Entry> result;
|
||||||
|
const QList<Entry*> pwEntries = searchEntries(url);
|
||||||
|
Q_FOREACH (Entry * entry, pwEntries) {
|
||||||
|
//Filter accepted/denied entries
|
||||||
|
// if (c.Allow.Contains(formHost) && (submitHost == null || c.Allow.Contains(submitHost)))
|
||||||
|
// return true;
|
||||||
|
// if (c.Deny.Contains(formHost) || (submitHost != null && c.Deny.Contains(submitHost)))
|
||||||
|
// return false;
|
||||||
|
// if (realm != null && c.Realm != realm)
|
||||||
|
// return false;
|
||||||
|
|
||||||
|
//If we are unsure for some entries:
|
||||||
|
//- balloon to grant accessc if possible
|
||||||
|
//- if clicked, show confirmation dialog --> accept/reject (w/ list of items?)
|
||||||
|
// The website XXX wants to access your credentials
|
||||||
|
// MORE (---> if clicked, shows the list of returned entries)
|
||||||
|
// [x] Ask me again [Allow] [Deny]
|
||||||
|
// If accepted, store that entry can be accessed without confirmation
|
||||||
|
//- else, show only items which do not require validation
|
||||||
|
|
||||||
|
//TODO: sort [--> need a flag], or do this in Server class [--> need an extra 'sort order' key in Entry, and we always compute it]
|
||||||
|
result << KeepassHttpProtocol::Entry(entry->title(), entry->username(), entry->password(), entry->uuid().toHex());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Service::countMatchingEntries(const QString &id, const QString &url, const QString &submitUrl, const QString &realm)
|
||||||
|
{
|
||||||
|
return searchEntries(url).count();
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<KeepassHttpProtocol::Entry> Service::searchAllEntries(const QString &id)
|
||||||
|
{
|
||||||
|
QList<KeepassHttpProtocol::Entry> result;
|
||||||
|
if (DatabaseWidget * dbWidget = m_dbTabWidget->currentDatabaseWidget())
|
||||||
|
if (Database * db = dbWidget->database())
|
||||||
|
if (Group * rootGroup = db->rootGroup())
|
||||||
|
Q_FOREACH (Entry * entry, rootGroup->entriesRecursive())
|
||||||
|
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()) {
|
||||||
|
const QString groupName = QLatin1String(KEEPASSHTTP_GROUP_NAME);//TODO: setting to decide where new keys are created
|
||||||
|
|
||||||
|
Q_FOREACH (const Group * g, rootGroup->groupsRecursive(true))
|
||||||
|
if (g->name() == groupName)
|
||||||
|
return db->resolveGroup(g->uuid());
|
||||||
|
|
||||||
|
Group * group;
|
||||||
|
group = new Group();
|
||||||
|
group->setUuid(Uuid::random());
|
||||||
|
group->setName(groupName);
|
||||||
|
group->setIcon(Group::DefaultIconNumber); //TODO: WorldIconNumber
|
||||||
|
group->setParent(rootGroup);
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Service::addEntry(const QString &id, 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(Entry::DefaultIconNumber); //TODO: WorldIconNumber
|
||||||
|
entry->setUsername(login);
|
||||||
|
entry->setPassword(password);
|
||||||
|
entry->setGroup(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Service::updateEntry(const QString &id, 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) {
|
||||||
|
bool autoAllow = false; //TODO: setting to request confirmation/auto-allow
|
||||||
|
if ( autoAllow
|
||||||
|
|| QMessageBox(QMessageBox::Warning, tr("KeyPassX/Http: Update Entry"),
|
||||||
|
tr("Do you want to update the information in {0} - {1}?").arg(QUrl(url).host()).arg(u),
|
||||||
|
QMessageBox::Yes|QMessageBox::No).exec() == QMessageBox::Yes ) {
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->setUsername(login);
|
||||||
|
entry->setPassword(password);
|
||||||
|
entry->endUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Service::generatePassword()
|
||||||
|
{
|
||||||
|
PasswordGenerator * pwGenerator = passwordGenerator();
|
||||||
|
//TODO: password generator settings
|
||||||
|
return pwGenerator->generatePassword(20,
|
||||||
|
PasswordGenerator::LowerLetters | PasswordGenerator::UpperLetters | PasswordGenerator::Numbers,
|
||||||
|
PasswordGenerator::ExcludeLookAlike | PasswordGenerator::CharFromEveryGroup);
|
||||||
|
}
|
51
src/http/Service.h
Normal file
51
src/http/Service.h
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
***************************************************************************
|
||||||
|
* @file Service.h
|
||||||
|
*
|
||||||
|
* @brief
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013
|
||||||
|
*
|
||||||
|
* @author Francois Ferrand
|
||||||
|
* @date 4/2013
|
||||||
|
***************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SERVICE_H
|
||||||
|
#define SERVICE_H
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#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<KeepassHttpProtocol::Entry> 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<KeepassHttpProtocol::Entry> 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();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Entry* getConfigEntry(bool create = false);
|
||||||
|
bool matchUrlScheme(const QString &url);
|
||||||
|
bool removeFirstDomain(QString &hostname);
|
||||||
|
Group *findCreateAddEntryGroup();
|
||||||
|
QList<Entry *> searchEntries(const QString &text);
|
||||||
|
|
||||||
|
DatabaseTabWidget * const m_dbTabWidget;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SERVICE_H
|
20
src/http/qhttpserver/CMakeLists.txt
Normal file
20
src/http/qhttpserver/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
|
||||||
|
set(qhttpserver_MOC_HDRS
|
||||||
|
qhttpserver.h
|
||||||
|
qhttpresponse.h
|
||||||
|
qhttprequest.h
|
||||||
|
qhttpconnection.h
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (NOT Qt5Core_FOUND)
|
||||||
|
qt4_wrap_cpp(qhttpserver_MOC_SRCS ${qhttpserver_MOC_HDRS})
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
set (qhttpserver_SRCS qhttpconnection.cpp qhttprequest.cpp qhttpresponse.cpp qhttpserver.cpp
|
||||||
|
http-parser/http_parser.c http-parser/url_parser.c)
|
||||||
|
set (qhttpserver_HEADERS qhttpconnection.h qhttprequest.h qhttpresponse.h qhttpserver.h
|
||||||
|
http-parser/http_parser.h)
|
||||||
|
|
||||||
|
INCLUDE_DIRECTORIES(http-parser)
|
||||||
|
|
||||||
|
add_library (qhttpserver STATIC ${qhttpserver_SRCS} ${qhttpserver_MOC_SRCS} ${qhttpserver_HEADERS})
|
19
src/http/qhttpserver/LICENSE
Normal file
19
src/http/qhttpserver/LICENSE
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Copyright (C) 2011-2012 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
|
||||||
|
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.
|
40
src/http/qhttpserver/http-parser/AUTHORS
Normal file
40
src/http/qhttpserver/http-parser/AUTHORS
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Authors ordered by first contribution.
|
||||||
|
Ryan Dahl <ry@tinyclouds.org>
|
||||||
|
Jeremy Hinegardner <jeremy@hinegardner.org>
|
||||||
|
Sergey Shepelev <temotor@gmail.com>
|
||||||
|
Joe Damato <ice799@gmail.com>
|
||||||
|
tomika <tomika_nospam@freemail.hu>
|
||||||
|
Phoenix Sol <phoenix@burninglabs.com>
|
||||||
|
Cliff Frey <cliff@meraki.com>
|
||||||
|
Ewen Cheslack-Postava <ewencp@cs.stanford.edu>
|
||||||
|
Santiago Gala <sgala@apache.org>
|
||||||
|
Tim Becker <tim.becker@syngenio.de>
|
||||||
|
Jeff Terrace <jterrace@gmail.com>
|
||||||
|
Ben Noordhuis <info@bnoordhuis.nl>
|
||||||
|
Nathan Rajlich <nathan@tootallnate.net>
|
||||||
|
Mark Nottingham <mnot@mnot.net>
|
||||||
|
Aman Gupta <aman@tmm1.net>
|
||||||
|
Tim Becker <tim.becker@kuriositaet.de>
|
||||||
|
Sean Cunningham <sean.cunningham@mandiant.com>
|
||||||
|
Peter Griess <pg@std.in>
|
||||||
|
Salman Haq <salman.haq@asti-usa.com>
|
||||||
|
Cliff Frey <clifffrey@gmail.com>
|
||||||
|
Jon Kolb <jon@b0g.us>
|
||||||
|
Fouad Mardini <f.mardini@gmail.com>
|
||||||
|
Paul Querna <pquerna@apache.org>
|
||||||
|
Felix Geisendörfer <felix@debuggable.com>
|
||||||
|
koichik <koichik@improvement.jp>
|
||||||
|
Andre Caron <andre.l.caron@gmail.com>
|
||||||
|
Ivo Raisr <ivosh@ivosh.net>
|
||||||
|
James McLaughlin <jamie@lacewing-project.org>
|
||||||
|
David Gwynne <loki@animata.net>
|
||||||
|
LE ROUX Thomas <thomas@november-eleven.fr>
|
||||||
|
Randy Rizun <rrizun@ortivawireless.com>
|
||||||
|
Andre Louis Caron <andre.louis.caron@usherbrooke.ca>
|
||||||
|
Simon Zimmermann <simonz05@gmail.com>
|
||||||
|
Erik Dubbelboer <erik@dubbelboer.com>
|
||||||
|
Martell Malone <martellmalone@gmail.com>
|
||||||
|
Bertrand Paquet <bpaquet@octo.com>
|
||||||
|
BogDan Vatra <bogdan@kde.org>
|
||||||
|
Peter Faiman <peter@thepicard.org>
|
||||||
|
Corey Richardson <corey@octayn.net>
|
4
src/http/qhttpserver/http-parser/CONTRIBUTIONS
Normal file
4
src/http/qhttpserver/http-parser/CONTRIBUTIONS
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
Contributors must agree to the Contributor License Agreement before patches
|
||||||
|
can be accepted.
|
||||||
|
|
||||||
|
http://spreadsheets2.google.com/viewform?hl=en&formkey=dDJXOGUwbzlYaWM4cHN1MERwQS1CSnc6MQ
|
23
src/http/qhttpserver/http-parser/LICENSE-MIT
Normal file
23
src/http/qhttpserver/http-parser/LICENSE-MIT
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
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.
|
178
src/http/qhttpserver/http-parser/README.md
Normal file
178
src/http/qhttpserver/http-parser/README.md
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
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:
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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 recieved.
|
||||||
|
*/
|
||||||
|
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 forth 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 Web Socket 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 http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75 for more
|
||||||
|
information the Web Socket 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_uri,
|
||||||
|
(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.
|
||||||
|
|
||||||
|
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 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
|
2174
src/http/qhttpserver/http-parser/http_parser.c
Normal file
2174
src/http/qhttpserver/http-parser/http_parser.c
Normal file
File diff suppressed because it is too large
Load Diff
111
src/http/qhttpserver/http-parser/http_parser.gyp
Normal file
111
src/http/qhttpserver/http-parser/http_parser.gyp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
# This file is used with the GYP meta build system.
|
||||||
|
# http://code.google.com/p/gyp/
|
||||||
|
# To build try this:
|
||||||
|
# svn co http://gyp.googlecode.com/svn/trunk gyp
|
||||||
|
# ./gyp/gyp -f make --depth=`pwd` http_parser.gyp
|
||||||
|
# ./out/Debug/test
|
||||||
|
{
|
||||||
|
'target_defaults': {
|
||||||
|
'default_configuration': 'Debug',
|
||||||
|
'configurations': {
|
||||||
|
# TODO: hoist these out and put them somewhere common, because
|
||||||
|
# RuntimeLibrary MUST MATCH across the entire project
|
||||||
|
'Debug': {
|
||||||
|
'defines': [ 'DEBUG', '_DEBUG' ],
|
||||||
|
'cflags': [ '-Wall', '-Wextra', '-O0', '-g', '-ftrapv' ],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'RuntimeLibrary': 1, # static debug
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'Release': {
|
||||||
|
'defines': [ 'NDEBUG' ],
|
||||||
|
'cflags': [ '-Wall', '-Wextra', '-O3' ],
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
'RuntimeLibrary': 0, # static release
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
},
|
||||||
|
'VCLibrarianTool': {
|
||||||
|
},
|
||||||
|
'VCLinkerTool': {
|
||||||
|
'GenerateDebugInformation': 'true',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'conditions': [
|
||||||
|
['OS == "win"', {
|
||||||
|
'defines': [
|
||||||
|
'WIN32'
|
||||||
|
],
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
'targets': [
|
||||||
|
{
|
||||||
|
'target_name': 'http_parser',
|
||||||
|
'type': 'static_library',
|
||||||
|
'include_dirs': [ '.' ],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
||||||
|
'include_dirs': [ '.' ],
|
||||||
|
},
|
||||||
|
'defines': [ 'HTTP_PARSER_STRICT=0' ],
|
||||||
|
'sources': [ './http_parser.c', ],
|
||||||
|
'conditions': [
|
||||||
|
['OS=="win"', {
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
# Compile as C++. http_parser.c is actually C99, but C++ is
|
||||||
|
# close enough in this case.
|
||||||
|
'CompileAs': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_name': 'http_parser_strict',
|
||||||
|
'type': 'static_library',
|
||||||
|
'include_dirs': [ '.' ],
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
||||||
|
'include_dirs': [ '.' ],
|
||||||
|
},
|
||||||
|
'defines': [ 'HTTP_PARSER_STRICT=1' ],
|
||||||
|
'sources': [ './http_parser.c', ],
|
||||||
|
'conditions': [
|
||||||
|
['OS=="win"', {
|
||||||
|
'msvs_settings': {
|
||||||
|
'VCCLCompilerTool': {
|
||||||
|
# Compile as C++. http_parser.c is actually C99, but C++ is
|
||||||
|
# close enough in this case.
|
||||||
|
'CompileAs': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_name': 'test-nonstrict',
|
||||||
|
'type': 'executable',
|
||||||
|
'dependencies': [ 'http_parser' ],
|
||||||
|
'sources': [ 'test.c' ]
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
'target_name': 'test-strict',
|
||||||
|
'type': 'executable',
|
||||||
|
'dependencies': [ 'http_parser_strict' ],
|
||||||
|
'sources': [ 'test.c' ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
302
src/http/qhttpserver/http-parser/http_parser.h
Normal file
302
src/http/qhttpserver/http-parser/http_parser.h
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
/* 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
|
||||||
|
|
||||||
|
#define HTTP_PARSER_VERSION_MAJOR 2
|
||||||
|
#define HTTP_PARSER_VERSION_MINOR 0
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600)
|
||||||
|
#include <BaseTsd.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
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 <stdint.h>
|
||||||
|
#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 */
|
||||||
|
#define HTTP_MAX_HEADER_SIZE (80*1024)
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* http_data_cb does not return data chunks. It will be call arbitrarally
|
||||||
|
* 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*);
|
||||||
|
|
||||||
|
|
||||||
|
/* 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) \
|
||||||
|
/* subversion */ \
|
||||||
|
XX(16, REPORT, REPORT) \
|
||||||
|
XX(17, MKACTIVITY, MKACTIVITY) \
|
||||||
|
XX(18, CHECKOUT, CHECKOUT) \
|
||||||
|
XX(19, MERGE, MERGE) \
|
||||||
|
/* upnp */ \
|
||||||
|
XX(20, MSEARCH, M-SEARCH) \
|
||||||
|
XX(21, NOTIFY, NOTIFY) \
|
||||||
|
XX(22, SUBSCRIBE, SUBSCRIBE) \
|
||||||
|
XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \
|
||||||
|
/* RFC-5789 */ \
|
||||||
|
XX(24, PATCH, PATCH) \
|
||||||
|
XX(25, PURGE, PURGE) \
|
||||||
|
|
||||||
|
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_TRAILING = 1 << 3
|
||||||
|
, F_UPGRADE = 1 << 4
|
||||||
|
, F_SKIPBODY = 1 << 5
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* 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") \
|
||||||
|
\
|
||||||
|
/* 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(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 char type : 2; /* enum http_parser_type */
|
||||||
|
unsigned char flags : 6; /* F_* values from 'flags' enum; semi-public */
|
||||||
|
unsigned char state; /* enum state from http_parser.c */
|
||||||
|
unsigned char header_state; /* enum header_state from http_parser.c */
|
||||||
|
unsigned char index; /* index into current matcher */
|
||||||
|
|
||||||
|
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 short status_code; /* responses only */
|
||||||
|
unsigned char method; /* requests only */
|
||||||
|
unsigned char 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 char 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_header_field;
|
||||||
|
http_data_cb on_header_value;
|
||||||
|
http_cb on_headers_complete;
|
||||||
|
http_data_cb on_body;
|
||||||
|
http_cb on_message_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];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void http_parser_init(http_parser *parser, enum http_parser_type type);
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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
|
3402
src/http/qhttpserver/http-parser/test.c
Normal file
3402
src/http/qhttpserver/http-parser/test.c
Normal file
File diff suppressed because it is too large
Load Diff
44
src/http/qhttpserver/http-parser/url_parser.c
Normal file
44
src/http/qhttpserver/http-parser/url_parser.c
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include "http_parser.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
dump_url (const char *url, const struct http_parser_url *u)
|
||||||
|
{
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
printf("\tfield_set: 0x%x, port: %u\n", u->field_set, u->port);
|
||||||
|
for (i = 0; i < UF_MAX; i++) {
|
||||||
|
if ((u->field_set & (1 << i)) == 0) {
|
||||||
|
printf("\tfield_data[%u]: unset\n", i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("\tfield_data[%u]: off: %u len: %u part: \"%.*s\n",
|
||||||
|
i,
|
||||||
|
u->field_data[i].off,
|
||||||
|
u->field_data[i].len,
|
||||||
|
u->field_data[i].len,
|
||||||
|
url + u->field_data[i].off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
if (argc != 3) {
|
||||||
|
printf("Syntax : %s connect|get url\n", argv[0]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
struct http_parser_url u;
|
||||||
|
int len = strlen(argv[2]);
|
||||||
|
int connect = strcmp("connect", argv[1]) == 0 ? 1 : 0;
|
||||||
|
printf("Parsing %s, connect %d\n", argv[2], connect);
|
||||||
|
|
||||||
|
int result = http_parser_parse_url(argv[2], len, connect, &u);
|
||||||
|
if (result != 0) {
|
||||||
|
printf("Parse error : %d\n", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
printf("Parse ok, result : \n");
|
||||||
|
dump_url(argv[2], &u);
|
||||||
|
return 0;
|
||||||
|
}
|
202
src/http/qhttpserver/qhttpconnection.cpp
Normal file
202
src/http/qhttpserver/qhttpconnection.cpp
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "qhttpconnection.h"
|
||||||
|
|
||||||
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
#include <QtNetwork/QHostAddress>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#include "qhttprequest.h"
|
||||||
|
#include "qhttpresponse.h"
|
||||||
|
|
||||||
|
QHttpConnection::QHttpConnection(QTcpSocket *socket, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_socket(socket)
|
||||||
|
, m_parser(0)
|
||||||
|
, m_request(0)
|
||||||
|
{
|
||||||
|
qDebug() << "Got new connection" << socket->peerAddress() << socket->peerPort();
|
||||||
|
|
||||||
|
m_parser = (http_parser*)malloc(sizeof(http_parser));
|
||||||
|
http_parser_init(m_parser, HTTP_REQUEST);
|
||||||
|
|
||||||
|
m_parserSettings.on_message_begin = MessageBegin;
|
||||||
|
m_parserSettings.on_url = Url;
|
||||||
|
m_parserSettings.on_header_field = HeaderField;
|
||||||
|
m_parserSettings.on_header_value = HeaderValue;
|
||||||
|
m_parserSettings.on_headers_complete = HeadersComplete;
|
||||||
|
m_parserSettings.on_body = Body;
|
||||||
|
m_parserSettings.on_message_complete = MessageComplete;
|
||||||
|
|
||||||
|
m_parser->data = this;
|
||||||
|
|
||||||
|
connect(socket, SIGNAL(readyRead()), this, SLOT(parseRequest()));
|
||||||
|
connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
|
||||||
|
}
|
||||||
|
|
||||||
|
QHttpConnection::~QHttpConnection()
|
||||||
|
{
|
||||||
|
delete m_socket;
|
||||||
|
m_socket = 0;
|
||||||
|
|
||||||
|
free(m_parser);
|
||||||
|
m_parser = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpConnection::socketDisconnected()
|
||||||
|
{
|
||||||
|
if(m_request) {
|
||||||
|
if(m_request->successful()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_request->setSuccessful(false);
|
||||||
|
Q_EMIT m_request->end();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpConnection::parseRequest()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_parser);
|
||||||
|
|
||||||
|
while(m_socket->bytesAvailable())
|
||||||
|
{
|
||||||
|
QByteArray arr = m_socket->readAll();
|
||||||
|
http_parser_execute(m_parser, &m_parserSettings, arr.constData(), arr.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpConnection::write(const QByteArray &data)
|
||||||
|
{
|
||||||
|
m_socket->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpConnection::flush()
|
||||||
|
{
|
||||||
|
m_socket->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********************
|
||||||
|
* Static Callbacks *
|
||||||
|
*******************/
|
||||||
|
int QHttpConnection::MessageBegin(http_parser *parser)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
theConnection->m_currentHeaders.clear();
|
||||||
|
theConnection->m_request = new QHttpRequest(theConnection);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::HeadersComplete(http_parser *parser)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
/** set method **/
|
||||||
|
theConnection->m_request->setMethod(static_cast<QHttpRequest::HttpMethod>(parser->method));
|
||||||
|
|
||||||
|
/** set version **/
|
||||||
|
theConnection->m_request->setVersion(QString("%1.%2").arg(parser->http_major).arg(parser->http_minor));
|
||||||
|
|
||||||
|
// Insert last remaining header
|
||||||
|
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
|
||||||
|
theConnection->m_request->setHeaders(theConnection->m_currentHeaders);
|
||||||
|
|
||||||
|
/** set client information **/
|
||||||
|
theConnection->m_request->m_remoteAddress = theConnection->m_socket->peerAddress().toString();
|
||||||
|
theConnection->m_request->m_remotePort = theConnection->m_socket->peerPort();
|
||||||
|
|
||||||
|
QHttpResponse *response = new QHttpResponse(theConnection);
|
||||||
|
if( parser->http_major < 1 || parser->http_minor < 1 )
|
||||||
|
response->m_keepAlive = false;
|
||||||
|
|
||||||
|
connect(theConnection, SIGNAL(destroyed()), response, SLOT(connectionClosed()));
|
||||||
|
|
||||||
|
// we are good to go!
|
||||||
|
Q_EMIT theConnection->newRequest(theConnection->m_request, response);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::MessageComplete(http_parser *parser)
|
||||||
|
{
|
||||||
|
// TODO: do cleanup and prepare for next request
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
theConnection->m_request->setSuccessful(true);
|
||||||
|
Q_EMIT theConnection->m_request->end();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::Url(http_parser *parser, const char *at, size_t length)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
QString url = QString::fromAscii(at, length);
|
||||||
|
theConnection->m_request->setUrl(QUrl(url));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::HeaderField(http_parser *parser, const char *at, size_t length)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
// insert the header we parsed previously
|
||||||
|
// into the header map
|
||||||
|
if( !theConnection->m_currentHeaderField.isEmpty() && !theConnection->m_currentHeaderValue.isEmpty() )
|
||||||
|
{
|
||||||
|
// header names are always lower-cased
|
||||||
|
theConnection->m_currentHeaders[theConnection->m_currentHeaderField.toLower()] = theConnection->m_currentHeaderValue;
|
||||||
|
// clear header value. this sets up a nice
|
||||||
|
// feedback loop where the next time
|
||||||
|
// HeaderValue is called, it can simply append
|
||||||
|
theConnection->m_currentHeaderField = QString();
|
||||||
|
theConnection->m_currentHeaderValue = QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fieldSuffix = QString::fromAscii(at, length);
|
||||||
|
theConnection->m_currentHeaderField += fieldSuffix;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::HeaderValue(http_parser *parser, const char *at, size_t length)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
QString valueSuffix = QString::fromAscii(at, length);
|
||||||
|
theConnection->m_currentHeaderValue += valueSuffix;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QHttpConnection::Body(http_parser *parser, const char *at, size_t length)
|
||||||
|
{
|
||||||
|
QHttpConnection *theConnection = (QHttpConnection *)parser->data;
|
||||||
|
Q_ASSERT(theConnection->m_request);
|
||||||
|
|
||||||
|
Q_EMIT theConnection->m_request->data(QByteArray(at, length));
|
||||||
|
return 0;
|
||||||
|
}
|
80
src/http/qhttpserver/qhttpconnection.h
Normal file
80
src/http/qhttpserver/qhttpconnection.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Q_HTTP_CONNECTION
|
||||||
|
#define Q_HTTP_CONNECTION
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
|
||||||
|
#include "http_parser.h"
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class QHttpRequest;
|
||||||
|
class QHttpResponse;
|
||||||
|
|
||||||
|
typedef QHash<QString, QString> HeaderHash;
|
||||||
|
|
||||||
|
class QHttpConnection : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
QHttpConnection(QTcpSocket *socket, QObject *parent = 0);
|
||||||
|
virtual ~QHttpConnection();
|
||||||
|
|
||||||
|
void write(const QByteArray &data);
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void newRequest(QHttpRequest*, QHttpResponse*);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void parseRequest();
|
||||||
|
void socketDisconnected();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int MessageBegin(http_parser *parser);
|
||||||
|
static int Url(http_parser *parser, const char *at, size_t length);
|
||||||
|
static int HeaderField(http_parser *parser, const char *at, size_t length);
|
||||||
|
static int HeaderValue(http_parser *parser, const char *at, size_t length);
|
||||||
|
static int HeadersComplete(http_parser *parser);
|
||||||
|
static int Body(http_parser *parser, const char *at, size_t length);
|
||||||
|
static int MessageComplete(http_parser *parser);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTcpSocket *m_socket;
|
||||||
|
http_parser_settings m_parserSettings;
|
||||||
|
http_parser *m_parser;
|
||||||
|
|
||||||
|
// since there can only be one request at any time
|
||||||
|
// even with pipelining
|
||||||
|
QHttpRequest *m_request;
|
||||||
|
|
||||||
|
// the ones we are reading in from the parser
|
||||||
|
HeaderHash m_currentHeaders;
|
||||||
|
QString m_currentHeaderField;
|
||||||
|
QString m_currentHeaderValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
38
src/http/qhttpserver/qhttprequest.cpp
Normal file
38
src/http/qhttpserver/qhttprequest.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "qhttprequest.h"
|
||||||
|
|
||||||
|
#include "qhttpconnection.h"
|
||||||
|
|
||||||
|
QHttpRequest::QHttpRequest(QHttpConnection *connection, QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_connection(connection)
|
||||||
|
, m_url("http://localhost/")
|
||||||
|
, m_success(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QHttpRequest::~QHttpRequest()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
245
src/http/qhttpserver/qhttprequest.h
Normal file
245
src/http/qhttpserver/qhttprequest.h
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Q_HTTP_REQUEST
|
||||||
|
#define Q_HTTP_REQUEST
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QMetaEnum>
|
||||||
|
#include <QtCore/QMetaType>
|
||||||
|
#include <QtCore/QUrl>
|
||||||
|
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class QHttpConnection;
|
||||||
|
|
||||||
|
typedef QHash<QString, QString> HeaderHash;
|
||||||
|
|
||||||
|
/* Request Methods */
|
||||||
|
|
||||||
|
/*! \class QHttpRequest
|
||||||
|
*
|
||||||
|
* The QHttpRequest class represents the header and data
|
||||||
|
* sent by the client.
|
||||||
|
*
|
||||||
|
* Header data is available immediately.
|
||||||
|
*
|
||||||
|
* Body data is streamed as it comes in via the data(const QByteArray&) signal.
|
||||||
|
* As a consequence the application's request callback should ensure that it
|
||||||
|
* connects to the data() signal before control returns back to the event loop.
|
||||||
|
* Otherwise there is a risk of some data never being received by the
|
||||||
|
* application.
|
||||||
|
*
|
||||||
|
* The class is <strong>read-only</strong> by users of %QHttpServer.
|
||||||
|
*/
|
||||||
|
class QHttpRequest : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(HeaderHash headers READ headers);
|
||||||
|
Q_PROPERTY(QString remoteAddress READ remoteAddress);
|
||||||
|
Q_PROPERTY(quint16 remotePort READ remotePort);
|
||||||
|
Q_PROPERTY(QString method READ method);
|
||||||
|
Q_PROPERTY(QUrl url READ url);
|
||||||
|
Q_PROPERTY(QString path READ path);
|
||||||
|
Q_PROPERTY(QString httpVersion READ httpVersion);
|
||||||
|
Q_ENUMS(HttpMethod);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~QHttpRequest();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Request Methods
|
||||||
|
* Taken from http_parser.h -- make sure to keep synced
|
||||||
|
*/
|
||||||
|
enum HttpMethod {
|
||||||
|
HTTP_DELETE = 0,
|
||||||
|
HTTP_GET,
|
||||||
|
HTTP_HEAD,
|
||||||
|
HTTP_POST,
|
||||||
|
HTTP_PUT,
|
||||||
|
/* pathological */
|
||||||
|
HTTP_CONNECT,
|
||||||
|
HTTP_OPTIONS,
|
||||||
|
HTTP_TRACE,
|
||||||
|
/* webdav */
|
||||||
|
HTTP_COPY,
|
||||||
|
HTTP_LOCK,
|
||||||
|
HTTP_MKCOL,
|
||||||
|
HTTP_MOVE,
|
||||||
|
HTTP_PROPFIND,
|
||||||
|
HTTP_PROPPATCH,
|
||||||
|
HTTP_SEARCH,
|
||||||
|
HTTP_UNLOCK,
|
||||||
|
/* subversion */
|
||||||
|
HTTP_REPORT,
|
||||||
|
HTTP_MKACTIVITY,
|
||||||
|
HTTP_CHECKOUT,
|
||||||
|
HTTP_MERGE,
|
||||||
|
/* upnp */
|
||||||
|
HTTP_MSEARCH,
|
||||||
|
HTTP_NOTIFY,
|
||||||
|
HTTP_SUBSCRIBE,
|
||||||
|
HTTP_UNSUBSCRIBE,
|
||||||
|
/* RFC-5789 */
|
||||||
|
HTTP_PATCH,
|
||||||
|
HTTP_PURGE
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Returns the method string for the request
|
||||||
|
*/
|
||||||
|
const QString methodString() const { return MethodToString(method()); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The method used for the request.
|
||||||
|
*/
|
||||||
|
HttpMethod method() const { return m_method; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The complete URL for the request. This
|
||||||
|
* includes the path and query string.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const QUrl& url() const { return m_url; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The path portion of the query URL.
|
||||||
|
*
|
||||||
|
* \sa url()
|
||||||
|
*/
|
||||||
|
const QString path() const { return m_url.path(); };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The HTTP version used by the client as a
|
||||||
|
* 'x.x' string.
|
||||||
|
*/
|
||||||
|
const QString& httpVersion() const { return m_version; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Any query string included as part of a request.
|
||||||
|
* Usually used to send data in a GET request.
|
||||||
|
*/
|
||||||
|
const QString& queryString() const;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get a hash of the headers sent by the client.
|
||||||
|
* NOTE: All header names are <strong>lowercase</strong>
|
||||||
|
* so that Content-Length becomes content-length and so on.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
const HeaderHash& headers() const { return m_headers; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Get the value of a header
|
||||||
|
*
|
||||||
|
* \param field Name of the header field (lowercase).
|
||||||
|
* \return Value of the header or null QString()
|
||||||
|
*/
|
||||||
|
QString header(const QString &field) { return m_headers[field]; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* IP Address of the client in dotted decimal format
|
||||||
|
*/
|
||||||
|
const QString& remoteAddress() const { return m_remoteAddress; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Outbound connection port for the client.
|
||||||
|
*/
|
||||||
|
quint16 remotePort() const { return m_remotePort; };
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Post data
|
||||||
|
*/
|
||||||
|
const QByteArray &body() const { return m_body; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set immediately before end has been emitted,
|
||||||
|
* stating whether the message was properly received.
|
||||||
|
* Defaults to false untiil the message has completed.
|
||||||
|
*/
|
||||||
|
bool successful() const { return m_success; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* connect to data and store all data in a QByteArray
|
||||||
|
* accessible at body()
|
||||||
|
*/
|
||||||
|
void storeBody()
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(data(const QByteArray &)),
|
||||||
|
this, SLOT(appendBody(const QByteArray &)),
|
||||||
|
Qt::UniqueConnection);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*!
|
||||||
|
* This signal is emitted whenever body data is encountered
|
||||||
|
* in a message.
|
||||||
|
* This may be emitted zero or more times.
|
||||||
|
*/
|
||||||
|
void data(const QByteArray &);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Emitted at the end of the HTTP request.
|
||||||
|
* No data() signals will be emitted after this.
|
||||||
|
*/
|
||||||
|
void end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHttpRequest(QHttpConnection *connection, QObject *parent = 0);
|
||||||
|
|
||||||
|
static QString MethodToString(HttpMethod method)
|
||||||
|
{
|
||||||
|
int index = staticMetaObject.indexOfEnumerator("HttpMethod");
|
||||||
|
return staticMetaObject.enumerator(index).valueToKey(method);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMethod(HttpMethod method) { m_method = method; }
|
||||||
|
void setVersion(const QString &version) { m_version = version; }
|
||||||
|
void setUrl(const QUrl &url) { m_url = url; }
|
||||||
|
void setHeaders(const HeaderHash headers) { m_headers = headers; }
|
||||||
|
void setSuccessful(bool success) { m_success = success; }
|
||||||
|
|
||||||
|
QHttpConnection *m_connection;
|
||||||
|
HeaderHash m_headers;
|
||||||
|
HttpMethod m_method;
|
||||||
|
QUrl m_url;
|
||||||
|
QString m_version;
|
||||||
|
QString m_remoteAddress;
|
||||||
|
quint16 m_remotePort;
|
||||||
|
QByteArray m_body;
|
||||||
|
bool m_success;
|
||||||
|
|
||||||
|
friend class QHttpConnection;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void appendBody(const QByteArray &body)
|
||||||
|
{
|
||||||
|
m_body.append(body);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
193
src/http/qhttpserver/qhttpresponse.cpp
Normal file
193
src/http/qhttpserver/qhttpresponse.cpp
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "qhttpresponse.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
|
||||||
|
#include "qhttpserver.h"
|
||||||
|
#include "qhttpconnection.h"
|
||||||
|
|
||||||
|
QHttpResponse::QHttpResponse(QHttpConnection *connection)
|
||||||
|
// TODO: parent child relation
|
||||||
|
: QObject(0)
|
||||||
|
, m_connection(connection)
|
||||||
|
, m_headerWritten(false)
|
||||||
|
, m_sentConnectionHeader(false)
|
||||||
|
, m_sentContentLengthHeader(false)
|
||||||
|
, m_sentTransferEncodingHeader(false)
|
||||||
|
, m_sentDate(false)
|
||||||
|
, m_keepAlive(true)
|
||||||
|
, m_last(false)
|
||||||
|
, m_useChunkedEncoding(false)
|
||||||
|
, m_finished(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QHttpResponse::~QHttpResponse()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::setHeader(const QString &field, const QString &value)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_headers[field] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::writeHeader(const char *field, const QString &value)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connection->write(field);
|
||||||
|
m_connection->write(": ");
|
||||||
|
m_connection->write(value.toUtf8());
|
||||||
|
m_connection->write("\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::writeHeaders()
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Q_FOREACH(QString name, m_headers.keys())
|
||||||
|
{
|
||||||
|
QString value = m_headers[name];
|
||||||
|
if( name.compare("connection", Qt::CaseInsensitive) == 0 )
|
||||||
|
{
|
||||||
|
m_sentConnectionHeader = true;
|
||||||
|
if( value == "close" )
|
||||||
|
m_last = true;
|
||||||
|
else
|
||||||
|
m_keepAlive = true;
|
||||||
|
}
|
||||||
|
else if( name.compare("transfer-encoding", Qt::CaseInsensitive) == 0 )
|
||||||
|
{
|
||||||
|
m_sentTransferEncodingHeader = true;
|
||||||
|
if( value == "chunked" )
|
||||||
|
m_useChunkedEncoding = true;
|
||||||
|
}
|
||||||
|
else if( name.compare("content-length", Qt::CaseInsensitive) == 0 )
|
||||||
|
{
|
||||||
|
m_sentContentLengthHeader = true;
|
||||||
|
}
|
||||||
|
else if( name.compare("date", Qt::CaseInsensitive) == 0 )
|
||||||
|
{
|
||||||
|
m_sentDate = true;
|
||||||
|
}
|
||||||
|
//TODO: Expect case
|
||||||
|
|
||||||
|
writeHeader(name.toAscii(), value.toAscii());
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_sentConnectionHeader )
|
||||||
|
{
|
||||||
|
if( m_keepAlive &&
|
||||||
|
( m_sentContentLengthHeader || m_useChunkedEncoding ) )
|
||||||
|
{
|
||||||
|
writeHeader("Connection", "keep-alive");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_last = true;
|
||||||
|
writeHeader("Connection", "close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_sentContentLengthHeader && !m_sentTransferEncodingHeader )
|
||||||
|
{
|
||||||
|
if( m_useChunkedEncoding )
|
||||||
|
writeHeader("Transfer-Encoding", "chunked");
|
||||||
|
else
|
||||||
|
m_last = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_sentDate )
|
||||||
|
{
|
||||||
|
writeHeader("Date", QDateTime::currentDateTimeUtc().toString("ddd, dd MMM yyyy hh:mm:ss G'M'T"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::writeHead(int status)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_headerWritten ) return;
|
||||||
|
|
||||||
|
m_connection->write(QString("HTTP/1.1 %1 %2\r\n").arg(status).arg(STATUS_CODES[status]).toAscii());
|
||||||
|
|
||||||
|
writeHeaders();
|
||||||
|
|
||||||
|
m_connection->write("\r\n");
|
||||||
|
m_headerWritten = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::write(const QByteArray &data)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !m_headerWritten )
|
||||||
|
{
|
||||||
|
qDebug() << "You MUST call writeHead() before writing body data";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connection->write(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::write(const QString &data)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_connection->write(data.toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::end(const QString &data)
|
||||||
|
{
|
||||||
|
if(m_finished) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_finished = true;
|
||||||
|
|
||||||
|
write(data);
|
||||||
|
|
||||||
|
Q_EMIT done();
|
||||||
|
deleteLater();
|
||||||
|
// TODO: end connection and delete ourselves
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpResponse::connectionClosed()
|
||||||
|
{
|
||||||
|
m_finished = true;
|
||||||
|
deleteLater();
|
||||||
|
}
|
174
src/http/qhttpserver/qhttpresponse.h
Normal file
174
src/http/qhttpserver/qhttpresponse.h
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Q_HTTP_RESPONSE
|
||||||
|
#define Q_HTTP_RESPONSE
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
|
||||||
|
//
|
||||||
|
class QTcpSocket;
|
||||||
|
|
||||||
|
class QHttpConnection;
|
||||||
|
|
||||||
|
typedef QHash<QString, QString> HeaderHash;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* The QHttpResponse class handles sending
|
||||||
|
* data back to the client in response to a request.
|
||||||
|
*
|
||||||
|
* The way to respond is to:
|
||||||
|
* <ol>
|
||||||
|
* <li>Set headers (optional).</li>
|
||||||
|
* <li>Call writeHead() with the HTTP status code.</li>
|
||||||
|
* <li>Call write() zero or more times.</li>
|
||||||
|
* <li>Call end() when you are ready to end the request.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class QHttpResponse : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum StatusCode {
|
||||||
|
STATUS_CONTINUE = 100,
|
||||||
|
STATUS_SWITCH_PROTOCOLS = 101,
|
||||||
|
STATUS_OK = 200,
|
||||||
|
STATUS_CREATED = 201,
|
||||||
|
STATUS_ACCEPTED = 202,
|
||||||
|
STATUS_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
STATUS_NO_CONTENT = 204,
|
||||||
|
STATUS_RESET_CONTENT = 205,
|
||||||
|
STATUS_PARTIAL_CONTENT = 206,
|
||||||
|
STATUS_MULTIPLE_CHOICES = 300,
|
||||||
|
STATUS_MOVED_PERMANENTLY = 301,
|
||||||
|
STATUS_FOUND = 302,
|
||||||
|
STATUS_SEE_OTHER = 303,
|
||||||
|
STATUS_NOT_MODIFIED = 304,
|
||||||
|
STATUS_USE_PROXY = 305,
|
||||||
|
STATUS_TEMPORARY_REDIRECT = 307,
|
||||||
|
STATUS_BAD_REQUEST = 400,
|
||||||
|
STATUS_UNAUTHORIZED = 401,
|
||||||
|
STATUS_PAYMENT_REQUIRED = 402,
|
||||||
|
STATUS_FORBIDDEN = 403,
|
||||||
|
STATUS_NOT_FOUND = 404,
|
||||||
|
STATUS_METHOD_NOT_ALLOWED = 405,
|
||||||
|
STATUS_NOT_ACCEPTABLE = 406,
|
||||||
|
STATUS_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
STATUS_REQUEST_TIMEOUT = 408,
|
||||||
|
STATUS_CONFLICT = 409,
|
||||||
|
STATUS_GONE = 410,
|
||||||
|
STATUS_LENGTH_REQUIRED = 411,
|
||||||
|
STATUS_PRECONDITION_FAILED = 412,
|
||||||
|
STATUS_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||||
|
STATUS_REQUEST_URI_TOO_LONG = 414,
|
||||||
|
STATUS_REQUEST_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
STATUS_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
STATUS_EXPECTATION_FAILED = 417,
|
||||||
|
STATUS_INTERNAL_SERVER_ERROR = 500,
|
||||||
|
STATUS_NOT_IMPLEMENTED = 501,
|
||||||
|
STATUS_BAD_GATEWAY = 502,
|
||||||
|
STATUS_SERVICE_UNAVAILABLE = 503,
|
||||||
|
STATUS_GATEWAY_TIMEOUT = 504,
|
||||||
|
STATUS_HTTP_VERSION_NOT_SUPPORTED = 505
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual ~QHttpResponse();
|
||||||
|
|
||||||
|
public Q_SLOTS:
|
||||||
|
/*!
|
||||||
|
* Write the header of the response
|
||||||
|
* using @c status as the response status
|
||||||
|
* code. Any headers should be set before this
|
||||||
|
* is called.
|
||||||
|
*/
|
||||||
|
void writeHead(int status);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Write the block of data to the client.
|
||||||
|
*
|
||||||
|
* \note
|
||||||
|
* writeHead() has to be called before write(), otherwise the call will
|
||||||
|
* fail.
|
||||||
|
*/
|
||||||
|
void write(const QByteArray &data);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Write a QString instead of a QByteArray.
|
||||||
|
* \see write(const QByteArray &);
|
||||||
|
*/
|
||||||
|
void write(const QString &data);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* End the response. Data will be flushed
|
||||||
|
* to the underlying socket and the connection
|
||||||
|
* itself will be closed if this is the last
|
||||||
|
* response.
|
||||||
|
*
|
||||||
|
* This will emit done() and queue this object
|
||||||
|
* for deletion. For details see \ref memorymanagement
|
||||||
|
*/
|
||||||
|
void end(const QString &data=QString());
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set a response header @c field to @c value
|
||||||
|
*/
|
||||||
|
void setHeader(const QString &field, const QString &value);
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*!
|
||||||
|
* Emitted once the response is finished.
|
||||||
|
* You should NOT interact with this object
|
||||||
|
* after done() has been emitted as the object
|
||||||
|
* is scheduled for deletion at any time.
|
||||||
|
*/
|
||||||
|
void done();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QHttpResponse(QHttpConnection *connection);
|
||||||
|
|
||||||
|
void writeHeaders();
|
||||||
|
void writeHeader(const char *field, const QString &value);
|
||||||
|
|
||||||
|
QHttpConnection *m_connection;
|
||||||
|
|
||||||
|
bool m_headerWritten;
|
||||||
|
HeaderHash m_headers;
|
||||||
|
friend class QHttpConnection;
|
||||||
|
|
||||||
|
bool m_sentConnectionHeader;
|
||||||
|
bool m_sentContentLengthHeader;
|
||||||
|
bool m_sentTransferEncodingHeader;
|
||||||
|
bool m_sentDate;
|
||||||
|
bool m_keepAlive;
|
||||||
|
bool m_last;
|
||||||
|
bool m_useChunkedEncoding;
|
||||||
|
bool m_finished;
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void connectionClosed();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
125
src/http/qhttpserver/qhttpserver.cpp
Normal file
125
src/http/qhttpserver/qhttpserver.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 "qhttpserver.h"
|
||||||
|
|
||||||
|
#include <QtNetwork/QTcpServer>
|
||||||
|
#include <QtNetwork/QTcpSocket>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#include "qhttpconnection.h"
|
||||||
|
|
||||||
|
QHash<int, QString> STATUS_CODES;
|
||||||
|
|
||||||
|
QHttpServer::QHttpServer(QObject *parent)
|
||||||
|
: QObject(parent)
|
||||||
|
, m_tcpServer(0)
|
||||||
|
{
|
||||||
|
#define STATUS_CODE(num, reason) STATUS_CODES.insert(num, reason);
|
||||||
|
// {{{
|
||||||
|
STATUS_CODE(100, "Continue")
|
||||||
|
STATUS_CODE(101, "Switching Protocols")
|
||||||
|
STATUS_CODE(102, "Processing") // RFC 2518) obsoleted by RFC 4918
|
||||||
|
STATUS_CODE(200, "OK")
|
||||||
|
STATUS_CODE(201, "Created")
|
||||||
|
STATUS_CODE(202, "Accepted")
|
||||||
|
STATUS_CODE(203, "Non-Authoritative Information")
|
||||||
|
STATUS_CODE(204, "No Content")
|
||||||
|
STATUS_CODE(205, "Reset Content")
|
||||||
|
STATUS_CODE(206, "Partial Content")
|
||||||
|
STATUS_CODE(207, "Multi-Status") // RFC 4918
|
||||||
|
STATUS_CODE(300, "Multiple Choices")
|
||||||
|
STATUS_CODE(301, "Moved Permanently")
|
||||||
|
STATUS_CODE(302, "Moved Temporarily")
|
||||||
|
STATUS_CODE(303, "See Other")
|
||||||
|
STATUS_CODE(304, "Not Modified")
|
||||||
|
STATUS_CODE(305, "Use Proxy")
|
||||||
|
STATUS_CODE(307, "Temporary Redirect")
|
||||||
|
STATUS_CODE(400, "Bad Request")
|
||||||
|
STATUS_CODE(401, "Unauthorized")
|
||||||
|
STATUS_CODE(402, "Payment Required")
|
||||||
|
STATUS_CODE(403, "Forbidden")
|
||||||
|
STATUS_CODE(404, "Not Found")
|
||||||
|
STATUS_CODE(405, "Method Not Allowed")
|
||||||
|
STATUS_CODE(406, "Not Acceptable")
|
||||||
|
STATUS_CODE(407, "Proxy Authentication Required")
|
||||||
|
STATUS_CODE(408, "Request Time-out")
|
||||||
|
STATUS_CODE(409, "Conflict")
|
||||||
|
STATUS_CODE(410, "Gone")
|
||||||
|
STATUS_CODE(411, "Length Required")
|
||||||
|
STATUS_CODE(412, "Precondition Failed")
|
||||||
|
STATUS_CODE(413, "Request Entity Too Large")
|
||||||
|
STATUS_CODE(414, "Request-URI Too Large")
|
||||||
|
STATUS_CODE(415, "Unsupported Media Type")
|
||||||
|
STATUS_CODE(416, "Requested Range Not Satisfiable")
|
||||||
|
STATUS_CODE(417, "Expectation Failed")
|
||||||
|
STATUS_CODE(418, "I\"m a teapot") // RFC 2324
|
||||||
|
STATUS_CODE(422, "Unprocessable Entity") // RFC 4918
|
||||||
|
STATUS_CODE(423, "Locked") // RFC 4918
|
||||||
|
STATUS_CODE(424, "Failed Dependency") // RFC 4918
|
||||||
|
STATUS_CODE(425, "Unordered Collection") // RFC 4918
|
||||||
|
STATUS_CODE(426, "Upgrade Required") // RFC 2817
|
||||||
|
STATUS_CODE(500, "Internal Server Error")
|
||||||
|
STATUS_CODE(501, "Not Implemented")
|
||||||
|
STATUS_CODE(502, "Bad Gateway")
|
||||||
|
STATUS_CODE(503, "Service Unavailable")
|
||||||
|
STATUS_CODE(504, "Gateway Time-out")
|
||||||
|
STATUS_CODE(505, "HTTP Version not supported")
|
||||||
|
STATUS_CODE(506, "Variant Also Negotiates") // RFC 2295
|
||||||
|
STATUS_CODE(507, "Insufficient Storage") // RFC 4918
|
||||||
|
STATUS_CODE(509, "Bandwidth Limit Exceeded")
|
||||||
|
STATUS_CODE(510, "Not Extended") // RFC 2774
|
||||||
|
// }}}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHttpServer::~QHttpServer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpServer::newConnection()
|
||||||
|
{
|
||||||
|
Q_ASSERT(m_tcpServer);
|
||||||
|
while(m_tcpServer->hasPendingConnections()) {
|
||||||
|
QHttpConnection *connection = new QHttpConnection(m_tcpServer->nextPendingConnection(), this);
|
||||||
|
connect(connection, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)),
|
||||||
|
this, SIGNAL(newRequest(QHttpRequest*, QHttpResponse*)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QHttpServer::listen(const QHostAddress &address, quint16 port)
|
||||||
|
{
|
||||||
|
m_tcpServer = new QTcpServer;
|
||||||
|
|
||||||
|
connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(newConnection()));
|
||||||
|
return m_tcpServer->listen(address, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QHttpServer::listen(quint16 port)
|
||||||
|
{
|
||||||
|
return listen(QHostAddress::Any, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QHttpServer::close()
|
||||||
|
{
|
||||||
|
m_tcpServer->close();
|
||||||
|
}
|
230
src/http/qhttpserver/qhttpserver.h
Normal file
230
src/http/qhttpserver/qhttpserver.h
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 Nikhil Marathe <nsm.nikhil@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 Q_HTTP_SERVER
|
||||||
|
#define Q_HTTP_SERVER
|
||||||
|
|
||||||
|
#define QHTTPSERVER_VERSION_MAJOR 0
|
||||||
|
#define QHTTPSERVER_VERSION_MINOR 1
|
||||||
|
#define QHTTPSERVER_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtNetwork/QHostAddress>
|
||||||
|
|
||||||
|
class QTcpServer;
|
||||||
|
|
||||||
|
class QHttpRequest;
|
||||||
|
class QHttpResponse;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* A map of request or response headers
|
||||||
|
*/
|
||||||
|
typedef QHash<QString, QString> HeaderHash;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Maps status codes to string reason phrases
|
||||||
|
*/
|
||||||
|
extern QHash<int, QString> STATUS_CODES;
|
||||||
|
|
||||||
|
/*! \mainpage %QHttpServer Documentation
|
||||||
|
*
|
||||||
|
* \section introduction Introduction
|
||||||
|
*
|
||||||
|
* %QHttpServer is a easy to use, fast and light-weight
|
||||||
|
* HTTP Server suitable for C++ web applications backed
|
||||||
|
* by Qt. Since C++ web applications are pretty uncommon
|
||||||
|
* the market for this project is pretty low.
|
||||||
|
*
|
||||||
|
* But integrating this with a module like QtScript
|
||||||
|
* and using it to write JavaScript web applications is
|
||||||
|
* a tempting possibility, and something that I want to
|
||||||
|
* demonstrate at <a href="http://conf.kde.in">conf.kde.in 2011</a>.
|
||||||
|
*
|
||||||
|
* %QHttpServer uses a signal-slots based mechanism
|
||||||
|
* for all communication, so no inheritance is required.
|
||||||
|
* It tries to be as asynchronous as possible, to the
|
||||||
|
* extent that request body data is also delivered as and
|
||||||
|
* when it is received over the socket via signals. This
|
||||||
|
* kind of programming may take some getting used to.
|
||||||
|
*
|
||||||
|
* %QHttpServer is backed by <a href="http://github.com/ry/http-parser">Ryan
|
||||||
|
* Dahl's secure and fast http parser</a> which makes it streaming
|
||||||
|
* till the lowest level.
|
||||||
|
*
|
||||||
|
* \section usage Usage
|
||||||
|
*
|
||||||
|
* Using %QHttpServer is very simple. Simply create a QHttpServer,
|
||||||
|
* connect a slot to the newRequest() signal and use the request and
|
||||||
|
* response objects.
|
||||||
|
* See the QHttpServer class documentation for an example.
|
||||||
|
*
|
||||||
|
* \example helloworld/helloworld.cpp
|
||||||
|
* \example helloworld/helloworld.h
|
||||||
|
* \example greeting/greeting.cpp
|
||||||
|
* \example greeting/greeting.h
|
||||||
|
* \example bodydata/bodydata.cpp
|
||||||
|
* \example bodydata/bodydata.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \class QHttpServer
|
||||||
|
* The QHttpServer class forms the basis of the %QHttpServer
|
||||||
|
* project. It is a fast, non-blocking HTTP server.
|
||||||
|
*
|
||||||
|
* These are the steps to create a server and respond to requests.
|
||||||
|
*
|
||||||
|
* <ol>
|
||||||
|
* <li>Create an instance of QHttpServer.</li>
|
||||||
|
* <li>Connect a slot to the newRequest(QHttpRequest*, QHttpResponse*)
|
||||||
|
* signal.</li>
|
||||||
|
* <li>Create a QCoreApplication to drive the server event loop.</li>
|
||||||
|
* <li>Respond to clients by writing out to the QHttpResponse object.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* helloworld.cpp
|
||||||
|
* \include helloworld/helloworld.cpp
|
||||||
|
* helloworld.h
|
||||||
|
* \include helloworld/helloworld.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class QHttpServer : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
* Create a new HTTP Server
|
||||||
|
*/
|
||||||
|
QHttpServer(QObject *parent = 0);
|
||||||
|
virtual ~QHttpServer();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Start the server bound to the @c address and @c port.
|
||||||
|
* This function returns immediately!
|
||||||
|
*
|
||||||
|
* \param address Address on which to listen to. Default is to listen on
|
||||||
|
* all interfaces which means the server can be accessed from anywhere.
|
||||||
|
* \param port Port number on which the server should run.
|
||||||
|
* \return true if the server was started successfully, false otherwise.
|
||||||
|
* \sa listen(quint16)
|
||||||
|
*/
|
||||||
|
bool listen(const QHostAddress &address = QHostAddress::Any, quint16 port=0);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Starts the server on @c port listening on all interfaces.
|
||||||
|
*
|
||||||
|
* \param port Port number on which the server should run.
|
||||||
|
* \return true if the server was started successfully, false otherwise.
|
||||||
|
* \sa listen(const QHostAddress&, quint16)
|
||||||
|
*/
|
||||||
|
bool listen(quint16 port);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Stop listening for connections
|
||||||
|
*/
|
||||||
|
void close();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/*!
|
||||||
|
* This signal is emitted whenever a client
|
||||||
|
* makes a new request to the server.
|
||||||
|
*
|
||||||
|
* The slot should use the @c request and @c response
|
||||||
|
* objects to communicate with the client.
|
||||||
|
*
|
||||||
|
* \section memorymanagement Memory Management
|
||||||
|
*
|
||||||
|
* The QHttpRequest and QHttpResponse deletion policies
|
||||||
|
* are such.
|
||||||
|
*
|
||||||
|
* QHttpRequest is <strong>never</strong> deleted by %QHttpServer.
|
||||||
|
* Since it is not possible to determine till what point the application
|
||||||
|
* may want access to its data, it is up to the application to delete it.
|
||||||
|
* A recommended way to handle this is to create a new responder object for
|
||||||
|
* every request and to delete the request in that object's destructor. The
|
||||||
|
* object itself can be deleted by connecting to QHttpResponse's done()
|
||||||
|
* slot as explained below.
|
||||||
|
*
|
||||||
|
* You should <strong>NOT</strong> delete the QHttpRequest object until it
|
||||||
|
* has emitted an QHttpRequest::end() signal.
|
||||||
|
*
|
||||||
|
* QHttpResponse queues itself up for auto-deletion once the application
|
||||||
|
* calls its end() method. Once the data has been flushed to the underlying
|
||||||
|
* socket, the object will emit a QHttpResponse::done() signal before queueing itself up
|
||||||
|
* for deletion. You should <strong>NOT</strong> interact with the response
|
||||||
|
* object once it has emitted QHttpResponse::done() although actual deletion does not
|
||||||
|
* happen until QHttpResponse::destroyed() is emitted.
|
||||||
|
* QHttpResponse::done() serves as a useful way to handle memory management of the
|
||||||
|
* application itself. For example:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* MyApp::MyApp()
|
||||||
|
* : QObject(0)
|
||||||
|
* {
|
||||||
|
* QHttpServer *s = new QHttpServer;
|
||||||
|
* connect(s, SIGNAL(newRequest(...)), this, SLOT(handle(...)));
|
||||||
|
* s.listen(8000);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void MyApp::handle(QHttpRequest *request, QHttpResponse *response)
|
||||||
|
* {
|
||||||
|
* if( request->url() matches a route )
|
||||||
|
* new Responder(request, response);
|
||||||
|
* else
|
||||||
|
* new PageNotFound(request, response);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* Responder::Responder(QHttpRequest *request, QHttpResponse *response)
|
||||||
|
* {
|
||||||
|
* m_request = request;
|
||||||
|
*
|
||||||
|
* connect(request, SIGNAL(end()), response, SLOT(end()));
|
||||||
|
* // Once the request is complete, the response is ended.
|
||||||
|
* // when the response ends, it deletes itself
|
||||||
|
* // the Responder object connects to done()
|
||||||
|
* // which will lead to it being deleted
|
||||||
|
* // and this will delete the request.
|
||||||
|
* // So all 3 are properly deleted.
|
||||||
|
* connect(response, SIGNAL(done()), this, SLOT(deleteLater()));
|
||||||
|
* response->writeHead(200);
|
||||||
|
* response->write("Quitting soon");
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Responder::~Responder()
|
||||||
|
* {
|
||||||
|
* delete m_request;
|
||||||
|
* m_request = 0;
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void newRequest(QHttpRequest *request, QHttpResponse *response);
|
||||||
|
|
||||||
|
private Q_SLOTS:
|
||||||
|
void newConnection();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTcpServer *m_tcpServer;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
28
src/http/qjson/CMakeLists.txt
Normal file
28
src/http/qjson/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# add_custom_command (OUTPUT ${qjson_SOURCE_DIR}/lib/json_parser.cc
|
||||||
|
# PRE_BUILD
|
||||||
|
# COMMAND bison -t -o json_parser.cc -d json_parser.yy
|
||||||
|
# DEPENDS json_parser.yy
|
||||||
|
# WORKING_DIRECTORY ${qjson_SOURCE_DIR}/lib/
|
||||||
|
# )
|
||||||
|
|
||||||
|
# To regenerate json_scanner.cc use:
|
||||||
|
# flex json_scanner.yy
|
||||||
|
|
||||||
|
set(qjson_MOC_HDRS
|
||||||
|
parserrunnable.h
|
||||||
|
serializerrunnable.h
|
||||||
|
)
|
||||||
|
|
||||||
|
IF (NOT Qt5Core_FOUND)
|
||||||
|
qt4_wrap_cpp(qjson_MOC_SRCS ${qjson_MOC_HDRS})
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
set (qjson_SRCS parser.cpp qobjecthelper.cpp json_scanner.cpp json_parser.cc parserrunnable.cpp serializer.cpp serializerrunnable.cpp)
|
||||||
|
set (qjson_HEADERS parser.h parserrunnable.h qobjecthelper.h serializer.h serializerrunnable.h qjson_export.h)
|
||||||
|
|
||||||
|
# Required to use the intree copy of FlexLexer.h
|
||||||
|
INCLUDE_DIRECTORIES(.)
|
||||||
|
|
||||||
|
add_gcc_compiler_cxxflags("-fexceptions")
|
||||||
|
remove_definitions(-DQT_NO_KEYWORDS)
|
||||||
|
add_library (qjson STATIC ${qjson_SRCS} ${qjson_MOC_SRCS} ${qjson_HEADERS})
|
206
src/http/qjson/FlexLexer.h
Normal file
206
src/http/qjson/FlexLexer.h
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
// -*-C++-*-
|
||||||
|
// FlexLexer.h -- define interfaces for lexical analyzer classes generated
|
||||||
|
// by flex
|
||||||
|
|
||||||
|
// Copyright (c) 1993 The Regents of the University of California.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// This code is derived from software contributed to Berkeley by
|
||||||
|
// Kent Williams and Tom Epperly.
|
||||||
|
//
|
||||||
|
// Redistribution and use in source and binary forms, with or without
|
||||||
|
// modification, are permitted provided that the following conditions
|
||||||
|
// are met:
|
||||||
|
|
||||||
|
// 1. Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
// Neither the name of the University nor the names of its contributors
|
||||||
|
// may be used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
|
||||||
|
// THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
||||||
|
// IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
||||||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
// PURPOSE.
|
||||||
|
|
||||||
|
// This file defines FlexLexer, an abstract class which specifies the
|
||||||
|
// external interface provided to flex C++ lexer objects, and yyFlexLexer,
|
||||||
|
// which defines a particular lexer class.
|
||||||
|
//
|
||||||
|
// If you want to create multiple lexer classes, you use the -P flag
|
||||||
|
// to rename each yyFlexLexer to some other xxFlexLexer. You then
|
||||||
|
// include <FlexLexer.h> in your other sources once per lexer class:
|
||||||
|
//
|
||||||
|
// #undef yyFlexLexer
|
||||||
|
// #define yyFlexLexer xxFlexLexer
|
||||||
|
// #include <FlexLexer.h>
|
||||||
|
//
|
||||||
|
// #undef yyFlexLexer
|
||||||
|
// #define yyFlexLexer zzFlexLexer
|
||||||
|
// #include <FlexLexer.h>
|
||||||
|
// ...
|
||||||
|
|
||||||
|
#ifndef __FLEX_LEXER_H
|
||||||
|
// Never included before - need to define base class.
|
||||||
|
#define __FLEX_LEXER_H
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
# ifndef FLEX_STD
|
||||||
|
# define FLEX_STD std::
|
||||||
|
# endif
|
||||||
|
|
||||||
|
extern "C++" {
|
||||||
|
|
||||||
|
struct yy_buffer_state;
|
||||||
|
typedef int yy_state_type;
|
||||||
|
|
||||||
|
class FlexLexer {
|
||||||
|
public:
|
||||||
|
virtual ~FlexLexer() { }
|
||||||
|
|
||||||
|
const char* YYText() const { return yytext; }
|
||||||
|
int YYLeng() const { return yyleng; }
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
yy_switch_to_buffer( struct yy_buffer_state* new_buffer ) = 0;
|
||||||
|
virtual struct yy_buffer_state*
|
||||||
|
yy_create_buffer( FLEX_STD istream* s, int size ) = 0;
|
||||||
|
virtual void yy_delete_buffer( struct yy_buffer_state* b ) = 0;
|
||||||
|
virtual void yyrestart( FLEX_STD istream* s ) = 0;
|
||||||
|
|
||||||
|
virtual int yylex() = 0;
|
||||||
|
|
||||||
|
// Call yylex with new input/output sources.
|
||||||
|
int yylex( FLEX_STD istream* new_in, FLEX_STD ostream* new_out = 0 )
|
||||||
|
{
|
||||||
|
switch_streams( new_in, new_out );
|
||||||
|
return yylex();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to new input/output streams. A nil stream pointer
|
||||||
|
// indicates "keep the current one".
|
||||||
|
virtual void switch_streams( FLEX_STD istream* new_in = 0,
|
||||||
|
FLEX_STD ostream* new_out = 0 ) = 0;
|
||||||
|
|
||||||
|
int lineno() const { return yylineno; }
|
||||||
|
|
||||||
|
int debug() const { return yy_flex_debug; }
|
||||||
|
void set_debug( int flag ) { yy_flex_debug = flag; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
char* yytext;
|
||||||
|
int yyleng;
|
||||||
|
int yylineno; // only maintained if you use %option yylineno
|
||||||
|
int yy_flex_debug; // only has effect with -d or "%option debug"
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
#endif // FLEXLEXER_H
|
||||||
|
|
||||||
|
#if defined(yyFlexLexer) || ! defined(yyFlexLexerOnce)
|
||||||
|
// Either this is the first time through (yyFlexLexerOnce not defined),
|
||||||
|
// or this is a repeated include to define a different flavor of
|
||||||
|
// yyFlexLexer, as discussed in the flex manual.
|
||||||
|
#define yyFlexLexerOnce
|
||||||
|
|
||||||
|
extern "C++" {
|
||||||
|
|
||||||
|
class yyFlexLexer : public FlexLexer {
|
||||||
|
public:
|
||||||
|
// arg_yyin and arg_yyout default to the cin and cout, but we
|
||||||
|
// only make that assignment when initializing in yylex().
|
||||||
|
yyFlexLexer( FLEX_STD istream* arg_yyin = 0, FLEX_STD ostream* arg_yyout = 0 );
|
||||||
|
|
||||||
|
virtual ~yyFlexLexer();
|
||||||
|
|
||||||
|
void yy_switch_to_buffer( struct yy_buffer_state* new_buffer );
|
||||||
|
struct yy_buffer_state* yy_create_buffer( FLEX_STD istream* s, int size );
|
||||||
|
void yy_delete_buffer( struct yy_buffer_state* b );
|
||||||
|
void yyrestart( FLEX_STD istream* s );
|
||||||
|
|
||||||
|
void yypush_buffer_state( struct yy_buffer_state* new_buffer );
|
||||||
|
void yypop_buffer_state();
|
||||||
|
|
||||||
|
virtual int yylex();
|
||||||
|
virtual void switch_streams( FLEX_STD istream* new_in, FLEX_STD ostream* new_out = 0 );
|
||||||
|
virtual int yywrap();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int LexerInput( char* buf, int max_size );
|
||||||
|
virtual void LexerOutput( const char* buf, int size );
|
||||||
|
virtual void LexerError( const char* msg );
|
||||||
|
|
||||||
|
void yyunput( int c, char* buf_ptr );
|
||||||
|
int yyinput();
|
||||||
|
|
||||||
|
void yy_load_buffer_state();
|
||||||
|
void yy_init_buffer( struct yy_buffer_state* b, FLEX_STD istream* s );
|
||||||
|
void yy_flush_buffer( struct yy_buffer_state* b );
|
||||||
|
|
||||||
|
int yy_start_stack_ptr;
|
||||||
|
int yy_start_stack_depth;
|
||||||
|
int* yy_start_stack;
|
||||||
|
|
||||||
|
void yy_push_state( int new_state );
|
||||||
|
void yy_pop_state();
|
||||||
|
int yy_top_state();
|
||||||
|
|
||||||
|
yy_state_type yy_get_previous_state();
|
||||||
|
yy_state_type yy_try_NUL_trans( yy_state_type current_state );
|
||||||
|
int yy_get_next_buffer();
|
||||||
|
|
||||||
|
FLEX_STD istream* yyin; // input source for default LexerInput
|
||||||
|
FLEX_STD ostream* yyout; // output sink for default LexerOutput
|
||||||
|
|
||||||
|
// yy_hold_char holds the character lost when yytext is formed.
|
||||||
|
char yy_hold_char;
|
||||||
|
|
||||||
|
// Number of characters read into yy_ch_buf.
|
||||||
|
int yy_n_chars;
|
||||||
|
|
||||||
|
// Points to current character in buffer.
|
||||||
|
char* yy_c_buf_p;
|
||||||
|
|
||||||
|
int yy_init; // whether we need to initialize
|
||||||
|
int yy_start; // start state number
|
||||||
|
|
||||||
|
// Flag which is used to allow yywrap()'s to do buffer switches
|
||||||
|
// instead of setting up a fresh yyin. A bit of a hack ...
|
||||||
|
int yy_did_buffer_switch_on_eof;
|
||||||
|
|
||||||
|
|
||||||
|
size_t yy_buffer_stack_top; /**< index of top of stack. */
|
||||||
|
size_t yy_buffer_stack_max; /**< capacity of stack. */
|
||||||
|
struct yy_buffer_state ** yy_buffer_stack; /**< Stack as an array. */
|
||||||
|
void yyensure_buffer_stack(void);
|
||||||
|
|
||||||
|
// The following are not always needed, but may be depending
|
||||||
|
// on use of certain flex features (like REJECT or yymore()).
|
||||||
|
|
||||||
|
yy_state_type yy_last_accepting_state;
|
||||||
|
char* yy_last_accepting_cpos;
|
||||||
|
|
||||||
|
yy_state_type* yy_state_buf;
|
||||||
|
yy_state_type* yy_state_ptr;
|
||||||
|
|
||||||
|
char* yy_full_match;
|
||||||
|
int* yy_full_state;
|
||||||
|
int yy_full_lp;
|
||||||
|
|
||||||
|
int yy_lp;
|
||||||
|
int yy_looking_for_trail_begin;
|
||||||
|
|
||||||
|
int yy_more_flag;
|
||||||
|
int yy_more_len;
|
||||||
|
int yy_more_offset;
|
||||||
|
int yy_prev_more_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // yyFlexLexer || ! yyFlexLexerOnce
|
||||||
|
|
89
src/http/qjson/README.license
Normal file
89
src/http/qjson/README.license
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
Qjson version xxxx, Date
|
||||||
|
|
||||||
|
The following files are licensed under LGPL V2.1:
|
||||||
|
------------------------------------------------
|
||||||
|
src/json_parser.yy
|
||||||
|
src/json_scanner.cpp
|
||||||
|
src/json_scanner.h
|
||||||
|
src/parser.cpp
|
||||||
|
src/parser.h
|
||||||
|
src/parser_p.h
|
||||||
|
src/parserrunnable.cpp
|
||||||
|
src/parserrunnable.h
|
||||||
|
src/qjson_debug.h
|
||||||
|
src/qjson_export.h
|
||||||
|
src/qobjecthelper.cpp
|
||||||
|
src/serializer.cpp
|
||||||
|
src/qobjecthelper.h
|
||||||
|
src/serializer.h
|
||||||
|
src/serializerrunnable.cpp
|
||||||
|
src/serializerrunnable.h
|
||||||
|
tests/cmdline_tester/cmdline_tester.cpp
|
||||||
|
tests/cmdline_tester/cmdlineparser.cpp
|
||||||
|
tests/cmdline_tester/cmdlineparser.h
|
||||||
|
tests/parser/testparser.cpp
|
||||||
|
tests/qobjecthelper/person.h
|
||||||
|
tests/qobjecthelper/testqobjecthelper.cpp
|
||||||
|
tests/serializer/testserializer.cpp
|
||||||
|
|
||||||
|
|
||||||
|
The following files are licensed under GPL V2 with Bison Exception:
|
||||||
|
--------------------------------------------------------------------
|
||||||
|
/src/json_parser.cc
|
||||||
|
/src/stack.hh
|
||||||
|
/src/location.hh
|
||||||
|
/src/position.hh
|
||||||
|
/src/json_parser.hh
|
||||||
|
|
||||||
|
|
||||||
|
Copyrights:
|
||||||
|
----------
|
||||||
|
Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||||
|
Copyright (C) 2009 Flavio Castelli <flavio@castelli.name> 2009 Frank Osterfeld <osterfeld@kde.org>
|
||||||
|
Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
Copyright (C) 2009 Till Adam <adam@kde.org>
|
||||||
|
Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
|
||||||
|
Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
|
||||||
|
Copyright (C) 2009 Pino Toscano <pino@kde.org>
|
||||||
|
Copyright (C) 2010 Flavio Castelli <flavio@castelli.name>
|
||||||
|
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999:
|
||||||
|
-------------------------------------------------------------
|
||||||
|
|
||||||
|
Checkout COPYING.lib
|
||||||
|
|
||||||
|
|
||||||
|
GPL V2 with Bison Exception:
|
||||||
|
----------------------------
|
||||||
|
Copyright (C) 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
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)
|
||||||
|
any later version.
|
||||||
|
|
||||||
|
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, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison.
|
||||||
|
|
1103
src/http/qjson/json_parser.cc
Normal file
1103
src/http/qjson/json_parser.cc
Normal file
File diff suppressed because it is too large
Load Diff
300
src/http/qjson/json_parser.hh
Normal file
300
src/http/qjson/json_parser.hh
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
/* A Bison parser, made by GNU Bison 2.7. */
|
||||||
|
|
||||||
|
/* Skeleton interface for Bison LALR(1) parsers in C++
|
||||||
|
|
||||||
|
Copyright (C) 2002-2012 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \file json_parser.hh
|
||||||
|
** Define the yy::parser class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* C++ LALR(1) parser skeleton written by Akim Demaille. */
|
||||||
|
|
||||||
|
#ifndef YY_YY_JSON_PARSER_HH_INCLUDED
|
||||||
|
# define YY_YY_JSON_PARSER_HH_INCLUDED
|
||||||
|
|
||||||
|
/* "%code requires" blocks. */
|
||||||
|
/* Line 33 of lalr1.cc */
|
||||||
|
#line 26 "json_parser.yy"
|
||||||
|
|
||||||
|
#include "parser_p.h"
|
||||||
|
#include "json_scanner.h"
|
||||||
|
#include "qjson_debug.h"
|
||||||
|
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
class JSonScanner;
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
class Parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define YYERROR_VERBOSE 1
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QVector<QVariant>*)
|
||||||
|
Q_DECLARE_METATYPE(QVariantMap*)
|
||||||
|
|
||||||
|
|
||||||
|
/* Line 33 of lalr1.cc */
|
||||||
|
#line 72 "json_parser.hh"
|
||||||
|
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include "stack.hh"
|
||||||
|
#include "location.hh"
|
||||||
|
|
||||||
|
/* Enabling traces. */
|
||||||
|
#ifndef YYDEBUG
|
||||||
|
# define YYDEBUG 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
/* Line 33 of lalr1.cc */
|
||||||
|
#line 88 "json_parser.hh"
|
||||||
|
|
||||||
|
/// A Bison parser.
|
||||||
|
class json_parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Symbol semantic values.
|
||||||
|
#ifndef YYSTYPE
|
||||||
|
typedef int semantic_type;
|
||||||
|
#else
|
||||||
|
typedef YYSTYPE semantic_type;
|
||||||
|
#endif
|
||||||
|
/// Symbol locations.
|
||||||
|
typedef location location_type;
|
||||||
|
/// Tokens.
|
||||||
|
struct token
|
||||||
|
{
|
||||||
|
/* Tokens. */
|
||||||
|
enum yytokentype {
|
||||||
|
END = 0,
|
||||||
|
CURLY_BRACKET_OPEN = 1,
|
||||||
|
CURLY_BRACKET_CLOSE = 2,
|
||||||
|
SQUARE_BRACKET_OPEN = 3,
|
||||||
|
SQUARE_BRACKET_CLOSE = 4,
|
||||||
|
COLON = 5,
|
||||||
|
COMMA = 6,
|
||||||
|
NUMBER = 7,
|
||||||
|
TRUE_VAL = 8,
|
||||||
|
FALSE_VAL = 9,
|
||||||
|
NULL_VAL = 10,
|
||||||
|
STRING = 11,
|
||||||
|
INVALID = 12
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
/// Token type.
|
||||||
|
typedef token::yytokentype token_type;
|
||||||
|
|
||||||
|
/// Build a parser object.
|
||||||
|
json_parser (QJson::ParserPrivate* driver_yyarg);
|
||||||
|
virtual ~json_parser ();
|
||||||
|
|
||||||
|
/// Parse.
|
||||||
|
/// \returns 0 iff parsing succeeded.
|
||||||
|
virtual int parse ();
|
||||||
|
|
||||||
|
#if YYDEBUG
|
||||||
|
/// The current debugging stream.
|
||||||
|
std::ostream& debug_stream () const;
|
||||||
|
/// Set the current debugging stream.
|
||||||
|
void set_debug_stream (std::ostream &);
|
||||||
|
|
||||||
|
/// Type for debugging levels.
|
||||||
|
typedef int debug_level_type;
|
||||||
|
/// The current debugging level.
|
||||||
|
debug_level_type debug_level () const;
|
||||||
|
/// Set the current debugging level.
|
||||||
|
void set_debug_level (debug_level_type l);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Report a syntax error.
|
||||||
|
/// \param loc where the syntax error is found.
|
||||||
|
/// \param msg a description of the syntax error.
|
||||||
|
virtual void error (const location_type& loc, const std::string& msg);
|
||||||
|
|
||||||
|
/// Generate an error message.
|
||||||
|
/// \param state the state where the error occurred.
|
||||||
|
/// \param tok the lookahead token.
|
||||||
|
virtual std::string yysyntax_error_ (int yystate, int tok);
|
||||||
|
|
||||||
|
#if YYDEBUG
|
||||||
|
/// \brief Report a symbol value on the debug stream.
|
||||||
|
/// \param yytype The token type.
|
||||||
|
/// \param yyvaluep Its semantic value.
|
||||||
|
/// \param yylocationp Its location.
|
||||||
|
virtual void yy_symbol_value_print_ (int yytype,
|
||||||
|
const semantic_type* yyvaluep,
|
||||||
|
const location_type* yylocationp);
|
||||||
|
/// \brief Report a symbol on the debug stream.
|
||||||
|
/// \param yytype The token type.
|
||||||
|
/// \param yyvaluep Its semantic value.
|
||||||
|
/// \param yylocationp Its location.
|
||||||
|
virtual void yy_symbol_print_ (int yytype,
|
||||||
|
const semantic_type* yyvaluep,
|
||||||
|
const location_type* yylocationp);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/// State numbers.
|
||||||
|
typedef int state_type;
|
||||||
|
/// State stack type.
|
||||||
|
typedef stack<state_type> state_stack_type;
|
||||||
|
/// Semantic value stack type.
|
||||||
|
typedef stack<semantic_type> semantic_stack_type;
|
||||||
|
/// location stack type.
|
||||||
|
typedef stack<location_type> location_stack_type;
|
||||||
|
|
||||||
|
/// The state stack.
|
||||||
|
state_stack_type yystate_stack_;
|
||||||
|
/// The semantic value stack.
|
||||||
|
semantic_stack_type yysemantic_stack_;
|
||||||
|
/// The location stack.
|
||||||
|
location_stack_type yylocation_stack_;
|
||||||
|
|
||||||
|
/// Whether the given \c yypact_ value indicates a defaulted state.
|
||||||
|
/// \param yyvalue the value to check
|
||||||
|
static bool yy_pact_value_is_default_ (int yyvalue);
|
||||||
|
|
||||||
|
/// Whether the given \c yytable_ value indicates a syntax error.
|
||||||
|
/// \param yyvalue the value to check
|
||||||
|
static bool yy_table_value_is_error_ (int yyvalue);
|
||||||
|
|
||||||
|
/// Internal symbol numbers.
|
||||||
|
typedef unsigned char token_number_type;
|
||||||
|
/* Tables. */
|
||||||
|
/// For a state, the index in \a yytable_ of its portion.
|
||||||
|
static const signed char yypact_[];
|
||||||
|
static const signed char yypact_ninf_;
|
||||||
|
|
||||||
|
/// For a state, default reduction number.
|
||||||
|
/// Unless\a yytable_ specifies something else to do.
|
||||||
|
/// Zero means the default is an error.
|
||||||
|
static const unsigned char yydefact_[];
|
||||||
|
|
||||||
|
static const signed char yypgoto_[];
|
||||||
|
static const signed char yydefgoto_[];
|
||||||
|
|
||||||
|
/// What to do in a state.
|
||||||
|
/// \a yytable_[yypact_[s]]: what to do in state \a s.
|
||||||
|
/// - if positive, shift that token.
|
||||||
|
/// - if negative, reduce the rule which number is the opposite.
|
||||||
|
/// - if zero, do what YYDEFACT says.
|
||||||
|
static const unsigned char yytable_[];
|
||||||
|
static const signed char yytable_ninf_;
|
||||||
|
|
||||||
|
static const signed char yycheck_[];
|
||||||
|
|
||||||
|
/// For a state, its accessing symbol.
|
||||||
|
static const unsigned char yystos_[];
|
||||||
|
|
||||||
|
/// For a rule, its LHS.
|
||||||
|
static const unsigned char yyr1_[];
|
||||||
|
/// For a rule, its RHS length.
|
||||||
|
static const unsigned char yyr2_[];
|
||||||
|
|
||||||
|
/// Convert the symbol name \a n to a form suitable for a diagnostic.
|
||||||
|
static std::string yytnamerr_ (const char *n);
|
||||||
|
|
||||||
|
|
||||||
|
/// For a symbol, its name in clear.
|
||||||
|
static const char* const yytname_[];
|
||||||
|
#if YYDEBUG
|
||||||
|
/// A type to store symbol numbers and -1.
|
||||||
|
typedef signed char rhs_number_type;
|
||||||
|
/// A `-1'-separated list of the rules' RHS.
|
||||||
|
static const rhs_number_type yyrhs_[];
|
||||||
|
/// For each rule, the index of the first RHS symbol in \a yyrhs_.
|
||||||
|
static const unsigned char yyprhs_[];
|
||||||
|
/// For each rule, its source line number.
|
||||||
|
static const unsigned char yyrline_[];
|
||||||
|
/// For each scanner token number, its symbol number.
|
||||||
|
static const unsigned short int yytoken_number_[];
|
||||||
|
/// Report on the debug stream that the rule \a r is going to be reduced.
|
||||||
|
virtual void yy_reduce_print_ (int r);
|
||||||
|
/// Print the state stack on the debug stream.
|
||||||
|
virtual void yystack_print_ ();
|
||||||
|
|
||||||
|
/* Debugging. */
|
||||||
|
int yydebug_;
|
||||||
|
std::ostream* yycdebug_;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/// Convert a scanner token number \a t to a symbol number.
|
||||||
|
token_number_type yytranslate_ (int t);
|
||||||
|
|
||||||
|
/// \brief Reclaim the memory associated to a symbol.
|
||||||
|
/// \param yymsg Why this token is reclaimed.
|
||||||
|
/// If null, do not display the symbol, just free it.
|
||||||
|
/// \param yytype The symbol type.
|
||||||
|
/// \param yyvaluep Its semantic value.
|
||||||
|
/// \param yylocationp Its location.
|
||||||
|
inline void yydestruct_ (const char* yymsg,
|
||||||
|
int yytype,
|
||||||
|
semantic_type* yyvaluep,
|
||||||
|
location_type* yylocationp);
|
||||||
|
|
||||||
|
/// Pop \a n symbols the three stacks.
|
||||||
|
inline void yypop_ (unsigned int n = 1);
|
||||||
|
|
||||||
|
/* Constants. */
|
||||||
|
static const int yyeof_;
|
||||||
|
/* LAST_ -- Last index in TABLE_. */
|
||||||
|
static const int yylast_;
|
||||||
|
static const int yynnts_;
|
||||||
|
static const int yyempty_;
|
||||||
|
static const int yyfinal_;
|
||||||
|
static const int yyterror_;
|
||||||
|
static const int yyerrcode_;
|
||||||
|
static const int yyntokens_;
|
||||||
|
static const unsigned int yyuser_token_number_max_;
|
||||||
|
static const token_number_type yyundef_token_;
|
||||||
|
|
||||||
|
/* User arguments. */
|
||||||
|
QJson::ParserPrivate* driver;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // yy
|
||||||
|
/* Line 33 of lalr1.cc */
|
||||||
|
#line 297 "json_parser.hh"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* !YY_YY_JSON_PARSER_HH_INCLUDED */
|
162
src/http/qjson/json_parser.yy
Normal file
162
src/http/qjson/json_parser.yy
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/* This file is part of QJSon
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
* Copyright (C) 2013 Silvio Moioli <silvio@moioli.net>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
%skeleton "lalr1.cc"
|
||||||
|
%defines
|
||||||
|
%define "parser_class_name" "json_parser"
|
||||||
|
|
||||||
|
%code requires{
|
||||||
|
#include "parser_p.h"
|
||||||
|
#include "json_scanner.h"
|
||||||
|
#include "qjson_debug.h"
|
||||||
|
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
|
#include <QtCore/QMap>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
class JSonScanner;
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
class Parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define YYERROR_VERBOSE 1
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QVector<QVariant>*)
|
||||||
|
Q_DECLARE_METATYPE(QVariantMap*)
|
||||||
|
}
|
||||||
|
|
||||||
|
%parse-param { QJson::ParserPrivate* driver }
|
||||||
|
%lex-param { QJson::ParserPrivate* driver }
|
||||||
|
|
||||||
|
%locations
|
||||||
|
|
||||||
|
%debug
|
||||||
|
%error-verbose
|
||||||
|
|
||||||
|
%token END 0 "end of file"
|
||||||
|
|
||||||
|
%token CURLY_BRACKET_OPEN 1 "{"
|
||||||
|
%token CURLY_BRACKET_CLOSE 2 "}"
|
||||||
|
%token SQUARE_BRACKET_OPEN 3 "["
|
||||||
|
%token SQUARE_BRACKET_CLOSE 4 "]"
|
||||||
|
%token COLON 5 ":"
|
||||||
|
%token COMMA 6 ","
|
||||||
|
|
||||||
|
%token NUMBER 7 "number"
|
||||||
|
%token TRUE_VAL 8 "true"
|
||||||
|
%token FALSE_VAL 9 "false"
|
||||||
|
%token NULL_VAL 10 "null"
|
||||||
|
%token STRING 11 "string"
|
||||||
|
|
||||||
|
%token INVALID 12 "invalid"
|
||||||
|
|
||||||
|
// define the initial token
|
||||||
|
%start start
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
// grammar rules
|
||||||
|
|
||||||
|
start: data {
|
||||||
|
driver->m_result = $1;
|
||||||
|
qjsonDebug() << "json_parser - parsing finished";
|
||||||
|
};
|
||||||
|
|
||||||
|
data: value { $$ = $1; }
|
||||||
|
| error
|
||||||
|
{
|
||||||
|
qCritical()<< "json_parser - syntax error found, "
|
||||||
|
<< "forcing abort, Line" << @$.begin.line << "Column" << @$.begin.column;
|
||||||
|
YYABORT;
|
||||||
|
}
|
||||||
|
| END;
|
||||||
|
|
||||||
|
object: CURLY_BRACKET_OPEN CURLY_BRACKET_CLOSE {
|
||||||
|
$$ = QVariant(QVariantMap());
|
||||||
|
}
|
||||||
|
| CURLY_BRACKET_OPEN members CURLY_BRACKET_CLOSE {
|
||||||
|
QVariantMap* map = $2.value<QVariantMap*>();
|
||||||
|
$$ = QVariant(*map);
|
||||||
|
delete map;
|
||||||
|
};
|
||||||
|
|
||||||
|
members: STRING COLON value {
|
||||||
|
QVariantMap* pair = new QVariantMap();
|
||||||
|
pair->insert($1.toString(), $3);
|
||||||
|
$$.setValue<QVariantMap* >(pair);
|
||||||
|
}
|
||||||
|
| members COMMA STRING COLON value {
|
||||||
|
$$.value<QVariantMap*>()->insert($3.toString(), $5);
|
||||||
|
};
|
||||||
|
|
||||||
|
array: SQUARE_BRACKET_OPEN SQUARE_BRACKET_CLOSE {
|
||||||
|
$$ = QVariant(QVariantList());
|
||||||
|
}
|
||||||
|
| SQUARE_BRACKET_OPEN values SQUARE_BRACKET_CLOSE {
|
||||||
|
QVector<QVariant>* list = $2.value<QVector<QVariant>* >();
|
||||||
|
$$ = QVariant(list->toList());
|
||||||
|
delete list;
|
||||||
|
};
|
||||||
|
|
||||||
|
values: value {
|
||||||
|
QVector<QVariant>* list = new QVector<QVariant>(1);
|
||||||
|
list->replace(0, $1);
|
||||||
|
$$.setValue(list);
|
||||||
|
}
|
||||||
|
| values COMMA value {
|
||||||
|
$$.value<QVector<QVariant>* >()->append($3);
|
||||||
|
};
|
||||||
|
|
||||||
|
value: STRING
|
||||||
|
| NUMBER
|
||||||
|
| TRUE_VAL
|
||||||
|
| FALSE_VAL
|
||||||
|
| NULL_VAL
|
||||||
|
| object
|
||||||
|
| array;
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
int yy::yylex(YYSTYPE *yylval, yy::location *yylloc, QJson::ParserPrivate* driver)
|
||||||
|
{
|
||||||
|
JSonScanner* scanner = driver->m_scanner;
|
||||||
|
yylval->clear();
|
||||||
|
int ret = scanner->yylex(yylval, yylloc);
|
||||||
|
|
||||||
|
qjsonDebug() << "json_parser::yylex - calling scanner yylval==|"
|
||||||
|
<< yylval->toByteArray() << "|, ret==|" << QString::number(ret) << "|";
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void yy::json_parser::error (const yy::location& yyloc, const std::string& error)
|
||||||
|
{
|
||||||
|
/*qjsonDebug() << yyloc.begin.line;
|
||||||
|
qjsonDebug() << yyloc.begin.column;
|
||||||
|
qjsonDebug() << yyloc.end.line;
|
||||||
|
qjsonDebug() << yyloc.end.column;*/
|
||||||
|
qjsonDebug() << "json_parser::error [line" << yyloc.end.line << "] -" << error.c_str() ;
|
||||||
|
driver->setError(QString::fromLatin1(error.c_str()), yyloc.end.line);
|
||||||
|
}
|
4507
src/http/qjson/json_scanner.cc
Normal file
4507
src/http/qjson/json_scanner.cc
Normal file
File diff suppressed because it is too large
Load Diff
75
src/http/qjson/json_scanner.cpp
Normal file
75
src/http/qjson/json_scanner.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
* Copyright (C) 2013 Silvio Moioli <silvio@moioli.net>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#include "json_scanner.cc"
|
||||||
|
|
||||||
|
#include "qjson_debug.h"
|
||||||
|
#include "json_scanner.h"
|
||||||
|
#include "json_parser.hh"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QRegExp>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
JSonScanner::JSonScanner(QIODevice* io)
|
||||||
|
: m_allowSpecialNumbers(false),
|
||||||
|
m_io (io),
|
||||||
|
m_criticalError(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void JSonScanner::allowSpecialNumbers(bool allow) {
|
||||||
|
m_allowSpecialNumbers = allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JSonScanner::yylex(YYSTYPE* yylval, yy::location *yylloc) {
|
||||||
|
m_yylval = yylval;
|
||||||
|
m_yylloc = yylloc;
|
||||||
|
m_yylloc->step();
|
||||||
|
int result = yylex();
|
||||||
|
|
||||||
|
if (m_criticalError) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int JSonScanner::LexerInput(char* buf, int max_size) {
|
||||||
|
if (!m_io->isOpen()) {
|
||||||
|
qCritical() << "JSonScanner::yylex - io device is not open";
|
||||||
|
m_criticalError = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readBytes = m_io->read(buf, max_size);
|
||||||
|
if(readBytes < 0) {
|
||||||
|
qCritical() << "JSonScanner::yylex - error while reading from io device";
|
||||||
|
m_criticalError = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return readBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
60
src/http/qjson/json_scanner.h
Normal file
60
src/http/qjson/json_scanner.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _JSON_SCANNER
|
||||||
|
#define _JSON_SCANNER
|
||||||
|
|
||||||
|
#include <QtCore/QIODevice>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
#define YYSTYPE QVariant
|
||||||
|
|
||||||
|
// Only include FlexLexer.h if it hasn't been already included
|
||||||
|
#if ! defined(yyFlexLexerOnce)
|
||||||
|
#include <FlexLexer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "parser_p.h"
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
class location;
|
||||||
|
int yylex(YYSTYPE *yylval, yy::location *yylloc, QJson::ParserPrivate* driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
class JSonScanner : public yyFlexLexer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit JSonScanner(QIODevice* io);
|
||||||
|
void allowSpecialNumbers(bool allow);
|
||||||
|
|
||||||
|
int yylex(YYSTYPE* yylval, yy::location *yylloc);
|
||||||
|
int yylex();
|
||||||
|
int LexerInput(char* buf, int max_size);
|
||||||
|
protected:
|
||||||
|
bool m_allowSpecialNumbers;
|
||||||
|
QIODevice* m_io;
|
||||||
|
|
||||||
|
YYSTYPE* m_yylval;
|
||||||
|
yy::location* m_yylloc;
|
||||||
|
bool m_criticalError;
|
||||||
|
QString m_currentString;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
248
src/http/qjson/json_scanner.yy
Normal file
248
src/http/qjson/json_scanner.yy
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Silvio Moioli <silvio@moioli.net>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110yy::json_parser::token::INVALID301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Flex output settings */
|
||||||
|
%option 8bit c++ full warn
|
||||||
|
%option noyywrap nounistd
|
||||||
|
%option noinput nounput noyy_push_state noyy_pop_state noyy_top_state noyy_scan_buffer noyy_scan_bytes noyy_scan_string noyyget_extra noyyset_extra noyyget_leng noyyget_text noyyget_lineno noyyset_lineno noyyget_in noyyset_in noyyget_out noyyset_out noyyget_lval noyyset_lval noyyget_lloc noyyset_lloc noyyget_debug noyyset_debug
|
||||||
|
%option yyclass="JSonScanner"
|
||||||
|
%option outfile="json_scanner.cc"
|
||||||
|
|
||||||
|
%{
|
||||||
|
#include "json_scanner.h"
|
||||||
|
#include "json_parser.hh"
|
||||||
|
|
||||||
|
#define YY_USER_INIT if(m_allowSpecialNumbers) { \
|
||||||
|
BEGIN(ALLOW_SPECIAL_NUMBERS); \
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
|
||||||
|
/* Exclusive subscanners for strings and escaped hex sequences */
|
||||||
|
%x QUOTMARK_OPEN HEX_OPEN
|
||||||
|
|
||||||
|
/* Extra-JSON rules active iff m_allowSpecialNumbers is true */
|
||||||
|
%s ALLOW_SPECIAL_NUMBERS
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
/* Whitespace */
|
||||||
|
[\v\f\t ]+ {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
}
|
||||||
|
|
||||||
|
[\r\n]+ {
|
||||||
|
m_yylloc->lines(yyleng);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Special values */
|
||||||
|
true {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(true);
|
||||||
|
return yy::json_parser::token::TRUE_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
false {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(false);
|
||||||
|
return yy::json_parser::token::FALSE_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
null {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant();
|
||||||
|
return yy::json_parser::token::NULL_VAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Numbers */
|
||||||
|
[0-9] |
|
||||||
|
[1-9][0-9]+ {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(strtoull(yytext, NULL, 10));
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
qCritical() << "Number is out of range: " << yytext;
|
||||||
|
return yy::json_parser::token::INVALID;
|
||||||
|
}
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
-[0-9] |
|
||||||
|
-[1-9][0-9]+ {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(strtoll(yytext, NULL, 10));
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
qCritical() << "Number is out of range: " << yytext;
|
||||||
|
return yy::json_parser::token::INVALID;
|
||||||
|
}
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
-?(([0-9])|([1-9][0-9]+))(\.[0-9]+)?([Ee][+\-]?[0-9]+)? {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(strtod(yytext, NULL));
|
||||||
|
if (errno == ERANGE) {
|
||||||
|
qCritical() << "Number is out of range: " << yytext;
|
||||||
|
return yy::json_parser::token::INVALID;
|
||||||
|
}
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Strings */
|
||||||
|
\" {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
BEGIN(QUOTMARK_OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
<QUOTMARK_OPEN>{
|
||||||
|
\\\" {
|
||||||
|
m_currentString.append(QLatin1String("\""));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\\\ {
|
||||||
|
m_currentString.append(QLatin1String("\\"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\\/ {
|
||||||
|
m_currentString.append(QLatin1String("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\b {
|
||||||
|
m_currentString.append(QLatin1String("\b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\f {
|
||||||
|
m_currentString.append(QLatin1String("\f"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\n {
|
||||||
|
m_currentString.append(QLatin1String("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\r {
|
||||||
|
m_currentString.append(QLatin1String("\r"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\t {
|
||||||
|
m_currentString.append(QLatin1String("\t"));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\u {
|
||||||
|
BEGIN(HEX_OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
[^\"\\]+ {
|
||||||
|
m_currentString.append(QString::fromUtf8(yytext));
|
||||||
|
}
|
||||||
|
|
||||||
|
\\ {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
\" {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(m_currentString);
|
||||||
|
m_currentString.clear();
|
||||||
|
BEGIN(INITIAL);
|
||||||
|
return yy::json_parser::token::STRING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<HEX_OPEN>{
|
||||||
|
[0-9A-Fa-f]{4} {
|
||||||
|
QString hexDigits = QString::fromUtf8(yytext, yyleng);
|
||||||
|
bool ok;
|
||||||
|
ushort hexDigit1 = hexDigits.left(2).toShort(&ok, 16);
|
||||||
|
ushort hexDigit2 = hexDigits.right(2).toShort(&ok, 16);
|
||||||
|
m_currentString.append(QChar(hexDigit2, hexDigit1));
|
||||||
|
BEGIN(QUOTMARK_OPEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
.|\n {
|
||||||
|
qCritical() << "Invalid hex string";
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(QLatin1String(""));
|
||||||
|
BEGIN(QUOTMARK_OPEN);
|
||||||
|
return yy::json_parser::token::INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* "Compound type" related tokens */
|
||||||
|
: {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::COLON;
|
||||||
|
}
|
||||||
|
|
||||||
|
, {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::COMMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
\[ {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::SQUARE_BRACKET_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
\] {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::SQUARE_BRACKET_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
\{ {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::CURLY_BRACKET_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
\} {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::CURLY_BRACKET_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Extra-JSON numbers */
|
||||||
|
<ALLOW_SPECIAL_NUMBERS>{
|
||||||
|
(?i:nan) {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(std::numeric_limits<double>::quiet_NaN());
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Ii]nfinity {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(std::numeric_limits<double>::infinity());
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
-[Ii]nfinity {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
*m_yylval = QVariant(-std::numeric_limits<double>::infinity());
|
||||||
|
return yy::json_parser::token::NUMBER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If all else fails */
|
||||||
|
. {
|
||||||
|
m_yylloc->columns(yyleng);
|
||||||
|
return yy::json_parser::token::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
<<EOF>> return yy::json_parser::token::END;
|
181
src/http/qjson/location.hh
Normal file
181
src/http/qjson/location.hh
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
/* A Bison parser, made by GNU Bison 2.7. */
|
||||||
|
|
||||||
|
/* Locations for Bison parsers in C++
|
||||||
|
|
||||||
|
Copyright (C) 2002-2007, 2009-2012 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \file location.hh
|
||||||
|
** Define the yy::location class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef YY_YY_LOCATION_HH_INCLUDED
|
||||||
|
# define YY_YY_LOCATION_HH_INCLUDED
|
||||||
|
|
||||||
|
# include "position.hh"
|
||||||
|
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
/* Line 166 of location.cc */
|
||||||
|
#line 47 "location.hh"
|
||||||
|
|
||||||
|
/// Abstract a location.
|
||||||
|
class location
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Construct a location from \a b to \a e.
|
||||||
|
location (const position& b, const position& e)
|
||||||
|
: begin (b)
|
||||||
|
, end (e)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a 0-width location in \a p.
|
||||||
|
explicit location (const position& p = position ())
|
||||||
|
: begin (p)
|
||||||
|
, end (p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct a 0-width location in \a f, \a l, \a c.
|
||||||
|
explicit location (std::string* f,
|
||||||
|
unsigned int l = 1u,
|
||||||
|
unsigned int c = 1u)
|
||||||
|
: begin (f, l, c)
|
||||||
|
, end (f, l, c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Initialization.
|
||||||
|
void initialize (std::string* f = YY_NULL,
|
||||||
|
unsigned int l = 1u,
|
||||||
|
unsigned int c = 1u)
|
||||||
|
{
|
||||||
|
begin.initialize (f, l, c);
|
||||||
|
end = begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \name Line and Column related manipulators
|
||||||
|
** \{ */
|
||||||
|
public:
|
||||||
|
/// Reset initial location to final location.
|
||||||
|
void step ()
|
||||||
|
{
|
||||||
|
begin = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extend the current location to the COUNT next columns.
|
||||||
|
void columns (unsigned int count = 1)
|
||||||
|
{
|
||||||
|
end += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extend the current location to the COUNT next lines.
|
||||||
|
void lines (unsigned int count = 1)
|
||||||
|
{
|
||||||
|
end.lines (count);
|
||||||
|
}
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
/// Beginning of the located region.
|
||||||
|
position begin;
|
||||||
|
/// End of the located region.
|
||||||
|
position end;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Join two location objects to create a location.
|
||||||
|
inline const location operator+ (const location& begin, const location& end)
|
||||||
|
{
|
||||||
|
location res = begin;
|
||||||
|
res.end = end.end;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two location objects.
|
||||||
|
inline const location operator+ (const location& begin, unsigned int width)
|
||||||
|
{
|
||||||
|
location res = begin;
|
||||||
|
res.columns (width);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add and assign a location.
|
||||||
|
inline location& operator+= (location& res, unsigned int width)
|
||||||
|
{
|
||||||
|
res.columns (width);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two location objects.
|
||||||
|
inline bool
|
||||||
|
operator== (const location& loc1, const location& loc2)
|
||||||
|
{
|
||||||
|
return loc1.begin == loc2.begin && loc1.end == loc2.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two location objects.
|
||||||
|
inline bool
|
||||||
|
operator!= (const location& loc1, const location& loc2)
|
||||||
|
{
|
||||||
|
return !(loc1 == loc2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Intercept output stream redirection.
|
||||||
|
** \param ostr the destination output stream
|
||||||
|
** \param loc a reference to the location to redirect
|
||||||
|
**
|
||||||
|
** Avoid duplicate information.
|
||||||
|
*/
|
||||||
|
template <typename YYChar>
|
||||||
|
inline std::basic_ostream<YYChar>&
|
||||||
|
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
|
||||||
|
{
|
||||||
|
position last = loc.end - 1;
|
||||||
|
ostr << loc.begin;
|
||||||
|
if (last.filename
|
||||||
|
&& (!loc.begin.filename
|
||||||
|
|| *loc.begin.filename != *last.filename))
|
||||||
|
ostr << '-' << last;
|
||||||
|
else if (loc.begin.line != last.line)
|
||||||
|
ostr << '-' << last.line << '.' << last.column;
|
||||||
|
else if (loc.begin.column != last.column)
|
||||||
|
ostr << '-' << last.column;
|
||||||
|
return ostr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // yy
|
||||||
|
/* Line 296 of location.cc */
|
||||||
|
#line 180 "location.hh"
|
||||||
|
|
||||||
|
#endif /* !YY_YY_LOCATION_HH_INCLUDED */
|
125
src/http/qjson/parser.cpp
Normal file
125
src/http/qjson/parser.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
#include "parser_p.h"
|
||||||
|
#include "json_parser.hh"
|
||||||
|
#include "json_scanner.h"
|
||||||
|
|
||||||
|
#include <QtCore/QBuffer>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QTextStream>
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
using namespace QJson;
|
||||||
|
|
||||||
|
ParserPrivate::ParserPrivate() :
|
||||||
|
m_scanner(0)
|
||||||
|
, m_negate(false)
|
||||||
|
, m_error(false)
|
||||||
|
, m_errorLine(0)
|
||||||
|
, m_specialNumbersAllowed(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserPrivate::~ParserPrivate()
|
||||||
|
{
|
||||||
|
delete m_scanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParserPrivate::setError(QString errorMsg, int errorLine) {
|
||||||
|
m_error = true;
|
||||||
|
m_errorMsg = errorMsg;
|
||||||
|
m_errorLine = errorLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser::Parser() :
|
||||||
|
d(new ParserPrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Parser::~Parser()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Parser::parse (QIODevice* io, bool* ok)
|
||||||
|
{
|
||||||
|
d->m_errorMsg.clear();
|
||||||
|
delete d->m_scanner;
|
||||||
|
d->m_scanner = 0;
|
||||||
|
|
||||||
|
if (!io->isOpen()) {
|
||||||
|
if (!io->open(QIODevice::ReadOnly)) {
|
||||||
|
if (ok != 0)
|
||||||
|
*ok = false;
|
||||||
|
qCritical ("Error opening device");
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!io->isReadable()) {
|
||||||
|
if (ok != 0)
|
||||||
|
*ok = false;
|
||||||
|
qCritical ("Device is not readable");
|
||||||
|
io->close();
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
d->m_scanner = new JSonScanner (io);
|
||||||
|
d->m_scanner->allowSpecialNumbers(d->m_specialNumbersAllowed);
|
||||||
|
yy::json_parser parser(d);
|
||||||
|
parser.parse();
|
||||||
|
|
||||||
|
delete d->m_scanner;
|
||||||
|
d->m_scanner = 0;
|
||||||
|
|
||||||
|
if (ok != 0)
|
||||||
|
*ok = !d->m_error;
|
||||||
|
|
||||||
|
io->close();
|
||||||
|
return d->m_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant Parser::parse(const QByteArray& jsonString, bool* ok) {
|
||||||
|
QBuffer buffer;
|
||||||
|
buffer.open(QBuffer::ReadWrite);
|
||||||
|
buffer.write(jsonString);
|
||||||
|
buffer.seek(0);
|
||||||
|
return parse (&buffer, ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Parser::errorString() const
|
||||||
|
{
|
||||||
|
return d->m_errorMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Parser::errorLine() const
|
||||||
|
{
|
||||||
|
return d->m_errorLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QJson::Parser::allowSpecialNumbers(bool allowSpecialNumbers) {
|
||||||
|
d->m_specialNumbersAllowed = allowSpecialNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parser::specialNumbersAllowed() const {
|
||||||
|
return d->m_specialNumbersAllowed;
|
||||||
|
}
|
99
src/http/qjson/parser.h
Normal file
99
src/http/qjson/parser.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QJSON_PARSER_H
|
||||||
|
#define QJSON_PARSER_H
|
||||||
|
|
||||||
|
#include "qjson_export.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QIODevice;
|
||||||
|
class QVariant;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Namespace used by QJson
|
||||||
|
*/
|
||||||
|
namespace QJson {
|
||||||
|
|
||||||
|
class ParserPrivate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main class used to convert JSON data to QVariant objects
|
||||||
|
*/
|
||||||
|
class QJSON_EXPORT Parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Parser();
|
||||||
|
~Parser();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read JSON string from the I/O Device and converts it to a QVariant object
|
||||||
|
* @param io Input output device
|
||||||
|
* @param ok if a conversion error occurs, *ok is set to false; otherwise *ok is set to true.
|
||||||
|
* @returns a QVariant object generated from the JSON string
|
||||||
|
*/
|
||||||
|
QVariant parse(QIODevice* io, bool* ok = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a method provided for convenience.
|
||||||
|
* @param jsonData data containing the JSON object representation
|
||||||
|
* @param ok if a conversion error occurs, *ok is set to false; otherwise *ok is set to true.
|
||||||
|
* @returns a QVariant object generated from the JSON string
|
||||||
|
* @sa errorString
|
||||||
|
* @sa errorLine
|
||||||
|
*/
|
||||||
|
QVariant parse(const QByteArray& jsonData, bool* ok = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns the error message
|
||||||
|
* @returns a QString object containing the error message of the last parse operation
|
||||||
|
* @sa errorLine
|
||||||
|
*/
|
||||||
|
QString errorString() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method returns line number where the error occurred
|
||||||
|
* @returns the line number where the error occurred
|
||||||
|
* @sa errorString
|
||||||
|
*/
|
||||||
|
int errorLine() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether special numbers (Infinity, -Infinity, NaN) are allowed as an extension to
|
||||||
|
* the standard
|
||||||
|
* @param allowSpecialNumbers new value of whether special numbers are allowed
|
||||||
|
* @sa specialNumbersAllowed
|
||||||
|
*/
|
||||||
|
void allowSpecialNumbers(bool allowSpecialNumbers);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns whether special numbers (Infinity, -Infinity, NaN) are allowed
|
||||||
|
* @sa allowSpecialNumbers
|
||||||
|
*/
|
||||||
|
bool specialNumbersAllowed() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(Parser)
|
||||||
|
ParserPrivate* const d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QJSON_PARSER_H
|
56
src/http/qjson/parser_p.h
Normal file
56
src/http/qjson/parser_p.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* This file is part of QJson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2008 Flavio Castelli <flavio.castelli@gmail.com>
|
||||||
|
* Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QJSON_PARSER_P_H
|
||||||
|
#define QJSON_PARSER_P_H
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
class JSonScanner;
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
class json_parser;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
|
||||||
|
class ParserPrivate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ParserPrivate();
|
||||||
|
~ParserPrivate();
|
||||||
|
|
||||||
|
void setError(QString errorMsg, int line);
|
||||||
|
|
||||||
|
JSonScanner* m_scanner;
|
||||||
|
bool m_negate;
|
||||||
|
bool m_error;
|
||||||
|
int m_errorLine;
|
||||||
|
QString m_errorMsg;
|
||||||
|
QVariant m_result;
|
||||||
|
bool m_specialNumbersAllowed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QJSON_PARSER_H
|
68
src/http/qjson/parserrunnable.cpp
Normal file
68
src/http/qjson/parserrunnable.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parserrunnable.h"
|
||||||
|
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
using namespace QJson;
|
||||||
|
|
||||||
|
class QJson::ParserRunnable::Private
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QByteArray m_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
ParserRunnable::ParserRunnable(QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
QRunnable(),
|
||||||
|
d(new Private)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<QVariant>("QVariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserRunnable::~ParserRunnable()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParserRunnable::setData( const QByteArray& data ) {
|
||||||
|
d->m_data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParserRunnable::run()
|
||||||
|
{
|
||||||
|
qDebug() << Q_FUNC_INFO;
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
Parser parser;
|
||||||
|
QVariant result = parser.parse (d->m_data, &ok);
|
||||||
|
if (ok) {
|
||||||
|
qDebug() << "successfully converted json item to QVariant object";
|
||||||
|
emit parsingFinished(result, true, QString());
|
||||||
|
} else {
|
||||||
|
const QString errorText = tr("An error occurred while parsing json: %1").arg(parser.errorString());
|
||||||
|
qCritical() << errorText;
|
||||||
|
emit parsingFinished(QVariant(), false, errorText);
|
||||||
|
}
|
||||||
|
}
|
64
src/http/qjson/parserrunnable.h
Normal file
64
src/http/qjson/parserrunnable.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PARSERRUNNABLE_H
|
||||||
|
#define PARSERRUNNABLE_H
|
||||||
|
|
||||||
|
#include "qjson_export.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QRunnable>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QVariant;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
/**
|
||||||
|
* @brief Convenience class for converting JSON data to QVariant objects using a dedicated thread
|
||||||
|
*/
|
||||||
|
class QJSON_EXPORT ParserRunnable : public QObject, public QRunnable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ParserRunnable(QObject* parent = 0);
|
||||||
|
~ParserRunnable();
|
||||||
|
|
||||||
|
void setData( const QByteArray& data );
|
||||||
|
|
||||||
|
void run();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the parsing process has been completed
|
||||||
|
* @param json contains the result of the parsing
|
||||||
|
* @param ok if a parsing error occurs ok is set to false, otherwise it's set to true.
|
||||||
|
* @param error_msg contains a string explaining the failure reason
|
||||||
|
**/
|
||||||
|
void parsingFinished(const QVariant& json, bool ok, const QString& error_msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(ParserRunnable)
|
||||||
|
class Private;
|
||||||
|
Private* const d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // PARSERRUNNABLE_H
|
172
src/http/qjson/position.hh
Normal file
172
src/http/qjson/position.hh
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
/* A Bison parser, made by GNU Bison 2.7. */
|
||||||
|
|
||||||
|
/* Positions for Bison parsers in C++
|
||||||
|
|
||||||
|
Copyright (C) 2002-2007, 2009-2012 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \file position.hh
|
||||||
|
** Define the yy::position class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef YY_YY_POSITION_HH_INCLUDED
|
||||||
|
# define YY_YY_POSITION_HH_INCLUDED
|
||||||
|
|
||||||
|
# include <algorithm> // std::max
|
||||||
|
# include <iostream>
|
||||||
|
# include <string>
|
||||||
|
|
||||||
|
# ifndef YY_NULL
|
||||||
|
# if defined __cplusplus && 201103L <= __cplusplus
|
||||||
|
# define YY_NULL nullptr
|
||||||
|
# else
|
||||||
|
# define YY_NULL 0
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
/* Line 36 of location.cc */
|
||||||
|
#line 57 "position.hh"
|
||||||
|
/// Abstract a position.
|
||||||
|
class position
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Construct a position.
|
||||||
|
explicit position (std::string* f = YY_NULL,
|
||||||
|
unsigned int l = 1u,
|
||||||
|
unsigned int c = 1u)
|
||||||
|
: filename (f)
|
||||||
|
, line (l)
|
||||||
|
, column (c)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Initialization.
|
||||||
|
void initialize (std::string* fn = YY_NULL,
|
||||||
|
unsigned int l = 1u,
|
||||||
|
unsigned int c = 1u)
|
||||||
|
{
|
||||||
|
filename = fn;
|
||||||
|
line = l;
|
||||||
|
column = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \name Line and Column related manipulators
|
||||||
|
** \{ */
|
||||||
|
/// (line related) Advance to the COUNT next lines.
|
||||||
|
void lines (int count = 1)
|
||||||
|
{
|
||||||
|
column = 1u;
|
||||||
|
line += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// (column related) Advance to the COUNT next columns.
|
||||||
|
void columns (int count = 1)
|
||||||
|
{
|
||||||
|
column = std::max (1u, column + count);
|
||||||
|
}
|
||||||
|
/** \} */
|
||||||
|
|
||||||
|
/// File name to which this position refers.
|
||||||
|
std::string* filename;
|
||||||
|
/// Current line number.
|
||||||
|
unsigned int line;
|
||||||
|
/// Current column number.
|
||||||
|
unsigned int column;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Add and assign a position.
|
||||||
|
inline position&
|
||||||
|
operator+= (position& res, const int width)
|
||||||
|
{
|
||||||
|
res.columns (width);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two position objects.
|
||||||
|
inline const position
|
||||||
|
operator+ (const position& begin, const int width)
|
||||||
|
{
|
||||||
|
position res = begin;
|
||||||
|
return res += width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add and assign a position.
|
||||||
|
inline position&
|
||||||
|
operator-= (position& res, const int width)
|
||||||
|
{
|
||||||
|
return res += -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add two position objects.
|
||||||
|
inline const position
|
||||||
|
operator- (const position& begin, const int width)
|
||||||
|
{
|
||||||
|
return begin + -width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two position objects.
|
||||||
|
inline bool
|
||||||
|
operator== (const position& pos1, const position& pos2)
|
||||||
|
{
|
||||||
|
return (pos1.line == pos2.line
|
||||||
|
&& pos1.column == pos2.column
|
||||||
|
&& (pos1.filename == pos2.filename
|
||||||
|
|| (pos1.filename && pos2.filename
|
||||||
|
&& *pos1.filename == *pos2.filename)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare two position objects.
|
||||||
|
inline bool
|
||||||
|
operator!= (const position& pos1, const position& pos2)
|
||||||
|
{
|
||||||
|
return !(pos1 == pos2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** \brief Intercept output stream redirection.
|
||||||
|
** \param ostr the destination output stream
|
||||||
|
** \param pos a reference to the position to redirect
|
||||||
|
*/
|
||||||
|
template <typename YYChar>
|
||||||
|
inline std::basic_ostream<YYChar>&
|
||||||
|
operator<< (std::basic_ostream<YYChar>& ostr, const position& pos)
|
||||||
|
{
|
||||||
|
if (pos.filename)
|
||||||
|
ostr << *pos.filename << ':';
|
||||||
|
return ostr << pos.line << '.' << pos.column;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // yy
|
||||||
|
/* Line 148 of location.cc */
|
||||||
|
#line 172 "position.hh"
|
||||||
|
#endif /* !YY_YY_POSITION_HH_INCLUDED */
|
34
src/http/qjson/qjson_debug.h
Normal file
34
src/http/qjson/qjson_debug.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Michael Leupold <lemma@confuego.org>
|
||||||
|
* Copyright (C) 2013 Silvio Moioli <silvio@moioli.net>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QJSON_DEBUG_H
|
||||||
|
#define QJSON_DEBUG_H
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
// define qjsonDebug()
|
||||||
|
#ifdef QJSON_VERBOSE_DEBUG_OUTPUT
|
||||||
|
inline QDebug qjsonDebug() { return QDebug(QtDebugMsg); }
|
||||||
|
#else
|
||||||
|
#define qjsonDebug() if(false) QDebug(QtDebugMsg)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
35
src/http/qjson/qjson_export.h
Normal file
35
src/http/qjson/qjson_export.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
/* This file is part of the KDE project
|
||||||
|
Copyright (C) 2009 Pino Toscano <pino@kde.org>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License version 2.1, as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
|
||||||
|
This library 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
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QJSON_EXPORT_H
|
||||||
|
#define QJSON_EXPORT_H
|
||||||
|
|
||||||
|
#include <QtCore/qglobal.h>
|
||||||
|
|
||||||
|
#ifndef QJSON_EXPORT
|
||||||
|
# if defined(QJSON_MAKEDLL)
|
||||||
|
/* We are building this library */
|
||||||
|
# define QJSON_EXPORT Q_DECL_EXPORT
|
||||||
|
# else
|
||||||
|
/* We are using this library */
|
||||||
|
# define QJSON_EXPORT Q_DECL_IMPORT
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
85
src/http/qjson/qobjecthelper.cpp
Normal file
85
src/http/qjson/qobjecthelper.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Till Adam <adam@kde.org>
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "qobjecthelper.h"
|
||||||
|
|
||||||
|
#include <QtCore/QMetaObject>
|
||||||
|
#include <QtCore/QMetaProperty>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
|
||||||
|
using namespace QJson;
|
||||||
|
|
||||||
|
class QObjectHelper::QObjectHelperPrivate {
|
||||||
|
};
|
||||||
|
|
||||||
|
QObjectHelper::QObjectHelper()
|
||||||
|
: d (new QObjectHelperPrivate)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QObjectHelper::~QObjectHelper()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap QObjectHelper::qobject2qvariant( const QObject* object,
|
||||||
|
const QStringList& ignoredProperties)
|
||||||
|
{
|
||||||
|
QVariantMap result;
|
||||||
|
const QMetaObject *metaobject = object->metaObject();
|
||||||
|
int count = metaobject->propertyCount();
|
||||||
|
for (int i=0; i<count; ++i) {
|
||||||
|
QMetaProperty metaproperty = metaobject->property(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QObjectHelper::qvariant2qobject(const QVariantMap& variant, QObject* object)
|
||||||
|
{
|
||||||
|
const QMetaObject *metaobject = object->metaObject();
|
||||||
|
|
||||||
|
QVariantMap::const_iterator iter;
|
||||||
|
for (iter = variant.constBegin(); iter != variant.constEnd(); ++iter) {
|
||||||
|
int pIdx = metaobject->indexOfProperty( iter.key().toLatin1() );
|
||||||
|
|
||||||
|
if ( pIdx < 0 ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMetaProperty metaproperty = metaobject->property( pIdx );
|
||||||
|
QVariant::Type type = metaproperty.type();
|
||||||
|
QVariant v( iter.value() );
|
||||||
|
if ( v.canConvert( type ) ) {
|
||||||
|
v.convert( type );
|
||||||
|
metaproperty.write( object, v );
|
||||||
|
} else if (QString(QLatin1String("QVariant")).compare(QLatin1String(metaproperty.typeName())) == 0) {
|
||||||
|
metaproperty.write( object, v );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
147
src/http/qjson/qobjecthelper.h
Normal file
147
src/http/qjson/qobjecthelper.h
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QOBJECTHELPER_H
|
||||||
|
#define QOBJECTHELPER_H
|
||||||
|
|
||||||
|
#include "qjson_export.h"
|
||||||
|
|
||||||
|
#include <QtCore/QLatin1String>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QVariantMap>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QObject;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
/**
|
||||||
|
* @brief Class used to convert QObject into QVariant and vivce-versa.
|
||||||
|
* During these operations only the class attributes defined as properties will
|
||||||
|
* be considered.
|
||||||
|
* Properties marked as 'non-stored' will be ignored.
|
||||||
|
*
|
||||||
|
* Suppose the declaration of the Person class looks like this:
|
||||||
|
* \code
|
||||||
|
* class Person : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString name READ name WRITE setName)
|
||||||
|
Q_PROPERTY(int phoneNumber READ phoneNumber WRITE setPhoneNumber)
|
||||||
|
Q_PROPERTY(Gender gender READ gender WRITE setGender)
|
||||||
|
Q_PROPERTY(QDate dob READ dob WRITE setDob)
|
||||||
|
Q_ENUMS(Gender)
|
||||||
|
|
||||||
|
public:
|
||||||
|
Person(QObject* parent = 0);
|
||||||
|
~Person();
|
||||||
|
|
||||||
|
QString name() const;
|
||||||
|
void setName(const QString& name);
|
||||||
|
|
||||||
|
int phoneNumber() const;
|
||||||
|
void setPhoneNumber(const int phoneNumber);
|
||||||
|
|
||||||
|
enum Gender {Male, Female};
|
||||||
|
void setGender(Gender gender);
|
||||||
|
Gender gender() const;
|
||||||
|
|
||||||
|
QDate dob() const;
|
||||||
|
void setDob(const QDate& dob);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString m_name;
|
||||||
|
int m_phoneNumber;
|
||||||
|
Gender m_gender;
|
||||||
|
QDate m_dob;
|
||||||
|
};
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The following code will serialize an instance of Person to JSON :
|
||||||
|
|
||||||
|
\code
|
||||||
|
Person person;
|
||||||
|
person.setName("Flavio");
|
||||||
|
person.setPhoneNumber(123456);
|
||||||
|
person.setGender(Person::Male);
|
||||||
|
person.setDob(QDate(1982, 7, 12));
|
||||||
|
|
||||||
|
QVariantMap variant = QObjectHelper::qobject2qvariant(&person);
|
||||||
|
Serializer serializer;
|
||||||
|
qDebug() << serializer.serialize( variant);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The generated output will be:
|
||||||
|
\code
|
||||||
|
{ "dob" : "1982-07-12", "gender" : 0, "name" : "Flavio", "phoneNumber" : 123456 }
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
It's also possible to initialize a QObject using the values stored inside of
|
||||||
|
a QVariantMap.
|
||||||
|
|
||||||
|
Suppose you have the following JSON data stored into a QString:
|
||||||
|
\code
|
||||||
|
{ "dob" : "1982-07-12", "gender" : 0, "name" : "Flavio", "phoneNumber" : 123456 }
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
The following code will initialize an already allocated instance of Person
|
||||||
|
using the JSON values:
|
||||||
|
\code
|
||||||
|
Parser parser;
|
||||||
|
QVariant variant = parser.parse(json);
|
||||||
|
|
||||||
|
Person person;
|
||||||
|
QObjectHelper::qvariant2qobject(variant.toMap(), &person);
|
||||||
|
\endcode
|
||||||
|
|
||||||
|
\sa Parser
|
||||||
|
\sa Serializer
|
||||||
|
*/
|
||||||
|
class QJSON_EXPORT QObjectHelper {
|
||||||
|
public:
|
||||||
|
QObjectHelper();
|
||||||
|
~QObjectHelper();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts a QObject instance into a QVariantMap.
|
||||||
|
*
|
||||||
|
* @param object The QObject instance to be converted.
|
||||||
|
* @param ignoredProperties Properties that won't be converted.
|
||||||
|
*/
|
||||||
|
static QVariantMap qobject2qvariant( const QObject* object,
|
||||||
|
const QStringList& ignoredProperties = QStringList(QString(QLatin1String("objectName"))));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method converts a QVariantMap instance into a QObject
|
||||||
|
*
|
||||||
|
* @param variant Attributes to assign to the object.
|
||||||
|
* @param object The QObject instance to update.
|
||||||
|
*/
|
||||||
|
static void qvariant2qobject(const QVariantMap& variant, QObject* object);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(QObjectHelper)
|
||||||
|
class QObjectHelperPrivate;
|
||||||
|
QObjectHelperPrivate* const d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QOBJECTHELPER_H
|
410
src/http/qjson/serializer.cpp
Normal file
410
src/http/qjson/serializer.cpp
Normal file
@ -0,0 +1,410 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Till Adam <adam@kde.org>
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "serializer.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDataStream>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER // using MSVC compiler
|
||||||
|
#include <float.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace QJson;
|
||||||
|
|
||||||
|
class Serializer::SerializerPrivate {
|
||||||
|
public:
|
||||||
|
SerializerPrivate() :
|
||||||
|
specialNumbersAllowed(false),
|
||||||
|
indentMode(QJson::IndentNone),
|
||||||
|
doublePrecision(6) {
|
||||||
|
errorMessage.clear();
|
||||||
|
}
|
||||||
|
QString errorMessage;
|
||||||
|
bool specialNumbersAllowed;
|
||||||
|
IndentMode indentMode;
|
||||||
|
int doublePrecision;
|
||||||
|
QByteArray buildIndent(int spaces);
|
||||||
|
QByteArray serialize( const QVariant &v, bool *ok, int indentLevel = 0);
|
||||||
|
QString sanitizeString( QString str );
|
||||||
|
QByteArray join( const QList<QByteArray>& list, const QByteArray& sep );
|
||||||
|
};
|
||||||
|
|
||||||
|
QByteArray Serializer::SerializerPrivate::join( const QList<QByteArray>& list, const QByteArray& sep ) {
|
||||||
|
QByteArray res;
|
||||||
|
Q_FOREACH( const QByteArray& i, list ) {
|
||||||
|
if ( !res.isEmpty() )
|
||||||
|
res += sep;
|
||||||
|
res += i;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Serializer::SerializerPrivate::serialize( const QVariant &v, bool *ok, int indentLevel)
|
||||||
|
{
|
||||||
|
QByteArray str;
|
||||||
|
|
||||||
|
if ( ! v.isValid() ) { // invalid or null?
|
||||||
|
str = "null";
|
||||||
|
} else if (( v.type() == QVariant::List ) || ( v.type() == QVariant::StringList )){ // an array or a stringlist?
|
||||||
|
const QVariantList list = v.toList();
|
||||||
|
QList<QByteArray> values;
|
||||||
|
Q_FOREACH( const QVariant& var, list )
|
||||||
|
{
|
||||||
|
indentLevel++;
|
||||||
|
QByteArray serializedValue = serialize( var, ok, indentLevel);
|
||||||
|
indentLevel--;
|
||||||
|
if ( !*ok ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
values << serializedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentMinimum) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel - 1);
|
||||||
|
str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
str = "[\n" + join( values, ",\n" ) + "\n" + indent + "]";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str = "[" + join( values, "," ) + "]";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = "[ " + join( values, ", " ) + " ]";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ( v.type() == QVariant::Map ) { // variant is a map?
|
||||||
|
const QVariantMap vmap = v.toMap();
|
||||||
|
QMapIterator<QString, QVariant> it( vmap );
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentMinimum) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
str = indent + "{ ";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
QByteArray nextindent = buildIndent(indentLevel + 1);
|
||||||
|
str = indent + "{\n" + nextindent;
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str = "{";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = "{ ";
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> pairs;
|
||||||
|
while ( it.hasNext() ) {
|
||||||
|
it.next();
|
||||||
|
indentLevel++;
|
||||||
|
QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
|
||||||
|
indentLevel--;
|
||||||
|
if ( !*ok ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QByteArray key = sanitizeString( it.key() ).toUtf8();
|
||||||
|
QByteArray value = serializedValue;
|
||||||
|
if (indentMode == QJson::IndentCompact) {
|
||||||
|
pairs << key + ":" + value;
|
||||||
|
} else {
|
||||||
|
pairs << key + " : " + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel + 1);
|
||||||
|
str += join( pairs, ",\n" + indent);
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str += join( pairs, "," );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str += join( pairs, ", " );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
str += "\n" + indent + "}";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str += "}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str += " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ( v.type() == QVariant::Hash ) { // variant is a hash?
|
||||||
|
const QVariantHash vhash = v.toHash();
|
||||||
|
QHashIterator<QString, QVariant> it( vhash );
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentMinimum) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
str = indent + "{ ";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
QByteArray nextindent = buildIndent(indentLevel + 1);
|
||||||
|
str = indent + "{\n" + nextindent;
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str = "{";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str = "{ ";
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QByteArray> pairs;
|
||||||
|
while ( it.hasNext() ) {
|
||||||
|
it.next();
|
||||||
|
indentLevel++;
|
||||||
|
QByteArray serializedValue = serialize( it.value(), ok, indentLevel);
|
||||||
|
indentLevel--;
|
||||||
|
if ( !*ok ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QByteArray key = sanitizeString( it.key() ).toUtf8();
|
||||||
|
QByteArray value = serializedValue;
|
||||||
|
if (indentMode == QJson::IndentCompact) {
|
||||||
|
pairs << key + ":" + value;
|
||||||
|
} else {
|
||||||
|
pairs << key + " : " + value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel + 1);
|
||||||
|
str += join( pairs, ",\n" + indent);
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str += join( pairs, "," );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str += join( pairs, ", " );
|
||||||
|
}
|
||||||
|
|
||||||
|
if (indentMode == QJson::IndentMedium || indentMode == QJson::IndentFull) {
|
||||||
|
QByteArray indent = buildIndent(indentLevel);
|
||||||
|
str += "\n" + indent + "}";
|
||||||
|
}
|
||||||
|
else if (indentMode == QJson::IndentCompact) {
|
||||||
|
str += "}";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
str += " }";
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (( v.type() == QVariant::String ) || ( v.type() == QVariant::ByteArray )) { // a string or a byte array?
|
||||||
|
str = sanitizeString( v.toString() ).toUtf8();
|
||||||
|
} else if (( v.type() == QVariant::Double) || ((QMetaType::Type)v.type() == QMetaType::Float)) { // a double or a float?
|
||||||
|
const double value = v.toDouble();
|
||||||
|
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
|
||||||
|
const bool special = _isnan(value) || !_finite(value);
|
||||||
|
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY)
|
||||||
|
const bool special = isnan(value) || isinf(value);
|
||||||
|
#else
|
||||||
|
const bool special = std::isnan(value) || std::isinf(value);
|
||||||
|
#endif
|
||||||
|
if (special) {
|
||||||
|
if (specialNumbersAllowed) {
|
||||||
|
#if defined _WIN32 && !defined(Q_OS_SYMBIAN)
|
||||||
|
if (_isnan(value)) {
|
||||||
|
#elif defined(Q_OS_SYMBIAN) || defined(Q_OS_ANDROID) || defined(Q_OS_BLACKBERRY)
|
||||||
|
if (isnan(value)) {
|
||||||
|
#else
|
||||||
|
if (std::isnan(value)) {
|
||||||
|
#endif
|
||||||
|
str += "NaN";
|
||||||
|
} else {
|
||||||
|
if (value<0) {
|
||||||
|
str += '-';
|
||||||
|
}
|
||||||
|
str += "Infinity";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
errorMessage += QLatin1String("Attempt to write NaN or infinity, which is not supported by json\n");
|
||||||
|
*ok = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
str = QByteArray::number( value , 'g', doublePrecision);
|
||||||
|
if( ! str.contains( "." ) && ! str.contains( "e" ) ) {
|
||||||
|
str += ".0";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ( v.type() == QVariant::Bool ) { // boolean value?
|
||||||
|
str = ( v.toBool() ? "true" : "false" );
|
||||||
|
} else if ( v.type() == QVariant::ULongLong ) { // large unsigned number?
|
||||||
|
str = QByteArray::number( v.value<qulonglong>() );
|
||||||
|
} else if ( v.type() == QVariant::UInt ) { // unsigned int number?
|
||||||
|
str = QByteArray::number( v.value<quint32>() );
|
||||||
|
} else if ( v.canConvert<qlonglong>() ) { // any signed number?
|
||||||
|
str = QByteArray::number( v.value<qlonglong>() );
|
||||||
|
} else if ( v.canConvert<int>() ) { // unsigned short number?
|
||||||
|
str = QByteArray::number( v.value<int>() );
|
||||||
|
} else if ( v.canConvert<QString>() ){ // can value be converted to string?
|
||||||
|
// this will catch QDate, QDateTime, QUrl, ...
|
||||||
|
str = sanitizeString( v.toString() ).toUtf8();
|
||||||
|
//TODO: catch other values like QImage, QRect, ...
|
||||||
|
} else {
|
||||||
|
*ok = false;
|
||||||
|
errorMessage += QLatin1String("Cannot serialize ");
|
||||||
|
errorMessage += v.toString();
|
||||||
|
errorMessage += QLatin1String(" because type ");
|
||||||
|
errorMessage += QLatin1String(v.typeName());
|
||||||
|
errorMessage += QLatin1String(" is not supported by QJson\n");
|
||||||
|
}
|
||||||
|
if ( *ok )
|
||||||
|
{
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Serializer::SerializerPrivate::buildIndent(int spaces)
|
||||||
|
{
|
||||||
|
QByteArray indent;
|
||||||
|
if (spaces < 0) {
|
||||||
|
spaces = 0;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < spaces; i++ ) {
|
||||||
|
indent += " ";
|
||||||
|
}
|
||||||
|
return indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString Serializer::SerializerPrivate::sanitizeString( QString str )
|
||||||
|
{
|
||||||
|
str.replace( QLatin1String( "\\" ), QLatin1String( "\\\\" ) );
|
||||||
|
|
||||||
|
// escape unicode chars
|
||||||
|
QString result;
|
||||||
|
const ushort* unicode = str.utf16();
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
while ( unicode[ i ] ) {
|
||||||
|
if ( unicode[ i ] < 128 ) {
|
||||||
|
result.append( QChar( unicode[ i ] ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
QString hexCode = QString::number( unicode[ i ], 16 ).rightJustified( 4,
|
||||||
|
QLatin1Char('0') );
|
||||||
|
|
||||||
|
result.append( QLatin1String ("\\u") ).append( hexCode );
|
||||||
|
}
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
str = result;
|
||||||
|
|
||||||
|
str.replace( QLatin1String( "\"" ), QLatin1String( "\\\"" ) );
|
||||||
|
str.replace( QLatin1String( "\b" ), QLatin1String( "\\b" ) );
|
||||||
|
str.replace( QLatin1String( "\f" ), QLatin1String( "\\f" ) );
|
||||||
|
str.replace( QLatin1String( "\n" ), QLatin1String( "\\n" ) );
|
||||||
|
str.replace( QLatin1String( "\r" ), QLatin1String( "\\r" ) );
|
||||||
|
str.replace( QLatin1String( "\t" ), QLatin1String( "\\t" ) );
|
||||||
|
|
||||||
|
return QString( QLatin1String( "\"%1\"" ) ).arg( str );
|
||||||
|
}
|
||||||
|
|
||||||
|
Serializer::Serializer()
|
||||||
|
: d( new SerializerPrivate )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Serializer::~Serializer() {
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Serializer::serialize( const QVariant& v, QIODevice* io, bool* ok)
|
||||||
|
{
|
||||||
|
Q_ASSERT( io );
|
||||||
|
*ok = true;
|
||||||
|
|
||||||
|
if (!io->isOpen()) {
|
||||||
|
if (!io->open(QIODevice::WriteOnly)) {
|
||||||
|
d->errorMessage = QLatin1String("Error opening device");
|
||||||
|
*ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!io->isWritable()) {
|
||||||
|
d->errorMessage = QLatin1String("Device is not readable");
|
||||||
|
io->close();
|
||||||
|
*ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QByteArray str = serialize( v, ok);
|
||||||
|
if (*ok && (io->write(str) != str.count())) {
|
||||||
|
*ok = false;
|
||||||
|
d->errorMessage = QLatin1String("Something went wrong while writing to IO device");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Serializer::serialize( const QVariant &v)
|
||||||
|
{
|
||||||
|
bool ok;
|
||||||
|
|
||||||
|
return serialize(v, &ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Serializer::serialize( const QVariant &v, bool *ok)
|
||||||
|
{
|
||||||
|
bool _ok = true;
|
||||||
|
d->errorMessage.clear();
|
||||||
|
|
||||||
|
if (ok) {
|
||||||
|
*ok = true;
|
||||||
|
} else {
|
||||||
|
ok = &_ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
return d->serialize(v, ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QJson::Serializer::allowSpecialNumbers(bool allow) {
|
||||||
|
d->specialNumbersAllowed = allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QJson::Serializer::specialNumbersAllowed() const {
|
||||||
|
return d->specialNumbersAllowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QJson::Serializer::setIndentMode(IndentMode mode) {
|
||||||
|
d->indentMode = mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QJson::Serializer::setDoublePrecision(int precision) {
|
||||||
|
d->doublePrecision = precision;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndentMode QJson::Serializer::indentMode() const {
|
||||||
|
return d->indentMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString QJson::Serializer::errorMessage() const {
|
||||||
|
return d->errorMessage;
|
||||||
|
}
|
||||||
|
|
191
src/http/qjson/serializer.h
Normal file
191
src/http/qjson/serializer.h
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Till Adam <adam@kde.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef QJSON_SERIALIZER_H
|
||||||
|
#define QJSON_SERIALIZER_H
|
||||||
|
|
||||||
|
#include "qjson_export.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QIODevice;
|
||||||
|
class QString;
|
||||||
|
class QVariant;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
/**
|
||||||
|
@brief How the indentation should work.
|
||||||
|
\verbatim
|
||||||
|
none (default) : { "foo" : 0, "foo1" : 1, "foo2" : [ { "foo3" : 3, "foo4" : 4 } ] }
|
||||||
|
|
||||||
|
compact : {"foo":0,"foo1":1,"foo2":[{"foo3":3,"foo4":4}]}
|
||||||
|
|
||||||
|
minimum : { "foo" : 0, "foo1" : 1, "foo2" : [
|
||||||
|
{ "foo3" : 3, "foo4" : 4 }
|
||||||
|
] }
|
||||||
|
|
||||||
|
medium : {
|
||||||
|
"foo" : 0, "foo1" : 1, "foo2" : [
|
||||||
|
{
|
||||||
|
"foo3" : 3, "foo4" : 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
full : {
|
||||||
|
"foo" : 0,
|
||||||
|
"foo1" : 1,
|
||||||
|
"foo2" : [
|
||||||
|
{
|
||||||
|
"foo3" : 3,
|
||||||
|
"foo4" : 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
\endverbatim
|
||||||
|
*/
|
||||||
|
enum IndentMode {
|
||||||
|
IndentNone,
|
||||||
|
IndentCompact,
|
||||||
|
IndentMinimum,
|
||||||
|
IndentMedium,
|
||||||
|
IndentFull
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief Main class used to convert QVariant objects to JSON data.
|
||||||
|
*
|
||||||
|
* QVariant objects are converted to a string containing the JSON data.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* QVariantList people;
|
||||||
|
*
|
||||||
|
* QVariantMap bob;
|
||||||
|
* bob.insert("Name", "Bob");
|
||||||
|
* bob.insert("Phonenumber", 123);
|
||||||
|
*
|
||||||
|
* QVariantMap alice;
|
||||||
|
* alice.insert("Name", "Alice");
|
||||||
|
* alice.insert("Phonenumber", 321);
|
||||||
|
*
|
||||||
|
* people << bob << alice;
|
||||||
|
*
|
||||||
|
* QJson::Serializer serializer;
|
||||||
|
* bool ok;
|
||||||
|
* QByteArray json = serializer.serialize(people, &ok);
|
||||||
|
*
|
||||||
|
* if (ok) {
|
||||||
|
* qDebug() << json;
|
||||||
|
* } else {
|
||||||
|
* qCritical() << "Something went wrong:" << serializer.errorMessage();
|
||||||
|
* }
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* The output will be:
|
||||||
|
*
|
||||||
|
* \code
|
||||||
|
* "[ { "Name" : "Bob", "Phonenumber" : 123 },
|
||||||
|
* { "Name" : "Alice", "Phonenumber" : 321 } ]"
|
||||||
|
* \endcode
|
||||||
|
*
|
||||||
|
* It's possible to tune the indentation level of the resulting string. \sa setIndentMode
|
||||||
|
*/
|
||||||
|
class QJSON_EXPORT Serializer {
|
||||||
|
public:
|
||||||
|
Serializer();
|
||||||
|
~Serializer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method generates a textual JSON representation and outputs it to the
|
||||||
|
* passed in I/O Device.
|
||||||
|
* @param variant The JSON document in its in-memory representation as generated by the
|
||||||
|
* parser.
|
||||||
|
* @param out Input output device
|
||||||
|
* @param ok if a conversion error occurs, *ok is set to false; otherwise *ok is set to true
|
||||||
|
*/
|
||||||
|
void serialize( const QVariant& variant, QIODevice* out, bool* ok);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a method provided for convenience. It turns the passed in in-memory
|
||||||
|
* representation of the JSON document into a textual one, which is returned.
|
||||||
|
* If the returned string is empty, the document was empty. If it was null, there
|
||||||
|
* was a parsing error.
|
||||||
|
*
|
||||||
|
* @param variant The JSON document in its in-memory representation as generated by the
|
||||||
|
* parser.
|
||||||
|
*
|
||||||
|
* \deprecated This method is going to be removed with the next major release of QJson.
|
||||||
|
*/
|
||||||
|
QByteArray serialize( const QVariant& variant);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a method provided for convenience. It turns the passed in in-memory
|
||||||
|
* representation of the JSON document into a textual one, which is returned.
|
||||||
|
* If the returned string is empty, the document was empty. If it was null, there
|
||||||
|
* was a parsing error.
|
||||||
|
*
|
||||||
|
* @param variant The JSON document in its in-memory representation as generated by the
|
||||||
|
* parser.
|
||||||
|
* @param ok if a conversion error occurs, *ok is set to false; otherwise *ok is set to true
|
||||||
|
*/
|
||||||
|
QByteArray serialize( const QVariant& variant, bool *ok);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow or disallow writing of NaN and/or Infinity (as an extension to QJson)
|
||||||
|
*/
|
||||||
|
void allowSpecialNumbers(bool allow);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is Nan and/or Infinity allowed?
|
||||||
|
*/
|
||||||
|
bool specialNumbersAllowed() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set output indentation mode as defined in QJson::IndentMode
|
||||||
|
*/
|
||||||
|
void setIndentMode(IndentMode mode = QJson::IndentNone);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set double precision used while converting Double
|
||||||
|
* \sa QByteArray::number
|
||||||
|
*/
|
||||||
|
void setDoublePrecision(int precision);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns one of the indentation modes defined in QJson::IndentMode
|
||||||
|
*/
|
||||||
|
IndentMode indentMode() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the error message
|
||||||
|
*/
|
||||||
|
QString errorMessage() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(Serializer)
|
||||||
|
class SerializerPrivate;
|
||||||
|
SerializerPrivate* const d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // QJSON_SERIALIZER_H
|
62
src/http/qjson/serializerrunnable.cpp
Normal file
62
src/http/qjson/serializerrunnable.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "serializerrunnable.h"
|
||||||
|
|
||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Flavio Castelli <flavio@castelli.name>
|
||||||
|
* 2009 Frank Osterfeld <osterfeld@kde.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "parserrunnable.h"
|
||||||
|
#include "serializer.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QVariant>
|
||||||
|
|
||||||
|
using namespace QJson;
|
||||||
|
|
||||||
|
class SerializerRunnable::Private
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QVariant json;
|
||||||
|
};
|
||||||
|
|
||||||
|
SerializerRunnable::SerializerRunnable(QObject* parent)
|
||||||
|
: QObject(parent),
|
||||||
|
QRunnable(),
|
||||||
|
d(new Private)
|
||||||
|
{
|
||||||
|
qRegisterMetaType<QVariant>("QVariant");
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializerRunnable::~SerializerRunnable()
|
||||||
|
{
|
||||||
|
delete d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializerRunnable::setJsonObject( const QVariant& json )
|
||||||
|
{
|
||||||
|
d->json = json;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SerializerRunnable::run()
|
||||||
|
{
|
||||||
|
Serializer serializer;
|
||||||
|
bool ok;
|
||||||
|
const QByteArray serialized = serializer.serialize( d->json, &ok);
|
||||||
|
emit parsingFinished( serialized, ok, serializer.errorMessage() );
|
||||||
|
}
|
71
src/http/qjson/serializerrunnable.h
Normal file
71
src/http/qjson/serializerrunnable.h
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/* This file is part of qjson
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Frank Osterfeld <osterfeld@kde.org>
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* This library 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
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this library; see the file COPYING.LIB. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SERIALIZERRUNNABLE_H
|
||||||
|
#define SERIALIZERRUNNABLE_H
|
||||||
|
|
||||||
|
#include "qjson_export.h"
|
||||||
|
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QRunnable>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QByteArray;
|
||||||
|
class QString;
|
||||||
|
class QVariant;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace QJson {
|
||||||
|
/**
|
||||||
|
* @brief Convenience class for converting JSON data to QVariant objects using a dedicated thread
|
||||||
|
*/
|
||||||
|
class QJSON_EXPORT SerializerRunnable : public QObject, public QRunnable
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit SerializerRunnable(QObject* parent = 0);
|
||||||
|
~SerializerRunnable();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the json object to serialize.
|
||||||
|
*
|
||||||
|
* @param json QVariant containing the json representation to be serialized
|
||||||
|
*/
|
||||||
|
void setJsonObject( const QVariant& json );
|
||||||
|
|
||||||
|
/* reimp */ void run();
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
/**
|
||||||
|
* This signal is emitted when the serialization process has been completed
|
||||||
|
* @param serialized contains the result of the serialization
|
||||||
|
* @param ok if a serialization error occurs ok is set to false, otherwise it's set to true.
|
||||||
|
* @param error_msg contains a string explaining the failure reason
|
||||||
|
**/
|
||||||
|
void parsingFinished(const QByteArray& serialized, bool ok, const QString& error_msg);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(SerializerRunnable)
|
||||||
|
class Private;
|
||||||
|
Private* const d;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SERIALIZERRUNNABLE_H
|
133
src/http/qjson/stack.hh
Normal file
133
src/http/qjson/stack.hh
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
/* A Bison parser, made by GNU Bison 2.7. */
|
||||||
|
|
||||||
|
/* Stack handling for Bison parsers in C++
|
||||||
|
|
||||||
|
Copyright (C) 2002-2012 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
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 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
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 <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
/* As a special exception, you may create a larger work that contains
|
||||||
|
part or all of the Bison parser skeleton and distribute that work
|
||||||
|
under terms of your choice, so long as that work isn't itself a
|
||||||
|
parser generator using the skeleton or a modified version thereof
|
||||||
|
as a parser skeleton. Alternatively, if you modify or redistribute
|
||||||
|
the parser skeleton itself, you may (at your option) remove this
|
||||||
|
special exception, which will cause the skeleton and the resulting
|
||||||
|
Bison output files to be licensed under the GNU General Public
|
||||||
|
License without this special exception.
|
||||||
|
|
||||||
|
This special exception was added by the Free Software Foundation in
|
||||||
|
version 2.2 of Bison. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
** \file stack.hh
|
||||||
|
** Define the yy::stack class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef YY_YY_STACK_HH_INCLUDED
|
||||||
|
# define YY_YY_STACK_HH_INCLUDED
|
||||||
|
|
||||||
|
# include <deque>
|
||||||
|
|
||||||
|
|
||||||
|
namespace yy {
|
||||||
|
/* Line 34 of stack.hh */
|
||||||
|
#line 47 "stack.hh"
|
||||||
|
template <class T, class S = std::deque<T> >
|
||||||
|
class stack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Hide our reversed order.
|
||||||
|
typedef typename S::reverse_iterator iterator;
|
||||||
|
typedef typename S::const_reverse_iterator const_iterator;
|
||||||
|
|
||||||
|
stack () : seq_ ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
stack (unsigned int n) : seq_ (n)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
T&
|
||||||
|
operator [] (unsigned int i)
|
||||||
|
{
|
||||||
|
return seq_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const T&
|
||||||
|
operator [] (unsigned int i) const
|
||||||
|
{
|
||||||
|
return seq_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
push (const T& t)
|
||||||
|
{
|
||||||
|
seq_.push_front (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
void
|
||||||
|
pop (unsigned int n = 1)
|
||||||
|
{
|
||||||
|
for (; n; --n)
|
||||||
|
seq_.pop_front ();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
unsigned int
|
||||||
|
height () const
|
||||||
|
{
|
||||||
|
return seq_.size ();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const_iterator begin () const { return seq_.rbegin (); }
|
||||||
|
inline const_iterator end () const { return seq_.rend (); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
S seq_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Present a slice of the top of a stack.
|
||||||
|
template <class T, class S = stack<T> >
|
||||||
|
class slice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
slice (const S& stack, unsigned int range)
|
||||||
|
: stack_ (stack)
|
||||||
|
, range_ (range)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline
|
||||||
|
const T&
|
||||||
|
operator [] (unsigned int i) const
|
||||||
|
{
|
||||||
|
return stack_[range_ - i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const S& stack_;
|
||||||
|
unsigned int range_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // yy
|
||||||
|
/* Line 116 of stack.hh */
|
||||||
|
#line 132 "stack.hh"
|
||||||
|
|
||||||
|
#endif /* !YY_YY_STACK_HH_INCLUDED */
|
Loading…
Reference in New Issue
Block a user