1
0
mirror of https://bitbucket.org/seefoe/src.git synced 2026-01-16 23:04:30 -05:00

Reverted webAPI and converted auth to old version

This commit is contained in:
seefo
2018-04-15 10:22:16 -04:00
parent 2635dd00f3
commit fbf2b0ca93
5 changed files with 253 additions and 539 deletions

View File

@@ -126,8 +126,6 @@
#include "webAPI.h"
using namespace StellaBellum;
namespace CentralServerNamespace
{
bool gs_connectionServersPublic=false;
@@ -2941,7 +2939,7 @@ void CentralServer::sendPopulationUpdateToLoginServer()
void CentralServer::sendMetricsToWebAPI()
{
static const std::string metricsURL(ConfigCentralServer::getMetricsDataURL());
/*static const std::string metricsURL(ConfigCentralServer::getMetricsDataURL());
if (!metricsURL.empty())
{
@@ -2979,7 +2977,7 @@ void CentralServer::sendMetricsToWebAPI()
#else
api.submit();
#endif
}
}*/
}
//-----------------------------------------------------------------------

View File

@@ -56,8 +56,6 @@
#include "webAPI.h"
using namespace StellaBellum;
//-----------------------------------------------------------------------
namespace ClientConnectionNamespace {
@@ -269,137 +267,91 @@ void ClientConnection::handleGameServerForLoginMessage(uint32 serverId) {
* to be validated. Central will reply with a list of permissions.
* @see onIdValidated
*/
void ClientConnection::handleClientIdMessage(const ClientIdMsg &msg) {
//Only check clients that have not been validated.
if (m_hasBeenValidated) {
return;
}
void ClientConnection::handleClientIdMessage(const ClientIdMsg& msg)
{
//Only check clients that have not been validated.
if (m_hasBeenValidated)
return;
DEBUG_FATAL(m_hasSelectedCharacter, ("Trying to validate a client who already has a character selected.\n"));
bool result = false;
m_gameBitsToClear = msg.getGameBitsToClear();
DEBUG_FATAL(m_hasSelectedCharacter, ("Trying to validate a client who already has a character selected.\n"));
bool result = false;
char sessionId[apiSessionIdWidth];
if (msg.getTokenSize() > 0) {
Archive::ByteStream t(msg.getToken(), msg.getTokenSize());
Archive::ReadIterator ri(t);
KeyShare::Token token(ri);
char sessionId[apiSessionIdWidth];
m_gameBitsToClear = msg.getGameBitsToClear();
if (!ConfigConnectionServer::getValidateStationKey()) {
// get SUID from token
result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName);
} else {
result = ConnectionServer::decryptToken(token, sessionId, m_requestedSuid);
}
if(msg.getTokenSize() > 0)
{
Archive::ByteStream t(msg.getToken(), msg.getTokenSize());
Archive::ReadIterator ri(t);
KeyShare::Token token(ri);
static const std::string sessURL(ConfigConnectionServer::getSessionURL());
if (!ConfigConnectionServer::getValidateStationKey())
{
// get SUID from token
result = ConnectionServer::decryptToken(token, m_suid, m_isSecure, m_accountName);
}
else
{
result = ConnectionServer::decryptToken(token, sessionId, m_requestedSuid);
}
if (result || strlen(sessionId) != 0) {
if (ConfigConnectionServer::getValidateStationKey()) {
bool cont = false;
StationId apiSuid = 0;
static const std::string loginTrace("TRACE_LOGIN");
LOG(loginTrace, ("ClientConnection SUID = %d", m_suid));
FATAL(sessURL.empty(), ("Session URL is empty in connection server."));
}
if (result)
{
// verify version
if (ConfigConnectionServer::getValidateClientVersion() && msg.getVersion() != GameNetworkMessage::NetworkVersionId)
{
std::string strSessionId(sessionId, apiSessionIdWidth);
strSessionId += '\0';
const std::string clientIP(getRemoteAddress());
const std::string sess(sessionId);
const int bufferSize = 255 + apiSessionIdWidth;
char * buffer = new char[bufferSize];
snprintf(buffer, bufferSize-1, "network version mismatch: got (ip=[%s], sessionId=[%s], version=[%s]), required (version=[%s])", getRemoteAddress().c_str(), strSessionId.c_str(), msg.getVersion().c_str(), GameNetworkMessage::NetworkVersionId.c_str());
buffer[bufferSize-1] = '\0';
FATAL(clientIP.empty(), ("Remote IP is empty"));
ConnectionServer::dropClient(this, std::string(buffer));
disconnect();
DEBUG_WARNING(true, ("ConnectionServer::handleClientIdMessage - For ip %s suid is %lu requestedSUID is %lu and session is %s", clientIP.c_str(), m_suid, m_requestedSuid, sess.c_str()));
delete[] buffer;
webAPI api(sessURL);
return;
}
// add our data
api.addJsonData<std::string>("session_key", sess);
if (api.submit()) {
bool status = api.getNullableValue<bool>("status");
if (status) {
std::string apiUser = api.getString("user_name");
std::string apiIP = api.getString("ip");
int expired = api.getNullableValue<int>("expired");
if (!ConfigConnectionServer::getUseOldSuidGenerator()) {
apiSuid = api.getNullableValue<int>("user_id");
} else {
if (apiUser.length() > MAX_ACCOUNT_NAME_LENGTH) {
apiUser.resize(MAX_ACCOUNT_NAME_LENGTH);
}
apiSuid = std::hash < std::string > {}(apiUser.c_str());
}
if (apiIP == clientIP && expired == 0) {
m_suid = apiSuid;
cont = true;
}
}
}
if (!cont) {
LOG("ClientDisconnect", ("SUID %d (%d) passed a bad token to the connections erver. Disconnecting.", m_suid, apiSuid));
disconnect();
return;
}
}
static const std::string loginTrace("TRACE_LOGIN");
LOG(loginTrace, ("ClientConnection SUID = %d", m_suid));
//check for duplicate login
ClientConnection *oldConnection = ConnectionServer::getClientConnection(m_suid);
if (oldConnection) {
//There is already someone connected to this cluster with this suid.
LOG("Network", ("SUID %d already logged in, disconnecting client.\n", m_suid));
ConnectionServer::dropClient(oldConnection, "Already Connected");
disconnect();
return;
}
// verify version
if (ConfigConnectionServer::getValidateClientVersion() &&
msg.getVersion() != GameNetworkMessage::NetworkVersionId) {
std::string strSessionId(sessionId, apiSessionIdWidth);
strSessionId += '\0';
const int bufferSize = 255 + apiSessionIdWidth;
char *buffer = new char[bufferSize];
snprintf(buffer, bufferSize -
1, "network version mismatch: got (ip=[%s], sessionId=[%s], version=[%s]), required (version=[%s])", getRemoteAddress().c_str(), strSessionId.c_str(), msg.getVersion().c_str(), GameNetworkMessage::NetworkVersionId.c_str());
buffer[bufferSize - 1] = '\0';
ConnectionServer::dropClient(this, std::string(buffer));
disconnect();
delete[] buffer;
return;
}
// test mode only
if (!m_suid && !ConfigConnectionServer::getValidateStationKey()) {
WARNING(true, ("Generating suid from username. This is not safe or secure."));
m_suid = atoi(m_accountName.c_str());
if (m_suid == 0) {
m_suid = std::hash < std::string > {}(m_accountName.c_str());
}
}
onValidateClient(m_suid, m_accountName, m_isSecure, nullptr, ConfigConnectionServer::getDefaultGameFeatures(), ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, ConfigConnectionServer::getFakeBuddyPoints());
}
} else {
WARNING(true, ("SUID %d passed a bad token to the connections server (cache issue or hacker?). Disconnecting.", m_suid));
// They sent us a token that was no good -- either a hack attempt, or
// possibly it was just too old.
LOG("ClientDisconnect", ("SUID %d passed a bad token to the connections server (cache issue or hacker?). Disconnecting.", m_suid));
disconnect();
}
if (ConfigConnectionServer::getValidateStationKey())
{
SessionApiClient * session = ConnectionServer::getSessionApiClient();
NOT_NULL(session);
if(session)
{
session->validateClient(this, sessionId);
}
else
{
ConnectionServer::dropClient(this, "SessionApiClient is not available!");
disconnect();
}
}
else
{
m_suid = atoi(m_accountName.c_str());
if (m_suid == 0)
{
std::hash<std::string> h;
m_suid = h(m_accountName.c_str());
}
onValidateClient(m_suid, m_accountName, m_isSecure, nullptr, ConfigConnectionServer::getDefaultGameFeatures(), ConfigConnectionServer::getDefaultSubscriptionFeatures(), 0, 0, 0, 0, ConfigConnectionServer::getFakeBuddyPoints());
}
}
else
{
// They sent us a token that was no good -- either a hack attempt, or
// possibly it was just too old.
LOG("ClientDisconnect", ("SUID %d passed a bad token to the connections erver. Disconnecting.", m_suid));
disconnect();
}
}
//-----------------------------------------------------------------------

View File

@@ -23,8 +23,6 @@
#include "webAPI.h"
using namespace StellaBellum;
//-----------------------------------------------------------------------
ClientConnection::ClientConnection(UdpConnectionMT *u, TcpClient *t)
@@ -150,127 +148,64 @@ void ClientConnection::onReceive(const Archive::ByteStream &message) {
//-----------------------------------------------------------------------
// originally was used to validate station API credentials, now uses our custom api
void ClientConnection::validateClient(const std::string &id, const std::string &key) {
bool authOK = false;
bool testMode = false;
static const std::string authURL(ConfigLoginServer::getExternalAuthUrl());
void ClientConnection::validateClient(const std::string & id, const std::string & key)
{
// to avoid having to re-type this stupid var all over the place
// ideally we wouldn't copy this here, but it would be a huge pain
const std::string trimmedId = trim(id);
const std::string trimmedKey = trim(key);
std::string uname;
std::string parentAccount;
std::string sessionID;
// and to avoid funny business with atoi and casing
// make it a separate var than the one we send the auth server
std::string lcaseId;
lcaseId.resize(trimmedId.size());
StationId user_id;
StationId parent_id;
std::unordered_map<int, std::string> childAccounts;
std::transform(trimmedId.begin(),trimmedId.end(),lcaseId.begin(),::tolower);
if (!authURL.empty()) {
// create the object
webAPI api(authURL);
StationId suid = atoi(lcaseId.c_str());
int authOK = 0;
// add our data
api.addJsonData<std::string>("user_name", id);
api.addJsonData<std::string>("user_password", key);
api.addJsonData<std::string>("ip", getRemoteAddress());
if (suid == 0)
{
std::hash<std::string> h;
suid = h(lcaseId.c_str()); //lint !e603 // Symbol 'h' not initialized (it's a functor)
}
LOG("LoginClientConnection", ("validateClient() for stationId (%lu) at IP (%s), id (%s)", m_stationId, getRemoteAddress().c_str(), lcaseId.c_str()));
if (api.submit()) {
bool status = api.getNullableValue<bool>("status");
uname = api.getString("username");
sessionID = api.getString("session_key");
std::string authURL(ConfigLoginServer::getExternalAuthUrl());
if (status && !sessionID.empty() && !uname.empty()) {
authOK = true;
if (!authURL.empty())
{
std::ostringstream postBuf;
postBuf << "user_name=" << trimmedId << "&user_password=" << trimmedKey << "&stationID=" << suid << "&ip=" << getRemoteAddress();
parentAccount = api.getString("mainAccount");
childAccounts = api.getStringMap("subAccounts");
std::string response = webAPI::simplePost(authURL, std::string(postBuf.str()), "");
if (!ConfigLoginServer::getUseOldSuidGenerator()) {
user_id = static_cast<StationId>(api.getNullableValue<int>("user_id"));
parent_id = static_cast<StationId>(api.getNullableValue<int>("parent_id"));
} else {
if (parentAccount.length() > MAX_ACCOUNT_NAME_LENGTH) {
parentAccount.resize(MAX_ACCOUNT_NAME_LENGTH);
}
if (response == "success")
{
authOK = 1;
}
else
{
ErrorMessage err("Login Failed", response);
this->send(err, true);
}
}
else
{
authOK = 1;
}
if (uname.length() > MAX_ACCOUNT_NAME_LENGTH) {
uname.resize(MAX_ACCOUNT_NAME_LENGTH);
}
parent_id = std::hash < std::string > {}(parentAccount.c_str());
user_id = std::hash < std::string > {}(uname.c_str());
}
} else {
std::string msg(api.getString("message"));
if (msg.empty()) {
msg = "Invalid username or password.";
}
ErrorMessage err("Login Failed", msg);
this->send(err, true);
}
} else {
ErrorMessage err("Login Failed", "Could not connect to remote.");
this->send(err, true);
}
} else {
// test mode
authOK = true;
testMode = true;
uname = id;
if (uname.length() > MAX_ACCOUNT_NAME_LENGTH) {
uname.resize(MAX_ACCOUNT_NAME_LENGTH);
}
user_id = std::hash < std::string > {}(uname.c_str());
}
if (authOK) {
m_stationId = user_id;
if (!testMode) {
REPORT_LOG(true, ("Client connected. Username: %s (%i) \n", uname.c_str(), user_id));
if (!parentAccount.empty()) {
if (parentAccount != uname) {
REPORT_LOG(true, ("\t%s's parent is %s (%i) \n", uname.c_str(), parentAccount.c_str(), parent_id));
}
} else {
parentAccount = "(Empty Parent!) " + uname;
}
for (auto i : childAccounts) {
StationId child_id = static_cast<StationId>(i.first);
std::string child(i.second);
if (!child.empty() && i.first > 0) {
if (ConfigLoginServer::getUseOldSuidGenerator()) {
if (child.length() > MAX_ACCOUNT_NAME_LENGTH) {
child.resize(MAX_ACCOUNT_NAME_LENGTH);
}
child_id = std::hash < std::string > {}(child.c_str());
}
REPORT_LOG((parent_id !=
child_id), ("\tchild of %s (%i) is %s (%i) \n", parentAccount.c_str(), parent_id, child.c_str(), child_id));
// insert all related accounts, if not already there, into the db
if (parent_id != child_id) {
DatabaseConnection::getInstance().upsertAccountRelationship(parent_id, child_id);
}
} else {
WARNING(true, ("Login API returned empty child account(s)."));
}
}
LoginServer::getInstance().onValidateClient(m_stationId, uname, this, true, sessionID.c_str(), 0xFFFFFFFF, 0xFFFFFFFF);
} else {
LoginServer::getInstance().onValidateClient(m_stationId, uname, this, true, nullptr, 0xFFFFFFFF, 0xFFFFFFFF);
}
LOG("LoginClientConnection", ("validateClient() for stationId (%i) at IP (%s), id (%s)", user_id, getRemoteAddress().c_str(), uname.c_str()));
}
if (authOK)
{
LoginServer::getInstance().onValidateClient(suid, lcaseId, this, true, NULL, 0xFFFFFFFF, 0xFFFFFFFF);
}
// else this case will never be reached, noop
}
// ----------------------------------------------------------------------------
/**

View File

@@ -1,207 +1,123 @@
/*
* Version: 1.75
*
* This code is just a simple wrapper around nlohmann's wonderful json lib
* (https://github.com/nlohmann/json) and libcurl. While originally included directly,
* we have come to realize that we may require web API functionality elsewhere in the future.
*
* As such, and in an effort to keep the code clean, we've broken it out into this simple little
* namespace/lib that is easy to include. Just make sure to link against curl when including, and
* make all the cmake modifications required to properly use it.
*
* (c) DarthArgus
* based on the original prototype by parz1val
*
* License: LGPL, don't be a dick please
*/
This code is just a simple wrapper around nlohmann's wonderful json lib
(https://github.com/nlohmann/json) and libcurl. While originally included directly,
we have come to realize that we may require web API functionality elsewhere in the future.
As such, and in an effort to keep the code clean, we've broken it out into this simple little
namespace/lib that is easy to include. Just make sure to link against curl when including, and
make all the cmake modifications required to properly use it.
(c) stellabellum/swgilluminati (combined crews), written by DA with help from DC
based on the original prototype by parz1val
License: what's a license? we're a bunch of dirty pirates!
*/
#include "webAPI.h"
using namespace StellaBellum;
using namespace std;
webAPI::webAPI(std::string endpoint, std::string userAgent) : uri(endpoint), userAgent(userAgent), statusCode(0) {}
webAPI::~webAPI() {
requestData.clear();
responseData.clear();
}
bool webAPI::setEndpoint(const std::string endpoint) {
uri = endpoint;
return true;
}
std::string webAPI::getRaw() {
return sResponse;
}
bool webAPI::setData(std::string &data) {
if (!data.empty()) {
sRequest = data;
return true;
}
return false;
}
std::string webAPI::getString(const std::string &slot) {
if (!responseData.empty() && !slot.empty() && responseData.count(slot) && !responseData[slot].is_null()) {
return responseData[slot].get<std::string>();
}
return std::string("");
}
std::unordered_map<int, std::string> webAPI::getStringMap(const std::string &slot) {
std::unordered_map<int, std::string> ret = std::unordered_map<int, std::string>();
if (!responseData.empty() && !slot.empty() && responseData.count(slot) && !responseData[slot].is_null()) {
nlohmann::json j = responseData[slot];
for (nlohmann::json::iterator it = j.begin(); it != j.end(); ++it) {
int k = std::stoi(it.key());
std::string val = it.value();
ret.insert({k, val});
}
}
return ret;
}
bool webAPI::submit(const int &reqType, const int &getPost, const int &respType) {
if (reqType == DTYPE::JSON) // json request
{
if (!requestData.empty()) {
// serialize our data into sRequest
sRequest = requestData.dump();
// clear our the object for next time
requestData.clear();
}
}
if (fetch(getPost, respType) && !(sResponse.empty())) {
return true;
}
sResponse.clear();
return false;
}
bool webAPI::fetch(const int &getPost, const int &mimeType) // 0 for json 1 for string
// if status == success, returns "success", or slotName's contents if specified...
// otherwise returns the "message" if no success
string webAPI::simplePost(string endpoint, string data, string slotName)
{
bool fetchStatus = false;
// declare our output and go ahead and attempt to get data from remote
nlohmann::json response = request(endpoint, data, 1);
string output;
if (!uri.empty()) //data is allowed to be an empty string if we're doing a normal GET
{
CURL *curl = curl_easy_init(); // start up curl
// if we got data back...
if (response.count("status") && response["status"].get<std::string>() == "success")
{
// use custom slot if specified (not "")
if (!(slotName.empty()) && response.count(slotName))
{
output = response[slotName].get<std::string>();
}
else
{
output = "success";
}
}
else //default message is an error, the other end always assumes "success" or the specified slot
{
if (response.count("message"))
{
output = response["message"].get<std::string>();
}
else
{
output = "Message not provided by remote.";
}
}
if (curl) {
std::string readBuffer = ""; // container for the remote response
struct curl_slist *slist = nullptr;
return output;
}
// set the content type
if (mimeType == DTYPE::JSON) {
slist = curl_slist_append(slist, "Accept: application/json");
slist = curl_slist_append(slist, "Content-Type: application/json");
} else {
slist = curl_slist_append(slist, "Content-Type: application/x-www-form-urlencoded");
}
// this can be broken out to separate the json bits later if we need raw or other http type requests
// all it does is fetch via get or post, and if the status is 200 returns the json, else error json
nlohmann::json webAPI::request(string endpoint, string data, int reqType)
{
nlohmann::json response;
slist = curl_slist_append(slist, "charsets: utf-8");
if (!endpoint.empty()) //data is allowed to be an empty string if we're doing a normal GET
{
CURL *curl = curl_easy_init(); // start up curl
CURLcode res = curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // place the data into readBuffer using writeCallback
res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // specify readBuffer as the container for data
res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
switch (getPost) {
case HTTP::GET:
res = curl_easy_setopt(curl, CURLOPT_URL, std::string(uri + "?" + sRequest).c_str());
break;
case HTTP::POST:
res = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, sRequest.c_str());
res = curl_easy_setopt(curl, CURLOPT_URL, uri.c_str());
break;
// want to do a put, or whatever other type? feel free to add here
}
// I suggest leaving VERIFYPEER = 0 because system SSL stores tend to be outdated
//if (uri.find(vxENCRYPT("stellabellum").decrypt()) != std::string::npos) {
// the public one will verify but since this is pinned we don't care about the CA
// to grab/generate, see https://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html
// under the PUBLIC KEY EXTRACTION heading
res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
// if you want to pin to your own cert or cloudflares, learn how and use the below
// res = curl_easy_setopt(curl, CURLOPT_PINNEDPUBLICKEY, vxENCRYPT("sha256//YOURKEYHERE").decrypt());
//}
if (res == CURLE_OK) {
res = curl_easy_perform(curl); // make the request!
}
if (res == CURLE_OK) {
char *contentType;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode); //get status code
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &contentType); // get response mime type
std::string conType(contentType);
if (statusCode == 200 && !(readBuffer.empty())) // check it all out and parse
if (curl)
{
sResponse = readBuffer;
if (conType == "application/json") {
fetchStatus = processJSON();
} else {
responseData.clear();
fetchStatus = true;
}
}
}
string readBuffer; // container for the remote response
long http_code = 0; // we get this after performing the get or post
curl_slist_free_all(slist);
curl_easy_cleanup(curl); // always wipe our butt
}
}
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str()); // endpoint is always specified by caller
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // place the data into readBuffer using writeCallback
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // specify readBuffer as the container for data
if (!fetchStatus) {
sResponse.clear();
responseData.clear();
}
if (reqType == 1)
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
}
return fetchStatus;
CURLcode res = curl_easy_perform(curl); // make the request!
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); //get status code
if (res == CURLE_OK && http_code == 200 && !(readBuffer.empty())) // check it all out and parse
{
try {
response = nlohmann::json::parse(readBuffer);
} catch (string e) {
response["message"] = e;
response["status"] = "failure";
} catch (...) {
response["message"] = "JSON parse error for endpoint.";
response["status"] = "failure";
}
}
else
{
response["message"] = "Error fetching data from remote.";
response["status"] = "failure";
}
curl_easy_cleanup(curl); // always wipe our butt
}
else //default err messages below
{
response["message"] = "Failed to initialize cURL.";
response["status"] = "failure";
}
}
else
{
response["message"] = "Invalid endpoint URL.";
response["status"] = "failure";
}
return response;
}
// This is used by curl to grab the response and put it into a var
size_t webAPI::writeCallback(void *contents, size_t size, size_t nmemb, void *userp) {
((std::string *) userp)->append((char *) contents, size * nmemb);
return size * nmemb;
}
bool webAPI::processJSON() {
if (!(sResponse.empty())) // check it all out and parse
{
try {
responseData = nlohmann::json::parse(sResponse);
return true;
} catch (std::string &e) {
responseData["message"] = e;
responseData["status"] = "failure";
} catch (...) {
responseData["message"] = "JSON parse error for endpoint.";
responseData["status"] = "failure";
}
} else {
responseData["message"] = "Error fetching data from remote.";
responseData["status"] = "failure";
}
return false;
size_t webAPI::writeCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
((string*)userp)->append((char*)contents, size * nmemb);
return size * nmemb;
}

