mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
3220 lines
78 KiB
C++
3220 lines
78 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2016 Martin Raiber
|
|
*
|
|
* 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 "RestoreFiles.h"
|
|
#include "../Interface/Server.h"
|
|
#include "../Interface/SettingsReader.h"
|
|
#include "ClientService.h"
|
|
#include "InternetClient.h"
|
|
#include "ServerIdentityMgr.h"
|
|
#include "../common/data.h"
|
|
#include "../urbackupcommon/capa_bits.h"
|
|
#include "../urbackupcommon/escape.h"
|
|
#include "client.h"
|
|
#include "database.h"
|
|
#include "../stringtools.h"
|
|
#include "../urbackupcommon/json.h"
|
|
#include "../cryptoplugin/ICryptoFactory.h"
|
|
#include "file_permissions.h"
|
|
#ifdef _WIN32
|
|
#include "win_sysvol.h"
|
|
#include "win_ver.h"
|
|
#include "win_all_volumes.h"
|
|
#include "DirectoryWatcherThread.h"
|
|
#include "win_network_cost.h"
|
|
#else
|
|
#include "lin_ver.h"
|
|
#include "lin_sysvol.h"
|
|
#endif
|
|
#include "../client_version.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <limits.h>
|
|
|
|
|
|
#ifndef _WIN32
|
|
#define _atoi64 atoll
|
|
#endif
|
|
|
|
extern std::string time_format_str;
|
|
|
|
extern ICryptoFactory *crypto_fak;
|
|
|
|
void ClientConnector::CMD_ADD_IDENTITY(const std::string &identity, const std::string &cmd, bool ident_ok)
|
|
{
|
|
bool create_token_dir = false;
|
|
|
|
if(identity.empty())
|
|
{
|
|
tcpstack.Send(pipe, "Identity empty");
|
|
}
|
|
else
|
|
{
|
|
if(Server->getServerParameter("restore_mode")=="true" && !ident_ok )
|
|
{
|
|
ServerIdentityMgr::addServerIdentity(identity, SPublicKeys());
|
|
createFacet(identity, endpoint_name);
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else if( ident_ok )
|
|
{
|
|
createFacet(identity, endpoint_name);
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else
|
|
{
|
|
ServerIdentityMgr::loadServerIdentities();
|
|
if( ServerIdentityMgr::checkServerIdentity(identity) )
|
|
{
|
|
createFacet(identity, endpoint_name);
|
|
if(ServerIdentityMgr::hasPublicKey(identity, true))
|
|
{
|
|
tcpstack.Send(pipe, "needs certificate");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
return;
|
|
}
|
|
|
|
if( ServerIdentityMgr::numServerIdentities()==0 )
|
|
{
|
|
ServerIdentityMgr::addServerIdentity(identity, SPublicKeys());
|
|
createFacet(identity, endpoint_name);
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Rejected server identity " + identity, LL_INFO);
|
|
if( !ServerIdentityMgr::hasOnlineServer() && ServerIdentityMgr::isNewIdentity(identity) )
|
|
{
|
|
IScopedLock lock(ident_mutex);
|
|
new_server_idents.push_back(identity);
|
|
}
|
|
tcpstack.Send(pipe, "failed");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_ADD_IDENTITY(const std::string & params)
|
|
{
|
|
str_map m_params;
|
|
ParseParamStrHttp(params, &m_params);
|
|
|
|
ServerIdentityMgr::loadServerIdentities();
|
|
std::string identity = m_params["identity"];
|
|
if (!ServerIdentityMgr::checkServerIdentity(identity))
|
|
{
|
|
ServerIdentityMgr::addServerIdentity(identity, SPublicKeys());
|
|
}
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_CHALLENGE(const std::string &identity, const std::string& cmd)
|
|
{
|
|
if(identity.empty())
|
|
{
|
|
tcpstack.Send(pipe, "");
|
|
return;
|
|
}
|
|
|
|
std::string clientsubname;
|
|
bool with_enc = false;
|
|
std::string ecies_pubkey;
|
|
if (cmd.size() > 14)
|
|
{
|
|
std::string s_params = cmd.substr(14);
|
|
str_map params;
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
|
|
clientsubname = params["clientsubname"];
|
|
ecies_pubkey = base64_decode_dash(params["ecies_pubkey"]);
|
|
with_enc = params["with_enc"] == "1";
|
|
}
|
|
|
|
bool local_encrypted = false;
|
|
bool local_compressed = false;
|
|
IECDHKeyExchange* shared_key_exchange = nullptr;
|
|
std::string ret_params;
|
|
|
|
if (!internet_conn)
|
|
{
|
|
std::unique_ptr<ISettingsReader> settings(
|
|
Server->createFileSettingsReader("urbackup/data/settings.cfg"));
|
|
|
|
local_encrypted = settings->getValue("local_encrypted", true);
|
|
local_compressed = settings->getValue("local_compressed", true);
|
|
}
|
|
|
|
if (with_enc)
|
|
{
|
|
if (local_compressed)
|
|
{
|
|
#ifndef NO_ZSTD_COMPRESSION
|
|
ret_params += "&compress=zstd";
|
|
#else
|
|
ret_params += "&compress=zlib";
|
|
#endif
|
|
}
|
|
|
|
if (local_encrypted)
|
|
{
|
|
shared_key_exchange = crypto_fak->createECDHKeyExchange();
|
|
ret_params += "&pubkey_ecdh233k1=" + base64_encode_dash(shared_key_exchange->getPublicKey());
|
|
}
|
|
|
|
if (!ret_params.empty())
|
|
ret_params[0] = '?';
|
|
}
|
|
else if (local_encrypted)
|
|
{
|
|
Server->Log("Client requires encryption which server does not offer", LL_ERROR);
|
|
return;
|
|
}
|
|
|
|
IScopedLock lock(ident_mutex);
|
|
std::string challenge = Server->secureRandomString(30)+"-"+convert(Server->getTimeSeconds())+"-"+convert(Server->getTimeMS());
|
|
challenges[std::make_pair(identity, clientsubname)]=SChallenge(challenge, shared_key_exchange, local_compressed);
|
|
|
|
tcpstack.Send(pipe, challenge + ret_params);
|
|
}
|
|
|
|
void ClientConnector::CMD_SIGNATURE(const std::string &identity, const std::string &cmd)
|
|
{
|
|
if(identity.empty())
|
|
{
|
|
Server->Log("Signature error: Empty identity", LL_ERROR);
|
|
tcpstack.Send(pipe, "empty identity");
|
|
return;
|
|
}
|
|
if(crypto_fak==nullptr)
|
|
{
|
|
Server->Log("Signature error: No crypto module", LL_ERROR);
|
|
tcpstack.Send(pipe, "no crypto");
|
|
return;
|
|
}
|
|
|
|
size_t hashpos = cmd.find("#");
|
|
if (hashpos == std::string::npos)
|
|
{
|
|
Server->Log("Signature error: No parameters", LL_ERROR);
|
|
tcpstack.Send(pipe, "no parameters");
|
|
return;
|
|
}
|
|
|
|
str_map params;
|
|
ParseParamStrHttp(cmd.substr(hashpos + 1), ¶ms);
|
|
|
|
std::string clientsubname = params["clientsubname"];
|
|
|
|
IScopedLock lock(ident_mutex);
|
|
|
|
std::map<std::pair<std::string, std::string>, SChallenge>::iterator challenge_it = challenges.find(std::make_pair(identity, clientsubname));
|
|
|
|
if(challenge_it==challenges.end() || challenge_it->second.challenge_str.empty())
|
|
{
|
|
Server->Log("Signature error: No challenge", LL_ERROR);
|
|
tcpstack.Send(pipe, "no challenge");
|
|
return;
|
|
}
|
|
|
|
const std::string& challenge = challenge_it->second.challenge_str;
|
|
|
|
|
|
std::string pubkey = base64_decode_dash(params["pubkey"]);
|
|
std::string pubkey_ecdsa409k1 = base64_decode_dash(params["pubkey_ecdsa409k1"]);
|
|
std::string signature = base64_decode_dash(params["signature"]);
|
|
std::string signature_ecdsa409k1 = base64_decode_dash(params["signature_ecdsa409k1"]);
|
|
std::string session_identity = params["session_identity"];
|
|
std::string pubkey_ecdh233k1 = base64_decode_dash(params["pubkey_ecdh233k1"]);
|
|
std::string signature_ecdh233k1 = base64_decode_dash(params["signature_ecdh233k1"]);
|
|
std::string secret_session_key = base64_decode_dash(params["secret_session_key"]);
|
|
std::string signature_ecies = base64_decode_dash(params["signature_ecies"]);
|
|
std::string ecdh_shared_key;
|
|
std::string secret_session_key_decrypted;
|
|
|
|
if(!ServerIdentityMgr::hasPublicKey(identity, false))
|
|
{
|
|
if (!ServerIdentityMgr::setPublicKeys(identity, SPublicKeys(pubkey, pubkey_ecdsa409k1)))
|
|
{
|
|
tcpstack.Send(pipe, "pubkey fingerprint mismatch");
|
|
return;
|
|
}
|
|
}
|
|
|
|
SPublicKeys pubkeys = ServerIdentityMgr::getPublicKeys(identity);
|
|
|
|
if( (!pubkeys.ecdsa409k1_key.empty() && crypto_fak->verifyData(pubkeys.ecdsa409k1_key, challenge, signature_ecdsa409k1))
|
|
|| (pubkeys.ecdsa409k1_key.empty() && !pubkeys.dsa_key.empty() && crypto_fak->verifyDataDSA(pubkeys.dsa_key, challenge, signature)) )
|
|
{
|
|
if (challenge_it->second.shared_key_exchange==nullptr ||
|
|
(crypto_fak->verifyData(pubkeys.ecdsa409k1_key, challenge + pubkey_ecdh233k1, signature_ecdh233k1) &&
|
|
!(ecdh_shared_key = challenge_it->second.shared_key_exchange->getSharedKey(pubkey_ecdh233k1)).empty() &&
|
|
!(secret_session_key_decrypted = crypto_fak->decryptAuthenticatedAES(secret_session_key,
|
|
ecdh_shared_key, 1)).empty()
|
|
) )
|
|
{
|
|
if (!ServerIdentityMgr::setPublicKeys(identity, SPublicKeys(pubkey, pubkey_ecdsa409k1)))
|
|
{
|
|
tcpstack.Send(pipe, "pubkey fingerprint mismatch (2)");
|
|
return;
|
|
}
|
|
|
|
ServerIdentityMgr::addSessionIdentity(session_identity, endpoint_name, identity,
|
|
secret_session_key_decrypted);
|
|
|
|
std::string facet_dir = Server->getServerWorkingDir() + os_file_sep()
|
|
+ "urbackup" + os_file_sep() + "data_" + convert(getFacetId(identity));
|
|
IndexThread::getFileSrv()->shareDir("urbackup", facet_dir, "#I" + session_identity + "#", false);
|
|
|
|
tcpstack.Send(pipe, "ok");
|
|
Server->destroy(challenge_it->second.shared_key_exchange);
|
|
challenges.erase(challenge_it);
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Encryption session key exchange failed", LL_ERROR);
|
|
tcpstack.Send(pipe, "encryption session key exchange failed");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Signature error: Verification failed", LL_ERROR);
|
|
tcpstack.Send(pipe, "signature verification failed");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_START_INCR_FILEBACKUP(const std::string &cmd, const std::string& server_identity)
|
|
{
|
|
timeoutAsyncFileIndex();
|
|
|
|
std::string s_params;
|
|
if(next(cmd, 0, "3START BACKUP"))
|
|
{
|
|
file_version=2;
|
|
if(cmd.size()>14)
|
|
s_params=cmd.substr(14);
|
|
}
|
|
else if(cmd=="2START BACKUP")
|
|
{
|
|
file_version=2;
|
|
}
|
|
|
|
str_map params;
|
|
if(!s_params.empty())
|
|
{
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
}
|
|
|
|
int64 server_id = watoi64(params["status_id"]);
|
|
std::string resume = params["resume"];
|
|
std::string sha_version_str = params["sha"];
|
|
int sha_version = 512;
|
|
if(!sha_version_str.empty())
|
|
{
|
|
sha_version = watoi(sha_version_str);
|
|
}
|
|
|
|
int group=c_group_default;
|
|
|
|
str_map::iterator it_group = params.find("group");
|
|
if(it_group!=params.end())
|
|
{
|
|
group = watoi(it_group->second);
|
|
}
|
|
|
|
std::string clientsubname;
|
|
|
|
str_map::iterator it_clientsubname = params.find("clientsubname");
|
|
if(it_clientsubname!=params.end())
|
|
{
|
|
clientsubname = conv_filename((it_clientsubname->second));
|
|
}
|
|
|
|
int facet_id = getFacetId(ServerIdentityMgr::getIdentityFromSessionIdentity(server_identity));
|
|
std::string dest, dest_params, computername;
|
|
str_map dest_secret_params;
|
|
size_t max_backups=1;
|
|
std::string perm_uid;
|
|
getBackupDest(clientsubname, facet_id, dest, dest_params,
|
|
dest_secret_params, computername, max_backups, perm_uid);
|
|
|
|
if (localBackup(dest, dest_params, dest_secret_params, computername,
|
|
false, max_backups, server_identity, params, perm_uid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
unsigned int flags = 0;
|
|
|
|
if(params.find("with_scripts")!=params.end())
|
|
{
|
|
flags |= flag_with_scripts;
|
|
}
|
|
|
|
if(params.find("with_orig_path")!=params.end())
|
|
{
|
|
flags |= flag_with_orig_path;
|
|
}
|
|
|
|
if(params.find("with_sequence")!=params.end())
|
|
{
|
|
flags |= flag_with_sequence;
|
|
}
|
|
|
|
if(params.find("with_proper_symlinks")!=params.end())
|
|
{
|
|
flags |= flag_with_proper_symlinks;
|
|
}
|
|
|
|
if(end_to_end_file_backup_verification_enabled)
|
|
{
|
|
flags |= flag_end_to_end_verification;
|
|
}
|
|
|
|
if(calculateFilehashesOnClient(clientsubname, facet_id))
|
|
{
|
|
flags |= flag_calc_checksums;
|
|
}
|
|
|
|
int running_jobs = 2;
|
|
str_map::iterator it_running_jobs = params.find("running_jobs");
|
|
if (it_running_jobs != params.end())
|
|
{
|
|
running_jobs = watoi(it_running_jobs->second);
|
|
}
|
|
|
|
bool async_list = params.find("async") != params.end()
|
|
&& params["async"] == "1";
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_StartIncrFileBackup);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(server_token);
|
|
data.addInt(group);
|
|
data.addInt(flags);
|
|
data.addString(clientsubname);
|
|
data.addInt(sha_version);
|
|
data.addInt(running_jobs);
|
|
data.addInt(getFacetId(ServerIdentityMgr::getIdentityFromSessionIdentity(server_identity)));
|
|
data.addChar(async_list ? 1 : 0);
|
|
|
|
std::string async_id;
|
|
if (async_list)
|
|
{
|
|
async_id.resize(16);
|
|
Server->randomFill(&async_id[0], async_id.size());
|
|
}
|
|
|
|
if (async_list &&
|
|
(flags & flag_calc_checksums) &&
|
|
params["phash"] == "1")
|
|
{
|
|
data.addString2(async_id);
|
|
}
|
|
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
lasttime=Server->getTimeMS();
|
|
|
|
RunningAction action = RUNNING_NONE;
|
|
if(resume.empty())
|
|
{
|
|
action =RUNNING_INCR_FILE;
|
|
}
|
|
else if(resume=="full")
|
|
{
|
|
action =RUNNING_RESUME_FULL_FILE;
|
|
}
|
|
else if(resume=="incr")
|
|
{
|
|
action =RUNNING_RESUME_INCR_FILE;
|
|
}
|
|
|
|
SRunningProcess new_proc;
|
|
|
|
new_proc.action = action;
|
|
new_proc.server_id = server_id;
|
|
new_proc.id = ++curr_backup_running_id;
|
|
new_proc.server_token = server_token;
|
|
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
removeTimedOutProcesses(server_token, true);
|
|
|
|
running_processes.push_back(new_proc);
|
|
|
|
status_updated = true;
|
|
|
|
end_to_end_file_backup_verification_enabled=false;
|
|
|
|
if (!async_list)
|
|
{
|
|
state = CCSTATE_START_FILEBACKUP;
|
|
curr_backup_tt = ILLEGAL_THREADPOOL_TICKET;
|
|
}
|
|
else
|
|
{
|
|
process_lock.relock(nullptr);
|
|
|
|
SAsyncFileList new_async_file_list = {
|
|
Server->getTimeMS(),
|
|
curr_result_id,
|
|
0,
|
|
ILLEGAL_THREADPOOL_TICKET
|
|
};
|
|
|
|
async_file_index[async_id] = new_async_file_list;
|
|
|
|
lock.relock(nullptr);
|
|
|
|
IndexThread::refResult(curr_result_id);
|
|
|
|
tcpstack.Send(pipe, "ASYNC-async_id=" + bytesToHex(async_id));
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_START_FULL_FILEBACKUP(const std::string &cmd, const std::string& server_identity)
|
|
{
|
|
timeoutAsyncFileIndex();
|
|
|
|
std::string s_params;
|
|
if(cmd=="2START FULL BACKUP")
|
|
{
|
|
file_version=2;
|
|
}
|
|
else if(next(cmd,0,"3START FULL BACKUP"))
|
|
{
|
|
file_version=2;
|
|
if(cmd.size()>19)
|
|
s_params=cmd.substr(19);
|
|
}
|
|
|
|
str_map params;
|
|
if(!s_params.empty())
|
|
{
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
}
|
|
|
|
int group=c_group_default;
|
|
|
|
str_map::iterator it_group = params.find("group");
|
|
if(it_group!=params.end())
|
|
{
|
|
group = watoi(it_group->second);
|
|
}
|
|
|
|
int64 server_id = watoi64(params["status_id"]);
|
|
|
|
std::string clientsubname;
|
|
str_map::iterator it_clientsubname = params.find("clientsubname");
|
|
if (it_clientsubname != params.end())
|
|
{
|
|
clientsubname = conv_filename((it_clientsubname->second));
|
|
}
|
|
|
|
int facet_id = getFacetId(ServerIdentityMgr::getIdentityFromSessionIdentity(server_identity));
|
|
std::string dest, dest_params, computername;
|
|
str_map dest_secret_params;
|
|
size_t max_backups = 1;
|
|
std::string perm_uid;
|
|
getBackupDest(clientsubname, facet_id, dest, dest_params, dest_secret_params,
|
|
computername, max_backups, perm_uid);
|
|
|
|
if (localBackup(dest, dest_params, dest_secret_params, computername,
|
|
true, max_backups, server_identity, params, perm_uid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string sha_version_str = params["sha"];
|
|
int sha_version = 512;
|
|
if(!sha_version_str.empty())
|
|
{
|
|
sha_version = watoi(sha_version_str);
|
|
}
|
|
|
|
|
|
int flags = 0;
|
|
|
|
if(params.find("with_scripts")!=params.end())
|
|
{
|
|
flags |= flag_with_scripts;
|
|
}
|
|
|
|
if(params.find("with_orig_path")!=params.end())
|
|
{
|
|
flags |= flag_with_orig_path;
|
|
}
|
|
|
|
if(params.find("with_sequence")!=params.end())
|
|
{
|
|
flags |= flag_with_sequence;
|
|
}
|
|
|
|
if(params.find("with_proper_symlinks")!=params.end())
|
|
{
|
|
flags |= flag_with_proper_symlinks;
|
|
}
|
|
|
|
if(end_to_end_file_backup_verification_enabled)
|
|
{
|
|
flags |= flag_end_to_end_verification;
|
|
}
|
|
|
|
if(calculateFilehashesOnClient(clientsubname, facet_id))
|
|
{
|
|
flags |= flag_calc_checksums;
|
|
}
|
|
|
|
int running_jobs = 1;
|
|
str_map::iterator it_running_jobs = params.find("running_jobs");
|
|
if (it_running_jobs != params.end())
|
|
{
|
|
running_jobs = watoi(it_running_jobs->second);
|
|
}
|
|
|
|
bool async_list = params.find("async") != params.end()
|
|
&& params["async"] == "1";
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_StartFullFileBackup);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(server_token);
|
|
data.addInt(group);
|
|
data.addInt(flags);
|
|
data.addString(clientsubname);
|
|
data.addInt(sha_version);
|
|
data.addInt(running_jobs);
|
|
data.addInt(facet_id);
|
|
data.addChar(async_list ? 1 : 0);
|
|
|
|
std::string async_id;
|
|
if (async_list)
|
|
{
|
|
async_id.resize(16);
|
|
Server->randomFill(&async_id[0], async_id.size());
|
|
}
|
|
|
|
if (async_list &&
|
|
(flags & flag_calc_checksums) &&
|
|
params["phash"] == "1")
|
|
{
|
|
data.addString2(async_id);
|
|
}
|
|
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
lasttime=Server->getTimeMS();
|
|
|
|
end_to_end_file_backup_verification_enabled=false;
|
|
|
|
SRunningProcess new_proc;
|
|
|
|
new_proc.action = RUNNING_FULL_FILE;
|
|
new_proc.server_id = server_id;
|
|
new_proc.id = ++curr_backup_running_id;
|
|
new_proc.server_token = server_token;
|
|
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
removeTimedOutProcesses(server_token, true);
|
|
|
|
running_processes.push_back(new_proc);
|
|
|
|
status_updated = true;
|
|
|
|
if (!async_list)
|
|
{
|
|
state = CCSTATE_START_FILEBACKUP;
|
|
curr_backup_tt = ILLEGAL_THREADPOOL_TICKET;
|
|
}
|
|
else
|
|
{
|
|
process_lock.relock(nullptr);
|
|
|
|
SAsyncFileList new_async_file_list = {
|
|
Server->getTimeMS(),
|
|
curr_result_id,
|
|
0,
|
|
ILLEGAL_THREADPOOL_TICKET
|
|
};
|
|
|
|
async_file_index[async_id] = new_async_file_list;
|
|
Server->Log("Async index " + bytesToHex(async_id), LL_DEBUG);
|
|
|
|
lock.relock(nullptr);
|
|
|
|
IndexThread::refResult(curr_result_id);
|
|
|
|
tcpstack.Send(pipe, "ASYNC-async_id="+ bytesToHex(async_id));
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_WAIT_FOR_INDEX(const std::string &cmd)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(cmd.substr(15), ¶ms);
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
std::string async_id = params["async_id"];
|
|
async_file_list_id = hexToBytes(async_id);
|
|
std::map<std::string, SAsyncFileList>::iterator it = async_file_index.find(async_file_list_id);
|
|
if (it == async_file_index.end())
|
|
{
|
|
for (std::deque<std::pair<std::string, std::string> >::iterator it2 = finished_async_file_index.begin();
|
|
it2 != finished_async_file_index.end(); ++it2)
|
|
{
|
|
if (it2->first == async_file_list_id)
|
|
{
|
|
Server->Log("Previously finished async index " + async_id + " with \"" + it2->second + "\"", LL_DEBUG);
|
|
if (it2->second == "done")
|
|
{
|
|
tcpstack.Send(pipe, "DONE");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, it2->second);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
lock.relock(nullptr);
|
|
|
|
Server->Log("Async index " + async_id + " not found", LL_DEBUG);
|
|
|
|
tcpstack.Send(pipe, "error - Async indexing process not found");
|
|
}
|
|
else if (Server->getTimeMS() - it->second.last_update > async_index_timeout)
|
|
{
|
|
unsigned int result_id = it->second.result_id;
|
|
async_file_index.erase(it);
|
|
lock.relock(nullptr);
|
|
|
|
IndexThread::removeResult(result_id);
|
|
|
|
Server->Log("Async index " + async_id + " timeout", LL_DEBUG);
|
|
|
|
tcpstack.Send(pipe, "error - Async indexing process timeout");
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Wait for async index " + async_id, LL_DEBUG);
|
|
state = CCSTATE_START_FILEBACKUP_ASYNC;
|
|
++it->second.refcount;
|
|
it->second.last_update = Server->getTimeMS();
|
|
curr_result_id = it->second.result_id;
|
|
if (curr_result_id != 0)
|
|
{
|
|
IndexThread::refResult(curr_result_id);
|
|
curr_backup_tt = ILLEGAL_THREADPOOL_TICKET;
|
|
}
|
|
else
|
|
{
|
|
curr_backup_tt = it->second.backup_ticket;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_START_SHADOWCOPY(const std::string &cmd)
|
|
{
|
|
if(cmd[cmd.size()-1]=='"')
|
|
{
|
|
state=CCSTATE_SHADOWCOPY;
|
|
std::string dir=cmd.substr(10, cmd.size()-11);
|
|
|
|
std::string clientsubname;
|
|
if (dir.find("/") != std::string::npos)
|
|
{
|
|
std::string str_params = getafter("/", dir);
|
|
dir = getuntil("/", dir);
|
|
|
|
str_map params;
|
|
ParseParamStrHttp(str_params, ¶ms);
|
|
|
|
clientsubname = params["clientsubname"];
|
|
}
|
|
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_ReferenceShadowcopy);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(dir);
|
|
data.addString(server_token);
|
|
data.addUChar(0);
|
|
data.addUChar(1);
|
|
data.addString(clientsubname);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Invalid command", LL_ERROR);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_STOP_SHADOWCOPY(const std::string &cmd)
|
|
{
|
|
if(cmd[cmd.size()-1]=='"')
|
|
{
|
|
state=CCSTATE_SHADOWCOPY;
|
|
std::string dir=cmd.substr(9, cmd.size()-10);
|
|
|
|
std::string clientsubname;
|
|
int issues = 0;
|
|
if (dir.find("/") != std::string::npos)
|
|
{
|
|
std::string str_params = getafter("/", dir);
|
|
dir = getuntil("/", dir);
|
|
|
|
str_map params;
|
|
ParseParamStrHttp(str_params, ¶ms);
|
|
|
|
clientsubname = params["clientsubname"];
|
|
|
|
str_map::iterator it_issues = params.find("issues");
|
|
if (it_issues != params.end())
|
|
{
|
|
issues = watoi(it_issues->second);
|
|
}
|
|
}
|
|
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_ReleaseShadowcopy);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(dir);
|
|
data.addString(server_token);
|
|
data.addUChar(0);
|
|
data.addInt(-1);
|
|
data.addString(clientsubname);
|
|
data.addInt(issues);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Invalid command", LL_ERROR);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_SET_INCRINTERVAL(const std::string &cmd)
|
|
{
|
|
if(cmd[cmd.size()-1]=='"')
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
std::string intervall=cmd.substr(15, cmd.size()-16);
|
|
|
|
bool update_db=false;
|
|
|
|
if(intervall.find("?")!=std::string::npos)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(getafter("?", intervall), ¶ms);
|
|
|
|
str_map::iterator it_startup_delay=params.find("startup_delay");
|
|
if(it_startup_delay!=params.end())
|
|
{
|
|
int new_waittime = watoi(it_startup_delay->second);
|
|
if(new_waittime>backup_alert_delay)
|
|
{
|
|
update_db=true;
|
|
backup_alert_delay = new_waittime;
|
|
}
|
|
}
|
|
}
|
|
|
|
int new_interval=atoi(intervall.c_str());
|
|
if(new_interval!=0 && new_interval!=backup_interval)
|
|
{
|
|
update_db=true;
|
|
backup_interval = new_interval;
|
|
}
|
|
tcpstack.Send(pipe, "OK");
|
|
lasttime=Server->getTimeMS();
|
|
|
|
if(update_db)
|
|
{
|
|
IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
ClientDAO dao(db);
|
|
dao.updateMiscValue("backup_interval", convert(backup_interval));
|
|
dao.updateMiscValue("backup_alert_delay", convert(backup_alert_delay));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Invalid command", LL_ERROR);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_BACKUPDIRS(const std::string &cmd)
|
|
{
|
|
IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
IQuery *q=db->Prepare("SELECT id, name, path, tgroup, optional, server_default FROM backupdirs WHERE symlinked=0");
|
|
int timeoutms=300;
|
|
db_results res=q->Read(&timeoutms);
|
|
|
|
IQuery* q_get_virtual_client = db->Prepare("SELECT virtual_client FROM virtual_client_group_offsets WHERE group_offset=?");
|
|
|
|
if(timeoutms==0)
|
|
{
|
|
JSON::Object ret;
|
|
|
|
JSON::Array dirs;
|
|
for(size_t i=0;i<res.size();++i)
|
|
{
|
|
if(res[i]["name"]=="*") continue;
|
|
|
|
JSON::Object cdir;
|
|
|
|
cdir.set("id", watoi(res[i]["id"]));
|
|
cdir.set("name", res[i]["name"]);
|
|
cdir.set("path", res[i]["path"]);
|
|
int tgroup = watoi(res[i]["tgroup"]);
|
|
cdir.set("group", tgroup%c_group_size);
|
|
cdir.set("server_default", watoi(res[i]["server_default"]));
|
|
|
|
if (tgroup < 0) continue;
|
|
|
|
if (tgroup >= c_group_size)
|
|
{
|
|
int offset = (tgroup / c_group_size)*c_group_size;
|
|
q_get_virtual_client->Bind(offset);
|
|
db_results res_vc = q_get_virtual_client->Read();
|
|
q_get_virtual_client->Reset();
|
|
|
|
if (!res_vc.empty())
|
|
{
|
|
cdir.set("virtual_client", res_vc[0]["virtual_client"]);
|
|
}
|
|
}
|
|
|
|
int flags = watoi(res[i]["optional"]);
|
|
|
|
std::vector<std::pair<int, std::string> > flag_mapping = getFlagStrMapping();
|
|
|
|
|
|
std::string str_flags;
|
|
for (size_t j = 0; j < flag_mapping.size(); ++j)
|
|
{
|
|
if (flags & flag_mapping[j].first)
|
|
{
|
|
if (!str_flags.empty()) str_flags += ",";
|
|
str_flags += flag_mapping[j].second;
|
|
}
|
|
}
|
|
|
|
cdir.set("flags", str_flags);
|
|
|
|
dirs.add(cdir);
|
|
}
|
|
|
|
ret.set("dirs", dirs);
|
|
|
|
tcpstack.Send(pipe, ret.stringify(false));
|
|
}
|
|
else
|
|
{
|
|
pipe->shutdown();
|
|
}
|
|
db->destroyAllQueries();
|
|
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
|
|
void ClientConnector::CMD_SAVE_BACKUPDIRS(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
if(last_capa & DONT_ALLOW_CONFIG_PATHS)
|
|
{
|
|
tcpstack.Send(pipe, "FAILED");
|
|
return;
|
|
}
|
|
|
|
std::string facet_name = params["facet"];
|
|
|
|
if (facet_name.empty())
|
|
facet_name = "default";
|
|
|
|
|
|
if(saveBackupDirs(params, false, 0, getFacetIdByName(facet_name)))
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
|
|
void ClientConnector::CMD_DID_BACKUP(const std::string &cmd)
|
|
{
|
|
updateLastBackup();
|
|
tcpstack.Send(pipe, "OK");
|
|
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
SRunningProcess* proc = getRunningFileBackupProcess(std::string(), 0);
|
|
|
|
if (proc != nullptr)
|
|
{
|
|
removeRunningProcess(proc->id, true);
|
|
}
|
|
|
|
lasttime=Server->getTimeMS();
|
|
status_updated = true;
|
|
}
|
|
|
|
IndexThread::execute_postbackup_hook("postfilebackup", 0, std::string());
|
|
|
|
exit_backup_immediate(0);
|
|
}
|
|
|
|
void ClientConnector::CMD_DID_BACKUP2(const std::string &cmd)
|
|
{
|
|
std::string params_str = cmd.substr(12);
|
|
str_map params;
|
|
ParseParamStrHttp(params_str, ¶ms);
|
|
|
|
updateLastBackup();
|
|
tcpstack.Send(pipe, "OK");
|
|
|
|
std::string server_token = params["server_token"];
|
|
if (server_token.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
SRunningProcess* proc = getRunningFileBackupProcess(server_token, watoi64(params["status_id"]));
|
|
|
|
if (proc != nullptr)
|
|
{
|
|
removeRunningProcess(proc->id, true);
|
|
}
|
|
|
|
lasttime = Server->getTimeMS();
|
|
status_updated = true;
|
|
}
|
|
|
|
IndexThread::execute_postbackup_hook("postfilebackup", atoi(params["group"].c_str()), params["clientsubname"]);
|
|
|
|
exit_backup_immediate(0);
|
|
}
|
|
|
|
void ClientConnector::CMD_BACKUP_FAILED(const std::string & cmd)
|
|
{
|
|
std::string params_str = cmd.substr(14);
|
|
str_map params;
|
|
ParseParamStrHttp(params_str, ¶ms);
|
|
|
|
tcpstack.Send(pipe, "OK");
|
|
|
|
std::string server_token = params["server_token"];
|
|
if (server_token.empty())
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
SRunningProcess* proc = getRunningFileBackupProcess(server_token, watoi64(params["status_id"]));
|
|
|
|
if (proc != nullptr)
|
|
{
|
|
removeRunningProcess(proc->id, false);
|
|
}
|
|
|
|
lasttime = Server->getTimeMS();
|
|
status_updated = true;
|
|
}
|
|
|
|
IndexThread::execute_postbackup_hook("postfilebackup_failed", atoi(params["group"].c_str()), params["clientsubname"]);
|
|
|
|
exit_backup_immediate(1);
|
|
}
|
|
|
|
int64 ClientConnector::getLastBackupTime()
|
|
{
|
|
IDatabase *db=Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
IQuery *q=db->Prepare("SELECT strftime('%s',last_backup) AS last_backup FROM status", false);
|
|
if (q == nullptr)
|
|
return 0;
|
|
|
|
int timeoutms=300;
|
|
db_results res=q->Read(&timeoutms);
|
|
if(timeoutms==1)
|
|
{
|
|
res=cached_status;
|
|
}
|
|
else
|
|
{
|
|
cached_status=res;
|
|
}
|
|
db->destroyQuery(q);
|
|
|
|
if(res.size()>0)
|
|
{
|
|
return watoi64(res[0]["last_backup"]);
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
std::string ClientConnector::getCurrRunningJob(bool reset_done, int& pcdone)
|
|
{
|
|
IScopedLock lock_process(process_mutex);
|
|
|
|
SRunningProcess* proc = getActiveProcess(x_pingtimeout);
|
|
|
|
if(proc==nullptr )
|
|
{
|
|
lock_process.relock(nullptr);
|
|
return getHasNoRecentBackup();
|
|
}
|
|
else
|
|
{
|
|
pcdone = proc->pcdone;
|
|
return actionToStr(proc->action);
|
|
}
|
|
}
|
|
|
|
SChannel * ClientConnector::getCurrChannel()
|
|
{
|
|
for (size_t i = 0; i < channel_pipes.size(); ++i)
|
|
{
|
|
if (channel_pipes[i].pipe == pipe)
|
|
{
|
|
return &channel_pipes[i];
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ClientConnector::CMD_STATUS(const std::string &cmd)
|
|
{
|
|
state=CCSTATE_STATUS;
|
|
|
|
if (cmd == "FSTATUS")
|
|
{
|
|
sendStatus();
|
|
state = CCSTATE_NORMAL;
|
|
}
|
|
else
|
|
{
|
|
lasttime = Server->getTimeMS();
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_STATUS_DETAIL(const std::string &cmd)
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
JSON::Object ret;
|
|
|
|
ret.set("last_backup_time", getLastBackupTime());
|
|
|
|
JSON::Array j_running_processes;
|
|
|
|
for (size_t i = 0; i < running_processes.size(); ++i)
|
|
{
|
|
if (Server->getTimeMS() - running_processes[i].last_pingtime > x_pingtimeout)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
JSON::Object obj;
|
|
obj.set("percent_done", running_processes[i].pcdone);
|
|
obj.set("action", actionToStr(running_processes[i].action));
|
|
obj.set("eta_ms", running_processes[i].eta_ms);
|
|
|
|
if (!running_processes[i].details.empty())
|
|
{
|
|
obj.set("details", running_processes[i].details);
|
|
}
|
|
|
|
if (running_processes[i].detail_pc != -1)
|
|
{
|
|
obj.set("detail_pc", running_processes[i].detail_pc);
|
|
}
|
|
|
|
if (running_processes[i].total_bytes >= 0)
|
|
{
|
|
obj.set("total_bytes", running_processes[i].total_bytes);
|
|
obj.set("done_bytes", running_processes[i].done_bytes);
|
|
}
|
|
|
|
obj.set("process_id", running_processes[i].id);
|
|
obj.set("server_status_id", running_processes[i].server_id);
|
|
|
|
obj.set("speed_bpms", running_processes[i].speed_bpms);
|
|
|
|
j_running_processes.add(obj);
|
|
}
|
|
|
|
ret.set("running_processes", j_running_processes);
|
|
|
|
JSON::Array j_finished_processes;
|
|
for (size_t i = 0; i < finished_processes.size(); ++i)
|
|
{
|
|
JSON::Object obj;
|
|
obj.set("process_id", finished_processes[i].id);
|
|
obj.set("success", finished_processes[i].success);
|
|
j_finished_processes.add(obj);
|
|
}
|
|
|
|
ret.set("finished_processes", j_finished_processes);
|
|
|
|
JSON::Array servers;
|
|
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
JSON::Object obj;
|
|
obj.set("internet_connection", channel_pipes[i].internet_connection);
|
|
obj.set("name", channel_pipes[i].endpoint_name);
|
|
servers.add(obj);
|
|
}
|
|
|
|
ret.set("servers", servers);
|
|
|
|
ret.set("time_since_last_lan_connection", InternetClient::timeSinceLastLanConnection());
|
|
|
|
ret.set("internet_connected", InternetClient::isConnected());
|
|
|
|
ret.set("internet_status", InternetClient::getStatusMsg(getFacetIdByName("default")));
|
|
|
|
IDatabase *db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
|
|
ret.set("capability_bits", getCapabilities(db));
|
|
|
|
tcpstack.Send(pipe, ret.stringify(false));
|
|
|
|
db->destroyAllQueries();
|
|
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
|
|
void ClientConnector::CMD_UPDATE_SETTINGS(const std::string &cmd, const std::string& server_identity)
|
|
{
|
|
std::string s_settings=cmd.substr(9);
|
|
unescapeMessage(s_settings);
|
|
updateSettings( s_settings, server_identity);
|
|
tcpstack.Send(pipe, "OK");
|
|
lasttime=Server->getTimeMS();
|
|
}
|
|
|
|
void ClientConnector::CMD_PING_RUNNING(const std::string &cmd)
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
|
|
idle_timeout = 120000;
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
lasttime=Server->getTimeMS();
|
|
std::string pcdone_new = getbetween("-", "-", cmd);
|
|
|
|
last_token_times[server_token] = Server->getTimeSeconds();
|
|
|
|
SRunningProcess* proc = getRunningFileBackupProcess(server_token, 0);
|
|
|
|
if (proc == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int pcdone_old = proc->pcdone;
|
|
|
|
if (pcdone_new.empty())
|
|
proc->pcdone = -1;
|
|
else
|
|
proc->pcdone = atoi(pcdone_new.c_str());
|
|
|
|
if (pcdone_old != proc->pcdone)
|
|
{
|
|
status_updated = true;
|
|
}
|
|
|
|
proc->eta_ms = 0;
|
|
proc->last_pingtime = Server->getTimeMS();
|
|
|
|
#ifdef _WIN32
|
|
SetThreadExecutionState(ES_SYSTEM_REQUIRED);
|
|
#endif
|
|
}
|
|
|
|
void ClientConnector::CMD_PING_RUNNING2(const std::string &cmd)
|
|
{
|
|
std::string params_str = cmd.substr(14);
|
|
str_map params;
|
|
ParseParamStrHttp(params_str, ¶ms);
|
|
str_map::iterator it_paused_fb = params.find("paused_fb");
|
|
if (it_paused_fb != params.end()
|
|
&& it_paused_fb->second == "1"
|
|
&& IdleCheckerThread::getPause())
|
|
{
|
|
tcpstack.Send(pipe, "PAUSED");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
|
|
idle_timeout = 120000;
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
IScopedLock process_lock(process_mutex);
|
|
|
|
last_token_times[server_token] = Server->getTimeSeconds();
|
|
lasttime=Server->getTimeMS();
|
|
|
|
SRunningProcess* proc = getRunningBackupProcess(server_token, watoi64(params["status_id"]));
|
|
|
|
if (proc == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string pcdone_new=params["pc_done"];
|
|
|
|
int pcdone_old = proc->pcdone;
|
|
|
|
if(pcdone_new.empty())
|
|
proc->pcdone =-1;
|
|
else
|
|
proc->pcdone =watoi(pcdone_new);
|
|
|
|
if(pcdone_old!= proc->pcdone)
|
|
{
|
|
status_updated = true;
|
|
}
|
|
|
|
proc->eta_ms = watoi64(params["eta_ms"]);
|
|
proc->last_pingtime = Server->getTimeMS();
|
|
|
|
proc->speed_bpms = atof(params["speed_bpms"].c_str());
|
|
proc->total_bytes = watoi64(params["total_bytes"]);
|
|
proc->done_bytes = watoi64(params["done_bytes"]);
|
|
|
|
#ifdef _WIN32
|
|
SetThreadExecutionState(ES_SYSTEM_REQUIRED);
|
|
#endif
|
|
}
|
|
|
|
void ClientConnector::CMD_CHANNEL(const std::string &cmd, IScopedLock *g_lock, const std::string& identity)
|
|
{
|
|
bool img_download_running = false;
|
|
{
|
|
IScopedLock lock(process_mutex);
|
|
|
|
SRunningProcess* proc = getRunningProcess(RUNNING_RESTORE_IMAGE, std::string());
|
|
|
|
img_download_running = proc != nullptr;
|
|
}
|
|
|
|
if(!img_download_running)
|
|
{
|
|
if (!FileExists("urbackup/access_keys.properties"))
|
|
{
|
|
tcpstack.Send(pipe, "UPDATE ACCESS KEY");
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
bool metered;
|
|
if (AllCurrentConnectionsMetered(metered))
|
|
{
|
|
IScopedLock lock(ident_mutex);
|
|
if (last_metered != metered)
|
|
{
|
|
last_metered = metered;
|
|
lock.relock(NULL);
|
|
tcpstack.Send(pipe, std::string("METERED metered=") + (metered ? "1" : "0"));
|
|
}
|
|
}
|
|
|
|
{
|
|
IScopedLock lock(ident_mutex);
|
|
bool locked = IndexThread::isWindowsLocked();
|
|
if (locked != last_locked)
|
|
{
|
|
last_locked = locked;
|
|
lock.relock(NULL);
|
|
tcpstack.Send(pipe, std::string("LOCKED locked=") + (locked ? "1" : "0"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
std::string token;
|
|
|
|
std::string s_params = cmd.substr(9);
|
|
str_map params;
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
int capa = watoi(params["capa"]);
|
|
token = params["token"];
|
|
|
|
if (params["startup"] == "1")
|
|
{
|
|
tcpstack.Send(pipe, "STARTUP timestamp=" + convert(startup_timestamp));
|
|
}
|
|
|
|
g_lock->relock(backup_mutex);
|
|
|
|
channel_pipes.push_back(SChannel(pipe, internet_conn, endpoint_name, token,
|
|
&make_fileserv, identity, capa, watoi(params["restore_version"]), params["virtual_client"]));
|
|
is_channel=true;
|
|
state=CCSTATE_CHANNEL;
|
|
last_channel_ping=Server->getTimeMS();
|
|
lasttime=Server->getTimeMS();
|
|
Server->Log("New channel: Number of Channels: "+convert((int)channel_pipes.size()), LL_DEBUG);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_CHANNEL_PONG(const std::string &cmd, const std::string& endpoint_name)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
SChannel* chan = getCurrChannel();
|
|
if (chan != nullptr && chan->state == SChannel::EChannelState_Pinging)
|
|
{
|
|
chan->state = SChannel::EChannelState_Idle;
|
|
}
|
|
refreshSessionFromChannel(endpoint_name);
|
|
}
|
|
|
|
void ClientConnector::CMD_CHANNEL_PING(const std::string &cmd, const std::string& endpoint_name)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
if(tcpstack.Send(pipe, "PONG")==0)
|
|
{
|
|
do_quit=true;
|
|
}
|
|
|
|
refreshSessionFromChannel(endpoint_name);
|
|
}
|
|
|
|
void ClientConnector::CMD_TOCHANNEL_START_INCR_FILEBACKUP(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
tochannelSendStartbackup(RUNNING_INCR_FILE, params["virtual_client"]);
|
|
}
|
|
|
|
void ClientConnector::CMD_TOCHANNEL_START_FULL_FILEBACKUP(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
tochannelSendStartbackup(RUNNING_FULL_FILE, params["virtual_client"]);
|
|
}
|
|
|
|
void ClientConnector::CMD_TOCHANNEL_START_FULL_IMAGEBACKUP(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
tochannelSendStartbackup(RUNNING_FULL_IMAGE, params["virtual_client"]);
|
|
}
|
|
|
|
void ClientConnector::CMD_TOCHANNEL_START_INCR_IMAGEBACKUP(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
tochannelSendStartbackup(RUNNING_INCR_IMAGE, params["virtual_client"]);
|
|
}
|
|
|
|
void ClientConnector::CMD_TOCHANNEL_UPDATE_SETTINGS(const std::string &cmd)
|
|
{
|
|
if(last_capa & DONT_SHOW_SETTINGS)
|
|
{
|
|
tcpstack.Send(pipe, "FAILED");
|
|
return;
|
|
}
|
|
|
|
std::string s_settings=cmd.substr(16);
|
|
lasttime=Server->getTimeMS();
|
|
unescapeMessage(s_settings);
|
|
replaceSettings( s_settings );
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
bool ok=false;
|
|
for(size_t o=0;o<channel_pipes.size();++o)
|
|
{
|
|
CTCPStack tmpstack(channel_pipes[o].internet_connection);
|
|
_u32 rc=(_u32)tmpstack.Send(channel_pipes[o].pipe, "UPDATE SETTINGS");
|
|
if(rc!=0)
|
|
ok=true;
|
|
}
|
|
if(!ok)
|
|
{
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_UpdateCbt);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
tcpstack.Send(pipe, "NOSERVER");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_LOGDATA(const std::string &cmd)
|
|
{
|
|
std::string ldata=cmd.substr(9);
|
|
size_t cpos=ldata.find(" ");
|
|
std::string created=getuntil(" ", ldata);
|
|
lasttime=Server->getTimeMS();
|
|
saveLogdata(created, getafter(" ",ldata));
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
|
|
void ClientConnector::CMD_PAUSE(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
std::string b=cmd.substr(6);
|
|
bool ok=false;
|
|
IScopedLock lock(backup_mutex);
|
|
status_updated = true;
|
|
if(b=="true")
|
|
{
|
|
ok=true;
|
|
IdleCheckerThread::setPause(true);
|
|
IndexThread::getFileSrv()->setPause(true);
|
|
}
|
|
else if(b=="false")
|
|
{
|
|
ok=true;
|
|
IdleCheckerThread::setPause(false);
|
|
IndexThread::getFileSrv()->setPause(false);
|
|
}
|
|
if(ok) tcpstack.Send(pipe, "OK");
|
|
else tcpstack.Send(pipe, "FAILED");
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_LOGPOINTS(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
tcpstack.Send(pipe, getLogpoints() );
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_LOGDATA(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
int logid=watoi(params["logid"]);
|
|
int loglevel=watoi(params["loglevel"]);
|
|
std::string ret;
|
|
getLogLevel(logid, loglevel, ret);
|
|
tcpstack.Send(pipe, ret);
|
|
}
|
|
|
|
void ClientConnector::CMD_FULL_IMAGE(const std::string &cmd, bool ident_ok)
|
|
{
|
|
if(ident_ok)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
std::string s_params=cmd.substr(11);
|
|
str_map params;
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
|
|
server_token=(params["token"]);
|
|
image_inf.image_letter=(params["letter"]);
|
|
image_inf.orig_image_letter = image_inf.image_letter;
|
|
image_inf.server_status_id = watoi(params["status_id"]);
|
|
image_inf.shadowdrive=(params["shadowdrive"]);
|
|
if(params.find("start")!=params.end())
|
|
{
|
|
image_inf.startpos=watoi64(params["start"]);
|
|
}
|
|
else
|
|
{
|
|
image_inf.startpos=-1;
|
|
}
|
|
if(params.find("shadowid")!=params.end())
|
|
{
|
|
image_inf.shadow_id=watoi(params["shadowid"]);
|
|
}
|
|
else
|
|
{
|
|
image_inf.shadow_id=-1;
|
|
}
|
|
image_inf.with_checksum=false;
|
|
if(params.find("checksum")!=params.end())
|
|
{
|
|
if(params["checksum"]=="1")
|
|
image_inf.with_checksum=true;
|
|
}
|
|
image_inf.with_bitmap=false;
|
|
if(params.find("bitmap")!=params.end())
|
|
{
|
|
if(params["bitmap"]=="1")
|
|
image_inf.with_bitmap=true;
|
|
}
|
|
|
|
image_inf.no_shadowcopy=false;
|
|
|
|
image_inf.clientsubname = params["clientsubname"];
|
|
|
|
if(image_inf.image_letter=="SYSVOL"
|
|
|| image_inf.image_letter=="ESP")
|
|
{
|
|
std::string mpath;
|
|
std::string sysvol;
|
|
if(image_inf.image_letter=="SYSVOL")
|
|
{
|
|
sysvol=getSysVolumeCached(mpath);
|
|
}
|
|
else
|
|
{
|
|
sysvol=getEspVolumeCached(mpath);
|
|
}
|
|
|
|
if(!mpath.empty())
|
|
{
|
|
image_inf.image_letter=mpath;
|
|
}
|
|
else if(!sysvol.empty())
|
|
{
|
|
image_inf.image_letter=sysvol;
|
|
image_inf.no_shadowcopy=true;
|
|
}
|
|
else
|
|
{
|
|
ImageErr("Not found");
|
|
return;
|
|
}
|
|
}
|
|
#ifndef _WIN32
|
|
else
|
|
{
|
|
image_inf.image_letter = mapLinuxDev(image_inf.image_letter);
|
|
}
|
|
#endif
|
|
|
|
int running_jobs = 2;
|
|
if (params.find("running_jobs") != params.end())
|
|
{
|
|
running_jobs = watoi(params["running_jobs"]);
|
|
}
|
|
|
|
if (image_inf.shadow_id != -1)
|
|
{
|
|
image_inf.shadowdrive.clear();
|
|
CWData data;
|
|
data.addChar(4);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addInt(image_inf.shadow_id);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
}
|
|
else if(!image_inf.no_shadowcopy)
|
|
{
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_CreateShadowcopy);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(image_inf.image_letter);
|
|
data.addString(server_token);
|
|
data.addUChar(1); //full image backup
|
|
data.addUChar(0); //filesrv
|
|
data.addString(image_inf.clientsubname);
|
|
data.addInt(running_jobs);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
}
|
|
|
|
if(image_inf.no_shadowcopy)
|
|
{
|
|
image_inf.shadowdrive=image_inf.image_letter;
|
|
if(!image_inf.shadowdrive.empty() && image_inf.shadowdrive[0]!='\\')
|
|
{
|
|
image_inf.shadowdrive="\\\\.\\"+image_inf.image_letter;
|
|
}
|
|
}
|
|
|
|
lasttime=Server->getTimeMS();
|
|
sendFullImage();
|
|
}
|
|
else
|
|
{
|
|
ImageErr("Ident reset (1)");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_INCR_IMAGE(const std::string &cmd, bool ident_ok)
|
|
{
|
|
if(ident_ok)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
std::string s_params=cmd.substr(11);
|
|
str_map params;
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
|
|
server_token=(params["token"]);
|
|
|
|
str_map::iterator f_hashsize=params.find("hashsize");
|
|
if(f_hashsize!=params.end())
|
|
{
|
|
hashdataok=false;
|
|
hashdataleft=watoi(f_hashsize->second);
|
|
image_inf.image_letter=(params["letter"]);
|
|
image_inf.shadowdrive=(params["shadowdrive"]);
|
|
image_inf.server_status_id = watoi(params["status_id"]);
|
|
if(params.find("start")!=params.end())
|
|
{
|
|
image_inf.startpos=watoi64(params["start"]);
|
|
}
|
|
else
|
|
{
|
|
image_inf.startpos=-1;
|
|
}
|
|
if(params.find("shadowid")!=params.end())
|
|
{
|
|
image_inf.shadow_id=watoi(params["shadowid"]);
|
|
}
|
|
else
|
|
{
|
|
image_inf.shadow_id=-1;
|
|
}
|
|
image_inf.with_checksum=false;
|
|
if(params.find("checksum")!=params.end())
|
|
{
|
|
if(params["checksum"]=="1")
|
|
image_inf.with_checksum=true;
|
|
}
|
|
image_inf.with_bitmap=false;
|
|
if(params.find("bitmap")!=params.end())
|
|
{
|
|
if(params["bitmap"]=="1")
|
|
image_inf.with_bitmap=true;
|
|
}
|
|
|
|
image_inf.no_shadowcopy=false;
|
|
image_inf.clientsubname = params["clientsubname"];
|
|
|
|
#ifndef _WIN32
|
|
image_inf.image_letter = mapLinuxDev(image_inf.image_letter);
|
|
#endif
|
|
|
|
str_map::iterator f_cbitmapsize = params.find("cbitmapsize");
|
|
if (f_cbitmapsize != params.end())
|
|
{
|
|
bitmapleft = watoi(f_cbitmapsize->second);
|
|
|
|
bitmapfile = Server->openTemporaryFile();
|
|
if (bitmapfile == nullptr)
|
|
{
|
|
Server->Log("Error creating temporary bitmap file in CMD_INCR_IMAGE", LL_ERROR);
|
|
do_quit = true;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bitmapfile = nullptr;
|
|
}
|
|
|
|
int running_jobs = 2;
|
|
if (params.find("running_jobs") != params.end())
|
|
{
|
|
running_jobs = watoi(params["running_jobs"]);
|
|
}
|
|
|
|
if (image_inf.shadow_id != -1)
|
|
{
|
|
image_inf.shadowdrive.clear();
|
|
CWData data;
|
|
data.addChar(4);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addInt(image_inf.shadow_id);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
}
|
|
else if(!image_inf.no_shadowcopy)
|
|
{
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_CreateShadowcopy);
|
|
curr_result_id = IndexThread::getResultId();
|
|
data.addUInt(curr_result_id);
|
|
data.addString(image_inf.image_letter);
|
|
data.addString(server_token);
|
|
data.addUChar(2); //incr image backup
|
|
data.addUChar(0); //file serv?
|
|
data.addString(image_inf.clientsubname);
|
|
data.addInt(running_jobs);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
}
|
|
|
|
hashdatafile=Server->openTemporaryFile();
|
|
if(hashdatafile==nullptr)
|
|
{
|
|
Server->Log("Error creating temporary file in CMD_INCR_IMAGE", LL_ERROR);
|
|
do_quit=true;
|
|
return;
|
|
}
|
|
|
|
state = CCSTATE_IMAGE_HASHDATA;
|
|
|
|
size_t bufpos = 0;
|
|
IFile* datafile = hashdatafile;
|
|
_u32* dataleft = &hashdataleft;
|
|
while(bufpos<tcpstack.getBuffersize())
|
|
{
|
|
_u32 towrite = (std::min)(static_cast<_u32>(tcpstack.getBuffersize() - bufpos), *dataleft);
|
|
if(datafile->Write(tcpstack.getBuffer()+ bufpos, towrite)!= towrite)
|
|
{
|
|
Server->Log("Error writing to data temporary file in CMD_INCR_IMAGE", LL_ERROR);
|
|
do_quit=true;
|
|
return;
|
|
}
|
|
|
|
bufpos += towrite;
|
|
*dataleft -= towrite;
|
|
|
|
tcpstack.removeFront(towrite);
|
|
|
|
if(*dataleft == 0)
|
|
{
|
|
if (bitmapfile == nullptr
|
|
|| datafile==bitmapfile)
|
|
{
|
|
hashdataok = true;
|
|
state = CCSTATE_IMAGE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
datafile = bitmapfile;
|
|
dataleft = &bitmapleft;
|
|
state = CCSTATE_IMAGE_BITMAP;
|
|
}
|
|
}
|
|
}
|
|
|
|
lasttime=Server->getTimeMS();
|
|
sendIncrImage();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImageErr("Ident reset (2)");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_MBR(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
std::string s_params=cmd.substr(4);
|
|
str_map params;
|
|
ParseParamStrHttp(s_params, ¶ms);
|
|
|
|
std::string dl=params["driveletter"];
|
|
|
|
if(dl=="SYSVOL")
|
|
{
|
|
std::string mpath;
|
|
dl=getSysVolumeCached(mpath);
|
|
}
|
|
else if(dl=="ESP")
|
|
{
|
|
std::string mpath;
|
|
dl=getEspVolumeCached(mpath);
|
|
}
|
|
#ifndef _WIN32
|
|
else if (dl == "C" || dl == "C:")
|
|
{
|
|
dl = getRootVol();
|
|
}
|
|
else if(params.find("disk_path")!=params.end())
|
|
{
|
|
dl= mapLinuxDev(params["disk_path"]);
|
|
}
|
|
#endif
|
|
|
|
bool b=false;
|
|
std::string errmsg;
|
|
if(!dl.empty())
|
|
{
|
|
b=sendMBR(dl, errmsg);
|
|
}
|
|
if(!b)
|
|
{
|
|
CWData r;
|
|
r.addChar(0);
|
|
r.addString((errmsg));
|
|
tcpstack.Send(pipe, r);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_GET_BACKUPCLIENTS(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "0");
|
|
}
|
|
else
|
|
{
|
|
std::string clients;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
sendChannelPacket(channel_pipes[i], "GET BACKUPCLIENTS");
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after request -1", LL_DEBUG);
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after read -1", LL_DEBUG);
|
|
|
|
Server->Log("Client "+convert(i)+"/"+convert(channel_pipes.size())+": --"+nc+"--", LL_DEBUG);
|
|
|
|
if(!nc.empty())
|
|
{
|
|
clients+=nc+"\n";
|
|
}
|
|
}
|
|
tcpstack.Send(pipe, "1"+clients);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_GET_BACKUPIMAGES(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "0");
|
|
}
|
|
else
|
|
{
|
|
std::string imgs;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
sendChannelPacket(channel_pipes[i], cmd);
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
if(!nc.empty())
|
|
{
|
|
imgs+=nc+"\n";
|
|
}
|
|
}
|
|
tcpstack.Send(pipe, "1"+imgs);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_GET_FILE_BACKUPS(const std::string &cmd)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "0");
|
|
}
|
|
else
|
|
{
|
|
std::string filebackups;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
sendChannelPacket(channel_pipes[i], cmd);
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
if(!nc.empty())
|
|
{
|
|
filebackups+=nc+"\n";
|
|
}
|
|
}
|
|
tcpstack.Send(pipe, "1"+filebackups);
|
|
}
|
|
}
|
|
|
|
|
|
void ClientConnector::CMD_RESTORE_GET_FILE_BACKUPS_TOKENS( const std::string &cmd, str_map ¶ms )
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "1");
|
|
}
|
|
else
|
|
{
|
|
|
|
std::string utf8_tokens = params["tokens"];
|
|
std::string filebackups;
|
|
std::string accessparams;
|
|
std::string virtual_client = params["virtual_client"];
|
|
|
|
if(!multipleChannelServers())
|
|
{
|
|
accessparams+=" with_id_offset=false";
|
|
}
|
|
|
|
bool has_token_params=false;
|
|
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
if (channel_pipes[i].virtual_client != virtual_client)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(size_t j=0;j<2;++j)
|
|
{
|
|
if(channel_pipes[i].last_tokens!=utf8_tokens)
|
|
{
|
|
if(!has_token_params)
|
|
{
|
|
std::string token_params = getAccessTokensParams(params["tokens"], true, virtual_client);
|
|
|
|
if(token_params.empty())
|
|
{
|
|
tcpstack.Send(pipe, "2");
|
|
return;
|
|
}
|
|
|
|
if(accessparams.empty())
|
|
{
|
|
accessparams = token_params;
|
|
accessparams[0]=' ';
|
|
}
|
|
else
|
|
{
|
|
accessparams+=token_params;
|
|
}
|
|
|
|
has_token_params=true;
|
|
}
|
|
}
|
|
|
|
sendChannelPacket(channel_pipes[i], cmd+accessparams);
|
|
std::string nc=receivePacket(channel_pipes[i],
|
|
10*60*1000);
|
|
if(!nc.empty() && nc!="err")
|
|
{
|
|
channel_pipes[i].last_tokens = utf8_tokens;
|
|
|
|
if(!filebackups.empty())
|
|
{
|
|
filebackups[filebackups.size()-1] = ',';
|
|
nc[0]=' ';
|
|
}
|
|
filebackups+=nc;
|
|
|
|
break;
|
|
}
|
|
else if(!has_token_params)
|
|
{
|
|
channel_pipes[i].last_tokens="";
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
if(filebackups.empty())
|
|
{
|
|
tcpstack.Send(pipe, "3");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "0"+filebackups);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_FILE_LIST_TOKENS(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "1");
|
|
}
|
|
else
|
|
{
|
|
std::string accessparams;
|
|
|
|
str_map::iterator it_path = params.find("path");
|
|
|
|
if(it_path!=params.end())
|
|
{
|
|
accessparams+="&path="+EscapeParamString((it_path->second));
|
|
}
|
|
|
|
str_map::iterator it_backupid = params.find("backupid");
|
|
|
|
if(it_backupid!=params.end())
|
|
{
|
|
accessparams+="&backupid="+EscapeParamString((it_backupid->second));
|
|
}
|
|
|
|
if(!multipleChannelServers())
|
|
{
|
|
accessparams+="&with_id_offset=false";
|
|
}
|
|
|
|
std::string filelist;
|
|
|
|
if(!accessparams.empty())
|
|
{
|
|
accessparams[0]=' ';
|
|
}
|
|
|
|
std::string utf8_tokens = params["tokens"];
|
|
std::string virtual_client = params["virtual_client"];
|
|
bool has_token_params=false;
|
|
bool break_outer=false;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
if (channel_pipes[i].virtual_client != virtual_client)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(size_t j=0;j<2;++j)
|
|
{
|
|
if(channel_pipes[i].last_tokens!=utf8_tokens)
|
|
{
|
|
if(!has_token_params)
|
|
{
|
|
std::string token_params = getAccessTokensParams(params["tokens"], true, virtual_client);
|
|
|
|
if(token_params.empty())
|
|
{
|
|
tcpstack.Send(pipe, "2");
|
|
return;
|
|
}
|
|
|
|
if(accessparams.empty())
|
|
{
|
|
accessparams = token_params;
|
|
accessparams[0]=' ';
|
|
}
|
|
else
|
|
{
|
|
accessparams+=token_params;
|
|
}
|
|
|
|
has_token_params=true;
|
|
}
|
|
}
|
|
|
|
sendChannelPacket(channel_pipes[i], cmd+accessparams);
|
|
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
if(!nc.empty() && nc!="err")
|
|
{
|
|
channel_pipes[i].last_tokens = utf8_tokens;
|
|
|
|
if(!filelist.empty())
|
|
{
|
|
filelist[filelist.size()-1] = ',';
|
|
nc[0]=' ';
|
|
}
|
|
|
|
filelist+=nc;
|
|
|
|
if(it_backupid!=params.end())
|
|
{
|
|
break_outer=true;
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
else if(!has_token_params)
|
|
{
|
|
channel_pipes[i].last_tokens="";
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(break_outer)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!filelist.empty())
|
|
{
|
|
tcpstack.Send(pipe, "0"+filelist);
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "3");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_DOWNLOAD_FILES_TOKENS(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "1");
|
|
return;
|
|
}
|
|
|
|
std::string accessparams;
|
|
|
|
str_map::iterator it_path = params.find("path");
|
|
|
|
if(it_path==params.end())
|
|
{
|
|
tcpstack.Send(pipe, "2");
|
|
return;
|
|
}
|
|
|
|
accessparams+="&path="+EscapeParamString((it_path->second));
|
|
|
|
|
|
str_map::iterator it_backupid = params.find("backupid");
|
|
|
|
if(it_backupid==params.end())
|
|
{
|
|
tcpstack.Send(pipe, "3");
|
|
return;
|
|
}
|
|
|
|
accessparams+="&backupid="+EscapeParamString(it_backupid->second);
|
|
|
|
if(!multipleChannelServers())
|
|
{
|
|
accessparams+="&with_id_offset=false";
|
|
}
|
|
|
|
std::string restore_token_binary;
|
|
restore_token_binary.resize(16);
|
|
Server->secureRandomFill(&restore_token_binary[0], restore_token_binary.size());
|
|
|
|
restore_token.restore_token = bytesToHex(restore_token_binary);
|
|
restore_token.restore_token_time = Server->getTimeMS();
|
|
|
|
{
|
|
IScopedLock process_lock(process_mutex);
|
|
restore_token.process_id = ++curr_backup_running_id;
|
|
}
|
|
|
|
accessparams+="&restore_token="+ restore_token.restore_token+"&process_id="+convert(restore_token.process_id);
|
|
|
|
str_map::iterator it_clean_other = params.find("clean_other");
|
|
if (it_clean_other != params.end())
|
|
{
|
|
accessparams += "&clean_other=" + EscapeParamString(it_clean_other->second);
|
|
}
|
|
|
|
str_map::iterator it_ignore_other_fs = params.find("ignore_other_fs");
|
|
if (it_clean_other != params.end())
|
|
{
|
|
accessparams += "&ignore_other_fs=" + EscapeParamString(it_ignore_other_fs->second);
|
|
}
|
|
|
|
str_map::iterator it_follow_symlinks = params.find("follow_symlinks");
|
|
if (it_follow_symlinks != params.end())
|
|
{
|
|
accessparams += "&follow_symlinks=" + EscapeParamString(it_follow_symlinks->second);
|
|
}
|
|
|
|
str_map::iterator it_restore_flags = params.find("restore_flags");
|
|
if (it_restore_flags != params.end())
|
|
{
|
|
accessparams += "&restore_flags=" + EscapeParamString(it_restore_flags->second);
|
|
}
|
|
|
|
for (size_t i = 0; params.find("map_path_source" + convert(i)) != params.end(); ++i)
|
|
{
|
|
accessparams += "&map_path_source" + convert(i) + "=" + EscapeParamString(params["map_path_source" + convert(i)]);
|
|
accessparams += "&map_path_target" + convert(i) + "=" + EscapeParamString(params["map_path_target" + convert(i)]);
|
|
}
|
|
|
|
std::string filelist;
|
|
|
|
accessparams[0]=' ';
|
|
|
|
std::string utf8_tokens = params["tokens"];
|
|
std::string virtual_client = params["virtual_client"];
|
|
bool has_token_params=false;
|
|
std::string last_err;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
if (channel_pipes[i].virtual_client != virtual_client)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(size_t j=0;j<2;++j)
|
|
{
|
|
if(channel_pipes[i].last_tokens!=utf8_tokens)
|
|
{
|
|
if(!has_token_params)
|
|
{
|
|
std::string token_params = getAccessTokensParams(params["tokens"], true, virtual_client);
|
|
|
|
if(token_params.empty())
|
|
{
|
|
tcpstack.Send(pipe, "error encrypting access tokens");
|
|
return;
|
|
}
|
|
|
|
if(accessparams.empty())
|
|
{
|
|
accessparams = token_params;
|
|
accessparams[0]=' ';
|
|
}
|
|
else
|
|
{
|
|
accessparams+=token_params;
|
|
}
|
|
|
|
has_token_params=true;
|
|
}
|
|
}
|
|
|
|
sendChannelPacket(channel_pipes[i], cmd+accessparams);
|
|
|
|
last_err=receivePacket(channel_pipes[i]);
|
|
if(!last_err.empty() && last_err!="err")
|
|
{
|
|
channel_pipes[i].last_tokens = utf8_tokens;
|
|
|
|
tcpstack.Send(pipe, "0" + last_err);
|
|
return;
|
|
}
|
|
else if(!has_token_params)
|
|
{
|
|
channel_pipes[i].last_tokens="";
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
tcpstack.Send(pipe, last_err);
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_DOWNLOAD_IMAGE(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
Server->Log("Downloading image...", LL_DEBUG);
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
Server->Log("In mutex...",LL_DEBUG);
|
|
downloadImage(params, lock);
|
|
Server->Log("Download done -2", LL_DEBUG);
|
|
do_quit=true;
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_DOWNLOAD_FILES(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
Server->Log("Downloading files...", LL_DEBUG);
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "No backup server found");
|
|
return;
|
|
}
|
|
|
|
std::string last_error;
|
|
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
IPipe *c=channel_pipes[i].pipe;
|
|
|
|
std::string with_id_offset = "";
|
|
if (multipleChannelServers())
|
|
{
|
|
with_id_offset += "&with_id_offset=true";
|
|
}
|
|
|
|
CTCPStack recv_stack(channel_pipes[i].internet_connection);
|
|
recv_stack.Send(c, "DOWNLOAD FILES backupid="+params["backupid"]+"&time="+params["time"]+ with_id_offset);
|
|
|
|
Server->Log("Start downloading files from channel "+convert((int)i), LL_DEBUG);
|
|
|
|
int64 starttime = Server->getTimeMS();
|
|
|
|
while(Server->getTimeMS()-starttime<60000)
|
|
{
|
|
std::string msg;
|
|
if(c->Read(&msg, 60000)>0)
|
|
{
|
|
recv_stack.AddData(&msg[0], msg.size());
|
|
|
|
if(recv_stack.getPacket(msg) )
|
|
{
|
|
if(msg=="ok")
|
|
{
|
|
tcpstack.Send(pipe, "ok");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
last_error=msg;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!last_error.empty())
|
|
{
|
|
tcpstack.Send(pipe, last_error);
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "timeout");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_DOWNLOADPROGRESS(const std::string &cmd)
|
|
{
|
|
Server->Log("Sending progress...", LL_DEBUG);
|
|
lasttime=Server->getTimeMS();
|
|
|
|
bool img_download_running = false;
|
|
{
|
|
IScopedLock lock(process_mutex);
|
|
SRunningProcess* proc = getRunningProcess(RUNNING_RESTORE_IMAGE, std::string());
|
|
img_download_running = proc != nullptr;
|
|
}
|
|
|
|
if(!img_download_running)
|
|
{
|
|
pipe->Write("100");
|
|
do_quit=true;
|
|
}
|
|
else
|
|
{
|
|
while(true)
|
|
{
|
|
{
|
|
int progress=0;
|
|
{
|
|
IScopedLock lock(process_mutex);
|
|
SRunningProcess* proc = getRunningProcess(RUNNING_RESTORE_IMAGE, std::string());
|
|
if (proc != nullptr)
|
|
{
|
|
progress = proc->pcdone;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if(!pipe->Write(convert(progress)+"\n", 10000))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
{
|
|
Server->wait(1000);
|
|
}
|
|
}
|
|
do_quit=true;
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_LOGIN_FOR_DOWNLOAD(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "no channels available");
|
|
}
|
|
else
|
|
{
|
|
bool one_ok=false;
|
|
std::string errors;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
if(params["username"].empty())
|
|
{
|
|
sendChannelPacket(channel_pipes[i], "LOGIN username=&password=");
|
|
}
|
|
else
|
|
{
|
|
sendChannelPacket(channel_pipes[i], "LOGIN username="+(params["username"])
|
|
+"&password="+(params["password"+convert(i)]));
|
|
}
|
|
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after request -1", LL_DEBUG);
|
|
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after read -1", LL_DEBUG);
|
|
|
|
Server->Log("Client "+convert(i)+"/"+convert(channel_pipes.size())+": --"+nc+"--", LL_DEBUG);
|
|
|
|
if(nc=="ok")
|
|
{
|
|
one_ok=true;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Client "+convert(i)+"/"+convert(channel_pipes.size())+" ERROR: --"+nc+"--", LL_ERROR);
|
|
if(!errors.empty())
|
|
{
|
|
errors+=" -- ";
|
|
}
|
|
errors+=nc;
|
|
}
|
|
}
|
|
if(one_ok)
|
|
{
|
|
tcpstack.Send(pipe, "ok");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, errors);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_GET_SALT(const std::string &cmd, str_map ¶ms)
|
|
{
|
|
lasttime=Server->getTimeMS();
|
|
IScopedLock lock(backup_mutex);
|
|
waitForPings(&lock);
|
|
if(channel_pipes.size()==0)
|
|
{
|
|
tcpstack.Send(pipe, "no channels available");
|
|
}
|
|
else
|
|
{
|
|
std::string salts;
|
|
for(size_t i=0;i<channel_pipes.size();++i)
|
|
{
|
|
sendChannelPacket(channel_pipes[i], "SALT username="+(params["username"]));
|
|
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after request -1", LL_DEBUG);
|
|
|
|
std::string nc=receivePacket(channel_pipes[i]);
|
|
|
|
if(channel_pipes[i].pipe->hasError())
|
|
Server->Log("Channel has error after read -1", LL_DEBUG);
|
|
|
|
Server->Log("Client "+convert(i)+"/"+convert(channel_pipes.size())+": --"+nc+"--", LL_DEBUG);
|
|
|
|
if(nc.find("ok;")==0)
|
|
{
|
|
if(!salts.empty())
|
|
{
|
|
salts+="/";
|
|
}
|
|
salts+=nc;
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Client "+convert(i)+"/"+convert(channel_pipes.size())+" ERROR: --"+nc+"--", LL_ERROR);
|
|
salts+="err;"+nc;
|
|
}
|
|
}
|
|
tcpstack.Send(pipe, salts);
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_VERSION_UPDATE(const std::string &cmd)
|
|
{
|
|
std::string server_version = cmd.substr(8);
|
|
|
|
#ifdef _WIN32
|
|
#define VERSION_FILE_PREFIX ""
|
|
#else
|
|
#define VERSION_FILE_PREFIX "urbackup/"
|
|
|
|
if (atoi(server_version.c_str()) < 125)
|
|
{
|
|
tcpstack.Send(pipe, "noop");
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(_WIN32) || defined(URB_WITH_CLIENTUPDATE)
|
|
std::string version_1=getFile(VERSION_FILE_PREFIX "version.txt");
|
|
std::string version_2=getFile(VERSION_FILE_PREFIX "curr_version.txt");
|
|
if(version_1.empty() ) version_1="0";
|
|
if(version_2.empty() ) version_2="0";
|
|
|
|
if(versionNeedsUpdate(version_1, server_version)
|
|
&& versionNeedsUpdate(version_2, server_version) )
|
|
{
|
|
tcpstack.Send(pipe, "update");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "noop");
|
|
}
|
|
#else
|
|
tcpstack.Send(pipe, "noop");
|
|
return;
|
|
#endif
|
|
|
|
#undef VERSION_FILE_PREFIX
|
|
}
|
|
|
|
void ClientConnector::CMD_CLIENT_UPDATE(const std::string &cmd)
|
|
{
|
|
hashdatafile=Server->openTemporaryFile();
|
|
if(hashdatafile==nullptr)
|
|
{
|
|
Server->Log("Error creating temporary file in CMD_CLIENT_UPDATE", LL_ERROR);
|
|
do_quit=true;
|
|
return;
|
|
}
|
|
|
|
str_map params;
|
|
ParseParamStrHttp(cmd.substr(14), ¶ms);
|
|
|
|
hashdataleft=watoi(params["size"]);
|
|
silent_update=(params["silent_update"]=="true");
|
|
|
|
hashdataok=false;
|
|
state=CCSTATE_UPDATE_DATA;
|
|
|
|
if(tcpstack.getBuffersize()>0)
|
|
{
|
|
if(hashdatafile->Write(tcpstack.getBuffer(), (_u32)tcpstack.getBuffersize())!=tcpstack.getBuffersize())
|
|
{
|
|
Server->Log("Error writing to hashdata temporary file -1update", LL_ERROR);
|
|
do_quit=true;
|
|
return;
|
|
}
|
|
if(hashdataleft>=tcpstack.getBuffersize())
|
|
{
|
|
hashdataleft-=(_u32)tcpstack.getBuffersize();
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Too much hashdata - error -1update", LL_ERROR);
|
|
}
|
|
|
|
tcpstack.reset();
|
|
|
|
if(hashdataleft==0)
|
|
{
|
|
hashdataok=true;
|
|
state=CCSTATE_UPDATE_FINISH;
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
void ClientConnector::CMD_CAPA(const std::string &cmd)
|
|
{
|
|
str_map params;
|
|
if (cmd.size() > 5)
|
|
{
|
|
ParseParamStrHttp(cmd.substr(5), ¶ms);
|
|
}
|
|
|
|
std::string clientsubname;
|
|
str_map::iterator it_clientsubname = params.find("clientsubname");
|
|
if (it_clientsubname != params.end())
|
|
clientsubname = it_clientsubname->second;
|
|
|
|
std::string client_version_str=std::string(c_client_version);
|
|
|
|
std::string restore=Server->getServerParameter("allow_restore");
|
|
|
|
if(restore.empty() || restore=="default")
|
|
{
|
|
restore="client-confirms";
|
|
}
|
|
|
|
std::string clientuid;
|
|
IDatabase *db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
IQuery* q_get_uid = db->Prepare("SELECT tvalue FROM misc WHERE tkey=?", false);
|
|
q_get_uid->Bind("clientuid_" + clientsubname);
|
|
db_results res = q_get_uid->Read();
|
|
if (res.empty())
|
|
{
|
|
IQuery* q_ins_uid = db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES (?,?)", false);
|
|
q_ins_uid->Bind("clientuid_" + clientsubname);
|
|
q_ins_uid->Bind(Server->secureRandomString(16));
|
|
q_ins_uid->Write();
|
|
db->destroyQuery(q_ins_uid);
|
|
res = q_get_uid->Read();
|
|
}
|
|
db->destroyQuery(q_get_uid);
|
|
|
|
if (!res.empty())
|
|
{
|
|
clientuid = res[0]["tvalue"];
|
|
}
|
|
|
|
std::string imm_backup = Server->getServerParameter("backup_immediate");
|
|
if (!imm_backup.empty())
|
|
{
|
|
imm_backup = "&BACKUP=" + EscapeParamString(imm_backup);
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
std::string buf;
|
|
buf.resize(1024);
|
|
std::string os_version_str = get_windows_version();
|
|
std::string win_volumes;
|
|
std::string win_nonusb_volumes;
|
|
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
win_volumes = get_all_volumes_list(false, volumes_cache);
|
|
win_nonusb_volumes = get_all_volumes_list(true, volumes_cache);
|
|
}
|
|
|
|
std::string send_prev_cbitmap;
|
|
if (os_get_file_type("urbctctl.exe")!=0)
|
|
{
|
|
send_prev_cbitmap = "&REQ_PREV_CBITMAP=1";
|
|
}
|
|
|
|
std::string conn_metered = "";
|
|
bool metered;
|
|
if (AllCurrentConnectionsMetered(metered))
|
|
{
|
|
conn_metered = std::string("&METERED=") + (metered ? "1" : "0");
|
|
IScopedLock lock(ident_mutex);
|
|
last_metered = metered;
|
|
}
|
|
|
|
bool locked = IndexThread::isWindowsLocked();
|
|
std::string locked_str = std::string("&LOCKED=") + (locked ? "1" : "0");
|
|
|
|
tcpstack.Send(pipe, "FILE=2&FILE2=1&IMAGE=1&UPDATE=1&MBR=1&FILESRV=3&SET_SETTINGS=1&IMAGE_VER=1&CLIENTUPDATE=2&ASYNC_INDEX=1"
|
|
"&CLIENT_VERSION_STR="+EscapeParamString((client_version_str))+"&OS_VERSION_STR="+EscapeParamString(os_version_str)+
|
|
"&ALL_VOLUMES="+EscapeParamString(win_volumes)+"&ETA=1&CDP=0&ALL_NONUSB_VOLUMES="+EscapeParamString(win_nonusb_volumes)+"&EFI=1"
|
|
"&FILE_META=1&SELECT_SHA=1&PHASH=1&RESTORE="+restore+"&RESTORE_VER=1&CLIENT_BITMAP=1&CMD=2&SYMBIT=1&WTOKENS=1&FILESRVTUNNEL=1&FACET=1&OS_SIMPLE=windows"
|
|
"&clientuid="+EscapeParamString(clientuid)+conn_metered+ send_prev_cbitmap + imm_backup + locked_str);
|
|
#else
|
|
|
|
#ifdef __APPLE__
|
|
std::string os_simple = "osx";
|
|
#elif __linux__
|
|
std::string os_simple = "linux";
|
|
#else
|
|
std::string os_simple = "unknown";
|
|
#endif
|
|
|
|
std::string image_args = "&IMAGE=0";
|
|
if(!trim(IndexThread::get_snapshot_script_location("create_volume_snapshot", clientsubname)).empty())
|
|
{
|
|
image_args = "&IMAGE=1&REQ_PREV_CBITMAP=1";
|
|
}
|
|
|
|
|
|
std::string os_version_str=get_lin_os_version();
|
|
tcpstack.Send(pipe, "FILE=2&FILE2=1&FILESRV=3&SET_SETTINGS=1&IMAGE_VER=1&CLIENTUPDATE=2&ASYNC_INDEX=1"
|
|
"&CLIENT_VERSION_STR="+EscapeParamString((client_version_str))+"&OS_VERSION_STR="+EscapeParamString(os_version_str)
|
|
+"&ETA=1&CPD=0&EFI=1&FILE_META=1&SELECT_SHA=1&PHASH=1&RESTORE="+restore+"&RESTORE_VER=1&CLIENT_BITMAP=1&CMD=2&SYMBIT=1&WTOKENS=1&FILESRVTUNNEL=1&FACET=1&OS_SIMPLE="+os_simple
|
|
+"&clientuid=" + EscapeParamString(clientuid) + imm_backup + image_args);
|
|
#endif
|
|
}
|
|
|
|
void ClientConnector::CMD_NEW_SERVER(str_map ¶ms)
|
|
{
|
|
std::string ident=(params["ident"]);
|
|
if(!ident.empty())
|
|
{
|
|
ServerIdentityMgr::addServerIdentity(ident, SPublicKeys());
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "FAILED");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_RESET_KEEP(str_map ¶ms)
|
|
{
|
|
std::string virtual_client = params["virtual_client"];
|
|
std::string folder_name = params["folder_name"];
|
|
int tgroup = watoi(params["tgroup"]);
|
|
|
|
IDatabase *db = Server->getDatabase(Server->getThreadID(), URBACKUPDB_CLIENT);
|
|
|
|
int group_offset = 0;
|
|
if (!virtual_client.empty())
|
|
{
|
|
IQuery *q = db->Prepare("SELECT group_offset FROM virtual_client_group_offsets WHERE virtual_client=?", false);
|
|
q->Bind(virtual_client);
|
|
db_results res = q->Read();
|
|
db->destroyQuery(q);
|
|
|
|
if (res.empty())
|
|
{
|
|
tcpstack.Send(pipe, "err_virtual_client_not_found");
|
|
return;
|
|
}
|
|
|
|
group_offset = watoi(res[0]["group_offset"]);
|
|
}
|
|
|
|
IQuery* q = db->Prepare(std::string("UPDATE backupdirs SET reset_keep=1 WHERE tgroup=?")
|
|
+ (folder_name.empty() ? "" : " AND name=?"), false);
|
|
q->Bind(tgroup + group_offset);
|
|
if (!folder_name.empty())
|
|
{
|
|
q->Bind(folder_name);
|
|
}
|
|
q->Write();
|
|
q->Reset();
|
|
|
|
if (db->getLastChanges() > 0)
|
|
{
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "err_backup_folder_not_found");
|
|
}
|
|
|
|
db->destroyQuery(q);
|
|
}
|
|
|
|
void ClientConnector::CMD_ENABLE_END_TO_END_FILE_BACKUP_VERIFICATION(const std::string &cmd)
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
end_to_end_file_backup_verification_enabled=true;
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_VSSLOG(const std::string &cmd)
|
|
{
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_GetLog);
|
|
unsigned int result_id = IndexThread::getResultId();
|
|
data.addUInt(result_id);
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
std::string ret;
|
|
IndexThread::getResult(result_id, 8000, ret);
|
|
tcpstack.Send(pipe, ret);
|
|
IndexThread::removeResult(result_id);
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_ACCESS_PARAMS(str_map ¶ms)
|
|
{
|
|
if(crypto_fak==nullptr)
|
|
{
|
|
Server->Log("No cryptoplugin present. Action not possible.", LL_ERROR);
|
|
tcpstack.Send(pipe, "");
|
|
return;
|
|
}
|
|
|
|
std::string tokens=params["tokens"];
|
|
|
|
std::unique_ptr<ISettingsReader> settings(
|
|
Server->createFileSettingsReader("urbackup/data/settings.cfg"));
|
|
|
|
std::string server_url;
|
|
if( (!settings->getValue("server_url", &server_url)
|
|
&& !settings->getValue("server_url_def", &server_url) )
|
|
|| server_url.empty())
|
|
{
|
|
Server->Log("Server url empty", LL_ERROR);
|
|
tcpstack.Send(pipe, "");
|
|
return;
|
|
}
|
|
|
|
std::string ret = getAccessTokensParams(tokens, true, params["virtual_client"]);
|
|
|
|
if(!ret.empty())
|
|
{
|
|
ret[0]='#';
|
|
ret = server_url + ret;
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "");
|
|
return;
|
|
}
|
|
|
|
tcpstack.Send(pipe, ret);
|
|
}
|
|
|
|
void ClientConnector::CMD_CONTINUOUS_WATCH_START()
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
|
|
void ClientConnector::CMD_SCRIPT_STDERR(const std::string& cmd)
|
|
{
|
|
std::string script_cmd = (cmd.substr(14));
|
|
|
|
if(next(script_cmd, 0, "SCRIPT|"))
|
|
{
|
|
script_cmd = script_cmd.substr(7);
|
|
}
|
|
|
|
std::string stderr_out;
|
|
int exit_code;
|
|
if(IndexThread::getFileSrv()->getExitInformation(script_cmd, stderr_out, exit_code))
|
|
{
|
|
tcpstack.Send(pipe, convert(exit_code)+" "+convert(Server->getTimeMS())+" "+stderr_out);
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "err");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_FILE_RESTORE(const std::string& cmd, const std::string& identity)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(cmd, ¶ms);
|
|
|
|
int64 restore_process_id = 0;
|
|
|
|
bool has_restore_token = false;
|
|
if (!restore_token.restore_token.empty()
|
|
&& params["restore_token"] == restore_token.restore_token
|
|
&& Server->getTimeMS() - restore_token.restore_token_time<60*60*1000)
|
|
{
|
|
restore_token.restore_token = "";
|
|
has_restore_token = true;
|
|
restore_process_id = restore_token.process_id;
|
|
restore_token.process_id = 0;
|
|
}
|
|
|
|
std::string restore=Server->getServerParameter("allow_restore");
|
|
|
|
if(restore=="default")
|
|
{
|
|
restore="client-confirms";
|
|
}
|
|
|
|
if(restore!="client-confirms" && restore!="server-confirms"
|
|
&& !has_restore_token)
|
|
{
|
|
tcpstack.Send(pipe, "disabled");
|
|
return;
|
|
}
|
|
|
|
bool client_token_encrypted = true;
|
|
std::string client_token = (params["client_token"]);
|
|
if (client_token.empty()
|
|
&& params.find("client_token_d") != params.end())
|
|
{
|
|
client_token = params["client_token_d"];
|
|
client_token_encrypted = false;
|
|
}
|
|
std::string server_token=params["server_token"];
|
|
int64 restore_id=watoi64(params["id"]);
|
|
int64 status_id=watoi64(params["status_id"]);
|
|
int64 log_id=watoi64(params["log_id"]);
|
|
std::string restore_path = params["restore_path"];
|
|
bool single_file = params["single_file"]=="1";
|
|
bool clean_other = params["clean_other"] == "1";
|
|
bool ignore_other_fs = params["ignore_other_fs"] != "0";
|
|
int tgroup = watoi(params["tgroup"]);
|
|
std::string clientsubname = params["clientsubname"];
|
|
int64 restore_flags = 0;
|
|
str_map::iterator restore_flags_it = params.find("restore_flags");
|
|
if (restore_flags_it != params.end())
|
|
{
|
|
restore_flags = watoi64(restore_flags_it->second);
|
|
}
|
|
|
|
if(restore_process_id==0)
|
|
{
|
|
IScopedLock lock(process_mutex);
|
|
restore_process_id = ++curr_backup_running_id;
|
|
}
|
|
|
|
if (crypto_fak != nullptr
|
|
&& client_token_encrypted)
|
|
{
|
|
std::unique_ptr<ISettingsReader> access_keys(
|
|
Server->createFileSettingsReader("urbackup/access_keys.properties"));
|
|
|
|
if (access_keys.get() != nullptr)
|
|
{
|
|
std::string access_key;
|
|
if (access_keys->getValue("key." + server_token, &access_key))
|
|
{
|
|
client_token = crypto_fak->decryptAuthenticatedAES(base64_decode_dash(client_token), access_key, 1);
|
|
|
|
if (client_token.empty()
|
|
&& access_keys->getValue("last.key." + server_token, &access_key))
|
|
{
|
|
client_token = crypto_fak->decryptAuthenticatedAES(base64_decode_dash(client_token), access_key, 1);
|
|
}
|
|
|
|
if (client_token.empty())
|
|
{
|
|
Server->Log("Error decrypting server access token. Access key might be wrong.", LL_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("key."+ server_token+" not found in urbackup/access_keys.properties", LL_ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Server->Log("Error opening urbackup/access_keys.properties", LL_ERROR);
|
|
}
|
|
}
|
|
else if(client_token_encrypted)
|
|
{
|
|
Server->Log("Error decrypting server access token. Crypto_fak not loaded.", LL_ERROR);
|
|
}
|
|
|
|
int facet_id = getFacetId(ServerIdentityMgr::getIdentityFromSessionIdentity(identity));
|
|
|
|
RestoreFiles* local_restore_files = new RestoreFiles(facet_id, restore_process_id, restore_id, status_id, log_id,
|
|
client_token, server_token, restore_path, single_file, clean_other, ignore_other_fs, restore_flags,
|
|
tgroup, clientsubname);
|
|
|
|
if(restore == "client-confirms" && !has_restore_token)
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
restore_ok_status = RestoreOk_Wait;
|
|
++ask_restore_ok;
|
|
status_updated = true;
|
|
|
|
if(restore_files!=nullptr)
|
|
{
|
|
delete restore_files;
|
|
}
|
|
|
|
restore_files = local_restore_files;
|
|
}
|
|
else
|
|
{
|
|
Server->createThread(local_restore_files, "file restore", IServer::CreateThreadFlags_LargeStackSize);
|
|
}
|
|
|
|
tcpstack.Send(pipe, "ok");
|
|
}
|
|
|
|
void ClientConnector::CMD_RESTORE_OK( str_map ¶ms )
|
|
{
|
|
IScopedLock lock(backup_mutex);
|
|
JSON::Object ret;
|
|
if(params["ok"]=="true")
|
|
{
|
|
restore_ok_status = RestoreOk_Ok;
|
|
|
|
ret.set("accepted", true);
|
|
|
|
if(restore_files!=nullptr)
|
|
{
|
|
ret.set("process_id", restore_files->get_local_process_id());
|
|
|
|
Server->createThread(restore_files, "file restore", IServer::CreateThreadFlags_LargeStackSize);
|
|
restore_files=nullptr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
restore_ok_status = RestoreOk_Declined;
|
|
|
|
if (restore_files != nullptr)
|
|
{
|
|
restore_files->set_restore_declined(true);
|
|
Server->createThread(restore_files, "file restore", IServer::CreateThreadFlags_LargeStackSize);
|
|
restore_files = nullptr;
|
|
}
|
|
}
|
|
|
|
delete restore_files;
|
|
restore_files=nullptr;
|
|
|
|
ret.set("ok", true);
|
|
tcpstack.Send(pipe, ret.stringify(false));
|
|
}
|
|
|
|
void ClientConnector::CMD_CLIENT_ACCESS_KEY(const std::string& cmd)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(cmd, ¶ms);
|
|
|
|
std::string key = params["key"];
|
|
std::string token = params["token"];
|
|
|
|
if (!key.empty()
|
|
&& !token.empty()
|
|
&& !FileExists("urbackup/access_keys.properties") )
|
|
{
|
|
std::string access_keys_data;
|
|
access_keys_data += "key." + token + "=" +
|
|
key + "\n";
|
|
|
|
access_keys_data += "key_age." + token + "=" +
|
|
convert(Server->getTimeSeconds()) + "\n";
|
|
|
|
write_file_only_admin(access_keys_data, "urbackup/access_keys.properties.new");
|
|
os_rename_file("urbackup/access_keys.properties.new",
|
|
"urbackup/access_keys.properties");
|
|
|
|
tcpstack.Send(pipe, "OK");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "err: key or token empty");
|
|
}
|
|
}
|
|
|
|
void ClientConnector::CMD_WRITE_TOKENS(const std::string& cmd)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(cmd, ¶ms);
|
|
|
|
std::string async_id;
|
|
async_id.resize(16);
|
|
Server->randomFill(&async_id[0], async_id.size());
|
|
|
|
curr_result_id = IndexThread::getResultId();
|
|
|
|
SAsyncFileList new_async_file_list = {
|
|
Server->getTimeMS(),
|
|
curr_result_id,
|
|
0,
|
|
ILLEGAL_THREADPOOL_TICKET
|
|
};
|
|
|
|
CWData data;
|
|
data.addChar(IndexThread::IndexThreadAction_WriteTokens);
|
|
data.addUInt(curr_result_id);
|
|
data.addString(server_token);
|
|
|
|
IndexThread::getMsgPipe()->Write(data.getDataPtr(), data.getDataSize());
|
|
|
|
std::string msg;
|
|
if (IndexThread::getResult(curr_result_id, 3000, msg)
|
|
&& !msg.empty())
|
|
{
|
|
if(msg=="done")
|
|
tcpstack.Send(pipe, "OK");
|
|
else
|
|
tcpstack.Send(pipe, msg);
|
|
IndexThread::removeResult(curr_result_id);
|
|
return;
|
|
}
|
|
|
|
IndexThread::refResult(curr_result_id);
|
|
|
|
IScopedLock lock(backup_mutex);
|
|
|
|
async_file_index[async_id] = new_async_file_list;
|
|
|
|
lock.relock(nullptr);
|
|
|
|
tcpstack.Send(pipe, "ASYNC-async_id=" + bytesToHex(async_id));
|
|
}
|
|
|
|
void ClientConnector::CMD_GET_CLIENTNAME(const std::string& cmd)
|
|
{
|
|
std::string name = IndexThread::getFileSrv()->getServerName();
|
|
tcpstack.Send(pipe, "name="+EscapeParamString(name));
|
|
}
|
|
|
|
void ClientConnector::CMD_FINISH_LBACKUP(const std::string& cmd)
|
|
{
|
|
str_map params;
|
|
ParseParamStrHttp(cmd.substr(15), ¶ms);
|
|
|
|
int step = watoi(params["step"]);
|
|
|
|
if (step == 1)
|
|
{
|
|
std::vector<SFile> files = getFiles("urbackup");
|
|
|
|
size_t idx = 0;
|
|
std::string ret = "ok=1";
|
|
|
|
for (SFile& file : files)
|
|
{
|
|
if (!file.isdir && next(file.name, 0, "bg_del_"+conv_filename(server_token)))
|
|
{
|
|
ret += "&del_fn" + std::to_string(idx) + "=" + EscapeParamString(file.name);
|
|
++idx;
|
|
}
|
|
}
|
|
|
|
tcpstack.Send(pipe, ret);
|
|
}
|
|
else if (step == 2)
|
|
{
|
|
std::vector<std::string> del_fns;
|
|
size_t idx = 0;
|
|
while (params.find("del_fn" + std::to_string(idx)) != params.end())
|
|
{
|
|
del_fns.push_back(params["del_fn" + std::to_string(idx)]);
|
|
++idx;
|
|
}
|
|
|
|
std::vector<SFile> files = getFiles("urbackup");
|
|
|
|
std::sort(files.begin(), files.end());
|
|
|
|
for (std::string del_fn : del_fns)
|
|
{
|
|
SFile cf;
|
|
cf.name = del_fn;
|
|
|
|
if (std::binary_search(files.begin(), files.end(), cf))
|
|
{
|
|
Server->deleteFile("urbackup" + os_file_sep() + conv_filename(cf.name));
|
|
}
|
|
}
|
|
|
|
tcpstack.Send(pipe, "ok=1");
|
|
}
|
|
else
|
|
{
|
|
tcpstack.Send(pipe, "ok=0&err=no step");
|
|
}
|
|
}
|
|
|