/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 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 "models/itemcontent.h" #include "core/crypt.h" #include "core/misc.h" #include "templates/misc.h" #include "core/bbcodeparser.h" #include "core/request.h" #include "core/users.h" #include "core/groups.h" namespace Winix { ItemContent::ItemContent() { Clear(); } void ItemContent::fields() { int content_raw_type_helper = static_cast(content_raw_type); int content_parsed_type_helper = static_cast(content_parsed_type); field(L"id", id, morm::FT::no_insertable | morm::FT::no_updatable | morm::FT::primary_key); field(L"references", references); field(L"user_id", user_id); field(L"group_id", group_id); field(L"guest_name", guest_name); field(L"modification_user_id", modification_user_id); field(L"privileges", privileges); field(L"date_creation", date_creation); field(L"date_modification", date_modification); field(L"link_to", link_to); field(L"link_redirect", link_redirect); field(L"file_path", file_path); field(L"file_fs", file_fs); field(L"file_type", file_type); field(L"file_has_thumb", file_has_thumb); field(L"file_hash", file_hash); field(L"file_hash_type", file_hash_type); field(L"file_size", file_size); field(L"content_raw", content_raw); field(L"content_raw_type", content_raw_type_helper); field(L"content_parsed", content_parsed); field(L"content_parsed_type", content_parsed_type_helper); field(L"meta", meta); field(L"meta_admin", meta_admin); field(L"print_content", &ItemContent::print_content); field(L"has_static_file", &ItemContent::has_static_file); field(L"privileges_octal", &ItemContent::privileges_octal); field(L"user_name", &ItemContent::user_name); field(L"group_name", &ItemContent::group_name); field(L"type_is", &ItemContent::type_is); field(L"is_empty", &ItemContent::is_empty); field(L"file_type_is_none", &ItemContent::file_type_is_none); field(L"file_type_is_image", &ItemContent::file_type_is_image); field(L"file_type_is_video", &ItemContent::file_type_is_video); field(L"file_type_is_sound", &ItemContent::file_type_is_sound); field(L"has_thumb", &ItemContent::has_thumb); // IMPROVEME prepare a setter functions which tests whether content_raw_type_helper and content_parsed_type_helper are correct values content_raw_type = static_cast(content_raw_type_helper); content_parsed_type = static_cast(content_parsed_type_helper); } void ItemContent::table() { table_name(L"core", L"content"); } void ItemContent::after_insert() { get_last_sequence_for_primary_key(L"core.content_id_seq", id); } // !! IMPROVEME // now we have Request::start_time and Request::start_date // we can somehow get the current time from the request // may setting the date should be completetly removed from here? void ItemContent::SetDateToNow() { date_creation = std::time(0); date_modification = date_creation; } void ItemContent::SetDateModifyToNow() { date_modification = std::time(0); } void ItemContent::Clear() { id = -1; references = 1; user_id = -1; group_id = -1; guest_name.clear(); modification_user_id = -1; privileges = 0; link_to.clear(); link_redirect = false; file_path.clear(); file_fs = -1; file_type = WINIX_ITEM_FILETYPE_NONE; file_has_thumb = false; file_hash.clear(); file_hash_type = WINIX_CRYPT_HASH_NONE; file_size = 0; content_raw.clear(); content_raw_type = ct_formatted_text; content_parsed.clear(); content_parsed_type = ct_formatted_text; meta.clear(); meta_admin.clear(); SetDateToNow(); } bool ItemContent::do_migration(int & current_table_version) { bool ok = true; ok = ok && morm::Model::do_migration(current_table_version, 1, this, &ItemContent::do_migration_to_1); ok = ok && morm::Model::do_migration(current_table_version, 2, this, &ItemContent::do_migration_to_2); ok = ok && morm::Model::do_migration(current_table_version, 3, this, &ItemContent::do_migration_to_3); return ok; } bool ItemContent::do_migration_to_1() { const char * str = R"sql( CREATE TABLE core.content ( id serial, content text, content_type smallint, file_path character varying(2048), file_fs smallint, file_type smallint, has_thumb smallint, ref integer, modify_index smallint, hash character varying(255), hash_type smallint, file_size bigint ); )sql"; db_query(str); return true; // IMPROVEME remove me in the future: this is only for a moment until we do migration on all our sites } bool ItemContent::do_migration_to_2() { const char * str = R"sql( alter table core.content add column user_id integer, add column group_id integer, add column guest_name character varying(20), add column modification_user_id integer, add column privileges integer, add column date_creation timestamp without time zone, add column date_modification timestamp without time zone, add column link_to character varying(2048), add column link_redirect smallint, add column meta text, add column meta_admin text, add column content_parsed text, add column content_parsed_type smallint; )sql"; return db_query(str); } bool ItemContent::do_migration_to_3() { const char * str[] = { "alter table core.content rename column ref to \"references\";", "alter table core.content rename column content to content_raw;", "alter table core.content rename column content_type to content_raw_type;", "alter table core.content rename column has_thumb to file_has_thumb;", "alter table core.content rename column hash to file_hash;", "alter table core.content rename column hash_type to file_hash_type;", "alter table core.content drop column modify_index;", "alter table core.content add column file_has_thumb_new boolean;", "update core.content as c1 set file_has_thumb_new = (select case when file_has_thumb <> 0 then true else false end from core.content as c2 where c1.id = c2.id);", "alter table core.content drop column file_has_thumb;", "alter table core.content rename file_has_thumb_new to file_has_thumb;", }; size_t len = sizeof(str) / sizeof(const char*); for(size_t i=0 ; i < len ; ++i) { if( !db_query(str[i]) ) { return false; } } return true; } bool ItemContent::has_access(const User * current_user, int mask) const { if( current_user ) { if( current_user->super_user ) { // super user is allowed everything return true; } if( user_id != -1 && current_user->id == user_id ) { // the owner return ((privileges >> 9) & mask) == mask; } if( group_id != -1 && current_user->IsMemberOf(group_id) ) { // group return ((privileges >> 6) & mask) == mask; } // others -- others logged users return ((privileges >> 3) & mask) == mask; } // guests -- not logged users return (privileges & mask) == mask; } bool ItemContent::has_read_access(const User * current_user) const { return has_access(current_user, 4); // r } bool ItemContent::has_write_access(const User * current_user) const { return has_access(current_user, 2); // w } bool ItemContent::has_read_write_access(const User * current_user) const { return has_access(current_user, 6); // r+w } bool ItemContent::has_read_exec_access(const User * current_user) const { if( current_user && current_user->super_user ) { // there must be at least one 'x' (for the root) // !! CHECK ME: is it applicable to directories too? return (privileges & 01111) != 0; } return has_access(current_user, 5); // r+x } bool ItemContent::has_read_access() const { return has_read_access(get_current_user()); } bool ItemContent::has_write_access() const { return has_write_access(get_current_user()); } bool ItemContent::has_read_write_access() const { return has_read_write_access(get_current_user()); } bool ItemContent::has_read_exec_access() const { return has_read_exec_access(get_current_user()); } bool ItemContent::is_sticky_bit_set() const { int mask = 010000; return (privileges & mask) == mask; } /* * we're using the HtmlFilter only for those contents * */ bool ItemContent::CanContentBeHtmlFiltered(ItemContent::ContentType ct) { return ct == ct_text || ct == ct_formatted_text || ct == ct_html || ct == ct_bbcode; } bool ItemContent::CanContentBeHtmlFiltered() { return CanContentBeHtmlFiltered(content_raw_type); } void ItemContent::print_content(HtmlTextStream & out, const std::wstring & content, ItemContent::ContentType content_type, bool is_html_filter_on) { using TemplatesFunctions::R; if( is_html_filter_on && !ItemContent::CanContentBeHtmlFiltered(content_type) ) out << R(""); if( content_type == ItemContent::ct_text ) { out << content; } else if( content_type == ItemContent::ct_formatted_text ) { TemplatesFunctions::HtmlEscapeFormTxt(out, content); } else if( content_type == ItemContent::ct_bbcode ) { static std::wstring out_temp; out_temp.clear(); out_temp.reserve(content.size()*2); BBCODEParser bbcode_parser; // IMPROVE ME move me to a better place bbcode_parser.Filter(content.c_str(), out_temp); out << R(out_temp); } else { // ct_html, ct_other out << R(content); } if( is_html_filter_on && !ItemContent::CanContentBeHtmlFiltered(content_type) ) out << R(""); } void ItemContent::print_content(EzcEnv & env) { print_content(env.out, content_raw, content_raw_type, true); // IMPROVE ME get the 'true' from the config (config->html_filter) } void ItemContent::has_static_file(EzcEnv & env) { env.res = file_type != WINIX_ITEM_FILETYPE_NONE && !file_path.empty(); } void ItemContent::privileges_octal(EzcEnv & env) { env.out << Toa(privileges, 8); } void ItemContent::user_name(EzcEnv & env) { Users * users = get_users(); if( users ) { User * puser = users->GetUser(user_id); TemplatesFunctions::print_user_name(env, puser, guest_name); } } void ItemContent::group_name(EzcEnv & env) { Groups * groups = get_groups(); if( groups ) { Group * pgroup = groups->GetGroup(group_id); if( pgroup ) env.out << pgroup->name; else env.out << group_id; } } void ItemContent::type_is(EzcEnv & env) { if( content_raw_type == ItemContent::ct_text && env.par == L"text" ) env.res = true; else if( content_raw_type == ItemContent::ct_formatted_text && env.par == L"formatted text" ) env.res = true; else if( content_raw_type == ItemContent::ct_html && env.par == L"html" ) env.res = true; else if( content_raw_type == ItemContent::ct_bbcode && env.par == L"bbcode" ) env.res = true; else if( content_raw_type == ItemContent::ct_other && env.par == L"other" ) env.res = true; } void ItemContent::is_empty(EzcEnv & env) { env.res = content_raw.empty(); } void ItemContent::file_type_is_none(EzcEnv & env) { env.res = file_type == WINIX_ITEM_FILETYPE_NONE; } /* * when we change file_type to string the we can return true only for * those images which can be able to show by a webbrowser * */ void ItemContent::file_type_is_image(EzcEnv & env) { env.res = file_type == WINIX_ITEM_FILETYPE_IMAGE; } /* * similar we can return true only for those videos which can be rendered by a webbrowser */ void ItemContent::file_type_is_video(EzcEnv & env) { env.res = file_type == WINIX_ITEM_FILETYPE_VIDEO; } void ItemContent::file_type_is_sound(EzcEnv & env) { //env.res = file_type == WINIX_ITEM_FILETYPE_; env.res = false; } void ItemContent::has_thumb(EzcEnv & env) { env.res = file_has_thumb; } } // namespace Winix