mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-04-06 20:11:49 +02:00
Added the web module from rcpp_framework, and a test attempt at binding HTMLBuilder. The module is not in the build yet.
This commit is contained in:
parent
36355600e6
commit
48d65fd93d
25
modules/web/SCsub
Normal file
25
modules/web/SCsub
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
Import('env')
|
||||||
|
|
||||||
|
module_env = env.Clone()
|
||||||
|
|
||||||
|
sources = [
|
||||||
|
|
||||||
|
"register_types.cpp",
|
||||||
|
|
||||||
|
"html/html_builder_bind.cpp",
|
||||||
|
]
|
||||||
|
|
||||||
|
if ARGUMENTS.get('custom_modules_shared', 'no') == 'yes':
|
||||||
|
# Shared lib compilation
|
||||||
|
module_env.Append(CCFLAGS=['-fPIC'])
|
||||||
|
module_env['LIBS'] = []
|
||||||
|
shared_lib = module_env.SharedLibrary(target='#bin/web', source=sources)
|
||||||
|
shared_lib_shim = shared_lib[0].name.rsplit('.', 1)[0]
|
||||||
|
env.Append(LIBS=[shared_lib_shim])
|
||||||
|
env.Append(LIBPATH=['#bin'])
|
||||||
|
else:
|
||||||
|
# Static compilation
|
||||||
|
module_env.add_source_files(env.modules_sources, sources)
|
||||||
|
|
22
modules/web/config.py
Normal file
22
modules/web/config.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
|
||||||
|
def can_build(env, platform):
|
||||||
|
#return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def configure(env):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def get_doc_classes():
|
||||||
|
return [
|
||||||
|
#"WorldArea",
|
||||||
|
|
||||||
|
#"HTMLBuilder",
|
||||||
|
#"HTMLTag",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_doc_path():
|
||||||
|
return "doc_classes"
|
135
modules/web/file_cache.cpp
Normal file
135
modules/web/file_cache.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include "file_cache.h"
|
||||||
|
|
||||||
|
#include "core/os/directory.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
void FileCache::wwwroot_register_file(const String &file_path) {
|
||||||
|
registered_files.insert(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileCache::wwwroot_deregister_file(const String &file_path) {
|
||||||
|
registered_files.erase(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileCache::wwwroot_has_file(const String &file_path) {
|
||||||
|
return registered_files.find(file_path) != registered_files.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileCache::wwwroot_refresh_cache() {
|
||||||
|
_lock.write_lock();
|
||||||
|
|
||||||
|
registered_files.clear();
|
||||||
|
|
||||||
|
wwwroot.path_clean_end_slash();
|
||||||
|
|
||||||
|
wwwroot_evaluate_dir(wwwroot.c_str());
|
||||||
|
|
||||||
|
_lock.write_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileCache::wwwroot_evaluate_dir(const char *path, const bool should_exist) {
|
||||||
|
Ref<Directory> dir;
|
||||||
|
dir.instance();
|
||||||
|
|
||||||
|
ERR_FAIL_COND_MSG(dir->open_dir(path) != OK, "Error opening wwwroot! folder: " + String(path));
|
||||||
|
|
||||||
|
while (dir->next()) {
|
||||||
|
if (dir->current_is_file()) {
|
||||||
|
String np = dir->current_get_path_cstr();
|
||||||
|
|
||||||
|
np = np.substr(wwwroot.size(), np.size() - wwwroot.size());
|
||||||
|
|
||||||
|
registered_files.insert(np);
|
||||||
|
} else {
|
||||||
|
wwwroot_evaluate_dir(dir->current_get_path_cstr());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dir->close_dir();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileCache::get_cached_body(const String &path, String *body) {
|
||||||
|
//TODO ERROR MACRO body == null
|
||||||
|
|
||||||
|
_lock.read_lock();
|
||||||
|
CacheEntry *e = cache_map[path];
|
||||||
|
_lock.read_unlock();
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t current_timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
int64_t diff = current_timestamp - e->timestamp;
|
||||||
|
|
||||||
|
if (diff > cache_invalidation_time) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
body->append_str(e->body);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileCache::set_cached_body(const String &path, const String &body) {
|
||||||
|
_lock.write_lock();
|
||||||
|
|
||||||
|
CacheEntry *e = cache_map[path];
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
e = new CacheEntry();
|
||||||
|
cache_map[path] = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t current_timestamp = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||||
|
|
||||||
|
e->timestamp = current_timestamp;
|
||||||
|
e->body = body;
|
||||||
|
|
||||||
|
_lock.write_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileCache::clear() {
|
||||||
|
_lock.write_lock();
|
||||||
|
|
||||||
|
registered_files.clear();
|
||||||
|
|
||||||
|
for (std::map<String, CacheEntry *>::iterator E = cache_map.begin(); E != cache_map.end(); E++) {
|
||||||
|
CacheEntry * ce = E->second;
|
||||||
|
|
||||||
|
if (ce) {
|
||||||
|
delete ce;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_map.clear();
|
||||||
|
|
||||||
|
_lock.write_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCache::FileCache(bool singleton) {
|
||||||
|
if (singleton) {
|
||||||
|
if (_instance) {
|
||||||
|
printf("FileCache: Filecache instance is set as singleton, but an another FileCache instance is already set up as singleton! Ignoring setting!\n");
|
||||||
|
} else {
|
||||||
|
_instance = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cache_invalidation_time = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCache::~FileCache() {
|
||||||
|
registered_files.clear();
|
||||||
|
|
||||||
|
if (_instance == this)
|
||||||
|
_instance = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCache *FileCache::get_singleton() {
|
||||||
|
return _instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileCache *FileCache::_instance = nullptr;
|
59
modules/web/file_cache.h
Normal file
59
modules/web/file_cache.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef FILE_CACHE_H
|
||||||
|
#define FILE_CACHE_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <map>
|
||||||
|
#include <chrono>
|
||||||
|
#include "core/threading/rw_lock.h"
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
|
||||||
|
class FileCache : public Object {
|
||||||
|
RCPP_OBJECT(FileCache, Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
String wwwroot;
|
||||||
|
int cache_invalidation_time;
|
||||||
|
|
||||||
|
//Note: file path should be the url you want to access the file with, inculding lead slash
|
||||||
|
//e.g. http://127.0.0.1/a/b/d.jpg -> /a/b/d.jpg
|
||||||
|
void wwwroot_register_file(const String &file_path);
|
||||||
|
void wwwroot_deregister_file(const String &file_path);
|
||||||
|
bool wwwroot_has_file(const String &file_path);
|
||||||
|
void wwwroot_refresh_cache();
|
||||||
|
void wwwroot_evaluate_dir(const char *path, const bool should_exist = true);
|
||||||
|
|
||||||
|
bool get_cached_body(const String &path, String *body);
|
||||||
|
void set_cached_body(const String &path, const String &body);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
FileCache(bool singleton = false);
|
||||||
|
virtual ~FileCache();
|
||||||
|
|
||||||
|
static FileCache *get_singleton();
|
||||||
|
|
||||||
|
std::set<String> registered_files;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
struct CacheEntry {
|
||||||
|
int64_t timestamp;
|
||||||
|
String body;
|
||||||
|
|
||||||
|
CacheEntry() {
|
||||||
|
timestamp = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RWLock _lock;
|
||||||
|
std::map<String, CacheEntry *> cache_map;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static FileCache *_instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
540
modules/web/html/bbcode_parser.cpp
Normal file
540
modules/web/html/bbcode_parser.cpp
Normal file
@ -0,0 +1,540 @@
|
|||||||
|
#include "bbcode_parser.h"
|
||||||
|
#include "core/error_macros.h"
|
||||||
|
|
||||||
|
bool BBCodeParserAttribute::match_attrib(const String &attrib) {
|
||||||
|
return attribute == attrib;
|
||||||
|
}
|
||||||
|
bool BBCodeParserAttribute::match_data(const String &d) {
|
||||||
|
return data == d;
|
||||||
|
}
|
||||||
|
bool BBCodeParserAttribute::match_data(const Vector<String> &d) {
|
||||||
|
// todo
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool BBCodeParserAttribute::contains_data(const String &d) {
|
||||||
|
return data.find(d) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String BBCodeParserAttribute::to_string() {
|
||||||
|
if (single) {
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.find('"' == -1)) {
|
||||||
|
return attribute + "=\"" + data + "\"";
|
||||||
|
} else {
|
||||||
|
return attribute + "=\'" + data + "\'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BBCodeParserAttribute::print() {
|
||||||
|
to_string().print();
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserAttribute::BBCodeParserAttribute() {
|
||||||
|
single = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserAttribute::~BBCodeParserAttribute() {
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserTag *BBCodeParserTag::get_first(const String &t) {
|
||||||
|
if (tag == t) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
BBCodeParserTag *ht = tags[i]->get_first(t);
|
||||||
|
|
||||||
|
if (ht) {
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserTag *BBCodeParserTag::get_first(const String &t, const String &attrib, const String &val) {
|
||||||
|
if (tag == t) {
|
||||||
|
if (has_attribute(attrib, val)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
BBCodeParserTag *ht = tags[i]->get_first(t, attrib, val);
|
||||||
|
|
||||||
|
if (ht) {
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
String BBCodeParserTag::get_attribute_value(const String &attrib) {
|
||||||
|
BBCodeParserAttribute *a = get_attribute(attrib);
|
||||||
|
|
||||||
|
if (a) {
|
||||||
|
return a->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserAttribute *BBCodeParserTag::get_attribute(const String &attrib) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
BBCodeParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BBCodeParserTag::has_attribute(const String &attrib) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
BBCodeParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserAttribute *BBCodeParserTag::get_attribute(const String &attrib, const String &contains_val) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
BBCodeParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib) && a->contains_data(contains_val)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BBCodeParserTag::has_attribute(const String &attrib, const String &contains_val) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
BBCodeParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib) && a->contains_data(contains_val)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BBCodeParserTag::process() {
|
||||||
|
if (type != BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_FAIL_COND(data[0] != '[');
|
||||||
|
ERR_FAIL_COND(data[data.size() - 1] != ']');
|
||||||
|
|
||||||
|
int start_index = 1;
|
||||||
|
if (data[1] == '/') {
|
||||||
|
++start_index;
|
||||||
|
|
||||||
|
type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_CLOSING_TAG;
|
||||||
|
} else {
|
||||||
|
String tag_text;
|
||||||
|
|
||||||
|
if (data[data.size() - 2] == '/') {
|
||||||
|
// will catch all that looks like <br/>
|
||||||
|
// tags that look like <br> will be caught later in a post process, in a way
|
||||||
|
// which also tries to catch erroneously not closed tags that supposed to be closed
|
||||||
|
type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_SELF_CLOSING_TAG;
|
||||||
|
|
||||||
|
tag_text = data.substr(1, data.size() - 3);
|
||||||
|
} else {
|
||||||
|
type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_OPENING_TAG;
|
||||||
|
|
||||||
|
tag_text = data.substr(1, data.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fspc_index = tag_text.find(' ');
|
||||||
|
|
||||||
|
if (fspc_index == -1) {
|
||||||
|
// no args
|
||||||
|
|
||||||
|
int feq_ind = tag_text.find('=');
|
||||||
|
if (feq_ind == -1) {
|
||||||
|
tag = tag_text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Tag is like: [color=white]
|
||||||
|
//tag will be like color
|
||||||
|
tag = tag_text.substr(0, feq_ind);
|
||||||
|
|
||||||
|
//Add color=white as argument
|
||||||
|
parse_args(tag_text);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab the tag itself
|
||||||
|
tag = tag_text.substr(0, fspc_index);
|
||||||
|
|
||||||
|
if (fspc_index + 1 == tag_text.size()) {
|
||||||
|
// no args, but had a space like <br />
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String args = tag_text.substr(fspc_index + 1, tag_text.size() - fspc_index - 1);
|
||||||
|
parse_args(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tag_end_index = data.find(' ', start_index);
|
||||||
|
|
||||||
|
if (tag_end_index == -1) {
|
||||||
|
// simple tag
|
||||||
|
tag = data.substr(start_index, data.size() - start_index - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BBCodeParserTag::parse_args(const String &args) {
|
||||||
|
attributes.clear();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (i < args.size()) {
|
||||||
|
if (args[i] == ' ') {
|
||||||
|
//"trim"
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equals_index = args.find('=', i);
|
||||||
|
|
||||||
|
BBCodeParserAttribute *a = new BBCodeParserAttribute();
|
||||||
|
|
||||||
|
if (equals_index == -1) {
|
||||||
|
a->attribute = args.substr(i, args.size() - i);
|
||||||
|
a->single = true;
|
||||||
|
attributes.push_back(a);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->attribute = args.substr(i, equals_index - i);
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// a.trim();
|
||||||
|
|
||||||
|
int next_char_index = equals_index + 1;
|
||||||
|
|
||||||
|
if (next_char_index >= args.size()) {
|
||||||
|
// an attribute looks like this "... attrib="
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip spaces
|
||||||
|
while (args[next_char_index] == ' ') {
|
||||||
|
++next_char_index;
|
||||||
|
|
||||||
|
if (next_char_index >= args.size()) {
|
||||||
|
// an attribute looks like this "... attrib= "
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = args[next_char_index];
|
||||||
|
char find_char = ' ';
|
||||||
|
|
||||||
|
if (c == '"' || c == '\'') {
|
||||||
|
++next_char_index;
|
||||||
|
find_char = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int end_index = args.find(find_char, next_char_index);
|
||||||
|
|
||||||
|
if (end_index == -1) {
|
||||||
|
// missing closing ' or " if c is ' or "
|
||||||
|
// else missing parameter
|
||||||
|
|
||||||
|
a->data = args.substr(next_char_index, args.size() - next_char_index);
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->data = args.substr(next_char_index, end_index - next_char_index);
|
||||||
|
attributes.push_back(a);
|
||||||
|
|
||||||
|
i = end_index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String BBCodeParserTag::to_string(const int level) {
|
||||||
|
String s;
|
||||||
|
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
|
||||||
|
if (type == BBCODE_PARSER_TAG_TYPE_CONTENT) {
|
||||||
|
s += data + "\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!CONTENT TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == BBCODE_PARSER_TAG_TYPE_OPENING_TAG) {
|
||||||
|
int ln = level + 1;
|
||||||
|
|
||||||
|
s += "[" + tag;
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
s += " " + attributes[i]->to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "]\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
|
||||||
|
s += "[/" + tag + "]\n";
|
||||||
|
} else if (type == BBCODE_PARSER_TAG_TYPE_CLOSING_TAG) {
|
||||||
|
// BBCodeParserTag should handle this automatically
|
||||||
|
// it's here for debugging purposes though
|
||||||
|
s += "[/" + tag + "(!)]";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!CLOSING TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == BBCODE_PARSER_TAG_TYPE_SELF_CLOSING_TAG) {
|
||||||
|
s += "[" + tag;
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
s += " " + attributes[i]->to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "/]\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == BBCODE_PARSER_TAG_TYPE_NONE) {
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level) + "\n";
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
void BBCodeParserTag::print() {
|
||||||
|
to_string().print();
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserTag::BBCodeParserTag() {
|
||||||
|
type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserTag::~BBCodeParserTag() {
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
delete tags[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
delete attributes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BBCodeParser::parse(const String &data) {
|
||||||
|
Vector<BBCodeParserTag *> tags;
|
||||||
|
|
||||||
|
// split into tags
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (data[i] == '[') {
|
||||||
|
// tag
|
||||||
|
for (int j = i + 1; j < data.size(); ++j) {
|
||||||
|
if (data[j] == ']') {
|
||||||
|
BBCodeParserTag *t = new BBCodeParserTag();
|
||||||
|
|
||||||
|
t->data = data.substr(i, j - i + 1);
|
||||||
|
t->process();
|
||||||
|
|
||||||
|
tags.push_back(t);
|
||||||
|
|
||||||
|
i = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// content
|
||||||
|
|
||||||
|
for (int j = i + 1; j < data.size(); ++j) {
|
||||||
|
if (data[j] == '[') {
|
||||||
|
BBCodeParserTag *t = new BBCodeParserTag();
|
||||||
|
|
||||||
|
t->data = data.substr(i, j - i);
|
||||||
|
t->type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_CONTENT;
|
||||||
|
|
||||||
|
tags.push_back(t);
|
||||||
|
|
||||||
|
i = j - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = new BBCodeParserTag();
|
||||||
|
|
||||||
|
// process tags into hierarchical order
|
||||||
|
Vector<BBCodeParserTag *> tag_stack;
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
BBCodeParserTag *t = tags[i];
|
||||||
|
|
||||||
|
if (t == nullptr) {
|
||||||
|
RLOG_ERR("BBCodeParser::parse: t == nullptr!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_NONE) {
|
||||||
|
RLOG_ERR("BBCodeParser::parse: t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_NONE!");
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_OPENING_TAG) {
|
||||||
|
tag_stack.push_back(t);
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_SELF_CLOSING_TAG) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_CONTENT) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_CLOSING_TAG) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
|
||||||
|
// ill-formed html
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find it's pair
|
||||||
|
int tag_index = 0;
|
||||||
|
for (int j = tag_stack.size() - 1; j > 0; --j) {
|
||||||
|
BBCodeParserTag *ts = tag_stack[j];
|
||||||
|
|
||||||
|
// we sould only have opening tags on the stack
|
||||||
|
if (ts->tag == t->tag) {
|
||||||
|
// found
|
||||||
|
tag_index = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParserTag *opening_tag = tag_stack[tag_index];
|
||||||
|
|
||||||
|
// mark everything else that we found before finding the opening tag as self closing, and add them to out opening tag
|
||||||
|
// If the html is ill formed, it just grabs everything from the tag stack
|
||||||
|
for (int j = tag_index + 1; j < tag_stack.size(); ++j) {
|
||||||
|
BBCodeParserTag *ts = tag_stack[j];
|
||||||
|
|
||||||
|
ts->type = BBCodeParserTag::BBCODE_PARSER_TAG_TYPE_SELF_CLOSING_TAG;
|
||||||
|
opening_tag->tags.push_back(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_stack.resize(tag_index);
|
||||||
|
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(opening_tag);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(opening_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add everything remaining on the stack to root
|
||||||
|
for (int i = 0; i < tag_stack.size(); ++i) {
|
||||||
|
root->tags.push_back(tag_stack[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
BBCodeParserTag *t = tags[i];
|
||||||
|
|
||||||
|
if (t != nullptr) {
|
||||||
|
RLOG_ERR("BBCodeParser::parse(const String &data): tag was not processed!\n");
|
||||||
|
t->print();
|
||||||
|
|
||||||
|
delete t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String BBCodeParser::to_string() {
|
||||||
|
if (!root) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return root->to_string();
|
||||||
|
}
|
||||||
|
void BBCodeParser::print() {
|
||||||
|
if (root) {
|
||||||
|
root->print();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParser::BBCodeParser() {
|
||||||
|
root = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
BBCodeParser::~BBCodeParser() {
|
||||||
|
if (root) {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
}
|
78
modules/web/html/bbcode_parser.h
Normal file
78
modules/web/html/bbcode_parser.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef BBCODE_PARSER_H
|
||||||
|
#define BBCODE_PARSER_H
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
class BBCodeParserAttribute {
|
||||||
|
public:
|
||||||
|
String attribute;
|
||||||
|
String data;
|
||||||
|
bool single;
|
||||||
|
|
||||||
|
bool match_attrib(const String &attrib);
|
||||||
|
bool match_data(const String &d);
|
||||||
|
bool match_data(const Vector<String> &d);
|
||||||
|
bool contains_data(const String &d);
|
||||||
|
|
||||||
|
String to_string();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
BBCodeParserAttribute();
|
||||||
|
virtual ~BBCodeParserAttribute();
|
||||||
|
};
|
||||||
|
|
||||||
|
class BBCodeParserTag {
|
||||||
|
public:
|
||||||
|
enum BBCodeParserTagType {
|
||||||
|
BBCODE_PARSER_TAG_TYPE_NONE = 0,
|
||||||
|
BBCODE_PARSER_TAG_TYPE_OPENING_TAG,
|
||||||
|
BBCODE_PARSER_TAG_TYPE_CLOSING_TAG,
|
||||||
|
BBCODE_PARSER_TAG_TYPE_SELF_CLOSING_TAG,
|
||||||
|
BBCODE_PARSER_TAG_TYPE_CONTENT
|
||||||
|
};
|
||||||
|
|
||||||
|
int type;
|
||||||
|
|
||||||
|
String tag;
|
||||||
|
String data;
|
||||||
|
|
||||||
|
Vector<BBCodeParserTag*> tags;
|
||||||
|
Vector<BBCodeParserAttribute*> attributes;
|
||||||
|
|
||||||
|
BBCodeParserTag *get_first(const String &t);
|
||||||
|
BBCodeParserTag *get_first(const String &t, const String &attrib, const String &val);
|
||||||
|
|
||||||
|
String get_attribute_value(const String &attrib);
|
||||||
|
|
||||||
|
BBCodeParserAttribute *get_attribute(const String &attrib);
|
||||||
|
bool has_attribute(const String &attrib);
|
||||||
|
|
||||||
|
BBCodeParserAttribute *get_attribute(const String &attrib, const String &contains_val);
|
||||||
|
bool has_attribute(const String &attrib, const String &contains_val);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
void parse_args(const String &args);
|
||||||
|
|
||||||
|
String to_string(const int level = 0);
|
||||||
|
void print();
|
||||||
|
|
||||||
|
BBCodeParserTag();
|
||||||
|
virtual ~BBCodeParserTag();
|
||||||
|
};
|
||||||
|
|
||||||
|
class BBCodeParser {
|
||||||
|
public:
|
||||||
|
BBCodeParserTag *root;
|
||||||
|
|
||||||
|
void parse(const String &data);
|
||||||
|
//void parse_tag(const String &data, const int index);
|
||||||
|
|
||||||
|
String to_string();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
BBCodeParser();
|
||||||
|
virtual ~BBCodeParser();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
535
modules/web/html/form_validator.cpp
Normal file
535
modules/web/html/form_validator.cpp
Normal file
@ -0,0 +1,535 @@
|
|||||||
|
#include "form_validator.h"
|
||||||
|
|
||||||
|
#include "web/http/request.h"
|
||||||
|
|
||||||
|
//FormFieldEntry
|
||||||
|
|
||||||
|
bool FormFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormFieldEntry::FormFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
FormFieldEntry::~FormFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormExistsFieldEntry
|
||||||
|
|
||||||
|
bool FormExistsFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data == "") {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_exists_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormExistsFieldEntry::FormExistsFieldEntry() {
|
||||||
|
not_exists_error = " field need to exists!";
|
||||||
|
}
|
||||||
|
FormExistsFieldEntry::~FormExistsFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormIntFieldEntry
|
||||||
|
|
||||||
|
bool FormIntFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
//https://stackoverflow.com/questions/2844817/how-do-i-check-if-a-c-string-is-an-int
|
||||||
|
|
||||||
|
if (data.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (((!isdigit(data[0])) && (data[0] != '-') && (data[0] != '+'))) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_int_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *p;
|
||||||
|
strtol(data.c_str(), &p, 10);
|
||||||
|
|
||||||
|
bool is_int = (*p == 0);
|
||||||
|
|
||||||
|
if (!is_int) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_int_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormIntFieldEntry::FormIntFieldEntry() {
|
||||||
|
not_int_error = " needs to be an integer.";
|
||||||
|
}
|
||||||
|
|
||||||
|
FormIntFieldEntry::~FormIntFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormFloatFieldEntry
|
||||||
|
|
||||||
|
bool FormFloatFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data.empty()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//from https://stackoverflow.com/questions/447206/c-isfloat-function
|
||||||
|
char *ptr;
|
||||||
|
strtof(data.c_str(), &ptr);
|
||||||
|
bool is_float = (*ptr) == '\0';
|
||||||
|
|
||||||
|
if (!is_float) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_float_error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_float;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormFloatFieldEntry::FormFloatFieldEntry() {
|
||||||
|
not_float_error = " needs to be an floating point number.";
|
||||||
|
}
|
||||||
|
FormFloatFieldEntry::~FormFloatFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormAlphaFieldEntry
|
||||||
|
|
||||||
|
bool FormAlphaFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (!isalpha(data[i])) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_alpha_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormAlphaFieldEntry::FormAlphaFieldEntry() {
|
||||||
|
not_alpha_error = " needs to only contain caharcters.";
|
||||||
|
}
|
||||||
|
FormAlphaFieldEntry::~FormAlphaFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormAlphaNumericFieldEntry
|
||||||
|
|
||||||
|
bool FormAlphaNumericFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (!isalnum(data[i])) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + not_alpha_numeric_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormAlphaNumericFieldEntry::FormAlphaNumericFieldEntry() {
|
||||||
|
not_alpha_numeric_error = " needs to only contain caharcters of numbers.";
|
||||||
|
}
|
||||||
|
FormAlphaNumericFieldEntry::~FormAlphaNumericFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormNeedsLowercaseCharacterFieldEntry
|
||||||
|
|
||||||
|
bool FormNeedsLowercaseCharacterFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (islower(data[i])) {
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_have_lowercase_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormNeedsLowercaseCharacterFieldEntry::FormNeedsLowercaseCharacterFieldEntry() {
|
||||||
|
does_not_have_lowercase_error = " needs at least one lowercase character!";
|
||||||
|
}
|
||||||
|
FormNeedsLowercaseCharacterFieldEntry::~FormNeedsLowercaseCharacterFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormNeedsUppercaseCharacterFieldEntry
|
||||||
|
|
||||||
|
bool FormNeedsUppercaseCharacterFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (isupper(data[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_have_uppercase_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormNeedsUppercaseCharacterFieldEntry::FormNeedsUppercaseCharacterFieldEntry() {
|
||||||
|
does_not_have_uppercase_error = " needs at least one uppercase character!";
|
||||||
|
}
|
||||||
|
FormNeedsUppercaseCharacterFieldEntry::~FormNeedsUppercaseCharacterFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormNeedsOtherCharacterFieldEntry
|
||||||
|
|
||||||
|
bool FormNeedsOtherCharacterFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (!isalnum(data[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_have_other_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormNeedsOtherCharacterFieldEntry::FormNeedsOtherCharacterFieldEntry() {
|
||||||
|
does_not_have_other_error = " needs at least one other character!";
|
||||||
|
}
|
||||||
|
FormNeedsOtherCharacterFieldEntry::~FormNeedsOtherCharacterFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormMinimumLengthFieldEntry
|
||||||
|
|
||||||
|
bool FormMinimumLengthFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data.size() < min_length) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_have_min_length_errorf + min_length + does_not_have_min_length_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormMinimumLengthFieldEntry::FormMinimumLengthFieldEntry() {
|
||||||
|
does_not_have_min_length_errorf = " needs at least ";
|
||||||
|
does_not_have_min_length_errors = " characters!";
|
||||||
|
|
||||||
|
min_length = 5;
|
||||||
|
}
|
||||||
|
FormMinimumLengthFieldEntry::~FormMinimumLengthFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormMaximumLengthFieldEntry
|
||||||
|
|
||||||
|
bool FormMaximumLengthFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data.size() > max_length) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_have_max_length_errorf + max_length + does_not_have_max_length_errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormMaximumLengthFieldEntry::FormMaximumLengthFieldEntry() {
|
||||||
|
does_not_have_max_length_errorf = " needs at most ";
|
||||||
|
does_not_have_max_length_errors = " characters!";
|
||||||
|
|
||||||
|
max_length = 10;
|
||||||
|
}
|
||||||
|
FormMaximumLengthFieldEntry::~FormMaximumLengthFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormEmailFieldEntry
|
||||||
|
|
||||||
|
bool FormEmailFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data.size() == 0) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isalpha(data[0])) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dot_pos = -1;
|
||||||
|
int at_pos = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (data[i] == '.') {
|
||||||
|
if (dot_pos != -1) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
dot_pos = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dot_pos == -1) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (data[i] == '@') {
|
||||||
|
if (at_pos != -1) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
at_pos = i;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (at_pos == -1) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (i == at_pos || i == dot_pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isalnum(data[i])) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + email_format_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormEmailFieldEntry::FormEmailFieldEntry() {
|
||||||
|
email_format_error = " is invalid!";
|
||||||
|
}
|
||||||
|
FormEmailFieldEntry::~FormEmailFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormNeedToMatchOtherFieldEntry
|
||||||
|
|
||||||
|
bool FormNeedToMatchOtherFieldEntry::validate(Request *request, const FormField *field, const String &data, Vector<String> *errors) {
|
||||||
|
if (data != request->get_parameter(other_field)) {
|
||||||
|
if (errors) {
|
||||||
|
errors->push_back(field->human_name + does_not_match_error + field->name + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormNeedToMatchOtherFieldEntry::FormNeedToMatchOtherFieldEntry() {
|
||||||
|
does_not_match_error = " does not match ";
|
||||||
|
}
|
||||||
|
FormNeedToMatchOtherFieldEntry::~FormNeedToMatchOtherFieldEntry() {
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormField
|
||||||
|
|
||||||
|
FormField *FormField::need_to_exist() {
|
||||||
|
add_entry(new FormExistsFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_be_int() {
|
||||||
|
add_entry(new FormIntFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_be_float() {
|
||||||
|
add_entry(new FormFloatFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_be_alpha() {
|
||||||
|
add_entry(new FormAlphaFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_be_alpha_numeric() {
|
||||||
|
add_entry(new FormAlphaNumericFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_have_lowercase_character() {
|
||||||
|
add_entry(new FormNeedsLowercaseCharacterFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_have_uppercase_character() {
|
||||||
|
add_entry(new FormNeedsUppercaseCharacterFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_have_other_character() {
|
||||||
|
add_entry(new FormNeedsOtherCharacterFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_minimum_length(const int min_length) {
|
||||||
|
FormMinimumLengthFieldEntry *f = new FormMinimumLengthFieldEntry();
|
||||||
|
f->min_length = min_length;
|
||||||
|
add_entry(f);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_maximum_length(const int max_length) {
|
||||||
|
FormMaximumLengthFieldEntry *f = new FormMaximumLengthFieldEntry();
|
||||||
|
f->max_length = max_length;
|
||||||
|
add_entry(f);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
FormField *FormField::need_to_be_email() {
|
||||||
|
add_entry(new FormEmailFieldEntry());
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormField *FormField::need_to_match(const String &other) {
|
||||||
|
FormNeedToMatchOtherFieldEntry *f = new FormNeedToMatchOtherFieldEntry();
|
||||||
|
f->other_field = other;
|
||||||
|
add_entry(f);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormField *FormField::ignore_if_not_exists() {
|
||||||
|
_ignore_if_not_exists = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormField *FormField::ignore_if_other_field_not_exists(const String &other) {
|
||||||
|
_ignore_if_other_field_not_exists = true;
|
||||||
|
_ignore_if_other_field_not_exist_field = other;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormField::add_entry(FormFieldEntry *field) {
|
||||||
|
fields.push_back(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FormField::validate(Request *request, Vector<String> *errors) {
|
||||||
|
String param = request->get_parameter(name);
|
||||||
|
|
||||||
|
if (_ignore_if_not_exists && param == "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_ignore_if_other_field_not_exists) {
|
||||||
|
String op = request->get_parameter(_ignore_if_other_field_not_exist_field);
|
||||||
|
|
||||||
|
if (op == "") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.size(); ++i) {
|
||||||
|
if (!fields[i]->validate(request, this, param, errors)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormField::FormField() {
|
||||||
|
_ignore_if_not_exists = false;
|
||||||
|
_ignore_if_other_field_not_exists = false;
|
||||||
|
}
|
||||||
|
FormField::~FormField() {
|
||||||
|
for (int i = 0; i < fields.size(); ++i) {
|
||||||
|
delete fields[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//FormValidator
|
||||||
|
|
||||||
|
bool FormValidator::validate(Request *request, Vector<String> *errors) {
|
||||||
|
bool valid = true;
|
||||||
|
|
||||||
|
for (int i = 0; i < fields.size(); ++i) {
|
||||||
|
|
||||||
|
if (!fields[i]->validate(request, errors)) {
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormValidator::add_field(FormField *field) {
|
||||||
|
fields.push_back(field);
|
||||||
|
}
|
||||||
|
|
||||||
|
FormField *FormValidator::new_field(const String &name, const String &human_name) {
|
||||||
|
FormField *f = new FormField();
|
||||||
|
f->name = name;
|
||||||
|
f->human_name = human_name;
|
||||||
|
|
||||||
|
fields.push_back(f);
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormValidator::FormValidator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
FormValidator::~FormValidator() {
|
||||||
|
for (int i = 0; i < fields.size(); ++i) {
|
||||||
|
delete fields[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
fields.clear();
|
||||||
|
}
|
200
modules/web/html/form_validator.h
Normal file
200
modules/web/html/form_validator.h
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
#ifndef FORM_H
|
||||||
|
#define FORM_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class FormField;
|
||||||
|
|
||||||
|
class FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormFieldEntry();
|
||||||
|
virtual ~FormFieldEntry();
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormExistsFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormExistsFieldEntry();
|
||||||
|
~FormExistsFieldEntry();
|
||||||
|
|
||||||
|
String not_exists_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormIntFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormIntFieldEntry();
|
||||||
|
~FormIntFieldEntry();
|
||||||
|
|
||||||
|
String not_int_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormFloatFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormFloatFieldEntry();
|
||||||
|
~FormFloatFieldEntry();
|
||||||
|
|
||||||
|
String not_float_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormAlphaFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormAlphaFieldEntry();
|
||||||
|
~FormAlphaFieldEntry();
|
||||||
|
|
||||||
|
String not_alpha_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormAlphaNumericFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormAlphaNumericFieldEntry();
|
||||||
|
~FormAlphaNumericFieldEntry();
|
||||||
|
|
||||||
|
String not_alpha_numeric_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormNeedsLowercaseCharacterFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormNeedsLowercaseCharacterFieldEntry();
|
||||||
|
~FormNeedsLowercaseCharacterFieldEntry();
|
||||||
|
|
||||||
|
String does_not_have_lowercase_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormNeedsUppercaseCharacterFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormNeedsUppercaseCharacterFieldEntry();
|
||||||
|
~FormNeedsUppercaseCharacterFieldEntry();
|
||||||
|
|
||||||
|
String does_not_have_uppercase_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormNeedsOtherCharacterFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormNeedsOtherCharacterFieldEntry();
|
||||||
|
~FormNeedsOtherCharacterFieldEntry();
|
||||||
|
|
||||||
|
String does_not_have_other_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormMinimumLengthFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormMinimumLengthFieldEntry();
|
||||||
|
~FormMinimumLengthFieldEntry();
|
||||||
|
|
||||||
|
int min_length;
|
||||||
|
|
||||||
|
String does_not_have_min_length_errorf;
|
||||||
|
String does_not_have_min_length_errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormMaximumLengthFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormMaximumLengthFieldEntry();
|
||||||
|
~FormMaximumLengthFieldEntry();
|
||||||
|
|
||||||
|
int max_length;
|
||||||
|
|
||||||
|
String does_not_have_max_length_errorf;
|
||||||
|
String does_not_have_max_length_errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormEmailFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormEmailFieldEntry();
|
||||||
|
~FormEmailFieldEntry();
|
||||||
|
|
||||||
|
String email_format_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
class FormNeedToMatchOtherFieldEntry : public FormFieldEntry {
|
||||||
|
public:
|
||||||
|
virtual bool validate(Request *request, const FormField* field, const String &data, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormNeedToMatchOtherFieldEntry();
|
||||||
|
~FormNeedToMatchOtherFieldEntry();
|
||||||
|
|
||||||
|
String other_field;
|
||||||
|
|
||||||
|
String does_not_match_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
//FormField
|
||||||
|
|
||||||
|
class FormField {
|
||||||
|
public:
|
||||||
|
String name;
|
||||||
|
String human_name;
|
||||||
|
|
||||||
|
bool _ignore_if_not_exists;
|
||||||
|
|
||||||
|
bool _ignore_if_other_field_not_exists;
|
||||||
|
String _ignore_if_other_field_not_exist_field;
|
||||||
|
|
||||||
|
FormField *need_to_exist();
|
||||||
|
FormField *need_to_be_int();
|
||||||
|
FormField *need_to_be_float();
|
||||||
|
FormField *need_to_be_alpha();
|
||||||
|
FormField *need_to_be_alpha_numeric();
|
||||||
|
FormField *need_to_have_lowercase_character();
|
||||||
|
FormField *need_to_have_uppercase_character();
|
||||||
|
FormField *need_to_have_other_character();
|
||||||
|
FormField *need_minimum_length(const int min_length);
|
||||||
|
FormField *need_maximum_length(const int max_length);
|
||||||
|
FormField *need_to_be_email();
|
||||||
|
FormField *need_to_match(const String &other);
|
||||||
|
FormField *ignore_if_not_exists();
|
||||||
|
FormField *ignore_if_other_field_not_exists(const String &other);
|
||||||
|
|
||||||
|
void add_entry(FormFieldEntry *field);
|
||||||
|
|
||||||
|
bool validate(Request *request, Vector<String> *errors);
|
||||||
|
|
||||||
|
FormField();
|
||||||
|
virtual ~FormField();
|
||||||
|
|
||||||
|
Vector<FormFieldEntry *> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
//FormValidator
|
||||||
|
|
||||||
|
class FormValidator {
|
||||||
|
public:
|
||||||
|
bool validate(Request *request, Vector<String> *errors = nullptr);
|
||||||
|
|
||||||
|
void add_field(FormField *field);
|
||||||
|
FormField *new_field(const String &name, const String &human_name);
|
||||||
|
|
||||||
|
FormValidator();
|
||||||
|
virtual ~FormValidator();
|
||||||
|
|
||||||
|
Vector<FormField *> fields;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
3422
modules/web/html/html_builder.cpp
Normal file
3422
modules/web/html/html_builder.cpp
Normal file
File diff suppressed because it is too large
Load Diff
551
modules/web/html/html_builder.h
Normal file
551
modules/web/html/html_builder.h
Normal file
@ -0,0 +1,551 @@
|
|||||||
|
#ifndef HTML_BUILDER_H
|
||||||
|
#define HTML_BUILDER_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class HTMLBuilder;
|
||||||
|
|
||||||
|
class HTMLTag {
|
||||||
|
public:
|
||||||
|
bool simple;
|
||||||
|
String result;
|
||||||
|
|
||||||
|
HTMLTag *str(const String &str);
|
||||||
|
HTMLTag *style(const String &val);
|
||||||
|
HTMLTag *href(const String &val);
|
||||||
|
HTMLTag *cls(const String &val);
|
||||||
|
HTMLTag *clsse(const String &val); //se -> skip empty
|
||||||
|
HTMLTag *id(const String &val);
|
||||||
|
HTMLTag *name(const String &val);
|
||||||
|
HTMLTag *content(const String &val);
|
||||||
|
HTMLTag *value(const String &val);
|
||||||
|
HTMLTag *accept(const String &val);
|
||||||
|
HTMLTag *src(const String &val);
|
||||||
|
HTMLTag *alt(const String &val);
|
||||||
|
HTMLTag *inputmode(const String &val);
|
||||||
|
HTMLTag *list(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *autocomplete(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *autocomplete_off();
|
||||||
|
HTMLTag *autocomplete_on();
|
||||||
|
HTMLTag *autocomplete_name();
|
||||||
|
HTMLTag *autocomplete_name_honorific_prefix();
|
||||||
|
HTMLTag *autocomplete_name_given_name();
|
||||||
|
HTMLTag *autocomplete_name_additional_name();
|
||||||
|
HTMLTag *autocomplete_name_family_name();
|
||||||
|
HTMLTag *autocomplete_name_honorific_suffix();
|
||||||
|
HTMLTag *autocomplete_name_nickname();
|
||||||
|
HTMLTag *autocomplete_email();
|
||||||
|
HTMLTag *autocomplete_username();
|
||||||
|
HTMLTag *autocomplete_new_password();
|
||||||
|
HTMLTag *autocomplete_current_password();
|
||||||
|
HTMLTag *autocomplete_one_time_code();
|
||||||
|
HTMLTag *autocomplete_organization_title();
|
||||||
|
HTMLTag *autocomplete_organization();
|
||||||
|
HTMLTag *autocomplete_street_address();
|
||||||
|
HTMLTag *autocomplete_address_line1();
|
||||||
|
HTMLTag *autocomplete_address_line2();
|
||||||
|
HTMLTag *autocomplete_address_line3();
|
||||||
|
HTMLTag *autocomplete_address_level_1();
|
||||||
|
HTMLTag *autocomplete_address_level_2();
|
||||||
|
HTMLTag *autocomplete_address_level_3();
|
||||||
|
HTMLTag *autocomplete_address_level_4();
|
||||||
|
HTMLTag *autocomplete_country();
|
||||||
|
HTMLTag *autocomplete_country_name();
|
||||||
|
HTMLTag *autocomplete_postal_code();
|
||||||
|
HTMLTag *autocomplete_cc_name();
|
||||||
|
HTMLTag *autocomplete_cc_given_name();
|
||||||
|
HTMLTag *autocomplete_cc_additional_name();
|
||||||
|
HTMLTag *autocomplete_cc_family_name();
|
||||||
|
HTMLTag *autocomplete_cc_number();
|
||||||
|
HTMLTag *autocomplete_cc_exp();
|
||||||
|
HTMLTag *autocomplete_cc_exp_month();
|
||||||
|
HTMLTag *autocomplete_cc_exp_year();
|
||||||
|
HTMLTag *autocomplete_cc_csc();
|
||||||
|
HTMLTag *autocomplete_cc_type();
|
||||||
|
HTMLTag *autocomplete_transaction_currency();
|
||||||
|
HTMLTag *autocomplete_transaction_amount();
|
||||||
|
HTMLTag *autocomplete_language();
|
||||||
|
HTMLTag *autocomplete_bday();
|
||||||
|
HTMLTag *autocomplete_bday_day();
|
||||||
|
HTMLTag *autocomplete_bday_month();
|
||||||
|
HTMLTag *autocomplete_bday_year();
|
||||||
|
HTMLTag *autocomplete_sex();
|
||||||
|
HTMLTag *autocomplete_tel();
|
||||||
|
HTMLTag *autocomplete_tel_country_code();
|
||||||
|
HTMLTag *autocomplete_tel_national();
|
||||||
|
HTMLTag *autocomplete_tel_area_code();
|
||||||
|
HTMLTag *autocomplete_tel_local();
|
||||||
|
HTMLTag *autocomplete_tel_extension();
|
||||||
|
HTMLTag *autocomplete_impp();
|
||||||
|
HTMLTag *autocomplete_url();
|
||||||
|
HTMLTag *autocomplete_photo();
|
||||||
|
|
||||||
|
HTMLTag *onclick(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *checked(const bool val = true);
|
||||||
|
HTMLTag *selected(const bool val = true);
|
||||||
|
HTMLTag *autofocus(const bool val = true);
|
||||||
|
HTMLTag *disabled(const bool val = true);
|
||||||
|
HTMLTag *multiple(const bool val = true);
|
||||||
|
HTMLTag *required(const bool val = true);
|
||||||
|
HTMLTag *spellcheck(const bool val);
|
||||||
|
|
||||||
|
HTMLTag *max(const String &val);
|
||||||
|
HTMLTag *min(const String &val);
|
||||||
|
HTMLTag *step(const String &val);
|
||||||
|
HTMLTag *step_any();
|
||||||
|
|
||||||
|
HTMLTag *minlength(const int val);
|
||||||
|
HTMLTag *minlength(const String &val);
|
||||||
|
HTMLTag *maxlength(const int val);
|
||||||
|
HTMLTag *maxlength(const String &val);
|
||||||
|
HTMLTag *size(const int val);
|
||||||
|
HTMLTag *size(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *width(const int val);
|
||||||
|
HTMLTag *width(const String &val);
|
||||||
|
HTMLTag *height(const int val);
|
||||||
|
HTMLTag *height(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *pattern(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *method(const String &val);
|
||||||
|
HTMLTag *method_get();
|
||||||
|
HTMLTag *method_post();
|
||||||
|
|
||||||
|
HTMLTag *action(const String &val);
|
||||||
|
HTMLTag *type(const String &val);
|
||||||
|
HTMLTag *placeholder(const String &val);
|
||||||
|
HTMLTag *fora(const String &val); // for attrib -> for is reserved keyword
|
||||||
|
|
||||||
|
HTMLTag *rel(const String &val);
|
||||||
|
HTMLTag *rel_stylesheet();
|
||||||
|
HTMLTag *rel_alternate();
|
||||||
|
HTMLTag *rel_author();
|
||||||
|
HTMLTag *rel_bookmark();
|
||||||
|
HTMLTag *rel_external();
|
||||||
|
HTMLTag *rel_help();
|
||||||
|
HTMLTag *rel_license();
|
||||||
|
HTMLTag *rel_next();
|
||||||
|
HTMLTag *rel_nofollow();
|
||||||
|
HTMLTag *rel_noopener();
|
||||||
|
HTMLTag *rel_noreferrer();
|
||||||
|
HTMLTag *rel_prev();
|
||||||
|
HTMLTag *rel_search();
|
||||||
|
HTMLTag *rel_tag();
|
||||||
|
|
||||||
|
HTMLTag *charset(const String &val);
|
||||||
|
HTMLTag *charset_utf_8();
|
||||||
|
|
||||||
|
HTMLTag *itbutton();
|
||||||
|
HTMLTag *itcheckbox();
|
||||||
|
HTMLTag *itcolor();
|
||||||
|
HTMLTag *itdate();
|
||||||
|
HTMLTag *itdatetime_local();
|
||||||
|
HTMLTag *itemail();
|
||||||
|
HTMLTag *itfile();
|
||||||
|
HTMLTag *ithidden();
|
||||||
|
HTMLTag *itimage();
|
||||||
|
HTMLTag *itmonth();
|
||||||
|
HTMLTag *itnumber();
|
||||||
|
HTMLTag *itpassword();
|
||||||
|
HTMLTag *itradio();
|
||||||
|
HTMLTag *itrange();
|
||||||
|
HTMLTag *itreset();
|
||||||
|
HTMLTag *itsearch();
|
||||||
|
HTMLTag *itsubmit();
|
||||||
|
HTMLTag *ittel();
|
||||||
|
HTMLTag *ittext();
|
||||||
|
HTMLTag *ittime();
|
||||||
|
HTMLTag *iturl();
|
||||||
|
HTMLTag *itweek();
|
||||||
|
|
||||||
|
HTMLTag *inputmode_none();
|
||||||
|
HTMLTag *inputmode_text();
|
||||||
|
HTMLTag *inputmode_decimal();
|
||||||
|
HTMLTag *inputmode_numeric();
|
||||||
|
HTMLTag *inputmode_tel();
|
||||||
|
HTMLTag *inputmode_search();
|
||||||
|
HTMLTag *inputmode_email();
|
||||||
|
HTMLTag *inputmode_url();
|
||||||
|
|
||||||
|
HTMLTag *attrib(const String &attr, const String &val);
|
||||||
|
|
||||||
|
HTMLTag *start(const String &p_new_tag, const bool p_simple = false);
|
||||||
|
HTMLTag *reset();
|
||||||
|
HTMLTag *close();
|
||||||
|
|
||||||
|
HTMLBuilder *f();
|
||||||
|
|
||||||
|
bool has_data();
|
||||||
|
|
||||||
|
HTMLTag();
|
||||||
|
|
||||||
|
HTMLBuilder *owner;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HTMLBuilder {
|
||||||
|
public:
|
||||||
|
String result;
|
||||||
|
|
||||||
|
HTMLBuilder *comment(const String &val);
|
||||||
|
HTMLTag *doctype();
|
||||||
|
HTMLBuilder *doctype(const String &val);
|
||||||
|
|
||||||
|
HTMLTag *a();
|
||||||
|
HTMLTag *abbr();
|
||||||
|
HTMLTag *acronym(); // Not supported in HTML5.
|
||||||
|
HTMLTag *address();
|
||||||
|
HTMLTag *applet(); // Not supported in HTML5.
|
||||||
|
HTMLTag *area();
|
||||||
|
HTMLTag *article();
|
||||||
|
HTMLTag *aside();
|
||||||
|
HTMLTag *audio();
|
||||||
|
HTMLTag *b();
|
||||||
|
HTMLTag *basefont(); // Not supported in HTML5.
|
||||||
|
HTMLTag *bdi();
|
||||||
|
HTMLTag *bdo();
|
||||||
|
HTMLTag *big(); // Not supported in HTML5.
|
||||||
|
HTMLTag *blockquote();
|
||||||
|
HTMLTag *body();
|
||||||
|
HTMLTag *br();
|
||||||
|
HTMLTag *button();
|
||||||
|
HTMLTag *canvas();
|
||||||
|
HTMLTag *caption();
|
||||||
|
HTMLTag *center(); // Not supported in HTML5.
|
||||||
|
HTMLTag *cite();
|
||||||
|
HTMLTag *code();
|
||||||
|
HTMLTag *col();
|
||||||
|
HTMLTag *colgroup();
|
||||||
|
HTMLTag *data();
|
||||||
|
HTMLTag *datalist();
|
||||||
|
HTMLTag *dd();
|
||||||
|
HTMLTag *del();
|
||||||
|
HTMLTag *details();
|
||||||
|
HTMLTag *dfn();
|
||||||
|
HTMLTag *dialog();
|
||||||
|
HTMLTag *dir(); // Not supported in HTML5.
|
||||||
|
HTMLTag *div();
|
||||||
|
HTMLTag *dl();
|
||||||
|
HTMLTag *dt();
|
||||||
|
HTMLTag *em();
|
||||||
|
HTMLTag *embed();
|
||||||
|
HTMLTag *fieldset();
|
||||||
|
HTMLTag *figcaption();
|
||||||
|
HTMLTag *figure();
|
||||||
|
HTMLTag *font(); // Not supported in HTML5.
|
||||||
|
HTMLTag *footer();
|
||||||
|
HTMLTag *form();
|
||||||
|
HTMLTag *frame(); // Not supported in HTML5.
|
||||||
|
HTMLTag *frameset(); // Not supported in HTML5.
|
||||||
|
HTMLTag *h1();
|
||||||
|
HTMLTag *h2();
|
||||||
|
HTMLTag *h3();
|
||||||
|
HTMLTag *h4();
|
||||||
|
HTMLTag *h5();
|
||||||
|
HTMLTag *h6();
|
||||||
|
HTMLTag *head();
|
||||||
|
HTMLTag *header();
|
||||||
|
HTMLTag *hr();
|
||||||
|
HTMLTag *html();
|
||||||
|
|
||||||
|
HTMLTag *i();
|
||||||
|
HTMLTag *iframe();
|
||||||
|
HTMLTag *img();
|
||||||
|
HTMLTag *input();
|
||||||
|
HTMLTag *ins();
|
||||||
|
HTMLTag *kbd();
|
||||||
|
HTMLTag *label();
|
||||||
|
HTMLTag *legend();
|
||||||
|
HTMLTag *li();
|
||||||
|
HTMLTag *link();
|
||||||
|
HTMLTag *main();
|
||||||
|
HTMLTag *map();
|
||||||
|
HTMLTag *mark();
|
||||||
|
HTMLTag *meta();
|
||||||
|
HTMLTag *meter();
|
||||||
|
|
||||||
|
HTMLTag *nav();
|
||||||
|
HTMLTag *noframes(); // Not supported in HTML5.
|
||||||
|
HTMLTag *noscript();
|
||||||
|
HTMLTag *object();
|
||||||
|
HTMLTag *ol();
|
||||||
|
HTMLTag *optgroup();
|
||||||
|
HTMLTag *option();
|
||||||
|
HTMLTag *output();
|
||||||
|
HTMLTag *p();
|
||||||
|
HTMLTag *param();
|
||||||
|
HTMLTag *picture();
|
||||||
|
HTMLTag *pre();
|
||||||
|
HTMLTag *progress();
|
||||||
|
HTMLTag *q();
|
||||||
|
HTMLTag *rp();
|
||||||
|
|
||||||
|
HTMLTag *rt();
|
||||||
|
HTMLTag *ruby();
|
||||||
|
HTMLTag *s();
|
||||||
|
HTMLTag *samp();
|
||||||
|
HTMLTag *script();
|
||||||
|
HTMLTag *section();
|
||||||
|
HTMLTag *select();
|
||||||
|
HTMLTag *small();
|
||||||
|
HTMLTag *source();
|
||||||
|
HTMLTag *span();
|
||||||
|
HTMLTag *strike(); // Not supported in HTML5
|
||||||
|
HTMLTag *strong();
|
||||||
|
HTMLTag *style();
|
||||||
|
HTMLTag *sub();
|
||||||
|
HTMLTag *summary();
|
||||||
|
HTMLTag *sup();
|
||||||
|
|
||||||
|
HTMLTag *svg();
|
||||||
|
HTMLTag *table();
|
||||||
|
HTMLTag *tbody();
|
||||||
|
HTMLTag *td();
|
||||||
|
HTMLTag *templateh();
|
||||||
|
HTMLTag *textarea();
|
||||||
|
HTMLTag *tfoot();
|
||||||
|
HTMLTag *th();
|
||||||
|
HTMLTag *thead();
|
||||||
|
HTMLTag *time();
|
||||||
|
HTMLTag *title();
|
||||||
|
HTMLTag *tr();
|
||||||
|
HTMLTag *track();
|
||||||
|
HTMLTag *tt(); // Not supported in HTML5.
|
||||||
|
HTMLTag *u();
|
||||||
|
HTMLTag *ul();
|
||||||
|
HTMLTag *var();
|
||||||
|
HTMLTag *video();
|
||||||
|
HTMLTag *wbr();
|
||||||
|
|
||||||
|
HTMLBuilder *a(const String &href, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *fa(const String &href, const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLBuilder *div(const String &cls, const String &id = "");
|
||||||
|
HTMLBuilder *fdiv(const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLBuilder *textarea(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *ftextarea(const String &name, const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLBuilder *select(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLTag *option(const String &value);
|
||||||
|
HTMLBuilder *foption(const String &value, const String &body, const bool selected = false);
|
||||||
|
|
||||||
|
// closing tags c prefix means close
|
||||||
|
// Note simple tags should not have these like <br>
|
||||||
|
// Note that I might have a few that shouldn't be here, those will be removed as I find them
|
||||||
|
HTMLBuilder *ca();
|
||||||
|
HTMLBuilder *cabbr();
|
||||||
|
HTMLBuilder *cacronym();
|
||||||
|
HTMLBuilder *caddress();
|
||||||
|
HTMLBuilder *capplet();
|
||||||
|
HTMLBuilder *carea();
|
||||||
|
HTMLBuilder *carticle();
|
||||||
|
HTMLBuilder *caside();
|
||||||
|
HTMLBuilder *caudio();
|
||||||
|
HTMLBuilder *cb();
|
||||||
|
HTMLBuilder *cbasefont();
|
||||||
|
HTMLBuilder *cbdi();
|
||||||
|
HTMLBuilder *cbdo();
|
||||||
|
HTMLBuilder *cbig();
|
||||||
|
HTMLBuilder *cblockquote();
|
||||||
|
HTMLBuilder *cbody();
|
||||||
|
HTMLBuilder *cbutton();
|
||||||
|
HTMLBuilder *ccanvas();
|
||||||
|
|
||||||
|
HTMLBuilder *ccaption();
|
||||||
|
HTMLBuilder *ccenter();
|
||||||
|
HTMLBuilder *ccite();
|
||||||
|
HTMLBuilder *ccode();
|
||||||
|
HTMLBuilder *ccol();
|
||||||
|
HTMLBuilder *ccolgroup();
|
||||||
|
HTMLBuilder *cdata();
|
||||||
|
HTMLBuilder *cdatalist();
|
||||||
|
HTMLBuilder *cdd();
|
||||||
|
HTMLBuilder *cdel();
|
||||||
|
HTMLBuilder *cdetails();
|
||||||
|
HTMLBuilder *cdfn();
|
||||||
|
HTMLBuilder *cdialog();
|
||||||
|
HTMLBuilder *cdir();
|
||||||
|
HTMLBuilder *cdiv();
|
||||||
|
HTMLBuilder *cdl();
|
||||||
|
HTMLBuilder *cdt();
|
||||||
|
|
||||||
|
HTMLBuilder *cem();
|
||||||
|
HTMLBuilder *cembed();
|
||||||
|
HTMLBuilder *cfieldset();
|
||||||
|
HTMLBuilder *cfigcaption();
|
||||||
|
HTMLBuilder *cfigure();
|
||||||
|
HTMLBuilder *cfont();
|
||||||
|
HTMLBuilder *cfooter();
|
||||||
|
HTMLBuilder *cform();
|
||||||
|
HTMLBuilder *cframe();
|
||||||
|
HTMLBuilder *cframeset();
|
||||||
|
HTMLBuilder *ch1();
|
||||||
|
HTMLBuilder *ch2();
|
||||||
|
HTMLBuilder *ch3();
|
||||||
|
HTMLBuilder *ch4();
|
||||||
|
HTMLBuilder *ch5();
|
||||||
|
HTMLBuilder *ch6();
|
||||||
|
HTMLBuilder *chead();
|
||||||
|
HTMLBuilder *cheader();
|
||||||
|
HTMLBuilder *chr();
|
||||||
|
HTMLBuilder *chtml();
|
||||||
|
|
||||||
|
HTMLBuilder *ci();
|
||||||
|
HTMLBuilder *ciframe();
|
||||||
|
HTMLBuilder *cimg();
|
||||||
|
HTMLBuilder *cinput();
|
||||||
|
HTMLBuilder *cins();
|
||||||
|
HTMLBuilder *ckbd();
|
||||||
|
HTMLBuilder *clabel();
|
||||||
|
HTMLBuilder *clegend();
|
||||||
|
HTMLBuilder *cli();
|
||||||
|
HTMLBuilder *clink();
|
||||||
|
HTMLBuilder *cmain();
|
||||||
|
HTMLBuilder *cmap();
|
||||||
|
HTMLBuilder *cmark();
|
||||||
|
HTMLBuilder *cmeta();
|
||||||
|
HTMLBuilder *cmeter();
|
||||||
|
|
||||||
|
HTMLBuilder *cnav();
|
||||||
|
HTMLBuilder *cnoframes();
|
||||||
|
HTMLBuilder *cnoscript();
|
||||||
|
HTMLBuilder *cobject();
|
||||||
|
HTMLBuilder *c_ol();
|
||||||
|
HTMLBuilder *coptgroup();
|
||||||
|
HTMLBuilder *coption();
|
||||||
|
HTMLBuilder *coutput();
|
||||||
|
HTMLBuilder *cp();
|
||||||
|
HTMLBuilder *cparam();
|
||||||
|
HTMLBuilder *cpicture();
|
||||||
|
HTMLBuilder *cpre();
|
||||||
|
HTMLBuilder *cprogress();
|
||||||
|
HTMLBuilder *cq();
|
||||||
|
HTMLBuilder *crp();
|
||||||
|
|
||||||
|
HTMLBuilder *crt();
|
||||||
|
HTMLBuilder *cruby();
|
||||||
|
HTMLBuilder *cs();
|
||||||
|
HTMLBuilder *csamp();
|
||||||
|
HTMLBuilder *cscript();
|
||||||
|
HTMLBuilder *csection();
|
||||||
|
HTMLBuilder *cselect();
|
||||||
|
HTMLBuilder *csmall();
|
||||||
|
HTMLBuilder *csource();
|
||||||
|
HTMLBuilder *cspan();
|
||||||
|
HTMLBuilder *cstrike();
|
||||||
|
HTMLBuilder *cstrong();
|
||||||
|
HTMLBuilder *cstyle();
|
||||||
|
HTMLBuilder *csub();
|
||||||
|
HTMLBuilder *csummary();
|
||||||
|
HTMLBuilder *csup();
|
||||||
|
|
||||||
|
HTMLBuilder *csvg();
|
||||||
|
HTMLBuilder *ctable();
|
||||||
|
HTMLBuilder *ctbody();
|
||||||
|
HTMLBuilder *ctd();
|
||||||
|
HTMLBuilder *ctemplateh();
|
||||||
|
HTMLBuilder *ctextarea();
|
||||||
|
HTMLBuilder *ctfoot();
|
||||||
|
HTMLBuilder *cth();
|
||||||
|
HTMLBuilder *cthead();
|
||||||
|
HTMLBuilder *ctime();
|
||||||
|
HTMLBuilder *ctitle();
|
||||||
|
HTMLBuilder *ctr();
|
||||||
|
HTMLBuilder *ctrack();
|
||||||
|
HTMLBuilder *ctt();
|
||||||
|
HTMLBuilder *cu();
|
||||||
|
HTMLBuilder *cul();
|
||||||
|
HTMLBuilder *cvar();
|
||||||
|
HTMLBuilder *cvideo();
|
||||||
|
HTMLBuilder *cwbr();
|
||||||
|
|
||||||
|
HTMLTag *form_get();
|
||||||
|
HTMLTag *form_post();
|
||||||
|
HTMLBuilder *form_get(const String &action, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *form_post(const String &action, const String &cls = "", const String &id = "");
|
||||||
|
// will add a csrf token from request
|
||||||
|
HTMLBuilder *form_post(const String &action, Request *request, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLTag *input_button();
|
||||||
|
HTMLTag *input_checkbox();
|
||||||
|
HTMLTag *input_color();
|
||||||
|
HTMLTag *input_date();
|
||||||
|
HTMLTag *input_datetime_local();
|
||||||
|
HTMLTag *input_email();
|
||||||
|
HTMLTag *input_file();
|
||||||
|
HTMLTag *input_hidden();
|
||||||
|
HTMLTag *input_image();
|
||||||
|
HTMLTag *input_month();
|
||||||
|
HTMLTag *input_number();
|
||||||
|
HTMLTag *input_password();
|
||||||
|
HTMLTag *input_radio();
|
||||||
|
HTMLTag *input_range();
|
||||||
|
HTMLTag *input_reset();
|
||||||
|
HTMLTag *input_search();
|
||||||
|
HTMLTag *input_submit();
|
||||||
|
HTMLTag *input_tel();
|
||||||
|
HTMLTag *input_text();
|
||||||
|
HTMLTag *input_time();
|
||||||
|
HTMLTag *input_url();
|
||||||
|
HTMLTag *input_week();
|
||||||
|
|
||||||
|
HTMLBuilder *label(const String &pfor, const String &plabel, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
HTMLBuilder *input_button(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_checkbox(const String &name, const String &value = "", const bool checked = false, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_color(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_date(const String &name, const String &value = "", const String &cls = "", const String &id = "", const String &date_min = "", const String &date_max = "", const String &date_step = "");
|
||||||
|
HTMLBuilder *input_datetime_local(const String &name, const String &value = "", const String &cls = "", const String &id = "", const String &date_min = "", const String &date_max = "", const String &date_step = "");
|
||||||
|
HTMLBuilder *input_email(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_file(const String &name, const String &accept = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_image(const String &name, const String &src = "", const String &alt = "", const String &cls = "", const String &id = "", const int width = 0, const int height = 0);
|
||||||
|
HTMLBuilder *input_month(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_number(const String &name, const String & = "", const String & = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_password(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
HTMLBuilder *input_radio(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_range(const String &name, const String &value = "", const String &vmin = "", const String &vmax = "", const String &vstep = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_reset(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_search(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "", const String &pattern = "");
|
||||||
|
HTMLBuilder *input_submit(const String &value, const String &cls = "", const String &id = "");
|
||||||
|
HTMLBuilder *input_tel(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "", const String &pattern = "");
|
||||||
|
HTMLBuilder *input_text(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
HTMLBuilder *input_time(const String &name, const String &cls = "", const String &id = "", const String &vmin = "", const String &vmax = "", const String &vstep = "");
|
||||||
|
HTMLBuilder *input_url(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
HTMLBuilder *input_week(const String &name, const String &cls = "", const String &id = "", const String &vmin = "", const String &vmax = "");
|
||||||
|
HTMLBuilder *input_hidden(const String &name, const String &value);
|
||||||
|
|
||||||
|
HTMLBuilder *csrf_token(const String &token);
|
||||||
|
HTMLBuilder *csrf_token(Request *request);
|
||||||
|
|
||||||
|
void f();
|
||||||
|
|
||||||
|
// write
|
||||||
|
HTMLBuilder *w(const String &val);
|
||||||
|
|
||||||
|
HTMLBuilder *wn(const double val, int p_decimals = -1);
|
||||||
|
HTMLBuilder *wns(const double val);
|
||||||
|
HTMLBuilder *wr(const double val, const bool p_trailing = true);
|
||||||
|
HTMLBuilder *wi(const int64_t val, const int base = 10, const bool capitalize_hex = false);
|
||||||
|
HTMLBuilder *wui(const uint64_t val, const int base = 10, const bool capitalize_hex = false);
|
||||||
|
HTMLBuilder *wbn(const bool val);
|
||||||
|
HTMLBuilder *wbs(const bool val);
|
||||||
|
|
||||||
|
// write_escaped
|
||||||
|
HTMLBuilder *we(const String &val);
|
||||||
|
|
||||||
|
HTMLBuilder *write_tag();
|
||||||
|
|
||||||
|
HTMLBuilder();
|
||||||
|
virtual ~HTMLBuilder();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HTMLTag tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
3440
modules/web/html/html_builder_bind.cpp
Normal file
3440
modules/web/html/html_builder_bind.cpp
Normal file
File diff suppressed because it is too large
Load Diff
562
modules/web/html/html_builder_bind.h
Normal file
562
modules/web/html/html_builder_bind.h
Normal file
@ -0,0 +1,562 @@
|
|||||||
|
#ifndef HTML_BUILDER_BIND_H
|
||||||
|
#define HTML_BUILDER_BIND_H
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
#include "core/ustring.h"
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
class _HTMLBuilder;
|
||||||
|
|
||||||
|
class _HTMLTag : public Reference {
|
||||||
|
GDCLASS(_HTMLTag, Reference)
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool simple;
|
||||||
|
String result;
|
||||||
|
|
||||||
|
_HTMLTag *str(const String &str);
|
||||||
|
_HTMLTag *style(const String &val);
|
||||||
|
_HTMLTag *href(const String &val);
|
||||||
|
_HTMLTag *cls(const String &val);
|
||||||
|
_HTMLTag *clsse(const String &val); //se -> skip empty
|
||||||
|
_HTMLTag *id(const String &val);
|
||||||
|
_HTMLTag *name(const String &val);
|
||||||
|
_HTMLTag *content(const String &val);
|
||||||
|
_HTMLTag *value(const String &val);
|
||||||
|
_HTMLTag *accept(const String &val);
|
||||||
|
_HTMLTag *src(const String &val);
|
||||||
|
_HTMLTag *alt(const String &val);
|
||||||
|
_HTMLTag *inputmode(const String &val);
|
||||||
|
_HTMLTag *list(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *autocomplete(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *autocomplete_off();
|
||||||
|
_HTMLTag *autocomplete_on();
|
||||||
|
_HTMLTag *autocomplete_name();
|
||||||
|
_HTMLTag *autocomplete_name_honorific_prefix();
|
||||||
|
_HTMLTag *autocomplete_name_given_name();
|
||||||
|
_HTMLTag *autocomplete_name_additional_name();
|
||||||
|
_HTMLTag *autocomplete_name_family_name();
|
||||||
|
_HTMLTag *autocomplete_name_honorific_suffix();
|
||||||
|
_HTMLTag *autocomplete_name_nickname();
|
||||||
|
_HTMLTag *autocomplete_email();
|
||||||
|
_HTMLTag *autocomplete_username();
|
||||||
|
_HTMLTag *autocomplete_new_password();
|
||||||
|
_HTMLTag *autocomplete_current_password();
|
||||||
|
_HTMLTag *autocomplete_one_time_code();
|
||||||
|
_HTMLTag *autocomplete_organization_title();
|
||||||
|
_HTMLTag *autocomplete_organization();
|
||||||
|
_HTMLTag *autocomplete_street_address();
|
||||||
|
_HTMLTag *autocomplete_address_line1();
|
||||||
|
_HTMLTag *autocomplete_address_line2();
|
||||||
|
_HTMLTag *autocomplete_address_line3();
|
||||||
|
_HTMLTag *autocomplete_address_level_1();
|
||||||
|
_HTMLTag *autocomplete_address_level_2();
|
||||||
|
_HTMLTag *autocomplete_address_level_3();
|
||||||
|
_HTMLTag *autocomplete_address_level_4();
|
||||||
|
_HTMLTag *autocomplete_country();
|
||||||
|
_HTMLTag *autocomplete_country_name();
|
||||||
|
_HTMLTag *autocomplete_postal_code();
|
||||||
|
_HTMLTag *autocomplete_cc_name();
|
||||||
|
_HTMLTag *autocomplete_cc_given_name();
|
||||||
|
_HTMLTag *autocomplete_cc_additional_name();
|
||||||
|
_HTMLTag *autocomplete_cc_family_name();
|
||||||
|
_HTMLTag *autocomplete_cc_number();
|
||||||
|
_HTMLTag *autocomplete_cc_exp();
|
||||||
|
_HTMLTag *autocomplete_cc_exp_month();
|
||||||
|
_HTMLTag *autocomplete_cc_exp_year();
|
||||||
|
_HTMLTag *autocomplete_cc_csc();
|
||||||
|
_HTMLTag *autocomplete_cc_type();
|
||||||
|
_HTMLTag *autocomplete_transaction_currency();
|
||||||
|
_HTMLTag *autocomplete_transaction_amount();
|
||||||
|
_HTMLTag *autocomplete_language();
|
||||||
|
_HTMLTag *autocomplete_bday();
|
||||||
|
_HTMLTag *autocomplete_bday_day();
|
||||||
|
_HTMLTag *autocomplete_bday_month();
|
||||||
|
_HTMLTag *autocomplete_bday_year();
|
||||||
|
_HTMLTag *autocomplete_sex();
|
||||||
|
_HTMLTag *autocomplete_tel();
|
||||||
|
_HTMLTag *autocomplete_tel_country_code();
|
||||||
|
_HTMLTag *autocomplete_tel_national();
|
||||||
|
_HTMLTag *autocomplete_tel_area_code();
|
||||||
|
_HTMLTag *autocomplete_tel_local();
|
||||||
|
_HTMLTag *autocomplete_tel_extension();
|
||||||
|
_HTMLTag *autocomplete_impp();
|
||||||
|
_HTMLTag *autocomplete_url();
|
||||||
|
_HTMLTag *autocomplete_photo();
|
||||||
|
|
||||||
|
_HTMLTag *onclick(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *checked(const bool val = true);
|
||||||
|
_HTMLTag *selected(const bool val = true);
|
||||||
|
_HTMLTag *autofocus(const bool val = true);
|
||||||
|
_HTMLTag *disabled(const bool val = true);
|
||||||
|
_HTMLTag *multiple(const bool val = true);
|
||||||
|
_HTMLTag *required(const bool val = true);
|
||||||
|
_HTMLTag *spellcheck(const bool val);
|
||||||
|
|
||||||
|
_HTMLTag *max(const String &val);
|
||||||
|
_HTMLTag *min(const String &val);
|
||||||
|
_HTMLTag *step(const String &val);
|
||||||
|
_HTMLTag *step_any();
|
||||||
|
|
||||||
|
_HTMLTag *minlength(const int val);
|
||||||
|
_HTMLTag *minlength(const String &val);
|
||||||
|
_HTMLTag *maxlength(const int val);
|
||||||
|
_HTMLTag *maxlength(const String &val);
|
||||||
|
_HTMLTag *size(const int val);
|
||||||
|
_HTMLTag *size(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *width(const int val);
|
||||||
|
_HTMLTag *width(const String &val);
|
||||||
|
_HTMLTag *height(const int val);
|
||||||
|
_HTMLTag *height(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *pattern(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *method(const String &val);
|
||||||
|
_HTMLTag *method_get();
|
||||||
|
_HTMLTag *method_post();
|
||||||
|
|
||||||
|
_HTMLTag *action(const String &val);
|
||||||
|
_HTMLTag *type(const String &val);
|
||||||
|
_HTMLTag *placeholder(const String &val);
|
||||||
|
_HTMLTag *fora(const String &val); // for attrib -> for is reserved keyword
|
||||||
|
|
||||||
|
_HTMLTag *rel(const String &val);
|
||||||
|
_HTMLTag *rel_stylesheet();
|
||||||
|
_HTMLTag *rel_alternate();
|
||||||
|
_HTMLTag *rel_author();
|
||||||
|
_HTMLTag *rel_bookmark();
|
||||||
|
_HTMLTag *rel_external();
|
||||||
|
_HTMLTag *rel_help();
|
||||||
|
_HTMLTag *rel_license();
|
||||||
|
_HTMLTag *rel_next();
|
||||||
|
_HTMLTag *rel_nofollow();
|
||||||
|
_HTMLTag *rel_noopener();
|
||||||
|
_HTMLTag *rel_noreferrer();
|
||||||
|
_HTMLTag *rel_prev();
|
||||||
|
_HTMLTag *rel_search();
|
||||||
|
_HTMLTag *rel_tag();
|
||||||
|
|
||||||
|
_HTMLTag *charset(const String &val);
|
||||||
|
_HTMLTag *charset_utf_8();
|
||||||
|
|
||||||
|
_HTMLTag *itbutton();
|
||||||
|
_HTMLTag *itcheckbox();
|
||||||
|
_HTMLTag *itcolor();
|
||||||
|
_HTMLTag *itdate();
|
||||||
|
_HTMLTag *itdatetime_local();
|
||||||
|
_HTMLTag *itemail();
|
||||||
|
_HTMLTag *itfile();
|
||||||
|
_HTMLTag *ithidden();
|
||||||
|
_HTMLTag *itimage();
|
||||||
|
_HTMLTag *itmonth();
|
||||||
|
_HTMLTag *itnumber();
|
||||||
|
_HTMLTag *itpassword();
|
||||||
|
_HTMLTag *itradio();
|
||||||
|
_HTMLTag *itrange();
|
||||||
|
_HTMLTag *itreset();
|
||||||
|
_HTMLTag *itsearch();
|
||||||
|
_HTMLTag *itsubmit();
|
||||||
|
_HTMLTag *ittel();
|
||||||
|
_HTMLTag *ittext();
|
||||||
|
_HTMLTag *ittime();
|
||||||
|
_HTMLTag *iturl();
|
||||||
|
_HTMLTag *itweek();
|
||||||
|
|
||||||
|
_HTMLTag *inputmode_none();
|
||||||
|
_HTMLTag *inputmode_text();
|
||||||
|
_HTMLTag *inputmode_decimal();
|
||||||
|
_HTMLTag *inputmode_numeric();
|
||||||
|
_HTMLTag *inputmode_tel();
|
||||||
|
_HTMLTag *inputmode_search();
|
||||||
|
_HTMLTag *inputmode_email();
|
||||||
|
_HTMLTag *inputmode_url();
|
||||||
|
|
||||||
|
_HTMLTag *attrib(const String &attr, const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *start(const String &p_new_tag, const bool p_simple = false);
|
||||||
|
_HTMLTag *reset();
|
||||||
|
_HTMLTag *close();
|
||||||
|
|
||||||
|
_HTMLBuilder *f();
|
||||||
|
|
||||||
|
bool has_data();
|
||||||
|
|
||||||
|
_HTMLTag();
|
||||||
|
|
||||||
|
_HTMLBuilder *owner;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
};
|
||||||
|
|
||||||
|
class _HTMLBuilder : public Reference {
|
||||||
|
GDCLASS(_HTMLBuilder, Reference)
|
||||||
|
|
||||||
|
public:
|
||||||
|
String result;
|
||||||
|
|
||||||
|
_HTMLBuilder *comment(const String &val);
|
||||||
|
_HTMLTag *doctype();
|
||||||
|
_HTMLBuilder *doctype(const String &val);
|
||||||
|
|
||||||
|
_HTMLTag *a();
|
||||||
|
_HTMLTag *abbr();
|
||||||
|
_HTMLTag *acronym(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *address();
|
||||||
|
_HTMLTag *applet(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *area();
|
||||||
|
_HTMLTag *article();
|
||||||
|
_HTMLTag *aside();
|
||||||
|
_HTMLTag *audio();
|
||||||
|
_HTMLTag *b();
|
||||||
|
_HTMLTag *basefont(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *bdi();
|
||||||
|
_HTMLTag *bdo();
|
||||||
|
_HTMLTag *big(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *blockquote();
|
||||||
|
_HTMLTag *body();
|
||||||
|
_HTMLTag *br();
|
||||||
|
_HTMLTag *button();
|
||||||
|
_HTMLTag *canvas();
|
||||||
|
_HTMLTag *caption();
|
||||||
|
_HTMLTag *center(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *cite();
|
||||||
|
_HTMLTag *code();
|
||||||
|
_HTMLTag *col();
|
||||||
|
_HTMLTag *colgroup();
|
||||||
|
_HTMLTag *data();
|
||||||
|
_HTMLTag *datalist();
|
||||||
|
_HTMLTag *dd();
|
||||||
|
_HTMLTag *del();
|
||||||
|
_HTMLTag *details();
|
||||||
|
_HTMLTag *dfn();
|
||||||
|
_HTMLTag *dialog();
|
||||||
|
_HTMLTag *dir(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *div();
|
||||||
|
_HTMLTag *dl();
|
||||||
|
_HTMLTag *dt();
|
||||||
|
_HTMLTag *em();
|
||||||
|
_HTMLTag *embed();
|
||||||
|
_HTMLTag *fieldset();
|
||||||
|
_HTMLTag *figcaption();
|
||||||
|
_HTMLTag *figure();
|
||||||
|
_HTMLTag *font(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *footer();
|
||||||
|
_HTMLTag *form();
|
||||||
|
_HTMLTag *frame(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *frameset(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *h1();
|
||||||
|
_HTMLTag *h2();
|
||||||
|
_HTMLTag *h3();
|
||||||
|
_HTMLTag *h4();
|
||||||
|
_HTMLTag *h5();
|
||||||
|
_HTMLTag *h6();
|
||||||
|
_HTMLTag *head();
|
||||||
|
_HTMLTag *header();
|
||||||
|
_HTMLTag *hr();
|
||||||
|
_HTMLTag *html();
|
||||||
|
|
||||||
|
_HTMLTag *i();
|
||||||
|
_HTMLTag *iframe();
|
||||||
|
_HTMLTag *img();
|
||||||
|
_HTMLTag *input();
|
||||||
|
_HTMLTag *ins();
|
||||||
|
_HTMLTag *kbd();
|
||||||
|
_HTMLTag *label();
|
||||||
|
_HTMLTag *legend();
|
||||||
|
_HTMLTag *li();
|
||||||
|
_HTMLTag *link();
|
||||||
|
_HTMLTag *main();
|
||||||
|
_HTMLTag *map();
|
||||||
|
_HTMLTag *mark();
|
||||||
|
_HTMLTag *meta();
|
||||||
|
_HTMLTag *meter();
|
||||||
|
|
||||||
|
_HTMLTag *nav();
|
||||||
|
_HTMLTag *noframes(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *noscript();
|
||||||
|
_HTMLTag *object();
|
||||||
|
_HTMLTag *ol();
|
||||||
|
_HTMLTag *optgroup();
|
||||||
|
_HTMLTag *option();
|
||||||
|
_HTMLTag *output();
|
||||||
|
_HTMLTag *p();
|
||||||
|
_HTMLTag *param();
|
||||||
|
_HTMLTag *picture();
|
||||||
|
_HTMLTag *pre();
|
||||||
|
_HTMLTag *progress();
|
||||||
|
_HTMLTag *q();
|
||||||
|
_HTMLTag *rp();
|
||||||
|
|
||||||
|
_HTMLTag *rt();
|
||||||
|
_HTMLTag *ruby();
|
||||||
|
_HTMLTag *s();
|
||||||
|
_HTMLTag *samp();
|
||||||
|
_HTMLTag *script();
|
||||||
|
_HTMLTag *section();
|
||||||
|
_HTMLTag *select();
|
||||||
|
_HTMLTag *small();
|
||||||
|
_HTMLTag *source();
|
||||||
|
_HTMLTag *span();
|
||||||
|
_HTMLTag *strike(); // Not supported in HTML5
|
||||||
|
_HTMLTag *strong();
|
||||||
|
_HTMLTag *style();
|
||||||
|
_HTMLTag *sub();
|
||||||
|
_HTMLTag *summary();
|
||||||
|
_HTMLTag *sup();
|
||||||
|
|
||||||
|
_HTMLTag *svg();
|
||||||
|
_HTMLTag *table();
|
||||||
|
_HTMLTag *tbody();
|
||||||
|
_HTMLTag *td();
|
||||||
|
_HTMLTag *templateh();
|
||||||
|
_HTMLTag *textarea();
|
||||||
|
_HTMLTag *tfoot();
|
||||||
|
_HTMLTag *th();
|
||||||
|
_HTMLTag *thead();
|
||||||
|
_HTMLTag *time();
|
||||||
|
_HTMLTag *title();
|
||||||
|
_HTMLTag *tr();
|
||||||
|
_HTMLTag *track();
|
||||||
|
_HTMLTag *tt(); // Not supported in HTML5.
|
||||||
|
_HTMLTag *u();
|
||||||
|
_HTMLTag *ul();
|
||||||
|
_HTMLTag *var();
|
||||||
|
_HTMLTag *video();
|
||||||
|
_HTMLTag *wbr();
|
||||||
|
|
||||||
|
//_HTMLBuilder *a(const String &href, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *fa(const String &href, const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLBuilder *div(const String &cls, const String &id = "");
|
||||||
|
_HTMLBuilder *fdiv(const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLBuilder *textarea(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *ftextarea(const String &name, const String &body, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLBuilder *select(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLTag *option(const String &value);
|
||||||
|
_HTMLBuilder *foption(const String &value, const String &body, const bool selected = false);
|
||||||
|
|
||||||
|
// closing tags c prefix means close
|
||||||
|
// Note simple tags should not have these like <br>
|
||||||
|
// Note that I might have a few that shouldn't be here, those will be removed as I find them
|
||||||
|
_HTMLBuilder *ca();
|
||||||
|
_HTMLBuilder *cabbr();
|
||||||
|
_HTMLBuilder *cacronym();
|
||||||
|
_HTMLBuilder *caddress();
|
||||||
|
_HTMLBuilder *capplet();
|
||||||
|
_HTMLBuilder *carea();
|
||||||
|
_HTMLBuilder *carticle();
|
||||||
|
_HTMLBuilder *caside();
|
||||||
|
_HTMLBuilder *caudio();
|
||||||
|
_HTMLBuilder *cb();
|
||||||
|
_HTMLBuilder *cbasefont();
|
||||||
|
_HTMLBuilder *cbdi();
|
||||||
|
_HTMLBuilder *cbdo();
|
||||||
|
_HTMLBuilder *cbig();
|
||||||
|
_HTMLBuilder *cblockquote();
|
||||||
|
_HTMLBuilder *cbody();
|
||||||
|
_HTMLBuilder *cbutton();
|
||||||
|
_HTMLBuilder *ccanvas();
|
||||||
|
|
||||||
|
_HTMLBuilder *ccaption();
|
||||||
|
_HTMLBuilder *ccenter();
|
||||||
|
_HTMLBuilder *ccite();
|
||||||
|
_HTMLBuilder *ccode();
|
||||||
|
_HTMLBuilder *ccol();
|
||||||
|
_HTMLBuilder *ccolgroup();
|
||||||
|
_HTMLBuilder *cdata();
|
||||||
|
_HTMLBuilder *cdatalist();
|
||||||
|
_HTMLBuilder *cdd();
|
||||||
|
_HTMLBuilder *cdel();
|
||||||
|
_HTMLBuilder *cdetails();
|
||||||
|
_HTMLBuilder *cdfn();
|
||||||
|
_HTMLBuilder *cdialog();
|
||||||
|
_HTMLBuilder *cdir();
|
||||||
|
_HTMLBuilder *cdiv();
|
||||||
|
_HTMLBuilder *cdl();
|
||||||
|
_HTMLBuilder *cdt();
|
||||||
|
|
||||||
|
_HTMLBuilder *cem();
|
||||||
|
_HTMLBuilder *cembed();
|
||||||
|
_HTMLBuilder *cfieldset();
|
||||||
|
_HTMLBuilder *cfigcaption();
|
||||||
|
_HTMLBuilder *cfigure();
|
||||||
|
_HTMLBuilder *cfont();
|
||||||
|
_HTMLBuilder *cfooter();
|
||||||
|
_HTMLBuilder *cform();
|
||||||
|
_HTMLBuilder *cframe();
|
||||||
|
_HTMLBuilder *cframeset();
|
||||||
|
_HTMLBuilder *ch1();
|
||||||
|
_HTMLBuilder *ch2();
|
||||||
|
_HTMLBuilder *ch3();
|
||||||
|
_HTMLBuilder *ch4();
|
||||||
|
_HTMLBuilder *ch5();
|
||||||
|
_HTMLBuilder *ch6();
|
||||||
|
_HTMLBuilder *chead();
|
||||||
|
_HTMLBuilder *cheader();
|
||||||
|
_HTMLBuilder *chr();
|
||||||
|
_HTMLBuilder *chtml();
|
||||||
|
|
||||||
|
_HTMLBuilder *ci();
|
||||||
|
_HTMLBuilder *ciframe();
|
||||||
|
_HTMLBuilder *cimg();
|
||||||
|
_HTMLBuilder *cinput();
|
||||||
|
_HTMLBuilder *cins();
|
||||||
|
_HTMLBuilder *ckbd();
|
||||||
|
_HTMLBuilder *clabel();
|
||||||
|
_HTMLBuilder *clegend();
|
||||||
|
_HTMLBuilder *cli();
|
||||||
|
_HTMLBuilder *clink();
|
||||||
|
_HTMLBuilder *cmain();
|
||||||
|
_HTMLBuilder *cmap();
|
||||||
|
_HTMLBuilder *cmark();
|
||||||
|
_HTMLBuilder *cmeta();
|
||||||
|
_HTMLBuilder *cmeter();
|
||||||
|
|
||||||
|
_HTMLBuilder *cnav();
|
||||||
|
_HTMLBuilder *cnoframes();
|
||||||
|
_HTMLBuilder *cnoscript();
|
||||||
|
_HTMLBuilder *cobject();
|
||||||
|
_HTMLBuilder *c_ol();
|
||||||
|
_HTMLBuilder *coptgroup();
|
||||||
|
_HTMLBuilder *coption();
|
||||||
|
_HTMLBuilder *coutput();
|
||||||
|
_HTMLBuilder *cp();
|
||||||
|
_HTMLBuilder *cparam();
|
||||||
|
_HTMLBuilder *cpicture();
|
||||||
|
_HTMLBuilder *cpre();
|
||||||
|
_HTMLBuilder *cprogress();
|
||||||
|
_HTMLBuilder *cq();
|
||||||
|
_HTMLBuilder *crp();
|
||||||
|
|
||||||
|
_HTMLBuilder *crt();
|
||||||
|
_HTMLBuilder *cruby();
|
||||||
|
_HTMLBuilder *cs();
|
||||||
|
_HTMLBuilder *csamp();
|
||||||
|
_HTMLBuilder *cscript();
|
||||||
|
_HTMLBuilder *csection();
|
||||||
|
_HTMLBuilder *cselect();
|
||||||
|
_HTMLBuilder *csmall();
|
||||||
|
_HTMLBuilder *csource();
|
||||||
|
_HTMLBuilder *cspan();
|
||||||
|
_HTMLBuilder *cstrike();
|
||||||
|
_HTMLBuilder *cstrong();
|
||||||
|
_HTMLBuilder *cstyle();
|
||||||
|
_HTMLBuilder *csub();
|
||||||
|
_HTMLBuilder *csummary();
|
||||||
|
_HTMLBuilder *csup();
|
||||||
|
|
||||||
|
_HTMLBuilder *csvg();
|
||||||
|
_HTMLBuilder *ctable();
|
||||||
|
_HTMLBuilder *ctbody();
|
||||||
|
_HTMLBuilder *ctd();
|
||||||
|
_HTMLBuilder *ctemplateh();
|
||||||
|
_HTMLBuilder *ctextarea();
|
||||||
|
_HTMLBuilder *ctfoot();
|
||||||
|
_HTMLBuilder *cth();
|
||||||
|
_HTMLBuilder *cthead();
|
||||||
|
_HTMLBuilder *ctime();
|
||||||
|
_HTMLBuilder *ctitle();
|
||||||
|
_HTMLBuilder *ctr();
|
||||||
|
_HTMLBuilder *ctrack();
|
||||||
|
_HTMLBuilder *ctt();
|
||||||
|
_HTMLBuilder *cu();
|
||||||
|
_HTMLBuilder *cul();
|
||||||
|
_HTMLBuilder *cvar();
|
||||||
|
_HTMLBuilder *cvideo();
|
||||||
|
_HTMLBuilder *cwbr();
|
||||||
|
|
||||||
|
_HTMLTag *form_get();
|
||||||
|
_HTMLTag *form_post();
|
||||||
|
_HTMLBuilder *form_get(const String &action, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *form_post(const String &action, const String &cls = "", const String &id = "");
|
||||||
|
// will add a csrf token from request
|
||||||
|
//_HTMLBuilder *form_post(const String &action, Request *request, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLTag *input_button();
|
||||||
|
_HTMLTag *input_checkbox();
|
||||||
|
_HTMLTag *input_color();
|
||||||
|
_HTMLTag *input_date();
|
||||||
|
_HTMLTag *input_datetime_local();
|
||||||
|
_HTMLTag *input_email();
|
||||||
|
_HTMLTag *input_file();
|
||||||
|
_HTMLTag *input_hidden();
|
||||||
|
_HTMLTag *input_image();
|
||||||
|
_HTMLTag *input_month();
|
||||||
|
_HTMLTag *input_number();
|
||||||
|
_HTMLTag *input_password();
|
||||||
|
_HTMLTag *input_radio();
|
||||||
|
_HTMLTag *input_range();
|
||||||
|
_HTMLTag *input_reset();
|
||||||
|
_HTMLTag *input_search();
|
||||||
|
_HTMLTag *input_submit();
|
||||||
|
_HTMLTag *input_tel();
|
||||||
|
_HTMLTag *input_text();
|
||||||
|
_HTMLTag *input_time();
|
||||||
|
_HTMLTag *input_url();
|
||||||
|
_HTMLTag *input_week();
|
||||||
|
|
||||||
|
_HTMLBuilder *label(const String &pfor, const String &plabel, const String &cls = "", const String &id = "");
|
||||||
|
|
||||||
|
_HTMLBuilder *input_button(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_checkbox(const String &name, const String &value = "", const bool checked = false, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_color(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_date(const String &name, const String &value = "", const String &cls = "", const String &id = "", const String &date_min = "", const String &date_max = "", const String &date_step = "");
|
||||||
|
_HTMLBuilder *input_datetime_local(const String &name, const String &value = "", const String &cls = "", const String &id = "", const String &date_min = "", const String &date_max = "", const String &date_step = "");
|
||||||
|
_HTMLBuilder *input_email(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_file(const String &name, const String &accept = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_image(const String &name, const String &src = "", const String &alt = "", const String &cls = "", const String &id = "", const int width = 0, const int height = 0);
|
||||||
|
_HTMLBuilder *input_month(const String &name, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_number(const String &name, const String & = "", const String & = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_password(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
_HTMLBuilder *input_radio(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_range(const String &name, const String &value = "", const String &vmin = "", const String &vmax = "", const String &vstep = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_reset(const String &name, const String &value = "", const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_search(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "", const String &pattern = "");
|
||||||
|
_HTMLBuilder *input_submit(const String &value, const String &cls = "", const String &id = "");
|
||||||
|
_HTMLBuilder *input_tel(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "", const String &pattern = "");
|
||||||
|
_HTMLBuilder *input_text(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
_HTMLBuilder *input_time(const String &name, const String &cls = "", const String &id = "", const String &vmin = "", const String &vmax = "", const String &vstep = "");
|
||||||
|
_HTMLBuilder *input_url(const String &name, const String &value = "", const String &placeholder = "", const String &cls = "", const String &id = "", const String &minlength = "", const String &maxlength = "", const String &size = "");
|
||||||
|
_HTMLBuilder *input_week(const String &name, const String &cls = "", const String &id = "", const String &vmin = "", const String &vmax = "");
|
||||||
|
_HTMLBuilder *input_hidden(const String &name, const String &value);
|
||||||
|
|
||||||
|
_HTMLBuilder *csrf_token(const String &token);
|
||||||
|
//_HTMLBuilder *csrf_token(Request *request);
|
||||||
|
|
||||||
|
void f();
|
||||||
|
|
||||||
|
// write
|
||||||
|
_HTMLBuilder *w(const String &val);
|
||||||
|
|
||||||
|
_HTMLBuilder *wn(const double val, int p_decimals = -1);
|
||||||
|
_HTMLBuilder *wns(const double val);
|
||||||
|
_HTMLBuilder *wr(const double val, const bool p_trailing = true);
|
||||||
|
_HTMLBuilder *wi(const int64_t val, const int base = 10, const bool capitalize_hex = false);
|
||||||
|
_HTMLBuilder *wui(const uint64_t val, const int base = 10, const bool capitalize_hex = false);
|
||||||
|
_HTMLBuilder *wbn(const bool val);
|
||||||
|
_HTMLBuilder *wbs(const bool val);
|
||||||
|
|
||||||
|
// write_escaped
|
||||||
|
_HTMLBuilder *we(const String &val);
|
||||||
|
|
||||||
|
_HTMLBuilder *write_tag();
|
||||||
|
|
||||||
|
_HTMLBuilder();
|
||||||
|
virtual ~_HTMLBuilder();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void _bind_methods();
|
||||||
|
|
||||||
|
_HTMLTag tag;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
676
modules/web/html/html_parser.cpp
Normal file
676
modules/web/html/html_parser.cpp
Normal file
@ -0,0 +1,676 @@
|
|||||||
|
#include "html_parser.h"
|
||||||
|
#include "core/error_macros.h"
|
||||||
|
|
||||||
|
bool HTMLParserAttribute::match_attrib(const String &attrib) {
|
||||||
|
return attribute == attrib;
|
||||||
|
}
|
||||||
|
bool HTMLParserAttribute::match_data(const String &d) {
|
||||||
|
return data == d;
|
||||||
|
}
|
||||||
|
bool HTMLParserAttribute::match_data(const Vector<String> &d) {
|
||||||
|
// todo
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool HTMLParserAttribute::contains_data(const String &d) {
|
||||||
|
return data.find(d) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String HTMLParserAttribute::to_string() {
|
||||||
|
if (single) {
|
||||||
|
return attribute;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.find('"' == -1)) {
|
||||||
|
return attribute + "=\"" + data + "\"";
|
||||||
|
} else {
|
||||||
|
return attribute + "=\'" + data + "\'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTMLParserAttribute::print() {
|
||||||
|
to_string().print();
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserAttribute::HTMLParserAttribute() {
|
||||||
|
single = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserAttribute::~HTMLParserAttribute() {
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserTag *HTMLParserTag::get_first(const String &t) {
|
||||||
|
if (tag == t) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
HTMLParserTag *ht = tags[i]->get_first(t);
|
||||||
|
|
||||||
|
if (ht) {
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserTag *HTMLParserTag::get_first(const String &t, const String &attrib, const String &val) {
|
||||||
|
if (tag == t) {
|
||||||
|
if (has_attribute(attrib, val)) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
HTMLParserTag *ht = tags[i]->get_first(t, attrib, val);
|
||||||
|
|
||||||
|
if (ht) {
|
||||||
|
return ht;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
String HTMLParserTag::get_attribute_value(const String &attrib) {
|
||||||
|
HTMLParserAttribute *a = get_attribute(attrib);
|
||||||
|
|
||||||
|
if (a) {
|
||||||
|
return a->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserAttribute *HTMLParserTag::get_attribute(const String &attrib) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
HTMLParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTMLParserTag::has_attribute(const String &attrib) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
HTMLParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserAttribute *HTMLParserTag::get_attribute(const String &attrib, const String &contains_val) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
HTMLParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib) && a->contains_data(contains_val)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HTMLParserTag::has_attribute(const String &attrib, const String &contains_val) {
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
HTMLParserAttribute *a = attributes[i];
|
||||||
|
|
||||||
|
if (a->match_attrib(attrib) && a->contains_data(contains_val)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTMLParserTag::process() {
|
||||||
|
if (type != HTMLParserTag::HTML_PARSER_TAG_TYPE_NONE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_FAIL_COND(data[0] != '<');
|
||||||
|
ERR_FAIL_COND(data[data.size() - 1] != '>');
|
||||||
|
|
||||||
|
int start_index = 1;
|
||||||
|
if (data[1] == '/') {
|
||||||
|
++start_index;
|
||||||
|
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_CLOSING_TAG;
|
||||||
|
} else if (data[1] == '!') {
|
||||||
|
if (data.size() < 8) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for comment. <!-- -->
|
||||||
|
++start_index;
|
||||||
|
if (data[2] == '-' && data[3] == '-') {
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_COMMENT;
|
||||||
|
|
||||||
|
int comment_start_index = data.find(' ', 3);
|
||||||
|
|
||||||
|
if (comment_start_index == -1) {
|
||||||
|
comment_start_index = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tag = data.substr(comment_start_index, data.size() - comment_start_index - 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.size() < 11) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test for doctype. <!doctype >
|
||||||
|
int doctype_start_index = data.find("doctype ", 2);
|
||||||
|
|
||||||
|
if (doctype_start_index == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_DOCTYPE;
|
||||||
|
|
||||||
|
tag = data.substr(doctype_start_index + 8, data.size() - doctype_start_index - 8 - 1);
|
||||||
|
} else {
|
||||||
|
String tag_text;
|
||||||
|
|
||||||
|
if (data[data.size() - 2] == '/') {
|
||||||
|
// will catch all that looks like <br/>
|
||||||
|
// tags that look like <br> will be caught later in a post process, in a way
|
||||||
|
// which also tries to catch erroneously not closed tags that supposed to be closed
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_SELF_CLOSING_TAG;
|
||||||
|
|
||||||
|
tag_text = data.substr(1, data.size() - 3);
|
||||||
|
} else {
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_OPENING_TAG;
|
||||||
|
|
||||||
|
tag_text = data.substr(1, data.size() - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fspc_index = tag_text.find(' ');
|
||||||
|
|
||||||
|
if (fspc_index == -1) {
|
||||||
|
// no args
|
||||||
|
tag = tag_text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab the tag itself
|
||||||
|
tag = tag_text.substr(0, fspc_index);
|
||||||
|
|
||||||
|
if (fspc_index + 1 == tag_text.size()) {
|
||||||
|
// no args, but had a space like <br />
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String args = tag_text.substr(fspc_index + 1, tag_text.size() - fspc_index - 1);
|
||||||
|
parse_args(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tag_end_index = data.find(' ', start_index);
|
||||||
|
|
||||||
|
if (tag_end_index == -1) {
|
||||||
|
// simple tag
|
||||||
|
tag = data.substr(start_index, data.size() - start_index - 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTMLParserTag::parse_args(const String &args) {
|
||||||
|
attributes.clear();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (i < args.size()) {
|
||||||
|
if (args[i] == ' ') {
|
||||||
|
//"trim"
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int equals_index = args.find('=', i);
|
||||||
|
|
||||||
|
HTMLParserAttribute *a = new HTMLParserAttribute();
|
||||||
|
|
||||||
|
if (equals_index == -1) {
|
||||||
|
a->attribute = args.substr(i, args.size() - i);
|
||||||
|
a->single = true;
|
||||||
|
attributes.push_back(a);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->attribute = args.substr(i, equals_index - i);
|
||||||
|
|
||||||
|
// todo
|
||||||
|
// a.trim();
|
||||||
|
|
||||||
|
int next_char_index = equals_index + 1;
|
||||||
|
|
||||||
|
if (next_char_index >= args.size()) {
|
||||||
|
// an attribute looks like this "... attrib="
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip spaces
|
||||||
|
while (args[next_char_index] == ' ') {
|
||||||
|
++next_char_index;
|
||||||
|
|
||||||
|
if (next_char_index >= args.size()) {
|
||||||
|
// an attribute looks like this "... attrib= "
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = args[next_char_index];
|
||||||
|
char find_char = ' ';
|
||||||
|
|
||||||
|
if (c == '"' || c == '\'') {
|
||||||
|
++next_char_index;
|
||||||
|
find_char = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
int end_index = args.find(find_char, next_char_index);
|
||||||
|
|
||||||
|
if (end_index == -1) {
|
||||||
|
// missing closing ' or " if c is ' or "
|
||||||
|
// else missing parameter
|
||||||
|
|
||||||
|
a->data = args.substr(next_char_index, args.size() - next_char_index - 1);
|
||||||
|
attributes.push_back(a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
a->data = args.substr(next_char_index, end_index - next_char_index);
|
||||||
|
attributes.push_back(a);
|
||||||
|
|
||||||
|
i = end_index + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String HTMLParserTag::to_string(const int level) {
|
||||||
|
String s;
|
||||||
|
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
|
||||||
|
if (type == HTML_PARSER_TAG_TYPE_CONTENT) {
|
||||||
|
s += data + "\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!CONTENT TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_OPENING_TAG) {
|
||||||
|
int ln = level + 1;
|
||||||
|
|
||||||
|
s += "<" + tag;
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
s += " " + attributes[i]->to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
s += ">\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(ln);
|
||||||
|
}
|
||||||
|
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
|
||||||
|
s += "</" + tag + ">\n";
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_CLOSING_TAG) {
|
||||||
|
// HTMLParserTag should handle this automatically
|
||||||
|
// it's here for debugging purposes though
|
||||||
|
s += "</" + tag + "(!)>";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!CLOSING TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_SELF_CLOSING_TAG) {
|
||||||
|
s += "<" + tag;
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
s += " " + attributes[i]->to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
s += "/>\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!SELF CLOSING TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_COMMENT) {
|
||||||
|
s += "<!-- " + data + " -->\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!COMMENT TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_DOCTYPE) {
|
||||||
|
s += data + "\n";
|
||||||
|
|
||||||
|
if (tags.size() != 0) {
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
s += "(!DOCTYPE TAG HAS TAGS!)\n";
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level + 1) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (type == HTML_PARSER_TAG_TYPE_NONE) {
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
s += tags[i]->to_string(level) + "\n";
|
||||||
|
s.append_repeat(" ", level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
void HTMLParserTag::print() {
|
||||||
|
to_string().print();
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserTag::HTMLParserTag() {
|
||||||
|
type = HTMLParserTag::HTML_PARSER_TAG_TYPE_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserTag::~HTMLParserTag() {
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
delete tags[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < attributes.size(); ++i) {
|
||||||
|
delete attributes[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTMLParser::parse(const String &data) {
|
||||||
|
Vector<HTMLParserTag *> tags;
|
||||||
|
|
||||||
|
// <script> content parsing is based on https://stackoverflow.com/questions/14574471/how-do-browsers-parse-a-script-tag-exactly
|
||||||
|
const int STATE_NONE = 0;
|
||||||
|
const int STATE_DATA_1 = 1;
|
||||||
|
const int STATE_DATA_2 = 2;
|
||||||
|
const int STATE_DATA_3 = 3;
|
||||||
|
|
||||||
|
int state = STATE_NONE;
|
||||||
|
|
||||||
|
// split into tags
|
||||||
|
for (int i = 0; i < data.size(); ++i) {
|
||||||
|
if (state == STATE_NONE) {
|
||||||
|
if (data[i] == '<') {
|
||||||
|
// tag
|
||||||
|
|
||||||
|
if (data.is_word_at(i, "<script")) {
|
||||||
|
// after the opening <script> tag, the parser goes to data1 state
|
||||||
|
state = STATE_DATA_1;
|
||||||
|
// no else, we need to process the tag istelf!
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = i + 1; j < data.size(); ++j) {
|
||||||
|
if (data[j] == '>') {
|
||||||
|
HTMLParserTag *t = new HTMLParserTag();
|
||||||
|
|
||||||
|
t->data = data.substr(i, j - i + 1);
|
||||||
|
t->process();
|
||||||
|
|
||||||
|
tags.push_back(t);
|
||||||
|
|
||||||
|
i = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// content
|
||||||
|
|
||||||
|
for (int j = i + 1; j < data.size(); ++j) {
|
||||||
|
if (data[j] == '<') {
|
||||||
|
HTMLParserTag *t = new HTMLParserTag();
|
||||||
|
|
||||||
|
t->data = data.substr(i, j - i);
|
||||||
|
t->type = HTMLParserTag::HTML_PARSER_TAG_TYPE_CONTENT;
|
||||||
|
|
||||||
|
tags.push_back(t);
|
||||||
|
|
||||||
|
i = j - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// script tag content
|
||||||
|
|
||||||
|
bool done = false;
|
||||||
|
for (int j = i; j < data.size(); ++j) {
|
||||||
|
char c = data[j];
|
||||||
|
|
||||||
|
if (c != '<' && c != '-') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.is_word_at(j, "-->")) {
|
||||||
|
// if --> is encountered while in any state, switch to data1 state
|
||||||
|
state = STATE_DATA_1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == STATE_DATA_1) {
|
||||||
|
|
||||||
|
if (data.is_word_at(j, "<!--")) {
|
||||||
|
// if <!-- is encountered while in data1 state, switch to data2 state
|
||||||
|
state = STATE_DATA_2;
|
||||||
|
} else if (data.is_word_at(j, "</script")) {
|
||||||
|
// if </script[\s/>] is encountered while in any other state (than data3), stop parsing
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (state == STATE_DATA_2) {
|
||||||
|
|
||||||
|
if (data.is_word_at(j, "<script")) {
|
||||||
|
// if <script[\s/>] is encountered while in data2 state, switch to data3 state
|
||||||
|
state = STATE_DATA_3;
|
||||||
|
} else if (data.is_word_at(j, "</script")) {
|
||||||
|
// if </script[\s/>] is encountered while in any other state (than data3), stop parsing
|
||||||
|
done = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (state == STATE_DATA_3) {
|
||||||
|
|
||||||
|
// if </script[\s/>] is encountered while in data3 state, switch to data2 state
|
||||||
|
if (data.is_word_at(j, "</script")) {
|
||||||
|
state = STATE_DATA_2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (done) {
|
||||||
|
state = STATE_NONE;
|
||||||
|
HTMLParserTag *t = new HTMLParserTag();
|
||||||
|
|
||||||
|
t->data = data.substr(i, j - i);
|
||||||
|
t->type = HTMLParserTag::HTML_PARSER_TAG_TYPE_CONTENT;
|
||||||
|
|
||||||
|
tags.push_back(t);
|
||||||
|
|
||||||
|
i = j - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
|
||||||
|
root = new HTMLParserTag();
|
||||||
|
|
||||||
|
// process tags into hierarchical order
|
||||||
|
Vector<HTMLParserTag *> tag_stack;
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
HTMLParserTag *t = tags[i];
|
||||||
|
|
||||||
|
if (t == nullptr) {
|
||||||
|
RLOG_ERR("HTMLParser::parse: t == nullptr!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_NONE) {
|
||||||
|
RLOG_ERR("HTMLParser::parse: t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_NONE!");
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_OPENING_TAG) {
|
||||||
|
tag_stack.push_back(t);
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_SELF_CLOSING_TAG) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_CONTENT) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_COMMENT) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_DOCTYPE) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(t);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
tags[i] = nullptr;
|
||||||
|
continue;
|
||||||
|
} else if (t->type == HTMLParserTag::HTML_PARSER_TAG_TYPE_CLOSING_TAG) {
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
|
||||||
|
// ill-formed html
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// find it's pair
|
||||||
|
int tag_index = 0;
|
||||||
|
for (int j = tag_stack.size() - 1; j > 0; --j) {
|
||||||
|
HTMLParserTag *ts = tag_stack[j];
|
||||||
|
|
||||||
|
// we sould only have opening tags on the stack
|
||||||
|
if (ts->tag == t->tag) {
|
||||||
|
// found
|
||||||
|
tag_index = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParserTag *opening_tag = tag_stack[tag_index];
|
||||||
|
|
||||||
|
// mark everything else that we found before finding the opening tag as self closing, and add them to out opening tag
|
||||||
|
// If the html is ill formed, it just grabs everything from the tag stack
|
||||||
|
for (int j = tag_index + 1; j < tag_stack.size(); ++j) {
|
||||||
|
HTMLParserTag *ts = tag_stack[j];
|
||||||
|
|
||||||
|
ts->type = HTMLParserTag::HTML_PARSER_TAG_TYPE_SELF_CLOSING_TAG;
|
||||||
|
opening_tag->tags.push_back(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_stack.resize(tag_index);
|
||||||
|
|
||||||
|
if (tag_stack.size() == 0) {
|
||||||
|
root->tags.push_back(opening_tag);
|
||||||
|
} else {
|
||||||
|
tag_stack[tag_stack.size() - 1]->tags.push_back(opening_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete t;
|
||||||
|
tags[i] = nullptr;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add everything remaining on the stack to root
|
||||||
|
for (int i = 0; i < tag_stack.size(); ++i) {
|
||||||
|
root->tags.push_back(tag_stack[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < tags.size(); ++i) {
|
||||||
|
HTMLParserTag *t = tags[i];
|
||||||
|
|
||||||
|
if (t != nullptr) {
|
||||||
|
RLOG_ERR("HTMLParser::parse(const String &data): tag was not processed!\n");
|
||||||
|
t->print();
|
||||||
|
|
||||||
|
delete t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String HTMLParser::to_string() {
|
||||||
|
if (!root) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return root->to_string();
|
||||||
|
}
|
||||||
|
void HTMLParser::print() {
|
||||||
|
if (root) {
|
||||||
|
root->print();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParser::HTMLParser() {
|
||||||
|
root = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTMLParser::~HTMLParser() {
|
||||||
|
if (root) {
|
||||||
|
delete root;
|
||||||
|
}
|
||||||
|
}
|
80
modules/web/html/html_parser.h
Normal file
80
modules/web/html/html_parser.h
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#ifndef HTML_PARSER_H
|
||||||
|
#define HTML_PARSER_H
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
class HTMLParserAttribute {
|
||||||
|
public:
|
||||||
|
String attribute;
|
||||||
|
String data;
|
||||||
|
bool single;
|
||||||
|
|
||||||
|
bool match_attrib(const String &attrib);
|
||||||
|
bool match_data(const String &d);
|
||||||
|
bool match_data(const Vector<String> &d);
|
||||||
|
bool contains_data(const String &d);
|
||||||
|
|
||||||
|
String to_string();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
HTMLParserAttribute();
|
||||||
|
virtual ~HTMLParserAttribute();
|
||||||
|
};
|
||||||
|
|
||||||
|
class HTMLParserTag {
|
||||||
|
public:
|
||||||
|
enum HTMLParserTagType {
|
||||||
|
HTML_PARSER_TAG_TYPE_NONE = 0,
|
||||||
|
HTML_PARSER_TAG_TYPE_OPENING_TAG,
|
||||||
|
HTML_PARSER_TAG_TYPE_CLOSING_TAG,
|
||||||
|
HTML_PARSER_TAG_TYPE_SELF_CLOSING_TAG,
|
||||||
|
HTML_PARSER_TAG_TYPE_COMMENT,
|
||||||
|
HTML_PARSER_TAG_TYPE_DOCTYPE,
|
||||||
|
HTML_PARSER_TAG_TYPE_CONTENT
|
||||||
|
};
|
||||||
|
|
||||||
|
int type;
|
||||||
|
|
||||||
|
String tag;
|
||||||
|
String data;
|
||||||
|
|
||||||
|
Vector<HTMLParserTag*> tags;
|
||||||
|
Vector<HTMLParserAttribute*> attributes;
|
||||||
|
|
||||||
|
HTMLParserTag *get_first(const String &t);
|
||||||
|
HTMLParserTag *get_first(const String &t, const String &attrib, const String &val);
|
||||||
|
|
||||||
|
String get_attribute_value(const String &attrib);
|
||||||
|
|
||||||
|
HTMLParserAttribute *get_attribute(const String &attrib);
|
||||||
|
bool has_attribute(const String &attrib);
|
||||||
|
|
||||||
|
HTMLParserAttribute *get_attribute(const String &attrib, const String &contains_val);
|
||||||
|
bool has_attribute(const String &attrib, const String &contains_val);
|
||||||
|
|
||||||
|
void process();
|
||||||
|
void parse_args(const String &args);
|
||||||
|
|
||||||
|
String to_string(const int level = 0);
|
||||||
|
void print();
|
||||||
|
|
||||||
|
HTMLParserTag();
|
||||||
|
virtual ~HTMLParserTag();
|
||||||
|
};
|
||||||
|
|
||||||
|
class HTMLParser {
|
||||||
|
public:
|
||||||
|
HTMLParserTag *root;
|
||||||
|
|
||||||
|
void parse(const String &data);
|
||||||
|
//void parse_tag(const String &data, const int index);
|
||||||
|
|
||||||
|
String to_string();
|
||||||
|
void print();
|
||||||
|
|
||||||
|
HTMLParser();
|
||||||
|
virtual ~HTMLParser();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
243
modules/web/html/paginator.cpp
Normal file
243
modules/web/html/paginator.cpp
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
#include "paginator.h"
|
||||||
|
|
||||||
|
#include "core/math/math.h"
|
||||||
|
#include "html_builder.h"
|
||||||
|
|
||||||
|
int Paginator::get_item_count() const {
|
||||||
|
return _item_count;
|
||||||
|
}
|
||||||
|
void Paginator::set_item_count(const int val) {
|
||||||
|
_item_count = val;
|
||||||
|
_page_count = Math::floorf_int(Math::divf(val, _max_visible_links));
|
||||||
|
}
|
||||||
|
|
||||||
|
int Paginator::get_page_count() const {
|
||||||
|
return _page_count;
|
||||||
|
}
|
||||||
|
void Paginator::set_page_count(const int val) {
|
||||||
|
_page_count = val;
|
||||||
|
_item_count = val * _max_visible_links;
|
||||||
|
}
|
||||||
|
|
||||||
|
int Paginator::get_max_visible_links() const {
|
||||||
|
return _max_visible_links;
|
||||||
|
}
|
||||||
|
void Paginator::set_max_visible_links(const int val) {
|
||||||
|
_max_visible_links = val;
|
||||||
|
|
||||||
|
set_item_count(_item_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Paginator::start() {
|
||||||
|
_current_page_index = 0;
|
||||||
|
}
|
||||||
|
String Paginator::next() {
|
||||||
|
++_current_page_index;
|
||||||
|
|
||||||
|
return get_current();
|
||||||
|
}
|
||||||
|
String Paginator::get_current() {
|
||||||
|
return get_pagination_for_indx(_current_page_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Paginator::get_pagination_for_indx(const int page_index) {
|
||||||
|
if (use_links_array) {
|
||||||
|
return render_links(renderer, page_index);
|
||||||
|
} else {
|
||||||
|
return render_indexed(renderer, page_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String Paginator::get_pagination_for_num(const int page_num) {
|
||||||
|
return get_pagination_for_indx(page_num - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
String Paginator::render_indexed(Ref<Paginator> target, const int page_index) {
|
||||||
|
if (!target.is_valid()) {
|
||||||
|
target = Ref<Paginator>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = target->base_url;
|
||||||
|
if (s.size() > 0 && s[s.size() - 1] != '/') {
|
||||||
|
s += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_visible_links = target->_max_visible_links;
|
||||||
|
int page_count = target->_page_count;
|
||||||
|
const String &tclass_main_ul = target->class_main_ul;
|
||||||
|
const String &tclass_enabled_li = target->class_enabled_li;
|
||||||
|
const String &tclass_disabled_li = target->class_disabled_li;
|
||||||
|
const String &ttext_next_link = target->text_next_link;
|
||||||
|
const String &ttext_prev_link = target->text_prev_link;
|
||||||
|
|
||||||
|
int starti = page_index - max_visible_links / 2;
|
||||||
|
int toi = page_index + max_visible_links / 2;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
toi += -starti;
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toi > page_count) {
|
||||||
|
starti -= toi - page_count;
|
||||||
|
toi = page_count;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// int toi = max > max_visible_links ? max_visible_links : max;
|
||||||
|
|
||||||
|
HTMLBuilder b;
|
||||||
|
|
||||||
|
b.ul()->clsse(tclass_main_ul);
|
||||||
|
|
||||||
|
if (page_count != 0 && page_index != 0) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + std::to_string(page_index - 1))->rel_prev()->f()->w(ttext_prev_link)->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(ttext_prev_link)->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starti != toi) {
|
||||||
|
for (uint32_t i = starti; i < toi; ++i) {
|
||||||
|
if (i != page_index) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + std::to_string(i + 1))->f()->w(std::to_string(i + 1))->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(std::to_string(i + 1))->cli();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(std::to_string(1))->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_count != 0 && page_index < page_count - 1) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + std::to_string(page_index + 2))->rel_next()->f()->w(ttext_next_link)->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(ttext_next_link)->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.cul();
|
||||||
|
|
||||||
|
return b.result;
|
||||||
|
}
|
||||||
|
String Paginator::render_links(Ref<Paginator> target, const int page_index) {
|
||||||
|
if (!target.is_valid()) {
|
||||||
|
target = Ref<Paginator>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page_index < 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String s = target->base_url;
|
||||||
|
if (s.size() > 0 && s[s.size() - 1] != '/') {
|
||||||
|
s += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t max = target->links.size();
|
||||||
|
|
||||||
|
|
||||||
|
int max_visible_links = target->_max_visible_links;
|
||||||
|
int page_count = target->_page_count;
|
||||||
|
const String &tclass_main_ul = target->class_main_ul;
|
||||||
|
const String &tclass_enabled_li = target->class_enabled_li;
|
||||||
|
const String &tclass_disabled_li = target->class_disabled_li;
|
||||||
|
const String &ttext_next_link = target->text_next_link;
|
||||||
|
const String &ttext_prev_link = target->text_prev_link;
|
||||||
|
|
||||||
|
int starti = page_index - max_visible_links / 2;
|
||||||
|
int toi = page_index + max_visible_links / 2;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
toi += -starti;
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toi > max) {
|
||||||
|
starti -= toi - max;
|
||||||
|
toi = max;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// int toi = max > max_visible_links ? max_visible_links : max;
|
||||||
|
|
||||||
|
HTMLBuilder b;
|
||||||
|
|
||||||
|
b.ul()->clsse(tclass_main_ul);
|
||||||
|
|
||||||
|
if (max != 0 && page_index != 0) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + target->links[page_index - 1])->rel_prev()->f()->w(ttext_prev_link)->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(ttext_prev_link)->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starti != toi) {
|
||||||
|
for (uint32_t i = starti; i < toi; ++i) {
|
||||||
|
if (i != page_index) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + target->links[i])->f()->w(std::to_string(i + 1))->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(std::to_string(i + 1))->cli();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(std::to_string(1))->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max != 0 && page_index < max - 1) {
|
||||||
|
b.li()->clsse(tclass_enabled_li);
|
||||||
|
{
|
||||||
|
b.a()->href(s + target->links[page_index + 1])->rel_next()->f()->w(ttext_next_link)->ca();
|
||||||
|
}
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->clsse(tclass_disabled_li)->f()->w(ttext_next_link)->cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.cul();
|
||||||
|
|
||||||
|
return b.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Paginator::Paginator() {
|
||||||
|
use_links_array = false;
|
||||||
|
hide_if_one_page = false;
|
||||||
|
|
||||||
|
_item_count = 0;
|
||||||
|
_page_count = 0;
|
||||||
|
_max_visible_links = 10;
|
||||||
|
_current_page_index = 0;
|
||||||
|
|
||||||
|
class_main_ul = "pagination";
|
||||||
|
// class_enabled_li; -> no class by default
|
||||||
|
class_disabled_li = "disabled";
|
||||||
|
text_next_link = "next";
|
||||||
|
text_prev_link = "previous";
|
||||||
|
}
|
||||||
|
|
||||||
|
Paginator::~Paginator() {
|
||||||
|
renderer.unref();
|
||||||
|
}
|
57
modules/web/html/paginator.h
Normal file
57
modules/web/html/paginator.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef PAGINATOR_H
|
||||||
|
#define PAGINATOR_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
class Paginator : public Reference {
|
||||||
|
RCPP_OBJECT(Paginator, Reference);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//settint the item count will update page count and vice versa
|
||||||
|
int get_item_count() const;
|
||||||
|
void set_item_count(const int val);
|
||||||
|
|
||||||
|
int get_page_count() const;
|
||||||
|
void set_page_count(const int val);
|
||||||
|
|
||||||
|
int get_max_visible_links() const;
|
||||||
|
void set_max_visible_links(const int val);
|
||||||
|
|
||||||
|
String base_url;
|
||||||
|
Vector<String> links;
|
||||||
|
|
||||||
|
bool use_links_array;
|
||||||
|
bool hide_if_one_page;
|
||||||
|
|
||||||
|
String class_main_ul;
|
||||||
|
String class_enabled_li;
|
||||||
|
String class_disabled_li;
|
||||||
|
String text_next_link;
|
||||||
|
String text_prev_link;
|
||||||
|
|
||||||
|
void start();
|
||||||
|
String next();
|
||||||
|
String get_current();
|
||||||
|
|
||||||
|
String get_pagination_for_indx(const int page_index);
|
||||||
|
String get_pagination_for_num(const int page_num);
|
||||||
|
|
||||||
|
virtual String render_indexed(Ref<Paginator> target, const int page_index);
|
||||||
|
virtual String render_links(Ref<Paginator> target, const int page_index);
|
||||||
|
|
||||||
|
Ref<Paginator> renderer;
|
||||||
|
|
||||||
|
Paginator();
|
||||||
|
~Paginator();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int _item_count;
|
||||||
|
int _page_count;
|
||||||
|
int _max_visible_links;
|
||||||
|
int _current_page_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
180
modules/web/html/utils.cpp
Normal file
180
modules/web/html/utils.cpp
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include "html_builder.h"
|
||||||
|
#include <maddy/parser.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void Utils::markdown_to_html(String *str) {
|
||||||
|
std::shared_ptr<maddy::ParserConfig> config = std::make_shared<maddy::ParserConfig>();
|
||||||
|
config->isEmphasizedParserEnabled = false;
|
||||||
|
config->isHTMLWrappedInParagraph = true;
|
||||||
|
|
||||||
|
std::shared_ptr<maddy::Parser> parser = std::make_shared<maddy::Parser>(config);
|
||||||
|
|
||||||
|
std::stringstream ss((*str));
|
||||||
|
|
||||||
|
String htmlOutput = parser->Parse(ss);
|
||||||
|
|
||||||
|
(*str) = htmlOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Utils::get_pagination(const String &base_url, const uint32_t max, const uint32_t current_index, const uint32_t max_visible_links) {
|
||||||
|
String s = base_url;
|
||||||
|
if (s.size() > 0 && s[s.size() - 1] != '/') {
|
||||||
|
s += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
int starti = current_index - max_visible_links / 2;
|
||||||
|
int toi = current_index + max_visible_links / 2;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
toi += -starti;
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toi > max) {
|
||||||
|
starti -= toi - max;
|
||||||
|
toi = max;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//int toi = max > max_visible_links ? max_visible_links : max;
|
||||||
|
|
||||||
|
HTMLBuilder b;
|
||||||
|
|
||||||
|
b.ul()->cls("pagination");
|
||||||
|
|
||||||
|
if (max != 0 && current_index != 0) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + std::to_string(current_index - 1));
|
||||||
|
b.w("previous");
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w("previous");
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starti != toi) {
|
||||||
|
for (uint32_t i = starti; i < toi; ++i) {
|
||||||
|
if (i != current_index) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + std::to_string(i + 1));
|
||||||
|
b.w(std::to_string(i + 1));
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w(std::to_string(i + 1));
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w(std::to_string(1));
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max != 0 && current_index < max - 1) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + std::to_string(current_index + 2));
|
||||||
|
b.w("next");
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w("next");
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.cul();
|
||||||
|
|
||||||
|
return b.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Utils::get_pagination_links(const String &base_url, const Vector<String> &links, const uint32_t current_index, const uint32_t max_visible_links) {
|
||||||
|
String s = base_url;
|
||||||
|
if (s.size() > 0 && s[s.size() - 1] != '/') {
|
||||||
|
s += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t max = links.size();
|
||||||
|
|
||||||
|
int starti = current_index - max_visible_links / 2;
|
||||||
|
int toi = current_index + max_visible_links / 2;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
toi += -starti;
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toi > max) {
|
||||||
|
starti -= toi - max;
|
||||||
|
toi = max;
|
||||||
|
|
||||||
|
if (starti < 0) {
|
||||||
|
starti = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//int toi = max > max_visible_links ? max_visible_links : max;
|
||||||
|
|
||||||
|
HTMLBuilder b;
|
||||||
|
|
||||||
|
b.ul()->cls("pagination");
|
||||||
|
|
||||||
|
if (max != 0 && current_index != 0) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + links[current_index - 1]);
|
||||||
|
b.w("previous");
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w("previous");
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (starti != toi) {
|
||||||
|
for (uint32_t i = starti; i < toi; ++i) {
|
||||||
|
if (i != current_index) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + links[i]);
|
||||||
|
b.w(std::to_string(i + 1));
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w(std::to_string(i + 1));
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w(std::to_string(1));
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (max != 0 && current_index < max - 1) {
|
||||||
|
b.li();
|
||||||
|
b.a()->href(s + links[current_index + 1]);
|
||||||
|
b.w("next");
|
||||||
|
b.ca();
|
||||||
|
b.cli();
|
||||||
|
} else {
|
||||||
|
b.li()->cls("disabled");
|
||||||
|
b.w("next");
|
||||||
|
b.cli();
|
||||||
|
}
|
||||||
|
|
||||||
|
b.cul();
|
||||||
|
|
||||||
|
return b.result;
|
||||||
|
}
|
||||||
|
|
17
modules/web/html/utils.h
Normal file
17
modules/web/html/utils.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
|
||||||
|
class Utils {
|
||||||
|
public:
|
||||||
|
static void markdown_to_html(String *str);
|
||||||
|
|
||||||
|
static String get_pagination(const String &base_url, const uint32_t max, const uint32_t current_index, const uint32_t max_visible_links = 10);
|
||||||
|
static String get_pagination_links(const String &base_url, const Vector<String> &links, const uint32_t current_index, const uint32_t max_visible_links = 10);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
18
modules/web/http/cookie.cpp
Normal file
18
modules/web/http/cookie.cpp
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
#include "cookie.h"
|
||||||
|
|
||||||
|
Cookie::Cookie(const String &p_key, const String &p_value) {
|
||||||
|
http_only = true;
|
||||||
|
secure = false;
|
||||||
|
|
||||||
|
key = p_key;
|
||||||
|
value = p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cookie::Cookie() {
|
||||||
|
http_only = true;
|
||||||
|
secure = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cookie::~Cookie() {
|
||||||
|
}
|
21
modules/web/http/cookie.h
Normal file
21
modules/web/http/cookie.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef COOKIE_H
|
||||||
|
#define COOKIE_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
class Cookie {
|
||||||
|
public:
|
||||||
|
//todo date
|
||||||
|
String domain;
|
||||||
|
String path;
|
||||||
|
String key;
|
||||||
|
String value;
|
||||||
|
bool http_only;
|
||||||
|
bool secure;
|
||||||
|
|
||||||
|
Cookie();
|
||||||
|
Cookie(const String &p_key, const String &p_value);
|
||||||
|
~Cookie();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
70
modules/web/http/csrf_token.cpp
Normal file
70
modules/web/http/csrf_token.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include "csrf_token.h"
|
||||||
|
|
||||||
|
#include "crypto/hash/sha256.h"
|
||||||
|
#include "http_session.h"
|
||||||
|
#include "request.h"
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
bool CSRFTokenMiddleware::on_before_handle_request_main(Request *request) {
|
||||||
|
switch (request->get_method()) {
|
||||||
|
case HTTP_METHOD_POST:
|
||||||
|
case HTTP_METHOD_DELETE:
|
||||||
|
case HTTP_METHOD_PATCH:
|
||||||
|
case HTTP_METHOD_PUT: {
|
||||||
|
|
||||||
|
if (shold_ignore(request)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request->session.is_valid()) {
|
||||||
|
request->send_error(HTTP_STATUS_CODE_401_UNAUTHORIZED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request->validate_csrf_token()) {
|
||||||
|
request->send_error(HTTP_STATUS_CODE_401_UNAUTHORIZED);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// don't create the session itself
|
||||||
|
if (!request->session.is_valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request->has_csrf_token()) {
|
||||||
|
request->set_csrf_token(create_token());
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSRFTokenMiddleware::shold_ignore(Request *request) {
|
||||||
|
const String &path = request->get_path_full();
|
||||||
|
|
||||||
|
for (int i = 0; i < ignored_urls.size(); ++i) {
|
||||||
|
if (path.starts_with(ignored_urls[i])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String CSRFTokenMiddleware::create_token() {
|
||||||
|
Ref<SHA256> h = SHA256::get();
|
||||||
|
|
||||||
|
String s = h->compute(String::num(time(NULL)));
|
||||||
|
|
||||||
|
return s.substr(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
CSRFTokenMiddleware::CSRFTokenMiddleware() {
|
||||||
|
}
|
||||||
|
CSRFTokenMiddleware::~CSRFTokenMiddleware() {
|
||||||
|
}
|
28
modules/web/http/csrf_token.h
Normal file
28
modules/web/http/csrf_token.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef CSRF_TOKEN_H
|
||||||
|
#define CSRF_TOKEN_H
|
||||||
|
|
||||||
|
#include "middleware.h"
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
|
||||||
|
class CSRFTokenMiddleware : public Middleware {
|
||||||
|
RCPP_OBJECT(CSRFTokenMiddleware, Middleware);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//returnring true means handled, false means continue
|
||||||
|
bool on_before_handle_request_main(Request *request);
|
||||||
|
|
||||||
|
bool shold_ignore(Request *request);
|
||||||
|
|
||||||
|
virtual String create_token();
|
||||||
|
|
||||||
|
CSRFTokenMiddleware();
|
||||||
|
~CSRFTokenMiddleware();
|
||||||
|
|
||||||
|
Vector<String> ignored_urls;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
82
modules/web/http/http_enums.h
Normal file
82
modules/web/http/http_enums.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef HTTP_ENUMS_H
|
||||||
|
#define HTTP_ENUMS_H
|
||||||
|
|
||||||
|
enum HTTPMethod {
|
||||||
|
HTTP_METHOD_GET = 0,
|
||||||
|
HTTP_METHOD_POST,
|
||||||
|
HTTP_METHOD_HEAD,
|
||||||
|
HTTP_METHOD_PUT,
|
||||||
|
HTTP_METHOD_DELETE,
|
||||||
|
HTTP_METHOD_OPTIONS,
|
||||||
|
HTTP_METHOD_PATCH,
|
||||||
|
HTTP_METHOD_INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HTTPStatusCode {
|
||||||
|
HTTP_STATUS_CODE_UNKNOWN = 0,
|
||||||
|
HTTP_STATUS_CODE_100_CONTINUE = 100,
|
||||||
|
HTTP_STATUS_CODE_101_SWITCHING_PROTOCOLS = 101,
|
||||||
|
HTTP_STATUS_CODE_102_PROCESSING = 102,
|
||||||
|
HTTP_STATUS_CODE_103_EARLY_HINTS = 103,
|
||||||
|
HTTP_STATUS_CODE_200_OK = 200,
|
||||||
|
HTTP_STATUS_CODE_201_CREATED = 201,
|
||||||
|
HTTP_STATUS_CODE_202_ACCEPTED = 202,
|
||||||
|
HTTP_STATUS_CODE_203_NON_AUTHORITATIVE_INFORMATION = 203,
|
||||||
|
HTTP_STATUS_CODE_204_NO_CONTENT = 204,
|
||||||
|
HTTP_STATUS_CODE_205_RESET_CONTENT = 205,
|
||||||
|
HTTP_STATUS_CODE_206_PARTIAL_CONTENT = 206,
|
||||||
|
HTTP_STATUS_CODE_207_MULTI_STATUS = 207,
|
||||||
|
HTTP_STATUS_CODE_208_ALREADY_REPORTED = 208,
|
||||||
|
HTTP_STATUS_CODE_226_IM_USED = 226,
|
||||||
|
HTTP_STATUS_CODE_300_MULTIPLE_CHOICES = 300,
|
||||||
|
HTTP_STATUS_CODE_301_MOVED_PERMANENTLY = 301,
|
||||||
|
HTTP_STATUS_CODE_302_FOUND = 302,
|
||||||
|
HTTP_STATUS_CODE_303_SEE_OTHER = 303,
|
||||||
|
HTTP_STATUS_CODE_304_NOT_MODIFIED = 304,
|
||||||
|
HTTP_STATUS_CODE_305_USE_PROXY = 305,
|
||||||
|
HTTP_STATUS_CODE_306_UNUSED = 306,
|
||||||
|
HTTP_STATUS_CODE_307_TEMPORARY_REDIRECT = 307,
|
||||||
|
HTTP_STATUS_CODE_308_PERMANENT_REDIRECT = 308,
|
||||||
|
HTTP_STATUS_CODE_400_BAD_REQUEST = 400,
|
||||||
|
HTTP_STATUS_CODE_401_UNAUTHORIZED = 401,
|
||||||
|
HTTP_STATUS_CODE_402_PAYMENT_REQUIRED = 402,
|
||||||
|
HTTP_STATUS_CODE_403_FORBIDDEN = 403,
|
||||||
|
HTTP_STATUS_CODE_404_NOT_FOUND = 404,
|
||||||
|
HTTP_STATUS_CODE_405_METHOD_NOT_ALLOWED = 405,
|
||||||
|
HTTP_STATUS_CODE_406_NOT_ACCEPTABLE = 406,
|
||||||
|
HTTP_STATUS_CODE_407_PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||||
|
HTTP_STATUS_CODE_408_REQUEST_TIMEOUT = 408,
|
||||||
|
HTTP_STATUS_CODE_409_CONFLICT = 409,
|
||||||
|
HTTP_STATUS_CODE_410_GONE = 410,
|
||||||
|
HTTP_STATUS_CODE_411_LENGTH_REQUIRED = 411,
|
||||||
|
HTTP_STATUS_CODE_412_PRECONDITION_FAILED = 412,
|
||||||
|
HTTP_STATUS_CODE_413_REQUEST_ENTITY_TOO_LARGE = 413,
|
||||||
|
HTTP_STATUS_CODE_414_REQUEST_URI_TOO_LARGE = 414,
|
||||||
|
HTTP_STATUS_CODE_415_UNSUPPORTED_MEDIA_TYPE = 415,
|
||||||
|
HTTP_STATUS_CODE_416_REQUESTED_RANGE_NOT_SATISFIABLE = 416,
|
||||||
|
HTTP_STATUS_CODE_417_EXPECTATION_FAILED = 417,
|
||||||
|
HTTP_STATUS_CODE_418_IM_A_TEAPOT = 418,
|
||||||
|
HTTP_STATUS_CODE_421_MISDIRECTED_REQUEST = 421,
|
||||||
|
HTTP_STATUS_CODE_422_UNPROCESSABLE_ENTITY = 422,
|
||||||
|
HTTP_STATUS_CODE_423_LOCKED = 423,
|
||||||
|
HTTP_STATUS_CODE_424_FAILED_DEPENDENCY = 424,
|
||||||
|
HTTP_STATUS_CODE_425_TOO_EARLY = 425,
|
||||||
|
HTTP_STATUS_CODE_426_UPGRADE_REQUIRED = 426,
|
||||||
|
HTTP_STATUS_CODE_428_PRECONDITION_REQUIRED = 428,
|
||||||
|
HTTP_STATUS_CODE_429_TOO_MANY_REQUESTS = 429,
|
||||||
|
HTTP_STATUS_CODE_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||||
|
HTTP_STATUS_CODE_451_UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||||
|
HTTP_STATUS_CODE_500_INTERNAL_SERVER_ERROR = 500,
|
||||||
|
HTTP_STATUS_CODE_501_NOT_IMPLEMENTED = 501,
|
||||||
|
HTTP_STATUS_CODE_502_BAD_GATEWAY = 502,
|
||||||
|
HTTP_STATUS_CODE_503_SERVICE_UNAVAILABLE = 503,
|
||||||
|
HTTP_STATUS_CODE_504_GATEWAY_TIMEOUT = 504,
|
||||||
|
HTTP_STATUS_CODE_505_HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||||
|
HTTP_STATUS_CODE_506_VARIANT_ALSO_NEGOTIATES = 506,
|
||||||
|
HTTP_STATUS_CODE_507_INSUFFICIENT_STORAGE = 507,
|
||||||
|
HTTP_STATUS_CODE_508_LOOP_DETECTED = 508,
|
||||||
|
HTTP_STATUS_CODE_510_NOT_EXTENDED = 510,
|
||||||
|
HTTP_STATUS_CODE_511_NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
66
modules/web/http/http_session.cpp
Normal file
66
modules/web/http/http_session.cpp
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
#include "http_session.h"
|
||||||
|
|
||||||
|
void HTTPSession::add(const String &key, const Variant &value) {
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
_data[key] = value;
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
void HTTPSession::remove(const String &key) {
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
_data[key] = Variant();
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
bool HTTPSession::has(const String &key) {
|
||||||
|
return !_data[key].is_null();
|
||||||
|
}
|
||||||
|
|
||||||
|
Variant HTTPSession::get(const String &key) {
|
||||||
|
return _data[key];
|
||||||
|
}
|
||||||
|
const Variant &HTTPSession::get_const(const String &key) {
|
||||||
|
return _data[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
Object *HTTPSession::get_object(const String &key) {
|
||||||
|
// don't lock here
|
||||||
|
|
||||||
|
return _data[key].to_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Reference> HTTPSession::get_reference(const String &key) {
|
||||||
|
// don't lock here
|
||||||
|
|
||||||
|
return _data[key].to_reference();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HTTPSession::get_int(const String &key) {
|
||||||
|
// don't lock here
|
||||||
|
|
||||||
|
return _data[key].to_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPSession::clear() {
|
||||||
|
_data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HTTPSession::reset() {
|
||||||
|
clear();
|
||||||
|
session_id = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<String, Variant> *HTTPSession::get_data() {
|
||||||
|
return &_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPSession::HTTPSession() {
|
||||||
|
id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPSession::~HTTPSession() {
|
||||||
|
clear();
|
||||||
|
}
|
44
modules/web/http/http_session.h
Normal file
44
modules/web/http/http_session.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef HTTP_SESSION_H
|
||||||
|
#define HTTP_SESSION_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
#include "core/variant.h"
|
||||||
|
#include "core/threading/mutex.h"
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPSession : public Reference {
|
||||||
|
RCPP_OBJECT(HTTPSession, Reference);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add(const String &key, const Variant &value);
|
||||||
|
void remove(const String &key);
|
||||||
|
bool has(const String &key);
|
||||||
|
|
||||||
|
Variant get(const String &key);
|
||||||
|
const Variant &get_const(const String &key);
|
||||||
|
Object *get_object(const String &key);
|
||||||
|
Ref<Reference> get_reference(const String &key);
|
||||||
|
int get_int(const String &key);
|
||||||
|
|
||||||
|
String session_id;
|
||||||
|
int id;
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
std::map<String, Variant> *get_data();
|
||||||
|
|
||||||
|
HTTPSession();
|
||||||
|
~HTTPSession();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Mutex _mutex;
|
||||||
|
|
||||||
|
std::map<String, Variant> _data;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
14
modules/web/http/middleware.cpp
Normal file
14
modules/web/http/middleware.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
#include "middleware.h"
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
bool Middleware::on_before_handle_request_main(Request *request) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Middleware::Middleware() : Reference() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Middleware::~Middleware() {
|
||||||
|
}
|
21
modules/web/http/middleware.h
Normal file
21
modules/web/http/middleware.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef MIDDLEWARE_H
|
||||||
|
#define MIDDLEWARE_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
|
||||||
|
class Middleware : public Reference {
|
||||||
|
RCPP_OBJECT(Middleware, Reference);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//returnring true means handled, false, means continue
|
||||||
|
virtual bool on_before_handle_request_main(Request *request);
|
||||||
|
|
||||||
|
Middleware();
|
||||||
|
~Middleware();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
378
modules/web/http/request.cpp
Normal file
378
modules/web/http/request.cpp
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include "web/http/cookie.h"
|
||||||
|
#include "web_server.h"
|
||||||
|
|
||||||
|
#include "http_session.h"
|
||||||
|
|
||||||
|
#include "session_manager.h"
|
||||||
|
#include "web/http/web_root.h"
|
||||||
|
|
||||||
|
#include "web_permission.h"
|
||||||
|
|
||||||
|
Ref<HTTPSession> Request::get_or_create_session() {
|
||||||
|
if (session.is_valid()) {
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
session = SessionManager::get_singleton()->create_session();
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::can_view() const {
|
||||||
|
return (permissions & WebPermission::WEB_PERMISSION_VIEW) != 0;
|
||||||
|
}
|
||||||
|
bool Request::can_create() const {
|
||||||
|
return (permissions & WebPermission::WEB_PERMISSION_CREATE) != 0;
|
||||||
|
}
|
||||||
|
bool Request::can_edit() const {
|
||||||
|
return (permissions & WebPermission::WEB_PERMISSION_EDIT) != 0;
|
||||||
|
}
|
||||||
|
bool Request::can_delete() const {
|
||||||
|
return (permissions & WebPermission::WEB_PERMISSION_DELETE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::has_csrf_token() {
|
||||||
|
if (!session.is_valid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return session->has("csrf_token");
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_csrf_token() {
|
||||||
|
if (!session.is_valid()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const Variant &val = session->get_const("csrf_token");
|
||||||
|
|
||||||
|
if (val.is_simple_type()) {
|
||||||
|
return val.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::set_csrf_token(const String &value) {
|
||||||
|
if (session.is_valid()) {
|
||||||
|
session->add("csrf_token", value);
|
||||||
|
|
||||||
|
SessionManager::get_singleton()->save_session(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Request::validate_csrf_token() {
|
||||||
|
String param_token = get_parameter("csrf_token");
|
||||||
|
param_token.trim();
|
||||||
|
|
||||||
|
if (param_token == "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String token = get_csrf_token();
|
||||||
|
|
||||||
|
if (token == "") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return param_token == token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String Request::get_cookie(const String &key) {
|
||||||
|
static String str(0);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::add_cookie(const ::Cookie &cookie) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::remove_cookie(const String &key) {
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPMethod Request::get_method() const {
|
||||||
|
return HTTP_METHOD_GET;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::parse_files() {
|
||||||
|
}
|
||||||
|
int Request::get_file_count() const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int Request::get_file_length(const int index) const {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const uint8_t *Request::get_file_data(const int index) const {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String Request::get_parameter(const String &key) const {
|
||||||
|
static String str(0);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
HTTPStatusCode Request::get_status_code() const {
|
||||||
|
return _status_code;
|
||||||
|
}
|
||||||
|
void Request::set_status_code(const HTTPStatusCode status_code) {
|
||||||
|
_status_code = status_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::send_redirect(const String &location, const HTTPStatusCode status_code) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::compile_body() {
|
||||||
|
compiled_body.ensure_capacity(body.size() + head.size() + 15 + 13 + 14 + 15 + 1);
|
||||||
|
|
||||||
|
// 15
|
||||||
|
compiled_body += "<!DOCTYPE html>";
|
||||||
|
|
||||||
|
// 13
|
||||||
|
compiled_body += "<html>"
|
||||||
|
"<head>";
|
||||||
|
|
||||||
|
compiled_body += head;
|
||||||
|
|
||||||
|
// 14
|
||||||
|
compiled_body += "</head>"
|
||||||
|
"<body>";
|
||||||
|
|
||||||
|
compiled_body += body;
|
||||||
|
compiled_body += footer;
|
||||||
|
|
||||||
|
// 15
|
||||||
|
compiled_body += "</body>"
|
||||||
|
"</html>";
|
||||||
|
|
||||||
|
// response->setBody(compiled_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::compile_and_send_body() {
|
||||||
|
compile_body();
|
||||||
|
send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::send() {
|
||||||
|
// if (connection_closed) {
|
||||||
|
// RequestPool::return_request(this);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// RequestPool::return_request(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::send_file(const String &p_file_path) {
|
||||||
|
// RequestPool::return_request(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::send_error(int error_code) {
|
||||||
|
server->get_web_root()->handle_error_send_request(this, error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::reset() {
|
||||||
|
session = nullptr;
|
||||||
|
server = nullptr;
|
||||||
|
_path_stack.clear();
|
||||||
|
_path_stack_pointer = 0;
|
||||||
|
file_size = 0;
|
||||||
|
current_file_progress = 0;
|
||||||
|
connection_closed = false;
|
||||||
|
_full_path = "";
|
||||||
|
_status_code = HTTP_STATUS_CODE_200_OK;
|
||||||
|
// Maybe set NONE or only VIEW as default?
|
||||||
|
permissions = WebPermission::WEB_PERMISSION_ALL;
|
||||||
|
active_permission.unref();
|
||||||
|
|
||||||
|
head.clear();
|
||||||
|
body.clear();
|
||||||
|
footer.clear();
|
||||||
|
compiled_body.clear();
|
||||||
|
|
||||||
|
data.clear();
|
||||||
|
reference_data.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::parser_get_path() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::setup_url_stack() {
|
||||||
|
_full_path = parser_get_path();
|
||||||
|
String path = parser_get_path();
|
||||||
|
|
||||||
|
size_t pos = 0;
|
||||||
|
String st;
|
||||||
|
while ((pos = path.find('/')) != -1) {
|
||||||
|
st = path.substr(0, pos);
|
||||||
|
|
||||||
|
if (st.size() != 0) {
|
||||||
|
_path_stack.push_back(st);
|
||||||
|
}
|
||||||
|
|
||||||
|
path.erase(0, pos + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.size() != 0) {
|
||||||
|
_path_stack.push_back(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_path(const bool beginning_slash, const bool end_slash) const {
|
||||||
|
String path;
|
||||||
|
|
||||||
|
if (beginning_slash) {
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = _path_stack_pointer; i < _path_stack.size(); ++i) {
|
||||||
|
path += _path_stack[i];
|
||||||
|
path += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!end_slash && path.size() > 1) {
|
||||||
|
path.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &Request::get_path_full() const {
|
||||||
|
return _full_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &Request::get_path_segment(const uint32_t i) const {
|
||||||
|
return _path_stack[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &Request::get_current_path_segment() const {
|
||||||
|
if (_path_stack_pointer >= _path_stack.size()) {
|
||||||
|
// for convenience
|
||||||
|
static const String e_str = "";
|
||||||
|
return e_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _path_stack[_path_stack_pointer];
|
||||||
|
}
|
||||||
|
|
||||||
|
const String &Request::get_next_path_segment() const {
|
||||||
|
int pst = _path_stack_pointer + 1;
|
||||||
|
|
||||||
|
if (pst >= _path_stack.size()) {
|
||||||
|
// for convenience
|
||||||
|
static const String e_str = "";
|
||||||
|
return e_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _path_stack[pst];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Request::get_path_segment_count() const {
|
||||||
|
return _path_stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Request::get_current_segment_index() const {
|
||||||
|
return _path_stack_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Request::get_remaining_segment_count() const {
|
||||||
|
if (_path_stack_pointer > _path_stack.size()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _path_stack.size() - _path_stack_pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::pop_path() {
|
||||||
|
_path_stack_pointer -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::push_path() {
|
||||||
|
_path_stack_pointer += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_url_root_parent(const int parent) const {
|
||||||
|
String path = "/";
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < _path_stack_pointer - parent; ++i) {
|
||||||
|
path += _path_stack[i];
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_url_root() const {
|
||||||
|
int pst = _path_stack_pointer + 1;
|
||||||
|
|
||||||
|
if (pst > _path_stack.size()) {
|
||||||
|
pst = _path_stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = "/";
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < _path_stack_pointer; ++i) {
|
||||||
|
path += _path_stack[i];
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_url_root_current() const {
|
||||||
|
int pst = _path_stack_pointer + 1;
|
||||||
|
|
||||||
|
if (pst > _path_stack.size()) {
|
||||||
|
pst = _path_stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = "/";
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < pst; ++i) {
|
||||||
|
path += _path_stack[i];
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_url_site() const {
|
||||||
|
String path = get_host();
|
||||||
|
|
||||||
|
for (uint32_t i = _path_stack_pointer; i < _path_stack.size(); ++i) {
|
||||||
|
path += _path_stack[i];
|
||||||
|
path += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_url_root_parent(const String &add) const {
|
||||||
|
return get_url_root_parent() + add;
|
||||||
|
}
|
||||||
|
String Request::get_url_root(const String &add) const {
|
||||||
|
return get_url_root() + add;
|
||||||
|
}
|
||||||
|
String Request::get_url_site(const String &add) const {
|
||||||
|
return get_url_site() + add;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Request::get_host() const {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::update() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Request::pool() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Request::Request() {
|
||||||
|
// This value will need benchmarks, 2 MB seems to be just as fast for me as 4 MB, but 1MB is slower
|
||||||
|
// It is a tradeoff on server memory though, as every active download will consume this amount of memory
|
||||||
|
// where the file is bigger than this number
|
||||||
|
file_chunk_size = 1 << 21; // 2MB
|
||||||
|
}
|
||||||
|
|
||||||
|
Request::~Request() {
|
||||||
|
}
|
175
modules/web/http/request.h
Normal file
175
modules/web/http/request.h
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
#ifndef REQUEST_H
|
||||||
|
#define REQUEST_H
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
#include "http_enums.h"
|
||||||
|
|
||||||
|
class WebServer;
|
||||||
|
class Cookie;
|
||||||
|
class HTTPSession;
|
||||||
|
class WebPermission;
|
||||||
|
|
||||||
|
class Request {
|
||||||
|
public:
|
||||||
|
WebServer *server;
|
||||||
|
|
||||||
|
String head;
|
||||||
|
String body;
|
||||||
|
String footer;
|
||||||
|
String compiled_body;
|
||||||
|
|
||||||
|
String file_path;
|
||||||
|
long file_size;
|
||||||
|
long current_file_progress;
|
||||||
|
long file_chunk_size;
|
||||||
|
bool file_next;
|
||||||
|
|
||||||
|
bool connection_closed;
|
||||||
|
|
||||||
|
Ref<HTTPSession> session;
|
||||||
|
std::map<String, Object *> data;
|
||||||
|
std::map<String, Ref<Reference> > reference_data;
|
||||||
|
|
||||||
|
Ref<HTTPSession> get_or_create_session();
|
||||||
|
|
||||||
|
Ref<WebPermission> active_permission;
|
||||||
|
int permissions;
|
||||||
|
|
||||||
|
bool can_view() const;
|
||||||
|
bool can_create() const;
|
||||||
|
bool can_edit() const;
|
||||||
|
bool can_delete() const;
|
||||||
|
|
||||||
|
bool has_csrf_token();
|
||||||
|
String get_csrf_token();
|
||||||
|
void set_csrf_token(const String &value);
|
||||||
|
bool validate_csrf_token();
|
||||||
|
|
||||||
|
virtual const String get_cookie(const String &key);
|
||||||
|
virtual void add_cookie(const ::Cookie &cookie);
|
||||||
|
virtual void remove_cookie(const String &key);
|
||||||
|
|
||||||
|
virtual HTTPMethod get_method() const;
|
||||||
|
|
||||||
|
virtual void parse_files();
|
||||||
|
virtual int get_file_count() const;
|
||||||
|
virtual int get_file_length(const int index) const;
|
||||||
|
virtual const uint8_t *get_file_data(const int index) const;
|
||||||
|
|
||||||
|
virtual const String get_parameter(const String &key) const;
|
||||||
|
|
||||||
|
HTTPStatusCode get_status_code() const;
|
||||||
|
void set_status_code(const HTTPStatusCode status_code);
|
||||||
|
|
||||||
|
virtual void send_redirect(const String &location, const HTTPStatusCode status_code = HTTP_STATUS_CODE_302_FOUND);
|
||||||
|
virtual void compile_body();
|
||||||
|
virtual void compile_and_send_body();
|
||||||
|
virtual void send();
|
||||||
|
virtual void send_file(const String &p_file_path);
|
||||||
|
virtual void send_error(int error_code);
|
||||||
|
virtual void reset();
|
||||||
|
virtual String parser_get_path();
|
||||||
|
virtual String get_host() const;
|
||||||
|
|
||||||
|
void setup_url_stack();
|
||||||
|
String get_path(const bool beginning_slash = false, const bool end_slash = true) const;
|
||||||
|
virtual const String &get_path_full() const;
|
||||||
|
const String &get_path_segment(const uint32_t i) const;
|
||||||
|
const String &get_current_path_segment() const;
|
||||||
|
const String &get_next_path_segment() const;
|
||||||
|
uint32_t get_path_segment_count() const;
|
||||||
|
uint32_t get_current_segment_index() const;
|
||||||
|
uint32_t get_remaining_segment_count() const;
|
||||||
|
void pop_path();
|
||||||
|
void push_path();
|
||||||
|
|
||||||
|
String get_url_root_parent(const int parent = 1) const;
|
||||||
|
String get_url_root() const;
|
||||||
|
String get_url_root_current() const;
|
||||||
|
String get_url_site() const;
|
||||||
|
|
||||||
|
String get_url_root_parent(const String &add) const;
|
||||||
|
String get_url_root(const String &add) const;
|
||||||
|
String get_url_site(const String &add) const;
|
||||||
|
|
||||||
|
virtual void update();
|
||||||
|
virtual void pool();
|
||||||
|
|
||||||
|
Request();
|
||||||
|
virtual ~Request();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
HTTPStatusCode _status_code;
|
||||||
|
String _full_path;
|
||||||
|
Vector<String> _path_stack;
|
||||||
|
uint32_t _path_stack_pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class RequestPool {
|
||||||
|
public:
|
||||||
|
T *get_request();
|
||||||
|
void return_request(T *request);
|
||||||
|
|
||||||
|
RequestPool();
|
||||||
|
~RequestPool();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::mutex _mutex;
|
||||||
|
std::vector<T *> _requests;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
T *RequestPool<T>::get_request() {
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
T *request;
|
||||||
|
|
||||||
|
if (_requests.size() == 0) {
|
||||||
|
_mutex.unlock();
|
||||||
|
|
||||||
|
request = new T();
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
request = _requests[_requests.size() - 1];
|
||||||
|
_requests.pop_back();
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
|
||||||
|
request->reset();
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void RequestPool<T>::return_request(T *request) {
|
||||||
|
_mutex.lock();
|
||||||
|
_requests.push_back(request);
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
RequestPool<T>::RequestPool() {
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
RequestPool<T>::~RequestPool() {
|
||||||
|
for (uint32_t i = 0; i < _requests.size(); ++i) {
|
||||||
|
delete _requests[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
_requests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
315
modules/web/http/session_manager.cpp
Normal file
315
modules/web/http/session_manager.cpp
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
#include "session_manager.h"
|
||||||
|
|
||||||
|
#include "http_session.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "database/database_manager.h"
|
||||||
|
#include "database/query_builder.h"
|
||||||
|
#include "database/query_result.h"
|
||||||
|
#include "database/table_builder.h"
|
||||||
|
|
||||||
|
#include "crypto/hash/sha256.h"
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include "cookie.h"
|
||||||
|
|
||||||
|
void SessionManager::add_session(Ref<HTTPSession> &session) {
|
||||||
|
if (!session.is_valid()) {
|
||||||
|
printf("SessionManager::add_session: ERROR, session is null!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
_sessions_vec.push_back(session);
|
||||||
|
_sessions[session->session_id] = session;
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::remove_session(Ref<HTTPSession> &session) {
|
||||||
|
if (!session.is_valid()) {
|
||||||
|
printf("SessionManager::remove_session: ERROR, session is null!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
_sessions.erase(session->session_id);
|
||||||
|
|
||||||
|
for (int i = 0; i < _sessions_vec.size(); ++i) {
|
||||||
|
if (_sessions_vec[i] == session) {
|
||||||
|
_sessions_vec[i] = _sessions_vec[_sessions_vec.size() - 1];
|
||||||
|
_sessions_vec.pop_back();
|
||||||
|
_mutex.unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::delete_session(const String &session_id) {
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
Ref<HTTPSession> s = _sessions[session_id];
|
||||||
|
|
||||||
|
_sessions.erase(session_id);
|
||||||
|
|
||||||
|
for (int i = 0; i < _sessions_vec.size(); ++i) {
|
||||||
|
Ref<HTTPSession> sess = _sessions_vec[i];
|
||||||
|
|
||||||
|
if (sess->session_id == session_id) {
|
||||||
|
|
||||||
|
_sessions_vec[i] = _sessions_vec[_sessions_vec.size() - 1];
|
||||||
|
_sessions_vec.pop_back();
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
|
||||||
|
if (!s.is_valid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s->id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<QueryBuilder> b = DatabaseManager::get_singleton()->ddb->get_query_builder();
|
||||||
|
|
||||||
|
b->del(_data_table_name)->where()->wp("session_db_id", s->id)->end_command();
|
||||||
|
b->del(_table_name)->where()->wp("id", s->id)->end_command();
|
||||||
|
b->run_query();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::save_session(Ref<HTTPSession> &session) {
|
||||||
|
Ref<QueryBuilder> b = DatabaseManager::get_singleton()->ddb->get_query_builder();
|
||||||
|
|
||||||
|
if (!session->id) {
|
||||||
|
b->insert(_table_name, "session_id");
|
||||||
|
b->values();
|
||||||
|
b->val(session->session_id);
|
||||||
|
b->cvalues();
|
||||||
|
b->end_command();
|
||||||
|
b->select_last_insert_id();
|
||||||
|
|
||||||
|
session->id = b->run()->get_last_insert_rowid();
|
||||||
|
|
||||||
|
b->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
b->del(_data_table_name)->where()->wp("session_db_id", session->id)->end_command();
|
||||||
|
int id = session->id;
|
||||||
|
|
||||||
|
std::map<String, Variant> *m = session->get_data();
|
||||||
|
for (std::map<String, Variant>::iterator it = m->begin(); it != m->end(); it++) {
|
||||||
|
const Variant &val = it->second;
|
||||||
|
|
||||||
|
if (val.is_simple_type()) {
|
||||||
|
b->insert(_data_table_name, "session_db_id,key,value")->values()->val(id)->val(it->first)->val(val.to_string())->cvalues()->end_command();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b->run_query();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<HTTPSession> SessionManager::get_session(const String &session_id) {
|
||||||
|
return _sessions[session_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<HTTPSession> SessionManager::create_session() {
|
||||||
|
Ref<HTTPSession> session = new HTTPSession();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
session->session_id = generate_session_id(session->session_id);
|
||||||
|
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
if (_sessions[session->session_id] == nullptr) {
|
||||||
|
|
||||||
|
_sessions_vec.push_back(session);
|
||||||
|
_sessions[session->session_id] = session;
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
save_session(session);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::load_sessions() {
|
||||||
|
clear();
|
||||||
|
|
||||||
|
Ref<QueryBuilder> b = DatabaseManager::get_singleton()->ddb->get_query_builder();
|
||||||
|
|
||||||
|
b->select("id, session_id");
|
||||||
|
b->from(_table_name);
|
||||||
|
// b->print();
|
||||||
|
Ref<QueryResult> r = b->run();
|
||||||
|
|
||||||
|
while (r->next_row()) {
|
||||||
|
int id = r->get_cell_int(0);
|
||||||
|
String session_id = r->get_cell(1);
|
||||||
|
|
||||||
|
Ref<HTTPSession> s = new HTTPSession();
|
||||||
|
s->id = id;
|
||||||
|
|
||||||
|
s->session_id = session_id;
|
||||||
|
|
||||||
|
add_session(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
b->reset();
|
||||||
|
|
||||||
|
b->select("session_db_id,key,value");
|
||||||
|
b->from(_data_table_name);
|
||||||
|
// b->print();
|
||||||
|
r = b->run();
|
||||||
|
|
||||||
|
while (r->next_row()) {
|
||||||
|
int session_db_id = r->get_cell_int(0);
|
||||||
|
|
||||||
|
Ref<HTTPSession> s;
|
||||||
|
|
||||||
|
for (int i = 0; i < _sessions_vec.size(); ++i) {
|
||||||
|
Ref<HTTPSession> ss = _sessions_vec[i];
|
||||||
|
|
||||||
|
if (ss.is_valid() && session_db_id == ss->id) {
|
||||||
|
s = ss;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s.is_valid()) {
|
||||||
|
printf("Error: SessionManager::load_sessions(): %d sid doesn't exists!\n", session_db_id);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String key = r->get_cell(1);
|
||||||
|
String value = r->get_cell(2);
|
||||||
|
|
||||||
|
s->add(key, Variant::parse_string(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::clear() {
|
||||||
|
_mutex.lock();
|
||||||
|
|
||||||
|
_sessions.clear();
|
||||||
|
_sessions_vec.clear();
|
||||||
|
|
||||||
|
_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
String SessionManager::generate_session_id(const String &base) {
|
||||||
|
// todo make something simpler / better
|
||||||
|
|
||||||
|
Ref<SHA256> h = SHA256::get();
|
||||||
|
String sid = base;
|
||||||
|
|
||||||
|
sid += rand();
|
||||||
|
h->compute(sid);
|
||||||
|
|
||||||
|
sid = h->get_hash();
|
||||||
|
sid.resize(20);
|
||||||
|
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::migrate() {
|
||||||
|
drop_table();
|
||||||
|
create_table();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SessionManager::create_table() {
|
||||||
|
Ref<TableBuilder> tb = DatabaseManager::get_singleton()->ddb->get_table_builder();
|
||||||
|
|
||||||
|
tb->create_table(_table_name);
|
||||||
|
tb->integer("id")->auto_increment()->next_row();
|
||||||
|
tb->varchar("session_id", 100)->next_row();
|
||||||
|
tb->primary_key("id");
|
||||||
|
tb->ccreate_table();
|
||||||
|
// tb->print();
|
||||||
|
tb->run_query();
|
||||||
|
|
||||||
|
tb->result = "";
|
||||||
|
|
||||||
|
tb->create_table(_data_table_name);
|
||||||
|
tb->integer("session_db_id")->not_null()->next_row();
|
||||||
|
tb->varchar("key", 100)->next_row();
|
||||||
|
tb->text("value")->not_null()->next_row();
|
||||||
|
tb->foreign_key("session_db_id");
|
||||||
|
tb->references(_table_name, "id");
|
||||||
|
tb->ccreate_table();
|
||||||
|
// tb->print();
|
||||||
|
tb->run_query();
|
||||||
|
}
|
||||||
|
void SessionManager::drop_table() {
|
||||||
|
Ref<TableBuilder> tb = DatabaseManager::get_singleton()->ddb->get_table_builder();
|
||||||
|
|
||||||
|
tb->drop_table_if_exists(_table_name)->run_query();
|
||||||
|
tb->drop_table_if_exists(_data_table_name)->run_query();
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager *SessionManager::get_singleton() {
|
||||||
|
return _self;
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager::SessionManager() :
|
||||||
|
Object() {
|
||||||
|
|
||||||
|
if (_self) {
|
||||||
|
printf("SessionManager::SessionManager(): Error! self is not null!/n");
|
||||||
|
}
|
||||||
|
|
||||||
|
_self = this;
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager::~SessionManager() {
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (_self == this) {
|
||||||
|
_self = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager *SessionManager::_self = nullptr;
|
||||||
|
String SessionManager::_table_name = "sessions";
|
||||||
|
String SessionManager::_data_table_name = "session_data";
|
||||||
|
|
||||||
|
bool SessionSetupMiddleware::on_before_handle_request_main(Request *request) {
|
||||||
|
const String sid = request->get_cookie("session_id");
|
||||||
|
|
||||||
|
if (sid == "") {
|
||||||
|
// You could create a session here if you want to always assign sessions to visitors.
|
||||||
|
// Example code:
|
||||||
|
// HTTPSession *session = SessionManager::get_singleton()->create_session();
|
||||||
|
// request->session = session;
|
||||||
|
// request->add_cookie(::Cookie("session_id", session->session_id));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
request->session = SessionManager::get_singleton()->get_session(sid);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionSetupMiddleware::SessionSetupMiddleware() {
|
||||||
|
}
|
||||||
|
SessionSetupMiddleware::~SessionSetupMiddleware() {
|
||||||
|
}
|
64
modules/web/http/session_manager.h
Normal file
64
modules/web/http/session_manager.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef SESSION_MANAGER_H
|
||||||
|
#define SESSION_MANAGER_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "core/threading/mutex.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "core/object.h"
|
||||||
|
|
||||||
|
#include "middleware.h"
|
||||||
|
|
||||||
|
class HTTPSession;
|
||||||
|
class Request;
|
||||||
|
|
||||||
|
class SessionManager : public Object {
|
||||||
|
RCPP_OBJECT(SessionManager, Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void add_session(Ref<HTTPSession> &session);
|
||||||
|
void remove_session(Ref<HTTPSession> &session);
|
||||||
|
void delete_session(const String &session_id);
|
||||||
|
void save_session(Ref<HTTPSession> &session);
|
||||||
|
Ref<HTTPSession> get_session(const String &session_id);
|
||||||
|
Ref<HTTPSession> create_session();
|
||||||
|
|
||||||
|
void load_sessions();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
virtual String generate_session_id(const String &base = "");
|
||||||
|
|
||||||
|
virtual void migrate();
|
||||||
|
virtual void create_table();
|
||||||
|
virtual void drop_table();
|
||||||
|
|
||||||
|
static SessionManager *get_singleton();
|
||||||
|
|
||||||
|
SessionManager();
|
||||||
|
~SessionManager();
|
||||||
|
|
||||||
|
std::map<String, Ref<HTTPSession>> _sessions;
|
||||||
|
Vector<Ref<HTTPSession>> _sessions_vec;
|
||||||
|
Mutex _mutex;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static SessionManager *_self;
|
||||||
|
|
||||||
|
static String _table_name;
|
||||||
|
static String _data_table_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SessionSetupMiddleware : public Middleware {
|
||||||
|
RCPP_OBJECT(SessionSetupMiddleware, Middleware);
|
||||||
|
|
||||||
|
public:
|
||||||
|
//returnring true means handled, false means continue
|
||||||
|
bool on_before_handle_request_main(Request *request);
|
||||||
|
|
||||||
|
SessionSetupMiddleware();
|
||||||
|
~SessionSetupMiddleware();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
345
modules/web/http/web_node.cpp
Normal file
345
modules/web/http/web_node.cpp
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
|
||||||
|
#include "web_node.h"
|
||||||
|
|
||||||
|
#include "http_enums.h"
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include "core/settings/settings.h"
|
||||||
|
#include "web/http/web_server.h"
|
||||||
|
#include "web_permission.h"
|
||||||
|
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
#include "database/database.h"
|
||||||
|
#include "database/database_manager.h"
|
||||||
|
#include "database/query_builder.h"
|
||||||
|
#include "database/query_result.h"
|
||||||
|
#include "database/table_builder.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String WebNode::get_uri_segment() {
|
||||||
|
return _uri_segment;
|
||||||
|
}
|
||||||
|
void WebNode::set_uri_segment(const String &val) {
|
||||||
|
_uri_segment = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
String WebNode::get_full_uri(const bool slash_at_the_end) {
|
||||||
|
if (slash_at_the_end) {
|
||||||
|
return get_full_uri_parent(true) + _uri_segment + '/';
|
||||||
|
} else {
|
||||||
|
return get_full_uri_parent(true) + _uri_segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String WebNode::get_full_uri_parent(const bool slash_at_the_end) {
|
||||||
|
String uri = "/";
|
||||||
|
|
||||||
|
WebNode *n = get_parent_webnode();
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
if (n->_uri_segment == "" || n->_uri_segment == '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = "/" + n->_uri_segment + uri;
|
||||||
|
|
||||||
|
n = n->get_parent_webnode();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slash_at_the_end) {
|
||||||
|
uri.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings *WebNode::get_settings() {
|
||||||
|
if (_settings) {
|
||||||
|
return _settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Settings::get_singleton();
|
||||||
|
}
|
||||||
|
void WebNode::set_settings(Settings *settings) {
|
||||||
|
_settings = settings;
|
||||||
|
|
||||||
|
// todo send event to children when it's implemented?
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<WebPermission> WebNode::get_web_permission() {
|
||||||
|
return _web_permission;
|
||||||
|
}
|
||||||
|
void WebNode::set_web_permission(const Ref<WebPermission> &wp) {
|
||||||
|
_web_permission = wp;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebNode::get_routing_enabled() {
|
||||||
|
return _routing_enabled;
|
||||||
|
}
|
||||||
|
void WebNode::set_routing_enabled(const bool value) {
|
||||||
|
if (_routing_enabled == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_routing_enabled = value;
|
||||||
|
|
||||||
|
if (!_routing_enabled) {
|
||||||
|
clear_handlers();
|
||||||
|
} else {
|
||||||
|
if (is_in_tree()) {
|
||||||
|
build_handler_map();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
|
||||||
|
Database *WebNode::get_database() {
|
||||||
|
if (_database) {
|
||||||
|
return _database;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DatabaseManager::get_singleton()->ddb;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<TableBuilder> WebNode::get_table_builder() {
|
||||||
|
Database *db = get_database();
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(!db, Ref<TableBuilder>());
|
||||||
|
|
||||||
|
return db->get_table_builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<QueryBuilder> WebNode::get_query_builder() {
|
||||||
|
Database *db = get_database();
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(!db, Ref<QueryBuilder>());
|
||||||
|
|
||||||
|
return db->get_query_builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::set_database(Database *db) {
|
||||||
|
_database = db;
|
||||||
|
|
||||||
|
// todo send event to children when it's implemented?
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void WebNode::handle_request_main(Request *request) {
|
||||||
|
if (_web_permission.is_valid()) {
|
||||||
|
if (_web_permission->activate(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_routing_enabled) {
|
||||||
|
_handle_request_main(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!try_route_request_to_children(request)) {
|
||||||
|
_handle_request_main(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::_handle_request_main(Request *request) {
|
||||||
|
request->send_error(HTTP_STATUS_CODE_404_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::handle_error_send_request(Request *request, const int error_code) {
|
||||||
|
// this is a fallback error handler.
|
||||||
|
// Webroot implements a proper one
|
||||||
|
request->compiled_body = "<html><body>Internal server error!</body></html>";
|
||||||
|
request->set_status_code(HTTP_STATUS_CODE_503_SERVICE_UNAVAILABLE);
|
||||||
|
request->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::render_index(Request *request) {
|
||||||
|
}
|
||||||
|
void WebNode::render_preview(Request *request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::render_menu(Request *request) {
|
||||||
|
WebNode *root = get_root();
|
||||||
|
|
||||||
|
if (root) {
|
||||||
|
root->_render_menu(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void WebNode::_render_menu(Request *request) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::create_validators() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::create_table() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::drop_table() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::udpate_table() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::create_default_entries() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::migrate(const bool clear, const bool seed_db) {
|
||||||
|
_migrate(clear, seed_db);
|
||||||
|
|
||||||
|
for (int i = 0; i < _children.size(); ++i) {
|
||||||
|
WebNode *c = Object::cast_to<WebNode>(_children[i]);
|
||||||
|
c->migrate(clear, seed_db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::_migrate(const bool clear, const bool seed_db) {
|
||||||
|
if (clear) {
|
||||||
|
drop_table();
|
||||||
|
create_table();
|
||||||
|
} else {
|
||||||
|
udpate_table();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seed_db) {
|
||||||
|
create_default_entries();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebNode::try_route_request_to_children(Request *request) {
|
||||||
|
WebNode *handler = nullptr;
|
||||||
|
|
||||||
|
// if (path == "/") {
|
||||||
|
if (request->get_path_segment_count() == 0) {
|
||||||
|
// quick shortcut
|
||||||
|
handler = _index_node;
|
||||||
|
} else {
|
||||||
|
const String &main_route = request->get_current_path_segment();
|
||||||
|
|
||||||
|
handler = _node_route_map[main_route];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!handler) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
request->push_path();
|
||||||
|
handler->handle_request_main(request);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNode *WebNode::get_request_handler_child(Request *request) {
|
||||||
|
WebNode *handler = nullptr;
|
||||||
|
|
||||||
|
// if (path == "/") {
|
||||||
|
if (request->get_path_segment_count() == 0) {
|
||||||
|
// quick shortcut
|
||||||
|
handler = _index_node;
|
||||||
|
} else {
|
||||||
|
const String &main_route = request->get_current_path_segment();
|
||||||
|
handler = _node_route_map[main_route];
|
||||||
|
}
|
||||||
|
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::build_handler_map() {
|
||||||
|
_index_node = nullptr;
|
||||||
|
_node_route_map.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < get_child_count(); ++i) {
|
||||||
|
WebNode *c = Object::cast_to<WebNode>(get_child(i));
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri_segment = c->get_uri_segment();
|
||||||
|
|
||||||
|
if (uri_segment == "") {
|
||||||
|
// ignore
|
||||||
|
continue;
|
||||||
|
} else if (uri_segment == "/") {
|
||||||
|
if (_index_node) {
|
||||||
|
RLOG_ERR("You have multiple root nodes!");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_index_node = c;
|
||||||
|
} else {
|
||||||
|
if (_node_route_map[uri_segment]) {
|
||||||
|
RLOG_ERR("You have multiple of the same uri! URI:" + uri_segment);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_node_route_map[uri_segment] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::clear_handlers() {
|
||||||
|
_index_node = nullptr;
|
||||||
|
_node_route_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebServer *WebNode::get_server() {
|
||||||
|
// todo this shoult probably be cached
|
||||||
|
return Object::cast_to<WebServer>(get_tree());
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNode *WebNode::get_root() {
|
||||||
|
WebServer *s = get_server();
|
||||||
|
|
||||||
|
if (!s) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return s->get_web_root();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNode *WebNode::get_parent_webnode() {
|
||||||
|
return Object::cast_to<WebNode>(get_parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebNode::_notification(const int what) {
|
||||||
|
switch (what) {
|
||||||
|
case NOTIFICATION_ENTER_TREE:
|
||||||
|
if (_routing_enabled) {
|
||||||
|
build_handler_map();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NOTIFICATION_CHILD_ADDED:
|
||||||
|
if (_routing_enabled && is_in_tree()) {
|
||||||
|
build_handler_map();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NOTIFICATION_CHILD_REMOVED:
|
||||||
|
if (_routing_enabled && is_in_tree()) {
|
||||||
|
build_handler_map();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNode::WebNode() :
|
||||||
|
Node() {
|
||||||
|
// should look this up in parents when parented (and node parenting is implemented)
|
||||||
|
// should have an event later when a parent gets one
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
_database = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// same for this
|
||||||
|
_settings = nullptr;
|
||||||
|
|
||||||
|
_routing_enabled = true;
|
||||||
|
_index_node = nullptr;
|
||||||
|
|
||||||
|
create_validators();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebNode::~WebNode() {
|
||||||
|
}
|
96
modules/web/http/web_node.h
Normal file
96
modules/web/http/web_node.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#ifndef WEB_NODE_H
|
||||||
|
#define WEB_NODE_H
|
||||||
|
|
||||||
|
#include "core/nodes/node.h"
|
||||||
|
#include "core/reference.h"
|
||||||
|
#include "core/variant.h"
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class Settings;
|
||||||
|
class WebServer;
|
||||||
|
class WebPermission;
|
||||||
|
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
class DataBase;
|
||||||
|
class TableBuilder;
|
||||||
|
class QueryBuilder;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class WebNode : public Node {
|
||||||
|
RCPP_OBJECT(WebNode, Node);
|
||||||
|
|
||||||
|
public:
|
||||||
|
String get_uri_segment();
|
||||||
|
void set_uri_segment(const String &val);
|
||||||
|
|
||||||
|
virtual String get_full_uri(const bool slash_at_the_end = true);
|
||||||
|
virtual String get_full_uri_parent(const bool slash_at_the_end = true);
|
||||||
|
|
||||||
|
Settings *get_settings();
|
||||||
|
void set_settings(Settings *settings);
|
||||||
|
|
||||||
|
Ref<WebPermission> get_web_permission();
|
||||||
|
void set_web_permission(const Ref<WebPermission> &wp);
|
||||||
|
|
||||||
|
virtual bool get_routing_enabled();
|
||||||
|
virtual void set_routing_enabled(const bool value);
|
||||||
|
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
Database *get_database();
|
||||||
|
Ref<TableBuilder> get_table_builder();
|
||||||
|
Ref<QueryBuilder> get_query_builder();
|
||||||
|
void set_database(Database *db);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
virtual void handle_request_main(Request *request);
|
||||||
|
virtual void _handle_request_main(Request *request);
|
||||||
|
virtual void handle_error_send_request(Request *request, const int error_code);
|
||||||
|
|
||||||
|
virtual void render_index(Request *request);
|
||||||
|
virtual void render_preview(Request *request);
|
||||||
|
|
||||||
|
virtual void render_menu(Request *request);
|
||||||
|
virtual void _render_menu(Request *request);
|
||||||
|
|
||||||
|
virtual void create_validators();
|
||||||
|
|
||||||
|
virtual void create_table();
|
||||||
|
virtual void drop_table();
|
||||||
|
virtual void udpate_table();
|
||||||
|
virtual void create_default_entries();
|
||||||
|
|
||||||
|
virtual void migrate(const bool clear, const bool seed);
|
||||||
|
virtual void _migrate(const bool clear, const bool seed);
|
||||||
|
|
||||||
|
bool try_route_request_to_children(Request *request);
|
||||||
|
WebNode *get_request_handler_child(Request *request);
|
||||||
|
void build_handler_map();
|
||||||
|
void clear_handlers();
|
||||||
|
|
||||||
|
WebServer *get_server();
|
||||||
|
WebNode *get_root();
|
||||||
|
WebNode *get_parent_webnode();
|
||||||
|
|
||||||
|
void _notification(const int what);
|
||||||
|
|
||||||
|
WebNode();
|
||||||
|
~WebNode();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
String _uri_segment;
|
||||||
|
|
||||||
|
Settings *_settings;
|
||||||
|
|
||||||
|
#ifdef DATABASES_ENABLED
|
||||||
|
Database *_database;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool _routing_enabled;
|
||||||
|
WebNode *_index_node;
|
||||||
|
std::map<String, WebNode *> _node_route_map;
|
||||||
|
|
||||||
|
Ref<WebPermission> _web_permission;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
31
modules/web/http/web_permission.cpp
Normal file
31
modules/web/http/web_permission.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
#include "web_permission.h"
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
bool WebPermission::activate(Request *request) {
|
||||||
|
request->active_permission.reference_ptr(this);
|
||||||
|
request->permissions = _get_permissions(request);
|
||||||
|
|
||||||
|
if (!request->can_view()) {
|
||||||
|
handle_view_permission_missing(request);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebPermission::_get_permissions(Request *request) {
|
||||||
|
return WEB_PERMISSION_ALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebPermission::handle_view_permission_missing(Request *request) {
|
||||||
|
request->send_error(HTTP_STATUS_CODE_404_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPermission::WebPermission() :
|
||||||
|
Reference() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WebPermission::~WebPermission() {
|
||||||
|
}
|
33
modules/web/http/web_permission.h
Normal file
33
modules/web/http/web_permission.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef WEB_PERMISSION_H
|
||||||
|
#define WEB_PERMISSION_H
|
||||||
|
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
#include "core/reference.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
|
||||||
|
class WebPermission : public Reference {
|
||||||
|
RCPP_OBJECT(WebPermission, Reference);
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum WebPermissions {
|
||||||
|
WEB_PERMISSION_VIEW = 1 << 0,
|
||||||
|
WEB_PERMISSION_CREATE = 1 << 1,
|
||||||
|
WEB_PERMISSION_EDIT = 1 << 2,
|
||||||
|
WEB_PERMISSION_DELETE = 1 << 3,
|
||||||
|
|
||||||
|
WEB_PERMISSION_ALL = WEB_PERMISSION_VIEW | WEB_PERMISSION_CREATE | WEB_PERMISSION_EDIT | WEB_PERMISSION_DELETE,
|
||||||
|
WEB_PERMISSION_NONE = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
//like in middlewate returns whether it handled the request or not
|
||||||
|
virtual bool activate(Request *request);
|
||||||
|
virtual int _get_permissions(Request *request);
|
||||||
|
virtual void handle_view_permission_missing(Request *request);
|
||||||
|
|
||||||
|
WebPermission();
|
||||||
|
~WebPermission();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
183
modules/web/http/web_root.cpp
Normal file
183
modules/web/http/web_root.cpp
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
#include "web_root.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
|
||||||
|
#include "web/file_cache.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "web_permission.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
void WebRoot::setup() {
|
||||||
|
setup_error_handlers();
|
||||||
|
setup_middleware();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::setup_error_handlers() {
|
||||||
|
default_error_handler_func = WebRoot::default_fallback_error_handler;
|
||||||
|
error_handler_map[404] = WebRoot::default_404_error_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::setup_middleware() {
|
||||||
|
// Middlewares get processed in the order they are in the _middlewares array
|
||||||
|
|
||||||
|
// ------- Built in middleware selection -------
|
||||||
|
|
||||||
|
// --- SessionSetupMiddleware ---
|
||||||
|
// If you want sessions add this to your inherited class.
|
||||||
|
// (#include "web/http/session_manager.h")
|
||||||
|
|
||||||
|
// _middlewares.push_back(Ref<SessionSetupMiddleware>(new SessionSetupMiddleware()));
|
||||||
|
|
||||||
|
// --- UserSessionSetupMiddleware ---
|
||||||
|
// This one looks up users based on sessions
|
||||||
|
// (#include "web_modules/users/user_controller.h")
|
||||||
|
|
||||||
|
// _middlewares.push_back(Ref<UserSessionSetupMiddleware>(new UserSessionSetupMiddleware()));
|
||||||
|
|
||||||
|
// --- RBACUserSessionSetupMiddleware / RBACDefaultUserSessionSetupMiddleware ---
|
||||||
|
// Same as the previous, but if you want the RBAC system to work use one of these
|
||||||
|
// UserSessionSetupMiddleware is not needed if you need these
|
||||||
|
// (#include "web_modules/rbac_users/rbac_user_controller.h")
|
||||||
|
|
||||||
|
// _middlewares.push_back(Ref<RBACUserSessionSetupMiddleware>(new RBACUserSessionSetupMiddleware()));
|
||||||
|
// _middlewares.push_back(Ref<RBACDefaultUserSessionSetupMiddleware>(new RBACDefaultUserSessionSetupMiddleware()));
|
||||||
|
|
||||||
|
// --- CSRF Token Middlweare ---
|
||||||
|
// (#include "web/http/csrf_token.h")
|
||||||
|
|
||||||
|
// Ref<CSRFTokenMiddleware> csrf_middleware;
|
||||||
|
// csrf_middleware.instance();
|
||||||
|
// csrf_middleware->ignored_urls.push_back("/user/login");
|
||||||
|
// csrf_middleware->ignored_urls.push_back("/user/register");
|
||||||
|
// _middlewares.push_back(csrf_middleware);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::default_fallback_error_handler(Request *request, int error_code) {
|
||||||
|
request->compiled_body = default_generic_error_body;
|
||||||
|
request->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::default_404_error_handler(Request *request, int error_code) {
|
||||||
|
request->compiled_body = default_error_404_body;
|
||||||
|
request->send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::handle_request_main(Request *request) {
|
||||||
|
if (process_middlewares(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_web_permission.is_valid()) {
|
||||||
|
if (_web_permission->activate(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle files first
|
||||||
|
if (try_send_wwwroot_file(request)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// normal routing
|
||||||
|
if (!_routing_enabled) {
|
||||||
|
_handle_request_main(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!try_route_request_to_children(request)) {
|
||||||
|
_handle_request_main(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::handle_error_send_request(Request *request, const int error_code) {
|
||||||
|
std::function<void(Request *, int)> func = error_handler_map[error_code];
|
||||||
|
|
||||||
|
if (!func) {
|
||||||
|
if (!default_error_handler_func) {
|
||||||
|
WebNode::handle_error_send_request(request, error_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
default_error_handler_func(request, error_code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
func(request, error_code);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebRoot::process_middlewares(Request *request) {
|
||||||
|
for (int i = 0; i < _middlewares.size(); ++i) {
|
||||||
|
if (_middlewares[i]->on_before_handle_request_main(request)) {
|
||||||
|
// handled
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebRoot::try_send_wwwroot_file(Request *request) {
|
||||||
|
const String &path = request->get_path_full();
|
||||||
|
|
||||||
|
if (FileCache::get_singleton()->wwwroot_has_file(path)) {
|
||||||
|
send_file(path, request);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::send_file(const String &path, Request *request) {
|
||||||
|
String fp = FileCache::get_singleton()->wwwroot + path;
|
||||||
|
|
||||||
|
request->send_file(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::register_request_update(Request *request) {
|
||||||
|
std::lock_guard<std::mutex> lock(_update_registered_requests_mutex);
|
||||||
|
|
||||||
|
_update_registered_requests.push_back(request);
|
||||||
|
}
|
||||||
|
void WebRoot::unregister_request_update(Request *request) {
|
||||||
|
std::lock_guard<std::mutex> lock(_update_registered_requests_mutex);
|
||||||
|
|
||||||
|
std::size_t s = _update_registered_requests.size();
|
||||||
|
for (std::size_t i = 0; i < s; ++i) {
|
||||||
|
Request *r = _update_registered_requests[i];
|
||||||
|
|
||||||
|
if (r == request) {
|
||||||
|
_update_registered_requests[i] = _update_registered_requests[s - 1];
|
||||||
|
|
||||||
|
_update_registered_requests.pop_back();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebRoot::update() {
|
||||||
|
for (std::size_t i = 0; i < _update_registered_requests.size(); ++i) {
|
||||||
|
Request *r = _update_registered_requests[i];
|
||||||
|
|
||||||
|
r->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRoot::WebRoot() :
|
||||||
|
WebNode() {
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRoot::~WebRoot() {
|
||||||
|
error_handler_map.clear();
|
||||||
|
_middlewares.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
String WebRoot::default_error_404_body = "<html><body>404 :(</body></html>";
|
||||||
|
String WebRoot::default_generic_error_body = "<html><body>Internal server error! :(</body></html>";
|
59
modules/web/http/web_root.h
Normal file
59
modules/web/http/web_root.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef WEB_ROOT_H
|
||||||
|
#define WEB_ROOT_H
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
#include "mutex"
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include "core/string.h"
|
||||||
|
|
||||||
|
#include "web_node.h"
|
||||||
|
|
||||||
|
#include "middleware.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
|
||||||
|
// TODO FileCache -> set up, for this webroot, don't use singleton
|
||||||
|
|
||||||
|
class WebRoot : public WebNode {
|
||||||
|
RCPP_OBJECT(WebRoot, WebNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static String default_error_404_body;
|
||||||
|
static String default_generic_error_body;
|
||||||
|
|
||||||
|
void handle_request_main(Request *request);
|
||||||
|
void handle_error_send_request(Request *request, const int error_code);
|
||||||
|
|
||||||
|
bool process_middlewares(Request *request);
|
||||||
|
bool try_send_wwwroot_file(Request *request);
|
||||||
|
void send_file(const String &path, Request *request);
|
||||||
|
|
||||||
|
static void default_fallback_error_handler(Request *request, int error_code);
|
||||||
|
static void default_404_error_handler(Request *request, int error_code);
|
||||||
|
|
||||||
|
virtual void setup();
|
||||||
|
virtual void setup_error_handlers();
|
||||||
|
virtual void setup_middleware();
|
||||||
|
|
||||||
|
void default_routing_middleware(Object *instance, Request *request);
|
||||||
|
|
||||||
|
void register_request_update(Request *request);
|
||||||
|
void unregister_request_update(Request *request);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
WebRoot();
|
||||||
|
virtual ~WebRoot();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Vector<Ref<Middleware> > _middlewares;
|
||||||
|
|
||||||
|
std::map<int, std::function<void(Request *, int)> > error_handler_map;
|
||||||
|
std::function<void(Request *, int)> default_error_handler_func;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::mutex _update_registered_requests_mutex;
|
||||||
|
std::vector<Request *> _update_registered_requests;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
38
modules/web/http/web_server.cpp
Normal file
38
modules/web/http/web_server.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include "web_server.h"
|
||||||
|
|
||||||
|
#include "request.h"
|
||||||
|
#include "web/http/web_node.h"
|
||||||
|
|
||||||
|
WebNode *WebServer::get_web_root() {
|
||||||
|
return _web_root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebServer::set_root(Node *root) {
|
||||||
|
WebNode *web_root = Object::cast_to<WebNode>(root);
|
||||||
|
|
||||||
|
ERR_FAIL_COND(!web_root);
|
||||||
|
|
||||||
|
_web_root = web_root;
|
||||||
|
|
||||||
|
NodeTree::set_root(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebServer::handle_request(Request *request) {
|
||||||
|
ERR_FAIL_COND(!_web_root);
|
||||||
|
|
||||||
|
_rw_lock.read_lock();
|
||||||
|
_web_root->handle_request_main(request);
|
||||||
|
_rw_lock.read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
float WebServer::get_update_delta_time() {
|
||||||
|
return _update_interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebServer::WebServer() : NodeTree() {
|
||||||
|
_web_root = nullptr;
|
||||||
|
_update_interval = 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebServer::~WebServer() {
|
||||||
|
}
|
29
modules/web/http/web_server.h
Normal file
29
modules/web/http/web_server.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef WEB_SERVER_H
|
||||||
|
#define WEB_SERVER_H
|
||||||
|
|
||||||
|
#include "core/nodes/node_tree.h"
|
||||||
|
|
||||||
|
class Request;
|
||||||
|
class WebNode;
|
||||||
|
|
||||||
|
class WebServer : public NodeTree {
|
||||||
|
RCPP_OBJECT(WebServer, NodeTree);
|
||||||
|
|
||||||
|
public:
|
||||||
|
WebNode *get_web_root();
|
||||||
|
|
||||||
|
void set_root(Node *root);
|
||||||
|
|
||||||
|
void handle_request(Request *request);
|
||||||
|
|
||||||
|
float get_update_delta_time();
|
||||||
|
|
||||||
|
WebServer();
|
||||||
|
virtual ~WebServer();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WebNode *_web_root;
|
||||||
|
float _update_interval;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
36
modules/web/register_types.cpp
Normal file
36
modules/web/register_types.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2022 Péter Magyar
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "register_types.h"
|
||||||
|
|
||||||
|
//#include "core/engine.h"
|
||||||
|
|
||||||
|
#include "html/html_builder_bind.h"
|
||||||
|
|
||||||
|
void register_web_types() {
|
||||||
|
//ClassDB::register_class<TiledWall>();
|
||||||
|
ClassDB::register_class<_HTMLBuilder>();
|
||||||
|
ClassDB::register_class<_HTMLTag>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unregister_web_types() {
|
||||||
|
}
|
28
modules/web/register_types.h
Normal file
28
modules/web/register_types.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef WEB_REGISTER_TYPES_H
|
||||||
|
#define WEB_REGISTER_TYPES_H
|
||||||
|
/*
|
||||||
|
Copyright (c) 2022 Péter Magyar
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void register_web_types();
|
||||||
|
void unregister_web_types();
|
||||||
|
|
||||||
|
#endif
|
13
modules/web/temp/SCsub
Normal file
13
modules/web/temp/SCsub
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
Import("env")
|
||||||
|
|
||||||
|
env.core_sources = []
|
||||||
|
|
||||||
|
env.add_source_files(env.core_sources, "*.cpp")
|
||||||
|
env.add_source_files(env.core_sources, "./html/*.cpp")
|
||||||
|
env.add_source_files(env.core_sources, "./http/*.cpp")
|
||||||
|
|
||||||
|
# Build it all as a library
|
||||||
|
lib = env.add_library("web", env.core_sources)
|
||||||
|
env.Prepend(LIBS=[lib])
|
2528
modules/web/temp/string.cpp
Normal file
2528
modules/web/temp/string.cpp
Normal file
File diff suppressed because it is too large
Load Diff
280
modules/web/temp/string.h
Normal file
280
modules/web/temp/string.h
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#ifndef STRING_H
|
||||||
|
#define STRING_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "core/containers/vector.h"
|
||||||
|
|
||||||
|
#ifndef DEFAULT_DIRECTORY_SEPARATOR
|
||||||
|
#define DEFAULT_DIRECTORY_SEPARATOR '/'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class Variant;
|
||||||
|
|
||||||
|
// TODO move to wchar_t!
|
||||||
|
|
||||||
|
class String {
|
||||||
|
public:
|
||||||
|
void push_back(const char element);
|
||||||
|
void push_back(const wchar_t element);
|
||||||
|
void pop_back();
|
||||||
|
void remove(const int index);
|
||||||
|
void erase(const char element);
|
||||||
|
void erase(const int start_index, const int length);
|
||||||
|
void clear();
|
||||||
|
bool empty() const;
|
||||||
|
char get(const int index);
|
||||||
|
const char get(const int index) const;
|
||||||
|
void set(const int index, const char value);
|
||||||
|
|
||||||
|
int size() const;
|
||||||
|
int capacity() const;
|
||||||
|
void ensure_capacity(const int capacity);
|
||||||
|
void resize(const int s);
|
||||||
|
int find(const char val, const int from = 0) const;
|
||||||
|
int find(const String &val, const int from = 0) const;
|
||||||
|
int find_reversed(const char val, const int from = -1) const;
|
||||||
|
int find_reversed(const String &val, const int from = -1) const;
|
||||||
|
void get_substr(char *into_buf, const int start_index, const int len);
|
||||||
|
void get_substr_nt(char *into_buf, const int start_index, const int len);
|
||||||
|
String substr(const int start_index, const int len) const;
|
||||||
|
String substr_index(const int start_index, const int end_index) const; //end_index is not included
|
||||||
|
bool contains(const char val) const;
|
||||||
|
bool contains(const String &val) const;
|
||||||
|
|
||||||
|
bool is_word_at(const int index, const char *str) const;
|
||||||
|
bool is_word_at(const int index, const String &val) const;
|
||||||
|
|
||||||
|
void replace_from(const int start_index, const int length, const String &with);
|
||||||
|
void replace(const String &find_str, const String &with);
|
||||||
|
void replace(const String &find_str, const String &with, const int count);
|
||||||
|
|
||||||
|
int compare(const String &other) const;
|
||||||
|
|
||||||
|
int first_difference_index(const String &other) const;
|
||||||
|
|
||||||
|
void to_lower();
|
||||||
|
String as_lower() const;
|
||||||
|
|
||||||
|
void trim();
|
||||||
|
void trim_beginning();
|
||||||
|
void trim_end();
|
||||||
|
|
||||||
|
bool ends_with(const char c) const;
|
||||||
|
bool ends_with(const String &str) const;
|
||||||
|
|
||||||
|
bool starts_with(const char c) const;
|
||||||
|
bool starts_with(const String &str) const;
|
||||||
|
|
||||||
|
int get_slice_count(const char splitter) const;
|
||||||
|
int get_slice_count(const String &splitter) const;
|
||||||
|
String get_slice(const char splitter, int index);
|
||||||
|
String get_slice(const String &splitter, int index);
|
||||||
|
|
||||||
|
Vector<String> split(const char splitter) const;
|
||||||
|
Vector<String> split(const String &splitter) const;
|
||||||
|
|
||||||
|
uint8_t read_uint8_bytes_at(int &index, bool advance_index = true);
|
||||||
|
uint16_t read_uint16_bytes_at(int &index, bool advance_index = true);
|
||||||
|
uint32_t read_uint32_bytes_at(int &index, bool advance_index = true);
|
||||||
|
uint64_t read_uint64_bytes_at(int &index, bool advance_index = true);
|
||||||
|
|
||||||
|
int8_t read_int8_bytes_at(int &index, bool advance_index = true);
|
||||||
|
int16_t read_int16_bytes_at(int &index, bool advance_index = true);
|
||||||
|
int32_t read_int32_bytes_at(int &index, bool advance_index = true);
|
||||||
|
int64_t read_int64_bytes_at(int &index, bool advance_index = true);
|
||||||
|
|
||||||
|
void append_uint8_bytes(const uint8_t val);
|
||||||
|
void append_uint16_bytes(const uint16_t val);
|
||||||
|
void append_uint32_bytes(const uint32_t val);
|
||||||
|
void append_uint64_bytes(const uint64_t val);
|
||||||
|
|
||||||
|
void append_int8_bytes(const int8_t val);
|
||||||
|
void append_int16_bytes(const int16_t val);
|
||||||
|
void append_int32_bytes(const int32_t val);
|
||||||
|
void append_int64_bytes(const int64_t val);
|
||||||
|
|
||||||
|
float read_float_bytes_at(int &index, bool advance_index = true);
|
||||||
|
void append_float_bytes(const float val);
|
||||||
|
double read_double_bytes_at(int &index, bool advance_index = true);
|
||||||
|
void append_double_bytes(const double val);
|
||||||
|
|
||||||
|
void append_str(const char *str);
|
||||||
|
void append_str(const wchar_t *str);
|
||||||
|
void append_str(const String &other);
|
||||||
|
void append_str(const std::string &str);
|
||||||
|
void append_str(const String &other, const int from);
|
||||||
|
void append_str(const std::string &str, const int from);
|
||||||
|
|
||||||
|
void append_repeat(const char *str, const int times);
|
||||||
|
void append_repeat(const String &other, const int times);
|
||||||
|
|
||||||
|
void append_path(const char *path);
|
||||||
|
void append_path(const String &path);
|
||||||
|
void path_clean_end_slash();
|
||||||
|
void path_ensure_end_slash();
|
||||||
|
String path_get_basename() const;
|
||||||
|
String path_get_last_segment() const;
|
||||||
|
String path_get_prev_dir() const;
|
||||||
|
String file_get_extension() const;
|
||||||
|
|
||||||
|
void to_html_special_chars();
|
||||||
|
void from_html_special_chars();
|
||||||
|
void newline_to_br();
|
||||||
|
|
||||||
|
bool to_bool() const;
|
||||||
|
float to_float() const;
|
||||||
|
double to_double() const;
|
||||||
|
int to_int() const;
|
||||||
|
|
||||||
|
bool is_bool() const;
|
||||||
|
bool is_numeric() const;
|
||||||
|
bool is_int() const;
|
||||||
|
bool is_uint() const;
|
||||||
|
bool is_zero() const;
|
||||||
|
|
||||||
|
uint32_t to_uint() const;
|
||||||
|
std::string to_string() const;
|
||||||
|
void print() const;
|
||||||
|
|
||||||
|
// Generic set of append helpers
|
||||||
|
void append(const char *str);
|
||||||
|
void append(const wchar_t *str);
|
||||||
|
void append(const String &other);
|
||||||
|
void append(const std::string &str);
|
||||||
|
void append(const char chr);
|
||||||
|
void append(const wchar_t chr);
|
||||||
|
void append(const int num);
|
||||||
|
void append(const unsigned int num);
|
||||||
|
void append(const float num);
|
||||||
|
void append(const double num);
|
||||||
|
void append(const Variant &variant);
|
||||||
|
|
||||||
|
static String bool_num(bool val);
|
||||||
|
static String bool_str(bool val);
|
||||||
|
|
||||||
|
// Taken from the Godot Engine (MIT License)
|
||||||
|
// Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.
|
||||||
|
// Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).
|
||||||
|
static String num(double p_num, int p_decimals = -1);
|
||||||
|
static String num_scientific(double p_num);
|
||||||
|
static String num_real(double p_num, bool p_trailing = true);
|
||||||
|
static String num_int64(int64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||||
|
static String num_uint64(uint64_t p_num, int base = 10, bool capitalize_hex = false);
|
||||||
|
static String chr(char32_t p_char);
|
||||||
|
|
||||||
|
// Taken from the Godot Engine (MIT License)
|
||||||
|
// Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.
|
||||||
|
// Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).
|
||||||
|
String ascii(bool p_allow_extended = false) const;
|
||||||
|
String utf8() const;
|
||||||
|
bool parse_utf8(const char *p_utf8, int p_len = -1); // return true on error
|
||||||
|
static String utf8(const char *p_utf8, int p_len = -1);
|
||||||
|
|
||||||
|
// Taken from the Godot Engine (MIT License)
|
||||||
|
// Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.
|
||||||
|
// Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).
|
||||||
|
static uint32_t hash(const wchar_t *p_cstr, int p_len); /* hash the string */
|
||||||
|
static uint32_t hash(const wchar_t *p_cstr); /* hash the string */
|
||||||
|
static uint32_t hash(const char *p_cstr, int p_len); /* hash the string */
|
||||||
|
static uint32_t hash(const char *p_cstr); /* hash the string */
|
||||||
|
uint32_t hash() const; /* hash the string */
|
||||||
|
uint64_t hash64() const; /* hash the string */
|
||||||
|
|
||||||
|
char *c_str();
|
||||||
|
const char *c_str() const;
|
||||||
|
|
||||||
|
char *dataw();
|
||||||
|
const char *data() const;
|
||||||
|
|
||||||
|
const char operator[](const int index) const;
|
||||||
|
char &operator[](const int index);
|
||||||
|
|
||||||
|
String &operator+=(const String &b);
|
||||||
|
String &operator+=(const char chr);
|
||||||
|
String &operator+=(const char *p_c_str);
|
||||||
|
String &operator+=(const std::string &b);
|
||||||
|
|
||||||
|
friend String operator+(String lhs, const String &rhs);
|
||||||
|
friend String operator+(String lhs, const char *rhs);
|
||||||
|
friend String operator+(String lhs, const char rhs);
|
||||||
|
friend String operator+(String lhs, const std::string &rhs);
|
||||||
|
|
||||||
|
friend bool operator==(const String &a, const String &b);
|
||||||
|
friend bool operator!=(const String &a, const String &b);
|
||||||
|
|
||||||
|
friend bool operator==(const String &a, const char *b);
|
||||||
|
friend bool operator!=(const String &a, const char *b);
|
||||||
|
|
||||||
|
friend bool operator==(const char *b, const String &a);
|
||||||
|
friend bool operator!=(const char *b, const String &a);
|
||||||
|
|
||||||
|
friend bool operator==(const String &a, const wchar_t *b);
|
||||||
|
friend bool operator!=(const String &a, const wchar_t *b);
|
||||||
|
|
||||||
|
friend bool operator==(const wchar_t *b, const String &a);
|
||||||
|
friend bool operator!=(const wchar_t *b, const String &a);
|
||||||
|
|
||||||
|
friend bool operator==(const String &a, std::string &b);
|
||||||
|
friend bool operator!=(const String &a, std::string &b);
|
||||||
|
|
||||||
|
friend bool operator==(std::string &b, const String &a);
|
||||||
|
friend bool operator!=(std::string &b, const String &a);
|
||||||
|
|
||||||
|
friend bool operator<(const String &a, const String &b);
|
||||||
|
friend bool operator>(const String &a, const String &b);
|
||||||
|
friend bool operator<=(const String &a, const String &b);
|
||||||
|
friend bool operator>=(const String &a, const String &b);
|
||||||
|
|
||||||
|
operator std::string() { return to_string(); }
|
||||||
|
operator std::string() const { return to_string(); }
|
||||||
|
|
||||||
|
String &operator=(const String &other);
|
||||||
|
String &operator=(const std::string &other);
|
||||||
|
String &operator=(const char *other);
|
||||||
|
String &operator=(const wchar_t *other);
|
||||||
|
|
||||||
|
String();
|
||||||
|
String(const String &other);
|
||||||
|
String(const String &other, const int grow_by);
|
||||||
|
String(const char *p_c_str);
|
||||||
|
String(const char *p_c_str, const int grow_by);
|
||||||
|
String(const wchar_t *p_c_str);
|
||||||
|
String(const int prealloc);
|
||||||
|
String(const int prealloc, const int grow_by);
|
||||||
|
String(const std::string &str);
|
||||||
|
~String();
|
||||||
|
|
||||||
|
private:
|
||||||
|
char *_data;
|
||||||
|
int _actual_size;
|
||||||
|
int _size;
|
||||||
|
int _grow_by;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Taken from the Godot Engine (MIT License)
|
||||||
|
// Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.
|
||||||
|
// Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).
|
||||||
|
template <typename L, typename R>
|
||||||
|
bool is_str_less(const L *l_ptr, const R *r_ptr) {
|
||||||
|
while (true) {
|
||||||
|
if (*l_ptr == 0 && *r_ptr == 0) {
|
||||||
|
return false;
|
||||||
|
} else if (*l_ptr == 0) {
|
||||||
|
return true;
|
||||||
|
} else if (*r_ptr == 0) {
|
||||||
|
return false;
|
||||||
|
} else if (*l_ptr < *r_ptr) {
|
||||||
|
return true;
|
||||||
|
} else if (*l_ptr > *r_ptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
l_ptr++;
|
||||||
|
r_ptr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue
Block a user