From f8f8c81e6fd8993a9de830af0be1709955c28a5f Mon Sep 17 00:00:00 2001 From: special Date: Thu, 13 Sep 2007 14:55:21 +0000 Subject: Redesigned m_rpc_json to work properly in a modular environment, and added the beginnings of a framework-inspecific RPC interface for modules. Be warned, this WILL change some - this can be considered an alpha :P git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@8033 e03df62e-2008-0410-955e-edbf42e46eb7 --- src/modules/json.h | 544 ---------- src/modules/m_rpc_json.cpp | 2374 ++++++-------------------------------------- src/modules/m_rpctest.cpp | 68 ++ src/modules/rpc.h | 296 ++++++ 4 files changed, 688 insertions(+), 2594 deletions(-) delete mode 100644 src/modules/json.h create mode 100644 src/modules/m_rpctest.cpp create mode 100644 src/modules/rpc.h (limited to 'src/modules') diff --git a/src/modules/json.h b/src/modules/json.h deleted file mode 100644 index 7643d9713..000000000 --- a/src/modules/json.h +++ /dev/null @@ -1,544 +0,0 @@ -#ifndef JSON_H -#define JSON_H - -#include -#include -#include -#include -#include - -#if __GNUC__ >= 3 -# define maybe_expect(expr,value) __builtin_expect ((expr),(value)) -# define is_const(expr) __builtin_constant_p ((expr)) -#else -# define maybe_expect(expr,value) (expr) -# define is_const(expr) 0 -#endif - -#define expect_false(expr) maybe_expect ((expr) != 0, 0) -#define expect_true(expr) maybe_expect ((expr) != 0, 1) - -#ifdef __GNUC__ -# define CURFUNC __PRETTY_FUNCTION__ -#elif defined(__BORLANDC__) -# define CURFUNC __FUNC__ -#else -# define CURFUNC __FUNCTION__ -#endif - -namespace json -{ - class ValueIterator; - class ValueConstIterator; - - enum ValueType - { - nullValue = 0, - intValue, - uintValue, - realValue, - stringValue, - booleanValue, - arrayValue, - objectValue - }; - - class StaticString - { - public: - explicit StaticString (char const *czstring) - : str_ (czstring) - { - } - - operator char const *() const - { - return str_; - } - - char const *c_str() const - { - return str_; - } - - private: - char const *str_; - }; - - class Value - { - friend class ValueIteratorBase; - public: - typedef std::vector Members; - typedef ValueIterator iterator; - typedef ValueConstIterator const_iterator; - typedef unsigned ArrayIndex; - - static const Value null; - static const int minInt; - static const int maxInt; - static const unsigned maxUInt; - - private: - class CZString - { - public: - enum DuplicationPolicy - { - noDuplication = 0, - duplicate, - duplicateOnCopy - }; - CZString (int index); - CZString (char const *cstr, DuplicationPolicy allocate); - CZString (const CZString &other); - ~CZString (); - CZString &operator = (const CZString &other); - bool operator < (const CZString &other) const; - bool operator == (const CZString &other) const; - int index () const; - char const *c_str () const; - bool isStaticString () const; - private: - void swap (CZString &other); - char const *cstr_; - int index_; - }; - - public: - typedef std::map ObjectValues; - - public: - Value (ValueType type = nullValue); - Value (int value); - Value (unsigned value); - Value (double value); - Value (char const *value); - Value (const StaticString &value); - Value (const std::string &value); - Value (bool value); - Value (const Value &other); - ~Value (); - - Value &operator = (const Value &other); - - void swap (Value &other); - - ValueType type () const; - - bool operator < (const Value &other) const; - bool operator <= (const Value &other) const; - bool operator >= (const Value &other) const; - bool operator > (const Value &other) const; - - bool operator == (const Value &other) const; - bool operator != (const Value &other) const; - - operator char const * () const; - operator std::string () const; - operator int () const; - operator unsigned () const; - operator double () const; - operator bool () const; - - bool isNull () const; - bool isBool () const; - bool isInt () const; - bool isUInt () const; - bool isIntegral () const; - bool isDouble () const; - bool isNumeric () const; - bool isString () const; - bool isArray () const; - bool isObject () const; - - bool isConvertibleTo (ValueType other) const; - - unsigned size () const; - - bool empty () const; - - bool operator ! () const; - - void clear (); - - void resize (unsigned size); - - Value &operator [] (int index); - Value &operator [] (unsigned index); - const Value &operator [] (int index) const; - const Value &operator [] (unsigned index) const; - Value get (int index, const Value &defaultValue) const; - Value get (unsigned index, const Value &defaultValue) const; - bool isValidIndex (int index) const; - bool isValidIndex (unsigned index) const; - - Value &append (const Value &value); - - Value &operator [] (char const *key); - const Value &operator [] (char const *key) const; - Value &operator [] (const std::string &key); - const Value &operator [] (const std::string &key) const; - Value &operator [] (const StaticString &key); - Value get (char const *key, const Value &defaultValue) const; - Value get (const std::string &key, const Value &defaultValue) const; - Value removeMember (char const* key); - Value removeMember (const std::string &key); - - bool isMember (char const *key) const; - bool isMember (const std::string &key) const; - - Members getMemberNames () const; - - const_iterator begin () const; - const_iterator end () const; - - iterator begin (); - iterator end (); - - private: - Value &resolveReference (char const *key, bool isStatic); - - struct MemberNamesTransform - { - typedef char const *result_type; - char const *operator () (const CZString &name) const - { - return name.c_str (); - } - }; - - union ValueHolder - { - int int_; - unsigned uint_; - double real_; - bool bool_; - char *string_; - ObjectValues *map_; - } value_; - - ValueType type_ : 8; - int allocated_ : 1; - }; - - - class ValueAllocator - { - public: - enum { unknown = (unsigned)-1 }; - - virtual ~ValueAllocator (); - - virtual char *makeMemberName (char const *memberName) = 0; - virtual void releaseMemberName (char *memberName) = 0; - virtual char *duplicateStringValue (char const *value, - unsigned length = unknown) = 0; - virtual void releaseStringValue (char *value) = 0; - }; - - class ValueIteratorBase - { - public: - typedef unsigned size_t; - typedef int difference_type; - typedef ValueIteratorBase SelfType; - - ValueIteratorBase (); - explicit ValueIteratorBase (const Value::ObjectValues::iterator ¤t); - - bool operator == (const SelfType &other) const - { - return isEqual (other); - } - - bool operator != (const SelfType &other) const - { - return !isEqual (other); - } - - difference_type operator - (const SelfType &other) const - { - return computeDistance (other); - } - - Value key () const; - - unsigned index () const; - - char const *memberName () const; - - protected: - Value &deref () const; - - void increment (); - - void decrement (); - - difference_type computeDistance (const SelfType &other) const; - - bool isEqual (const SelfType &other) const; - - void copy (const SelfType &other); - - private: - Value::ObjectValues::iterator current_; - }; - - class ValueConstIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned size_t; - typedef int difference_type; - typedef const Value &reference; - typedef const Value *pointer; - typedef ValueConstIterator SelfType; - - ValueConstIterator (); - private: - explicit ValueConstIterator (const Value::ObjectValues::iterator ¤t); - public: - SelfType &operator = (const ValueIteratorBase &other); - - SelfType operator ++ (int) - { - SelfType temp (*this); - ++*this; - return temp; - } - - SelfType operator -- (int) - { - SelfType temp (*this); - --*this; - return temp; - } - - SelfType &operator -- () - { - decrement (); - return *this; - } - - SelfType &operator ++ () - { - increment (); - return *this; - } - - reference operator * () const - { - return deref (); - } - }; - - - class ValueIterator : public ValueIteratorBase - { - friend class Value; - public: - typedef unsigned size_t; - typedef int difference_type; - typedef Value &reference; - typedef Value *pointer; - typedef ValueIterator SelfType; - - ValueIterator (); - ValueIterator (const ValueConstIterator &other); - ValueIterator (const ValueIterator &other); - - private: - explicit ValueIterator (const Value::ObjectValues::iterator ¤t); - - public: - - SelfType &operator = (const SelfType &other); - - SelfType operator ++ (int) - { - SelfType temp (*this); - ++*this; - return temp; - } - - SelfType operator -- (int) - { - SelfType temp (*this); - --*this; - return temp; - } - - SelfType &operator -- () - { - decrement (); - return *this; - } - - SelfType &operator ++ () - { - increment (); - return *this; - } - - reference operator * () const - { - return deref (); - } - }; -} // namespace json - -namespace json -{ - class Value; - - class Reader - { - public: - typedef char const *Location; - - Reader (); - - bool parse (const std::string &document, Value &root); - bool parse (char const *beginDoc, char const *endDOc, Value &root); - bool parse (std::istream &, Value &root); - - std::string error_msgs () const; - - private: - enum TokenType - { - tokenEndOfStream = 0, - tokenObjectBegin, - tokenObjectEnd, - tokenArrayBegin, - tokenArrayEnd, - tokenString, - tokenNumber, - tokenTrue, - tokenFalse, - tokenNull, - tokenArraySeparator, - tokenMemberSeparator, - tokenComment, - tokenError - }; - - class Token - { - public: - TokenType type_; - Location start_; - Location end_; - }; - - class ErrorInfo - { - public: - Token token_; - std::string message_; - Location extra_; - }; - - typedef std::deque Errors; - - bool expectToken (TokenType type, Token &token, char const *message); - bool readToken (Token &token); - void skipSpaces (); - bool match (Location pattern, - int patternLength); - bool readString (); - void readNumber (); - bool readValue (); - bool readObject (); - bool readArray (); - bool decodeNumber (Token &token); - bool decodeString (Token &token); - bool decodeString (Token &token, std::string &decoded); - bool decodeDouble (Token &token); - bool decodeUnicodeEscapeSequence (Token &token, - Location ¤t, - Location end, - unsigned &unicode); - bool addError (const std::string &message, - Token &token, - Location extra = 0); - bool recoverFromError (TokenType skipUntilToken); - bool addErrorAndRecover (const std::string &message, - Token &token, - TokenType skipUntilToken); - void skipUntilSpace (); - Value ¤tValue (); - char getNextChar (); - void getLocationLineAndColumn (Location location, - int &line, - int &column) const; - std::string getLocationLineAndColumn (Location location) const; - - typedef std::stack Nodes; - Nodes nodes_; - Errors errors_; - std::string document_; - Location begin_; - Location end_; - Location current_; - Location lastValueEnd_; - Value *lastValue_; - }; - - std::istream& operator >> (std::istream&, Value&); - -} // namespace json - -namespace json -{ - class Value; - - class Writer - { - public: - Writer () { } - ~Writer () { } - - public: - std::string write (const Value &root); - - private: - void writeValue (const Value &value); - - std::string document_; - }; - - std::string valueToString (int value); - std::string valueToString (unsigned value); - std::string valueToString (double value); - std::string valueToString (bool value); - std::string valueToQuotedString (char const *value); -} // namespace json - -/* - * JSON-RPC implementation in C++ - */ - -namespace json -{ - namespace rpc - { - typedef void (Module::*method) (HTTPRequest *http, Value &request, Value &response); - - struct mfp - { - Module const *mod; - method mth; - }; - - typedef std::map method_map; - extern method_map methods; - - void add_method (char *name, Module const *mod, method mth); - void service (HTTPRequest *http, Value &request, Value &response); - void process (HTTPRequest *http, std::string &response, char const *request); - } -} - -#endif // JSON_H diff --git a/src/modules/m_rpc_json.cpp b/src/modules/m_rpc_json.cpp index 8de1b8a29..d2886cf74 100644 --- a/src/modules/m_rpc_json.cpp +++ b/src/modules/m_rpc_json.cpp @@ -6,2095 +6,369 @@ * See: http://www.inspircd.org/wiki/index.php/Credits * * This program is free but copyrighted software; see - * the file COPYING for details. + * the file COPYING for details. * * --------------------------------------------------- */ -#include -#include -#include -#include -#include - #include "inspircd.h" -#include "inspsocket.h" +#include "users.h" +#include "channels.h" +#include "modules.h" #include "httpd.h" -#include "json.h" +#include "rpc.h" +#include -/* $ModDesc: Provides a JSON-RPC interface for modules using m_httpd.so */ +/* $ModDesc: Encode and decode JSON-RPC requests for modules */ +/* $ModDep: httpd.h rpc.h */ -class ModuleRpcJson : public Module +class JsonException : public std::exception { - void MthModuleVersion (HTTPRequest *http, json::Value &request, json::Value &response) + private: + std::string _what; + public: + JsonException(const std::string &what) + : _what(what) { - Version v = this->GetVersion(); - std::string result = ConvToStr(v.Major) + "." + ConvToStr(v.Minor) + "." + ConvToStr(v.Revision) + "." + ConvToStr(v.Build); - response["result"] = result; } - - void system_list_methods (HTTPRequest *http, json::Value &request, json::Value &response) + + virtual ~JsonException() throw() { } + + virtual const char *what() const throw() { - unsigned i = 0; - json::Value method_list (json::arrayValue); - - json::rpc::method_map::iterator it; - for (it = json::rpc::methods.begin(); it != json::rpc::methods.end(); ++it) - { - method_list[i] = json::Value (it->first); - i++; - } - - response["result"] = method_list; + return _what.c_str(); } +}; +class ModuleRpcJson : public Module +{ + private: + public: - ModuleRpcJson(InspIRCd* Me) : Module(Me) + ModuleRpcJson(InspIRCd *Me) + : Module::Module(Me) { - ServerInstance->Modules->PublishInterface("JSON-RPC", this); - json::rpc::add_method ("system.listMethods", (Module *)this, (void (Module::*)(HTTPRequest*, json::Value&, json::Value&))&ModuleRpcJson::system_list_methods); - json::rpc::add_method ("ircd.moduleVersion", (Module *)this, (void (Module::*)(HTTPRequest*, json::Value&, json::Value&))&ModuleRpcJson::MthModuleVersion); + ServerInstance->Modules->PublishInterface("RPC", this); } - - void OnEvent(Event* event) + + virtual ~ModuleRpcJson() + { + ServerInstance->Modules->UnpublishInterface("RPC", this); + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION); + } + + void Implements(char *List) + { + List[I_OnEvent] = 1; + } + + virtual void OnEvent(Event *event) { - std::stringstream data(""); - if (event->GetEventID() == "httpd_url") { - HTTPRequest* http = (HTTPRequest*)event->GetData(); - - if (http->GetURI() == "/jsonrpc" && http->GetType() == "POST") + HTTPRequest *req = (HTTPRequest*) event->GetData(); + + if ((req->GetURI() == "/rpc/json") || (req->GetURI() == "/rpc/json/")) { + std::stringstream data; + + RPCValue *reqobj = NULL; + try { - std::string response_text; - json::rpc::process (http, response_text, http->GetPostData().c_str()); - data << response_text; + reqobj = this->JSONParse(req->GetPostData()); + + if (!reqobj || (reqobj->GetType() != RPCObject)) + throw JsonException("RPC requests must be in the form of a single object"); + + RPCValue *method = reqobj->GetObject("method"); + if (!method || method->GetType() != RPCString) + throw JsonException("RPC requests must have a 'method' string field"); + + RPCValue *params = reqobj->GetObject("params"); + if (!params || params->GetType() != RPCArray) + throw JsonException("RPC requests must have a 'params' array field"); + + RPCRequest modreq("json", method->GetString(), params); + Event mev((char*) &modreq, this, "RPCMethod"); + mev.Send(ServerInstance); + + if (!modreq.claimed) + throw JsonException("Unrecognized method"); + + if (!modreq.error.empty()) + { + data << "{\"result\":null,\"error\":\"" << modreq.error << "\""; + } + else + { + data << "{\"result\":"; + this->JSONSerialize(modreq.result, data); + data << ",\"error\":null"; + } + + if (reqobj->GetObject("id")) + { + data << ",\"id\":"; + this->JSONSerialize(reqobj->GetObject("id"), data); + } + data << "}"; + + delete reqobj; + reqobj = NULL; } - catch (std::runtime_error &) + catch (std::exception &e) { - data << "{ \"result\": \"JSON Fault\", \"error\": \"Invalid RPC call\", \"id\": 1}"; + if (reqobj) + delete reqobj; + data << "{\"result\":null,\"error\":\"" << e.what() << "\"}"; } - - /* Send the document back to m_httpd */ - HTTPDocument response(http->sock, &data, 200); + + HTTPDocument response(req->sock, &data, 200); response.headers.SetHeader("X-Powered-By", "m_rpc_json.so"); - response.headers.SetHeader("Content-Type", "application/json; charset=iso-8859-1"); + response.headers.SetHeader("Content-Type", "application/json"); response.headers.SetHeader("Connection", "Keep-Alive"); - Request req((char*)&response, (Module*)this, event->GetSource()); - req.Send(); + + Request rreq((char*) &response, (Module*) this, event->GetSource()); + rreq.Send(); } } } - - void Implements(char* List) + + void AttachToParent(RPCValue *parent, RPCValue *child, const std::string &key = "") { - List[I_OnEvent] = 1; + if (!parent || !child) + return; + + if (parent->GetType() == RPCArray) + parent->ArrayAdd(child); + else if (parent->GetType() == RPCObject) + parent->ObjectAdd(key, child); + else + throw JsonException("Cannot add a value to a non-container"); } - - virtual ~ModuleRpcJson() + + void AttachToParentReset(RPCValue *parent, RPCValue *&child, std::string &key) { - ServerInstance->Modules->UnpublishInterface("JSON-RPC", this); + AttachToParent(parent, child, key); + child = NULL; + key.clear(); } - - virtual Version GetVersion() + + RPCValue *JSONParse(const std::string &data) { - return Version(0, 1, 0, 0, VF_VENDOR, API_VERSION); - } -}; - -namespace json -{ - ValueIteratorBase::ValueIteratorBase () - { - } - - - ValueIteratorBase::ValueIteratorBase (const Value::ObjectValues::iterator ¤t) - : current_ (current) - { - } - - Value & - ValueIteratorBase::deref () const - { - return current_->second; - } - - - void - ValueIteratorBase::increment () - { - ++current_; - } - - - void - ValueIteratorBase::decrement () - { - --current_; - } - - - ValueIteratorBase::difference_type - ValueIteratorBase::computeDistance (const SelfType &other) const - { - return difference_type (std::distance (current_, other.current_)); - } - - - bool - ValueIteratorBase::isEqual (const SelfType &other) const - { - return current_ == other.current_; - } - - - void - ValueIteratorBase::copy (const SelfType &other) - { - current_ = other.current_; - } - - - Value - ValueIteratorBase::key () const - { - const Value::CZString czstring = (*current_).first; - if (czstring.c_str ()) - { - if (czstring.isStaticString ()) - return Value (StaticString (czstring.c_str ())); - return Value (czstring.c_str ()); - } - return Value (czstring.index ()); - } - - - unsigned - ValueIteratorBase::index () const - { - const Value::CZString czstring = (*current_).first; - if (!czstring.c_str ()) - return czstring.index (); - return unsigned (-1); - } - - - char const * - ValueIteratorBase::memberName () const - { - char const *name = (*current_).first.c_str (); - return name ? name : ""; - } - - - ValueConstIterator::ValueConstIterator () - { - } - - - ValueConstIterator::ValueConstIterator (const Value::ObjectValues::iterator ¤t) - : ValueIteratorBase (current) - { - } - - ValueConstIterator & - ValueConstIterator::operator = (const ValueIteratorBase &other) - { - copy (other); - return *this; - } - - - ValueIterator::ValueIterator () - { - } - - - ValueIterator::ValueIterator (const Value::ObjectValues::iterator ¤t) - : ValueIteratorBase (current) - { - } - - ValueIterator::ValueIterator (const ValueConstIterator &other) - : ValueIteratorBase (other) - { - } - - ValueIterator::ValueIterator (const ValueIterator &other) - : ValueIteratorBase (other) - { - } - - ValueIterator & - ValueIterator::operator = (const SelfType &other) - { - copy (other); - return *this; - } -} - -namespace json -{ - inline bool - in (char c, char c1, char c2, char c3, char c4) - { - return c == c1 || c == c2 || c == c3 || c == c4; - } - - inline bool - in (char c, char c1, char c2, char c3, char c4, char c5) - { - return c == c1 || c == c2 || c == c3 || c == c4 || c == c5; - } - - - Reader::Reader () - { - } - - bool - Reader::parse (const std::string &document, - Value &root) - { - document_ = document; - char const *begin = document_.c_str (); - char const *end = begin + document_.length (); - return parse (begin, end, root); - } - - bool - Reader::parse (std::istream& sin, - Value &root) - { - std::string doc; - std::getline (sin, doc, (char)EOF); - return parse (doc, root); - } - - bool - Reader::parse (char const *beginDoc, char const *endDOc, - Value &root) - { - begin_ = beginDoc; - end_ = endDOc; - current_ = begin_; - lastValueEnd_ = 0; - lastValue_ = 0; - errors_.clear (); - while (!nodes_.empty ()) - nodes_.pop (); - nodes_.push (&root); - - bool successful = readValue (); - return successful; - } - - - bool - Reader::readValue () - { - Token token; - do - readToken (token); - while (token.type_ == tokenComment); - bool successful = true; - - switch (token.type_) - { - case tokenObjectBegin: - successful = readObject (); - break; - case tokenArrayBegin: - successful = readArray (); - break; - case tokenNumber: - successful = decodeNumber (token); - break; - case tokenString: - successful = decodeString (token); - break; - case tokenTrue: - currentValue () = true; - break; - case tokenFalse: - currentValue () = false; - break; - case tokenNull: - currentValue () = Value (); - break; - default: - return addError ("Syntax error: value, object or array expected.", token); - } - - return successful; - } - - - bool - Reader::expectToken (TokenType type, Token &token, char const *message) - { - readToken (token); - if (token.type_ != type) - return addError (message, token); - return true; - } - - - bool - Reader::readToken (Token &token) - { - skipSpaces (); - token.start_ = current_; - char c = getNextChar (); - bool ok = true; - switch (c) - { - case '{': - token.type_ = tokenObjectBegin; - break; - case '}': - token.type_ = tokenObjectEnd; - break; - case '[': - token.type_ = tokenArrayBegin; - break; - case ']': - token.type_ = tokenArrayEnd; - break; - case '"': - token.type_ = tokenString; - ok = readString (); - break; -#if 0 -#ifdef __GNUC__ - case '0'...'9': -#endif -#else - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - case '8': case '9': -#endif - case '-': - token.type_ = tokenNumber; - readNumber (); - break; - case 't': - token.type_ = tokenTrue; - ok = match ("rue", 3); - break; - case 'f': - token.type_ = tokenFalse; - ok = match ("alse", 4); - break; - case 'n': - token.type_ = tokenNull; - ok = match ("ull", 3); - break; - case ',': - token.type_ = tokenArraySeparator; - break; - case ':': - token.type_ = tokenMemberSeparator; - break; - case 0: - token.type_ = tokenEndOfStream; - break; - default: - ok = false; - break; - } - if (!ok) - token.type_ = tokenError; - token.end_ = current_; - return true; - } - - - void - Reader::skipSpaces () - { - while (current_ != end_) - { - char c = *current_; - if (c == ' ' || c == '\t' || c == '\r' || c == '\n') - ++current_; - else - break; - } - } - - - bool - Reader::match (Location pattern, int patternLength) - { - if (end_ - current_ < patternLength) - return false; - int index = patternLength; - while (index--) - if (current_[index] != pattern[index]) - return false; - current_ += patternLength; - return true; - } - - - void - Reader::readNumber () - { - while (current_ != end_) - { - if (!(*current_ >= '0' && *current_ <= '9') && - !in (*current_, '.', 'e', 'E', '+', '-')) - break; - ++current_; - } - } - - bool - Reader::readString () - { - char c = 0; - while (current_ != end_) - { - c = getNextChar (); - if (c == '\\') - getNextChar (); - else if (c == '"') - break; - } - return c == '"'; - } - - - bool - Reader::readObject () - { - Token tokenName; - std::string name; - currentValue () = Value (objectValue); - while (readToken (tokenName)) - { - if (tokenName.type_ == tokenObjectEnd && name.empty ()) // empty object - return true; - if (tokenName.type_ != tokenString) - break; - - name = ""; - if (!decodeString (tokenName, name)) - return recoverFromError (tokenObjectEnd); - - Token colon; - if (!readToken (colon) || colon.type_ != tokenMemberSeparator) - { - return addErrorAndRecover ("Missing ':' after object member name", - colon, - tokenObjectEnd); - } - Value &value = currentValue ()[ name ]; - nodes_.push (&value); - bool ok = readValue (); - nodes_.pop (); - if (!ok) // error already set - return recoverFromError (tokenObjectEnd); - - Token comma; - if (!readToken (comma) - || (comma.type_ != tokenObjectEnd && - comma.type_ != tokenArraySeparator)) - { - return addErrorAndRecover ("Missing ',' or '}' in object declaration", - comma, - tokenObjectEnd); - } - if (comma.type_ == tokenObjectEnd) - return true; - } - return addErrorAndRecover ("Missing '}' or object member name", - tokenName, - tokenObjectEnd); - } - - - bool - Reader::readArray () - { - currentValue () = Value (arrayValue); - skipSpaces (); - if (*current_ == ']') // empty array - { - Token endArray; - readToken (endArray); - return true; - } - int index = 0; - while (true) - { - Value &value = currentValue ()[ index++ ]; - nodes_.push (&value); - bool ok = readValue (); - nodes_.pop (); - if (!ok) // error already set - return recoverFromError (tokenArrayEnd); - - Token token; - if (!readToken (token) - || (token.type_ != tokenArraySeparator && - token.type_ != tokenArrayEnd)) - { - return addErrorAndRecover ("Missing ',' or ']' in array declaration", - token, - tokenArrayEnd); - } - if (token.type_ == tokenArrayEnd) - break; - } - return true; - } - - - bool - Reader::decodeNumber (Token &token) - { - bool isDouble = false; - for (Location inspect = token.start_; inspect != token.end_; ++inspect) - { - isDouble = isDouble - || in (*inspect, '.', 'e', 'E', '+') - || (*inspect == '-' && inspect != token.start_); - } - if (isDouble) - return decodeDouble (token); - Location current = token.start_; - bool isNegative = *current == '-'; - if (isNegative) - ++current; - unsigned threshold = (isNegative ? unsigned (-Value::minInt) - : Value::maxUInt) / 10; - unsigned value = 0; - while (current < token.end_) - { - char c = *current++; - if (c < '0' || c > '9') - return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token); - if (value >= threshold) - return decodeDouble (token); - value = value * 10 + unsigned (c - '0'); - } - if (isNegative) - currentValue () = -int (value); - else if (value <= unsigned (Value::maxInt)) - currentValue () = int (value); - else - currentValue () = value; - return true; - } - - - bool - Reader::decodeDouble (Token &token) - { - double value = 0; - const int bufferSize = 32; - int count; - int length = int (token.end_ - token.start_); - if (length <= bufferSize) - { - char buffer[bufferSize]; - memcpy (buffer, token.start_, length); - buffer[length] = 0; - count = sscanf (buffer, "%lf", &value); - } - else - { - std::string buffer (token.start_, token.end_); - count = sscanf (buffer.c_str (), "%lf", &value); - } - - if (count != 1) - return addError ("'" + std::string (token.start_, token.end_) + "' is not a number.", token); - currentValue () = value; - return true; - } - - - bool - Reader::decodeString (Token &token) - { - std::string decoded; - if (!decodeString (token, decoded)) - return false; - currentValue () = decoded; - return true; - } - - - bool - Reader::decodeString (Token &token, std::string &decoded) - { - Location current = token.start_ + 1; // skip '"' - Location end = token.end_ - 1; // do not include '"' - decoded.reserve (long (end - current)); - - while (current != end) - { - char c = *current++; - if (expect_false (c == '"')) - break; - else if (expect_false (c == '\\')) - { - if (expect_false (current == end)) - return addError ("Empty escape sequence in string", token, current); - char escape = *current++; - switch (escape) - { - case '"': - case '/': - case '\\': decoded += escape; break; - - case 'b': decoded += '\010'; break; - case 't': decoded += '\011'; break; - case 'n': decoded += '\012'; break; - case 'f': decoded += '\014'; break; - case 'r': decoded += '\015'; break; - case 'u': + bool pisobject = false; + bool instring = false; + std::string stmp; + std::string vkey; + std::string pvkey; + RPCValue *aparent = NULL; + RPCValue *value = NULL; + + for (std::string::const_iterator i = data.begin(); i != data.end(); i++) { - unsigned unicode; - if (!decodeUnicodeEscapeSequence (token, current, end, unicode)) - return false; - // @todo encode unicode as utf8. - // @todo remember to alter the writer too. + if (instring) + { + // TODO escape sequences + if (*i == '"') + { + instring = false; + + if (pisobject && vkey.empty()) + vkey = stmp; + else + value = new RPCValue(stmp); + + stmp.clear(); + } + else + stmp += *i; + + continue; + } + + if ((*i == ' ') || (*i == '\t') || (*i == '\r') || (*i == '\n')) + continue; + + if (*i == '{') + { + // Begin object + if ((value) || (pisobject && vkey.empty())) + throw JsonException("Unexpected begin object token ('{')"); + + RPCValue *nobj = new RPCValue(RPCObject, aparent); + aparent = nobj; + pvkey = vkey; + vkey.clear(); + pisobject = true; + } + else if (*i == '}') + { + // End object + if ((!aparent) || (!pisobject) || (!vkey.empty() && !value)) + throw JsonException("Unexpected end object token ('}')"); + + // End value + if (value) + AttachToParentReset(aparent, value, vkey); + + if (!aparent->parent) + return aparent; + + value = aparent; + aparent = aparent->parent; + vkey = pvkey; + pvkey.clear(); + pisobject = (aparent->GetType() == RPCObject); + } + else if (*i == '"') + { + // Begin string + if (value) + throw JsonException("Unexpected begin string token ('\"')"); + + instring = true; + } + else if (*i == ':') + { + if ((!aparent) || (!pisobject) || (vkey.empty()) || (value)) + throw JsonException("Unexpected object value token (':')"); + } + else if (*i == ',') + { + if ((!aparent) || (!value) || ((pisobject) && (vkey.empty()))) + throw JsonException("Unexpected value seperator token (',')"); + + AttachToParentReset(aparent, value, vkey); + } + else if (*i == '[') + { + // Begin array + if ((value) || (pisobject && vkey.empty())) + throw JsonException("Unexpected begin array token ('[')"); + + RPCValue *nar = new RPCValue(RPCArray, aparent); + aparent = nar; + pvkey = vkey; + vkey.clear(); + pisobject = false; + } + else if (*i == ']') + { + // End array (also an end value delimiter) + if (!aparent || pisobject) + throw JsonException("Unexpected end array token (']')"); + + if (value) + AttachToParentReset(aparent, value, vkey); + + if (!aparent->parent) + return aparent; + + value = aparent; + aparent = aparent->parent; + vkey = pvkey; + pvkey.clear(); + pisobject = (aparent->GetType() == RPCObject); + } + else + { + // Numbers, false, null, and true fall under this heading. + if ((*i == 't') && ((i + 3) < data.end()) && (*(i + 1) == 'r') && (*(i + 2) == 'u') && (*(i + 3) == 'e')) + { + value = new RPCValue(true); + i += 3; + } + else if ((*i == 'f') && ((i + 4) < data.end()) && (*(i + 1) == 'a') && (*(i + 2) == 'l') && (*(i + 3) == 's') && (*(i + 4) == 'e')) + { + value = new RPCValue(false); + i += 4; + } + else if ((*i == 'n') && ((i + 3) < data.end()) && (*(i + 1) == 'u') && (*(i + 2) == 'l') && (*(i + 3) == 'l')) + { + value = new RPCValue(); + i += 3; + } + else if ((*i == '-') || (*i == '+') || (*i == '.') || ((*i >= '0') && (*i <= '9'))) + { + std::string ds = std::string(i, data.end()); + char *eds = NULL; + + errno = 0; + double v = strtod(ds.c_str(), &eds); + + if (errno != 0) + throw JsonException("Error parsing numeric value"); + + value = new RPCValue(v); + + i += eds - ds.c_str() - 1; + } + else + throw JsonException("Unknown data in value portion"); + } } - break; - default: - return addError ("Bad escape sequence in string", token, current); - } - } - else - { - decoded += c; - } - } - - return true; - } - - - bool - Reader::decodeUnicodeEscapeSequence (Token &token, - Location ¤t, - Location end, - unsigned &unicode) - { - if (end - current < 4) - return addError ("Bad unicode escape sequence in string: four digits expected.", token, current); - unicode = 0; - for (int index = 0; index < 4; ++index) - { - char c = *current++; - unicode *= 16; - if (c >= '0' && c <= '9') - unicode += c - '0'; - else if (c >= 'a' && c <= 'f') - unicode += c - 'a' + 10; - else if (c >= 'A' && c <= 'F') - unicode += c - 'A' + 10; - else - return addError ("Bad unicode escape sequence in string: hexadecimal digit expected.", token, current); - } - return true; - } - - - bool - Reader::addError (const std::string &message, - Token &token, - Location extra) - { - ErrorInfo info; - info.token_ = token; - info.message_ = message; - info.extra_ = extra; - errors_.push_back (info); - return false; - } - - - bool - Reader::recoverFromError (TokenType skipUntilToken) - { - int errorCount = int (errors_.size ()); - Token skip; - while (true) - { - if (!readToken (skip)) - errors_.resize (errorCount); // discard errors caused by recovery - if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream) - break; - } - errors_.resize (errorCount); - return false; - } - - - bool - Reader::addErrorAndRecover (const std::string &message, - Token &token, - TokenType skipUntilToken) - { - addError (message, token); - return recoverFromError (skipUntilToken); - } - - - Value & - Reader::currentValue () - { - return *(nodes_.top ()); - } - - - char - Reader::getNextChar () - { - if (current_ == end_) - return 0; - return *current_++; - } - - - void - Reader::getLocationLineAndColumn (Location location, - int &line, - int &column) const - { - Location current = begin_; - Location lastLineStart = current; - line = 0; - while (current < location && current != end_) - { - char c = *current++; - if (c == '\r') - { - if (*current == '\n') - ++current; - lastLineStart = current; - ++line; - } - else if (c == '\n') - { - lastLineStart = current; - ++line; - } - } - // column & line start at 1 - column = int (location - lastLineStart) + 1; - ++line; - } - - - std::string - Reader::getLocationLineAndColumn (Location location) const - { - int line, column; - getLocationLineAndColumn (location, line, column); - char buffer[18+16+16+1]; - sprintf (buffer, "Line %d, Column %d", line, column); - return buffer; - } - - - std::string - Reader::error_msgs () const - { - std::string formattedMessage; - for (Errors::const_iterator itError = errors_.begin (); - itError != errors_.end (); - ++itError) - { - const ErrorInfo &error = *itError; - formattedMessage += "* " + getLocationLineAndColumn (error.token_.start_) + "\n"; - formattedMessage += " " + error.message_ + "\n"; - if (error.extra_) - formattedMessage += "See " + getLocationLineAndColumn (error.extra_) + " for detail.\n"; - } - return formattedMessage; - } -} // namespace json - -namespace json -{ - void - unreachable_internal (char const *file, int line, char const *function) - { - char buf[1024]; - snprintf (buf, 1024, "%s (%d) [%s] critical: Unreachable line reached.", - file, line, function); - - throw std::runtime_error (buf); - } - - void - throw_unless_internal (char const *file, int line, char const *function, char const *condition) - { - char buf[1024]; - snprintf (buf, 1024, "%s (%d) [%s] critical: Assertion `%s' failed.", - file, line, function, condition); - - throw std::runtime_error (buf); - } - - void - throw_msg_unless_internal (char const *file, int line, char const *function, char const *message) - { - char buf[1024]; - snprintf (buf, 1024, "%s (%d) [%s] critical: %s.", - file, line, function, message); - - throw std::runtime_error (buf); - } - -#define throw_unreachable unreachable_internal (__FILE__, __LINE__, CURFUNC) -#define throw_unless(condition) if (!expect_false (condition)) throw_unless_internal (__FILE__, __LINE__, CURFUNC, #condition) -#define throw_msg_unless(condition, message) if (!expect_false (condition)) throw_msg_unless_internal (__FILE__, __LINE__, CURFUNC, message) - - const Value Value::null; - const int Value::minInt = int (~ (unsigned (-1)/2)); - const int Value::maxInt = int (unsigned (-1)/2); - const unsigned Value::maxUInt = unsigned (-1); - - ValueAllocator::~ValueAllocator () - { - } - - class DefaultValueAllocator : public ValueAllocator - { - public: - virtual ~DefaultValueAllocator () - { - } - - virtual char *makeMemberName (char const *memberName) - { - return duplicateStringValue (memberName); - } - - virtual void releaseMemberName (char *memberName) - { - releaseStringValue (memberName); - } - - virtual char *duplicateStringValue (char const *value, unsigned length = unknown) - { - //@todo invesgate this old optimization -#if 0 - if (!value || value[0] == 0) - return 0; -#endif - - if (length == unknown) - length = (unsigned)strlen (value); - char *newString = static_cast (malloc (length + 1)); - memcpy (newString, value, length); - newString[length] = 0; - return newString; - } - - virtual void releaseStringValue (char *value) - { - if (value) - free (value); - } - }; - - static ValueAllocator *&valueAllocator () - { - static DefaultValueAllocator defaultAllocator; - static ValueAllocator *valueAllocator = &defaultAllocator; - return valueAllocator; - } - - static struct DummyValueAllocatorInitializer { - DummyValueAllocatorInitializer () - { - valueAllocator (); // ensure valueAllocator () statics are initialized before main (). - } - } dummyValueAllocatorInitializer; - - - - Value::CZString::CZString (int index) - : cstr_ (0) - , index_ (index) - { - } - - Value::CZString::CZString (char const *cstr, DuplicationPolicy allocate) - : cstr_ (allocate == duplicate ? valueAllocator()->makeMemberName (cstr) - : cstr) - , index_ (allocate) - { - } - - Value::CZString::CZString (const CZString &other) - : cstr_ (other.index_ != noDuplication && other.cstr_ != 0 - ? valueAllocator()->makeMemberName (other.cstr_) - : other.cstr_) - , index_ (other.cstr_ ? (other.index_ == noDuplication ? noDuplication : duplicate) - : other.index_) - { - } - - Value::CZString::~CZString () - { - if (cstr_ && index_ == duplicate) - valueAllocator()->releaseMemberName (const_cast (cstr_)); - } - - void - Value::CZString::swap (CZString &other) - { - std::swap (cstr_, other.cstr_); - std::swap (index_, other.index_); - } - - Value::CZString & - Value::CZString::operator = (const CZString &other) - { - CZString temp (other); - swap (temp); - return *this; - } - - bool - Value::CZString::operator < (const CZString &other) const - { - if (cstr_) - return strcmp (cstr_, other.cstr_) < 0; - return index_ < other.index_; - } - - bool - Value::CZString::operator == (const CZString &other) const - { - if (cstr_) - return strcmp (cstr_, other.cstr_) == 0; - return index_ == other.index_; - } - - - int - Value::CZString::index () const - { - return index_; - } - - - char const * - Value::CZString::c_str () const - { - return cstr_; - } - - bool - Value::CZString::isStaticString () const - { - return index_ == noDuplication; - } - - - // class Value::Value - - Value::Value (ValueType type) - : type_ (type) - , allocated_ (0) - { - switch (type) - { - case nullValue: - break; - case intValue: - case uintValue: - value_.int_ = 0; - break; - case realValue: - value_.real_ = 0.0; - break; - case stringValue: - value_.string_ = 0; - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues (); - break; - case booleanValue: - value_.bool_ = false; - break; - default: - throw_unreachable; - } - } - - - Value::Value (int value) - : type_ (intValue) - { - value_.int_ = value; - } - - - Value::Value (unsigned value) - : type_ (uintValue) - { - value_.uint_ = value; - } - - Value::Value (double value) - : type_ (realValue) - { - value_.real_ = value; - } - - Value::Value (char const *value) - : type_ (stringValue) - , allocated_ (true) - { - value_.string_ = valueAllocator()->duplicateStringValue (value); - } - - Value::Value (const std::string &value) - : type_ (stringValue) - , allocated_ (true) - { - value_.string_ = valueAllocator()->duplicateStringValue (value.c_str (), (unsigned)value.length ()); - } - - Value::Value (const StaticString &value) - : type_ (stringValue) - , allocated_ (false) - { - value_.string_ = const_cast (value.c_str ()); - } - - - Value::Value (bool value) - : type_ (booleanValue) - { - value_.bool_ = value; - } - - - Value::Value (const Value &other) - : type_ (other.type_) - { - switch (type_) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - value_ = other.value_; - break; - case stringValue: - if (other.value_.string_) - { - value_.string_ = valueAllocator()->duplicateStringValue (other.value_.string_); - allocated_ = true; - } - else - value_.string_ = 0; - break; - case arrayValue: - case objectValue: - value_.map_ = new ObjectValues (*other.value_.map_); - break; - default: - throw_unreachable; - } - } - - - Value::~Value () - { - switch (type_) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - break; - case stringValue: - if (allocated_) - valueAllocator()->releaseStringValue (value_.string_); - break; - case arrayValue: - case objectValue: - delete value_.map_; - break; - default: - throw_unreachable; - } - } - - Value & - Value::operator = (const Value &other) - { - Value temp (other); - swap (temp); - return *this; - } - - void - Value::swap (Value &other) - { - ValueType temp = type_; - type_ = other.type_; - other.type_ = temp; - std::swap (value_, other.value_); - int temp2 = allocated_; - allocated_ = other.allocated_; - other.allocated_ = temp2; - } - - ValueType - Value::type () const - { - return type_; - } - - bool - Value::operator < (const Value &other) const - { - int typeDelta = type_ - other.type_; - if (typeDelta) - return typeDelta < 0 ? true : false; - switch (type_) - { - case nullValue: - return false; - case intValue: - return value_.int_ < other.value_.int_; - case uintValue: - return value_.uint_ < other.value_.uint_; - case realValue: - return value_.real_ < other.value_.real_; - case booleanValue: - return value_.bool_ < other.value_.bool_; - case stringValue: - return (value_.string_ == 0 && other.value_.string_) - || (other.value_.string_ - && value_.string_ - && strcmp (value_.string_, other.value_.string_) < 0); - case arrayValue: - case objectValue: - { - int delta = int (value_.map_->size () - other.value_.map_->size ()); - if (delta) - return delta < 0; - return (*value_.map_) < (*other.value_.map_); - } - default: - throw_unreachable; - } - return 0; // unreachable - } - - bool - Value::operator <= (const Value &other) const - { - return !(other > *this); - } - - bool - Value::operator >= (const Value &other) const - { - return !(*this < other); - } - - bool - Value::operator > (const Value &other) const - { - return other < *this; - } - - bool - Value::operator == (const Value &other) const - { - if (type_ != other.type_) - return false; - - switch (type_) - { - case nullValue: - return true; - case intValue: - return value_.int_ == other.value_.int_; - case uintValue: - return value_.uint_ == other.value_.uint_; - case realValue: - return value_.real_ == other.value_.real_; - case booleanValue: - return value_.bool_ == other.value_.bool_; - case stringValue: - return (value_.string_ == other.value_.string_) - || (other.value_.string_ - && value_.string_ - && strcmp (value_.string_, other.value_.string_) == 0); - case arrayValue: - case objectValue: - return value_.map_->size () == other.value_.map_->size () - && (*value_.map_) == (*other.value_.map_); - default: - throw_unreachable; - } - return 0; // unreachable - } - - bool - Value::operator != (const Value &other) const - { - return !(*this == other); - } - - Value::operator char const * () const - { - throw_unless (type_ == stringValue); - return value_.string_; - } - - - Value::operator std::string () const - { - switch (type_) - { - case nullValue: - return ""; - case stringValue: - return value_.string_ ? value_.string_ : ""; - case booleanValue: - return value_.bool_ ? "true" : "false"; - case intValue: - case uintValue: - case realValue: - case arrayValue: - case objectValue: - throw_msg_unless (false, "Type is not convertible to string"); - default: - throw_unreachable; - } - return ""; // unreachable - } - - Value::operator int () const - { - switch (type_) - { - case nullValue: - return 0; - case intValue: - return value_.int_; - case uintValue: - throw_msg_unless (value_.uint_ < (unsigned)maxInt, "integer out of signed integer range"); - return value_.uint_; - case realValue: - throw_msg_unless (value_.real_ >= minInt && value_.real_ <= maxInt, "Real out of signed integer range"); - return int (value_.real_); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - throw_msg_unless (false, "Type is not convertible to int"); - default: - throw_unreachable; - } - return 0; // unreachable; - } - - Value::operator unsigned () const - { - switch (type_) - { - case nullValue: - return 0; - case intValue: - throw_msg_unless (value_.int_ >= 0, "Negative integer can not be converted to unsigned integer"); - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - throw_msg_unless (value_.real_ >= 0 && value_.real_ <= maxUInt, "Real out of unsigned integer range"); - return unsigned (value_.real_); - case booleanValue: - return value_.bool_ ? 1 : 0; - case stringValue: - case arrayValue: - case objectValue: - throw_msg_unless (false, "Type is not convertible to uint"); - default: - throw_unreachable; - } - return 0; // unreachable; - } - - Value::operator double () const - { - switch (type_) - { - case nullValue: - return 0.0; - case intValue: - return value_.int_; - case uintValue: - return value_.uint_; - case realValue: - return value_.real_; - case booleanValue: - return value_.bool_ ? 1.0 : 0.0; - case stringValue: - case arrayValue: - case objectValue: - throw_msg_unless (false, "Type is not convertible to double"); - default: - throw_unreachable; - } - return 0; // unreachable; - } - - Value::operator bool () const - { - switch (type_) - { - case nullValue: - return false; - case intValue: - case uintValue: - return value_.int_ != 0; - case realValue: - return value_.real_ != 0.0; - case booleanValue: - return value_.bool_; - case stringValue: - return value_.string_ && value_.string_[0] != 0; - case arrayValue: - case objectValue: - return value_.map_->size () != 0; - default: - throw_unreachable; - } - return false; // unreachable; - } - - - bool - Value::isConvertibleTo (ValueType other) const - { - switch (type_) - { - case nullValue: - return true; - case intValue: - return (other == nullValue && value_.int_ == 0) - || other == intValue - || (other == uintValue && value_.int_ >= 0) - || other == realValue - || other == stringValue - || other == booleanValue; - case uintValue: - return (other == nullValue && value_.uint_ == 0) - || (other == intValue && value_.uint_ <= (unsigned)maxInt) - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case realValue: - return (other == nullValue && value_.real_ == 0.0) - || (other == intValue && value_.real_ >= minInt && value_.real_ <= maxInt) - || (other == uintValue && value_.real_ >= 0 && value_.real_ <= maxUInt) - || other == realValue - || other == stringValue - || other == booleanValue; - case booleanValue: - return (other == nullValue && value_.bool_ == false) - || other == intValue - || other == uintValue - || other == realValue - || other == stringValue - || other == booleanValue; - case stringValue: - return other == stringValue - || (other == nullValue && (!value_.string_ || value_.string_[0] == 0)); - case arrayValue: - return other == arrayValue - || (other == nullValue && value_.map_->size () == 0); - case objectValue: - return other == objectValue - || (other == nullValue && value_.map_->size () == 0); - default: - throw_unreachable; - } - return false; // unreachable; - } - - - /// Number of values in array or object - unsigned - Value::size () const - { - switch (type_) - { - case nullValue: - case intValue: - case uintValue: - case realValue: - case booleanValue: - case stringValue: - return 0; - case arrayValue: // size of the array is highest index + 1 - if (!value_.map_->empty ()) - { - ObjectValues::const_iterator itLast = value_.map_->end (); - --itLast; - return itLast->first.index ()+1; - } - return 0; - case objectValue: - return int (value_.map_->size ()); - default: - throw_unreachable; - } - return 0; // unreachable; - } - - - bool - Value::empty () const - { - if (isNull () || isArray () || isObject ()) - return size () == 0u; - else - return false; - } - - - bool - Value::operator ! () const - { - return isNull (); - } - - - void - Value::clear () - { - throw_unless (type_ == nullValue || type_ == arrayValue || type_ == objectValue); - - switch (type_) - { - case arrayValue: - case objectValue: - value_.map_->clear (); - break; - default: - break; - } - } - - void - Value::resize (unsigned newSize) - { - throw_unless (type_ == nullValue || type_ == arrayValue); - if (type_ == nullValue) - *this = Value (arrayValue); - unsigned oldSize = size (); - if (newSize == 0) - clear (); - else if (newSize > oldSize) - (*this)[ newSize - 1 ]; - else - { - for (unsigned index = newSize; index < oldSize; ++index) - value_.map_->erase (index); - throw_unless (size () == newSize); - } - } - - - Value & - Value::operator [] (int index) - { - return operator [] (static_cast (index)); - } - - - Value & - Value::operator [] (unsigned index) - { - throw_unless (type_ == nullValue || type_ == arrayValue); - if (type_ == nullValue) - *this = Value (arrayValue); - CZString key (index); - ObjectValues::iterator it = value_.map_->lower_bound (key); - if (it != value_.map_->end () && it->first == key) - return it->second; - - ObjectValues::value_type defaultValue (key, null); - it = value_.map_->insert (it, defaultValue); - return it->second; - } - - - const Value & - Value::operator [] (int index) const - { - return operator [] (static_cast (index)); - } - - - const Value & - Value::operator [] (unsigned index) const - { - throw_unless (type_ == nullValue || type_ == arrayValue); - if (type_ == nullValue) - return null; - CZString key (index); - ObjectValues::const_iterator it = value_.map_->find (key); - if (it == value_.map_->end ()) - return null; - return it->second; - } - - - Value & - Value::operator [] (char const *key) - { - return resolveReference (key, false); - } - - - Value & - Value::resolveReference (char const *key, bool isStatic) - { - throw_unless (type_ == nullValue || type_ == objectValue); - if (type_ == nullValue) - *this = Value (objectValue); - CZString actualKey (key, isStatic ? CZString::noDuplication - : CZString::duplicateOnCopy); - ObjectValues::iterator it = value_.map_->lower_bound (actualKey); - if (it != value_.map_->end () && it->first == actualKey) - return it->second; - - ObjectValues::value_type defaultValue (actualKey, null); - it = value_.map_->insert (it, defaultValue); - Value &value = it->second; - return value; - } - - - Value - Value::get (int index, const Value &defaultValue) const - { - return get (static_cast (index), defaultValue); - } - - - Value - Value::get (unsigned index, const Value &defaultValue) const - { - const Value *value = &((*this)[index]); - return value == &null ? defaultValue : *value; - } - - - bool - Value::isValidIndex (int index) const - { - return isValidIndex (static_cast (index)); - } - - - bool - Value::isValidIndex (unsigned index) const - { - return index < size (); - } - - - - const Value & - Value::operator [] (char const *key) const - { - throw_unless (type_ == nullValue || type_ == objectValue); - if (type_ == nullValue) - return null; - CZString actualKey (key, CZString::noDuplication); - ObjectValues::const_iterator it = value_.map_->find (actualKey); - if (it == value_.map_->end ()) - return null; - return it->second; - } - - - Value & - Value::operator [] (const std::string &key) - { - return (*this)[ key.c_str () ]; - } - - - const Value & - Value::operator [] (const std::string &key) const - { - return (*this)[ key.c_str () ]; - } - - Value & - Value::operator [] (const StaticString &key) - { - return resolveReference (key, true); - } - - - Value & - Value::append (const Value &value) - { - return (*this)[size ()] = value; - } - - - Value - Value::get (char const *key, const Value &defaultValue) const - { - const Value *value = &((*this)[key]); - return value == &null ? defaultValue : *value; - } - - - Value - Value::get (const std::string &key, const Value &defaultValue) const - { - return get (key.c_str (), defaultValue); - } - - Value - Value::removeMember (char const *key) - { - throw_unless (type_ == nullValue || type_ == objectValue); - if (type_ == nullValue) - return null; - CZString actualKey (key, CZString::noDuplication); - ObjectValues::iterator it = value_.map_->find (actualKey); - if (it == value_.map_->end ()) - return null; - Value old (it->second); - value_.map_->erase (it); - return old; - } - - Value - Value::removeMember (const std::string &key) - { - return removeMember (key.c_str ()); - } - - bool - Value::isMember (char const *key) const - { - const Value *value = &((*this)[key]); - return value != &null; - } - - - bool - Value::isMember (const std::string &key) const - { - return isMember (key.c_str ()); - } - - - Value::Members - Value::getMemberNames () const - { - throw_unless (type_ == nullValue || type_ == objectValue); - if (type_ == nullValue) - return Value::Members (); - Members members; - members.reserve (value_.map_->size ()); - ObjectValues::const_iterator it; - ObjectValues::const_iterator itEnd = value_.map_->end (); - for (it = value_.map_->begin (); it != itEnd; ++it) - members.push_back (std::string (it->first.c_str())); - return members; - } - - bool - Value::isNull () const - { - return type_ == nullValue; - } - - - bool - Value::isBool () const - { - return type_ == booleanValue; - } - - - bool - Value::isInt () const - { - return type_ == intValue; - } - - - bool - Value::isUInt () const - { - return type_ == uintValue; - } - - - bool - Value::isIntegral () const - { - return type_ == intValue - || type_ == uintValue - || type_ == booleanValue; - } - - - bool - Value::isDouble () const - { - return type_ == realValue; - } - - - bool - Value::isNumeric () const - { - return isIntegral () || isDouble (); - } - - - bool - Value::isString () const - { - return type_ == stringValue; - } - - - bool - Value::isArray () const - { - return type_ == nullValue || type_ == arrayValue; - } - - - bool - Value::isObject () const - { - return type_ == nullValue || type_ == objectValue; - } - - - Value::const_iterator - Value::begin () const - { - switch (type_) - { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator (value_.map_->begin ()); - break; - default: - break; - } - return const_iterator (); - } - - Value::const_iterator - Value::end () const - { - switch (type_) - { - case arrayValue: - case objectValue: - if (value_.map_) - return const_iterator (value_.map_->end ()); - break; - default: - break; - } - return const_iterator (); - } - - - Value::iterator - Value::begin () - { - switch (type_) - { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator (value_.map_->begin ()); - break; - default: - break; - } - return iterator (); - } - - Value::iterator - Value::end () - { - switch (type_) - { - case arrayValue: - case objectValue: - if (value_.map_) - return iterator (value_.map_->end ()); - break; - default: - break; - } - return iterator (); - } -} // namespace json - -namespace json -{ - static void uintToString (unsigned value, - char *¤t) - { - *--current = 0; - do - { - *--current = (value % 10) + '0'; - value /= 10; - } - while (value != 0); - } - - std::string valueToString (int value) - { - char buffer[32]; - char *current = buffer + sizeof (buffer); - bool isNegative = value < 0; - if (isNegative) - value = -value; - uintToString (unsigned (value), current); - if (isNegative) - *--current = '-'; - throw_unless (current >= buffer); - return current; - } - - - std::string valueToString (unsigned value) - { - char buffer[32]; - char *current = buffer + sizeof (buffer); - uintToString (value, current); - throw_unless (current >= buffer); - return current; - } - - std::string valueToString (double value) - { - char buffer[32]; - sprintf (buffer, "%.16g", value); - return buffer; - } - - - std::string valueToString (bool value) - { - return value ? "true" : "false"; - } - - std::string valueToQuotedString (char const *value) - { - // Not sure how to handle unicode... - if (std::strpbrk (value, "\"\\\b\f\n\r\t") == NULL) - return std::string ("\"") + value + "\""; - // We have to walk value and escape any special characters. - // Appending to std::string is not efficient, but this should be rare. - // (Note: forward slashes are *not* rare, but I am not escaping them.) - unsigned maxsize = strlen (value) * 2 + 3; // allescaped+quotes+NULL - std::string result; - result.reserve (maxsize); // to avoid lots of mallocs - result += "\""; - for (char const* c=value; *c != 0; ++c){ - switch (*c){ - case '\"': - result += "\\\""; - break; - case '\\': - result += "\\\\"; - break; - case '\b': - result += "\\b"; - break; - case '\f': - result += "\\f"; - break; - case '\n': - result += "\\n"; - break; - case '\r': - result += "\\r"; - break; - case '\t': - result += "\\t"; - break; - case '/': - // Even though \/ is considered a legal escape in JSON, a bare - // slash is also legal, so I see no reason to escape it. - // (I hope I am not misunderstanding something.) - default: - result += *c; - } - } - result += "\""; - return result; - } - - // Class Writer - std::string - Writer::write (const Value &root) - { - document_ = ""; - writeValue (root); - document_ += "\n"; - return document_; - } - - - void - Writer::writeValue (const Value &value) - { - switch (value.type ()) - { - case nullValue: - document_ += "null"; - break; - case intValue: - document_ += valueToString (static_cast (value)); - break; - case uintValue: - document_ += valueToString (static_cast (value)); - break; - case realValue: - document_ += valueToString (static_cast (value)); - break; - case stringValue: - document_ += valueToQuotedString (static_cast (value)); - break; - case booleanValue: - document_ += valueToString (static_cast (value)); - break; - case arrayValue: - { - document_ += "["; - int size = value.size (); - for (int index = 0; index < size; ++index) - { - if (index > 0) - document_ += ","; - writeValue (value[index]); - } - document_ += "]"; - } - break; - case objectValue: - { - Value::Members members (value.getMemberNames ()); - document_ += "{"; - for (Value::Members::iterator it = members.begin (); - it != members.end (); - ++it) - { - const std::string &name = *it; - if (it != members.begin ()) - document_ += ","; - document_ += valueToQuotedString (name.c_str ()); - document_ += ":"; - writeValue (value[name]); - } - document_ += "}"; + + if (instring) + throw JsonException("Unterminated string"); + + if (aparent && pisobject) + throw JsonException("Unterminated object"); + else if (aparent && !pisobject) + throw JsonException("Unterminated array"); + + if (value) + return value; + else + throw JsonException("No JSON data found"); } - break; - } - } -} // namespace json - -/** - * RPC - */ - -namespace json -{ - namespace rpc - { - method_map methods; - - void - add_method (char *name, Module const *mod, method mth) - { - mfp m = { mod, mth }; - methods[name] = m; - } - - void - service (HTTPRequest *http, Value &request, Value &response) - { - char const *methodName = static_cast (request["method"]); - - method_map::iterator mthit = methods.find (methodName); - if (mthit != methods.end ()) + + void JSONSerialize(RPCValue *value, std::stringstream &re) { - mfp m = mthit->second; - Module *mod = new Module (*m.mod); - method mth = m.mth; - (mod->*mth) (http, request, response); - delete mod; + int ac; + switch (value->GetType()) + { + case RPCNull: + re << "null"; + break; + case RPCBoolean: + re << ((value->GetBool()) ? "true" : "false"); + break; + case RPCInteger: + re << value->GetInt(); + break; + case RPCString: + re << "\"" << value->GetString() << "\""; + break; + case RPCArray: + re << "["; + ac = value->ArraySize(); + for (int i = 0; i < ac; i++) + { + this->JSONSerialize(value->GetArray(i), re); + if (i != (ac - 1)) + re << ","; + } + re << "]"; + break; + case RPCObject: + re << "{"; + std::pair its = value->GetObjectIterator(); + while (its.first != its.second) + { + re << "\"" << its.first->first << "\":"; + this->JSONSerialize(its.first->second, re); + if (++its.first != its.second) + re << ","; + } + re << "}"; + break; + } } - } - - void - process (HTTPRequest *http, std::string &response_text, char const *request_text) - { - std::string text; - bool parse_success; - Value request (objectValue); - Value response (objectValue); - Reader r; - Writer w; - - response["error"] = Value(nullValue); - response["result"] = Value(nullValue); - - parse_success = r.parse (request_text, request_text + strlen (request_text), request); - - response["id"] = request["id"]; - - service (http, request, response); - - text = w.write (response); - - response_text = text.c_str (); - - return; - } - } // namespace rpc -} // namespace json +}; -MODULE_INIT(ModuleRpcJson) +MODULE_INIT(ModuleRpcJson); diff --git a/src/modules/m_rpctest.cpp b/src/modules/m_rpctest.cpp new file mode 100644 index 000000000..faa0f9279 --- /dev/null +++ b/src/modules/m_rpctest.cpp @@ -0,0 +1,68 @@ +/* +------------------------------------+ + * | Inspire Internet Relay Chat Daemon | + * +------------------------------------+ + * + * InspIRCd: (C) 2002-2007 InspIRCd Development Team + * See: http://www.inspircd.org/wiki/index.php/Credits + * + * This program is free but copyrighted software; see + * the file COPYING for details. + * + * --------------------------------------------------- + */ + +#include "inspircd.h" +#include "users.h" +#include "channels.h" +#include "modules.h" +#include "rpc.h" + +/* $ModDesc: A test of the RPC API */ +/* $ModDep: rpc.h */ + +class ModuleRPCTest : public Module +{ + private: + + public: + ModuleRPCTest(InspIRCd *Me) + : Module::Module(Me) + { + } + + virtual ~ModuleRPCTest() + { + } + + virtual Version GetVersion() + { + return Version(1, 1, 0, 0, VF_VENDOR, API_VERSION); + } + + void Implements(char *List) + { + List[I_OnEvent] = 1; + } + + virtual void OnEvent(Event *ev) + { + if (ev->GetEventID() == "RPCMethod") + { + RPCRequest *req = (RPCRequest*) ev->GetData(); + + if (req->method == "test.echo") + { + req->claimed = true; + if (req->parameters->ArraySize() < 1) + { + req->error = "Insufficient parameters"; + return; + } + + req->result->SetString(req->parameters->GetArray(0)->GetString()); + } + } + } +}; + +MODULE_INIT(ModuleRPCTest); diff --git a/src/modules/rpc.h b/src/modules/rpc.h new file mode 100644 index 000000000..cdaec4082 --- /dev/null +++ b/src/modules/rpc.h @@ -0,0 +1,296 @@ +#ifndef RPC_H +#define RPC_H + +#include +#include +#include + +class RPCValue; + +typedef enum +{ + RPCNull, + RPCBoolean, + RPCInteger, + RPCString, + RPCArray, + RPCObject +} RPCValueType; + +typedef std::map RPCObjectContainer; +typedef std::vector RPCArrayContainer; + +class RPCValue : public classbase +{ + protected: + RPCValueType type; + void *value; + + double *CastInteger() + { + return (double*)value; + } + + std::string *CastString() + { + return (std::string*)value; + } + + RPCObjectContainer *CastObject() + { + return (RPCObjectContainer*)value; + } + + RPCArrayContainer *CastArray() + { + return (RPCArrayContainer*)value; + } + + void DestroyValue() + { + // Some versions of GCC complain about declaration in switch statements + RPCArrayContainer *a; + RPCObjectContainer *o; + switch (type) + { + case RPCInteger: + delete this->CastInteger(); + break; + case RPCString: + delete this->CastString(); + break; + case RPCArray: + a = this->CastArray(); + for (RPCArrayContainer::iterator i = a->begin(); i != a->end(); i++) + delete *i; + delete a; + break; + case RPCObject: + o = this->CastObject(); + for (RPCObjectContainer::iterator i = o->begin(); i != o->end(); i++) + delete i->second; + delete o; + break; + default: + break; + } + + value = NULL; + } + + void InitValue() + { + switch (type) + { + case RPCNull: + case RPCBoolean: + value = NULL; + break; + case RPCInteger: + value = new double; + break; + case RPCString: + value = new std::string; + break; + case RPCArray: + value = new RPCArrayContainer; + break; + case RPCObject: + value = new RPCObjectContainer; + break; + } + } + + RPCValue(const RPCValue &v) { } + + public: + RPCValue *parent; + + RPCValue(RPCValue *parent = NULL) : type(RPCNull), value(NULL), parent(parent) { } + RPCValue(RPCValueType type, RPCValue *parent = NULL) : type(type), value(NULL), parent(parent) { InitValue(); } + RPCValue(bool nvalue, RPCValue *parent = NULL) : type(RPCBoolean), value((void*)nvalue), parent(parent) { } + RPCValue(double nvalue, RPCValue *parent = NULL) : type(RPCInteger), parent(parent) { value = new double(nvalue); } + RPCValue(const std::string &nvalue, RPCValue *parent = NULL) : type(RPCString), parent(parent) { value = new std::string(nvalue); } + + virtual ~RPCValue() + { + DestroyValue(); + } + + RPCValueType GetType() + { + return type; + } + + void SetNull() + { + DestroyValue(); + type = RPCNull; + } + + void SetBoolean(bool nvalue) + { + DestroyValue(); + value = (void*)nvalue; + type = RPCBoolean; + } + + void SetInteger(double nvalue) + { + if (type == RPCInteger) + { + *this->CastInteger() = nvalue; + } + else + { + DestroyValue(); + value = new double(nvalue); + type = RPCInteger; + } + } + + void SetString(const std::string &nvalue) + { + if (type == RPCString) + { + this->CastString()->assign(nvalue); + } + else + { + DestroyValue(); + value = new std::string(nvalue); + type = RPCString; + } + } + + void SetArray() + { + if (type == RPCArray) + { + this->CastArray()->clear(); + } + else + { + DestroyValue(); + type = RPCArray; + InitValue(); + } + } + + void SetObject() + { + if (type == RPCObject) + { + this->CastObject()->clear(); + } + else + { + DestroyValue(); + type = RPCObject; + InitValue(); + } + } + + void ArrayAdd(RPCValue *nvalue) + { + if (type != RPCArray) + return; + RPCArrayContainer *a = this->CastArray(); + a->push_back(nvalue); + nvalue->parent = this; + } + + void ObjectAdd(const std::string &key, RPCValue *nvalue) + { + if (type != RPCObject) + return; + RPCObjectContainer *o = this->CastObject(); + o->insert(std::make_pair(key, nvalue)); + nvalue->parent = this; + } + + RPCValue *GetArray(int i) + { + if (type != RPCArray) + return NULL; + RPCArrayContainer *a = this->CastArray(); + if ((i < 0) || (i >= (signed)a->size())) + return NULL; + return a->at(i); + } + + int ArraySize() + { + if (type != RPCArray) + return 0; + RPCArrayContainer *a = this->CastArray(); + return a->size(); + } + + RPCValue *GetObject(const std::string &key) + { + if (type != RPCObject) + return NULL; + RPCObjectContainer *o = this->CastObject(); + RPCObjectContainer::iterator it = o->find(key); + if (it == o->end()) + return NULL; + return it->second; + } + + std::pair GetObjectIterator() throw() + { + if (type != RPCObject) + throw std::runtime_error("Cannot get iterator for a non-object RPC value"); + RPCObjectContainer *o = this->CastObject(); + return std::make_pair(o->begin(), o->end()); + } + + std::string GetString() + { + if (type != RPCString) + return std::string(); + return *this->CastString(); + } + + double GetInt() + { + if (type != RPCInteger) + return 0; + return *this->CastInteger(); + } + + bool GetBool() + { + if (type != RPCBoolean) + return 0; + return (value != NULL); + } +}; + +class RPCRequest : public classbase +{ + protected: + + public: + std::string method; + RPCValue *parameters; + RPCValue *result; + std::string provider; + bool claimed; + std::string error; + + RPCRequest(const std::string &provider, const std::string &method, RPCValue *parameters) + : method(method), parameters(parameters), provider(provider), claimed(false) + { + result = new RPCValue(); + } + + ~RPCRequest() + { + if (result) + delete result; + } +}; + +#endif -- cgit v1.2.3