diff options
| author | Roland Reichwein <mail@reichwein.it> | 2023-02-11 11:58:03 +0100 | 
|---|---|---|
| committer | Roland Reichwein <mail@reichwein.it> | 2023-02-11 11:58:03 +0100 | 
| commit | aaaaed8479368b87bc6960dc6abe5a97b4cc7ef9 (patch) | |
| tree | b42aba96bb84c0e3593b910011a8b05cffe4e7be /plugins/weblog | |
| parent | e1f1bf463631c73b5a7e70c3f3a1b1206df6c317 (diff) | |
Separated out weblog
Diffstat (limited to 'plugins/weblog')
| -rw-r--r-- | plugins/weblog/Makefile | 52 | ||||
| -rw-r--r-- | plugins/weblog/html/blog.css | 76 | ||||
| -rw-r--r-- | plugins/weblog/html/favicon.ico | bin | 2238 -> 0 bytes | |||
| -rwxr-xr-x | plugins/weblog/procmail/procmail.sh | 119 | ||||
| -rw-r--r-- | plugins/weblog/procmail/procmailrc | 12 | ||||
| -rw-r--r-- | plugins/weblog/weblog.cpp | 456 | ||||
| -rw-r--r-- | plugins/weblog/weblog.h | 23 | 
7 files changed, 0 insertions, 738 deletions
diff --git a/plugins/weblog/Makefile b/plugins/weblog/Makefile deleted file mode 100644 index 2e19495..0000000 --- a/plugins/weblog/Makefile +++ /dev/null @@ -1,52 +0,0 @@ -include ../../common.mk - -PROJECTNAME=weblog - -CXXFLAGS+= -fvisibility=hidden -fPIC - -CXXFLAGS+= -I../.. - -LDLIBS=\ --lreichwein \ --lboost_context \ --lboost_coroutine \ --lboost_program_options \ --lboost_system \ --lboost_thread \ --lboost_filesystem \ --lboost_regex \ --lpthread \ --lssl -lcrypto \ --ldl - -PROGSRC=\ -    weblog.cpp - -SRC=$(PROGSRC) - -all: $(PROJECTNAME).so - -$(PROJECTNAME).so: $(SRC:.cpp=.o) -	$(CXX) $(LDFLAGS) $^ -shared $(LDLIBS) $(LIBS) -o $@ - -%.d: %.cpp -	$(CXX) $(CXXFLAGS) -MM -MP -MF $@ -c $< - -%.o: %.cpp %.d -	$(CXX) $(CXXFLAGS) -c $< -o $@ - -# dependencies - -ADD_DEP=Makefile - -install: -	mkdir -p $(DESTDIR)/usr/lib/webserver/plugins -	cp $(PROJECTNAME).so $(DESTDIR)/usr/lib/webserver/plugins - -# misc --------------------------------------------------- -clean: -	-rm -f *.o *.so *.d - -.PHONY: clean install all - --include $(wildcard $(SRC:.cpp=.d)) diff --git a/plugins/weblog/html/blog.css b/plugins/weblog/html/blog.css deleted file mode 100644 index 5277980..0000000 --- a/plugins/weblog/html/blog.css +++ /dev/null @@ -1,76 +0,0 @@ -body { -	font-family: "sans-serif"; -} - -figcaption { -	text-align: center; -	font-size: 8px; -	color: #808080; -} - -figure { -	display: inline-block; -} - -h2 { -	margin: 25px 0px 3px 0px; -} - -div.date { -	font-size: 8px; -	color: #808080; -	margin: 0px 0px 10px 0px; -} - -div.impressum { -	margin: 500px 0px 0px 0px; -} - -.citation { -	font-style:italic; -	margin-left: 50px; -	margin-right: 50px; -} - -.reference { -	text-align: right; -	margin-bottom: 30px; -} - - -.mobile { -	width: 300px; -	border-width: 80px 15px 80px 15px; -       	border-style: solid; -	border-radius: 30px; -	border-color: #000000; -} - -.logo { -	display: block; -	margin: 0 auto; -} - -.screenshot { -	width: 400px; -	border: 2px solid; -	border-color: #8888AA; -} - -img.banner { -	vertical-align: -5px; -} - -@media only screen and (min-width: 1px) and (max-width: 630px) { -} - -@media only screen and (min-width: 631px) and (max-width: 950px) { -} - -@media only screen and (min-width: 951px) { -	div.page { -		max-width: 950px; -		width: 100%; -		margin: 0 auto; -	} -} diff --git a/plugins/weblog/html/favicon.ico b/plugins/weblog/html/favicon.ico Binary files differdeleted file mode 100644 index e8cbddb..0000000 --- a/plugins/weblog/html/favicon.ico +++ /dev/null diff --git a/plugins/weblog/procmail/procmail.sh b/plugins/weblog/procmail/procmail.sh deleted file mode 100755 index 134d957..0000000 --- a/plugins/weblog/procmail/procmail.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash -# -# Mail Blog input script, to be called by procmail -# -set -e - -function atexit { -	if [ "$ERRORMSG" != "" -o -s errormsg.txt ] ; then -		(echo "$ERRORMSG" ; cat errormsg.txt) | mutt -s "bloginput: Error" -- "$ADDR" >> ~/log 2>&1 -	fi -	cd -	rm -rf "$DIR" -} -trap atexit EXIT - -function mimedecode() { - perl -ne 'require MIME::Head; $head = MIME::Head->read(\*STDIN); $head->decode(); print $head->get("Subject")' -} - -function next_index() { -	DIR=$1 - -	LASTENTRY=`ls -1dr $DIR/$(date +%Y)/$(date +%Y%m%d)* 2>/dev/null | xargs -n 1 basename | head -n1` - -	if [ "$LASTENTRY" == "" ] ; then -		echo 001 -		return -	fi - -	INDEX=${LASTENTRY:9:3} - -	INDEX=$(($INDEX + 1)) - -	while [ "$((${#INDEX} < 3))" == "1" ] ; do INDEX=0$INDEX ; done - -	echo $INDEX -} - -echo "Log at `date`:" >> ~/log 2>&1 - -BLOGDIR=/var/www/rolandreichwein.de-blog -#BLOGDIR=/var/www/blog - -# Default: Error -ERRORMSG="General error." -USERLOG="" - -DIR=`mktemp -d` - -cd "$DIR" - -touch body.txt -touch errormsg.txt - -cat > inmail - -ADDR=`grep "^From: " inmail | sed -e 's/^From: //'` -if echo "$ADDR" | grep -q -v "^[a-zA-Z0-9@._-<>\" ]\+$" ; then -	# can't send error message to unknown requester -	ERRORMSG="" -	exit 1 -fi -if echo "$ADDR" | grep -q "weblog-bloginput" ; then -	# don't reply to mails from weblog-bloginput, i.e. prevent mail loops -	ERRORMSG="" -	exit 1 -fi - -ALLLINES=`wc -l inmail | cut -f1 -d" "` - -HEADERLINES=`cat inmail | (n=0; while read i ; do - if [ "$i" == "" ] ; then -  echo $n -  break - fi - n=$(($n + 1)) -done)` - -HEADER=`head -n$HEADERLINES inmail` -BODY=`tail -n$(($ALLLINES - $HEADERLINES - 1)) inmail` - -echo "$HEADER" > /home/weblog-bloginput/header.txt -echo "$BODY" > /home/weblog-bloginput/body.txt - -SUBJECT=`echo "$HEADER" | mimedecode` -SUBJECT_PATH=`echo "$SUBJECT" | sed -e 's/[^a-zA-Z0-9]/_/g'` -ARTICLEINDEX=`next_index $BLOGDIR` - -ARTICLEDIR="$BLOGDIR/`date +%Y/%Y%m%d`_${ARTICLEINDEX}_$SUBJECT_PATH" - -mkdir -p $ARTICLEDIR -echo "Subject: $SUBJECT" >> $ARTICLEDIR/article.data -echo "" >> $ARTICLEDIR/article.data - -# get attachments -munpack -C $ARTICLEDIR `pwd`/inmail >/dev/null || true - -DATANAME="`ls $ARTICLEDIR/*.desc || true`" 2>/dev/null -if [ "$DATANAME" = "" ] ; then - echo "$BODY" >> $ARTICLEDIR/article.data -else - cat "$DATANAME" >> $ARTICLEDIR/article.data - rm "$DATANAME" -fi - -chmod -R a+r $ARTICLEDIR -chmod a+x $ARTICLEDIR - -( -echo "Processed successfully." -echo "ARTICLEDIR=$ARTICLEDIR" -) | mutt -s "Bloginput: $SUBJECT_PATH" -- "$ADDR" >> ~/log 2>&1 - -echo "User log:" >> ~/log -cat body.txt >> ~/log - -ERRORMSG="" -echo "Done successfully at `date`." >> ~/log 2>&1 - diff --git a/plugins/weblog/procmail/procmailrc b/plugins/weblog/procmail/procmailrc deleted file mode 100644 index fa1fe6f..0000000 --- a/plugins/weblog/procmail/procmailrc +++ /dev/null @@ -1,12 +0,0 @@ -:0: -* ^X-Virus-Infected -Virus -# TODO: can be /dev/null later - -:0: -* ^X-Spam-Flag: YES -Spam -# TODO: can be /dev/null later - -:0 fhbw -| /home/weblog-bloginput/procmail.sh diff --git a/plugins/weblog/weblog.cpp b/plugins/weblog/weblog.cpp deleted file mode 100644 index cc2be34..0000000 --- a/plugins/weblog/weblog.cpp +++ /dev/null @@ -1,456 +0,0 @@ -#include "weblog.h" - -#include "libreichwein/mime.h" -#include "libreichwein/stringhelper.h" - -#include <boost/algorithm/string/predicate.hpp> -#include <boost/algorithm/string/replace.hpp> -#include <boost/property_tree/ptree.hpp> -#include <boost/property_tree/xml_parser.hpp> - -#include <algorithm> -#include <filesystem> -#include <fstream> -#include <iostream> -#include <regex> -#include <string> - -using namespace std::string_literals; -namespace fs = std::filesystem; -namespace pt = boost::property_tree; -using namespace Reichwein::Mime; -using namespace Reichwein::Stringhelper; - -namespace { - - const size_t number_of_articles_on_front_page {10}; - const std::string article_filename{"article.data"}; - - // Used to return errors by generating response page and HTTP status code - std::string HttpStatus(std::string status, std::string message, std::function<plugin_interface_setter_type>& SetResponseHeader) - { -  SetResponseHeader("status", status); -  SetResponseHeader("content_type", "text/html"); -  return status + " " + message; - } - - std::string getFile(const fs::path& filename) - { -  std::ifstream file(filename.string(), std::ios::in | std::ios::binary | std::ios::ate); - -  if (file.is_open()) { -   std::ifstream::pos_type fileSize = file.tellg(); -   file.seekg(0, std::ios::beg); - -   std::string bytes(fileSize, ' '); -   file.read(reinterpret_cast<char*>(bytes.data()), fileSize); - -   return bytes; - -  } else { -   throw std::runtime_error("Opening "s + filename.string() + " for reading"); -  } - } - - bool is_index_page(std::string& rel_target) - { -  return (rel_target.size() == 0 || rel_target == "/"); - } - - bool is_index_file(std::string& rel_target, fs::path& path) - { -  // must be top-level file, recognized as mime_type() -  return rel_target.find("/") == rel_target.npos && mime_type(path.string()) != "application/text"; - } - - bool is_article_page(std::string& rel_target, fs::path& path) - { -  return (rel_target.size() >= 2 && rel_target.back() == '/' && fs::is_directory(path)); - } - - bool is_article_file(std::string& rel_target, fs::path& path) - { -  return (fs::is_regular_file(path) && path.filename().string() != article_filename); - } - - struct ArticleInfo - { -  fs::path path; -  std::string subject; -  std::string date; - }; - - // get article metadata from header lines - std::unordered_map<std::string, std::string> getMetaData(fs::path path) - { -  if (path.string().size() > 0 && path.string().back() == '/') { -   std::string s {path.string()}; -   path = s.substr(0, s.size() - 1); -  } -  std::unordered_map<std::string, std::string> result; - -  std::string pathname{path.filename().string()}; -  // ISO date -  std::string date{pathname.substr(0, 4) + "-"s + pathname.substr(4, 2) + "-"s + pathname.substr(6, 2)}; - -  result["Date"] = date; - -  fs::path filepath {path / article_filename}; - -  std::ifstream file(filepath.string(), std::ios::in); - -  if (file.is_open()) { -   std::string line; -   while (!file.eof()) { -    std::getline(file, line); -    if (line.empty()) // found header end -     break; -    size_t pos {line.find(": ")}; -    if (pos == line.npos) { -     std::cerr << "Warning: Found bad header line in " << filepath << ": " << line << std::endl; -     continue; -    } -    result[line.substr(0, pos)] = line.substr(pos + 2); -   } -   return result; - -  } else { -   throw std::runtime_error("Opening "s + filepath.string() + " for reading"); -  } - } - - std::vector<ArticleInfo> getArticleList(fs::path& path, size_t page) - { -  std::vector<ArticleInfo> result; - -  for (auto& year_entry: fs::directory_iterator(path)) { -   std::string year_entry_filename{year_entry.path().filename().string()}; -   if (fs::is_directory(year_entry)) { -    for (auto& entry: fs::directory_iterator(year_entry.path())) { -     auto metaData{getMetaData(entry.path())}; -     result.emplace_back(ArticleInfo{entry.path(), metaData.at("Subject"), metaData.at("Date")}); -    } -   } -  } - -  size_t index0{std::min(number_of_articles_on_front_page * (page), result.size())}; -  size_t index1{std::min(number_of_articles_on_front_page * (page + 1), result.size())}; -  // sort backwards -  std::partial_sort(result.begin(), result.begin() + index1, result.end(), [](const ArticleInfo& a0, const ArticleInfo& a1){ return a0.path.string() > a1.path.string();}); -   -  return {result.begin() + index0, result.begin() + index1}; - } - - std::string plainTextFromPTree(const pt::ptree& tree) - { -  std::string result; - -  for (auto child: tree) { -   if (child.first == "<xmltext>") -    result += child.second.data(); -   else -    result += plainTextFromPTree(child.second); -  } - -  return result; - } - - // returns plain text of string (html xml elements removed) - std::string plainTextFromHTML(const std::string& text) - { -  pt::ptree tree; - -  std::istringstream ss{text}; -  pt::read_xml(ss, tree, pt::xml_parser::no_comments | pt::xml_parser::no_concat_text); - -  return plainTextFromPTree(tree); - } - - std::string verbatimText(std::string text) - { -  boost::algorithm::replace_all(text, "<", "<"); -  boost::algorithm::replace_all(text, ">", ">"); - -  return "<pre>"s + text + "</pre>"; - } - - std::regex re{"\\.[[:space:]\"]", std::regex::basic}; -  - // returns teaser of article in plain text - std::string shortVersion(const fs::path& path) - { -  std::string article {getFile(path / article_filename)}; -  size_t pos0 {article.find("\n\n")}; -  if (pos0 == article.npos) -   return ""; - -  article = "<file>" + article.substr(pos0 + 2) + "</file>"; - -  auto metaData{getMetaData(path)}; -  auto it {metaData.find("Content-Type")}; - -  // convert html to plaintext, if tagged as such -  // default: text/html -  if (it == metaData.end() || it->second == "text/html") -   article = plainTextFromHTML(article); - -  size_t pos{1000}; - -  std::smatch match; -  if (std::regex_search(article, match, re)) { -   pos = std::min(pos, static_cast<size_t>(match.position() + match.length())); -  } -   -  return article.substr(0, pos); - } - - class HtmlPage - { -  std::function<std::string(const std::string& key)>& mGetRequestParam; -  std::string mContents; -  std::string mHeader; -  const std::string mFooter; -   - public: -  HtmlPage(std::function<std::string(const std::string& key)>& GetRequestParam, -           std::string s = ""s) -   : mGetRequestParam(GetRequestParam) -   , mContents(s) -   , mHeader("<!DOCTYPE html><html><head>" -             "<meta charset=\"utf-8\"/>" -             "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">" -             "<title>" + GetRequestParam("WEBLOG_NAME") + "</title>" -             "<meta name=\"keywords\" content=\"" + GetRequestParam("WEBLOG_KEYWORDS") + "\"/>" -             "<link rel=\"shortcut icon\" href=\"" + mGetRequestParam("plugin_path") + "/favicon.ico\" type=\"image/x-icon\"/>" -	     "<link rel=\"stylesheet\" type=\"text/css\" href=\"" + mGetRequestParam("plugin_path") + "/blog.css\"/>" -             "</head><body><div class=\"page\">") -   , mFooter("<br/><br/><br/><div class=\"impressum\"><a href=\"" + mGetRequestParam("plugin_path") + "/impressum.html\">Impressum, Datenschutzerklärung</a></div></div></body></html>") -  { -  } - -  HtmlPage& operator+=(const std::string& s) -  { -   mContents += s; -   return *this; -  } - -  operator std::string() const -  { -   return mHeader + mContents + mFooter; -  } - }; - - std::string generateIndexPage(fs::path& path, -                               std::function<std::string(const std::string& key)>& GetRequestParam, -                               std::function<plugin_interface_setter_type>& SetResponseHeader, -                               size_t page) - { -  try { -   if (page > std::numeric_limits<int>::max()) -    throw std::runtime_error("Bad page index: "s + std::to_string(page)); - -   HtmlPage htmlPage{GetRequestParam, "<h1>"s + GetRequestParam("WEBLOG_NAME") + "</h1>"s}; - -   fs::path link{ GetRequestParam("plugin_path")}; -    -   auto list{getArticleList(path, page)}; -   if (list.empty()) -    htmlPage += "(no articles found.)"; -   else { -    for (const auto& article: list) { -     std::string linkstart{"<a href=\"" + (link / article.path.filename()).string() + "/\">"}; -     std::string linkend{"</a>"}; -     htmlPage += "<h2>"s + linkstart + article.subject + linkend + "</h2>"s; -     htmlPage += "<div class=\"date\">" + article.date + "</div>"s; -      -     auto sv{shortVersion(article.path)}; -     if (sv.size()) { -      htmlPage += sv + " "s + linkstart + "more..." + linkend; -     } -    } -    htmlPage += "<br/><br/><br/>"; -    if (page > 0) -     htmlPage += "<a href=\"?page="s + std::to_string(page - 1) + "\"><<newer</a> "s; -    htmlPage += "page "s + std::to_string(page + 1); -    if (list.size() == number_of_articles_on_front_page) -     htmlPage += " <a href=\"?page="s + std::to_string(page + 1) + "\">older>></a>"s; -    htmlPage += "<br/>"; -   } -   SetResponseHeader("cache_control", "no-store"); -   return htmlPage; -  } catch (const std::exception& ex) { -   return HttpStatus("500", "Reading Index page: "s + ex.what(), SetResponseHeader); -  } - } - - std::string generateArticlePage(fs::path& path, -                                 std::function<std::string(const std::string& key)>& GetRequestParam, -                                 std::function<plugin_interface_setter_type>& SetResponseHeader) - { -  try { -   auto metaData{getMetaData(path)}; - -   std::string data { getFile(path / article_filename)}; - -   size_t pos {data.find("\n\n")}; -   if (pos == data.npos) -    throw std::runtime_error("Error parsing article"); -    -   data = data.substr(pos + 2); - -   auto it {metaData.find("Content-Type")}; -   if (it != metaData.end() && it->second == "text/plain") -    data = verbatimText(data); - -   HtmlPage htmlPage{GetRequestParam, "<h1>"s + metaData.at("Subject") + "</h1>" -     "<div class=\"date\">" + metaData.at("Date") + "</div>" -     "<br/><br/>"s + data + "<br/>□"}; - -   return htmlPage; -  } catch (const std::exception& ex) { -   return HttpStatus("500", "Reading Article: "s + ex.what(), SetResponseHeader); -  } - } - - std::string generateStaticFile(fs::path& path, std::function<plugin_interface_setter_type>& SetResponseHeader) - { -  try { -   SetResponseHeader("content_type", mime_type(path.string())); -   return getFile(path); -  } catch (const std::exception& ex) { -   return HttpStatus("500", "Reading Article file: "s + ex.what(), SetResponseHeader); -  } - } - - std::string urlDecode(std::string s) - { -  std::string result; - -  size_t pos = 0; -  while (pos < s.size()) { -   char c {s[pos]}; -   if (c == '+') { -    result += ' '; -   } else if (c == '%' && pos + 2 < s.size()) { -    try { -     int i = stoi(s.substr(pos + 1, 2), 0, 16); -     if (i < 0 || i > 255) -      return result; - -     result += static_cast<char>(i); -    } catch (...) { -     return result; -    } - -    pos += 2; -   } else { -    result += c; -   } -   pos++; -  } - -  return result; - } - - std::unordered_map<std::string, std::string> SplitQueryString(std::string& s) - { -  std::unordered_map<std::string, std::string> result; - -  size_t qpos = s.find('?'); -  if (qpos != s.npos) { -   auto list {split(s.substr(qpos + 1), "&")}; -   for (auto i: list) { -    size_t apos = i.find('='); -    if (apos != i.npos) { -     result[urlDecode(i.substr(0, apos))] = urlDecode(i.substr(apos + 1)); -    } -   } -  } - -  s = s.substr(0, qpos); -   -  return result; - } - -} // anonymous namespace - -std::string weblog_plugin::name() -{ - return "weblog"; -} - -weblog_plugin::weblog_plugin() -{ - //std::cout << "Plugin constructor" << std::endl; -} - -weblog_plugin::~weblog_plugin() -{ - //std::cout << "Plugin destructor" << std::endl; -} - -std::string weblog_plugin::generate_page( -  std::function<std::string(const std::string& key)>& GetServerParam, -  std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...) -  std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string -) -{ - try { -  // Make sure we can handle the method -  std::string method {GetRequestParam("method")}; -  if (method != "GET" && method != "HEAD") -   return HttpStatus("400", "Unknown HTTP method", SetResponseHeader); - -  // Request path must not contain "..". -  std::string rel_target{GetRequestParam("rel_target")}; -  std::string target{GetRequestParam("target")}; -  if (rel_target.find("..") != std::string::npos) { -   return HttpStatus("400", "Illegal request: "s + target, SetResponseHeader); -  } - -  std::unordered_map<std::string, std::string> query { SplitQueryString(rel_target) }; - -  // Build the path to the requested file -  std::string doc_root{GetRequestParam("doc_root")}; -  if (rel_target.size() >= 4 && std::all_of(rel_target.begin(), rel_target.begin() + 4, isdigit)) { -   rel_target = rel_target.substr(0, 4) + "/" + rel_target; -  } -  fs::path path {fs::path{doc_root} / rel_target}; -  if (target.size() && target.back() != '/' && fs::is_directory(path)) { -   std::string location{GetRequestParam("location") + "/"s}; -   SetResponseHeader("location", location); -   return HttpStatus("301", "Correcting directory path", SetResponseHeader); -  } -   -  SetResponseHeader("content_type", "text/html"); -   -  size_t page {0}; -  auto it {query.find("page")}; -  if (it != query.end()) { -   try { -    page = stoul(it->second); -   } catch(...) { -    // ignore: keep default 0 -   } -  } - -  if (is_index_page(rel_target)) -   return generateIndexPage(path, GetRequestParam, SetResponseHeader, page); - -  if (is_article_page(rel_target, path)) -   return generateArticlePage(path, GetRequestParam, SetResponseHeader); - -  if (is_index_file(rel_target, path) || is_article_file(rel_target, path)) -   return generateStaticFile(path, SetResponseHeader); - -  return HttpStatus("404", "Bad path specification: "s + rel_target, SetResponseHeader); - - } catch (const std::exception& ex) { -  return HttpStatus("500", "Unknown Error: "s + ex.what(), SetResponseHeader); - } -} - -bool weblog_plugin::has_own_authentication() -{ - return false; -} diff --git a/plugins/weblog/weblog.h b/plugins/weblog/weblog.h deleted file mode 100644 index 0994b91..0000000 --- a/plugins/weblog/weblog.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../plugin_interface.h" - -class weblog_plugin: public webserver_plugin_interface  -{ -public: - weblog_plugin(); - ~weblog_plugin(); -  - std::string name() override; - - std::string generate_page( -  std::function<std::string(const std::string& key)>& GetServerParam, -  std::function<std::string(const std::string& key)>& GetRequestParam, // request including body (POST...) -  std::function<void(const std::string& key, const std::string& value)>& SetResponseHeader // to be added to result string - ) override; - - bool has_own_authentication() override; -}; - -extern "C" BOOST_SYMBOL_EXPORT weblog_plugin webserver_plugin; -weblog_plugin webserver_plugin;  | 
