/* * 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 "request.h" #include "log.h" #include "plugin.h" #include "misc.h" #include "functions/functionbase.h" namespace Winix { Request::Request() { id = 0; config = 0; } void Request::fields() { field(L"", L"dirs", dir_tab); field(L"", L"is_item", is_item); field(L"", L"current_dir", &Request::current_dir); field(L"", L"last_item", &Request::last_item_wrapper); } void Request::SetConfig(Config * pconfig) { config = pconfig; } void Request::ClearOutputStreams() { size_t len = 0; out_main_stream.clear(); if( config ) len = config->ezc_out_streams_size; /* * clearing buffers and setting 'escape' flag to true * for all streams which were used in the map */ out_streams.ClearMap(); out_streams.ResizeTab(len); } void Request::Clear() { // id is never 0 if( ++id == 0 ) ++id; RemovePostFileTmp(post_file_tab); ClearOutputStreams(); if( function ) function->Clear(); post_tab.clear(); post_file_tab.clear(); cookie_tab.clear(); post_in.clear(); is_postin_used = false; method = unknown_method; headers_in.clear(); out_headers.clear(); out_cookies.clear(); env_request_method.clear(); env_request_uri.clear(); env_http_cookie.clear(); env_http_host.clear(); env_http_user_agent.clear(); env_http_accept_encoding.clear(); env_http_accept.clear(); env_fcgi_role.clear(); env_content_type.clear(); env_https.clear(); item_tab.clear(); item.Clear(); item.set_connector(nullptr); dir_tab.clear(); last_item = &item; is_item = false; function = nullptr; param_tab.clear(); anchor.clear(); send_bin_stream = false; send_main_stream = false; send_all_frames = false; send_frames.clear(); use_ezc_engine = false; serialize_models = false; accept_mime_types.clear(); container_type = ContainerType::container_raw; status = WINIX_ERR_OK; browser_msie = false; redirect_to.clear(); redirect_type = 303; x_sendfile.clear(); send_as_attachment = false; using_ssl = false; is_htmx_request = false; start_time = 0; start_date.Clear(); timespec_req_start.tv_sec = 0; timespec_req_start.tv_nsec = 0; timespec_req_stop.tv_sec = 0; timespec_req_stop.tv_nsec = 0; timespec_req_diff.tv_sec = 0; timespec_req_diff.tv_nsec = 0; timespec_ezc_engine_start.tv_sec = 0; timespec_ezc_engine_start.tv_nsec = 0; timespec_ezc_engine_stop.tv_sec = 0; timespec_ezc_engine_stop.tv_nsec = 0; subdomain.clear(); models.Clear(); out_bin_stream.clear(); gen_trim_white = false; gen_skip_new_line = false; gen_use_special_chars = false; ip = 0; ip_str.clear(); use_200_status_for_not_found_and_permission_denied = false; html_template.clear(); use_html_filter = false; } void Request::RequestStarts() { // clearing it is better to use at the end of a request // so starting is much faster clock_gettime(CLOCK_REALTIME, ×pec_req_start); timespec_req_stop = timespec_req_start; start_time = timespec_req_start.tv_sec; start_date = start_time; } void Request::RequestEnds() { clock_gettime(CLOCK_REALTIME, ×pec_req_stop); calculate_timespec_diff(timespec_req_start, timespec_req_stop, timespec_req_diff); } void Request::PrepareAnswerType() { CheckAcceptHeader(); CheckContainerParameter(); serialize_models = (container_type != ContainerType::container_raw); send_all_frames = (ParamValuep(config->request_all_frames_parameter) != nullptr); send_main_stream = (ParamValuep(config->request_main_stream_parameter) != nullptr); use_html_filter = config->html_filter; PrepareFrameNames(); if( container_type == ContainerType::container_raw && !send_all_frames && send_frames.empty() ) { send_main_stream = true; } use_ezc_engine = send_main_stream || send_all_frames || !send_frames.empty(); } void Request::CheckAcceptHeader() { if( !accept_mime_types.empty() ) { bool found = false; for(HeaderValue & h: accept_mime_types) { if( h.value == Header::text_html || h.value == Header::application_xhtml_xml || h.value == Header::text_all || h.value == Header::all_all) { container_type = ContainerType::container_raw; found = true; break; } else if( h.value == Header::application_json || h.value == Header::application_all ) { container_type = ContainerType::container_json; found = true; break; } else if( h.value == Header::application_xml ) { container_type = ContainerType::container_xml; found = true; break; } else if( h.value == Header::text_csv ) { container_type = ContainerType::container_csv; found = true; break; } } if( !found ) { Log * log = get_logger(); if( log ) { (*log) << log2 << "App: an unknown " << Header::accept << " headers: "; HeaderValue::log_values(accept_mime_types, *log); (*log) << " (skipping)" << logend; } } } } void Request::CheckContainerParameter() { std::wstring * container = ParamValuep(L"container"); if( container ) { // IMPROVEME do a plugin call here // if a plugin can consume this then don't check text/json/xml/csv and just return true if( *container == L"raw" ) { container_type = ContainerType::container_raw; } else if( *container == L"json" ) { container_type = ContainerType::container_json; } else if( *container == L"xml" ) { container_type = ContainerType::container_xml; } else if( *container == L"csv" ) { container_type = ContainerType::container_csv; } else { Log * log = get_logger(); if( log ) { (*log) << log2 << "App: an unknown container url parameter: " << *container << " (skipping)" << logend; } } } } void Request::PrepareFrameNames() { Config * config = get_config(); Log * log = get_logger(); if( config && log ) { const std::wstring & frame = ParamValue(config->request_frame_parameter); if( frame.size() <= config->request_frame_parameter_max_length ) { send_frames.clear(); slice_by(frame, ',', send_frames); std::sort(send_frames.begin(), send_frames.end()); auto frames_end = std::unique(send_frames.begin(), send_frames.end()); send_frames.erase(frames_end, send_frames.end()); if( send_frames.size() > config->request_frame_parameter_max_frames ) { send_frames.clear(); (*log) << log2 << "Request: the number of frames exceeds " << config->request_frame_parameter_max_frames << " (skipping frames)" << logend; } } else { (*log) << log2 << "Request: the length of the frame url parameter exceeds " << config->request_frame_parameter_max_length << " characters (skiping frames)" << logend; } } } bool Request::IsPostVar(const wchar_t * var) { PostTab::iterator p; p = post_tab.find(var); if( p == post_tab.end() ) return false; return true; } bool Request::IsPostVar(const std::wstring & var) { PostTab::iterator p; p = post_tab.find(var); if( p == post_tab.end() ) return false; return true; } const std::wstring & Request::PostVar(const wchar_t * var) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) return str_empty; return p->second; } const std::wstring & Request::PostVar(const std::wstring & var) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) return str_empty; return p->second; } bool Request::PostVar(const wchar_t * var, std::wstring & result) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) { result.clear(); return false; } result = p->second; return true; } bool Request::PostVar(const std::wstring & var, std::wstring & result) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) { result.clear(); return false; } result = p->second; return true; } std::wstring * Request::PostVarp(const wchar_t * var) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) return 0; return &p->second; } std::wstring * Request::PostVarp(const std::wstring & var) { PostTab::iterator p = post_tab.find(var); if( p == post_tab.end() ) return 0; return &p->second; } bool Request::AllPostVarEmpty() { PostTab::iterator i; for(i=post_tab.begin() ; i!=post_tab.end() ; ++i) if( !i->second.empty() ) return false; return true; } std::wstring * Request::ParamValuep(const wchar_t * param_name) { ParamTab::iterator i; for(i=param_tab.begin() ; i!=param_tab.end() ; ++i) { if( i->name == param_name ) return &i->value; } return nullptr; } std::wstring * Request::ParamValuep(const std::wstring & param_name) { ParamTab::iterator i; for(i=param_tab.begin() ; i!=param_tab.end() ; ++i) { if( i->name == param_name ) return &i->value; } return nullptr; } bool Request::IsParam(const wchar_t * param_name) { return ParamValuep(param_name) != nullptr; } bool Request::IsParam(const std::wstring & param_name) { return ParamValuep(param_name) != nullptr; } const std::wstring & Request::ParamValue(const wchar_t * param_name) { const std::wstring * val = ParamValuep(param_name); if( val != nullptr ) return *val; return str_empty; } const std::wstring & Request::ParamValue(const std::wstring & param_name) { const std::wstring * val = ParamValuep(param_name); if( val != nullptr ) return *val; return str_empty; } void Request::current_dir(morm::Wrapper & wrapper) { wrapper.model = dir_tab.back(); } void Request::last_item_wrapper(morm::Wrapper & wrapper) { wrapper.model = last_item; } bool Request::has_frame(const wchar_t * frame) { for(std::wstring & f: send_frames) { if( f == frame ) return true; } return false; } bool Request::has_frame(const std::wstring & frame) { for(std::wstring & f: send_frames) { if( f == frame ) return true; } return false; } } // namespace Winix