/************************************************************************* * 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 . **************************************************************************/ #ifndef CLIENT_ONLY #include "action_header.h" #include "../server_settings.h" #include "../../urbackupcommon/os_functions.h" #include "../server_status.h" #include "../../cryptoplugin/ICryptoFactory.h" #include "../server.h" #include #include extern ICryptoFactory *crypto_fak; namespace { bool client_download(Helper& helper, JSON::Array &client_downloads) { IDatabase *db=helper.getDatabase(); ServerSettings settings(db); if(!FileExists("urbackup/UrBackupUpdate.exe")) return false; if(!FileExists("urbackup/UrBackupUpdate.sig2")) return false; if(crypto_fak==NULL) return false; bool clientid_rights_all; std::vector clientid_rights=helper.clientRights(RIGHT_SETTINGS, clientid_rights_all); db_results res=db->Read("SELECT id, name FROM clients ORDER BY name"); bool has_client=false; for(size_t i=0;iRead("SELECT tvalue FROM misc WHERE tkey='stop_show_version'"); if (!res.empty()) { return res[0]["tvalue"]; } return std::string(); } void set_server_version_info(IDatabase* db, JSON::Object& ret) { std::auto_ptr infoProperties(Server->createFileSettingsReader("urbackup/server_version_info.properties")); if(infoProperties.get()) { std::string stop_show_version = get_stop_show_version(db); std::string curr_version_str; if (infoProperties->getValue("curr_version_str", &curr_version_str)) { if (stop_show_version != curr_version_str) { ret.set("curr_version_str", curr_version_str); std::string curr_version_num; if (infoProperties->getValue("curr_version_num", &curr_version_num)) { ret.set("curr_version_num", watoi64(curr_version_num)); } } } } } void access_dir_details(std::string folder, std::string& ret) { bool has_error = false; getFiles(folder, &has_error); if (has_error) { ret += "Cannot access " + folder + ". " + os_last_error_str() + "\n"; } else { ret += "Can access " + folder + "\n"; } } std::string access_err_details(std::string folder) { std::vector toks; TokenizeMail(folder, toks, os_file_sep()); std::string ret; std::string cdir = os_file_sep(); access_dir_details(cdir, ret); for (size_t i = 0; i < toks.size(); ++i) { if (toks[i].empty()) continue; if (cdir!=os_file_sep()) { cdir += os_file_sep(); } cdir += toks[i]; access_dir_details(cdir, ret); } return ret; } std::string access_dir_hint(std::string folder) { if (folder.size() > 1 && folder[1] == ':') { bool has_error = false; getFiles(folder.substr(0, 2) + os_file_sep(), &has_error); if (has_error) { return "volume_not_accessible"; } } if ( (folder.size() > 2 && folder[0] == '\\' && folder[1] == '\\' && folder[2] != '?' ) || next(folder, 0, "\\\\?\\UNC") ) { bool has_error = false; getFiles(folder, &has_error); if (has_error && os_last_error() == 5 || os_last_error()== 1326 ) { return "folder_unc_access_denied"; } } return std::string(); } void add_remove_stop_show(IDatabase* db, std::string stop_show, bool add) { db_results res = db->Read("SELECT tvalue FROM misc WHERE tkey='stop_show'"); if (!res.empty()) { std::vector toks; TokenizeMail(res[0]["tvalue"], toks, ","); std::vector::iterator it = std::find(toks.begin(), toks.end(), stop_show); if (add) { if (it == toks.end()) { toks.push_back(stop_show); } } else { if (it != toks.end()) { toks.erase(it); } } std::string nval; for (size_t i = 0; i < toks.size(); ++i) { if (!nval.empty()) nval += ","; nval += toks[i]; } IQuery* q = db->Prepare("UPDATE misc SET tvalue=? WHERE tkey='stop_show'"); q->Bind(nval); q->Write(); q->Reset(); } else { IQuery* q = db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES ('stop_show', ?)"); q->Bind(stop_show); q->Write(); q->Reset(); } } bool is_stop_show(IDatabase* db, std::string stop_key) { db_results res = db->Read("SELECT tvalue FROM misc WHERE tkey='stop_show'"); if (!res.empty()) { std::vector toks; TokenizeMail(res[0]["tvalue"], toks, ","); return std::find(toks.begin(), toks.end(), stop_key) != toks.end(); } return false; } void set_stop_show_version(IDatabase* db, std::string ver) { db_results res = db->Read("SELECT tvalue FROM misc WHERE tkey='stop_show_version'"); if (!res.empty()) { IQuery* q = db->Prepare("UPDATE misc SET tvalue=? WHERE tkey='stop_show_version'"); q->Bind(ver); q->Write(); q->Reset(); } else { IQuery* q = db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES ('stop_show_version', ?)"); q->Bind(ver); q->Write(); q->Reset(); } } void add_stop_show(IDatabase* db, JSON::Object& ret, std::string dir_error_stop_show_key) { ret.set("dir_error_stop_show_key", dir_error_stop_show_key); if (is_stop_show(db, dir_error_stop_show_key)) { ret.set("dir_error_show", false); } } void access_dir_checks(IDatabase* db, ServerSettings& settings, std::string backupfolder, std::string backupfolder_uncompr, JSON::Object& ret) { #ifdef _WIN32 if (backupfolder.size() == 2 && backupfolder[1] == ':') { backupfolder += os_file_sep(); } if (backupfolder_uncompr.size() == 2 && backupfolder_uncompr[1] == ':') { backupfolder_uncompr += os_file_sep(); } #endif if (backupfolder.empty() || !os_directory_exists(os_file_prefix(backupfolder)) || !os_directory_exists(os_file_prefix(backupfolder_uncompr))) { if (!backupfolder.empty()) { ret.set("system_err", os_last_error_str()); } ret.set("dir_error", true); if (settings.getSettings()->backupfolder.empty()) { ret.set("dir_error_ext", "err_name_empty"); } else if (!os_directory_exists(os_file_prefix(settings.getSettings()->backupfolder))) { ret.set("dir_error_ext", "err_folder_not_found"); add_stop_show(db, ret, "dir_error_not_found"); } else { add_stop_show(db, ret, "dir_error_misc"); } #ifdef _WIN32 std::string hint = access_dir_hint(settings.getSettings()->backupfolder); if (!hint.empty()) { ret.set("dir_error_hint", hint); } #endif #ifndef _WIN32 ret.set("detail_err_str", access_err_details(settings.getSettings()->backupfolder)); #endif } else if (!os_directory_exists(os_file_prefix(backupfolder + os_file_sep() + "clients")) && !os_create_dir(os_file_prefix(backupfolder + os_file_sep() + "clients"))) { ret.set("system_err", os_last_error_str()); ret.set("dir_error", true); ret.set("dir_error_ext", "err_cannot_create_subdir"); add_stop_show(db, ret, "dir_error_cannot_create_subdir"); #ifdef _WIN32 std::string hint = access_dir_hint(backupfolder); if (!hint.empty()) { ret.set("dir_error_hint", hint); } #endif } else { bool has_access_error = false; std::string testfoldername = "testfolderHvfgh---dFFoeRRRf"; std::string testfolderpath = backupfolder + os_file_sep() + testfoldername; if (os_directory_exists(os_file_prefix(testfolderpath))) { if (!os_remove_dir(os_file_prefix(testfolderpath))) { ret.set("system_err", os_last_error_str()); ret.set("dir_error", true); ret.set("dir_error_ext", "err_cannot_create_subdir"); add_stop_show(db, ret, "dir_error_cannot_create_subdir2"); has_access_error = true; #ifdef _WIN32 std::string hint = access_dir_hint(backupfolder); if (!hint.empty()) { ret.set("dir_error_hint", hint); } #endif } } if (!has_access_error && !os_create_dir(os_file_prefix(testfolderpath))) { ret.set("system_err", os_last_error_str()); ret.set("dir_error", true); ret.set("dir_error_ext", "err_cannot_create_subdir"); add_stop_show(db, ret, "dir_error_cannot_create_subdir3"); #ifdef _WIN32 std::string hint = access_dir_hint(backupfolder); if (!hint.empty()) { ret.set("dir_error_hint", hint); } #endif has_access_error = true; } #ifdef _WIN32 else if (!settings.getSettings()->no_file_backups && os_directory_exists(os_file_prefix(backupfolder + os_file_sep() + "testfo~1"))) { ret.set("dir_error", true); ret.set("dir_error_ext", "dos_names_created"); add_stop_show(db, ret, "dos_names_created"); ret.set("dir_error_hint", "dos_names_created"); if (backupfolder.size() > 2 && backupfolder[1]==':') { ret.set("dir_error_volume", backupfolder.substr(0, 2)); } else { ret.set("dir_error_volume", ""); } } #endif if (!settings.getSettings()->no_file_backups) { std::string linkfolderpath = testfolderpath + "_link"; os_remove_symlink_dir(os_file_prefix(linkfolderpath)); Server->deleteFile(os_file_prefix(linkfolderpath)); if (!has_access_error && !os_link_symbolic(os_file_prefix(testfolderpath), os_file_prefix(linkfolderpath))) { ret.set("system_err", os_last_error_str()); ret.set("dir_error", true); ret.set("dir_error_ext", "err_cannot_create_symbolic_links"); ret.set("dir_error_hint", "UrBackup cannot create symbolic links on the backup storage. " "Your backup storage must support symbolic links in order for UrBackup to work correctly. " "The UrBackup Server must run as administrative user on Windows (If not you get error code 1314). " "Note: As of 2016-05-07 samba (which is used by many Linux based NAS operating systems for Windows file sharing) has not " "implemented the necessary functionality to support symbolic link creation from Windows (With this you get error code 4390)."); add_stop_show(db, ret, "dir_error_cannot_create_symbolic_links"); } os_remove_symlink_dir(os_file_prefix(linkfolderpath)); } if (!has_access_error && !os_remove_dir(os_file_prefix(testfolderpath))) { ret.set("system_err", os_last_error_str()); ret.set("dir_error", true); ret.set("dir_error_ext", "err_cannot_create_subdir"); add_stop_show(db, ret, "dir_error_cannot_create_subdir4"); has_access_error = true; } } IFile *tmp = Server->openTemporaryFile(); ScopedDeleteFile delete_tmp_file(tmp); if (tmp == NULL) { ret.set("tmpdir_error", true); ret.set("tmpdir_error_stop_show_key", "tmpdir_error"); if (is_stop_show(db, "tmpdir_error")) { ret.set("tmpdir_error_show", false); } } } } ACTION_IMPL(status) { Helper helper(tid, &POST, &PARAMS); JSON::Object ret; std::string rights=helper.getRights("status"); std::vector clientids; IDatabase *db=helper.getDatabase(); if(rights!="all" && rights!="none" ) { std::vector s_clientid; Tokenize(rights, s_clientid, ","); for(size_t i=0;iid==SESSION_ID_INVALID) return; if(session!=NULL && (rights=="all" || !clientids.empty()) ) { if (rights == "all" && POST.find("stop_show") != POST.end()) { add_remove_stop_show(db, POST["stop_show"], true); } if (rights == "all" && POST.find("stop_show_version") != POST.end()) { set_stop_show_version(db, POST["stop_show_version"]); } if (rights == "all" && POST.find("reset_error") != POST.end()) { std::string reset_error = POST["reset_error"]; if (reset_error == "nospc_stalled") { ServerStatus::resetServerNospcStalled(); } else if (reset_error == "nospc_fatal") { ServerStatus::setServerNospcFatal(false); } else if (reset_error == "database_error") { Server->clearFailBit(IServer::FAIL_DATABASE_CORRUPTED); Server->clearFailBit(IServer::FAIL_DATABASE_IOERR); Server->clearFailBit(IServer::FAIL_DATABASE_FULL); } } if(rights=="all") { ServerSettings settings(db); access_dir_checks(db, settings, settings.getSettings()->backupfolder, settings.getSettings()->backupfolder_uncompr, ret); if(ServerStatus::getServerNospcStalled()>0) { ret.set("nospc_stalled" ,true); } if(ServerStatus::getServerNospcFatal()) { ret.set("nospc_fatal" ,true); } if( (Server->getFailBits() & IServer::FAIL_DATABASE_CORRUPTED) || (Server->getFailBits() & IServer::FAIL_DATABASE_IOERR) || (Server->getFailBits() & IServer::FAIL_DATABASE_FULL) ) { ret.set("database_error", true); } } std::string hostname=POST["hostname"]; if(!hostname.empty() && rights=="all") { if(POST["remove"]=="true") { IQuery *q=db->Prepare("DELETE FROM settings_db.extra_clients WHERE id=?"); q->Bind(hostname); q->Write(); q->Reset(); } else { IQuery *q=db->Prepare("INSERT INTO settings_db.extra_clients (hostname) SELECT ? AS hostname WHERE NOT EXISTS (SELECT hostname FROM settings_db.extra_clients WHERE hostname=?)"); q->Bind(hostname); q->Bind(hostname); q->Write(); q->Reset(); } } std::string s_remove_client=POST["remove_client"]; if(!s_remove_client.empty() && helper.getRights("remove_client")=="all") { std::vector remove_client; Tokenize(s_remove_client, remove_client, ","); if(POST.find("stop_remove_client")!=POST.end()) { for(size_t i=0;iPrepare("UPDATE clients SET delete_pending=0 WHERE id=?"); q->Bind(remove_client[i]); q->Write(); q->Reset(); } } else { for(size_t i=0;iPrepare("UPDATE clients SET delete_pending=1 WHERE id=? OR virtualmain = (SELECT name FROM clients WHERE id=?)"); q->Bind(remove_client[i]); q->Bind(remove_client[i]); q->Write(); q->Reset(); } } BackupServer::updateDeletePending(); } JSON::Array status; IDatabase *db=helper.getDatabase(); std::string filter; if(!clientids.empty()) { filter=" WHERE "; for(size_t i=0;iRead("SELECT c.id AS id, delete_pending, c.name AS name, strftime('"+helper.getTimeFormatString()+"', lastbackup) AS lastbackup, strftime('"+helper.getTimeFormatString()+"', lastseen) AS lastseen," "strftime('"+helper.getTimeFormatString()+"', lastbackup_image) AS lastbackup_image, last_filebackup_issues, os_simple, os_version_str, client_version_str, cg.name AS groupname FROM " " clients c LEFT OUTER JOIN settings_db.si_client_groups cg ON c.groupid = cg.id "+filter+" ORDER BY name"); double backup_ok_mod_file=3.; db_results res_t=db->Read("SELECT value FROM settings_db.settings WHERE key='backup_ok_mod_file' AND clientid=0"); if(res_t.size()>0) { backup_ok_mod_file=atof((res_t[0]["value"]).c_str()); } double backup_ok_mod_image=3.; res_t=db->Read("SELECT value FROM settings_db.settings WHERE key='backup_ok_mod_image' AND clientid=0"); if(res_t.size()>0) { backup_ok_mod_image=atof((res_t[0]["value"]).c_str()); } std::vector client_status=ServerStatus::getStatus(); for(size_t i=0;i lastseen) { lastseen = client_status[j].lastseen; } switch(client_status[j].status_error) { case se_ident_error: i_status=11; break; case se_too_many_clients: i_status=12; break; case se_authentication_error: i_status=13; break; default: if(!client_status[j].processes.empty()) { i_status = client_status[j].processes[0].action; } } for(size_t k=0;k=0 && (time_filebackup<0 || time_filebackup_fullPrepare("SELECT id FROM clients WHERE lastbackup IS NOT NULL AND datetime('now','-"+convert((int)(time_filebackup*backup_ok_mod_file+0.5))+" seconds')Bind(clientid); db_results res_file_ok=q->Read(); q->Reset(); stat.set("file_ok", !res_file_ok.empty()); int time_imagebackup=settings.getUpdateFreqImageIncr(); int time_imagebackup_full=settings.getUpdateFreqImageFull(); if( time_imagebackup_full>=0 && (time_imagebackup<0 || time_imagebackup_fullPrepare("SELECT id FROM clients WHERE lastbackup_image IS NOT NULL AND datetime('now','-"+convert((int)(time_imagebackup*backup_ok_mod_image+0.5))+" seconds')Bind(clientid); res_file_ok=q->Read(); q->Reset(); stat.set("image_ok", !res_file_ok.empty()); status.add(stat); } if(rights=="all") { bool has_ident_error_clients = false; for(size_t i=0;iRead("SELECT id, hostname, lastip FROM settings_db.extra_clients"); for(size_t i=0;ino_images); ret.set("no_file_backups", settings.getSettings()->no_file_backups); if(helper.getRights("all")=="all") { ret.set("admin", JSON::Value(true)); set_server_version_info(db, ret); } if(is_big_endian()) { ret.set("big_endian", true); } } else { ret.set("error", 1); } helper.Write(ret.stringify(false)); } #endif //CLIENT_ONLY