mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2025-04-06 03:52:46 +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