/* * This file is a part of Winix * and is distributed under the 2-Clause BSD licence. * Author: Tomasz Sowa */ /* * Copyright (c) 2008-2022, 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 "functionparser.h" #include "core/log.h" #include "functions.h" #include "utf8/utf8.h" #include "models/item.h" namespace Winix { FunctionParser::FunctionParser() { } bool FunctionParser::Parse(Cur * pcur, Db * pdb, Functions * pfunctions, System * psystem) { db = pdb; cur = pcur; system = psystem; functions = pfunctions; last_dir = nullptr; path = cur->request->env_request_uri.c_str(); status = true; // IMPROVEME put WINIX_URL_MAX_SIZE to the config if( cur->request->env_request_uri.size() <= WINIX_URL_MAX_SIZE ) { if( !cur->request->dir_tab.empty() ) { last_dir = cur->request->dir_tab.back(); PrintCurrentDirs(); if( ParseDirsItemFunction() ) { ParseParams(); ParseAnchor(); } } else { status = false; cur->request->http_status = Header::status_500_internal_server_error; log << log1 << "FP: no root directory provided in the request dir_tab" << logend; } } else { status = false; cur->request->http_status = Header::status_414_uri_too_long; log << log1 << "FP: the URL is too long: " << cur->request->env_request_uri.size() << logend; } return status; } /* * there should be at least the root directory */ void FunctionParser::PrintCurrentDirs() { std::vector::iterator i = cur->request->dir_tab.begin(); for( ; i != cur->request->dir_tab.end() ; ++i) { LogDir(*i); } } bool FunctionParser::ParseDirsItemFunction() { ReadName(); while( IsDir() ) { AddDir(); ReadName(); } if( name.empty() ) { /* * no more names to analyze, return now */ return true; } if( CheckAddFunction() ) { /* * the name is a function so return now, we do not parse item */ return true; } if( !CheckAddItem() ) { /* * the name is not an item so a 404 error was set, return now */ return false; } /* * read next name and try if it is a function */ ReadName(); if( !name.empty() ) { if( !CheckAddFunction() ) { log << log3 << "FP: unknown function: " << name << logend; cur->request->http_status = Header::status_404_not_found; status = false; return false; } } return true; } bool FunctionParser::IsDir() { // directory names should not be empty if( name.empty() || !last_dir ) return false; last_dir = system->dirs.GetDir(name, last_dir->id); return last_dir != nullptr; } bool FunctionParser::CheckAddItem() { // cur->request->dir_tab has at least one element long parent_id = cur->request->dir_tab.back()->id; morm::Finder finder(model_connector); finder. select(). where(). eq(L"parent_id", parent_id). eq(L"url", name). get(cur->request->item); if( cur->request->item.found() ) { log << log3 << "FP: Item: id: " << cur->request->item.id << ", url: " << cur->request->item.url << logend; cur->request->last_item = &cur->request->item; cur->request->is_item = true; } else { log << log3 << "FP: No Item: url: " << name << logend; cur->request->http_status = Header::status_404_not_found; status = false; } return cur->request->item.found(); } bool FunctionParser::CheckAddFunction() { cur->request->function = functions->Find(name); if( cur->request->function ) { log << log3 << "FP: Function: " << cur->request->function->fun.url << logend; return true; } return false; } void FunctionParser::LogDir(Item * dir) { log << log3 << "FP: Directory: "; if( dir->parent_id == -1 ) log << "(root)" << logend; else log << dir->url << logend; } void FunctionParser::AddDir() { cur->request->dir_tab.push_back(last_dir); LogDir(last_dir); cur->request->last_item = cur->request->dir_tab.back(); } void FunctionParser::ParseParams() { SkipSlashes(); if( *path == '#' ) { // there are not any parameters return; } if( *path == '?' ) ParseOrdinaryParams(); else ParseWinixParams(); } void FunctionParser::ParseOrdinaryParams() { if( *path == '?' ) path += 1; do { ReadOrdinaryParName(); if( name.empty() ) break; ReadOrdinaryParValue(); if( *path == '&' ) path += 1; AddParam(); } while( true ); } void FunctionParser::ParseWinixParams() { do { SkipSlashes(); ReadWinixParName(); if( name.empty() ) break; ReadWinixParValue(); AddParam(); } while( true ); } void FunctionParser::AddParam() { param.name = name; param.value = value; cur->request->param_tab.push_back(param); log << log3 << "FP: Param: name=" << param.name; if( !param.value.empty() ) log << ", value=" << param.value; log << logend; } void FunctionParser::ParseAnchor() { if( *path == '#' ) { path += 1; name_ascii.clear(); while( *path ) name_ascii += GetChar(); pt::utf8_to_wide(name_ascii, cur->request->anchor); if( !cur->request->anchor.empty() ) log << log3 << "FP: anchor: " << cur->request->anchor << logend; } else if( *path != 0 ) { cur->request->http_status = Header::status_400_bad_request; status = false; } } void FunctionParser::SkipSlashes() { while( *path == '/' ) path += 1; } int FunctionParser::FromHex(int c) { if( c>='0' && c<='9' ) { return c - '0'; } else if( c>='a' && c<='f' ) { return c - 'a' + 10; } else if( c>='A' && c<='F' ) { return c - 'A' + 10; } else { cur->request->http_status = Header::status_400_bad_request; status = false; } return 0; } int FunctionParser::GetChar() { int c; if( *path == 0 ) return 0; if( *path == '%' && *(path+1)!=0 && *(path+2)!=0 ) { c = (FromHex(*(path+1)) << 4) + FromHex(*(path+2)); if( c == 0 && status ) { cur->request->http_status = Header::status_400_bad_request; status = false; } path += 3; } else if( *path == '+' ) { c = ' '; path += 1; } else { c = *path; path += 1; } return c; } void FunctionParser::ReadName() { SkipSlashes(); name_ascii.clear(); while( *path && *path!='/' && *path!='?' && *path!='#' ) name_ascii += GetChar(); pt::utf8_to_wide(name_ascii, name); } void FunctionParser::ReadOrdinaryParName() { name_ascii.clear(); while( *path && *path!='=' && *path!='&' && *path!='#' ) name_ascii += GetChar(); pt::utf8_to_wide(name_ascii, name); } void FunctionParser::ReadOrdinaryParValue() { value_ascii.clear(); if( *path=='=' ) path += 1; while( *path && *path!='&' && *path!='#' ) value_ascii += GetChar(); pt::utf8_to_wide(value_ascii, value); } void FunctionParser::ReadWinixParName() { name_ascii.clear(); while( *path && *path!='/' && *path!=':' && *path!='#' ) name_ascii += GetChar(); pt::utf8_to_wide(name_ascii, name); } void FunctionParser::ReadWinixParValue() { value_ascii.clear(); if( *path == ':' ) path += 1; while( *path && *path!='/' && *path!='#' ) value_ascii += GetChar(); pt::utf8_to_wide(value_ascii, value); } } // namespace Winix