View File

@@ -1,122 +1,35 @@
/*
* Version: 1.75
*
* This code is just a simple wrapper around nlohmann's wonderful json lib
* (https://github.com/nlohmann/json) and libcurl. While originally included directly,
* we have come to realize that we may require web API functionality elsewhere in the future.
*
* As such, and in an effort to keep the code clean, we've broken it out into this simple little
* namespace/lib that is easy to include. Just make sure to link against curl when including, and
* make all the cmake modifications required to properly use it.
*
* (c) DarthArgus
* based on the original prototype by parz1val
*
* License: LGPL, don't be a dick please
*/
This code is just a simple wrapper around nlohmann's wonderful json lib
(https://github.com/nlohmann/json) and libcurl. While originally included directly,
we have come to realize that we may require web API functionality elsewhere in the future.
As such, and in an effort to keep the code clean, we've broken it out into this simple little
namespace/lib that is easy to include. Just make sure to link against curl when including, and
make all the cmake modifications required to properly use it.
(c) stellabellum/swgilluminati (combined crews), written by DA with help from DC
based on the original prototype by parz1val
License: what's a license? we're a bunch of dirty pirates!
*/
#ifndef webAPI_H
#define webAPI_H
#include "json.hpp"
#ifdef WIN32
#include <curl.h>
#else
#include <unordered_map>
#include <curl/curl.h>
#endif
#include "../libLeff/libLeff.h"
namespace StellaBellum {
enum HTTP {
GET = 0, POST = 1
};
enum DTYPE {
JSON = 0, RAW = 1
};
class webAPI {
public:
// useragent
std::string userAgent;
// constructor - can setup with the endpoint from the start
webAPI(std::string endpoint, std::string userAgent = "StellaBellum webAPI");
~webAPI();
// submits the request
bool
submit(const int &reqType = DTYPE::JSON, const int &getPost = HTTP::POST, const int &respType = DTYPE::JSON);
// set the endpoint after object creation...or change the target if needed
bool setEndpoint(const std::string endpoint);
// get raw response
std::string getRaw();
// set a standard request string
bool setData(std::string &data); // all or nothing
// get a string from a given slot
std::string getString(const std::string &slot);
// get a vector of strings from a given slot
std::unordered_map<int, std::string> getStringMap(const std::string &slot);
// set json key and value for request
template<typename T> bool addJsonData(const std::string &key, const T &value) {
if (!key.empty() &&
responseData.count(key) == 0) // only alow one of a given key for now, unless we support nesting later
{
requestData[key] = value;
return true;
}
return false;
}
// get json response slot
template<typename T> T getNullableValue(const std::string &slot) {
if (!responseData.empty() && !slot.empty() && responseData.count(slot)) {
return responseData[slot].get<T>();
}
return 0;
}
private:
// json request data - object is serialized before sending, used with above setter template
nlohmann::json requestData;
// json response, stored so we can use the getter template above
nlohmann::json responseData;
// raw response
std::string sResponse;
// raw request string
std::string sRequest;
// API endpoint
std::string uri;
// fetcher - returns raw response direct from remote
bool fetch(const int &getPost = HTTP::POST, const int &mimeType = DTYPE::JSON);
// cURL writeback callback
static size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp);
// json processor - string to json
bool processJSON();
protected:
// http response code (200, 404, etc)
long statusCode;
};
}
namespace webAPI
{
using namespace std;
string simplePost(string endpoint, string data, string slotName);
//std::string simpleGet(char* endpoint, char* data);
//nlohmann::json post(char* endpoint, char* data);
//nlohmann::json get(char* endpoint, char* data);
nlohmann::json request(string endpoint, string data, int reqType); // 1 for post, 0 for get
size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp);
};
#endif