00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "inspircd.h"
00015 #include "httpd.h"
00016 #include "rpc.h"
00017 #include <exception>
00018
00019
00020
00021
00022 class JsonException : public std::exception
00023 {
00024 private:
00025 std::string _what;
00026 public:
00027 JsonException(const std::string &swhat)
00028 : _what(swhat)
00029 {
00030 }
00031
00032 virtual ~JsonException() throw() { }
00033
00034 virtual const char *what() const throw()
00035 {
00036 return _what.c_str();
00037 }
00038 };
00039
00040 class ModuleRpcJson : public Module
00041 {
00042 private:
00043
00044 public:
00045 ModuleRpcJson(InspIRCd *Me) : Module(Me)
00046 {
00047 ServerInstance->Modules->PublishInterface("RPC", this);
00048 Implementation eventlist[] = { I_OnEvent };
00049 ServerInstance->Modules->Attach(eventlist, this, 1);
00050 }
00051
00052 virtual ~ModuleRpcJson()
00053 {
00054 ServerInstance->Modules->UnpublishInterface("RPC", this);
00055 }
00056
00057 virtual Version GetVersion()
00058 {
00059 return Version("$Id: m_rpc_json.cpp 10622 2008-10-04 21:27:52Z brain $", VF_SERVICEPROVIDER | VF_VENDOR, API_VERSION);
00060 }
00061
00062
00063 virtual void OnEvent(Event *event)
00064 {
00065 if (event->GetEventID() == "httpd_url")
00066 {
00067 HTTPRequest *req = (HTTPRequest*) event->GetData();
00068
00069 if ((req->GetURI() == "/rpc/json") || (req->GetURI() == "/rpc/json/"))
00070 {
00071 std::stringstream data;
00072
00073 RPCValue *reqobj = NULL;
00074
00075 try
00076 {
00077 reqobj = this->JSONParse(req->GetPostData());
00078
00079 if (!reqobj || (reqobj->GetType() != RPCObject))
00080 throw JsonException("RPC requests must be in the form of a single object");
00081
00082 RPCValue *method = reqobj->GetObject("method");
00083 if (!method || method->GetType() != RPCString)
00084 throw JsonException("RPC requests must have a 'method' string field");
00085
00086 RPCValue *params = reqobj->GetObject("params");
00087 if (!params || params->GetType() != RPCArray)
00088 throw JsonException("RPC requests must have a 'params' array field");
00089
00090 RPCRequest modreq("json", method->GetString(), params);
00091 Event mev((char*) &modreq, this, "RPCMethod");
00092 mev.Send(ServerInstance);
00093
00094 if (!modreq.claimed)
00095 throw JsonException("Unrecognized method");
00096
00097 if (!modreq.error.empty())
00098 {
00099 data << "{\"result\":null,\"error\":\"" << modreq.error << "\"";
00100 }
00101 else
00102 {
00103 data << "{\"result\":";
00104 this->JSONSerialize(modreq.result, data);
00105 data << ",\"error\":null";
00106 }
00107
00108 if (reqobj->GetObject("id"))
00109 {
00110 data << ",\"id\":";
00111 this->JSONSerialize(reqobj->GetObject("id"), data);
00112 }
00113 data << "}";
00114
00115 delete reqobj;
00116 reqobj = NULL;
00117 }
00118 catch (std::exception &e)
00119 {
00120 if (reqobj)
00121 delete reqobj;
00122 data << "{\"result\":null,\"error\":\"" << e.what() << "\"}";
00123 }
00124
00125 HTTPDocument response(req->sock, &data, 200);
00126 response.headers.SetHeader("X-Powered-By", "m_rpc_json.so");
00127 response.headers.SetHeader("Content-Type", "application/json");
00128 response.headers.SetHeader("Connection", "Keep-Alive");
00129
00130 Request rreq((char*) &response, (Module*) this, event->GetSource());
00131 rreq.Send();
00132 }
00133 }
00134 }
00135
00136 void AttachToParent(RPCValue *parent, RPCValue *child, const std::string &key = "")
00137 {
00138 if (!parent || !child)
00139 return;
00140
00141 if (parent->GetType() == RPCArray)
00142 parent->ArrayAdd(child);
00143 else if (parent->GetType() == RPCObject)
00144 parent->ObjectAdd(key, child);
00145 else
00146 throw JsonException("Cannot add a value to a non-container");
00147 }
00148
00149 void AttachToParentReset(RPCValue *parent, RPCValue *&child, std::string &key)
00150 {
00151 AttachToParent(parent, child, key);
00152 child = NULL;
00153 key.clear();
00154 }
00155
00156 RPCValue *JSONParse(const std::string &data)
00157 {
00158 bool pisobject = false;
00159 bool instring = false;
00160 std::string stmp;
00161 std::string vkey;
00162 std::string pvkey;
00163 RPCValue *aparent = NULL;
00164 RPCValue *value = NULL;
00165
00166 for (std::string::const_iterator i = data.begin(); i != data.end(); i++)
00167 {
00168 if (instring)
00169 {
00170
00171 if (*i == '"')
00172 {
00173 instring = false;
00174
00175 if (pisobject && vkey.empty())
00176 vkey = stmp;
00177 else
00178 value = new RPCValue(stmp);
00179
00180 stmp.clear();
00181 }
00182 else
00183 stmp += *i;
00184
00185 continue;
00186 }
00187
00188 if ((*i == ' ') || (*i == '\t') || (*i == '\r') || (*i == '\n'))
00189 continue;
00190
00191 if (*i == '{')
00192 {
00193
00194 if ((value) || (pisobject && vkey.empty()))
00195 throw JsonException("Unexpected begin object token ('{')");
00196
00197 RPCValue *nobj = new RPCValue(RPCObject, aparent);
00198 aparent = nobj;
00199 pvkey = vkey;
00200 vkey.clear();
00201 pisobject = true;
00202 }
00203 else if (*i == '}')
00204 {
00205
00206 if ((!aparent) || (!pisobject) || (!vkey.empty() && !value))
00207 throw JsonException("Unexpected end object token ('}')");
00208
00209
00210 if (value)
00211 AttachToParentReset(aparent, value, vkey);
00212
00213 if (!aparent->parent)
00214 return aparent;
00215
00216 value = aparent;
00217 aparent = aparent->parent;
00218 vkey = pvkey;
00219 pvkey.clear();
00220 pisobject = (aparent->GetType() == RPCObject);
00221 }
00222 else if (*i == '"')
00223 {
00224
00225 if (value)
00226 throw JsonException("Unexpected begin string token ('\"')");
00227
00228 instring = true;
00229 }
00230 else if (*i == ':')
00231 {
00232 if ((!aparent) || (!pisobject) || (vkey.empty()) || (value))
00233 throw JsonException("Unexpected object value token (':')");
00234 }
00235 else if (*i == ',')
00236 {
00237 if ((!aparent) || (!value) || ((pisobject) && (vkey.empty())))
00238 throw JsonException("Unexpected value seperator token (',')");
00239
00240 AttachToParentReset(aparent, value, vkey);
00241 }
00242 else if (*i == '[')
00243 {
00244
00245 if ((value) || (pisobject && vkey.empty()))
00246 throw JsonException("Unexpected begin array token ('[')");
00247
00248 RPCValue *nar = new RPCValue(RPCArray, aparent);
00249 aparent = nar;
00250 pvkey = vkey;
00251 vkey.clear();
00252 pisobject = false;
00253 }
00254 else if (*i == ']')
00255 {
00256
00257 if (!aparent || pisobject)
00258 throw JsonException("Unexpected end array token (']')");
00259
00260 if (value)
00261 AttachToParentReset(aparent, value, vkey);
00262
00263 if (!aparent->parent)
00264 return aparent;
00265
00266 value = aparent;
00267 aparent = aparent->parent;
00268 vkey = pvkey;
00269 pvkey.clear();
00270 pisobject = (aparent->GetType() == RPCObject);
00271 }
00272 else
00273 {
00274
00275 if ((*i == 't') && ((i + 3) < data.end()) && (*(i + 1) == 'r') && (*(i + 2) == 'u') && (*(i + 3) == 'e'))
00276 {
00277 value = new RPCValue(true);
00278 i += 3;
00279 }
00280 else if ((*i == 'f') && ((i + 4) < data.end()) && (*(i + 1) == 'a') && (*(i + 2) == 'l') && (*(i + 3) == 's') && (*(i + 4) == 'e'))
00281 {
00282 value = new RPCValue(false);
00283 i += 4;
00284 }
00285 else if ((*i == 'n') && ((i + 3) < data.end()) && (*(i + 1) == 'u') && (*(i + 2) == 'l') && (*(i + 3) == 'l'))
00286 {
00287 value = new RPCValue();
00288 i += 3;
00289 }
00290 else if ((*i == '-') || (*i == '+') || (*i == '.') || ((*i >= '0') && (*i <= '9')))
00291 {
00292 std::string ds = std::string(i, data.end());
00293 char *eds = NULL;
00294
00295 errno = 0;
00296 double v = strtod(ds.c_str(), &eds);
00297
00298 if (errno != 0)
00299 throw JsonException("Error parsing numeric value");
00300
00301 value = new RPCValue(v);
00302
00303 i += eds - ds.c_str() - 1;
00304 }
00305 else
00306 throw JsonException("Unknown data in value portion");
00307 }
00308 }
00309
00310 if (instring)
00311 throw JsonException("Unterminated string");
00312
00313 if (aparent && pisobject)
00314 throw JsonException("Unterminated object");
00315 else if (aparent && !pisobject)
00316 throw JsonException("Unterminated array");
00317
00318 if (value)
00319 return value;
00320 else
00321 throw JsonException("No JSON data found");
00322 }
00323
00324 void JSONSerialize(RPCValue *value, std::stringstream &re)
00325 {
00326 int ac;
00327 switch (value->GetType())
00328 {
00329 case RPCNull:
00330 re << "null";
00331 break;
00332 case RPCBoolean:
00333 re << ((value->GetBool()) ? "true" : "false");
00334 break;
00335 case RPCInteger:
00336 re << value->GetInt();
00337 break;
00338 case RPCString:
00339 re << "\"" << value->GetString() << "\"";
00340 break;
00341 case RPCArray:
00342 re << "[";
00343 ac = value->ArraySize();
00344 for (int i = 0; i < ac; i++)
00345 {
00346 this->JSONSerialize(value->GetArray(i), re);
00347 if (i != (ac - 1))
00348 re << ",";
00349 }
00350 re << "]";
00351 break;
00352 case RPCObject:
00353 re << "{";
00354 std::pair<RPCObjectContainer::iterator,RPCObjectContainer::iterator> its = value->GetObjectIterator();
00355 while (its.first != its.second)
00356 {
00357 re << "\"" << its.first->first << "\":";
00358 this->JSONSerialize(its.first->second, re);
00359 if (++its.first != its.second)
00360 re << ",";
00361 }
00362 re << "}";
00363 break;
00364 }
00365 }
00366 };
00367
00368 MODULE_INIT(ModuleRpcJson)