/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2010-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 "system.h" #include "misc.h" #include "error.h" #include "templates/templates.h" #include "functions/functions.h" namespace Winix { void System::SetCur(Cur * pcur) { cur = pcur; } //void System::SetConfig(Config * pconfig) //{ // config = pconfig; //} void System::SetDb(Db * pdb) { db = pdb; } //void System::SetSynchro(Synchro * psynchro) //{ // synchro = psynchro; //} void System::SetFunctions(Functions * pfunctions) { functions = pfunctions; } void System::SetSessionManager(SessionManager * sm) { session_manager = sm; } void System::set_dependency(WinixModelDeprecated * winix_model) { WinixModelDeprecated::set_dependency(winix_model); dirs.set_dependency(this); mounts.set_dependency(this); //users.set_dependency(this); users.set_connector(model_connector); groups.set_dependency(this); rebus.set_dependency(this); load_avg.set_dependency(this); notify.set_dependency(this); image.set_dependency(this); crypt.set_dependency(this); thread_manager.set_dependency(this); job.set_dependency(winix_model); time_zones.set_dependency(winix_model); } void System::ReadTimeZones() { if( config->etc_dir.empty() ) { log << log1 << "System: I cannot read time zones, set etc_dir directory in the config" << logend; return; } if( config->time_zones_file.empty() ) { log << log1 << "System:: I cannot read time zones, set time_zones_file in the config" << logend; return; } name_temp = config->etc_dir; name_temp += '/'; name_temp += config->time_zones_file; time_zones.SetTimeZoneMaxId(config->time_zone_max_id); time_zones.ReadTimeZones(name_temp); } void System::Init() { //thread_manager.SetSynchro(synchro); thread_manager.Init(); dirs.SetDb(db); dirs.SetCur(cur); // only one method is using cur, can be passed as a parameter to the method dirs.SetNotify(¬ify); dirs.ReadDirs(); mounts.SkipStaticDirs(config->dont_use_static_dirs); mounts.SetDirs(&dirs); mounts.SetDb(db); mounts.SetCur(cur); // only one method is using cur, can be passed as a parameter to the method mounts.CreateMounts(); mounts.ReadMounts(); // users.SetCur(cur); // users.SetSessionManager(session_manager); users.set_connector(model_connector); users.ReadUsers(db); groups.ReadGroups(db); // !! chwilowe przekazanie argumentu, db bedzie zmienione rebus.SetCur(cur); rebus.Init(); notify.SetCur(cur); //notify.SetConfig(config); notify.SetUsers(&users); notify.SetDirs(&dirs); notify.SetThreadManager(&thread_manager); notify.Init(); image.SetDb(db); image.SetConfig(config); image.SetSystem(this); thread_manager.Add(&image, L"image"); // SetSynchro will be called by ThreadManager itself // job.ReadFromFile(); thread_manager.Add(&job, L"job"); ReadTimeZones(); } /* * try_to_use_ssl is to be meant: config->use_ssl, config->use_ssl_static, config->use_ssl_common */ bool System::IsSSLRequired(bool try_to_use_ssl) { bool ssl = false; if( try_to_use_ssl ) { if( !config->use_ssl_only_for_logged_users || cur->session->puser || (cur->request->function && cur->request->function->need_ssl) ) { ssl = true; } } return ssl; } bool System::IsSSLRequired() { return IsSSLRequired(config->use_ssl); } /* * try_to_use_ssl is to be meant: config->use_ssl, config->use_ssl_static, config->use_ssl_common */ void System::PutUrlProto(bool can_use_ssl, std::wstring & str, bool clear_str) { if( clear_str ) str.clear(); if( IsSSLRequired() ) str += config->url_ssl_proto; else str += config->url_proto; } /* * try_to_use_ssl is to be meant: config->use_ssl, config->use_ssl_static, config->use_ssl_common */ void System::PutUrlProto(bool can_use_ssl, pt::TextStream & str, bool clear_stream) { if( clear_stream ) str.clear(); if( IsSSLRequired(can_use_ssl) ) { str << config->url_ssl_proto; } else { str << config->url_proto; } } void System::PutUrlProto(std::wstring & str, bool clear_str) { return PutUrlProto(config->use_ssl, str, clear_str); } void System::PutUrlProto(pt::TextStream & str, bool clear_stream) { return PutUrlProto(config->use_ssl, str, clear_stream); } void System::CreateItemLink(long parent_id, const std::wstring & url, const std::wstring & subdomain, std::wstring & link, bool clear_str) { PutUrlProto(link, clear_str); if( !subdomain.empty() ) { link += subdomain; link += '.'; } link += config->base_url; dirs.MakePath(parent_id, link, false); // !! IMPROVE ME may some kind of error checks here? link += url; } void System::CreateItemLink(const Item & item, std::wstring & link, bool clear_str) { CreateItemLink(item.parent_id, item.url, cur->request->subdomain, link, clear_str); } // !! IMPROVE ME // !! mozna zrobic jakas obsluge kiedy nie mozemy sie redirectnac, np gdy wystapil blad // !! moze zwracac jakas wartosc? /* postfix will not be UrlEncoded */ void System::RedirectTo(const Item & item, const wchar_t * postfix, bool use_reqtype) { PutUrlProto(cur->request->redirect_to); if( !cur->request->subdomain.empty() ) { cur->request->redirect_to += cur->request->subdomain; cur->request->redirect_to += '.'; } cur->request->redirect_to += config->base_url; if( item.type == Item::dir ) { // item_id is pointing to a directory dirs.MakePath(item.id, cur->request->redirect_to, false); } else { // item_id is pointing to a file or a symlink if( dirs.MakePath(item.parent_id, cur->request->redirect_to, false) ) cur->request->redirect_to += item.url; } if( postfix ) cur->request->redirect_to += postfix; if( use_reqtype && cur->request->IsParam(L"reqtype") ) { cur->request->redirect_to += L"/-/reqtype:"; cur->request->redirect_to += cur->request->ParamValue(L"reqtype"); } } /* postfix will not be UrlEncoded */ void System::RedirectTo(long item_id, const wchar_t * postfix, bool use_reqtype) { PutUrlProto(cur->request->redirect_to); if( !cur->request->subdomain.empty() ) { cur->request->redirect_to += cur->request->subdomain; cur->request->redirect_to += '.'; } cur->request->redirect_to += config->base_url; Item * pdir = dirs.GetDir(item_id); if( pdir ) { // item_id is pointing to a directory dirs.MakePath(pdir->id, cur->request->redirect_to, false); } else { // item_id is pointing to a file // DbItemQuery iq; // iq.SetAllSel(false); // iq.WhereId(item_id); // iq.sel_parent_id = true; // iq.sel_url = true; morm::Finder finder(model_connector); item_temp = finder. select(). where(). eq(L"id", item_id). get(); //if( db->GetItem(item_temp, iq) == WINIX_ERR_OK ) if( item_temp.found() ) { if( dirs.MakePath(item_temp.parent_id, cur->request->redirect_to, false) ) cur->request->redirect_to += item_temp.url; } else { log << log1 << "System: can't redirect: no such item: id: " << item_id << logend; } } if( postfix ) cur->request->redirect_to += postfix; if( use_reqtype && cur->request->IsParam(L"reqtype") ) { cur->request->redirect_to += L"/-/reqtype:"; cur->request->redirect_to += cur->request->ParamValue(L"reqtype"); } } /* url will not be UrlEncoded */ void System::RedirectTo(const wchar_t * url, bool use_reqtype) { PutUrlProto(cur->request->redirect_to); if( !cur->request->subdomain.empty() ) { cur->request->redirect_to += cur->request->subdomain; cur->request->redirect_to += '.'; } cur->request->redirect_to += config->base_url; if( url[0] == '/' ) { // absolute path cur->request->redirect_to += url; } else { // relative path if( !cur->request->dir_tab.empty() ) { if( dirs.MakePath(cur->request->dir_tab.back()->id, cur->request->redirect_to, false) ) cur->request->redirect_to += url; } else { cur->request->redirect_to += '/'; cur->request->redirect_to += url; } } if( use_reqtype && cur->request->IsParam(L"reqtype") ) { cur->request->redirect_to += L"/-/reqtype:"; cur->request->redirect_to += cur->request->ParamValue(L"reqtype"); } } /* url will not be UrlEncoded */ void System::RedirectTo(const std::wstring & url, bool use_reqtype) { RedirectTo(url.c_str(), use_reqtype); } /* params will be UrlEncoded */ void System::AddParams(const ParamTab & param_tab, std::wstring & str, bool clear_str) { if( clear_str ) str.clear(); for(size_t i=0 ; irequest->function ) return; cur->request->redirect_to += '/'; cur->request->redirect_to += cur->request->function->fun.url; AddParams(cur->request->param_tab, cur->request->redirect_to, false); } /* url will not be UrlEncoded params will be UrlEncoded */ void System::RedirectWithFunctionAndParamsTo(const std::wstring & url) { RedirectWithFunctionAndParamsTo(url.c_str()); } void System::RedirectToLastDir(const wchar_t * postfix, bool use_reqtype) { if( !cur->request->dir_tab.empty() ) RedirectTo( *cur->request->dir_tab.back(), postfix, use_reqtype); } void System::RedirectToLastItem(const wchar_t * postfix, bool use_reqtype) { if( cur->request->last_item ) RedirectTo( *cur->request->last_item, postfix, use_reqtype ); } void System::RedirectToLastFunction(const wchar_t * postfix, bool use_reqtype) { RedirectToLastDir(0, false); TrimLast(cur->request->redirect_to, '/'); if( cur->request->is_item ) { cur->request->redirect_to += '/'; cur->request->redirect_to += cur->request->item.url; } if( cur->request->function ) { cur->request->redirect_to += '/'; cur->request->redirect_to += cur->request->function->fun.url; } if( postfix ) { cur->request->redirect_to += '/'; cur->request->redirect_to += postfix; } if( use_reqtype && cur->request->IsParam(L"reqtype") ) { if( !cur->request->function && !postfix ) cur->request->redirect_to += L"/-"; cur->request->redirect_to += L"/reqtype:"; cur->request->redirect_to += cur->request->ParamValue(L"reqtype"); } } bool System::CanChangeUser(const Item & item, long new_user_id) { if( !cur->session ) // session must be set return false; if( cur->session->puser && cur->session->puser->is_super_user ) // super user is allowed everything return true; if( item.item_content.user_id == -1 || new_user_id == -1 || item.item_content.user_id != new_user_id ) // only super user can change the owner of an item return false; // item.user_id is equal new_user_id -- we return true return true; } bool System::CanChangeGroup(const Item & item, long new_group_id) { if( !cur->session ) // session must be set return false; if( cur->session->puser && cur->session->puser->is_super_user ) // super user is allowed everything return true; if( item.item_content.group_id != new_group_id ) { // user is allowed to change the group only if he is an owner of the item // he can change only into a group in which he is a member of, or into a 'no_group' if( !cur->session->puser || cur->session->puser->id == -1 ) return false; if( item.item_content.user_id == -1 || cur->session->puser->id != item.item_content.user_id ) return false; if( new_group_id == -1 ) return true; if( !cur->session->puser->IsMemberOf(new_group_id) ) return false; // is logged, is the owner of the item, is the member of the new group } return true; } bool System::CanChangePrivileges(const Item & item, int new_priv) { if( !cur->session ) // session must be set return false; if( cur->session->puser && cur->session->puser->is_super_user ) // super user is allowed everything return true; if( item.item_content.privileges != new_priv ) { // the owner of an item is allowed to change the privileges if( !cur->session->puser || cur->session->puser->id == -1 ) return false; if( item.item_content.user_id == -1 || cur->session->puser->id != item.item_content.user_id ) return false; } return true; } // DEPRACATED bool System::HasReadAccess(const Item & item) { return item.item_content.has_read_access(); } // DEPRACATED bool System::HasWriteAccess(const Item & item) { return item.item_content.has_write_access(); } // DEPRACATED bool System::HasReadWriteAccess(const Item & item) { return item.item_content.has_read_write_access(); } // DEPRACATED bool System::HasReadExecAccess(const Item & item) { return item.item_content.has_read_exec_access(); } bool System::HasReadExecAccessToPath(long dir_id) { while( true ) { Item * pdir = dirs.GetDir(dir_id); if( !pdir ) return false; if( !HasReadExecAccess(*pdir) ) return false; dir_id = pdir->parent_id; if( dir_id == -1 ) return true; } } bool System::HasReadExecAccessToPath(const std::vector & dir_tab) { for(size_t i=0 ; i < dir_tab.size() ; ++i) { if( !HasReadExecAccess(*dir_tab[i]) ) return false; } return true; } bool System::DirsHaveReadExecPerm() { return HasReadExecAccessToPath(cur->request->dir_tab); } // if we don't have access we only remove the item from the table // !! moze zamienic nazwe na CheckReadAccessToItems ? void System::CheckAccessToItems(std::vector & item_tab) { size_t i = 0; while( i < item_tab.size() ) { if( !HasReadAccess(item_tab[i]) ) { item_tab.erase(item_tab.begin() + i); } else { i += 1; } } } void System::CheckWriteAccessToItems(std::vector & item_tab) { size_t i = 0; while( i < item_tab.size() ) { if( !HasWriteAccess(item_tab[i]) ) { item_tab.erase(item_tab.begin() + i); } else { i += 1; } } } int System::NewPrivileges(int creation_mask) { if( cur && cur->session && cur->session->puser ) { int umask = cur->session->puser->env.to_int(L"umask", config->umask); return (~umask) & creation_mask; } else { return (~config->umask) & creation_mask; } } /* from man sticky: A directory whose `sticky bit' is set becomes an append-only directory, or, more accurately, a directory in which the deletion of files is restricted. A file in a sticky directory may only be removed or renamed by a user if the user has write permission for the directory and the user is the owner of the file, the owner of the directory, or the super-user. This feature is usefully applied to directories such as /tmp which must be publicly writable but should deny users the license to arbitrarily delete or rename each others' files. */ bool System::CanRemoveRenameChild(const Item & dir, long child_item_user_id) { if( dir.type != Item::dir ) return false; if( !HasWriteAccess(dir) ) return false; if( (dir.item_content.privileges & 010000) == 0 ) // there is no a sticky bit set to this directory return true; if( cur->session->puser ) { if( cur->session->puser->is_super_user ) return true; if( dir.item_content.user_id != -1 && cur->session->puser->id != -1 && child_item_user_id != -1 ) { if( cur->session->puser->id == child_item_user_id || cur->session->puser->id == dir.item_content.user_id ) return true; } } return false; } int System::NewFilePrivileges() { return NewPrivileges(06666); } int System::NewDirPrivileges() { return NewPrivileges(07777); } bool System::CanUseHtml(long user_id) { return IsSuperUser(user_id) || IsMemberOfGroup(user_id, L"allow_html"); } bool System::CanUseBBCode(long user_id) { // logged users can use bbcode return (user_id != -1); } // !! IMPROVE ME change to a better name bool System::CanUseOther(long user_id) { return IsSuperUser(user_id) || IsMemberOfGroup(user_id, L"allow_other"); } bool System::IsSuperUser(long user_id) { User * puser = users.GetUser(user_id); if( !puser ) return false; return puser->is_super_user; } bool System::IsMemberOfGroup(long user_id, const wchar_t * group_name) { User * puser = users.GetUser(user_id); if( !puser ) return false; long group = groups.GetGroupId(group_name); if( group == -1 ) // there is no such a group return false; return puser->IsMemberOf(group); } // the path depends on parent_id bool System::CreateNewFileSimpleFs(Item & item) { bool res = dirs.MakePath(item.parent_id, item.item_content.file_path); if( res ) { if( !item.item_content.file_path.empty() && item.item_content.file_path[0] == '/' ) item.item_content.file_path.erase(0, 1); } else { log << log1 << "System: CreateNewFileSimpleFs: can't create a path to item.id: " << item.id << ", item.parent_id: " << item.parent_id << logend; } return res; } // the path depends on id bool System::CreateNewFileHashFs(Item & item) { wchar_t buffer[50]; wchar_t * hash = buffer; size_t buffer_len = sizeof(buffer)/sizeof(wchar_t); // get 'id' as hexadecimal buffer[0] = '0'; swprintf(buffer+1, buffer_len, L"%lx", (unsigned long)item.id); item.item_content.file_path.clear(); // make sure that the length is even if( (wcslen(hash) & 1) != 0 ) hash = buffer + 1; // the first character was zero for(size_t i=0 ; hash[i] != 0 ; i+=2) { item.item_content.file_path += hash[i]; item.item_content.file_path += hash[i+1]; if( hash[i+2] != 0 ) item.item_content.file_path += '/'; } // one character more to make sure the path is unique // (we can have a directory without the character) item.item_content.file_path += '_'; return true; } // creating item.file_path and item.file_fs // you should set file_type yourself // this method uses: item.id, item.url, item.parent_id (for selecting a mountpoint) bool System::CreateNewFile(Item & item) { bool res; if( item.type != Item::file ) { log << log1 << "System: CreateNewFile: the item should be a file" << logend; return false; } Mount * pmount = mounts.CalcMount(item.parent_id); if( !pmount || pmount->fs != mounts.MountFsHashfs() ) { res = CreateNewFileSimpleFs(item); item.item_content.file_fs = mounts.MountFsSimplefs(); } else { res = CreateNewFileHashFs(item); item.item_content.file_fs = mounts.MountFsHashfs(); } if( res ) item.item_content.file_path += item.url; else item.item_content.file_path.clear(); return res; } // making a global file path (in the unix file system) // you should call CreateNewFile before bool System::MakeFilePath(const Item & item, std::wstring & path, bool thumb, bool create_dir, int chmod, int group) { path.clear(); if( config->upload_dir.empty() ) { log << log1 << "System: MakePath: upload_dir is not set in the config file" << logend; return false; } if( item.item_content.file_path.empty() || item.item_content.file_type == WINIX_ITEM_FILETYPE_NONE ) { log << log1 << "System: MakePath: this item has not a static file" << logend; return false; } path = config->upload_dir; if( item.item_content.file_fs == mounts.MountFsHashfs() ) path += L"/hashfs"; else path += L"/simplefs"; if( thumb ) path += L"/thumb"; else path += L"/normal"; if( create_dir && !CreateDirs(path, item.item_content.file_path, chmod, group, true) ) return false; path += '/'; path += item.item_content.file_path; return true; } bool System::MakeRelativeFilePath(const Item & item, const std::wstring & path_prefix, std::wstring & path, bool thumb) { path.clear(); if( item.item_content.file_path.empty() || item.item_content.file_type == WINIX_ITEM_FILETYPE_NONE ) { log << log1 << "System: MakePath: this item has not a static file" << logend; return false; } // we allow the prefix to be empty if( !path_prefix.empty() ) { if( path_prefix[0] != '/' ) path += '/'; path += path_prefix; TrimLast(path, '/'); } if( item.item_content.file_fs == mounts.MountFsHashfs() ) path += L"/hashfs"; else path += L"/simplefs"; if( thumb ) path += L"/thumb"; else path += L"/normal"; path += '/'; path += item.item_content.file_path; return true; } // item can be a directory, file or a symlink // if item is a directory then the path will be with a slash at the end bool System::MakePath(const Item & item, std::wstring & path, bool clear_path) { bool res; if( clear_path ) path.clear(); if( item.type == Item::dir ) { res = dirs.MakePath(item.id, path); } else { res = dirs.MakePath(item.parent_id, path); if( res ) path += item.url; } return res; } bool System::AddFile(Item & item, int notify_code, bool call_plugins) { if( item.type != Item::file ) return WINIX_ERR_FILE_EXPECTED; //Error status = db->AddItem(item); item.set_connector(model_connector); bool status = item.insert(); if( status ) { log << log2 << "System: added a new file, url: " << item.url << ", id: " << item.id << ", parent_id: " << item.parent_id << logend; if( notify_code ) notify.ItemChanged(notify_code, item); if( call_plugins ) plugin->Call(WINIX_FILE_ADDED, &item); } return status; } bool System::EditFile(Item & item, bool with_url, int notify_code, bool call_plugins) { if( item.type != Item::file ) return WINIX_ERR_FILE_EXPECTED; if( cur->session && cur->session->puser ) cur->request->item.item_content.modification_user_id = cur->session->puser->id; else cur->request->item.item_content.modification_user_id = -1; item.item_content.SetDateModifyToNow(); ItemModelData item_model_data; item_model_data.prepare_unique_url = with_url; bool status = item.update(item_model_data); //Error status = db->EditItemById(item, with_url); if( status ) { TemplatesFunctions::pattern_cacher.UpdatePattern(item); log << log2 << "System: modified an item" << logend; if( notify_code ) notify.ItemChanged(notify_code, item); if( call_plugins ) plugin->Call(WINIX_FILE_CHANGED, &item); } return status; } time_t System::ToLocal(time_t utc_time) { size_t tz_id; if( cur->session && cur->session->puser ) tz_id = cur->session->puser->time_zone_id; else tz_id = config->time_zone_default_id; TimeZone * tz = time_zones.GetZone(tz_id); if( tz ) return tz->ToLocal(utc_time); return utc_time; } pt::Date System::ToLocal(const pt::Date & utc_date) { size_t tz_id; if( cur->session && cur->session->puser ) tz_id = cur->session->puser->time_zone_id; else tz_id = config->time_zone_default_id; TimeZone * tz = time_zones.GetZone(tz_id); if( tz ) return tz->ToLocal(utc_date); return utc_date; } time_t System::ToUTC(time_t local_time) { size_t tz_id; if( cur->session && cur->session->puser ) tz_id = cur->session->puser->time_zone_id; else tz_id = config->time_zone_default_id; TimeZone * tz = time_zones.GetZone(tz_id); if( tz ) return tz->ToUTC(local_time); return local_time; } pt::Date System::ToUTC(const pt::Date & local_date) { size_t tz_id; if( cur->session && cur->session->puser ) tz_id = cur->session->puser->time_zone_id; else tz_id = config->time_zone_default_id; TimeZone * tz = time_zones.GetZone(tz_id); if( tz ) return tz->ToUTC(local_date); return local_date; } /* 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 or a symlink (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) */ int System::FollowLink(const std::vector & current_dir_tab, const std::wstring & link_to, std::vector & out_dir_tab, Item & out_item) { link_to_temp = link_to; // copy the link because it can be from out_item (and will be cleared) out_item.Clear(); int res = dirs.FollowLink(current_dir_tab, link_to_temp, out_dir_tab, name_temp); if( res == 1 ) { morm::Finder finder(model_connector); bool status = finder. select(). where(). eq(L"parent_id", out_dir_tab.back()->id). eq(L"url", name_temp). get(out_item); //if( db->GetItem(out_dir_tab.back()->id, name_temp, out_item) == WINIX_ERR_OK ) if( status ) return 1; else return 2; } return res; } bool System::FollowAllLinksDirFound(std::vector & out_dir_tab, bool follow_dir_default, bool stop_on_link_redirect, bool check_access) { log << log3 << "System: link to a directory: "; dirs.LogDir(out_dir_tab); log << logend; if( check_access && !HasReadExecAccessToPath(out_dir_tab) ) { log << log1 << "System: no access to the directory structure" << logend; return false; } if( !out_dir_tab.back()->item_content.link_to.empty() ) { if( follow_dir_default ) { if( !(stop_on_link_redirect && out_dir_tab.back()->item_content.link_redirect==1) ) link_to_temp = out_dir_tab.back()->item_content.link_to; } } return true; } bool System::FollowAllLinksFileOrSymlinkFound(std::vector & out_dir_tab, Item & out_item, bool stop_on_link_redirect, bool check_access) { if( out_item.type == Item::symlink ) log << log3 << "System: link to a symlink: "; else log << log3 << "System: link to a file: "; dirs.LogDir(out_dir_tab); log << out_item.url << logend; if( check_access && !HasReadExecAccessToPath(out_dir_tab) ) { log << log1 << "System: no access to the directory structure" << logend; return false; } if( check_access && !HasReadAccess(out_item) ) { log << log1 << "System: no read access to the file or symlink" << logend; return false; } if( out_item.type == Item::symlink ) { if( out_item.item_content.link_to.empty() ) { log << log1 << "System: symlink empty" << logend; return false; } else { if( !(stop_on_link_redirect && out_item.item_content.link_redirect==1) ) link_to_temp = out_item.item_content.link_to; } } return true; } /* 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) (link_to can be a path to a symlink if stop_on_link_redirect is true) error: 2 - incorrect link_to 3 - there is not a root dir 4 - current_dir_tab was empty 5 - limit of symlinks exceeded 6 - permission denied to a file/symlink or a directory (or a symlink is empty) current_dir_tab can be the same container as out_dir_tab link_to can be a relative path (without the first slash) if follow_dir_default is true then directories with 'default' flags are followed as well if stop_on_link_redirect is true then the method stops on a symbolic link (or a directory with 'default' flag set) where the redirection should be done, you should check then whether the out_item.back()->link_to is not empty (if the result was 0 ) or whether out_item.type is symlink (if the result was 1) to make the redirection */ int System::FollowAllLinks(const std::vector & current_dir_tab, const std::wstring & link_to, std::vector & out_dir_tab, Item & out_item, bool follow_dir_default, bool stop_on_link_redirect, bool check_access) { int res; size_t level = 0; link_to_temp = link_to; if( current_dir_tab.empty() ) return 4; do { if( ++level > config->symlinks_follow_max ) { log << log1 << "System: the number of maximum symlinks exceeded (" << config->symlinks_follow_max << ")" << logend; res = 5; } else { // !! CHECK ME // FollowLink is using link_to_temp temporary variable too res = FollowLink(current_dir_tab, link_to_temp, out_dir_tab, out_item); link_to_temp.clear(); if( res == 0 ) { out_item.Clear(); res = FollowAllLinksDirFound(out_dir_tab, follow_dir_default, stop_on_link_redirect, check_access) ? 0 : 6; } else if( res == 1 ) { res = FollowAllLinksFileOrSymlinkFound(out_dir_tab, out_item, stop_on_link_redirect, check_access) ? 1 : 6; } else { log << log2 << "System: incorrect link: " << link_to << logend; } } } while( !link_to_temp.empty() ); return res; } // the same as FollowAllLinks but starts from the root directory // link_to must begin with a slash (or can be empty then the root directory is returned) int System::FollowAllLinks(const std::wstring & link_to, std::vector & out_dir_tab, Item & out_item, bool follow_dir_default, bool stop_on_link_redirect, bool check_access) { if( !link_to.empty() && link_to[0] != '/' ) return 2; if( root_follow_dir_tab.size() != 1 ) root_follow_dir_tab.resize(1); Item * root_dir = dirs.GetRootDir(); if( !root_dir ) return 3; root_follow_dir_tab[0] = root_dir; return FollowAllLinks(root_follow_dir_tab, link_to, out_dir_tab, out_item, follow_dir_default, stop_on_link_redirect, check_access); } // the same as FollowAllLinks but operates on cur->request->dir_tab and cur->request->item // and returns bool // the method is making a redirection if needed bool System::FollowAllLinks(const std::wstring & link_to, bool follow_dir_default, bool stop_on_link_redirect, bool check_access) { int res = FollowAllLinks(cur->request->dir_tab, link_to, temp_follow_dir_tab, temp_follow_item, follow_dir_default, stop_on_link_redirect, check_access); bool ok = (res == 0 || res == 1); if( ok ) { cur->request->dir_tab = temp_follow_dir_tab; if( res == 0 ) { cur->request->is_item = false; cur->request->item.Clear(); cur->request->last_item = cur->request->dir_tab.back(); if( !cur->request->dir_tab.back()->item_content.link_to.empty() ) RedirectTo(cur->request->dir_tab.back()->item_content.link_to); log << log3 << "System: current directory changed" << logend; } else { cur->request->is_item = true; cur->request->item = temp_follow_item; cur->request->last_item = &cur->request->item; if( cur->request->item.type == Item::symlink ) RedirectTo(cur->request->item.item_content.link_to); // cur->request->item.item_content.link_to is not empty log << log3 << "System: current directory changed and the new file loaded" << logend; } mounts.CalcCurMount(); } else { if( res == 5 || res == 6 ) cur->request->status = WINIX_ERR_PERMISSION_DENIED; else cur->request->status = WINIX_ERR_NO_ITEM; } return ok; } /* adding a new file to winix file_path - a name of a static file (from common directory) url - url of a file which will be inserted (in /var directory) current limitation: warning: the url is not prepared by PrepareUrl() (PrepareUrl is from functions) */ bool System::AddCommonFileToVar(const wchar_t * file_path, const wchar_t * url, const wchar_t * mime_type, bool overwrite_existing) { if( config->common_dir.empty() ) { log << log1 << "System: can't open a file from common directory, common_dir not set in the config" << logend; return false; } file_name = config->common_dir; file_name += '/'; file_name += file_path; if( !GetUTF8File(file_name, file_content) ) { log << log1 << "System: can't open a file: " << file_name << logend; return false; } Item * var = dirs.CreateVarDir(); if( !var ) { log << log1 << "System: can't create /var directory" << logend; return false; } morm::Finder finder(model_connector); file_content_item = finder.select().where().eq(L"parent_id", var->id).eq(L"url", url).get(); if( file_content_item.found() ) { if( !overwrite_existing ) return true; file_content_item.remove(); } file_content_item.Clear(); file_content_item.parent_id = var->id; file_content_item.item_content.user_id = var->item_content.user_id; file_content_item.item_content.group_id = var->item_content.group_id; file_content_item.item_content.privileges = 07555; // !! IMPROVE ME: may it should be added as a parameter to this function? file_content_item.subject = url; file_content_item.url = url; file_content_item.type = Item::file; file_content_item.html_template = config->templates_index_raw; file_content_item.item_content.content_raw = file_content; file_content_item.item_content.content_raw_type = ItemContent::ct_other; if( mime_type ) file_content_item.item_content.file_mime_type = mime_type; return AddFile(file_content_item, false); } } // namespace Winix