/************************************************************************* * UrBackup - Client/Server backup system * Copyright (C) 2011 Martin Raiber * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **************************************************************************/ #include "clientdao.h" #include "../stringtools.h" #include "../Interface/Server.h" #include /** * @-SQLGenTempSetup * @sql * CREATE TEMPORARY TABLE filehashes_tmp (name TEXT, filesize INTEGER, modifytime INTEGER, hashdata BLOB) */ ClientDAO::ClientDAO(IDatabase *pDB) { db=pDB; prepareQueries(); prepareQueriesGen(); } void ClientDAO::prepareQueries(void) { q_get_files=db->Prepare("SELECT data,num FROM files WHERE name=?", false); q_add_files=db->Prepare("INSERT INTO files_tmp (name, num, data) VALUES (?,?,?)", false); q_get_dirs=db->Prepare("SELECT name, path, id FROM backupdirs", false); q_remove_all=db->Prepare("DELETE FROM files", false); q_get_changed_dirs=db->Prepare("SELECT id, name FROM mdirs UNION SELECT id, name FROM mdirs_backup", false); q_remove_changed_dirs=db->Prepare("DELETE FROM mdirs", false); q_modify_files=db->Prepare("UPDATE files SET data=?, num=? WHERE name=?", false); q_has_files=db->Prepare("SELECT count(*) AS num FROM files WHERE name=?", false); q_insert_shadowcopy=db->Prepare("INSERT INTO shadowcopies (vssid, ssetid, target, path, tname, orig_target, filesrv, vol, starttime, refs, starttoken) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP, ?, ?)", false); q_get_shadowcopies=db->Prepare("SELECT id, vssid, ssetid, target, path, tname, orig_target, filesrv, vol, (strftime('%s','now') - strftime('%s', starttime)) AS passedtime, refs, starttoken FROM shadowcopies", false); q_remove_shadowcopies=db->Prepare("DELETE FROM shadowcopies WHERE id=?", false); q_save_changed_dirs=db->Prepare("INSERT OR REPLACE INTO mdirs_backup SELECT id,name FROM mdirs", false); q_delete_saved_changed_dirs=db->Prepare("DELETE FROM mdirs_backup", false); q_copy_from_tmp_files=db->Prepare("INSERT INTO files (num, data, name) SELECT num, data, name FROM files_tmp", false); q_delete_tmp_files=db->Prepare("DELETE FROM files_tmp", false); q_has_changed_gap=db->Prepare("SELECT name FROM mdirs WHERE name GLOB '##-GAP-##*'", false); q_get_del_dirs=db->Prepare("SELECT name FROM del_dirs UNION SELECT name FROM del_dirs_backup", false); q_del_del_dirs=db->Prepare("DELETE FROM del_dirs", false); q_copy_del_dirs=db->Prepare("INSERT INTO del_dirs_backup SELECT name FROM del_dirs", false); q_del_del_dirs_copy=db->Prepare("DELETE FROM del_dirs_backup", false); q_remove_del_dir=db->Prepare("DELETE FROM files WHERE name GLOB ?", false); q_get_shadowcopy_refcount=db->Prepare("SELECT refs FROM shadowcopies WHERE id=?", false); q_set_shadowcopy_refcount=db->Prepare("UPDATE shadowcopies SET refs=? WHERE id=?", false); q_save_changed_files=db->Prepare("INSERT OR REPLACE INTO mfiles_backup SELECT dir_id,name FROM mfiles", false); q_remove_changed_files=db->Prepare("DELETE FROM mfiles", false); q_delete_saved_changed_files=db->Prepare("DELETE FROM mfiles_backup", false); q_has_changed_file=db->Prepare("SELECT dir_id FROM mfiles_backup WHERE dir_id=? AND name=? UNION SELECT dir_id FROM mfiles WHERE dir_id=? AND name=?", false); q_get_changed_files=db->Prepare("SELECT name FROM mfiles_backup WHERE dir_id=? UNION SELECT name FROM mfiles WHERE dir_id=?", false); q_get_pattern=db->Prepare("SELECT tvalue FROM misc WHERE tkey=?", false); q_insert_pattern=db->Prepare("INSERT INTO misc (tkey, tvalue) VALUES (?, ?)", false); q_update_pattern=db->Prepare("UPDATE misc SET tvalue=? WHERE tkey=?", false); q_get_file_hash=db->Prepare("SELECT hashdata, filesize, modifytime FROM filehashes WHERE name=?", false); } void ClientDAO::destroyQueries(void) { db->destroyQuery(q_get_files); db->destroyQuery(q_add_files); db->destroyQuery(q_get_dirs); db->destroyQuery(q_remove_all); db->destroyQuery(q_get_changed_dirs); db->destroyQuery(q_remove_changed_dirs); db->destroyQuery(q_modify_files); db->destroyQuery(q_has_files); db->destroyQuery(q_insert_shadowcopy); db->destroyQuery(q_get_shadowcopies); db->destroyQuery(q_remove_shadowcopies); db->destroyQuery(q_save_changed_dirs); db->destroyQuery(q_delete_saved_changed_dirs); db->destroyQuery(q_copy_from_tmp_files); db->destroyQuery(q_delete_tmp_files); db->destroyQuery(q_has_changed_gap); db->destroyQuery(q_get_del_dirs); db->destroyQuery(q_del_del_dirs); db->destroyQuery(q_copy_del_dirs); db->destroyQuery(q_del_del_dirs_copy); db->destroyQuery(q_remove_del_dir); db->destroyQuery(q_get_shadowcopy_refcount); db->destroyQuery(q_set_shadowcopy_refcount); db->destroyQuery(q_save_changed_files); db->destroyQuery(q_remove_changed_files); db->destroyQuery(q_delete_saved_changed_files); db->destroyQuery(q_has_changed_file); db->destroyQuery(q_get_changed_files); db->destroyQuery(q_get_pattern); db->destroyQuery(q_insert_pattern); db->destroyQuery(q_update_pattern); db->destroyQuery(q_get_file_hash); } //@-SQLGenSetup void ClientDAO::prepareQueriesGen(void) { q_modifyFileHash=db->Prepare("UPDATE filehashes SET hashdata=?, filesize=?, modifytime=? WHERE name=?", false); q_addFileHash=db->Prepare("INSERT INTO filehashes_tmp (name, filesize, modifytime, hashdata) VALUES (?, ?, ?, ?)", false); q_copyFromTmpFileHashes=db->Prepare("INSERT INTO filehashes (name, filesize, modifytime, hashdata) SELECT name, filesize, modifytime, hashdata FROM filehashes_tmp", false); q_deleteTmpFileHashes=db->Prepare("DELETE FROM filehashes_tmp", false); } //@-SQLGenDestruction void ClientDAO::destroyQueriesGen(void) { db->destroyQuery(q_modifyFileHash); db->destroyQuery(q_addFileHash); db->destroyQuery(q_copyFromTmpFileHashes); db->destroyQuery(q_deleteTmpFileHashes); } void ClientDAO::restartQueries(void) { destroyQueries(); destroyQueriesGen(); prepareQueries(); prepareQueriesGen(); } bool ClientDAO::getFiles(std::wstring path, std::vector &data) { q_get_files->Bind(path); db_results res=q_get_files->Read(); q_get_files->Reset(); if(res.size()==0) return false; std::wstring &qdata=res[0][L"data"]; if(qdata.empty()) return true; int num=watoi(res[0][L"num"]); char *ptr=(char*)&qdata[0]; while(ptr-(char*)&qdata[0]ConvertToUnicode(tmp); ptr+=ss; memcpy(&f.size, ptr, sizeof(int64)); ptr+=sizeof(int64); memcpy(&f.last_modified, ptr, sizeof(int64)); ptr+=sizeof(int64); char isdir=*ptr; ++ptr; if(isdir==0) f.isdir=false; else f.isdir=true; data.push_back(f); } return true; } char * constructData(const std::vector &data, size_t &datasize) { datasize=0; std::vector utf; for(size_t i=0;iConvertToUTF8(data[i].name); datasize+=us.size(); datasize+=sizeof(unsigned short); datasize+=sizeof(int64)*2; ++datasize; utf.push_back(us); } char *buffer=new char[datasize]; char *ptr=buffer; for(size_t i=0;i &data) { size_t ds; char *buffer=constructData(data, ds); q_add_files->Bind(path); q_add_files->Bind(ds); q_add_files->Bind(buffer, (_u32)ds); q_add_files->Write(); q_add_files->Reset(); delete []buffer; } void ClientDAO::modifyFiles(std::wstring path, const std::vector &data) { size_t ds; char *buffer=constructData(data, ds); q_modify_files->Bind(buffer, (_u32)ds); q_modify_files->Bind(ds); q_modify_files->Bind(path); q_modify_files->Write(); q_modify_files->Reset(); delete []buffer; } bool ClientDAO::hasFiles(std::wstring path) { q_has_files->Bind(path); db_results res=q_has_files->Read(); q_has_files->Reset(); if(res.size()>0) return res[0][L"num"]==L"1"; else return false; } std::vector ClientDAO::getBackupDirs(void) { db_results res=q_get_dirs->Read(); q_get_dirs->Reset(); std::vector ret; for(size_t i=0;iWrite(); } std::vector ClientDAO::getChangedDirs(bool del) { std::vector ret; db->BeginTransaction(); if(del) { q_save_changed_dirs->Write(); q_save_changed_dirs->Reset(); q_remove_changed_dirs->Write(); q_remove_changed_dirs->Reset(); } db_results res=q_get_changed_dirs->Read(); q_get_changed_dirs->Reset(); db->EndTransaction(); for(size_t i=0;iBeginTransaction(); q_save_changed_files->Write(); q_save_changed_files->Reset(); q_remove_changed_files->Write(); q_remove_changed_files->Reset(); db->EndTransaction(); } } std::vector ClientDAO::getShadowcopies(void) { db_results res=q_get_shadowcopies->Read(); q_get_shadowcopies->Reset(); std::vector ret; for(size_t i=0;iBind((char*)&sc.vssid, sizeof(GUID) ); q_insert_shadowcopy->Bind((char*)&sc.ssetid, sizeof(GUID) ); q_insert_shadowcopy->Bind(sc.target); q_insert_shadowcopy->Bind(sc.path); q_insert_shadowcopy->Bind(sc.tname); q_insert_shadowcopy->Bind(sc.orig_target); q_insert_shadowcopy->Bind(sc.filesrv?1:0); q_insert_shadowcopy->Bind(sc.vol); q_insert_shadowcopy->Bind(sc.refs); q_insert_shadowcopy->Bind(sc.starttoken); q_insert_shadowcopy->Write(); q_insert_shadowcopy->Reset(); return (int)db->getLastInsertID(); } int ClientDAO::modShadowcopyRefCount(int id, int m) { q_get_shadowcopy_refcount->Bind(id); db_results res=q_get_shadowcopy_refcount->Read(); q_get_shadowcopy_refcount->Reset(); if(!res.empty()) { int refs=watoi(res[0][L"refs"]); refs+=m; q_set_shadowcopy_refcount->Bind(refs); q_set_shadowcopy_refcount->Bind(id); q_set_shadowcopy_refcount->Write(); q_set_shadowcopy_refcount->Reset(); return refs; } return -1; } void ClientDAO::deleteShadowcopy(int id) { q_remove_shadowcopies->Bind(id); q_remove_shadowcopies->Write(); q_remove_shadowcopies->Reset(); } void ClientDAO::deleteSavedChangedDirs(void) { q_delete_saved_changed_dirs->Write(); q_delete_saved_changed_dirs->Reset(); } void ClientDAO::deleteSavedChangedFiles(void) { q_delete_saved_changed_files->Write(); q_delete_saved_changed_files->Reset(); } void ClientDAO::copyFromTmpFiles(void) { q_copy_from_tmp_files->Write(); q_copy_from_tmp_files->Reset(); q_delete_tmp_files->Write(); q_delete_tmp_files->Reset(); } bool ClientDAO::hasChangedGap(void) { db_results res=q_has_changed_gap->Read(); q_has_changed_gap->Reset(); return !res.empty(); } void ClientDAO::deleteChangedDirs(void) { q_remove_changed_dirs->Write(); q_remove_changed_dirs->Reset(); } std::vector ClientDAO::getGapDirs(void) { db_results res=q_has_changed_gap->Read(); q_has_changed_gap->Reset(); std::vector ret; for(size_t i=0;i ClientDAO::getDelDirs(bool del) { std::vector ret; db->BeginTransaction(); if(del) { q_copy_del_dirs->Write(); q_copy_del_dirs->Reset(); q_del_del_dirs->Write(); q_del_del_dirs->Reset(); } db_results res=q_get_del_dirs->Read(); q_get_del_dirs->Reset(); db->EndTransaction(); for(size_t i=0;iWrite(); q_del_del_dirs_copy->Reset(); } void ClientDAO::removeDeletedDir(const std::wstring &dir) { q_remove_del_dir->Bind(dir+L"*"); q_remove_del_dir->Write(); q_remove_del_dir->Reset(); } bool ClientDAO::hasFileChange(_i64 dir_id, std::wstring fn) { q_has_changed_file->Bind(dir_id); q_has_changed_file->Bind(fn); q_has_changed_file->Bind(dir_id); q_has_changed_file->Bind(fn); db_results res=q_has_changed_file->Read(); q_has_changed_file->Reset(); return !res.empty(); } std::vector ClientDAO::getChangedFiles(_i64 dir_id) { q_get_changed_files->Bind(dir_id); q_get_changed_files->Bind(dir_id); db_results res=q_get_changed_files->Read(); q_get_changed_files->Reset(); std::vector ret; ret.resize(res.size()); for(size_t i=0;iBind(key); db_results res=q_get_pattern->Read(); q_get_pattern->Reset(); if(!res.empty()) { return res[0][L"tvalue"]; } else { return L""; } } void ClientDAO::updateMiscValue(const std::string& key, const std::wstring& value) { q_get_pattern->Bind(exclude_pattern_key); db_results res=q_get_pattern->Read(); q_get_pattern->Reset(); if(!res.empty()) { q_update_pattern->Bind(value); q_update_pattern->Bind(key); q_update_pattern->Write(); q_update_pattern->Reset(); } else { q_insert_pattern->Bind(key); q_insert_pattern->Bind(value); q_insert_pattern->Write(); q_insert_pattern->Reset(); } } bool ClientDAO::getFileHash(const std::wstring& path, _i64& filesize, _i64& modifytime, std::string& hash) { q_get_file_hash->Bind(path); db_results res=q_get_file_hash->Read(); q_get_file_hash->Reset(); if(!res.empty()) { std::wstring &hdata=res[0][L"hashdata"]; hash.resize(hdata.size()*sizeof(wchar_t)); memcpy(const_cast(hash.c_str()), hdata.c_str(), hdata.size()*sizeof(wchar_t)); filesize=watoi64(res[0][L"filesize"]); modifytime=watoi64(res[0][L"modifytime"]); return true; } return false; } /** * @-SQLGenAccess * @func void ClientDAO::modifyFileHash * @sql * UPDATE filehashes SET hashdata=:hash(blob), filesize=:filesize(int64), modifytime=:modifytime(int64) WHERE name=:path(string) */ void ClientDAO::modifyFileHash(const std::string& hash, int64 filesize, int64 modifytime, const std::wstring& path) { q_modifyFileHash->Bind(hash.c_str(), (_u32)hash.size()); q_modifyFileHash->Bind(filesize); q_modifyFileHash->Bind(modifytime); q_modifyFileHash->Bind(path); q_modifyFileHash->Reset(); } /** * @-SQLGenAccess * @func void ClientDAO::addFileHash * @sql * INSERT INTO filehashes_tmp (name, filesize, modifytime, hashdata) VALUES (:name(string), :filesize(int64), :modifytime(int64), :hashdata(blob)) */ void ClientDAO::addFileHash(const std::wstring& name, int64 filesize, int64 modifytime, const std::string& hashdata) { q_addFileHash->Bind(name); q_addFileHash->Bind(filesize); q_addFileHash->Bind(modifytime); q_addFileHash->Bind(hashdata.c_str(), (_u32)hashdata.size()); q_addFileHash->Write(); q_addFileHash->Reset(); } /** * @-SQLGenAccess * @func void ClientDAO::copyFromTmpFileHashes * @sql * INSERT INTO filehashes (name, filesize, modifytime, hashdata) SELECT name, filesize, modifytime, hashdata FROM filehashes_tmp */ void ClientDAO::copyFromTmpFileHashes(void) { q_copyFromTmpFileHashes->Write(); } /** * @-SQLGenAccess * @func void ClientDAO::deleteTmpFileHashes * @sql * DELETE FROM filehashes_tmp */ void ClientDAO::deleteTmpFileHashes(void) { q_deleteTmpFileHashes->Write(); }