/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2008-2021, Tomasz Sowa * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * */ #include #include "mv.h" #include "functions.h" namespace Winix { namespace Fun { Mv::Mv() { fun.url = L"mv"; follow_symlinks = false; Prepare(); } // !! CHECK ME // check if everywhere correct messages are sent (prepare_to, modified item/dir) bool Mv::HasAccess() { if( cur->request->is_item ) { if( !system->CanRemoveRenameChild(*cur->request->dir_tab.back(), cur->request->item.item_content.user_id) ) return false; } else { bool only_content = (cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c")); if( !CheckAccessFromToDir(*cur->request->dir_tab.back(), only_content) ) return false; } return true; } bool Mv::CheckAccessFromToDir(const Item & dir, bool only_content) { if( dir.parent_id == -1 ) { if( !only_content ) { // the root directory cannot be moved anywhere return false; } } else if( only_content ) { // sticky bit for a specified child will be checked later if( !system->HasWriteAccess(dir) ) return false; } else { Item * last_but_one_dir = system->dirs.GetDir(dir.parent_id); if( !last_but_one_dir ) // ops, there is no a parent dir return false; if( !system->CanRemoveRenameChild(*last_but_one_dir, dir.item_content.user_id) ) return false; } return true; } // !! IMPROVE ME: may a better name? void Mv::Prepare() { // content_dir_iq.SetAll(false, false); // content_dir_iq.sel_parent_id = true; // content_dir_iq.sel_type = true; // content_dir_iq.sel_url = true; // content_dir_iq.sel_file = true; // content_dir_iq.sel_user_id = true; // content_dir_iq.sel_group_id = true; // content_dir_iq.sel_privileges = true; // content_dir_iq.sel_meta = true; // // files_iq.SetAll(false, false); // files_iq.sel_parent_id = true; // files_iq.sel_type = true; // files_iq.sel_url = true; // files_iq.sel_file = true; // files_iq.sel_user_id = true; // files_iq.sel_group_id = true; // files_iq.sel_privileges = true; // files_iq.sel_meta = true; // files_iq.WhereType(Item::dir, false); } void Mv::Clear() { out_dir_tab.clear(); out_item.Clear(); out_filename.clear(); files_item_tab.clear(); item_tab.clear(); } bool Mv::ParseDirCheckLastName() { if( out_has_file ) { log << log1 << "Mv: incorrent path" << logend; slog << logerror << T("mv_incorrect_path") << logend; return false; } else { Item * dir = system->dirs.GetDir(out_filename, out_dir_tab.back()->id); if( dir ) { out_dir_tab.push_back(dir); out_filename.clear(); } else { morm::Finder finder(model_connector); out_item = finder. select(). where(). eq(L"parent_id", out_dir_tab.back()->id). eq(L"url", out_filename). get(); //if( db->GetItem(out_dir_tab.back()->id, out_filename, out_item) == WINIX_ERR_OK ) if( out_item.found() ) { out_has_file = true; out_filename.clear(); } } } return true; } bool Mv::ParseDir(const std::wstring & dst_path, bool check_access) { if( dst_path.empty() ) return false; // first we should remove the last name from the dst_path // (it may not exist in current file system and FollowAllLinks will fail) size_t last_slash = dst_path.find_last_of('/'); out_path = dst_path; out_filename.clear(); if( last_slash != std::wstring::npos && last_slash + 1 < dst_path.size() ) { out_path.erase(last_slash + 1); // leaving the slash at the end out_filename = dst_path.c_str() + last_slash + 1; } int res = system->FollowAllLinks(cur->request->dir_tab, out_path, out_dir_tab, out_item, false, false, check_access); if( res != 0 && res != 1 ) { slog << logerror << T("mv_incorrect_dst_path") << logend; return false; } out_has_file = (res == 1); if( !out_filename.empty() ) return ParseDirCheckLastName(); return true; } bool Mv::CanRemoveRenameChild(const Item & child) { Item * parent_dir = system->dirs.GetDir(child.parent_id); if( !parent_dir || !system->CanRemoveRenameChild(*parent_dir, child.item_content.user_id) ) { log << log1 << "Mv: permission denied to: " << child.url << logend; slog << logerror << T("mv_permission_denied_to") << ": " << child.url << logend; return false; } return true; } bool Mv::MoveStaticFile(const std::wstring & from, const std::wstring & to) { if( from == to ) { log << log3 << "Mv: the same path to a static file: " << to << " (skipped)" << logend; return true; } if( RenameFile(from, to) ) { log << log2 << "Mv: moved static file from: " << from << ", to: " << to << logend; return true; } else { log << log1 << "Mv: cannot move a static file from: " << from << ", to: " << to << logend; slog << logerror << T("internal_error") << logend; return false; } } void Mv::MoveStaticFile(Item & item) { bool ok = true; //bool res1, res2, res3, res4, res5; ok = ok && system->MakeFilePath(item, old_static_path, false); ok = ok && (!item.item_content.file_has_thumb || system->MakeFilePath(item, old_static_thumb_path, true)); ok = ok && system->CreateNewFile(item); ok = ok && system->MakeFilePath(item, new_static_path, false, true, config->upload_dirs_chmod); ok = ok && (!item.item_content.file_has_thumb || system->MakeFilePath(item, new_static_thumb_path, true, true, config->upload_dirs_chmod)); if( !ok ) { log << log1 << "Mv: cannot create a static path" << logend; slog << logerror << T("internal_error") << logend; return; } if( MoveStaticFile(old_static_path, new_static_path) ) { item.propagate_connector(); //if( db->EditFileById(item, item.id) != WINIX_ERR_OK ) if( !item.item_content.update() ) { log << log1 << "Mv: cannot move static file (database problem)" << logend; slog << logerror << T("internal_error") << logend; return; } if( item.item_content.file_has_thumb ) MoveStaticFile(old_static_thumb_path, new_static_thumb_path); } } void Mv::MoveFilesPrepareTreeGo(const Item & src_dir) { DirContainer::ParentIterator i = system->dirs.FindFirstChild(src_dir.id); // go through all directories for( ; i != system->dirs.ParentEnd() ; i = system->dirs.NextChild(i) ) MoveFilesPrepareTreeGo(*(i->second)); //files_iq.WhereParentId(src_dir.id); //db->GetItems(files_item_tab, files_iq); morm::Finder finder(model_connector); finder. select(). where(). eq(L"parent_id", src_dir.id). neq(L"type", static_cast(Item::dir)). get_vector(files_item_tab); for(size_t i=0 ; iCall(WINIX_FILE_PREPARE_TO_MOVE, &files_item_tab[i]); } void Mv::MoveFilesPrepareTree(const Item & src_dir) { // we only calling plugins here // so if there is no WINIX_FILE_PREPARE_TO_MOVE message // we can immediately return and the database will not be bothered if( plugin->HasMessage(WINIX_FILE_PREPARE_TO_MOVE) ) { MoveFilesPrepareTreeGo(src_dir); } } void Mv::MoveFilesTree(const Item & dir) { DirContainer::ParentIterator i = system->dirs.FindFirstChild(dir.id); // go through all directories for( ; i != system->dirs.ParentEnd() ; i = system->dirs.NextChild(i) ) MoveFilesTree(*(i->second)); morm::Finder finder(model_connector); finder. select(). where(). eq(L"parent_id", dir.id). neq(L"type", static_cast(Item::dir)). get_vector(files_item_tab); //files_iq.WhereParentId(dir.id); //db->GetItems(files_item_tab, files_iq); for(size_t i=0 ; iCall(WINIX_FILE_PREPARE_TO_MOVE, &files_item_tab[i]); MoveStaticFile(files_item_tab[i]); plugin->Call(WINIX_FILE_MOVED, &files_item_tab[i]); } } } // private // uses: out_dir_tab, out_filename bool Mv::MoveDir(Item & src_dir, std::vector & dst_dir_tab, const std::wstring & dst_name) { long dst_dir_id = dst_dir_tab.back()->id; old_url = src_dir.url; if( dst_dir_id == src_dir.id || system->dirs.HasParent(dst_dir_id, src_dir.id) ) { log << log1 << "Mv: cannot move directory to inside it" << logend; slog << logerror << T("mv_cannot_move_to_inside"); return false; } MoveFilesPrepareTree(src_dir); if( !system->dirs.ChangeParent(src_dir.id, dst_dir_id) ) return false; src_dir.parent_id = dst_dir_id; old_url = src_dir.url; if( !dst_name.empty() ) { src_dir.url = dst_name; functions->PrepareUrl(src_dir); } //Error status = db->EditParentUrlById(src_dir, src_dir.id); if( src_dir.update(false) ) { log << log3 << "Mv: directory: " << old_url << " was moved to: "; system->dirs.LogDir(dst_dir_tab); log << src_dir.url << logend; MoveFilesTree(src_dir); return true; } return false; } // public bool Mv::MoveDir(Item & src_dir, long dst_dir_id, const std::wstring & new_url, bool check_access) { bool res = MoveDir2(src_dir, dst_dir_id, new_url, check_access); Clear(); return res; } // private bool Mv::MoveDir2(Item & src_dir, long dst_dir_id, const std::wstring & new_url, bool check_access) { if( src_dir.type != Item::dir ) { log << "Mv: a directory required" << logend; return false; } if( src_dir.parent_id == -1 ) { log << log1 << "Mv: the root directory cannot be moved anywhere" << logend; slog << logerror << T("mv_cant_move_root_dir") << logend; return false; } if( check_access && !CanRemoveRenameChild(src_dir) ) return false; if( src_dir.id == dst_dir_id ) { if( new_url.empty() || src_dir.url == new_url ) return true; // the same directory -- there is nothing to do } if( !system->dirs.CreateDirTab(dst_dir_id, out_dir_tab) ) { log << log1 << "Mv: incorrect directory" << logend; slog << logerror << T("mv_incorrect_dir") << logend; return false; } return MoveDir(src_dir, out_dir_tab, new_url); } // public bool Mv::MoveDir(Item & src_dir, const std::wstring & dst_path, bool check_access) { bool res = MoveDir2(src_dir, dst_path, check_access); Clear(); return res; } // private bool Mv::MoveDir2(Item & src_dir, const std::wstring & dst_path, bool check_access) { if( src_dir.type != Item::dir ) { log << "Mv: a directory required" << logend; return false; } if( src_dir.parent_id == -1 ) { log << log1 << "Mv: the root directory cannot be moved anywhere" << logend; slog << logerror << T("mv_cant_move_root_dir") << logend; return false; } if( check_access && !CanRemoveRenameChild(src_dir) ) return false; if( !ParseDir(dst_path, check_access) ) return false; if( out_has_file ) { log << log1 << "Mv: directory can be moved only to a directory" << logend; slog << logerror << T("mv_dir_can_be_moved_to_dir") << logend; return false; } if( src_dir.id == out_dir_tab.back()->id ) { if( out_filename.empty() || src_dir.url == out_filename ) return true; // the same directory -- there is nothing to do } return MoveDir(src_dir, out_dir_tab, out_filename); } // private bool Mv::MoveFileOrSymlink(Item & src_file, std::vector & dst_dir_tab, const std::wstring & new_url) { plugin->Call(WINIX_FILE_PREPARE_TO_MOVE, &src_file); old_url = src_file.url; if( !new_url.empty() ) { src_file.url = new_url; functions->PrepareUrl(src_file); } src_file.parent_id = dst_dir_tab.back()->id; //Error status = db->EditParentUrlById(src_file, src_file.id); if( src_file.update(false) ) { if( src_file.type == Item::file ) log << log3 << "Mv: file: "; else log << log3 << "Mv: symlink: "; log << old_url << " was moved to: "; system->dirs.LogDir(dst_dir_tab); log << src_file.url << logend; if( src_file.item_content.file_type != WINIX_ITEM_FILETYPE_NONE ) MoveStaticFile(src_file); plugin->Call(WINIX_FILE_MOVED, &src_file); return true; } return false; } // public bool Mv::MoveFileOrSymlink(Item & src_file, long dst_dir_id, const std::wstring & new_url, bool check_access) { bool res = MoveFileOrSymlink2(src_file, dst_dir_id, new_url, check_access); Clear(); return res; } // private bool Mv::MoveFileOrSymlink2(Item & src_file, long dst_dir_id, const std::wstring & new_url, bool check_access) { if( src_file.type == Item::dir ) { log << "Mv: a file/symlink required" << logend; return false; } if( check_access && !CanRemoveRenameChild(src_file) ) return false; if( src_file.parent_id == dst_dir_id ) { if( new_url.empty() || src_file.url == new_url ) return true; // the same file -- there is nothing to do } if( !system->dirs.CreateDirTab(dst_dir_id, out_dir_tab) ) return false; return MoveFileOrSymlink(src_file, out_dir_tab, new_url); } // public bool Mv::MoveFileOrSymlink(Item & src_file, const std::wstring & dst_path, bool check_access) { bool res = MoveFileOrSymlink2(src_file, dst_path, check_access); Clear(); return res; } // private bool Mv::MoveFileOrSymlink2(Item & src_file, const std::wstring & dst_path, bool check_access) { if( src_file.type == Item::dir ) { log << "Mv: a file/symlink required" << logend; return false; } if( check_access && !CanRemoveRenameChild(src_file) ) return false; if( !ParseDir(dst_path, check_access) ) return false; if( out_has_file && src_file.parent_id == out_dir_tab.back()->id && src_file.url == out_item.url ) { return true; // the same file -- there is nothing to do } if( out_has_file ) { log << log3 << "Mv: such file already exists (skipping)" << logend; slog << logerror << T("mv_file_exists") << logend; return false; } return MoveFileOrSymlink(src_file, out_dir_tab, out_filename); } // private void Mv::MoveAllFilesFromDir(Item & src_dir, std::vector & dst_dir_tab, bool check_access) { // content_dir_iq.WhereParentId(src_dir.id); // db->GetItems(item_tab, content_dir_iq); morm::Finder finder(model_connector); finder. select(). where(). eq(L"parent_id", src_dir.id). get_vector(item_tab); out_filename.clear(); for(size_t i=0 ; iCanRemoveRenameChild(src_dir, item_tab[i].item_content.user_id) ) { log << log1 << "Mv: permission denied to: " << src_dir.url << logend; slog << logerror << T("mv_permission_denied_to") << ": " << src_dir.url << logend; } else { if( item_tab[i].type == Item::dir ) MoveDir(item_tab[i], dst_dir_tab, out_filename); else MoveFileOrSymlink(item_tab[i], dst_dir_tab, out_filename); } } } // public void Mv::MoveDirContent(Item & src_dir, long dst_dir_id, bool check_access) { MoveDirContent2(src_dir, dst_dir_id, check_access); Clear(); } // private void Mv::MoveDirContent2(Item & src_dir, long dst_dir_id, bool check_access) { if( src_dir.type != Item::dir ) { log << "Mv: a directory required" << logend; return; } if( src_dir.parent_id == -1 ) { log << log1 << "Mv: the root directory cannot be moved anywhere" << logend; slog << logerror << T("mv_cant_move_root_dir") << logend; return; } if( !system->dirs.CreateDirTab(dst_dir_id, out_dir_tab) ) { log << log1 << "Mv: incorrect directory" << logend; slog << logerror << T("mv_incorrect_dir") << logend; return; } MoveAllFilesFromDir(src_dir, out_dir_tab, check_access); } // public void Mv::MoveDirContent(Item & src_dir, const std::wstring & dst_dir, bool check_access) { MoveDirContent2(src_dir, dst_dir, check_access); Clear(); } // private void Mv::MoveDirContent2(Item & src_dir, const std::wstring & dst_dir, bool check_access) { if( !ParseDir(dst_dir, check_access) ) return; if( out_has_file || !out_filename.empty() ) { log << log1 << "Mv: directory content can be moved only to a directory" << logend; slog << logerror << T("mv_dir_content_can_be_moved_to_dir") << logend; return; } MoveDirContent2(src_dir, out_dir_tab.back()->id, check_access); } void Mv::MakePost() { const std::wstring & dst_path = cur->request->PostVar(L"dst_path"); bool ok = true; if( cur->request->is_item ) { ok = MoveFileOrSymlink(cur->request->item, dst_path, true); } else { if( cur->request->IsParam(L"c") || cur->request->IsPostVar(L"c") ) MoveDirContent(*cur->request->dir_tab.back(), dst_path, true); else ok = MoveDir(*cur->request->dir_tab.back(), dst_path, true); } if( ok ) system->RedirectToLastItem(); } } // namespace } // namespace Winix