452 lines
10 KiB
C++
452 lines
10 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include <nel/misc/types_nl.h>
|
|
#include <nel/misc/common.h>
|
|
#include <nel/misc/variable.h>
|
|
#include <nel/misc/command.h>
|
|
#include <nel/misc/algo.h>
|
|
#include <nel/misc/path.h>
|
|
|
|
#include <nel/net/service.h>
|
|
//#include <nel/net/buf_server.h>
|
|
|
|
using namespace std;
|
|
using namespace NLMISC;
|
|
using namespace NLNET;
|
|
|
|
|
|
/// Callback type for web connection
|
|
typedef void (*WebCallback)(CMemStream &msgin, TSockId host);
|
|
|
|
CBufServer* WebServer = NULL;
|
|
|
|
|
|
|
|
void initWebConnection()
|
|
{
|
|
nlassert(WebServer == NULL);
|
|
|
|
uint16 port = (uint16) IService::getInstance ()->ConfigFile.getVar ("WebPort").asInt();
|
|
|
|
// if the WebPort is set to 0 then we don't initialise the web server
|
|
if (port==0)
|
|
return;
|
|
|
|
nlinfo("Initialise Web socket on port %d", port);
|
|
|
|
WebServer = new CBufServer ();
|
|
nlassert(WebServer != NULL);
|
|
WebServer->init (port);
|
|
}
|
|
|
|
|
|
void cbExecute(CMemStream &msgin, TSockId host)
|
|
{
|
|
std::string str;
|
|
msgin.serial(str);
|
|
ICommand::execute(str, *NLMISC::InfoLog);
|
|
}
|
|
|
|
|
|
|
|
void cbGetSaveList(CMemStream &msgin, TSockId host);
|
|
void cbRestoreSave(CMemStream &msgin, TSockId host);
|
|
void cbCopyOverSave(CMemStream &msgin, TSockId host);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WebCallback WebCallbackArray[] =
|
|
{
|
|
cbExecute,
|
|
cbGetSaveList,
|
|
cbRestoreSave,
|
|
cbCopyOverSave,
|
|
};
|
|
|
|
|
|
void updateWebConnection()
|
|
{
|
|
|
|
nlassert(WebServer != NULL);
|
|
|
|
try
|
|
{
|
|
WebServer->update ();
|
|
|
|
while (WebServer->dataAvailable ())
|
|
{
|
|
// create a string mem stream to easily communicate with web server
|
|
NLMISC::CMemStream msgin (true);
|
|
TSockId host;
|
|
bool success = false;
|
|
std::string reason;
|
|
|
|
try
|
|
{
|
|
WebServer->receive (msgin, &host);
|
|
|
|
uint32 fake = 0;
|
|
msgin.serial(fake);
|
|
|
|
uint8 messageType = 0xff;
|
|
msgin.serial (messageType);
|
|
|
|
if (messageType < sizeof(WebCallbackArray)/sizeof(WebCallbackArray[0]))
|
|
{
|
|
WebCallbackArray[messageType](msgin, host);
|
|
success = true;
|
|
}
|
|
}
|
|
catch (Exception &e)
|
|
{
|
|
nlwarning ("Error during receiving: '%s'", e.what ());
|
|
reason = e.what();
|
|
}
|
|
|
|
if(!success)
|
|
{
|
|
nlwarning ("Failed to decode Web command");
|
|
|
|
CMemStream msgout;
|
|
uint32 fake = 0;
|
|
msgout.serial(fake);
|
|
|
|
std::string result = "0:Failed to decode command";
|
|
if (!reason.empty())
|
|
result += " ("+reason+")";
|
|
msgout.serial (result);
|
|
WebServer->send (msgout, host);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception &e)
|
|
{
|
|
nlwarning ("Error during update: '%s'", e.what ());
|
|
}
|
|
}
|
|
|
|
void releaseWebConnection()
|
|
{
|
|
nlassert(WebServer != NULL);
|
|
|
|
delete WebServer;
|
|
WebServer = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
* Callbacks
|
|
*
|
|
*/
|
|
|
|
// Automatic SaveShardRoot path standardization
|
|
void cbOnSaveShardRootModified( NLMISC::IVariable& var )
|
|
{
|
|
var.fromString( CPath::standardizePath( var.toString() ) );
|
|
}
|
|
|
|
CVariable<string> IncrementalBackupDirectory("backup", "IncrementalBackupDirectory", "Directory to find incremental backuped archives", "", 0, true);
|
|
CVariable<string> SaveShardRoot("backup", "SaveShardRoot", "Root directory of all saved data by BS", "/home/nevrax/save_shard", 0, true, cbOnSaveShardRootModified); // (SaveShardRoot from game_share/backup_service_interface.cpp is not instanciated because the nothing is used from that file)
|
|
CVariable<string> SaveTemplatePath("backup", "SaveTemplatePath", "Directory to find saves (with shard and account replacement strings)", "$shard/characters/account_$userid_$charid$ext", 0, true);
|
|
CVariable<string> SaveExtList("backup", "SaveExtList", "List of possible extensions for save files (space separated)", "_pdr.bin _pdr.xml .bin", 0, true);
|
|
|
|
|
|
string checkFile(const string& templatePath, const string& shard, const string& userid, const string& charid, const vector<string>& extensions)
|
|
{
|
|
string result;
|
|
|
|
uint i;
|
|
for (i=0; i<extensions.size(); ++i)
|
|
{
|
|
string file = templatePath;
|
|
|
|
strFindReplace(file, string("$shard"), shard);
|
|
strFindReplace(file, string("$userid"), userid);
|
|
strFindReplace(file, string("$charid"), charid);
|
|
strFindReplace(file, string("$ext"), extensions[i]);
|
|
|
|
if (CFile::fileExists(file))
|
|
{
|
|
result += file + ":" +
|
|
toString(CFile::getFileModificationDate(file)) + ":" +
|
|
toString(CFile::getFileSize(file)) + "\n";
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void cbGetSaveList(CMemStream &msgin, TSockId host)
|
|
{
|
|
string str;
|
|
msgin.serial(str);
|
|
|
|
vector<string> params;
|
|
|
|
explode(str, string("%%"), params, true);
|
|
|
|
string incrementalDir = IncrementalBackupDirectory;
|
|
string saveShardRoot = SaveShardRoot;
|
|
string templatePath = SaveTemplatePath;
|
|
string extList = SaveExtList;
|
|
|
|
string shard;
|
|
string userid;
|
|
string charid;
|
|
|
|
uint i;
|
|
for (i=0; i<params.size(); ++i)
|
|
{
|
|
vector<string> param;
|
|
explode(params[i], string("="), param, false);
|
|
|
|
if (param.empty())
|
|
continue;
|
|
|
|
string var = param[0];
|
|
string val;
|
|
if (param.size() > 1)
|
|
val = param[1];
|
|
|
|
if (var == "incrementalDir")
|
|
incrementalDir = val;
|
|
if (var == "saveShardRoot")
|
|
saveShardRoot = val;
|
|
if (var == "templatePath")
|
|
templatePath = val;
|
|
if (var == "extList")
|
|
extList = val;
|
|
if (var == "shard")
|
|
shard = val;
|
|
if (var == "userid")
|
|
userid = val;
|
|
if (var == "charid")
|
|
charid = val;
|
|
}
|
|
|
|
incrementalDir = CPath::standardizePath(incrementalDir);
|
|
saveShardRoot = CPath::standardizePath(saveShardRoot);
|
|
|
|
vector<string> extensions;
|
|
explode(extList, string(" "), extensions, false);
|
|
|
|
string result = checkFile(saveShardRoot+templatePath, shard, userid, charid, extensions);
|
|
|
|
vector<string> incrementalDirectories;
|
|
CPath::getPathContent(incrementalDir, false, true, false, incrementalDirectories);
|
|
|
|
if (!incrementalDirectories.empty())
|
|
{
|
|
std::sort(incrementalDirectories.begin(), incrementalDirectories.end());
|
|
|
|
for (i=incrementalDirectories.size()-1; (sint)i>=0; --i)
|
|
{
|
|
string p = CPath::standardizePath(incrementalDirectories[i], true);
|
|
// avoid double / inside path
|
|
p += (templatePath[0] == '/' ? templatePath.substr(1) : templatePath);
|
|
|
|
result += checkFile(p, shard, userid, charid, extensions);
|
|
}
|
|
}
|
|
|
|
CMemStream msgout;
|
|
uint32 fake = 0;
|
|
msgout.serial(fake);
|
|
|
|
result = "1:"+result;
|
|
|
|
msgout.serial (result);
|
|
WebServer->send (msgout, host);
|
|
}
|
|
|
|
|
|
void cbRestoreSave(CMemStream &msgin, TSockId host)
|
|
{
|
|
string str;
|
|
msgin.serial(str);
|
|
|
|
vector<string> params;
|
|
|
|
explode(str, string("%%"), params, true);
|
|
|
|
string saveShardRoot = SaveShardRoot;
|
|
string templatePath = SaveTemplatePath;
|
|
|
|
string shard;
|
|
string userid;
|
|
string charid;
|
|
string file;
|
|
|
|
uint i;
|
|
for (i=0; i<params.size(); ++i)
|
|
{
|
|
vector<string> param;
|
|
explode(params[i], string("="), param, false);
|
|
|
|
if (param.empty())
|
|
continue;
|
|
|
|
string var = param[0];
|
|
string val;
|
|
if (param.size() > 1)
|
|
val = param[1];
|
|
|
|
if (var == "saveShardRoot")
|
|
saveShardRoot = val;
|
|
if (var == "templatePath")
|
|
templatePath = val;
|
|
if (var == "shard")
|
|
shard = val;
|
|
if (var == "userid")
|
|
userid = val;
|
|
if (var == "charid")
|
|
charid = val;
|
|
if (var == "file")
|
|
file = val;
|
|
}
|
|
|
|
saveShardRoot = CPath::standardizePath(saveShardRoot);
|
|
|
|
string outputfile = CPath::standardizePath(CFile::getPath(saveShardRoot+templatePath))+CFile::getFilename(file);
|
|
|
|
strFindReplace(outputfile, string("$shard"), shard);
|
|
strFindReplace(outputfile, string("$userid"), userid); // just in case...
|
|
strFindReplace(outputfile, string("$charid"), charid); //
|
|
|
|
bool success;
|
|
|
|
success = CFile::copyFile(outputfile.c_str(), file.c_str(), false);
|
|
|
|
CMemStream msgout;
|
|
uint32 fake = 0;
|
|
msgout.serial(fake);
|
|
|
|
string result;
|
|
|
|
if (success)
|
|
{
|
|
result = "1:Restore "+outputfile+" succeded.";
|
|
}
|
|
else
|
|
{
|
|
result = "0:Failed to restore "+outputfile+", copy failed.";
|
|
}
|
|
msgout.serial (result);
|
|
WebServer->send (msgout, host);
|
|
}
|
|
|
|
void cbCopyOverSave(CMemStream &msgin, TSockId host)
|
|
{
|
|
string str;
|
|
msgin.serial(str);
|
|
|
|
vector<string> params;
|
|
|
|
explode(str, string("%%"), params, true);
|
|
|
|
string saveShardRoot = SaveShardRoot;
|
|
string templatePath = SaveTemplatePath;
|
|
string extList = SaveExtList;
|
|
|
|
string shard;
|
|
string userid;
|
|
string charid;
|
|
string file;
|
|
|
|
uint i;
|
|
for (i=0; i<params.size(); ++i)
|
|
{
|
|
vector<string> param;
|
|
explode(params[i], string("="), param, false);
|
|
|
|
if (param.empty())
|
|
continue;
|
|
|
|
string var = param[0];
|
|
string val;
|
|
if (param.size() > 1)
|
|
val = param[1];
|
|
|
|
if (var == "saveShardRoot")
|
|
saveShardRoot = val;
|
|
if (var == "templatePath")
|
|
templatePath = val;
|
|
if (var == "extList")
|
|
extList = val;
|
|
if (var == "shard")
|
|
shard = val;
|
|
if (var == "userid")
|
|
userid = val;
|
|
if (var == "charid")
|
|
charid = val;
|
|
if (var == "file")
|
|
file = val;
|
|
}
|
|
|
|
saveShardRoot = CPath::standardizePath(saveShardRoot);
|
|
|
|
vector<string> extensions;
|
|
explode(extList, string(" "), extensions, false);
|
|
|
|
for (i=0; i<extensions.size(); ++i)
|
|
{
|
|
if (file.size() >= extensions[i].size() && file.substr(file.size()-extensions[i].size()) == extensions[i])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool success = false;
|
|
string result;
|
|
|
|
if (i < extensions.size())
|
|
{
|
|
string outputfile = CPath::standardizePath(saveShardRoot)+templatePath;
|
|
|
|
strFindReplace(outputfile, string("$shard"), shard);
|
|
strFindReplace(outputfile, string("$userid"), userid);
|
|
strFindReplace(outputfile, string("$charid"), charid);
|
|
strFindReplace(outputfile, string("$ext"), extensions[i]);
|
|
|
|
success = CFile::copyFile(outputfile.c_str(), file.c_str(), false);
|
|
|
|
if (!success)
|
|
result = "Failed to copy "+file+" over "+outputfile+".";
|
|
}
|
|
else
|
|
{
|
|
result = "failed to match valid extension";
|
|
}
|
|
|
|
result = string(success ? "1:" : "0:")+result;
|
|
|
|
CMemStream msgout;
|
|
uint32 fake = 0;
|
|
msgout.serial(fake);
|
|
|
|
msgout.serial (result);
|
|
WebServer->send (msgout, host);
|
|
}
|