Support KeyPassHttp protocol.

Allows using passIfox (firefox) and Chromeipass (chrome).
This commit is contained in:
Francois Ferrand 2013-04-11 20:03:00 +02:00
parent 20f3f23576
commit ea992bc3e6
57 changed files with 17687 additions and 6 deletions

View File

@ -101,6 +101,9 @@ set(keepassx_SOURCES
gui/group/EditGroupWidget.cpp
gui/group/GroupModel.cpp
gui/group/GroupView.cpp
http/Protocol.cpp
http/Server.cpp
http/Service.cpp
keys/CompositeKey.cpp
keys/FileKey.cpp
keys/Key.h
@ -163,6 +166,9 @@ set(keepassx_MOC
gui/group/EditGroupWidget.h
gui/group/GroupModel.h
gui/group/GroupView.h
http/Protocol.h
http/Server.h
http/Service.h
keys/CompositeKey_p.h
streams/HashedBlockStream.h
streams/LayeredStream.h
@ -204,11 +210,17 @@ qt4_wrap_cpp(keepassx_SOURCES ${keepassx_MOC})
add_library(keepassx_core STATIC ${keepassx_SOURCES})
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})
target_link_libraries(${PROGNAME}
keepassx_core
qjson
qhttpserver
${QT_QTCORE_LIBRARY}
${QT_QTGUI_LIBRARY}
${QT_QTNETWORK_LIBRARY}
${GCRYPT_LIBRARIES}
${ZLIB_LIBRARIES})

View File

@ -89,6 +89,12 @@ Uuid Uuid::fromBase64(const QString& str)
return Uuid(data);
}
Uuid Uuid::fromHex(const QString& str)
{
QByteArray data = QByteArray::fromHex(str.toAscii());
return Uuid(data);
}
uint qHash(const Uuid& key)
{
return qHash(key.toByteArray());

View File

@ -37,6 +37,7 @@ public:
bool operator!=(const Uuid& other) const;
static const int Length;
static Uuid fromBase64(const QString& str);
static Uuid fromHex(const QString& str);
private:
QByteArray m_data;

View File

@ -20,13 +20,16 @@
#include "crypto/SymmetricCipherGcrypt.h"
#include "crypto/SymmetricCipherSalsa20.h"
SymmetricCipher::SymmetricCipher()
: m_backend(0)
{
}
SymmetricCipher::SymmetricCipher(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv)
: m_backend(createBackend(algo, mode, direction))
: m_backend(0)
{
m_backend->init();
m_backend->setKey(key);
m_backend->setIv(iv);
init(algo, mode, direction, key, iv);
}
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()
{
Q_ASSERT(m_backend);
m_backend->reset();
}
void SymmetricCipher::setIv(const QByteArray &iv)
{
Q_ASSERT(m_backend);
m_backend->setIv(iv);
}
int SymmetricCipher::blockSize() const
{
Q_ASSERT(m_backend);
return m_backend->blockSize();
}

View File

@ -47,31 +47,42 @@ public:
Encrypt
};
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 = QByteArray());
~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) {
Q_ASSERT(m_backend);
return m_backend->process(data);
}
inline void processInPlace(QByteArray& data) {
Q_ASSERT(m_backend);
m_backend->processInPlace(data);
}
inline void processInPlace(QByteArray& data, quint64 rounds) {
Q_ASSERT(rounds > 0);
Q_ASSERT(m_backend);
m_backend->processInPlace(data, rounds);
}
void reset();
void setIv(const QByteArray& iv);
int blockSize() const;
private:
static SymmetricCipherBackend* createBackend(SymmetricCipher::Algorithm algo, SymmetricCipher::Mode mode,
SymmetricCipher::Direction direction);
const QScopedPointer<SymmetricCipherBackend> m_backend;
QScopedPointer<SymmetricCipherBackend> m_backend;
Q_DISABLE_COPY(SymmetricCipher)
};

View File

@ -32,6 +32,7 @@
#include "gui/FileDialog.h"
#include "gui/entry/EntryView.h"
#include "gui/group/GroupView.h"
#include "http/Service.h"
DatabaseManagerStruct::DatabaseManagerStruct()
: dbWidget(Q_NULLPTR)
@ -53,6 +54,8 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
connect(this, SIGNAL(tabCloseRequested(int)), SLOT(closeDatabase(int)));
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
(new Service(this))->start();
}
DatabaseTabWidget::~DatabaseTabWidget()

477
src/http/Protocol.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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

View 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})

View 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.

View 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>

View 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

View 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.

View 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

File diff suppressed because it is too large Load Diff

View 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' ]
}
]
}

View 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

File diff suppressed because it is too large Load Diff

View 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;
}

View 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;
}

View 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

View 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()
{
}

View 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

View 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();
}

View 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

View 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();
}

View 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

View 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
View 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

View 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.

File diff suppressed because it is too large Load Diff

View 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 */

View 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);
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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

View 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
View 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
View 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
View 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
View 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

View 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);
}
}

View 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
View 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 */

View 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

View 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

View 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 );
}
}
}

View 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

View 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
View 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

View 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() );
}

View 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
View 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 */