2017-08-08 13:53:47 +02:00
|
|
|
#include "server_http.hpp"
|
|
|
|
#include "client_http.hpp"
|
|
|
|
|
|
|
|
#define BOOST_SPIRIT_THREADSAFE
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
#include <boost/property_tree/xml_parser.hpp>
|
|
|
|
#include <boost/property_tree/json_parser.hpp>
|
|
|
|
|
2017-08-19 00:18:40 +02:00
|
|
|
#include <src/servers/Server_Common/Logging/Logger.h>
|
|
|
|
#include <src/servers/Server_Common/Config/XMLConfig.h>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-19 00:18:40 +02:00
|
|
|
#include <src/servers/Server_Common/Network/Connection.h>
|
|
|
|
#include <src/servers/Server_Common/Network/Hive.h>
|
|
|
|
#include <src/servers/Server_Common/Network/Acceptor.h>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-19 00:18:40 +02:00
|
|
|
#include <src/servers/Server_Common/Exd/ExdData.h>
|
|
|
|
#include <src/servers/Server_Common/Crypt/base64.h>
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-10-07 23:10:13 +02:00
|
|
|
|
|
|
|
#include <Server_Common/Database/DbLoader.h>
|
|
|
|
#include <Server_Common/Database/CharaDbConnection.h>
|
|
|
|
#include <Server_Common/Database/DbWorkerPool.h>
|
|
|
|
#include <Server_Common/Database/PreparedStatement.h>
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
//Added for the default_resource example
|
|
|
|
#include <fstream>
|
|
|
|
#include <string>
|
|
|
|
#include <boost/filesystem.hpp>
|
2017-08-10 22:13:00 +01:00
|
|
|
#include <boost/make_shared.hpp>
|
2017-08-08 13:53:47 +02:00
|
|
|
#include <vector>
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
|
|
#include "Forwards.h"
|
|
|
|
#include "SapphireAPI.h"
|
|
|
|
|
|
|
|
|
|
|
|
Core::Logger g_log;
|
2017-10-07 23:10:13 +02:00
|
|
|
Core::Db::DbWorkerPool< Core::Db::CharaDbConnection > g_charaDb;
|
2017-08-08 13:53:47 +02:00
|
|
|
Core::Data::ExdData g_exdData;
|
|
|
|
Core::Network::SapphireAPI g_sapphireAPI;
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
|
2017-11-19 22:43:26 +01:00
|
|
|
using HttpServer = SimpleWeb::Server< SimpleWeb::HTTP >;
|
|
|
|
using HttpClient = SimpleWeb::Client< SimpleWeb::HTTP >;
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
//Added for the default_resource example
|
2017-10-26 12:16:34 +02:00
|
|
|
void default_resource_send( const HttpServer &server, const shared_ptr< HttpServer::Response > &response,
|
|
|
|
const shared_ptr< ifstream > &ifs );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
auto m_pConfig = boost::make_shared< Core::XMLConfig >();
|
2017-08-10 22:13:00 +01:00
|
|
|
HttpServer server;
|
2017-10-26 12:16:34 +02:00
|
|
|
std::string configPath( "config/settings_rest.xml" );
|
2017-08-10 22:13:00 +01:00
|
|
|
|
|
|
|
void reloadConfig()
|
|
|
|
{
|
2017-10-26 12:16:34 +02:00
|
|
|
m_pConfig = boost::make_shared< Core::XMLConfig >();
|
2017-08-10 22:13:00 +01:00
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
if( !m_pConfig->loadConfig( configPath ) )
|
2017-08-10 23:33:46 +01:00
|
|
|
throw "Error loading config ";
|
2017-08-10 22:13:00 +01:00
|
|
|
}
|
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
void print_request_info( shared_ptr< HttpServer::Request > request )
|
|
|
|
{
|
2017-08-08 13:53:47 +02:00
|
|
|
g_log.info( "Request from " + request->remote_endpoint_address + " (" + request->path + ")" );
|
|
|
|
}
|
2017-10-26 12:16:34 +02:00
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
bool loadSettings( int32_t argc, char* argv[] )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
2017-08-10 22:13:00 +01:00
|
|
|
g_log.info( "Loading config " + configPath );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
if( !m_pConfig->loadConfig( configPath ) )
|
|
|
|
{
|
|
|
|
g_log.fatal( "Error loading config " + configPath );
|
|
|
|
return false;
|
|
|
|
}
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
std::vector<std::string> args( argv + 1, argv + argc );
|
2017-10-19 07:12:09 -07:00
|
|
|
for( size_t i = 0; i + 1 < args.size(); i += 2 )
|
2017-08-10 22:13:00 +01:00
|
|
|
{
|
|
|
|
std::string arg( "" );
|
|
|
|
std::string val( "" );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
try
|
|
|
|
{
|
|
|
|
arg = boost::to_lower_copy( std::string( args[i] ) );
|
|
|
|
val = std::string( args[i + 1] );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// trim '-' from start of arg
|
|
|
|
arg = arg.erase( 0, arg.find_first_not_of( '-' ) );
|
|
|
|
|
|
|
|
if( arg == "ip" )
|
|
|
|
{
|
|
|
|
// todo: ip addr in config
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.ListenIP", val );
|
|
|
|
}
|
|
|
|
else if( arg == "p" || arg == "port" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.ListenPort", val );
|
|
|
|
}
|
|
|
|
else if( arg == "exdpath" || arg == "datapath" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.DataPath", val );
|
|
|
|
}
|
|
|
|
else if( arg == "h" || arg == "dbhost" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Host", val );
|
|
|
|
}
|
|
|
|
else if( arg == "dbport" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Port", val );
|
|
|
|
}
|
|
|
|
else if( arg == "u" || arg == "user" || arg == "dbuser" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Username", val );
|
|
|
|
}
|
|
|
|
else if( arg == "pass" || arg == "dbpass" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Pass", val );
|
|
|
|
}
|
|
|
|
else if( arg == "d" || arg == "db" || arg == "database" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.Mysql.Database", val );
|
|
|
|
}
|
|
|
|
else if ( arg == "lobbyip" || arg == "lobbyhost" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.LobbyHost", val );
|
|
|
|
}
|
|
|
|
else if ( arg == "lobbyport" )
|
|
|
|
{
|
|
|
|
m_pConfig->setValue< std::string >( "Settings.General.LobbyPort", val );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( ... )
|
|
|
|
{
|
|
|
|
g_log.error( "Error parsing argument: " + arg + " " + "value: " + val + "\n" );
|
|
|
|
g_log.error( "Usage: <arg> <val> \n" );
|
|
|
|
}
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
g_log.info( "Setting up EXD data" );
|
|
|
|
if( !g_exdData.init( m_pConfig->getValue< std::string >( "Settings.General.DataPath", "" ) ) )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
g_log.fatal( "Error setting up EXD data " );
|
2017-08-10 22:13:00 +01:00
|
|
|
return false;
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
2017-10-07 23:10:13 +02:00
|
|
|
Core::Db::DbLoader loader;
|
|
|
|
|
|
|
|
Core::Db::ConnectionInfo info;
|
|
|
|
info.password = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Pass", "" );
|
|
|
|
info.host = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Host", "127.0.0.1" );
|
|
|
|
info.database = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" );
|
|
|
|
info.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 );
|
|
|
|
info.user = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" );
|
|
|
|
info.syncThreads = m_pConfig->getValue< uint8_t >( "Settings.General.Mysql.SyncThreads", 2 );
|
|
|
|
info.asyncThreads = m_pConfig->getValue< uint8_t >( "Settings.General.Mysql.AsyncThreads", 2 );
|
|
|
|
|
|
|
|
loader.addDb( g_charaDb, info );
|
|
|
|
if( !loader.initDbs() )
|
|
|
|
return false;
|
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
server.config.port = static_cast< uint16_t >( std::stoul( m_pConfig->getValue< std::string >( "Settings.General.HttpPort", "80" ) ) );
|
2017-08-10 22:13:00 +01:00
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
g_log.info( "Database: Connected to " + info.host + ":" + std::to_string( info.port ) );
|
2017-08-10 22:13:00 +01:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-10-26 12:16:34 +02:00
|
|
|
int main( int argc, char* argv[] )
|
2017-08-10 22:13:00 +01:00
|
|
|
{
|
|
|
|
g_log.setLogPath( "log\\SapphireAPI" );
|
|
|
|
g_log.init();
|
|
|
|
|
|
|
|
g_log.info( "===========================================================" );
|
|
|
|
g_log.info( "Sapphire API Server " );
|
|
|
|
g_log.info( "Version: 0.0.1" );
|
|
|
|
g_log.info( "Compiled: " __DATE__ " " __TIME__ );
|
|
|
|
g_log.info( "===========================================================" );
|
|
|
|
|
|
|
|
if (!loadSettings(argc, argv))
|
|
|
|
{
|
|
|
|
throw std::exception();
|
2017-08-08 13:53:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
g_exdData.loadZoneInfo();
|
2017-08-09 00:36:26 +02:00
|
|
|
g_exdData.loadClassJobInfo();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
server.config.port = stoi( m_pConfig->getValue< std::string >( "Settings.General.HttpPort", "80" ) );
|
|
|
|
g_log.info( "Starting REST server at port " + m_pConfig->getValue< std::string >( "Settings.General.HttpPort", "80" ) + "..." );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/ZoneName/([0-9]+)$"]["GET"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
string number = request->path_match[1];
|
|
|
|
auto it = g_exdData.m_zoneInfoMap.find( atoi( number.c_str() ) );
|
|
|
|
std::string responseStr = "Not found!";
|
|
|
|
if( it != g_exdData.m_zoneInfoMap.end() )
|
|
|
|
{
|
|
|
|
responseStr = it->second.zone_name + ", " + it->second.zone_str;
|
|
|
|
}
|
|
|
|
*response << "HTTP/1.1 200 OK\r\nContent-Length: " << responseStr.length() << "\r\n\r\n" << responseStr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Create account */
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/createAccount"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
std::string responseStr = "HTTP/1.1 400\r\n\r\n";
|
|
|
|
try
|
|
|
|
{
|
|
|
|
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string pass = pt.get<string>( "pass" );
|
|
|
|
std::string user = pt.get<string>( "username" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
std::string sId;
|
|
|
|
if( g_sapphireAPI.createAccount( user, pass, sId ) )
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"sId\":\"" + sId + "\", \"lobbyHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.LobbyHost" ) + "\", \"frontierHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.FrontierHost" ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200 OK\r\n "
|
|
|
|
<< "Content-Type: application/json\r\n"
|
|
|
|
<< "Content-Length: " << json_string.length() << "\r\n\r\n"
|
|
|
|
<< json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*response << "HTTP/1.1 400\r\n\r\n";
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
*response << "HTTP/1.1 200 OK\r\nContent-Length: " << responseStr.length() << "\r\nContent-Type: text/xml\r\n\r\n" << responseStr;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/login"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string pass = pt.get<string>( "pass" );
|
|
|
|
std::string user = pt.get<string>( "username" );
|
|
|
|
|
|
|
|
std::string sId;
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
if( g_sapphireAPI.login( user, pass, sId ) )
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"sId\":\"" + sId + "\", \"lobbyHost\":\"" + m_pConfig->getValue< std::string >("Settings.General.LobbyHost") + "\", \"frontierHost\":\"" + m_pConfig->getValue< std::string >( "Settings.General.FrontierHost" ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*response << "HTTP/1.1 400\r\n\r\n";
|
|
|
|
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/deleteCharacter"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string sId = pt.get<string>( "sId" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
std::string name = pt.get<string>( "name" );
|
2017-08-10 22:13:00 +01:00
|
|
|
|
|
|
|
// reloadConfig();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t accountId = g_sapphireAPI.checkSession( sId );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_sapphireAPI.deleteCharacter( name, accountId );
|
|
|
|
std::string json_string = "{\"result\":\"success\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/createCharacter"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string sId = pt.get<string>( "sId" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
std::string name = pt.get<string>( "name" );
|
|
|
|
std::string infoJson = pt.get<string>( "infoJson" );
|
|
|
|
|
|
|
|
std::string finalJson = Core::Util::base64_decode( infoJson );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t result = g_sapphireAPI.checkSession( sId );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( result != -1 )
|
|
|
|
{
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-09-11 18:59:50 +02:00
|
|
|
int32_t charId = g_sapphireAPI.createCharacter( result, name, finalJson, m_pConfig->getValue< uint8_t >( "Settings.Parameters.DefaultGMRank", 255 ) );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
std::string json_string = "{\"result\":\"" + std::to_string( charId ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"invalid\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/insertSession"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 22:55:02 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string sId = pt.get<string>( "sId" );
|
|
|
|
uint32_t accountId = pt.get<uint32_t>( "accountId" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
2017-08-08 22:55:02 +02:00
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
g_sapphireAPI.insertSession( accountId, sId );
|
|
|
|
std::string json_string = "{\"result\":\"success\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/checkNameTaken"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string name = pt.get<string>( "name" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string;
|
|
|
|
if( !g_sapphireAPI.checkNameTaken( name ) )
|
|
|
|
json_string = "{\"result\":\"false\"}";
|
|
|
|
else
|
|
|
|
json_string = "{\"result\":\"true\"}";
|
|
|
|
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/checkSession"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string sId = pt.get<string>( "sId" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t result = g_sapphireAPI.checkSession( sId );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
if( result != -1 )
|
|
|
|
{
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"" + std::to_string( result ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"invalid\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/getNextCharId"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"" + std::to_string( g_sapphireAPI.getNextCharId() ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/getNextContentId"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"" + std::to_string( g_sapphireAPI.getNextContentId() ) + "\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^/sapphire-api/lobby/getCharacterList"]["POST"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
using namespace boost::property_tree;
|
|
|
|
ptree pt;
|
|
|
|
read_json( request->content, pt );
|
|
|
|
|
|
|
|
std::string sId = pt.get<string>( "sId" );
|
|
|
|
std::string secret = pt.get<string>( "secret" );
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
// reloadConfig();
|
|
|
|
|
2017-08-11 22:56:30 +01:00
|
|
|
int32_t result = g_sapphireAPI.checkSession( sId );
|
2017-08-08 13:53:47 +02:00
|
|
|
|
|
|
|
if( result != -1 )
|
|
|
|
{
|
|
|
|
|
|
|
|
if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) {
|
|
|
|
std::string json_string = "{\"result\":\"invalid_secret\"}";
|
|
|
|
*response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
auto charList = g_sapphireAPI.getCharList( result );
|
|
|
|
using boost::property_tree::ptree;
|
|
|
|
ptree pt;
|
|
|
|
ptree char_tree;
|
|
|
|
|
|
|
|
for( auto entry : charList )
|
|
|
|
{
|
|
|
|
ptree tree_entry;
|
|
|
|
tree_entry.put( "name", std::string( entry.getName() ) );
|
|
|
|
tree_entry.put( "charId", std::to_string( entry.getId() ) );
|
|
|
|
tree_entry.put( "contentId", std::to_string( entry.getContentId() ) );
|
|
|
|
tree_entry.put( "infoJson", std::string( entry.getInfoJson() ) );
|
|
|
|
char_tree.push_back( std::make_pair( "", tree_entry ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
pt.add_child( "charArray", char_tree );
|
|
|
|
pt.put( "result", "success" );
|
|
|
|
std::ostringstream oss;
|
|
|
|
write_json( oss, pt );
|
|
|
|
std::string responseStr = oss.str();
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << responseStr.length() << "\r\n\r\n" << responseStr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::string json_string = "{\"result\":\"invalid\"}";
|
|
|
|
*response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^(/frontier-api/ffxivsupport/view/get_init)(.*)"]["GET"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:05:33 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto web_root_path = boost::filesystem::canonical( "web" );
|
|
|
|
auto path = boost::filesystem::canonical( web_root_path / "news.xml" );
|
|
|
|
//Check if path is within web_root_path
|
|
|
|
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
|
2017-10-24 10:51:51 -07:00
|
|
|
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
|
2017-08-08 13:05:33 +02:00
|
|
|
throw invalid_argument( "path must be within root path" );
|
|
|
|
if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) )
|
|
|
|
throw invalid_argument( "file does not exist" );
|
|
|
|
|
|
|
|
std::string cache_control, etag;
|
|
|
|
|
|
|
|
// Uncomment the following line to enable Cache-Control
|
|
|
|
// cache_control="Cache-Control: max-age=86400\r\n";
|
|
|
|
|
|
|
|
auto ifs = make_shared<ifstream>();
|
|
|
|
ifs->open( path.string(), ifstream::in | ios::binary | ios::ate );
|
|
|
|
|
|
|
|
if( *ifs )
|
|
|
|
{
|
|
|
|
auto length = ifs->tellg();
|
|
|
|
ifs->seekg( 0, ios::beg );
|
|
|
|
|
|
|
|
*response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n";
|
|
|
|
default_resource_send( server, response, ifs );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw invalid_argument( "could not read file" );
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
server.resource["^(/frontier-api/ffxivsupport/information/get_headline_all)(.*)"]["GET"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:05:33 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto web_root_path = boost::filesystem::canonical( "web" );
|
|
|
|
auto path = boost::filesystem::canonical( web_root_path / "headlines.xml" );
|
|
|
|
//Check if path is within web_root_path
|
|
|
|
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
|
2017-10-24 10:51:51 -07:00
|
|
|
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
|
2017-08-08 13:05:33 +02:00
|
|
|
throw invalid_argument( "path must be within root path" );
|
|
|
|
if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) )
|
|
|
|
throw invalid_argument( "file does not exist" );
|
|
|
|
|
|
|
|
std::string cache_control, etag;
|
|
|
|
|
|
|
|
// Uncomment the following line to enable Cache-Control
|
|
|
|
// cache_control="Cache-Control: max-age=86400\r\n";
|
|
|
|
|
|
|
|
auto ifs = make_shared<ifstream>();
|
|
|
|
ifs->open( path.string(), ifstream::in | ios::binary | ios::ate );
|
|
|
|
|
|
|
|
if( *ifs )
|
|
|
|
{
|
|
|
|
auto length = ifs->tellg();
|
|
|
|
ifs->seekg( 0, ios::beg );
|
|
|
|
|
|
|
|
*response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n";
|
|
|
|
default_resource_send( server, response, ifs );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw invalid_argument( "could not read file" );
|
|
|
|
}
|
|
|
|
catch( exception& e )
|
|
|
|
{
|
|
|
|
*response << "HTTP/1.1 500\r\n\r\n";
|
|
|
|
g_log.error( e.what() );
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
2017-08-08 13:53:47 +02:00
|
|
|
//Default GET-example. If no other matches, this anonymous function will be called.
|
|
|
|
//Will respond with content in the web/-directory, and its subdirectories.
|
|
|
|
//Default file: index.html
|
|
|
|
//Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server
|
2017-08-10 22:13:00 +01:00
|
|
|
server.default_resource["GET"] = [&]( shared_ptr<HttpServer::Response> response, shared_ptr<HttpServer::Request> request ) {
|
2017-08-08 13:53:47 +02:00
|
|
|
print_request_info( request );
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto web_root_path = boost::filesystem::canonical( "web" );
|
|
|
|
auto path = boost::filesystem::canonical( web_root_path / request->path );
|
|
|
|
//Check if path is within web_root_path
|
|
|
|
if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) ||
|
2017-10-24 10:51:51 -07:00
|
|
|
!std::equal( web_root_path.begin(), web_root_path.end(), path.begin() ) )
|
2017-08-08 13:53:47 +02:00
|
|
|
throw invalid_argument( "path must be within root path" );
|
|
|
|
if( boost::filesystem::is_directory( path ) )
|
|
|
|
path /= "index.html";
|
|
|
|
if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) )
|
|
|
|
throw invalid_argument( "file does not exist" );
|
|
|
|
|
|
|
|
std::string cache_control, etag;
|
|
|
|
|
|
|
|
// Uncomment the following line to enable Cache-Control
|
|
|
|
// cache_control="Cache-Control: max-age=86400\r\n";
|
|
|
|
|
|
|
|
auto ifs = make_shared<ifstream>();
|
|
|
|
ifs->open( path.string(), ifstream::in | ios::binary | ios::ate );
|
|
|
|
|
|
|
|
if( *ifs )
|
|
|
|
{
|
|
|
|
auto length = ifs->tellg();
|
|
|
|
ifs->seekg( 0, ios::beg );
|
|
|
|
|
|
|
|
*response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n";
|
|
|
|
default_resource_send( server, response, ifs );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw invalid_argument( "could not read file" );
|
|
|
|
}
|
2017-10-21 20:47:50 -07:00
|
|
|
catch( const exception & )
|
2017-08-08 13:53:47 +02:00
|
|
|
{
|
|
|
|
string content = "Path not found: " + request->path;
|
|
|
|
*response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << content.length() << "\r\n\r\n" << content;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2017-08-10 22:13:00 +01:00
|
|
|
thread server_thread( [&]() {
|
2017-08-08 13:53:47 +02:00
|
|
|
//Start server
|
|
|
|
server.start();
|
|
|
|
} );
|
|
|
|
|
|
|
|
//Wait for server to start so that the client can connect
|
|
|
|
this_thread::sleep_for( chrono::seconds( 1 ) );
|
|
|
|
|
|
|
|
server_thread.join();
|
|
|
|
g_log.info( "Started REST server at port " + std::to_string( server.config.port ) );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void default_resource_send( const HttpServer &server, const shared_ptr<HttpServer::Response> &response,
|
|
|
|
const shared_ptr<ifstream> &ifs )
|
|
|
|
{
|
|
|
|
//read and send 128 KB at a time
|
|
|
|
static vector<char> buffer( 131072 ); // Safe when server is running on one thread
|
|
|
|
streamsize read_length;
|
|
|
|
if( ( read_length = ifs->read( &buffer[0], buffer.size() ).gcount() ) > 0 )
|
|
|
|
{
|
|
|
|
response->write( &buffer[0], read_length );
|
|
|
|
if( read_length == static_cast< streamsize >( buffer.size() ) )
|
|
|
|
{
|
|
|
|
server.send( response, [&server, response, ifs]( const boost::system::error_code &ec ) {
|
|
|
|
if( !ec )
|
|
|
|
default_resource_send( server, response, ifs );
|
|
|
|
else
|
|
|
|
cerr << "Connection interrupted" << endl;
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|