More work on getting remote calls up and running, added two remote calls list_zones and get_zone_info.

This commit is contained in:
KimLS
2014-07-30 15:23:14 -07:00
parent 927580b983
commit 780789fbad
21 changed files with 528 additions and 167 deletions
+4 -2
View File
@@ -1,13 +1,15 @@
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
SET(web_interface_sources
call_handler.cpp
method_handler.cpp
remote_call.cpp
web_interface.cpp
worldserver.cpp
)
SET(web_interface_headers
call_handler.h
method_handler.h
remote_call.h
worldserver.h
)
-35
View File
@@ -1,35 +0,0 @@
#include "web_interface.h"
#include "call_handler.h"
extern std::map<std::string, CallHandler> authorized_calls;
extern std::map<std::string, CallHandler> unauthorized_calls;
void register_authorized_calls()
{
}
void register_unauthorized_calls()
{
unauthorized_calls["token_auth"] = handle_call_token_auth;
}
void register_calls()
{
register_authorized_calls();
register_unauthorized_calls();
}
void handle_call_token_auth(per_session_data_eqemu *session, rapidjson::Document &document)
{
if (!document.HasMember("token")) {
WriteWebCallResponse(session, document, "token_missing");
return;
}
session->auth = document["token"].GetString();
if (!CheckTokenAuthorization(session)) {
WriteWebCallResponse(session, document, "unauthorized");
} else {
WriteWebCallResponse(session, document, "authorized");
}
}
+103
View File
@@ -0,0 +1,103 @@
#include "web_interface.h"
#include "method_handler.h"
#include "remote_call.h"
extern WorldServer *worldserver;
extern std::map<std::string, std::pair<int, MethodHandler>> authorized_methods;
extern std::map<std::string, MethodHandler> unauthorized_methods;
void register_authorized_methods()
{
authorized_methods["token_auth"] = std::make_pair(0, handle_method_token_auth);
authorized_methods["list_zones"] = std::make_pair(10, handle_method_no_args);
authorized_methods["get_zone_info"] = std::make_pair(10, handle_method_get_zone_info);
authorized_methods["subscribe"] = std::make_pair(10, handle_method_subscribe);
}
void register_unauthorized_methods()
{
unauthorized_methods["token_auth"] = handle_method_token_auth;
}
void register_methods()
{
register_authorized_methods();
register_unauthorized_methods();
}
void handle_method_token_auth(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method)
{
if (!document.HasMember("token")) {
WriteWebCallResponseString(session, document, "Auth token missing", true);
return;
}
session->auth = document["token"].GetString();
if (!CheckTokenAuthorization(session)) {
WriteWebCallResponseBoolean(session, document, "false", false);
} else {
WriteWebCallResponseBoolean(session, document, "true", false);
}
}
void handle_method_no_args(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method)
{
CheckParams(0, "[]");
VerifyID();
uint32 sz = (uint32)(id.size() + session->uuid.size() + method.size() + 3 + 16);
ServerPacket *pack = new ServerPacket(ServerOP_WIRemoteCall, sz);
pack->WriteUInt32((uint32)id.size());
pack->WriteString(id.c_str());
pack->WriteUInt32((uint32)session->uuid.size());
pack->WriteString(session->uuid.c_str());
pack->WriteUInt32((uint32)method.size());
pack->WriteString(method.c_str());
pack->WriteUInt32(0);
worldserver->SendPacket(pack);
safe_delete(pack);
}
void handle_method_get_zone_info(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method)
{
CheckParams(1, "[zoneserver_id]");
VerifyID();
uint32 sz = (uint32)(id.size() + session->uuid.size() + method.size() + 3 + 16);
auto &params = document["params"];
auto &param = params[(rapidjson::SizeType)0];
if(param.IsNull()) {
sz += 5;
}
else {
sz += (uint32)strlen(param.GetString());
sz += 5;
}
ServerPacket *pack = new ServerPacket(ServerOP_WIRemoteCall, sz);
pack->WriteUInt32((uint32)id.size());
pack->WriteString(id.c_str());
pack->WriteUInt32((uint32)session->uuid.size());
pack->WriteString(session->uuid.c_str());
pack->WriteUInt32((uint32)method.size());
pack->WriteString(method.c_str());
pack->WriteUInt32(1);
pack->WriteUInt32((uint32)strlen(param.GetString()));
pack->WriteString(param.GetString());
worldserver->SendPacket(pack);
safe_delete(pack);
}
void handle_method_subscribe(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method) {
CheckParams(3, "[event, zone_id, instance_id]");
VerifyID();
uint32 sz = (uint32)(id.size() + session->uuid.size() + method.size() + 3 + 16);
auto &params = document["params"];
for(int i = 0; i < 3; ++i) {
auto &param = params[i];
if(param.IsNull()) {
sz += 5;
} else {
sz += (uint32)strlen(param.GetString());
sz += 5;
}
}
}
+51
View File
@@ -0,0 +1,51 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net)
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; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY except by those people which sell it, which
are required to give you total support for your newly bought product;
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WI_METHOD_HANDLER_H
#define WI_METHOD_HANDLER_H
#define CheckParams(sz, msg) if (!document.HasMember("params")) { \
WriteWebCallResponseString(session, document, "Missing parameters, expected: " + std::string(msg), true); \
if (!document.HasMember("params") && sz > 0) { \
WriteWebCallResponseString(session, document, "Missing parameters, expected: " + std::string(msg), true); \
return; \
} \
auto &params = document["params"]; \
if(!params.IsArray()) { \
WriteWebCallResponseString(session, document, "Missing parameters, expected: " + std::string(msg), true); \
return; \
} \
if(params.Size() != sz) { \
WriteWebCallResponseString(session, document, "Missing parameters, expected: " + std::string(msg), true); \
return; \
} \
} \
#define VerifyID() std::string id; \
if (document.HasMember("id")) { \
id = document["id"].GetString(); \
} \
void register_methods();
void handle_method_token_auth(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method);
void handle_method_no_args(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method);
void handle_method_get_zone_info(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method);
void handle_method_subscribe(per_session_data_eqemu *session, rapidjson::Document &document, std::string &method);
#endif
+90
View File
@@ -0,0 +1,90 @@
#include "web_interface.h"
#include "remote_call.h"
void WriteWebCallResponseString(per_session_data_eqemu *session, rapidjson::Document &doc, std::string result, bool error, bool send_no_id) {
if (doc.HasMember("id") || send_no_id) {
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.String("id");
if (send_no_id) {
writer.Null();
}
else {
writer.String(doc["id"].GetString());
}
writer.String("result");
writer.String(result.c_str());
writer.String("error");
if (error) {
writer.Bool(true);
}
else {
writer.Null();
}
writer.EndObject();
session->send_queue->push_back(s.GetString());
}
}
void WriteWebCallResponseInt(per_session_data_eqemu *session, rapidjson::Document &doc, int result, bool error, bool send_no_id) {
if (doc.HasMember("id") || send_no_id) {
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.String("id");
if (send_no_id) {
writer.Null();
}
else {
writer.String(doc["id"].GetString());
}
writer.String("result");
writer.Int(result);
writer.String("error");
if (error) {
writer.Bool(true);
}
else {
writer.Null();
}
writer.EndObject();
session->send_queue->push_back(s.GetString());
}
}
void WriteWebCallResponseBoolean(per_session_data_eqemu *session, rapidjson::Document &doc, bool result, bool error, bool send_no_id) {
if (doc.HasMember("id") || send_no_id) {
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.String("id");
if (send_no_id) {
writer.Null();
}
else {
writer.String(doc["id"].GetString());
}
writer.String("result");
writer.Bool(result);
writer.String("error");
if (error) {
writer.Bool(true);
}
else {
writer.Null();
}
writer.EndObject();
session->send_queue->push_back(s.GetString());
}
}
int CheckTokenAuthorization(per_session_data_eqemu *session) {
//todo: actually check this against a table of tokens that is updated periodically
//right now i have just one entry harded coded for testing purposes
if (session->auth.compare("c5b80ec8-4174-4c4c-d332-dbf3c3a551fc") == 0) {
return 255;
}
return 0;
}
@@ -15,11 +15,13 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef WI_CALL_HANDLER_H
#define WI_CALL_HANDLER_H
#ifndef WI_RPC_H
#define WI_RPC_H
void register_calls();
void handle_call_token_auth(per_session_data_eqemu *session, rapidjson::Document &document);
void WriteWebCallResponseString(per_session_data_eqemu *session, rapidjson::Document &doc, std::string result, bool error, bool send_no_id = false);
void WriteWebCallResponseInt(per_session_data_eqemu *session, rapidjson::Document &doc, int result, bool error, bool send_no_id = false);
void WriteWebCallResponseBoolean(per_session_data_eqemu *session, rapidjson::Document &doc, bool result, bool error, bool send_no_id = false);
int CheckTokenAuthorization(per_session_data_eqemu*);
#endif
+29 -43
View File
@@ -1,5 +1,6 @@
#include "web_interface.h"
#include "call_handler.h"
#include "method_handler.h"
#include "remote_call.h"
volatile bool run = true;
TimeoutManager timeout_manager;
@@ -7,8 +8,8 @@ const EQEmuConfig *config = nullptr;
WorldServer *worldserver = nullptr;
libwebsocket_context *context = nullptr;
std::map<std::string, per_session_data_eqemu*> sessions;
std::map<std::string, CallHandler> authorized_calls;
std::map<std::string, CallHandler> unauthorized_calls;
std::map<std::string, std::pair<int, MethodHandler>> authorized_methods;
std::map<std::string, MethodHandler> unauthorized_methods;
void CatchSignal(int sig_num) {
run = false;
@@ -49,39 +50,47 @@ int callback_eqemu(libwebsocket_context *context, libwebsocket *wsi, libwebsocke
rapidjson::Document document;
if(document.Parse((const char*)in).HasParseError()) {
WriteWebCallResponseString(session, document, "Malformed JSON data", true, true);
break;
}
std::string call;
if(document.HasMember("call")) {
call = document["call"].GetString();
std::string method;
if(document.HasMember("method")) {
method = document["method"].GetString();
}
if(call.length() == 0) {
if(method.length() == 0) {
//No function called, toss this message
WriteWebCallResponse(session, document, "call_empty");
WriteWebCallResponseString(session, document, "No method specified", true);
break;
}
if (!CheckTokenAuthorization(session)) {
int status = CheckTokenAuthorization(session);
if (status == 0) {
//check func call against functions that dont req auth
if (unauthorized_calls.count(call) == 0) {
WriteWebCallResponse(session, document, "unauthorized_call_not_found: " + call);
if (unauthorized_methods.count(method) == 0) {
WriteWebCallResponseString(session, document, "No suitable method found: " + method, true);
break;
}
auto call_func = unauthorized_calls[call];
call_func(session, document);
auto call_func = unauthorized_methods[method];
call_func(session, document, method);
}
else {
else if(status > 0) {
//check func call against functions that req auth
if (authorized_calls.count(call) == 0) {
WriteWebCallResponse(session, document, "authorized_call_not_found: " + call);
if (authorized_methods.count(method) == 0) {
WriteWebCallResponseString(session, document, "No suitable method found: " + method, true);
break;
}
//check status level
auto iter = authorized_methods.find(method);
if(iter->second.first > status) {
WriteWebCallResponseString(session, document, "Method " + method + " requires status " + std::to_string((long)iter->second.first), true);
break;
}
auto call_func = authorized_calls[call];
call_func(session, document);
auto call_func = iter->second.second;
call_func(session, document, method);
}
break;
@@ -130,7 +139,7 @@ static struct libwebsocket_protocols protocols[] = {
int main() {
RegisterExecutablePlatform(ExePlatformWebInterface);
set_exception_handler();
register_calls();
register_methods();
Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect
_log(WEB_INTERFACE__INIT, "Starting EQEmu Web Server.");
@@ -189,27 +198,4 @@ int main() {
return 0;
}
void WriteWebCallResponse(per_session_data_eqemu *session, rapidjson::Document &doc, std::string status) {
if (doc.HasMember("id")) {
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.String("id");
writer.String(doc["id"].GetString());
writer.String("status");
writer.String(status.c_str());
writer.EndObject();
session->send_queue->push_back(s.GetString());
}
}
bool CheckTokenAuthorization(per_session_data_eqemu *session) {
//todo: actually check this against a table of tokens that is updated periodically
//right now i have just one entry harded coded for testing purposes
if(session->auth.compare("c5b80ec8-4174-4c4c-d332-dbf3c3a551fc") == 0) {
return true;
}
return false;
}
+1 -3
View File
@@ -46,8 +46,6 @@ struct per_session_data_eqemu {
std::list<std::string> *send_queue;
};
typedef void(*CallHandler)(per_session_data_eqemu*, rapidjson::Document&);
void WriteWebCallResponse(per_session_data_eqemu *session, rapidjson::Document &doc, std::string status);
bool CheckTokenAuthorization(per_session_data_eqemu*);
typedef void(*MethodHandler)(per_session_data_eqemu*, rapidjson::Document&, std::string&);
#endif
+68 -18
View File
@@ -30,14 +30,8 @@
#include "../common/packet_functions.h"
#include "../common/md5.h"
#include "../common/packet_dump.h"
#include "../common/web_interface_utils.h"
#include "worldserver.h"
struct per_session_data_eqemu {
bool auth;
std::string uuid;
std::list<std::string> *send_queue;
};
#include "web_interface.h"
extern std::map<std::string, per_session_data_eqemu*> sessions;
@@ -65,18 +59,74 @@ void WorldServer::Process(){
switch(pack->opcode) {
case 0: { break; }
case ServerOP_KeepAlive: { break; }
case ServerOP_WIWorldResponse: {
/* Generic Response routine: web_interface server recieves packet from World -
Relays data back to client
*/
_log(WEB_INTERFACE__ERROR, "WI Recieved packet from world 0x%04x, size %d", pack->opcode, pack->size);
WI_Client_Request_Struct* WICR = (WI_Client_Request_Struct*)pack->pBuffer;
std::string Data;
Data.assign(WICR->JSON_Data, pack->size - 64);
/* Check if Session is Valid before sending data back*/
if (sessions[WICR->Client_UUID]){
sessions[WICR->Client_UUID]->send_queue->push_back(Data.c_str());
case ServerOP_WIRemoteCallResponse: {
char *id = nullptr;
char *session_id = nullptr;
char *error = nullptr;
id = new char[pack->ReadUInt32() + 1];
pack->ReadString(id);
session_id = new char[pack->ReadUInt32() + 1];
pack->ReadString(session_id);
error = new char[pack->ReadUInt32() + 1];
pack->ReadString(error);
uint32 param_count = pack->ReadUInt32();
std::vector<std::string> params;
for(uint32 i = 0; i < param_count; ++i) {
char *p = new char[pack->ReadUInt32() + 1];
pack->ReadString(p);
params.push_back(p);
safe_delete_array(p);
}
//send the response to client...
rapidjson::StringBuffer s;
rapidjson::Writer<rapidjson::StringBuffer> writer(s);
writer.StartObject();
writer.String("id");
if(strlen(id) == 0) {
writer.Null();
} else {
writer.String(id);
}
if(strlen(error) != 0) {
writer.String("error");
writer.Bool(true);
writer.String("result");
writer.StartArray();
if(params.size() > 0) {
writer.String(params[0].c_str());
} else {
writer.String("");
}
writer.EndArray();
} else {
writer.String("error");
writer.Null();
writer.String("result");
writer.StartArray();
uint32 p_sz = (uint32)params.size();
for(uint32 i = 0; i < p_sz; ++i) {
writer.String(params[i].c_str());
}
writer.EndArray();
}
writer.EndObject();
if(sessions.count(session_id) != 0) {
per_session_data_eqemu *session = sessions[session_id];
session->send_queue->push_back(s.GetString());
}
safe_delete_array(id);
safe_delete_array(session_id);
safe_delete_array(error);
break;
}
}