/* * 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 "functions.h" #include "core/misc.h" #include "templates/templates.h" namespace Winix { //void Functions::SetConfig(Config * pconfig) //{ // config = pconfig; //} void Functions::SetCur(Cur * pcur) { cur = pcur; } void Functions::SetDb(Db * pdb) { db = pdb; } void Functions::SetSystem(System * psystem) { system = psystem; } void Functions::SetTemplates(Templates * ptemplates) { templates = ptemplates; } //void Functions::SetSynchro(Synchro * psynchro) //{ // synchro = psynchro; //} void Functions::SetSessionManager(SessionManager * pmanager) { session_manager = pmanager; } void Functions::set_dependency(WinixRequest * winix_request) { WinixRequest::set_dependency(winix_request); function_parser.set_dependency(winix_request); } size_t Functions::FunctionsSize() { return table.size(); } Functions::Iterator Functions::Begin() { return table.begin(); } Functions::Iterator Functions::End() { return table.end(); } FunctionBase * Functions::Find(const std::wstring & function_name) { Table::iterator i = table.find(function_name); if( i == table.end() ) return 0; FunctionBase * fun = i->second; // this is to set 'cur' and 'locale' for slog, but in the future slog will be moved to Session fun->set_cur(cur); fun->set_locale(locale); // return i->second; } void Functions::PrepareUrl(Item & item) { TrimWhite(item.url); if( item.url.empty() ) item.url = item.subject; // if the subject is empty then the url will be corrected by CorrectUrlOnlyAllowedChar() CorrectUrlOnlyAllowedChar(item.url); if( Find(item.url) ) { // the name provided by an user is the same as a name of a function // we add one underscore character at the beginning // names of functions should not begin with an underscore '_' // and we can simply add one '_' at the beginning // and the name will be unique item.url.insert(item.url.begin(), '_'); } } Error Functions::CheckSpecialFile(const Item & item) { static std::wstring fstab = L"fstab"; Item * etc = system->dirs.GetEtcDir(); if( !etc ) return WINIX_NOTHING_TO_DO; if( item.parent_id != etc->id ) return WINIX_NOTHING_TO_DO; if( item.url == fstab ) { log << log3 << "Functions: reloading mount points" << logend; cur->mount = system->mounts.GetEmptyMount(); system->mounts.ReadMounts(item.item_content.content_raw); cur->mount = system->mounts.pmount; templates->ReadNewIndexTemplates(); templates->ReadNewChangeTemplates(); return WINIX_ERR_OK; } return WINIX_NOTHING_TO_DO; } void Functions::SetObjects(FunctionBase * fun) { fun->set_dependency(this); //fun->SetConfig(config); //fun->SetCur(cur); fun->SetDb(db); //fun->SetSystem(system); fun->SetFunctions(this); fun->SetTemplates(templates); //fun->SetSynchro(synchro); //fun->SetSessionManager(session_manager); } void Functions::Add(FunctionBase * fun) { if( fun->fun.url.empty() ) { log << log1 << "Functions: skipping a function with an empty url" << logend; return; } if( Find(fun->fun.url) ) { log << log1 << "Functions: function " << fun->fun.url << " already exists (skipped)" << logend; return; } SetObjects(fun); table[fun->fun.url] = fun; } void Functions::Add(FunctionBase & fun) { Add(&fun); } void Functions::CreateFunctions() { Add(fun_account); Add(fun_adduser); Add(fun_cat); Add(fun_chmod); Add(fun_chown); Add(fun_ckeditor); Add(fun_cp); Add(fun_default); Add(fun_download); Add(fun_emacs); Add(fun_env); Add(fun_imgcrop); Add(fun_last); Add(fun_locale); Add(fun_login); Add(fun_logout); Add(fun_ln); Add(fun_ls); Add(fun_ipban); Add(fun_man); Add(fun_meta); Add(fun_mkdir); Add(fun_mount); Add(fun_mv); Add(fun_nicedit); Add(fun_node); Add(fun_passwd); Add(fun_priv); Add(fun_pw); Add(fun_reload); Add(fun_rm); Add(fun_rmuser); Add(fun_run); Add(fun_sort); Add(fun_special_default); Add(fun_stat); Add(fun_subject); Add(fun_template); Add(fun_time_zone); Add(fun_tinymce); Add(fun_uname); Add(fun_upload); Add(fun_uptime); Add(fun_who); Add(fun_vim); plugin->Call((Session*)0, WINIX_CREATE_FUNCTIONS); } void Functions::InitFunctions() { Table::iterator i = table.begin(); for( ; i!=table.end() ; ++i) i->second->Init(); } void Functions::Init() { CreateFunctions(); InitFunctions(); } void Functions::Parse() { function_parser.Parse(cur, db, this, system); } void Functions::SetDefaultFunctionForFile() { if( cur->request->item.item_content.file_type != WINIX_ITEM_FILETYPE_NONE ) cur->request->function = &fun_download; else if( system->HasReadExecAccess(cur->request->item) ) cur->request->function = &fun_run; else cur->request->function = &fun_cat; log << log3 << "Functions: default function: " << cur->request->function->fun.url << logend; } void Functions::SetDefaultFunctionForDir() { // !! nie potrzebne // if( system->mounts.pmount->type == system->mounts.MountTypeThread() ) // cur->request->function = &fun_thread; // else cur->request->function = &fun_ls; log << log3 << "Functions: default function: " << cur->request->function->fun.url << logend; } void Functions::SetDefaultFunction() { cur->request->function = 0; plugin->Call(WINIX_SELECT_DEFAULT_FUNCTION); if( cur->request->function ) { log << log3 << "Functions: default function: " << cur->request->function->fun.url << " (set by a plugin)" << logend; return; } if( cur->request->is_item ) SetDefaultFunctionForFile(); else SetDefaultFunctionForDir(); } void Functions::CheckFunctionFollowDir(bool was_default_function) { // directory with 'default' flag if( was_default_function ) { if( cur->request->dir_tab.back()->item_content.link_redirect == 1 ) { system->RedirectTo(cur->request->dir_tab.back()->item_content.link_to); } else { if( system->FollowAllLinks(cur->request->dir_tab.back()->item_content.link_to, true, true) ) SetDefaultFunction(); } } } void Functions::CheckFunctionFollowSymlink(bool was_default_function) { if( cur->request->item.item_content.link_redirect == 1 ) { if( was_default_function ) system->RedirectTo(cur->request->item.item_content.link_to); else system->RedirectWithFunctionAndParamsTo(cur->request->item.item_content.link_to); } else if( system->FollowAllLinks(cur->request->item.item_content.link_to, true, true) ) { if( was_default_function ) SetDefaultFunction(); if( cur->request->status == WINIX_ERR_OK && !cur->request->redirect_to.empty() && !was_default_function && cur->request->function ) { // !! nie jestem pewny dodania tej nowej funkcji do redirecta... (sprawdzic to) cur->request->redirect_to += '/'; cur->request->redirect_to += cur->request->function->fun.url; system->AddParams(cur->request->param_tab, cur->request->redirect_to, false); } } } // making a proper redirection from a directory with 'default' flag // or from a symlink (or just loading it if there is no redirection flag set) void Functions::CheckFunctionAndSymlink() { bool was_default_function = false; if( !cur->request->function || cur->request->function == &fun_special_default ) { was_default_function = true; SetDefaultFunction(); } // callback for plugins can set a different status if( cur->request->status != WINIX_ERR_OK || !cur->request->redirect_to.empty() ) return; if( !cur->request->is_item && !cur->request->dir_tab.back()->item_content.link_to.empty() ) CheckFunctionFollowDir(was_default_function); else if( cur->request->is_item && cur->request->item.type == Item::symlink && cur->request->function && cur->request->function->follow_symlinks ) CheckFunctionFollowSymlink(was_default_function); } void Functions::MakeFunction() { if( !cur->request->function ) { cur->request->status = WINIX_ERR_NO_FUNCTION; log << log1 << "Functions: no function (neither cat nor ls)" << logend; return; } if( !system->DirsHaveReadExecPerm() || !system->HasReadExecAccess(cur->request->function->fun) || !cur->request->function->HasAccess() ) { cur->request->status = WINIX_ERR_PERMISSION_DENIED; return; } if( cur->request->method == Request::get ) { if( cur->request->redirect_to.empty() ) cur->request->function->MakeGet(); } else if( cur->request->method == Request::post ) { // we don't use post with redirecting (the post variables would be lost) if( cur->request->redirect_to.empty() ) cur->request->function->MakePost(); else cur->request->status = WINIX_ERR_PERMISSION_DENIED; } else if( cur->request->method == Request::head ) { // do nothing // !! IMPROVE ME // we should make a page similar like in a GET request but the content should not be returned only } else if( cur->request->method == Request::delete_ ) { if( cur->request->redirect_to.empty() ) cur->request->function->MakeDelete(); } else { log << log1 << "Functions: unknown request method (skipping)" << logend; } } void Functions::CheckGetPostTimes(time_t difference) { time_t now = std::time(0); if( cur->session->puser ) return; if( cur->request->method != Request::post ) return; if( now - cur->session->last_time_get >= (time_t)difference ) return; if( cur->request->AllPostVarEmpty() ) return; cur->session->spam_score += 1; log << log1 << "Functions: spam +1: POST after GET sent too fast" << logend; } bool Functions::CheckAntispamCounter() { if( !cur->session->puser ) { long form_id = Tol(cur->request->PostVar(L"winix_form_id")); long counter_id = Tol(cur->request->PostVar(L"winix_form_counter")); auto i = cur->session->antispan.find(form_id); if( i != cur->session->antispan.end() ) { if( i->second != counter_id ) { log << log2 << "AP: you have provided a different counter, expecting: " << i->second << ", given: " << counter_id << logend; cur->session->antispan.erase(i); return true; } else { cur->session->antispan.erase(i); log << log2 << "AP: provided a correct counter for this form" << logend; } } else { log << log2 << "AP: nonexisting form_id" << logend; return true; } } return false; } // !!uwaga zwracana wartosc zmieniona (true/false) // !! IMPROVE ME in emacs.cpp there is a similar function bool Functions::CheckAbuse(SLog * slog) { if( !system->rebus.CheckRebus() ) { cur->request->status = WINIX_ERR_INCORRECT_REBUS; if( slog ) { // put to locale (*slog) << logerror << T(L"rebus_need_to_solve") << logend; } return true; } if( CheckAntispamCounter() ) { return true; } CheckGetPostTimes(); if( cur->session->spam_score > 0 ) { cur->request->status = WINIX_ERR_SPAM; log << log1 << "Functions: ignoring due to suspected spamming" << logend; return true; } return false; } // returning true if the 'url' has to be changed void Functions::ReadItemUrlSubject(Item & item, Item::Type item_type) { std::wstring * new_subject = cur->request->PostVarp(L"subject"); std::wstring * new_url = cur->request->PostVarp(L"url"); if( new_subject ) item.subject = *new_subject; /* if( item.subject.empty() ) { item.subject = cur->request->dir_tab.back()->subject; item.subject += L"_msg_"; Toa(db->Size(cur->request->dir_tab.back()->id, Item::file), item.subject, 10, false); } */ if( new_url ) item.url = *new_url; // if item.url is empty then it will be set from item.subject PrepareUrl(item); } void Functions::ReadItemFilterHtml(Item & item) { //html_filter.BreakWord(0); html_filter.WrapLine(0); //html_filter.TrimWhite(false); html_filter.InsertTabs(0); html_filter.SafeMode(true); html_filter.ClearOrphans(); // SetNoFilterTag doesn't have to be called (default empty tag) html_filter.Filter(cur->request->PostVar(L"itemcontent"), item.item_content.content_raw); } void Functions::ReadItemContent(Item & item, const std::wstring & content_type) { bool is_root = cur->session->puser && cur->session->puser->is_super_user; bool filter_html = (content_type == L"2") && config->editors_html_safe_mode; if( filter_html && is_root && config->editors_html_safe_mode_skip_root ) filter_html = false; if( filter_html ) ReadItemFilterHtml(item); else cur->request->PostVar(L"itemcontent", item.item_content.content_raw); } void Functions::ReadItemContentWithType(Item & item) { item.item_content.content_raw_type = ItemContent::ct_formatted_text; // default is formatted text cur->request->PostVar(L"contenttype", temp); ReadItemContent(item, temp); // ct_text and ct_formatted_text can use everyone if( temp == L"0" ) item.item_content.content_raw_type = ItemContent::ct_text; else if( temp == L"1" ) item.item_content.content_raw_type = ItemContent::ct_formatted_text; // those below need special privileges if( !cur->session->puser ) return; long user_id = cur->session->puser->id; if( temp == L"2" ) { if( system->CanUseHtml(user_id) ) item.item_content.content_raw_type = ItemContent::ct_html; } else if( temp == L"3" ) { if( system->CanUseBBCode(user_id) ) item.item_content.content_raw_type = ItemContent::ct_bbcode; } else if( temp == L"4" ) { if( system->CanUseOther(user_id) ) item.item_content.content_raw_type = ItemContent::ct_other; } } // item_type - the type of an item you are expecting to read // returns true if the url has to be changed // at the moment this is only checked for Item::file - for Item::dir it returns always true // !! zmienic nazwe na ReadUrlSubjectContent void Functions::ReadItem(Item & item, Item::Type item_type) { if( item_type == Item::none ) return; item.type = item_type; item.parent_id = cur->request->dir_tab.back()->id; // !! moze to dac jako parametr? ReadItemUrlSubject(item, item_type); if( item_type == Item::file ) ReadItemContentWithType(item); } void Functions::SetUser(Item & item) { if( cur->session && cur->session->puser ) { item.item_content.user_id = cur->session->puser->id; item.item_content.guest_name.clear(); } else { item.item_content.user_id = -1; cur->request->PostVar(L"guestname", item.item_content.guest_name); } item.item_content.group_id = cur->request->dir_tab.back()->item_content.group_id; } } // namespace Winix