/*************************************************************************
* 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 .
**************************************************************************/
#include "action_header.h"
#include "../../Interface/File.h"
#include "../../urbackupcommon/os_functions.h"
#include "../../urbackupcommon/file_metadata.h"
#include "../../urbackupcommon/mbrdata.h"
#include "../../Interface/SettingsReader.h"
#include "../../cryptoplugin/ICryptoFactory.h"
#include "../../fsimageplugin/IFSImageFactory.h"
#include "../../fsimageplugin/IVHDFile.h"
#include "../server_settings.h"
#include "backups.h"
#include
#include
#include
#include "../../fileservplugin/IFileServ.h"
#include "../server_status.h"
#include "../restore_client.h"
#include "../dao/ServerBackupDao.h"
#include "../server_dir_links.h"
#include "../ImageMount.h"
#include "../server.h"
#include "../server_cleanup.h"
#include "../dao/ServerCleanupDao.h"
extern ICryptoFactory *crypto_fak;
extern IFileServ* fileserv;
extern IFSImageFactory *image_fak;
namespace backupaccess
{
std::string constructFilter(const std::vector &clientid, std::string key)
{
std::string clientf="(";
for(size_t i=0;i &backup_tokens, const std::vector &tokens, bool skip_hashes);
namespace
{
bool sendFile(Helper& helper, const std::string& filename)
{
THREAD_ID tid = Server->getThreadID();
Server->setContentType(tid, "application/octet-stream");
Server->addHeader(tid, "Cache-Control: no-cache");
Server->addHeader(tid, "Content-Disposition: attachment; filename=\""+(ExtractFileName(filename))+"\"");
IFile *in=Server->openFile(os_file_prefix(filename), MODE_READ);
if(in!=NULL)
{
helper.releaseAll();
Server->addHeader(tid, "Content-Length: "+convert(in->Size()) );
char buf[4096];
_u32 r;
do
{
r=in->Read(buf, 4096);
Server->WriteRaw(tid, buf, r, false);
}
while(r>0);
Server->destroy(in);
return true;
}
else
{
Server->Log("Error opening file \""+filename+"\"", LL_ERROR);
return false;
}
}
bool sendZip(Helper& helper, std::string folderbase, std::string foldername, std::string hashfolderbase, std::string hashfoldername, const std::string& filter, bool token_authentication,
const std::vector& backup_tokens, const std::vector& tokens, bool skip_hashes)
{
std::string zipname=ExtractFileName(foldername)+".zip";
THREAD_ID tid = Server->getThreadID();
Server->setContentType(tid, "application/octet-stream");
Server->addHeader(tid, "Cache-Control: no-cache");
Server->addHeader(tid, "Content-Disposition: attachment; filename=\""+(zipname)+"\"");
helper.releaseAll();
if(!foldername.empty())
{
if(foldername[foldername.size()-1]==os_file_sep()[0])
{
foldername.resize(foldername.size()-1);
}
}
if(!hashfoldername.empty())
{
if(hashfoldername[hashfoldername.size()-1]==os_file_sep()[0])
{
hashfoldername.resize(hashfoldername.size()-1);
}
}
return create_zip_to_output(folderbase, foldername, hashfolderbase, hashfoldername, filter, token_authentication,
backup_tokens, tokens, skip_hashes);
}
std::vector getMetadata(std::string dir, const std::vector& files, bool skip_special)
{
std::vector ret;
ret.resize(files.size());
if(dir.empty() || dir[dir.size()-1]!=os_file_sep()[0])
{
dir+=os_file_sep();
}
for(size_t i=0;iLog("Error reading metadata of file "+dir+os_file_sep()+ file.name, LL_ERROR);
}
}
return ret;
}
FileMetadata getMetaData(std::string path, bool is_file)
{
if(path.empty() || (!is_file && path[path.size()-1]!=os_file_sep()[0]) )
{
path+=os_file_sep();
}
std::string metadata_fn;
if(is_file)
{
metadata_fn = ExtractFilePath(path) + os_file_sep() + escape_metadata_fn(ExtractFileName(path));
}
else
{
metadata_fn = ExtractFilePath(path) + os_file_sep() + escape_metadata_fn(ExtractFileName(path))+os_file_sep()+metadata_dir_fn;
}
FileMetadata ret;
if(!read_metadata(metadata_fn, ret) )
{
Server->Log("Error reading metadata of path "+path, LL_ERROR);
}
return ret;
}
int getClientid(IDatabase* db, const std::string& clientname)
{
IQuery* q=db->Prepare("SELECT id FROM clients WHERE name=?");
q->Bind(clientname);
db_results res=q->Read();
q->Reset();
if(!res.empty())
{
return watoi(res[0]["id"]);
}
return -1;
}
std::string getClientname(IDatabase* db, int clientid)
{
IQuery *q=db->Prepare("SELECT name FROM clients WHERE id=?");
q->Bind(clientid);
db_results res=q->Read();
q->Reset();
if(!res.empty())
{
return res[0]["name"];
}
else
{
return std::string();
}
}
bool checkBackupTokens(const std::string& fileaccesstokens, const std::string& backupfolder, const std::string& clientname, const std::string& path)
{
std::vector tokens;
Tokenize(fileaccesstokens, tokens, ";");
backupaccess::STokens backup_tokens = backupaccess::readTokens(backupfolder, clientname, path);
if(backup_tokens.tokens.empty())
{
return false;
}
for(size_t i=0;i tokens;
Tokenize(fileaccesstokens, tokens, ";");
backupaccess::STokens backup_tokens = backupaccess::readTokens(backupfolder, clientname, path);
if(backup_tokens.tokens.empty())
{
return false;
}
FileMetadata metadata;
if(!read_metadata(filemetadatapath, metadata))
{
return false;
}
return checkFileToken(backup_tokens.tokens, tokens, metadata);
}
} //unnamed namespace
namespace backupaccess
{
std::string getBackupFolder(IDatabase* db)
{
IQuery* q=db->Prepare("SELECT value FROM settings_db.settings WHERE key='backupfolder' AND clientid=0");
db_results res_bf=q->Read();
q->Reset();
if(!res_bf.empty() )
{
return res_bf[0]["value"];
}
else
{
return std::string();
}
}
std::string decryptTokens(IDatabase* db, const str_map& CURRP)
{
if(crypto_fak==NULL)
{
return std::string();
}
int clientid;
str_map::const_iterator iter_clientname =CURRP.find("clientname");
if(iter_clientname!=CURRP.end())
{
clientid = getClientid(db, iter_clientname->second);
}
else
{
str_map::const_iterator iter_clientid = CURRP.find("clientid");
if(iter_clientid!=CURRP.end())
{
clientid = watoi(iter_clientid->second);
}
else
{
return std::string();
}
}
if(clientid==-1)
{
return std::string();
}
ServerSettings server_settings(db, clientid);
std::string client_key = server_settings.getSettings()->client_access_key;
size_t i=0;
str_map::const_iterator iter;
do
{
iter = CURRP.find("tokens"+convert(i));
if(iter!=CURRP.end())
{
std::string bin_input = base64_decode_dash(iter->second);
std::string session_key = crypto_fak->decryptAuthenticatedAES(bin_input, client_key, 1);
if(!session_key.empty())
{
iter = CURRP.find("token_data");
if(iter==CURRP.end())
{
return std::string();
}
bin_input = base64_decode_dash(iter->second);
std::string decry = crypto_fak->decryptAuthenticatedAES(bin_input, session_key, 1);
if(!decry.empty())
{
std::string tokenhash = Server->GenerateBinaryMD5(bin_input);
ServerBackupDao backupdao(db);
if(backupdao.hasUsedAccessToken(tokenhash).exists)
{
return std::string();
}
else
{
backupdao.addUsedAccessToken(clientid, tokenhash);
}
}
return decry;
}
}
++i;
} while (iter!=CURRP.end());
return std::string();
}
STokens readTokens(const std::string& backupfolder, const std::string& clientname, const std::string& path)
{
if(backupfolder.empty() || clientname.empty() || path.empty())
{
return STokens();
}
std::auto_ptr backup_tokens(Server->createFileSettingsReader(backupfolder+os_file_sep()+clientname+os_file_sep()+path+os_file_sep()+".hashes"+os_file_sep()+".urbackup_tokens.properties"));
if(!backup_tokens.get())
{
return STokens();
}
std::string ids_str = backup_tokens->getValue("ids", "");
std::vector ids;
Tokenize(ids_str, ids, ",");
std::vector ret;
for(size_t i=0;igetValue(ids[i]+"."+"accountname", "")),
backup_tokens->getValue(ids[i]+"."+"token", "") };
ret.push_back(token);
}
STokens tokens = { backup_tokens->getValue("access_key", ""),
ret };
return tokens;
}
bool checkFileToken( const std::vector &backup_tokens, const std::vector &tokens, const FileMetadata &metadata )
{
bool has_permission=false;
for(size_t i=0;igetThreadID(), NULL, NULL);
int last_filebackup = 0;
IQuery* q_last = db->Prepare("SELECT id FROM backups WHERE clientid=? AND done=1 ORDER BY backuptime DESC LIMIT 1");
q_last->Bind(t_clientid);
db_results res_last = q_last->Read();
q_last->Reset();
if (!res_last.empty())
{
last_filebackup = watoi(res_last[0]["id"]);
}
IQuery *q=db->Prepare("SELECT id, strftime('"+helper.getTimeFormatString()+"', backuptime) AS t_backuptime, incremental, size_bytes, archived, archive_timeout, path, delete_pending FROM backups WHERE complete=1 AND done=1 AND clientid=? ORDER BY backuptime DESC");
q->Bind(t_clientid);
db_results res=q->Read();
JSON::Array backups;
has_access = false;
if (res.empty())
{
has_access = true;
}
for(size_t i=0;igetTimeSeconds();
}
}
if(archived!=0)
{
obj.set("archive_timeout", archive_timeout);
}
if (backupid == last_filebackup)
{
obj.set("disable_delete", true);
}
backups.add(obj);
}
return backups;
}
bool valid_path_component(const std::string& t_path)
{
return !t_path.empty() && t_path != " " && t_path != "." && t_path != ".."
&& t_path.find("/") == std::string::npos
&& t_path.find("\\") == std::string::npos;
}
SPathInfo get_metadata_path_with_tokens(const std::string& u_path, std::string* fileaccesstokens, std::string clientname, std::string backupfolder, int* backupid, std::string backuppath)
{
SPathInfo ret;
std::vector tokens;
if(fileaccesstokens)
{
ret.backup_tokens = readTokens(backupfolder, clientname, backuppath);
Tokenize(*fileaccesstokens, tokens, ";");
}
std::vector t_path;
Tokenize(u_path, t_path, "/");
ret.can_access_path=true;
for(size_t i=0;i t_path;
Tokenize(u_path, t_path, "/");
std::string rel_path;
for (size_t i = 0; i < t_path.size(); ++i)
{
if (valid_path_component(t_path[i]))
{
rel_path += os_file_sep() + t_path[i];
}
}
std::string content_path = backuppath + rel_path;
int ftype = os_get_file_type(os_file_prefix(content_path));
SPathInfo ret;
ret.can_access_path = ftype!=0;
ret.full_path = content_path;
ret.is_file = (ftype & EFileType_File) > 0;
ret.is_symlink = (ftype & EFileType_Symlink) > 0;
ret.rel_path = !rel_path.empty() ? rel_path.substr(1) : rel_path;
return ret;
}
std::string get_backup_path(IDatabase* db, int backupid, int t_clientid)
{
IQuery* q=db->Prepare("SELECT path FROM backups WHERE id=? AND clientid=?");
q->Bind(backupid);
q->Bind(t_clientid);
db_results res=q->Read();
q->Reset();
if(!res.empty())
{
return res[0]["path"];
}
else
{
return std::string();
}
}
bool get_files_with_tokens(IDatabase* db, int* backupid, int t_clientid, std::string clientname, std::string* fileaccesstokens,
const std::string& u_path, int backupid_offset, JSON::Object& ret)
{
Helper helper(Server->getThreadID(), NULL, NULL);
db_results res;
if(backupid)
{
IQuery* q=db->Prepare("SELECT path,strftime('"+helper.getTimeFormatString()+"', backuptime) AS backuptime FROM backups WHERE id=? AND clientid=?");
q->Bind(*backupid);
q->Bind(t_clientid);
res=q->Read();
q->Reset();
if(!res.empty())
{
ret.set("backuptime", watoi64(res[0]["backuptime"]));
ret.set("backupid", *backupid + backupid_offset);
}
else
{
return false;
}
}
else
{
IQuery* q=db->Prepare("SELECT id, path,strftime('"+helper.getTimeFormatString()+"', backuptime) AS backuptime FROM backups WHERE clientid=? ORDER BY backuptime DESC");
q->Bind(t_clientid);
res=q->Read();
q->Reset();
}
std::string backupfolder=getBackupFolder(db);
if(backupfolder.empty())
{
return false;
}
bool can_restore = false;
bool restore_server_confirms;
if (ServerStatus::canRestore(clientname, restore_server_confirms) )
{
can_restore = true;
if (fileaccesstokens)
{
ServerSettings clientsettings(db, t_clientid);
if (!clientsettings.getSettings()->allow_file_restore)
{
can_restore = false;
}
}
}
std::string path;
std::vector t_path;
Tokenize(u_path, t_path, "/");
bool is_file=false;
bool has_access=false;
JSON::Array ret_files;
for(size_t k=0;k tokens;
if(fileaccesstokens)
{
Tokenize(*fileaccesstokens, tokens, ";");
}
ret.set("clientname", clientname);
ret.set("clientid", t_clientid);
ret.set("path", u_path);
if(can_restore)
{
ret.set("can_restore", true);
if(restore_server_confirms)
{
ret.set("server_confirms_restore", true);
}
}
if(backupid)
{
std::string full_path = path_info.full_path;
std::string full_metadata_path = path_info.full_metadata_path;
std::string fn_filter;
if(is_file)
{
fn_filter = ExtractFileName(path_info.full_path, os_file_sep());
if(!fn_filter.empty())
{
full_path = ExtractFilePath(path_info.full_path, os_file_sep());
full_metadata_path = ExtractFilePath(path_info.full_metadata_path, os_file_sep());
}
}
std::vector tfiles=getFiles(os_file_prefix(full_path), NULL);
std::vector tmetadata=getMetadata(full_metadata_path, tfiles, path.empty());
JSON::Array files;
for(size_t i=0;i(tmetadata[i].shahash.c_str()), static_cast(tmetadata[i].shahash.size())));
}
files.add(obj);
}
}
ret.set("files", files);
}
else
{
std::auto_ptr f;
if(path_info.is_file)
{
f.reset(Server->openFile(os_file_prefix(path_info.full_path), MODE_READ));
}
if( (path_info.is_file && f.get()) || os_directory_exists(os_file_prefix(path_info.full_path)) )
{
FileMetadata metadata = getMetaData(path_info.full_metadata_path, path_info.is_file);
JSON::Object obj;
obj.set("name", ExtractFileName(path_info.full_path));
if(path_info.is_file)
{
obj.set("size", f->Size());
obj.set("dir", false);
}
else
{
obj.set("dir", true);
}
obj.set("mod", metadata.last_modified);
obj.set("creat", metadata.created);
obj.set("access", metadata.accessed);
obj.set("backupid", watoi(res[k]["id"])+backupid_offset);
obj.set("backuptime", watoi64(res[k]["backuptime"]));
if(!metadata.shahash.empty())
{
obj.set("shahash", base64_encode(reinterpret_cast(metadata.shahash.c_str()), static_cast(metadata.shahash.size())));
}
ret_files.add(obj);
}
}
}
}
if(!backupid)
{
ret.set("single_item", true);
ret.set("is_file", is_file);
ret.set("files", ret_files);
}
return has_access;
}
JSON::Array get_backup_images(IDatabase * db, int t_clientid, std::string clientname, int backupid_offset)
{
std::string backupfolder = getBackupFolder(db);
Helper helper(Server->getThreadID(), NULL, NULL);
IQuery *q = db->Prepare("SELECT id, strftime('" + helper.getTimeFormatString() + "', backuptime) AS t_backuptime, incremental, size_bytes, archived, archive_timeout, path, letter, delete_pending FROM backup_images WHERE complete=1 AND clientid=? ORDER BY backuptime DESC");
q->Bind(t_clientid);
db_results res = q->Read();
JSON::Array backups;
for (size_t i = 0; igetTimeSeconds();
}
}
if (archived != 0)
{
obj.set("archive_timeout", archive_timeout);
}
backups.add(obj);
}
return backups;
}
JSON::Object get_image_info(IDatabase* db, int backupid, int t_clientid, int backupid_offset, std::string& path,
std::vector& partitions)
{
Helper helper(Server->getThreadID(), NULL, NULL);
IQuery *q = db->Prepare("SELECT id, strftime('" + helper.getTimeFormatString() + "', backuptime) AS t_backuptime, incremental, "
"size_bytes, archived, archive_timeout, path, letter FROM backup_images WHERE complete=1 AND clientid=? AND id=?");
q->Bind(t_clientid);
q->Bind(backupid);
db_results res = q->Read();
JSON::Object ret;
if (!res.empty())
{
path = res[0]["path"];
ret.set("id", watoi(res[0]["id"]) + backupid_offset);
ret.set("backuptime", watoi64(res[0]["t_backuptime"]));
ret.set("incremental", watoi(res[0]["incremental"]));
ret.set("size_bytes", watoi64(res[0]["size_bytes"]));
ret.set("letter", res[0]["letter"]);
int archived = watoi(res[0]["archived"]);
ret.set("archived", watoi(res[0]["archived"]));
_i64 archive_timeout = 0;
if (!res[0]["archive_timeout"].empty())
{
archive_timeout = watoi64(res[0]["archive_timeout"]);
if (archive_timeout != 0)
{
archive_timeout -= Server->getTimeSeconds();
}
}
if (archived != 0)
{
ret.set("archive_timeout", archive_timeout);
}
std::string path = res[0]["path"];
std::string filename = ExtractFileName(path);
std::string extension = findextension(filename);
bool disk_image = false;
std::auto_ptr mbrfile(Server->openFile(os_file_prefix(path + ".mbr"), MODE_READ));
if (mbrfile.get() && mbrfile->Size()<10*1024*1024)
{
std::string mbrdata_header = mbrfile->Read(0LL, 2);
CRData mbrdata_header_view(mbrdata_header.data(), mbrdata_header.size());
char version = 0;
if (mbrdata_header_view.getChar(&version)
&& mbrdata_header_view.getChar(&version)
&& version != 100)
{
std::string mbrdata = mbrfile->Read(0LL, static_cast<_u32>(mbrfile->Size()));
if (mbrdata.size() == mbrfile->Size())
{
CRData mbrdata_view(mbrdata.data(), mbrdata.size());
SMBRData mbr(mbrdata_view);
if (!mbr.hasError())
{
disk_image = false;
ret.set("part_table", mbr.gpt_style ? "GPT" : "MBR");
ret.set("disk_number", mbr.device_number);
ret.set("partition_number", mbr.partition_number);
ret.set("volume_name", mbr.volume_name);
ret.set("fs_type", mbr.fsn);
ret.set("serial_number", mbr.serial_number);
}
}
}
else if (version == 100)
{
disk_image = true;
}
}
std::auto_ptr vhdfile;
if (extension == "vhd"
|| extension == "vhdz")
{
vhdfile.reset(image_fak->createVHDFile(path, true, 0));
}
else if(extension=="raw")
{
vhdfile.reset(image_fak->createVHDFile(path, true, 0, 2*1024*1024, false, IFSImageFactory::ImageFormat_RawCowFile));
}
else
{
assert(false);
}
if (vhdfile.get() != NULL)
{
ret.set("volume_size", vhdfile->getSize());
if (disk_image)
{
bool gpt_style;
partitions = image_fak->readPartitions(vhdfile.get(), 0, gpt_style);
ret.set("part_table", gpt_style ? "GPT" : "MBR");
}
}
}
return ret;
}
bool get_image_files(IDatabase* db, int backupid, int t_clientid, std::string clientname,
std::string u_path, int backupid_offset, bool do_mount, JSON::Object& ret)
{
std::string path;
std::vector partitions;
JSON::Object image_backup_info = get_image_info(db, backupid, t_clientid,
backupid_offset, path, partitions);
ret.set("image_backup_info", image_backup_info);
ret.set("single_item", false);
int partition = -1;
if (partitions.size()>1)
{
std::string m_u_path = u_path;
while (!m_u_path.empty()
&& m_u_path[0] == '/')
{
m_u_path.erase(0, 1);
}
if (next(m_u_path, 0, "partition "))
{
std::string part = getbetween("partition ", "/", m_u_path);
if (part.empty())
{
part = getafter("partition ", m_u_path);
}
partition = watoi(part)-1;
u_path = m_u_path.substr(10+part.size());
}
else
{
JSON::Array files;
for (size_t i = 0; i < partitions.size(); ++i)
{
JSON::Object obj;
obj.set("name", "partition "+convert(i+1));
obj.set("dir", true);
obj.set("size", partitions[i].length);
obj.set("mod", 0);
obj.set("creat", 0);
obj.set("access", 0);
files.add(obj);
}
ret.set("files", files);
ret.set("no_zip", true);
ret.set("backuptime", image_backup_info.get("backuptime"));
ret.set("backupid", backupid + backupid_offset);
return true;
}
}
else
{
partition = 0;
}
if (!path.empty())
{
ScopedMountedImage mounted_image;
bool has_mount_timeout;
std::string mount_errmsg;
std::string content_path = ImageMount::get_mount_path(backupid, partition,
!u_path.empty() && u_path!="/", mounted_image,
10000, has_mount_timeout, mount_errmsg);
if (content_path.empty()
|| !os_directory_exists(content_path) )
{
if (!do_mount)
{
if (BackupServer::canMountImages())
{
if (!has_mount_timeout)
{
ret.set("can_mount", true);
#ifndef __linux__
ret.set("os_mount", true);
#endif
}
else
{
ret.set("mount_in_progress", true);
}
}
else
{
ret.set("no_files", true);
}
content_path.clear();
}
else
{
content_path = ImageMount::get_mount_path(backupid, partition, true,
mounted_image, 10000, has_mount_timeout, mount_errmsg);
if (content_path.empty())
{
if (has_mount_timeout)
{
ret.set("mount_in_progress", true);
}
else
{
ret.set("mount_failed", true);
if (!mount_errmsg.empty())
{
ret.set("mount_errmsg", mount_errmsg);
}
}
}
}
}
JSON::Array files;
if (!content_path.empty())
{
std::vector t_path;
Tokenize(u_path, t_path, "/");
for (size_t i = 0; i < t_path.size(); ++i)
{
if (valid_path_component(t_path[i]))
{
content_path += os_file_sep() + t_path[i];
}
}
std::vector tfiles = getFiles(os_file_prefix(content_path), NULL);
for (size_t i = 0; igetThreadPool()->executeWait(new ServerCleanupThread(CleanupAction(backupid, false, &result)));
return result;
}
std::string deleteOrMarkImageBackup(IDatabase* db, int backupid, int clientid, bool mark_only)
{
ServerCleanupDao cleanup_dao(db);
if (cleanup_dao.getImageClientId(backupid).value != clientid)
{
return "wrong_clientid";
}
if (ServerCleanupThread::findArchivedImageRef(&cleanup_dao, backupid))
{
return "archived_ref";
}
if (!mark_only)
{
if (ServerCleanupThread::isImageLockedFromCleanup(backupid))
{
return "image_locked";
}
if (ServerCleanupThread::findLockedImageRef(&cleanup_dao, backupid))
{
return "locked_ref";
}
if (ServerCleanupThread::findUncompleteImageRef(&cleanup_dao, backupid))
{
return "incomplete_ref";
}
}
IQuery *q = db->Prepare("UPDATE backup_images SET delete_pending=1 WHERE id=? AND clientid=?");
std::vector refs = cleanup_dao.getImageRefs(backupid);
for (size_t i = 0; i < refs.size(); ++i)
{
std::vector new_refs = cleanup_dao.getImageRefs(refs[i].id);
refs.insert(refs.end(), new_refs.begin(), new_refs.end());
if (mark_only)
{
q->Bind(refs[i].id);
q->Bind(clientid);
q->Write();
q->Reset();
}
else
{
if (!removeImageBackup(refs[i].id))
{
return "remove_image_failed";
}
}
}
std::vector assoc_images = cleanup_dao.getAssocImageBackups(backupid);
for (size_t i = 0; i < assoc_images.size(); ++i)
{
if (mark_only)
{
q->Bind(assoc_images[i]);
q->Bind(clientid);
q->Write();
q->Reset();
}
else
{
if (!removeImageBackup(assoc_images[i]))
{
return "remove_image_failed";
}
}
}
if (mark_only)
{
q->Bind(backupid);
q->Bind(clientid);
q->Write();
q->Reset();
}
else
{
if (!removeImageBackup(backupid))
{
return "remove_image_failed";
}
}
return std::string();
}
void unmarkImageBackup(IDatabase* db, int backupid, int clientid)
{
ServerCleanupDao cleanup_dao(db);
if (cleanup_dao.getImageClientId(backupid).value != clientid)
{
return;
}
IQuery *q = db->Prepare("UPDATE backup_images SET delete_pending=0 WHERE id=? AND clientid=?");
std::vector assoc_images = cleanup_dao.getAssocImageBackups(backupid);
for (size_t i = 0; i < assoc_images.size(); ++i)
{
q->Bind(assoc_images[i]);
q->Bind(clientid);
q->Write();
q->Reset();
}
assoc_images = cleanup_dao.getAssocImageBackupsReverse(backupid);
for (size_t i = 0; i < assoc_images.size(); ++i)
{
q->Bind(assoc_images[i]);
q->Bind(clientid);
q->Write();
q->Reset();
}
std::vector refs = cleanup_dao.getImageRefsReverse(backupid);
for (size_t i = 0; i < refs.size(); ++i)
{
std::vector new_refs = cleanup_dao.getImageRefsReverse(refs[i].id);
refs.insert(refs.end(), new_refs.begin(), new_refs.end());
q->Bind(refs[i].id);
q->Bind(clientid);
q->Write();
q->Reset();
}
q->Bind(backupid);
q->Bind(clientid);
q->Write();
q->Reset();
}
void archiveBackup(IDatabase* db, int t_clientid, int backupid, int archive)
{
std::string tbl = "backups";
std::vector toarchive;
if (backupid < 0)
{
tbl = "backup_images";
backupid *= -1;
ServerCleanupDao cleanupdao(db);
std::vector assoc_images = cleanupdao.getAssocImageBackups(backupid);
toarchive.insert(toarchive.end(), assoc_images.begin(), assoc_images.end());
assoc_images = cleanupdao.getAssocImageBackupsReverse(backupid);
toarchive.insert(toarchive.end(), assoc_images.begin(), assoc_images.end());
}
toarchive.push_back(backupid);
IQuery *q = db->Prepare("UPDATE " + tbl + " SET archived="+convert(archive)+", archive_timeout=0 WHERE id=? AND clientid=?");
for (size_t i = 0; i < toarchive.size(); ++i)
{
q->Bind(toarchive[i]);
q->Bind(t_clientid);
q->Write();
q->Reset();
}
}
} // unnamed namespace
ACTION_IMPL(backups)
{
str_map& CURRP = GET.find("ses")==GET.end()? POST : GET;
Helper helper(tid, &CURRP, &PARAMS);
JSON::Object ret;
SUser *session=helper.getSession();
bool has_tokens = CURRP.find("tokens0")!=CURRP.end();
bool token_authentication=false;
std::string fileaccesstokens;
if( (session==NULL || session->id==SESSION_ID_TOKEN_AUTH) && has_tokens)
{
token_authentication=true;
fileaccesstokens = backupaccess::decryptTokens(helper.getDatabase(), CURRP);
if(fileaccesstokens.empty())
{
JSON::Object err_ret;
err_ret.set("err", "access_denied");
err_ret.set("errcode", "token_decryption_failed");
helper.Write(err_ret.stringify(false));
return;
}
else
{
std::string ses=helper.generateSession("anonymous");
ret.set("session", ses);
CURRP["ses"]=ses;
helper.update(tid, &CURRP, &PARAMS);
if(helper.getSession())
{
helper.getSession()->mStr["fileaccesstokens"]=fileaccesstokens;
helper.getSession()->id = SESSION_ID_TOKEN_AUTH;
}
}
}
else if(session!=NULL && session->id==SESSION_ID_TOKEN_AUTH)
{
fileaccesstokens = session->mStr["fileaccesstokens"];
if(!fileaccesstokens.empty())
{
token_authentication=true;
}
else
{
JSON::Object err_ret;
err_ret.set("err", "access_denied");
err_ret.set("errcode", "token_not_in_session");
helper.Write(err_ret.stringify(false));
return;
}
}
if(token_authentication)
{
ret.set("token_authentication", true);
}
std::string sa=CURRP["sa"];
std::string rights=helper.getRights("browse_backups");
std::string archive_rights=helper.getRights("manual_archive");
std::string delete_rights = helper.getRights("delete_backups");
std::vector clientid;
if(rights!="tokens")
{
clientid = helper.getRightIDs(rights);
}
std::vector clientid_archive=helper.getRightIDs(archive_rights);
std::vector clientid_delete = helper.getRightIDs(delete_rights);
if(clientid.size()==1 && sa.empty() )
{
sa="backups";
CURRP["clientid"]=convert(clientid[0]);
}
if( (session!=NULL && rights!="none" ) || token_authentication)
{
IDatabase *db=helper.getDatabase();
if(sa.empty())
{
std::string qstr = "SELECT id, name, strftime('"+helper.getTimeFormatString()+"', lastbackup) AS lastbackup FROM clients";
if(token_authentication)
{
if(CURRP.find("clientname")==CURRP.end())
{
std::vector tokens;
Tokenize(fileaccesstokens, tokens, ";");
IQuery* q_clients = db->Prepare("SELECT clientid FROM tokens_on_client WHERE token=?");
for(size_t i=0;iBind(tokens[i]);
db_results res = q_clients->Read();
q_clients->Reset();
if(!res.empty())
{
int n_clientid = watoi(res[0]["clientid"]);
if(std::find(clientid.begin(), clientid.end(), n_clientid)==
clientid.end())
{
clientid.push_back(n_clientid);
}
}
}
if(!clientid.empty())
{
qstr+=" WHERE "+backupaccess::constructFilter(clientid, "id");
}
else
{
sa="backups";
}
}
else
{
sa="backups";
}
}
else
{
if(!clientid.empty())
{
qstr+=" WHERE "+backupaccess::constructFilter(clientid, "id");
}
}
qstr+=" ORDER BY name";
if(sa!="backups")
{
IQuery *q=db->Prepare(qstr);
db_results res=q->Read();
q->Reset();
JSON::Array clients;
for(size_t i=0;iPrepare("UPDATE backups SET delete_pending=1 WHERE id=? AND clientid=?");
q->Bind(delete_id);
q->Bind(t_clientid);
q->Write();
q->Reset();
}
}
if (CURRP.find("stop_delete") != CURRP.end())
{
int stop_delete_id = watoi(CURRP["stop_delete"]);
if (stop_delete_id < 0)
{
stop_delete_id *= -1;
unmarkImageBackup(db, stop_delete_id, t_clientid);
}
else
{
IQuery *q = db->Prepare("UPDATE backups SET delete_pending=0 WHERE id=? AND clientid=?");
q->Bind(stop_delete_id);
q->Bind(t_clientid);
q->Write();
}
}
if (CURRP.find("delete_now") != CURRP.end())
{
int delete_now_id = watoi(CURRP["delete_now"]);
if (delete_now_id < 0)
{
delete_now_id *= -1;
std::string err = deleteOrMarkImageBackup(db, delete_now_id, t_clientid, false);
if (!err.empty())
{
ret.set("delete_now_err", err);
}
}
else
{
ServerSettings settings(db);
bool result = false;
Server->getThreadPool()->executeWait(new ServerCleanupThread(CleanupAction(settings.getSettings()->backupfolder, t_clientid, delete_now_id, false, &result)));
if (!result)
{
ret.set("delete_now_err", "delete_file_backup_failed");
}
}
}
}
bool has_access;
JSON::Array backups = backupaccess::get_backups_with_tokens(db, t_clientid, clientname,
token_authentication ? &fileaccesstokens : NULL, 0, has_access);
if (!has_access)
{
JSON::Object err_ret;
err_ret.set("err", "access_denied");
err_ret.set("errcode", "backups_access_denied");
helper.Write(err_ret.stringify(false));
return;
}
ret.set("backups", backups);
if (r_ok)
{
ret.set("backup_images", backupaccess::get_backup_images(db, t_clientid, clientname, 0));
}
else
{
JSON::Array empty;
ret.set("backup_images", empty);
}
ret.set("can_archive", archive_ok);
ret.set("can_delete", delete_ok);
ret.set("clientname", clientname);
ret.set("clientid", t_clientid);
}
else
{
ret.set("error", 2);
}
}
else if(sa=="files" || sa=="filesdl" || sa=="zipdl" || sa=="clientdl" )
{
int t_clientid;
std::string clientname;
if (token_authentication
&& CURRP.find("backupid") != CURRP.end()
&& watoi(CURRP["backupid"]) < 0)
{
ret.set("error", "2");
helper.Write(ret.stringify(false));
return;
}
if(token_authentication && CURRP.find("clientid")==CURRP.end())
{
clientname=CURRP["clientname"];
t_clientid = getClientid(helper.getDatabase(), clientname);
if(t_clientid==-1)
{
ret.set("error", "2");
helper.Write(ret.stringify(false));
return;
}
}
else
{
t_clientid = watoi(CURRP["clientid"]);
clientname = getClientname(helper.getDatabase(), t_clientid);
}
bool r_ok = token_authentication ? true :
helper.hasRights(t_clientid, rights, clientid);
if(r_ok)
{
bool has_backupid=CURRP.find("backupid")!=CURRP.end();
int backupid=0;
if(has_backupid)
{
backupid=watoi(CURRP["backupid"]);
}
std::string u_path=UnescapeHTML(UnescapeSQLString(CURRP["path"]));
if (backupid < 0
&& token_authentication)
{
clientname.clear();
}
if(!clientname.empty() )
{
if( (sa=="filesdl" || sa=="zipdl" || sa=="clientdl") && has_backupid)
{
std::string backupfolder = backupaccess::getBackupFolder(db);
if (backupfolder.empty())
{
return;
}
std::string backuppath;
backupaccess::SPathInfo path_info;
ScopedMountedImage mounted_image;
if (backupid >= 0)
{
backuppath = backupaccess::get_backup_path(db, backupid, t_clientid);
path_info = backupaccess::get_metadata_path_with_tokens(u_path, token_authentication ? &fileaccesstokens : NULL,
clientname, backupfolder, has_backupid ? &backupid : NULL, backuppath);
}
else
{
bool has_mount_timeout;
std::string mount_errmsg;
backuppath = ImageMount::get_mount_path(-1*backupid, 0, true, mounted_image, -1, has_mount_timeout, mount_errmsg);
path_info = backupaccess::get_image_path_info(u_path, clientname, backupfolder, backupid, backuppath);
}
if(token_authentication && !path_info.can_access_path)
{
JSON::Object err_ret;
err_ret.set("err", "access_denied");
err_ret.set("errcode", "path_dl_access_denied");
helper.Write(err_ret.stringify(false));
return;
}
std::vector tokens;
if(token_authentication)
{
Tokenize(fileaccesstokens, tokens, ";");
}
if(sa=="filesdl")
{
if(!token_authentication || checkFileToken(fileaccesstokens, backupfolder, clientname, backuppath, path_info.full_metadata_path))
{
sendFile(helper, path_info.full_path);
}
return;
}
else if(sa=="zipdl")
{
std::string bpath = backupfolder + os_file_sep() + clientname + os_file_sep() + backuppath;
sendZip(helper, bpath, path_info.full_path, backupid<0 ? "" : bpath + os_file_sep()+".hashes",
path_info.full_metadata_path, CURRP["filter"], token_authentication,
path_info.backup_tokens.tokens, tokens, backupid<0 ? false : path_info.rel_path.empty());
return;
}
else if(sa=="clientdl" && fileserv!=NULL)
{
if(ServerStatus::getStatus(clientname).comm_pipe==NULL)
{
ret.set("err", "client_not_online");
helper.Write(ret.stringify(false));
return;
}
int64 restore_id;
size_t status_id;
logid_t log_id;
THREADPOOL_TICKET ticket;
int64 restore_flags = 0;
if (!token_authentication)
{
restore_flags |= restore_flag_ignore_permissions;
}
if(!create_clientdl_thread(clientname, t_clientid, t_clientid, path_info.full_path, path_info.full_metadata_path, CURRP["filter"],
path_info.rel_path.empty(), path_info.rel_path, restore_id, status_id, log_id, std::string(),
std::vector< std::pair >(), true, true, greplace(os_file_sep(), "/", path_info.rel_path), true,
restore_flags, ticket, tokens, path_info.backup_tokens))
{
ret.set("err", "internal_error");
helper.Write(ret.stringify(false));
return;
}
if (session != NULL)
{
std::string wait_key = ServerSettings::generateRandomAuthKey();
session->mStr[wait_key] = convert(ticket);
ret.set("wait_key", wait_key);
}
ret.set("ok", "true");
helper.Write(ret.stringify(false));
return;
}
}
else
{
if (has_backupid && backupid < 0)
{
if (!backupaccess::get_image_files(db, -1 * backupid, t_clientid, clientname, u_path, 0, CURRP["mount"]=="1", ret))
{
JSON::Object err_ret;
err_ret.set("err", "internal_error");
helper.Write(err_ret.stringify(false));
return;
}
}
else
{
if (!backupaccess::get_files_with_tokens(db, has_backupid ? &backupid : NULL, t_clientid, clientname, token_authentication ? &fileaccesstokens : NULL,
u_path, 0, ret))
{
JSON::Object err_ret;
err_ret.set("err", "access_denied");
err_ret.set("errcode", "path_browse_access_denied");
helper.Write(err_ret.stringify(false));
return;
}
}
ret.set("clientname", clientname);
ret.set("clientid", t_clientid);
ret.set("path", u_path);
}
}
}
}
}
else
{
ret.set("error", 1);
}
helper.Write(ret.stringify(false));
}