summaryrefslogtreecommitdiffstats
path: root/lurker/render/main.cpp
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2013-10-06 10:33:05 -0700
committerKaz Kylheku <kaz@kylheku.com>2013-10-06 10:33:05 -0700
commitb479c22c713be413b9135be8f1d4e108d33f17f6 (patch)
treeaaaae4d4fcb5df6ef6414a6e825e8f4e4730f6ee /lurker/render/main.cpp
downloadlurker-b479c22c713be413b9135be8f1d4e108d33f17f6.tar.gz
lurker-b479c22c713be413b9135be8f1d4e108d33f17f6.tar.bz2
lurker-b479c22c713be413b9135be8f1d4e108d33f17f6.zip
lurker-2.3
mimelib-3.1.1
Diffstat (limited to 'lurker/render/main.cpp')
-rw-r--r--lurker/render/main.cpp310
1 files changed, 310 insertions, 0 deletions
diff --git a/lurker/render/main.cpp b/lurker/render/main.cpp
new file mode 100644
index 0000000..bf00e08
--- /dev/null
+++ b/lurker/render/main.cpp
@@ -0,0 +1,310 @@
+/* $Id: main.cpp 1649 2009-10-19 14:35:01Z terpstra $
+ *
+ * main.cpp - Transform a database snapshot to useful output
+ *
+ * Copyright (C) 2002 - Wesley W. Terpstra
+ *
+ * License: GPL
+ *
+ * Authors: 'Wesley W. Terpstra' <wesley@terpstra.ca>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define _FILE_OFFSET_BITS 64
+
+#include <cstdlib>
+#include <cerrno>
+#include <cstring>
+
+#include <iostream>
+#include <string>
+#include <memory>
+#include <vector>
+
+#include "commands.h"
+#include "parse.h"
+
+#include <XmlEscape.h>
+#include <unistd.h> // chdir
+
+/* #define DEBUG 1 */
+
+using namespace std;
+
+void error(
+ const string& main,
+ const string& sub,
+ const string& suggest,
+ const string& header)
+{
+ cout << "Status: 200 OK\r\n";
+ cout << header; /* optional additional header */
+ cout << "Content-Type: text/html\r\n\r\n";
+ cout << "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\r\n"
+ << "<html>\r\n"
+ << " <head><title>Lurker - "
+ << xmlEscape << main
+ << "</title></head>\r\n"
+ << " <body>\r\n"
+ << " <h1>Lurker - failed to render page:</h1>\r\n"
+ << " <h2>"
+ << xmlEscape << main << " (" << xmlEscape << sub << "):</h2><p>\r\n"
+ << xmlEscape << suggest << "\r\n"
+ << " <p><hr>\r\n"
+ << " </body>\r\n"
+ << "</html>\r\n";
+ exit(1);
+}
+
+void tokenize(
+ const string& str,
+ vector<string>& tokens,
+ const string& delimiters)
+{
+ string::size_type lastPos = str.find_first_not_of(delimiters, 0);
+ string::size_type pos = str.find_first_of(delimiters, lastPos);
+
+ while (string::npos != pos || string::npos != lastPos)
+ {
+ tokens.push_back(str.substr(lastPos, pos - lastPos));
+ lastPos = str.find_first_not_of(delimiters, pos);
+ pos = str.find_first_of(delimiters, lastPos);
+ }
+}
+
+Request parse_request(const string& param)
+{
+ string::size_type dot1 = param.rfind('.');
+ if (dot1 == string::npos || dot1 == 0)
+ error(_("Missing extension"), param,
+ _("An extension for the request was required, but missing"));
+
+ string::size_type dot2 = param.rfind('.', dot1-1);
+
+ // This is an attempt at backwards compatability.
+ // Previously lurker had no language code in the URL, but still had
+ // period delimited fields in message ids. The last part is always 8
+ // characters long, so we assume english if the country code is
+ // not of length in [2, 8).
+ // NOTE: some search URLs will still be broken (those with .s)
+ if (dot2 != string::npos && (dot1 - dot2 < 2+1 || dot1 - dot2 >= 8+1))
+ dot2 = string::npos;
+
+ Request out;
+
+ if (dot2 == string::npos)
+ {
+ out.options = param.substr(0, dot1);
+ out.language = "en";
+ out.ext = param.substr(dot1+1, string::npos);
+ }
+ else
+ {
+ out.options = param.substr(0, dot2);
+ out.language = param.substr(dot2+1, (dot1-dot2) - 1);
+ out.ext = param.substr(dot1+1, string::npos);
+ }
+
+ if (out.ext.length() < 3 || out.ext.length() > 6)
+ error(_("Bogus extension"), out.ext,
+ _("The extension is not 3-6 characters long."));
+
+ for (string::size_type i = 0; i < out.ext.length(); ++i)
+ if ((out.ext[i] < 'a' || out.ext[i] > 'z') &&
+ (out.ext[i] < '0' || out.ext[i] > '9'))
+ error(_("Bogus extension"), out.ext,
+ _("Not simple lower-case alphanumeric letters."));
+
+ if (!lstring::locale_normalize(out.language))
+ error(_("Bogus locale"), out.language,
+ _("The specified locale is not valid."));
+
+ return out;
+}
+
+int main(int argc, char** argv)
+{
+ string config, frontend, document, request, host, port, cgipath, https, ok;
+ const char* tmp;
+
+#if 0
+ setlocale(LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ bind_textdomain_codeset(PACKAGE, "utf-8");
+#endif
+
+ // Every document about CGI agrees these exist:
+ if ((tmp = getenv("SERVER_NAME" )) != 0) host = tmp;
+ if ((tmp = getenv("SERVER_PORT" )) != 0) port = tmp;
+ if ((tmp = getenv("SCRIPT_NAME" )) != 0) cgipath = tmp;
+ // Many CGI 'standards' seem to agree this one exists for https:
+ if ((tmp = getenv("HTTPS" )) != 0) https = tmp;
+
+ // CGI guarantees this in case called as an error document
+ if ((tmp = getenv("REDIRECT_URL")) != 0) request = tmp;
+ // ... however, as we aren't always called that way, try this too:
+ if ((tmp = getenv("REQUEST_URI" )) != 0) request = tmp;
+
+ // get an over-ridden config location
+ if ((tmp = getenv("REDIRECT_LURKER_CONFIG")) != 0) config = tmp;
+ if ((tmp = getenv("LURKER_CONFIG")) != 0) config = tmp;
+ // get the frontend location
+ if ((tmp = getenv("REDIRECT_LURKER_FRONTEND")) != 0) document = tmp;
+ if ((tmp = getenv("LURKER_FRONTEND")) != 0) document = tmp;
+
+ if (request == "" || host == "" || port == "" || cgipath == "")
+ error(_("Not invoked correctly"),
+ _("CGI environment variables missing"),
+ _("The lurker.cgi must be run as a CGI script. See the "
+ "INSTALL file distributed with lurker for help setting "
+ "up your webserver to run lurker.cgi. Lurker.cgi reads "
+ "the environment variables REDIRECT_URL or REQUEST_URI "
+ "to determine the missing file requested by the user. "
+ "Also, SERVER_NAME, SERVER_PORT, and SCRIPT_NAME are "
+ "used to build absolute redirected URLs."));
+
+ // be nice: use a default config file
+ if (config == "") config = DEFAULT_CONFIG_FILE;
+
+ Config cfg;
+ if (cfg.load(config.c_str()) != 0)
+ error(_("Cannot open config file"), "Config::load",
+ cfg.getError() +
+ _("\nPerhaps you should set the LURKER_CONFIG "
+ "environment variable to select the correct "
+ "config file location. See the INSTALL file for "
+ "help on configuring your webserver."));
+
+ if (document == "" && cfg.frontends.size() > 1)
+ error(_("No frontend specified"), "LURKER_FRONTEND",
+ _("The lurker config file lists multiple frontends, "
+ "however, the environment variable LURKER_FRONTEND "
+ "does not specify which to use. See the INSTALL file "
+ "for help on configuring your webserver."));
+
+ // be nice: if only one frontend, use it by default:
+ if (document == "" && cfg.frontends.size() == 1)
+ {
+ document = cfg.frontends.begin()->first;
+ }
+ else
+ {
+ // Simplify the path to the requested document
+ if ((ok = simplifyPath(document)) != "")
+ error(_("Bad document request"), document,
+ _("The path '") + ok + _("' could not be resolved while "
+ "attempting to determine which frontend the document "
+ "belongs to. Perhaps a directory does not exist?"));
+ }
+
+ // Look for the matching front-end
+ frontend = "";
+ Config::Frontends::const_iterator i, e;
+ for (i = cfg.frontends.begin(), e = cfg.frontends.end(); i != e; ++i)
+ {
+ // Either document IS the frontend path or it is a file
+ // contained in the frontend path.
+ if (i->first == document.substr(0, i->first.length()) ||
+ i->first + "/" == document.substr(0, i->first.length()+1))
+ {
+ frontend = i->first;
+ break;
+ }
+ }
+
+ if (frontend == "")
+ error(_("No matching frontend"), document,
+ _("The frontend specified in the webserver "
+ "configuration does not match any frontend in the "
+ "lurker config file."));
+
+ cfg.set_permissions(cfg.frontends[frontend]);
+
+ if (chdir(frontend.c_str()) != 0)
+ error(_("Cannot chdir"), frontend + ":" + strerror(errno),
+ _("The specified frontend path could "
+ "not be entered. Check the path and permissions."));
+
+ auto_ptr<ESort::Reader> db(ESort::Reader::opendb(cfg.dbdir + "/db"));
+ if (!db.get())
+ error(_("Cannot open database snapshot"), strerror(errno),
+ _("The configured database 'dbdir' in the config file "
+ "could not be opened. Typically this means that it is "
+ "not readable by the user which the cgi is invoked as. "
+ "We suggest making dbdir and all files in it readable "
+ "by everyone since you are serving them on a website "
+ "anyways."));
+
+ request = decipherHalf(request);
+ vector<string> tokens;
+ tokenize(request, tokens, "/");
+ if (tokens.size() < 2)
+ error(_("Request malformed"), "tokenize(request)",
+ _("The request does not have at least two directory "
+ "components. It must be like ..../command/param.xml"));
+
+ string param = tokens[tokens.size()-1];
+ string command = tokens[tokens.size()-2];
+ string server;
+
+ if (document != frontend &&
+ frontend + '/' + command + '/' + param != document)
+ error(_("Requested document is in error"), document,
+ _("The requested document does not match the file "
+ "lurker intends to generate: ")
+ + frontend + '/' + command + '/' + param);
+
+ if (https == "on")
+ {
+ server = "https://" + host;
+ if (port != "443") server += ":" + port;
+ }
+ else
+ {
+ server = "http://" + host;
+ if (port != "80") server += ":" + port;
+ }
+
+ cfg.cgiUrl = server + cgipath;
+ string::size_type psplit;
+ if ((psplit = cfg.cgiUrl.rfind('/')) != string::npos)
+ cfg.cgiUrl.resize(psplit);
+
+ vector<string>::size_type tok;
+ cfg.docUrl = server;
+ for (tok = 0; tok < tokens.size()-2; ++tok)
+ cfg.docUrl += "/" + tokens[tok];
+
+ // flush all request data in case user made it huge to be an ass
+ tokens.clear();
+ request = "";
+ config = "";
+
+ cfg.command = command;
+
+ if (command == "message") return handle_message(cfg, db.get(), param);
+ else if (command == "thread") return handle_thread (cfg, db.get(), param);
+ else if (command == "mindex") return handle_mindex (cfg, db.get(), param);
+ else if (command == "splash") return handle_splash (cfg, db.get(), param);
+ else if (command == "search") return handle_search (cfg, db.get(), param);
+ else if (command == "attach") return handle_attach (cfg, db.get(), param);
+ else if (command == "mbox") return handle_mbox (cfg, db.get(), param);
+ else if (command == "list") return handle_list (cfg, db.get(), param);
+ else if (command == "zap") return handle_zap (cfg, db.get(), param);
+ else
+ error(_("Bad command"), command,
+ _("The requested command is not supported."));
+}