/* * 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 "dirs.h" #include "error.h" #include "notify/notify.h" namespace Winix { void Dirs::SetDb(Db * pdb) { db = pdb; } void Dirs::SetCur(Cur * pcur) { cur = pcur; } void Dirs::SetNotify(Notify * pnotify) { notify = pnotify; } void Dirs::set_dependency(WinixModelDeprecated * winix_model) { WinixModelDeprecated::set_dependency(winix_model); dir_tab.set_dependency(winix_model); } void Dirs::Clear() { dir_tab.Clear(); } bool Dirs::HasReadExecAccessForRoot(const Item & item) { // there must be at least one 'x' (for the root) return (item.item_content.privileges & 01111) != 0; } void Dirs::CheckRootDir() { DirContainer::Iterator i = dir_tab.GetRoot(); if( i != dir_tab.End() ) { if( !HasReadExecAccessForRoot(*i) ) { i->item_content.privileges = 07555; log << log1 << "Dirs: there is no access for a root (admin) to the root dir, setting 07555 for the root directory" << logend; i->item_content.set_connector(model_connector); i->item_content.date_modification = std::time(nullptr); i->item_content.save(false); } return; } log << log1 << "Dirs: there is no a root directory in the database (creating one)" << logend; ItemModelData item_data; item_data.prepare_unique_url = false; Item root; root.set_connector(model_connector); root.type = Item::dir; root.parent_id = -1; root.item_content.user_id = -1; root.item_content.group_id = -1; root.item_content.privileges = 07555; root.item_content.date_creation = std::time(nullptr); root.item_content.date_modification = root.item_content.date_creation; if( root.insert(item_data) ) { dir_tab.PushBack(root); } } void Dirs::ReadDirs() { Clear(); //db->GetDirs(dir_tab); morm::Finder finder(model_connector); std::list all_dirs = finder.select().where().eq(L"type", static_cast(Item::Type::dir)).get_list(); for(Item & item : all_dirs) { dir_tab.PushBack(item); } CheckRootDir(); dir_tab.FindSpecialFolders(); } bool Dirs::ExtractName(const wchar_t * & s, std::wstring & name) { name.clear(); // skipping first slashes (can be more than one) for( ; *s == '/' ; ++s); for( ; *s != 0 && *s != '/' ; ++s) name += *s; return !name.empty(); } bool Dirs::IsDir(long id) { DirContainer::Iterator i = dir_tab.FindId(id); if( i == dir_tab.End() ) return false; return true; } // !! dac clearowanie childs_tab // !! ewentualnie mozna dodac trzeci domyslny parametr bool clear_tab = true bool Dirs::GetDirChilds(long parent, std::vector & childs_tab) { if( parent != -1 && !IsDir(parent) ) return false; DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent); for( ; i != dir_tab.ParentEnd() ; i = dir_tab.NextChild(i) ) childs_tab.push_back( &(*i->second) ); return true; } DirContainer::ParentIterator Dirs::FindFirstChild(long parent_id) { DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent_id); return i; } DirContainer::ParentIterator Dirs::NextChild(DirContainer::ParentIterator i) { return dir_tab.NextChild(i); } DirContainer::ParentIterator Dirs::ParentEnd() { return dir_tab.ParentEnd(); } // !! dodatkowo moze metoda AppendPath dodajaca sciezke do biezacego stringa? // albo tutaj stringa nie czyscic? // O(m * log n) (m- how many parts are in 'id') // path with a slash at the end and at the beginning bool Dirs::MakePath(long id, std::wstring & path, bool clear_path) { DirContainer::Iterator i; if( clear_path ) path.clear(); temp_path = '/'; while( true ) { i = dir_tab.FindId(id); if( i == dir_tab.End() || i->parent_id == id ) // means a loop (something wrong in the db) { // we don't change path if there is no such a directory return false; } if( i->parent_id == -1 ) { path += temp_path; return true; } id = i->parent_id; temp_path.insert(0, i->url); temp_path.insert(temp_path.begin(), '/'); } } void Dirs::MakePath(const std::vector dir_tab, std::wstring & path, bool clear_path) { if( clear_path ) path.clear(); for(size_t i=0 ; iurl; path += '/'; } } size_t Dirs::DirLevel(long id) { DirContainer::Iterator i; size_t level = 0; while( true ) { i = dir_tab.FindId(id); if( i == dir_tab.End() || i->parent_id == id ) // means a loop (something wrong in the db) { return level; } if( i->parent_id == -1 ) return level; id = i->parent_id; level += 1; } } bool Dirs::IsChild(long parent_id, long child_id) { if( child_id == parent_id ) return false; DirContainer::Iterator i; while( child_id != -1 ) { i = dir_tab.FindId(child_id); if( i == dir_tab.End() ) return false; if( i->parent_id == parent_id ) return true; child_id = i->parent_id; } return false; } bool Dirs::ChangeParent(long dir_id, long new_parent_id) { return dir_tab.ChangeParent(dir_id, new_parent_id); } /* checking whether dir_id has a parent parent_id (somewhere in the path) */ bool Dirs::HasParent(long dir_id, long parent_id) { DirContainer::Iterator i; while( true ) { i = dir_tab.FindId(dir_id); if( i==dir_tab.End() || i->parent_id==-1 ) return false; if( i->parent_id == parent_id ) return true; dir_id = i->parent_id; } } bool Dirs::CreateDirTab(long dir_id, std::vector & out_dir_tab) { DirContainer::Iterator i; out_dir_tab.clear(); do { i = dir_tab.FindId(dir_id); if( i == dir_tab.End() ) return false; if( out_dir_tab.empty() ) out_dir_tab.insert(out_dir_tab.end(), &(*i)); // !! I am not sure whether begin() can be used on an empty container else out_dir_tab.insert(out_dir_tab.begin(), &(*i)); dir_id = i->parent_id; } while( dir_id != -1 ); return true; } Item * Dirs::GetRootDir() { DirContainer::Iterator root = dir_tab.GetRoot(); if( root == dir_tab.End() ) return 0; return &(*root); } Item * Dirs::GetEtcDir() { DirContainer::Iterator etc = dir_tab.GetEtc(); if( etc == dir_tab.End() ) return 0; return &(*etc); } Item * Dirs::GetVarDir() { DirContainer::Iterator etc = dir_tab.GetVar(); if( etc == dir_tab.End() ) return 0; return &(*etc); } Item * Dirs::GetDir(const wchar_t * name, long parent) { DirContainer::ParentIterator i = dir_tab.FindFirstChild(parent); for( ; i!=dir_tab.ParentEnd() ; i = dir_tab.NextChild(i) ) if( i->second->url == name ) return &(*i->second); return 0; } Item * Dirs::GetDir(const std::wstring & name, long parent) { return GetDir(name.c_str(), parent); } Item * Dirs::GetDir(const wchar_t * path) { if( *path == 0 ) return 0; DirContainer::Iterator root = dir_tab.GetRoot(); if( root == dir_tab.End() ) // ops, we do not have a root dir return 0; Item * pitem = &(*root); const wchar_t * s = path; while( ExtractName(s, get_dir_temp) ) { pitem = GetDir(get_dir_temp, pitem->id); if( !pitem ) return 0; } return pitem; } Item * Dirs::GetDir(const std::wstring & path) { return GetDir(path.c_str()); } Item * Dirs::GetDir(long id) { DirContainer::Iterator i = dir_tab.FindId(id); if( i == dir_tab.End() ) return nullptr; return &(*i); } const Item * Dirs::GetDir(long id) const { DirContainer::ConstIterator i = dir_tab.FindId(id); if( i == dir_tab.End() ) return nullptr; return &(*i); } Item * Dirs::AddDir(const Item & item) { return &(*dir_tab.PushBack(item)); } size_t Dirs::AnalyzeDir(Item * pdir, const std::wstring & path, long & dir_id, std::wstring & dir) { size_t i = 0; size_t old_i; while( true ) { dir_id = pdir->id; // skipping slashes for( ; iid); if( !pdir ) return old_i; // analyze_temp is not a directory dir += analyze_temp; dir += '/'; } } /* the path should begin with a slash return values: 0 - directory exists dir_id - id of the directory dir - the path to the directory (with a slash at the end) file - if not empty means a file name (we don't check if the file really exists) 1 - there is not a root dir 2 - the path is empty 3 - there is not such a directory */ int Dirs::AnalyzePath(const std::wstring & path, long & dir_id, std::wstring & dir, std::wstring & file) { Item * pdir = GetRootDir(); dir = '/'; file.clear(); if( !pdir ) return 1; if( path.empty() ) return 2; if( path[0] != '/' ) return 3; size_t i = AnalyzeDir(pdir, path, dir_id, dir); if( i < path.size() ) { // checking if at least one slash has left for(size_t a=i ; a < path.size() ; ++a) if( path[a] == '/' ) return 3; // there is not such a directory // the rest of the path is a file name file = path.c_str() + i; } return 0; } // current_dir_tab can be the same container as out_dir_tab void Dirs::CopyDirTab(const std::vector & in, std::vector & out) { if( &in != &out ) { out.resize(in.size()); for(size_t i=0 ; i & dir_tab, const std::wstring & link_to, size_t & i) { size_t old_i; i = 0; if( dir_tab.empty() ) return false; while( true ) { // skipping slashes for( ; iid); if( !pdir ) { i = old_i; return true; // analyze_temp is not a directory } dir_tab.push_back(pdir); } } } int Dirs::FollowLink(std::vector & dir_tab, const std::wstring & link_to, std::wstring & out_item) { size_t i; if( !AnalyzeDir(dir_tab, link_to, i) ) return 2; // incorrect link_to if( i < link_to.size() ) { // checking if at least one slash has left for(size_t a=i ; a < link_to.size() ; ++a) if( link_to[a] == '/' ) return 2; // there is not such a directory // the rest of the path is a file name out_item = link_to.c_str() + i; return 1; } return 0; } /* return codes: ok: 0 - the link_to is a path to a directory (out_item skipped, out_dir_tab will not be empty) 1 - the link_to is a path to a file (out_item is used, out_dir_tab will not be empty) error: 2 - incorrect link_to 3 - there is not a root dir 4 - current_dir_tab was empty current_dir_tab can be the same container as out_dir_tab link_to can be a relative path (without the first slash) and can contain ".." or "." */ int Dirs::FollowLink(const std::vector & current_dir_tab, const std::wstring & link_to, std::vector & out_dir_tab, std::wstring & out_item) { temp_link_to = link_to; // link_to can be from the out_item and would be cleared next out_item.clear(); if( current_dir_tab.empty() ) return 4; if( temp_link_to.empty() ) { CopyDirTab(current_dir_tab, out_dir_tab); return 0; } if( temp_link_to[0] == '/' ) { // temp_link_to is an absolute path Item * pdir = GetRootDir(); if( !pdir ) return 3; out_dir_tab.clear(); out_dir_tab.push_back(pdir); } else { // temp_link_to is a relative path CopyDirTab(current_dir_tab, out_dir_tab); } return FollowLink(out_dir_tab, temp_link_to, out_item); } void Dirs::SplitPath(const std::wstring & path, std::wstring & dir, std::wstring & file) { std::wstring::size_type i; dir.clear(); file.clear(); if( path.empty() ) // !! moze dir ustawic na '/' ? return; for( i=path.size()-1 ; i>0 && path[i]!='/' ; --i); if( path[i] != '/' ) { // we do not have any slashes '/' file = path; return; } dir.assign(path, 0, i + 1); // +1 means with a slash at the end if( i < path.size() - 1 ) file.assign(path, i+1, path.size() - i - 1); } // !! dodac kasowanie z bazy bool Dirs::DelDir(long dir_id) { return dir_tab.DelById(dir_id); } bool Dirs::AddDirectory(Item & item, bool add_to_dir_tab, Item ** pdir, int notify_code) { if( pdir ) *pdir = 0; if( item.type != Item::dir ) return WINIX_ERR_DIR_EXPECTED; //Error status = db->AddItem(item); //item.set_connector(model_connector); bool status = item.insert(); if( status ) { Item * d = AddDir(item); if( add_to_dir_tab && !cur->request->dir_tab.empty() && cur->request->dir_tab.back()->id == item.parent_id ) cur->request->dir_tab.push_back(d); if( pdir ) *pdir = d; if( notify_code ) notify->ItemChanged(notify_code, item); } return status; } Item * Dirs::CreateVarDir() { Item * var = GetVarDir(); if( var ) return var; Item v; Item * root = GetRootDir(); if( root ) { v.set_connector(root->get_connector()); v.parent_id = root->id; v.item_content.user_id = root->item_content.user_id; v.item_content.group_id = root->item_content.group_id; v.item_content.privileges = root->item_content.privileges; v.subject = L"var"; v.url = L"var"; v.type = Item::dir; AddDirectory(v, false, &var); } return var; } // printing first and last slash void Dirs::LogDir(const std::vector & dir_tab) { log << '/'; // skipping the first (root) directory for(size_t i=1 ; iurl << '/'; } } // namespace Winix