Compare commits

...

30 Commits
master ... ajax

Author SHA1 Message Date
Tomasz Sowa f8e1037476 uikit updated to 3.9.4 2 weeks ago
Tomasz Sowa 35cb54324f added a new item content type: markdown 2 weeks ago
Tomasz Sowa 119497bb01 added winix_frame_is ezc winix function 1 month ago
Tomasz Sowa dfd6475a81 uikit updated to 3.7.6 1 month ago
Tomasz Sowa d6d5236a03 added fil_json_escape filter 2 months ago
Tomasz Sowa a327a26bd5 added winix_uikit_version parameter to index_head_adder.html 2 months ago
Tomasz Sowa d5ebb7ca12 changed the way how the request's answer is created, 2 months ago
Tomasz Sowa 3e46c5674c htmx updated to 1.6.0 2 months ago
Tomasz Sowa c4b5565995 don't check for request->is_htmx_request for sending frames, check only "frame" url paremeter 2 months ago
Tomasz Sowa 3f5eabeaa1 uikit updated to 3.7.5 2 months ago
Tomasz Sowa 9b8e48e7b2 added static libraries: clocklet (timepicker), flatpickr and litepicker (data pickers) 2 months ago
Tomasz Sowa c3122fb82f added [lang] ezc function for returning a localized string 2 months ago
Tomasz Sowa 1741597d52 uikit updated to 3.7.4 2 months ago
Tomasz Sowa 9c5c74ba84 added: setting a correct mime type for static files - using magic library 3 months ago
Tomasz Sowa 26ed7b80be changed values for send_file_mode config parameter: 3 months ago
Tomasz Sowa 55ac9a61ed added possibility to send static files to nginx via X-Accel-Redirect header 3 months ago
Tomasz Sowa 7673264fe1 make depend 4 months ago
Tomasz Sowa 42dc43b036 added WINIX_MAKE_DATABASE_MIGRATION plugin message for making migrations from plugins 4 months ago
Tomasz Sowa ef22d951a0 fixed: base class of System should be derived as public 4 months ago
Tomasz Sowa e95dc834a3 uikit updated to 3.7.2 4 months ago
Tomasz Sowa b424988d1b updated to the new pikotools api (api2021): HTMLFilter renamed to HTMLParser 4 months ago
Tomasz Sowa 22134b6cc0 commented some method calls to html_filter 4 months ago
Tomasz Sowa f861c0761e make depend 5 months ago
Tomasz Sowa 1899d5ee17 BBCODEParser has been moved to pikotools library 5 months ago
Tomasz Sowa 17bd48ece3 HTMLFilter has been moved to pikotools library 5 months ago
Tomasz Sowa c5c02d7f44 HtmlTextStream has now pt::Stream as a based class and uses pt::WTextStream as a buffer 5 months ago
Tomasz Sowa ba6159964b htmx updated to 1.5.0 5 months ago
Tomasz Sowa b834971f5e uikit updated to 3.7.1 5 months ago
Tomasz Sowa 9dc15d536c Makefile: removed macros: EZC_GENERATOR_HAS_PT_STREAM and EZC_GENERATOR_HAS_WINIX_STREAM 5 months ago
Tomasz Sowa 2e45cb6ac9 updated to the new ezc api (Generator has three additional parameters now) 5 months ago
  1. 4
      winixd/Makefile
  2. 2
      winixd/Makefile.dep
  3. 423
      winixd/core/Makefile.dep
  4. 2
      winixd/core/Makefile.o.dep
  5. 702
      winixd/core/app.cpp
  6. 38
      winixd/core/app.h
  7. 636
      winixd/core/bbcodeparser.cpp
  8. 127
      winixd/core/bbcodeparser.h
  9. 2
      winixd/core/compress.cpp
  10. 13
      winixd/core/config.cpp
  11. 62
      winixd/core/config.h
  12. 76
      winixd/core/header.h
  13. 1708
      winixd/core/htmlfilter.cpp
  14. 373
      winixd/core/htmlfilter.h
  15. 63
      winixd/core/misc.cpp
  16. 39
      winixd/core/misc.h
  17. 4
      winixd/core/pluginmsg.h
  18. 121
      winixd/core/request.cpp
  19. 261
      winixd/core/request.h
  20. 58
      winixd/core/system.cpp
  21. 6
      winixd/core/system.h
  22. 6
      winixd/db/Makefile.dep
  23. 274
      winixd/functions/Makefile.dep
  24. 5
      winixd/functions/cat.cpp
  25. 6
      winixd/functions/cp.cpp
  26. 33
      winixd/functions/download.cpp
  27. 44
      winixd/functions/emacs.cpp
  28. 4
      winixd/functions/emacs.h
  29. 7
      winixd/functions/functionbase.cpp
  30. 3
      winixd/functions/functionbase.h
  31. 146
      winixd/functions/functions.cpp
  32. 13
      winixd/functions/functions.h
  33. 6
      winixd/functions/mkdir.cpp
  34. 22
      winixd/functions/rm.cpp
  35. 2
      winixd/functions/rm.h
  36. 5
      winixd/functions/run.cpp
  37. 4
      winixd/functions/tinymce.cpp
  38. 100
      winixd/functions/upload.cpp
  39. 11
      winixd/functions/upload.h
  40. 58
      winixd/html/fun_emacs_post.html
  41. 8
      winixd/html/fun_run.html
  42. 1
      winixd/html/fun_tinymce.html
  43. 5
      winixd/html/fun_upload.html
  44. 232
      winixd/html/index_head_adder.html
  45. 7
      winixd/locale/en
  46. 7
      winixd/locale/pl
  47. 9
      winixd/main/Makefile.dep
  48. 90
      winixd/models/Makefile.dep
  49. 2
      winixd/models/item.cpp
  50. 126
      winixd/models/itemcontent.cpp
  51. 29
      winixd/models/itemcontent.h
  52. 26
      winixd/notify/Makefile.dep
  53. 2
      winixd/notify/notifythread.h
  54. 74
      winixd/plugins/export/Makefile.dep
  55. 21
      winixd/plugins/gallery/Makefile.dep
  56. 105
      winixd/plugins/group/Makefile.dep
  57. 13
      winixd/plugins/mailregister/Makefile.dep
  58. 86
      winixd/plugins/menu/Makefile.dep
  59. 8
      winixd/plugins/seo/Makefile.dep
  60. 25
      winixd/plugins/stats/Makefile.dep
  61. 67
      winixd/plugins/thread/Makefile.dep
  62. 4
      winixd/plugins/thread/templates.cpp
  63. 75
      winixd/plugins/ticket/Makefile.dep
  64. 331
      winixd/templates/Makefile.dep
  65. 119
      winixd/templates/filters.cpp
  66. 579
      winixd/templates/htmltextstream.cpp
  67. 161
      winixd/templates/htmltextstream.h
  68. 4
      winixd/templates/insert.cpp
  69. 2
      winixd/templates/misc.h
  70. 11
      winixd/templates/templates.cpp
  71. 9
      winixd/templates/templates.h
  72. 15
      winixd/templates/winix.cpp

4
winixd/Makefile

@ -27,7 +27,7 @@ endif
# CXX = g++-4.8
ifndef CXXFLAGS
CXXFLAGS = -Wall -O0 -g -fPIC -pthread -std=c++20 -I/usr/local/include -I/usr/include/postgresql -I$(GLOBAL_WORKING_DIR)/pikotools/src -I$(GLOBAL_WORKING_DIR)/ezc/src -I$(GLOBAL_WORKING_DIR)/morm/src -DPT_HAS_MORM_LIBRARY -DEZC_GENERATOR_HAS_PT_STREAM -DEZC_GENERATOR_HAS_WINIX_STREAM -DEZC_HAS_MORM_LIBRARY -DMORM_HAS_EZC_LIBRARY
CXXFLAGS = -Wall -O0 -g -fPIC -pthread -std=c++20 -I/usr/local/include -I/usr/include/postgresql -I$(GLOBAL_WORKING_DIR)/pikotools/src -I$(GLOBAL_WORKING_DIR)/ezc/src -I$(GLOBAL_WORKING_DIR)/morm/src -DPT_HAS_MORM_LIBRARY -DEZC_HAS_MORM_LIBRARY -DMORM_HAS_EZC_LIBRARY
endif
ifndef AR
@ -70,7 +70,7 @@ winix.so: $(winix.src.files)
@cd $(GLOBAL_WORKING_DIR)/tito/src ; $(MAKE) -e
@cd $(GLOBAL_WORKING_DIR)/pikotools ; $(MAKE) -e
@cd $(GLOBAL_WORKING_DIR)/morm/src ; $(MAKE) -e
$(CXX) -shared -rdynamic -Wl,-whole-archive -o winix.so $(CXXFLAGS) $(winix_include_paths) core/*.o db/*.o models/*.o functions/*.o templates/*.o notify/*.o $(GLOBAL_WORKING_DIR)/ezc/src/ezc.a $(GLOBAL_WORKING_DIR)/tito/src/tito.a $(GLOBAL_WORKING_DIR)/pikotools/src/pikotools.a $(GLOBAL_WORKING_DIR)/morm/src/morm.a $(LDFLAGS) -lfcgi -lpq -lz -lpthread -lcurl -Wl,-no-whole-archive
$(CXX) -shared -rdynamic -Wl,-whole-archive -o winix.so $(CXXFLAGS) $(winix_include_paths) core/*.o db/*.o models/*.o functions/*.o templates/*.o notify/*.o $(GLOBAL_WORKING_DIR)/ezc/src/ezc.a $(GLOBAL_WORKING_DIR)/tito/src/tito.a $(GLOBAL_WORKING_DIR)/pikotools/src/pikotools.a $(GLOBAL_WORKING_DIR)/morm/src/morm.a $(LDFLAGS) -lfcgi -lpq -lz -lpthread -lcurl -lmagic -Wl,-no-whole-archive
winix: winix.so $(winix.src.files)

2
winixd/Makefile.dep

File diff suppressed because one or more lines are too long

423
winixd/core/Makefile.dep

File diff suppressed because it is too large

2
winixd/core/Makefile.o.dep

@ -1 +1 @@
o = acceptbaseparser.o app.o basethread.o bbcodeparser.o compress.o config.o crypt.o dircontainer.o dirs.o filelog.o groups.o htmlfilter.o httpsimpleparser.o image.o ipbancontainer.o job.o lastcontainer.o loadavg.o lock.o log.o misc.o mount.o mountparser.o mounts.o plugin.o plugindata.o postmultiparser.o rebus.o request.o run.o session.o sessioncontainer.o sessionidmanager.o sessionmanager.o sessionparser.o slog.o synchro.o system.o threadmanager.o timezone.o timezones.o users.o winixbase.o winixmodeldeprecated.o winixrequest.o winixsystem.o
o = acceptbaseparser.o app.o basethread.o compress.o config.o crypt.o dircontainer.o dirs.o filelog.o groups.o httpsimpleparser.o image.o ipbancontainer.o job.o lastcontainer.o loadavg.o lock.o log.o misc.o mount.o mountparser.o mounts.o plugin.o plugindata.o postmultiparser.o rebus.o request.o run.o session.o sessioncontainer.o sessionidmanager.o sessionmanager.o sessionparser.o slog.o synchro.o system.o threadmanager.o timezone.o timezones.o users.o winixbase.o winixmodeldeprecated.o winixrequest.o winixsystem.o

702
winixd/core/app.cpp

@ -316,6 +316,14 @@ bool App::TryToMakeDatabaseMigration()
return false;
}
}
PluginRes res = plugin.Call(nullptr, WINIX_MAKE_DATABASE_MIGRATION);
if( config.db_stop_if_migration_fails && res.res_false > 0 )
{
log << log1 << "App: database migration of some plugins failed, stopping winix" << logend;
return false;
}
}
return true;
@ -397,6 +405,7 @@ void App::Close()
session_manager.DeleteSessions();
cur.request->Clear();
session_manager.UninitTmpSession();
functions.Finish();
// now all sessions are cleared
}
@ -696,14 +705,15 @@ void App::ClearAfterRequest()
try
{
// simple operations which should not throw an exception
json_out_stream.Clear();
json_out_stream.clear();
templates.ClearAfterRequest();
cur.request->Clear();
cur.session->ClearAfterRequest();
cur.session = session_manager.GetTmpSession();
output_8bit.clear();
output_8bit2.clear();
compressed_output.clear();
html_filtered.clear();
//html_filtered.clear();
aheader_name.clear();
aheader_value.clear();
cur.mount = system.mounts.GetEmptyMount();
@ -766,73 +776,11 @@ void App::SaveSessionsIfNeeded()
}
const std::wstring * App::CreateFrameAnswer()
{
Request & req = *cur.request;
auto i = req.out_streams.streams_map.begin();
const std::wstring * frame = cur.request->ParamValuep(L"frame");
for( ; i != req.out_streams.streams_map.end() ; ++i)
{
if( (frame && i->first == *frame) || (!frame && i->first == L"content") )
{
return &i->second->Str();
break;
}
}
return nullptr;
}
void App::CreateJSONAnswer()
{
Request & req = *cur.request;
json_out_stream.Clear();
if( !req.return_info_only )
{
json_out_stream << L"{\n\"frames\": {\n";
auto i = req.out_streams.streams_map.begin();
bool is_first = true;
for( ; i != req.out_streams.streams_map.end() ; ++i)
{
if( !is_first )
{
json_out_stream << L",\n";
}
json_out_stream << L"\"";
JSONescape(json_out_stream, i->first);
json_out_stream << L"\": \"";
JSONescape(json_out_stream, i->second->Str());
json_out_stream << L"\"";
is_first = false;
}
json_out_stream << L"}\n,\n\"info\": ";
}
req.info.serialize_to_json_stream(json_out_stream, false);
log << log3 << "App: sending JSON answer";
if( !req.return_info_only )
json_out_stream << L"}\n";
else
log << " (Request::info only)";
log << logend;
}
// !! IMPROVE ME change to a better name
void App::MakePage()
void App::MakeEzcGenerator()
{
if( cur.request->page_generated || !cur.request->redirect_to.empty() || !cur.request->x_sendfile.empty() )
return;
// if( cur.request->page_generated || !cur.request->redirect_to.empty() || !cur.request->x_sendfile.empty() )
// return;
clock_gettime(CLOCK_REALTIME, &cur.request->timespec_ezc_engine_start);
@ -875,10 +823,12 @@ void App::CheckPostRedirect()
void App::AddDefaultModels()
{
if( !cur.request->send_bin_stream && !cur.request->return_json )
// there is no need to add default models if we return a binary stream
if( cur.request->answer_source != Request::AnswerSource::answer_bin_stream )
{
if( cur.request->function && cur.request->function->register_default_models )
{
// may it would be better do not return cur.request by default?
cur.request->models.Add(L"request", cur.request);
if( cur.session && cur.session->puser )
@ -907,8 +857,8 @@ void App::Make()
return;
}
if( cur.request->ParamValue(L"reqtype") == L"json" )
cur.request->return_json = true;
if( !cur.request->PrepareAnswerType() )
return;
if( cur.session->ip_ban && cur.session->ip_ban->IsIPBanned() )
{
@ -960,9 +910,6 @@ void App::Make()
log << log1 << "App: there is no a root dir (dir_tab is empty -- after calling a function)" << logend;
return;
}
plugin.Call(WINIX_CONTENT_MAKE);
MakePage();
}
@ -1303,9 +1250,9 @@ void App::ReadPostVars()
post_multi_parser.Parse(fcgi_request.in, cur.request->post_tab, cur.request->post_file_tab);
}
else
if( pt::is_substr_nc(L"application/json", cur.request->env_content_type.c_str()) )
if( pt::is_substr_nc(Winix::Header::application_json, cur.request->env_content_type.c_str()) )
{
log << log3 << "App: post content type: application/json" << logend;
log << log3 << "App: post content type: " << Winix::Header::application_json << logend;
ReadPostJson();
}
else
@ -1472,34 +1419,56 @@ void App::PrepareHeadersStatic()
AddHeader(L"Status", L"200 OK");
if( AddHeader(config.http_header_send_file, path) )
/*
* FIX ME now we can send full path (apache, lighttpd) and relative path (nginx)
* but this feature for mounting static content probably will be removed
*/
if( AddHeader(config.send_file_header, path) )
log << log2 << "App: sending a file from a static mountpoint: " << path << logend;
}
void App::PrepareHeaderContentType()
{
if( !cur.request->out_headers.has_key(L"Content-Type") )
if( !cur.request->out_headers.has_key(Winix::Header::content_type) )
{
if( cur.request->return_json )
if( cur.request->answer_source == Request::AnswerSource::answer_bin_stream )
{
cur.request->out_headers.add(L"Content-Type", L"application/json; charset=UTF-8");
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::application_octet_stream);
}
else
{
switch( config.content_type_header )
if( cur.request->answer_container == Request::AnswerContainer::answer_json )
{
case 1:
cur.request->out_headers.add(L"Content-Type", L"application/xhtml+xml; charset=UTF-8");
break;
case 2:
cur.request->out_headers.add(L"Content-Type", L"application/xml; charset=UTF-8");
break;
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::application_json_utf8);
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
{
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::application_xml_utf8);
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_csv )
{
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::text_csv_utf8);
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_text )
{
switch( config.content_type_header )
{
case 1:
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::application_xhtml_xml_utf8);
break;
case 0:
default:
cur.request->out_headers.add(L"Content-Type", L"text/html; charset=UTF-8");
case 2:
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::application_xml_utf8);
break;
case 0:
default:
cur.request->out_headers.add(Winix::Header::content_type, Winix::Header::text_html_utf8);
}
}
}
}
@ -1549,7 +1518,7 @@ void App::PrepareHeadersSendFile()
{
AddHeader(L"Status", L"200 OK");
if( AddHeader(config.http_header_send_file, cur.request->x_sendfile) )
if( AddHeader(config.send_file_header, cur.request->x_sendfile) )
log << log2 << "App: sending file: " << cur.request->x_sendfile << logend;
}
@ -1669,7 +1638,8 @@ void App::PrepareHeaders(bool compressing, int compress_encoding, Header header,
if( cur.request->send_as_attachment )
AddHeader(L"Content-Disposition", L"attachment");
if( !cur.request->redirect_to.empty() && !cur.request->return_json )
//if( !cur.request->redirect_to.empty() && !cur.request->return_json )
if( !cur.request->redirect_to.empty() )
{
PrepareHeadersRedirect();
}
@ -1693,70 +1663,6 @@ void App::PrepareHeaders(bool compressing, int compress_encoding, Header header,
}
void App::PrepareStandardJSONFields()
{
pt::Space & info = cur.request->info;
if( !info.has_key(L"status") )
{
info.add(L"status", cur.request->status);
}
if( !cur.request->redirect_to.empty() && !info.has_key(L"redirect_to") )
{
info.add(L"redirect_to", cur.request->redirect_to);
}
}
void App::FilterContent()
{
Request & req = *cur.request;
bool filter_main_stream = false;
bool filter_json = false;
if( config.html_filter && !req.send_bin_stream )
{
if( req.return_json )
{
if( !req.return_info_only && req.out_streams_use_html_filter )
{
filter_json = true;
}
}
else
{
if( req.out_main_stream_use_html_filter )
filter_main_stream = true;
}
}
if( filter_main_stream )
{
// !! IMPROVE ME may some kind of html_filtered.reserve() here? (optimization)
TemplatesFunctions::html_filter.Filter(req.out_main_stream.Str(), html_filtered);
req.out_main_stream.Str(std::move(html_filtered)); // !! IMPROVE ME we do not have Str(&&) method
log << log3 << "App: html in the main stream has been filtered" << logend;
}
if( filter_json )
{
for(auto i = req.out_streams.streams_map.begin() ; i != req.out_streams.streams_map.end() ; ++i)
{
HtmlTextStream & stream = *i->second;
// !! IMPROVE ME may some kind of html_filtered.reserve() here? (optimization)
TemplatesFunctions::html_filter.Filter(stream.Str(), html_filtered);
stream.Str(std::move(html_filtered));
}
log << log3 << "App: html in json out streams have been filtered" << logend;
}
}
int App::SelectDeflateVersion()
@ -1824,11 +1730,11 @@ bool App::CanSendContent()
return false;
}
if( cur.request->return_json )
{
// if there is a redirect flag then it will be put to info struct
return true;
}
// if( cur.request->return_json )
// {
// // if there is a redirect flag then it will be put to info struct
// return true;
// }
if( !cur.request->redirect_to.empty() )
{
@ -1875,121 +1781,447 @@ return header;
}
bool App::IsRequestedFrame()
{
if( !config.request_frame_parameter.empty() )
{
return cur.request->ParamValuep(config.request_frame_parameter);
}
void App::SendTextAnswer()
return false;
}
// IMPROVEME
// we can send directly from BinaryPage without copying to a temporary buffer
// (but there is no an interface in BinaryPage yet)
void App::SendData(const BinaryPage & page, FCGX_Stream * out)
{
const std::wstring * source = nullptr;
bool compressing = false;
int compress_encoding = 0;
size_t output_size = 0;
const size_t buf_size = 4096;
if( send_data_buf.size() != buf_size )
send_data_buf.resize(buf_size);
Header header = GetHTTPStatusCode();
BinaryPage::const_iterator i = page.begin();
BinaryPage::const_iterator end = page.end();
if( CanSendContent() )
while( i != end )
{
FilterContent();
size_t s = 0;
if( cur.request->return_json )
{
CreateJSONAnswer();
source = &json_out_stream.Str(); // json_out_stream was prepared by CreateJSONAnswer()
}
else
if( cur.request->is_htmx_request )
for( ; i != end && s < buf_size ; ++i, ++s)
send_data_buf[s] = *i;
if( s > 0 )
FCGX_PutStr(send_data_buf.c_str(), s, out);
}
}
void App::SendAnswer()
{
output_8bit.clear();
output_8bit2.clear();
compressed_output.clear();
// may use CanSendContent() method?
// what about method HEAD?
if( !cur.request->redirect_to.empty() || !cur.request->x_sendfile.empty() )
{
Send8bitOutput(output_8bit);
return;
}
if( cur.request->answer_source == Request::AnswerSource::answer_bin_stream )
{
Send8bitOutput(cur.request->out_bin_stream);
}
else
{
// is this plugin call correct here?
plugin.Call(WINIX_CONTENT_MAKE);
if( cur.request->answer_source == Request::AnswerSource::answer_models && cur.request->use_ezc_engine )
{
source = CreateFrameAnswer();
MakeEzcGenerator(); // give me a better name
if( !source )
if( !cur.request->frame.empty() || cur.request->send_all_frames )
{
empty_response.clear();
source = &empty_response;
cur.request->answer_source = Request::AnswerSource::answer_frame_streams;
}
else
{
cur.request->answer_source = Request::AnswerSource::answer_main_stream;
}
}
if( cur.request->answer_source == Request::AnswerSource::answer_main_stream )
{
const wchar_t * field_name = nullptr;
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
field_name = config.xml_root.c_str();
SerializeStream(cur.request->out_main_stream.get_buffer(), field_name);
}
else
if( cur.request->answer_source == Request::AnswerSource::answer_frame_streams )
{
SerializeFrames();
}
else
if( cur.request->answer_source == Request::AnswerSource::answer_models )
{
source = &cur.request->out_main_stream.Str();
SerializeModels();
}
SelectCompression(source->length(), compressing, compress_encoding);
Send8bitOutput(output_8bit);
}
}
pt::wide_to_utf8(*source, output_8bit);
// !! IMPROVE ME add to log the binary stream as well
if( config.log_server_answer )
log << log1 << "App: the server's answer is:\n" << output_8bit << "\nApp: end of the server's answer" << logend;
void App::SerializeStream(const pt::WTextStream & input_stream, const wchar_t * field_name)
{
switch( cur.request->answer_container )
{
case Request::AnswerContainer::answer_json:
SerializeStreamJson(input_stream, field_name);
break;
if( compressing )
case Request::AnswerContainer::answer_xml:
SerializeStreamXml(input_stream, field_name);
break;
case Request::AnswerContainer::answer_csv:
SerializeStreamCsv(input_stream, field_name);
break;
case Request::AnswerContainer::answer_text:
default:
FilterHtmlIfNeeded(input_stream, output_8bit);
break;
}
}
void App::SerializeStreamJson(const pt::WTextStream & input_stream, const wchar_t * field_name)
{
if( field_name )
{
output_8bit << '"';
pt::esc_to_json(field_name, output_8bit);
output_8bit << "\":";
}
output_8bit << '"';
FilterHtmlIfNeeded(input_stream, output_8bit2);
pt::esc_to_json(output_8bit2, output_8bit);
output_8bit << '"';
}
void App::SerializeStreamXml(const pt::WTextStream & input_stream, const wchar_t * field_name)
{
if( field_name )
{
output_8bit << '<';
pt::esc_to_xml(field_name, output_8bit);
output_8bit << '>';
}
FilterHtmlIfNeeded(input_stream, output_8bit2);
pt::esc_to_xml(output_8bit2, output_8bit);
if( field_name )
{
output_8bit << "</";
pt::esc_to_xml(field_name, output_8bit);
output_8bit << '>';
}
}
void App::SerializeStreamCsv(const pt::WTextStream & input_stream, const wchar_t * field_name)
{
if( field_name )
{
output_8bit << '"';
pt::esc_to_csv(field_name, output_8bit);
output_8bit << "\";";
}
FilterHtmlIfNeeded(input_stream, output_8bit2);
output_8bit << '"';
pt::esc_to_csv(output_8bit2, output_8bit);
output_8bit << "\";\n";
}
void App::SerializeFrames()
{
if( cur.request->answer_container == Request::AnswerContainer::answer_json )
{
output_8bit << '{';
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
{
output_8bit << '<';
pt::esc_to_xml(config.xml_root, output_8bit);
output_8bit << '>';
}
if( cur.request->frame.empty() || cur.request->send_all_frames )
SerializeAllFrames();
else
SerializeOneFrame();
if( cur.request->answer_container == Request::AnswerContainer::answer_json )
{
output_8bit << '}';
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
{
output_8bit << "</";
pt::esc_to_xml(config.xml_root, output_8bit);
output_8bit << '>';
}
}
void App::SerializeAllFrames()
{
auto i = cur.request->out_streams.streams_map.begin();
bool is_first = true;
for( ; i != cur.request->out_streams.streams_map.end() ; ++i)
{
if( cur.request->answer_container == Request::AnswerContainer::answer_json && !is_first )
{
output_8bit << ',';
}
if( cur.request->answer_container == Request::AnswerContainer::answer_xml && i->first.empty() )
{
compress.Compressing(output_8bit.c_str(), output_8bit.length(), compressed_output, compress_encoding);
output_size = compressed_output.size();
log << log2 << "App: I cannot serialize a frame with an empty name to xml (frame skipped)" << logend;
}
else
{
output_size = output_8bit.size();
SerializeStream(i->second->get_buffer(), i->first.c_str());
}
is_first = false;
}
}
PrepareHeaders(compressing, compress_encoding, header, output_size);
SendHeaders();
SendCookies();
FCGX_PutS("\r\n", fcgi_request.out);
if( CanSendContent() )
void App::SerializeOneFrame()
{
auto i = cur.request->out_streams.streams_map.find(cur.request->frame);
if( i != cur.request->out_streams.streams_map.end() )
{
if( compressing )
SendData(compressed_output, fcgi_request.out);
SerializeStream(i->second->get_buffer(), cur.request->frame.c_str());
}
else
{
log << log2 << "App: there is no such a frame: " << cur.request->frame << logend;
// return 404 in such a case?
}
}
void App::SerializeModels()
{
Ezc::Models::ModelsMap models_map = cur.request->models.GetMap();
auto i = models_map.begin();
if( cur.request->answer_container == Request::AnswerContainer::answer_json )
{
output_8bit << '{';
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
{
output_8bit << '<';
pt::esc_to_xml(config.xml_root, output_8bit);
output_8bit << '>';
log << log2 << "App: serializing models to xml not implemented yet" << logend;
}
bool is_first = true;
for( ; i != models_map.end() ; ++i)
{
if( cur.request->answer_container == Request::AnswerContainer::answer_json && !is_first )
{
output_8bit << ',';
}
if( cur.request->answer_container == Request::AnswerContainer::answer_xml && i->first.empty() )
{
log << log2 << "App: I cannot serialize a model with an empty name to xml (model skipped)" << logend;
}
else
FCGX_PutStr(output_8bit.c_str(), output_8bit.size(), fcgi_request.out);
{
SerializeModel(i->second, i->first.c_str());
}
is_first = false;
}
if( cur.request->answer_container == Request::AnswerContainer::answer_json )
{
output_8bit << '}';
}
else
if( cur.request->answer_container == Request::AnswerContainer::answer_xml )
{
output_8bit << "</";
pt::esc_to_xml(config.xml_root, output_8bit);
output_8bit << '>';
}
}
void App::SendData(const BinaryPage & page, FCGX_Stream * out)
void App::SerializeModel(morm::Wrapper & wrapper, const wchar_t * field_name)
{
const size_t buf_size = 4096;
switch( cur.request->answer_container )
{
case Request::AnswerContainer::answer_json:
SerializeModelJson(wrapper, field_name);
break;
if( send_data_buf.size() != buf_size )
send_data_buf.resize(buf_size);
case Request::AnswerContainer::answer_xml:
SerializeModelXml(wrapper, field_name);
break;
BinaryPage::const_iterator i = page.begin();
BinaryPage::const_iterator end = page.end();
case Request::AnswerContainer::answer_csv:
SerializeModelCsv(wrapper, field_name);
break;
while( i != end )
case Request::AnswerContainer::answer_text:
default:
SerializeModelCsv(wrapper, field_name);
break;
}
}
void App::SerializeModelJson(morm::Wrapper & wrapper, const wchar_t * field_name)
{
if( field_name )
{
size_t s = 0;
output_8bit << '"';
pt::esc_to_json(field_name, output_8bit);
output_8bit << "\":";
}
for( ; i != end && s < buf_size ; ++i, ++s)
send_data_buf[s] = *i;
if( wrapper.model )
{
serialized_model.clear();
wrapper.model->set_connector(model_connector);
wrapper.model->to_text(serialized_model);
output_8bit << serialized_model;
}
if( s > 0 )
FCGX_PutStr(send_data_buf.c_str(), s, out);
if( wrapper.date )
{
output_8bit << '"';
wrapper.date->SerializeISO(output_8bit);
output_8bit << '"';
}
if( wrapper.space_wrapper )
{
wrapper.space_wrapper->get_space()->serialize_to_json_stream(output_8bit, false);
}
if( wrapper.model_container_wrapper )
{
wrapper.model_container_wrapper->set_iterator_at_first_model();
bool is_first = true;
output_8bit << '[';
while( wrapper.model_container_wrapper->is_iterator_correct() )
{
if( !is_first )
output_8bit << ',';
morm::Model * model = wrapper.model_container_wrapper->get_model();
serialized_model.clear();
model->set_connector(model_connector);
model->to_text(serialized_model);
output_8bit << serialized_model;
wrapper.model_container_wrapper->increment_iterator();
is_first = false;
}
output_8bit << ']';
}
}
void App::SendBinaryAnswer()
void App::SerializeModelXml(morm::Wrapper & wrapper, const wchar_t * field_name)
{
BinaryPage & source = cur.request->out_bin_stream;
Header header = h_200;
Error status = cur.request->status;
bool compressing;
int compress_encoding;
// IMPROVEME
log << log2 << "App: serializing models to xml not implemented yet" << logend;
}
SelectCompression(source.size(), compressing, compress_encoding);
if( status == WINIX_ERR_NO_ITEM || status == WINIX_ERR_NO_FUNCTION || status == WINIX_ERR_UNKNOWN_PARAM )
header = h_404;
void App::SerializeModelCsv(morm::Wrapper & wrapper, const wchar_t * field_name)
{
// IMPROVEME
log << log2 << "App: serializing models to csv not implemented yet" << logend;
}
if( status == WINIX_ERR_PERMISSION_DENIED || status == WINIX_ERR_CANT_CHANGE_USER || status == WINIX_ERR_CANT_CHANGE_GROUP )
header = h_403;
// !! IMPROVE ME add header: content-length (size from Item struct)
// warning: if someone changed a file on the disk (in the real os)
// then winix would send an incorrect content-lenght header,
// we are waiting for the fsck winix function to be implemented
PrepareHeaders(compressing, compress_encoding, header, static_cast<size_t>(-1));
// IMPROVEME
// gime me a better name
void App::FilterHtmlIfNeeded(const pt::WTextStream & input_stream, BinaryPage & output, bool clear_stream)
{
if( config.html_filter && cur.request->use_html_filter )
{
TemplatesFunctions::html_filter.filter(input_stream, output, clear_stream);
}
else
{
pt::wide_stream_to_utf8(input_stream, output, clear_stream);
}
}
void App::Send8bitOutput(BinaryPage & output)
{
bool compressing = false;
int compress_encoding = 0;
Header header = GetHTTPStatusCode();
size_t output_size = 0;
SelectCompression(output.size(), compressing, compress_encoding);
if( config.log_server_answer )
{
log << log1 << "App: the server's answer is:\n" << output << "\nApp: end of the server's answer" << logend;
}
if( compressing )
{
compress.Compressing(output, compressed_output, compress_encoding);
output_size = compressed_output.size();
}
else
{
output_size = output.size();
}
PrepareHeaders(compressing, compress_encoding, header, output_size);
SendHeaders();
SendCookies();
FCGX_PutS("\r\n", fcgi_request.out);
@ -1997,29 +2229,13 @@ int compress_encoding;
if( CanSendContent() )
{
if( compressing )
{
compress.Compressing(source, compressed_output, compress_encoding);
SendData(compressed_output, fcgi_request.out);
}
else
{
SendData(source, fcgi_request.out);
}
SendData(output, fcgi_request.out);
}
}
void App::SendAnswer()
{
if( cur.request->return_json )
PrepareStandardJSONFields();
if( cur.request->send_bin_stream )
SendBinaryAnswer();
else
SendTextAnswer();
}
void App::LogUser(const char * msg, uid_t id)
{

38
winixd/core/app.h

@ -147,16 +147,20 @@ private:
pthread_t signal_thread;
std::string socket_to_send_on_exit;
std::string send_data_buf;
TextStream<std::wstring> json_out_stream;
pt::WTextStream json_out_stream;
std::string aheader_name, aheader_value;
std::wstring html_filtered;
std::string output_8bit;
//std::wstring html_filtered;
//std::string output_8bit;
pt::TextStream serialized_model;
BinaryPage output_8bit, output_8bit2;
BinaryPage compressed_output;
std::wstring cookie_id_string;
std::wstring http_header_name;
std::wstring http_header_value;
std::string http_header_8bit;
std::wstring empty_response;
pt::WTextStream empty_response;
WinixModelConnector model_connector; // main thread model connector, each thread has its own connector
morm::JSONConnector json_connector;
@ -194,19 +198,32 @@ private:
void CheckIfNeedSSLredirect();
void SetLocale();
void CheckPostRedirect();
void MakePage();
void MakeEzcGenerator();
void AddDefaultModels();
void Make();
void SaveSessionsIfNeeded(); // !! IMPROVE ME wywalic do menagera sesji??
void LogAccess();
void SendData(const BinaryPage & page, FCGX_Stream * out);
const std::wstring * CreateFrameAnswer();
void CreateJSONAnswer();
void ReadRequest();
void SendTextAnswer();
void SendBinaryAnswer();
void SendAnswer();
void SerializeStream(const pt::WTextStream & input_stream, const wchar_t * field_name);
void SerializeStreamJson(const pt::WTextStream & input_stream, const wchar_t * field_name);
void SerializeStreamXml(const pt::WTextStream & input_stream, const wchar_t * field_name);
void SerializeStreamCsv(const pt::WTextStream & input_stream, const wchar_t * field_name);
void SerializeFrames();
void SerializeAllFrames();
void SerializeOneFrame();
void SerializeModels();
void Send8bitOutput(BinaryPage & output);
void SerializeModel(morm::Wrapper & wrapper, const wchar_t * field_name);
void SerializeModelJson(morm::Wrapper & wrapper, const wchar_t * field_name);
void SerializeModelXml(morm::Wrapper & wrapper, const wchar_t * field_name);
void SerializeModelCsv(morm::Wrapper & wrapper, const wchar_t * field_name);
void FilterHtmlIfNeeded(const pt::WTextStream & input_stream, BinaryPage & output, bool clear_stream = true);
void LogEnvironmentVariables();
void LogEnvironmentHTTPVariables();
@ -226,10 +243,10 @@ private:
void CheckHtmx();
void SetSubdomain();
bool IsRequestedFrame();
Header GetHTTPStatusCode();
void PrepareSessionCookie();
void FilterContent();
void SendHeaders();
void SendCookies();
bool AddHeader(const wchar_t * name, const wchar_t * value);
@ -245,7 +262,6 @@ private:
void PrepareHeadersCompression(int compress_encoding);
void PrepareHeadersNormal(Header header, size_t output_size);
void PrepareHeaders(bool compressing, int compress_encoding, Header header, size_t output_size);
void PrepareStandardJSONFields();
int SelectDeflateVersion();
void SelectCompression(size_t source_len, bool & compression_allowed, int & compression_encoding);
bool CanSendContent();

636
winixd/core/bbcodeparser.cpp

@ -1,636 +0,0 @@
/*
* This file is a part of Winix
* and is distributed under the 2-Clause BSD licence.
* Author: Tomasz Sowa <t.sowa@ttmath.org>
*/
/*
* Copyright (c) 2008-2018, Tomasz Sowa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "bbcodeparser.h"
namespace Winix
{
bool BBCODEParser::Equal(const wchar_t * str1, const wchar_t * str2)
{
while( *str1 == *str2 && *str1 != 0 )
{
str1 += 1;
str2 += 1;
}
return *str1 == *str2;
}
bool BBCODEParser::IsValidCharForName(int c)
{
if( (c>='a' && c<='z') ||
(c>='A' && c<='Z') ||
c=='*' || c=='_')
return true;
return false;
}
bool BBCODEParser::IsOpeningTagMark(wchar_t c)
{
return (c == '[');
}
bool BBCODEParser::IsClosingTagMark(wchar_t c)
{
return (c == ']');
}
bool BBCODEParser::IsClosingXmlSimpleTagMark(wchar_t c)
{
return false;
}
// there are no commentaries in bbcode
bool BBCODEParser::IsOpeningCommentaryTagMark(const wchar_t *)
{
return false;
}
size_t BBCODEParser::OpeningCommentaryTagMarkSize()
{
return 0;
}
bool BBCODEParser::SkipCommentaryTagIfExists()
{
return false;
}
// one enter will generate one <br>
// two enters or more will generate only two br (<br><br>)
void BBCODEParser::PutNormalText(const wchar_t * str, const wchar_t * end)
{
int br_len;
if( *pchar == 0 )
{
// trimming last white characters at end of the user text
while( str<end && (IsWhite(*(end-1)) || *(end-1)==10) )
--end;
}
while( str < end )
{
if( *str == 10 )
{
++str;
br_len = 1;
// skipping white characters without a new line character
while( str < end && IsWhite(*str) )
++str;
if( str < end && *str == 10 )
{
br_len = 2;
// skipping white characters with new line characters
while( str < end && (IsWhite(*str) || *str==10) )
++str;
}
if( !has_open_ol_tag && !has_open_ul_tag && !has_open_li_tag )
{
for(int i=0 ; i < br_len ; ++i)
(*out_string) += L"<br>\n";
}
}
else
{
PrintEscape(*str);
++str;
}
}
}
void BBCODEParser::ReadNormalTextSkipWhite(const wchar_t * & start, const wchar_t * & last_non_white)
{
}
void BBCODEParser::CheckExceptions()
{
if( stack_len >= 2 )
{
if( pstack[stack_len-1].type == Item::opening &&
pstack[stack_len-2].type == Item::opening &&
IsNameEqual(L"*", pstack[stack_len-1].name) &&
IsNameEqual(L"*", pstack[stack_len-2].name) )
{
// removing the last [*] from the stack
// </li> was put automatically
PopStack();
}
}
}
/*
bbcode format:
[bbcodetag=value]some text[/bbcodetag]
the value can be quoted, e.g.
[bbcodetag="value"]some text[/bbcodetag], or
[bbcodetag='value']some text[/bbcodetag]
the third string below (in tags table) is 'html_argument' from Tags,
it can contain a special character % followed by a string which means:
%1 - "value" escaped as for html
%2 - "some text" escaped as for html
%u1 - "value" trimmed and escaped as for url-es
%u2 - "some text" trimmed and escaped as for url-es
%% - one %
if you are using %2 or %u2 then "some text" is not treated as bbcode, e.g.
[bbcodetag=value]some [b]text[/b][/bbcodetag] will produce:
<htmltag arg="value">some [b]text[/b]</htmltag> (the inner tags [b][/b] were not parsed)
also when using %2 or %u2 the closing bbcode tag is skipped
(if you want this tag then you can put it in 'html_argument')
and when using u (%u1 or %u2) the argument is trimmed from whitespaces and new lines
at the beginning and at the end
(because otherwise a space would be changed to %20 and this were probably not what you really wanted)
*/
const BBCODEParser::Tags * BBCODEParser::FindTag(const wchar_t * tag)
{
static Tags tags[] = {
{L"*", L"li", L">", false},
{L"b", L"em", L">", true},
{L"i", L"span", L" class=\"bbitalic\">", true},
{L"u", L"span", L" class=\"bbunderline\">", true},
{L"s", L"span", L" class=\"bbstrike\">", true},
{L"code", L"code", L" class=\"bbcode\">", false},
{L"list", L"ul", L" class=\"bblist\">", false},
{L"color", L"span", L" class=\"bbcol%1\">", true},
{L"url", L"a", L" href=\"%u1\">", true},
{L"img", L"img", L" alt=\"%1\" src=\"%u2\">", true},
{L"quote", L"div", L" class=\"bbquote\">\n<span class=\"bbquotewho\">%1</span><br>\n", false},
};
size_t i;
size_t len = sizeof(tags) / sizeof(Tags);
for(i=0 ; i<len ; ++i)
{
if( Equal(tag, tags[i].bbcode) )
return &tags[i];
}
return 0;
}
const BBCODEParser::Tags * BBCODEParser::FindTag(const std::wstring & tag)
{
return FindTag(tag.c_str());
}
void BBCODEParser::PrintArgumentCheckQuotes(const wchar_t * & start, const wchar_t * & end)
{
// skipping white characters from the argument
while( start<end && IsWhite(*start) )
++start;
// skipping first '=' character if exists
if( start<end && *start == '=' )
++start;
// skipping white characters from the argument
// at the beginning
while( start<end && IsWhite(*start) )
++start;
// and at the end
while( start<end && IsWhite(*(end-1)) )
--end;
if( start<end && (*start=='\'' || *start=='\"') )
{
++start;
if( start<end && *(start-1) == *(end-1) )
--end;
// skipping white characters after a first quote char [url = " ww...."]
while( start<end && IsWhite(*start) )
++start;
}
}
void BBCODEParser::PrintEncode(int c)
{
if( c == '&' )
{
(*out_string) += L"&amp;";
}
else
if( (c>='a' && c<='z') ||
(c>='A' && c<='Z') ||
(c>='0' && c<='9') ||
(c=='_' || c=='?' || c=='.' || c==',' || c=='/' || c=='-' ||
c=='+' || c=='*' || c=='(' || c==')' || c=='=' || c==':')
)
{
(*out_string) += c;
}
else
{
wchar_t buffer[20];
swprintf(buffer, 20, L"%02X", c);
(*out_string) += '%';
(*out_string) += buffer;
}
}
void BBCODEParser::PrintEscape(int c, bool change_quote)
{
if( c == '<' )
{
(*out_string) += L"&lt;";
}
else
if( c == '>' )
{
(*out_string) += L"&gt;";
}
else
if( c == '&' )
{
(*out_string) += L"&amp;";
}
else
if( c == '\"' && change_quote )
{
(*out_string) += L"&quot;";
}
else
{
(*out_string) += c;
}
}
void BBCODEParser::PrintArgumentEncode(const wchar_t * start, const wchar_t * end)
{
PrintArgumentCheckQuotes(start, end);
TrimWhiteWithNewLines(start, end);
for( ; start<end ; ++start )
PrintEncode(*start);
}
void BBCODEParser::PrintArgumentEscape(const wchar_t * start, const wchar_t * end)
{
PrintArgumentCheckQuotes(start, end);
for( ; start<end ; ++start )
PrintEscape(*start, true); // quotes are escaped as well here
}
void BBCODEParser::CheckOpeningTag(const Tags * tag, const wchar_t *