Guest User

Untitled

a guest
Oct 31st, 2012
66
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 37.54 KB | None | 0 0
  1. module mspatest;
  2.  
  3. import std.string, sqlite3, std.boehm, std.zlib, std.http, std.util, std.file;
  4.  
  5. static import c.stdlib;
  6. import std.fun, std.cgi, std.process;
  7.  
  8. import mspa;
  9.  
  10. alias FOURSTRING = (string, string, string, string); // small shortcut
  11. alias FIVESTRING = (string, string, string, string, string);
  12.  
  13. string filterTag(string tag) {
  14.   char[auto~] filtered-tagname;
  15.   for auto ch <- tag {
  16.     if ch >= "a" && ch <= "z" || ch >= "A" && ch <= "Z" || ch >= "0" && ch <= "9"
  17.       || "_- .,".find(""~ch) != -1
  18.       filtered-tagname ~= ch;
  19.   }
  20.   return filtered-tagname[];
  21. }
  22.  
  23. string getThreadName(string url) {
  24.   url = url.between("?", "");
  25.   if (auto first = url.between("", "/")) url = first;
  26.   return url.between("-", "");
  27. }
  28.  
  29. int counter;
  30. string reformatPost(string content, bool spoilers, string idText, bool linkToAnchors, noNyud = false) {
  31.   /* strip out existing anchor links */
  32.   do {
  33.     auto startpos = content.find("#post");
  34.   } while (startpos) {
  35.     content = content[0 .. startpos] ~ "\"" ~ content[startpos .. $].between("\"", "");
  36.   }
  37.   if (linkToAnchors) {
  38.     content = content.replace("href=\"showthread.php?p=", "href=\"#post");
  39.   } else {
  40.     content = content.replace("href=\"showthread.php?p=", "href=\"$idText");
  41.   }
  42.   string startMarker = "<!-- BEGIN TEMPLATE: bbcode_quote";
  43.   string endMarker   = "<!-- END TEMPLATE: bbcode_quote";
  44.   string handleQuotes(string str) {
  45.     do {
  46.       auto quotePos = str.find(startMarker);
  47.       auto endPos = str.find(endMarker);
  48.     } while (quotePos && endPos && quotePos < endPos) {
  49.       auto startPos = quotePos + startMarker.length;
  50.       while (str[startPos .. endPos].find startMarker) != -1 {
  51.         str = str[0 .. startPos] ~ handleQuotes str[startPos .. $]; // recurse
  52.         endPos = str.find(endMarker);
  53.         if (endPos == -1) {
  54.           raise new Error "end marker no longer found after recursion step! unbalanced? $content";
  55.         }
  56.       }
  57.       auto end = int:endPos;
  58.       end += str[end .. $].find(">") + 1;
  59.       auto quote = str[quotePos .. end];
  60.       auto name = quote.between("<strong>", "</strong>");
  61.       auto ref = quote.between("href=\"", "\"");
  62.       auto msg = quote.between("class=\"message\">", "\t\t</div");
  63.       auto newtext = "<table><tr><td valign=\"top\">&gt;</td><td><div style=\"border: 1px solid; \"><div><span style=\"border-bottom: 1px solid; \">Originally posted by <a href=\"$ref\">$name</a></span></div>";
  64.       newtext = "$newtext$msg</td></tr></table>";
  65.       str = str[0..quotePos] ~ newtext ~ str[end .. $];
  66.     }
  67.     return str;
  68.   }
  69.   string nyudImages(string src, string baseUrl) {
  70.     int offset;
  71.     do {
  72.       int srcpos = src[offset .. $].find("src=\"");
  73.     } while (srcpos != -1) {
  74.       srcpos += offset;
  75.       srcpos += 5;
  76.       int endpos = src[srcpos .. $].find("\"");
  77.       if (endpos == -1) raise new Error "Unterminated img src string wtf!! ";
  78.       endpos += srcpos;
  79.       auto tag = src[srcpos .. endpos];
  80.       tag = baseUrl.followLink tag;
  81.       tag = tag.startsWith "http://";
  82.       tag = "http://" ~ slice(&tag, "/") ~ ".nyud.net/" ~ tag; // lol side effect abuse
  83.       src = src[0 .. srcpos] ~ tag ~ src[endpos .. $];
  84.       offset = srcpos + tag.length;
  85.     }
  86.     return src;
  87.   }
  88.   content = handleQuotes content;
  89.   if (!noNyud) content = nyudImages (content, "http://www.mspaforums.com/");
  90.   do {
  91.     auto spoilerpos = content.find spoilertext;
  92.   } while spoilerpos != -1 {
  93.     string replacement;
  94.     if (spoilers) {
  95.       replacement = `<div style="border: 1px solid gray; padding: 1px; margin: 2px; "><span style="border: 1px dotted; ">Spoiler</span><div>`;
  96.     } else {
  97.       int id = counter ++;
  98.       replacement = `<div style="border: 1px solid gray; padding: 1px; margin: 2px; "><span style="border: 1px dotted; ">
  99.      <span class="box" id="??C_plus" onclick="javascript: css_show('??C'); css_show('??C_minus'); css_hide('??C_plus'); ">Show Spoiler</span>
  100.      <span class="box" id="??C_minus" onclick="javascript: css_hide('??C'); css_show('??C_plus'); css_hide('??C_minus'); " style="display: none; ">Hide spoiler</span>
  101.      </span><div id="??C" style="display: none; ">`.replace("??C", "spoilerdiv$id");
  102.     }
  103.     content = content[0..spoilerpos] ~ replacement ~ content[spoilerpos + spoilertext.length .. $];
  104.   }
  105.   content = content.replace("src=\"images/smilies", "src=\"http://www.mspaforums.com/images/smilies");
  106.   content = content.replace("<b>", "<div class=\"inline bold\">").replace("</b>", "</div>");
  107.   return content;
  108. }
  109.  
  110. // prevent google from following links that potentially go to very large pages
  111. alias sharedCSS = `
  112.  <meta name="robots" content="index, nofollow" />
  113.  <meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
  114.  <link rel="shortcut icon" href="favicon.ico" />
  115. <style type="text/css">
  116.    table.boxed {
  117.      border: 1px solid black;
  118.      border-collapse: collapse;
  119.    }
  120.    th {
  121.      border-bottom: 2px solid black;
  122.    }
  123.    th.first, th.mid, td.first, td.mid {
  124.      border-right: 1px solid gray;
  125.    }
  126.    tr.even {
  127.      background-color: #def;
  128.    }
  129.    div.plustag:hover *.hidden {
  130.      visibility: visible;
  131.    }
  132.    div.minustag:hover *.hidden {
  133.      visibility: visible;
  134.    }
  135.    div.inline {
  136.      display: inline;
  137.    }
  138.    *.bold {
  139.      font-weight: bold;
  140.    }
  141.    *.box {
  142.      border: 1px solid;
  143.      background-color: #fff;
  144.      padding: 0px 2px 0px 2px;
  145.      margin: 1px;
  146.    }
  147.    *.box_outer {
  148.      border: 1px solid;
  149.      background-color: #fff;
  150.      padding: 0px 2px 0px 2px;
  151.      margin: 5px;
  152.    }
  153.    *.box_inner {
  154.      border: 1px solid;
  155.      padding: 0px 2px 0px 2px;
  156.      margin: 1px;
  157.      background-color: #efe;
  158.    }
  159.    *.hidden {
  160.      visibility: hidden;
  161.      position: absolute;
  162.      background-color: #ddd;
  163.    }
  164.    div.mewdiv {
  165.      position: absolute;
  166.      display: inline;
  167.      background-color: #eef;
  168.      visibility: hidden;
  169.      width: 60%; height: 90%;
  170.    }
  171.    table.mewtable {
  172.      width: 100%; height: 100%;
  173.      border-collapse: collapse;
  174.    }
  175.    iframe.mewframe {
  176.      width: 100%; height: 100%;
  177.      background-color: #ffe;
  178.    }
  179.    </style>`;
  180.  
  181. alias sharedJS = `
  182.  <script type="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script>
  183.  <script type="text/javascript">
  184.    function submitForm(name) {
  185.      document.getElementById(name).submit();
  186.    }
  187.    function loadFrame(name, divname, url) {
  188.      $('.mewdiv').css('visibility', 'hidden');
  189.      var frame = document.getElementById(name), div = document.getElementById(divname);
  190.      frame.src = url;
  191.      div.style.visibility = "visible";
  192.      scrollToThingy (div);
  193.    }
  194.    function scrollToThingy (thing) {
  195.      var targetY = 0;
  196.      
  197.      while (thing) {
  198.        targetY += thing.offsetTop;
  199.        thing = thing.offsetParent;
  200.      }
  201.      window.scroll(0, targetY);
  202.    }
  203.    function css_show (name) {
  204.      document.getElementById(name).style.display = "inline";
  205.    }
  206.    function css_hide (name) {
  207.      document.getElementById(name).style.display = "none";
  208.    }
  209.  </script>`;
  210.  
  211. import c.stdio, std.time;
  212.  
  213. extern(C) { int fprintf(void*, char*, ...); void* stderr; }
  214.  
  215. void main(string[] args) {
  216.   sqlite3.profile = false;
  217.   bool wroteHeader;
  218.   void writeHTMLHeader(bool doctype = true) {
  219.     if (wroteHeader) return;
  220.     writeln "Content-type: text/html; charset=utf-8";
  221.     writeln "";
  222.     if (doctype) {
  223.       writeln `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">`;
  224.       writeln `<html xmlns="http://www.w3.org/1999/xhtml">`;
  225.     } else {
  226.       writeln `<html>`;
  227.     }
  228.     wroteHeader = true;
  229.   }
  230.   set-handler (Error err) {
  231.     writeHTMLHeader();
  232.     writeln "<head><title>Error</title></head><body>";
  233.     writeln "<h2>An error has occurred! </h2>";
  234.     writeln "$err";
  235.     writeln "</body></html>";
  236.     invoke-exit "return";
  237.   }
  238.   set-handler (SQLiteBusy) {
  239.     fprintf(FILE*:stderr, "Database busy. \n");
  240.     sleep 1;
  241.     fprintf(FILE*:stderr, "Retrying. \n");
  242.     invoke-exit "retry";
  243.   }
  244.   define-exit "return" return;
  245.   initBoehm();
  246.   auto db = new Database "threads.sqlite3";
  247.   onExit db.close;
  248.   db.mkfast;
  249.  
  250.   onSuccess if sqlite3.profile {
  251.     void errprint(string s) fprintf(FILE*:stderr, toStringz "$s\n");
  252.     errprint "profile: ";
  253.     for auto tup <- profiledata {
  254.       errprint "  $(tup[2]): $(tup[1]): $(tup[0])";
  255.     }
  256.   }
  257.  
  258.   string getEnvVar(string name) {
  259.     return CToString c.stdlib.getenv toStringz name;
  260.   }
  261.   auto query_string = getEnvVar "QUERY_STRING";
  262.   auto http_cookie = getEnvVar "HTTP_COOKIE";
  263.   // lol wtf
  264.   while (query_string.find("&amp;") != -1) {
  265.     query_string = query_string.replace("&amp;", "&");
  266.   }
  267.   (string, string)[auto~] params;
  268.   for auto part <- query_string.split "&" {
  269.     if (part.find("=") != -1)
  270.       params ~= slice(part, "=");
  271.     else
  272.       params ~= (part, "");
  273.   }
  274.   void processCookie() {
  275.     if (http_cookie) {
  276.       (string, string)[auto~] cookieparts;
  277.       for auto part <- http_cookie.split(";") {
  278.         auto name = slice(&part, "=");
  279.         cookieparts ~= (strip name, strip part);
  280.       }
  281.       for (string name, string value) <- cookieparts if name == "idmask" {
  282.         bool hit;
  283.         for int i <- 0..params.length if !hit {
  284.           if (params[i][0] == "idmask") {
  285.             params[i][1] = "$value";
  286.             hit = true;
  287.           }
  288.         }
  289.         if !hit params ~= ("idmask", "$value");
  290.       }
  291.     }
  292.   }
  293.   string getParam(string name, deflt = null) {
  294.     for auto par <- params
  295.       if (par[0] == name) return par[1].urldecode();
  296.     return deflt;
  297.   }
  298.   void removeParam(string name) {
  299.     (string, string)[auto~] newparams;
  300.     for auto par <- params
  301.       if (par[0] != name) newparams ~= par;
  302.     params = newparams;
  303.   }
  304.   bool noNyud = getParam("nyud") == "0";
  305.   string urlWithParams((string, string)[] array) {
  306.     string[auto~] res;
  307.     auto handledEntries = new bool[] array.length;
  308.     for auto par <- params {
  309.       bool foundReplacement;
  310.       for (int i, (string, string) tup) <- zip(ints, array) if !foundReplacement {
  311.         if (par[0] == tup[0]) {
  312.           if (tup[1]) res ~= "$(tup[0])=$(tup[1])";
  313.           handledEntries[i] = true;
  314.           foundReplacement = true;
  315.         }
  316.       }
  317.       if !foundReplacement res ~= "$(par[0])=$(par[1])";
  318.     }
  319.     for (int i, bool handled) <- zip(ints, handledEntries)
  320.       if !handled res ~= "$(array[i][0])=$(array[i][1])";
  321.     return "?" ~ join!(string[auto~], string)(res, "&amp;");
  322.   }
  323.   string urlWithParam(string name, value) {
  324.     return urlWithParams [(name, value)];
  325.   }
  326.  
  327.   string authormask, authormask-sql = "and not length(?)";
  328.   if auto author = getParam "author" {
  329.     auto alts = author.split("|");
  330.     int offs;
  331.     authormask = author;
  332.     authormask-sql = "select ? AUTHORMASK where 0";
  333.     for auto alt <- alts {
  334.       authormask-sql ~= " or posts.name = substr(AUTHORMASK, $(offs+1), $(alt.length))";
  335.       offs += alt.length + 1; // for "|"
  336.     }
  337.     authormask-sql = "and exists ($authormask-sql)";
  338.   }
  339.   string threadmask, threadmask-sql = "and not length(?)";
  340.   if auto thread = getParam "thread" {
  341.     threadmask = thread;
  342.     threadmask-sql = "and thread_ids.thread_id = ?";
  343.   }
  344.   string tagmask, tagmask-sql = "and not length(?)";
  345.   if auto tag = getParam "tag" {
  346.     tagmask = tag;
  347.     tagmask-sql = "and tags.tag = ?";
  348.   }
  349.   bool textmask;
  350.   string textmask-sql = "where 1";
  351.   if "yes" == getParam ("noTextPosts", "no") {
  352.     textmask = true;
  353.     textmask-sql = "join image_post on thread_ids.msgid = image_post.msgid where 1";
  354.   }
  355.   string idmask, idmask-sql = "and not length(?)";
  356.   if auto id = getParam "idmask" {
  357.     idmask-sql = "select ? IDMASK where 0";
  358.     while (id.length) {
  359.       if (auto rest = id.startsWith "from_") {
  360.         int from = atoi slice(&rest, "_");
  361.         id = rest;
  362.         auto start = idmask.length;
  363.         idmask ~= "$from";
  364.         auto len = idmask.length - start;
  365.         idmask-sql ~= " or abs(posts.msgid) >= abs(substr(IDMASK, $(start+1), $(len)))";
  366.       } else {
  367.         raise new Error "Invalid id: $id (cookie $http_cookie)";
  368.       }
  369.     }
  370.     idmask-sql = "and exists ($idmask-sql)";
  371.   }
  372.   int count = -1;
  373.   if auto c = getParam "count"
  374.     count = atoi c;
  375.  
  376.   bool showSpoilers;
  377.   if (getParam("spoilers") == "visible") showSpoilers = true;
  378.  
  379.   void writeSiteHeader(bool handleSpoilers, linkToOverview, linkToList, linkToFullView, showRSS = true, floatIt = true, showFilters = true, string additionalText = null) {
  380.     if (floatIt) writeln "<div style=\"background-color: #eef; position: fixed; top: 0px; border: 1px solid; \">";
  381.     else writeln "<div style=\"background-color: #eef; border: 1px solid; \">";
  382.    
  383.     bool firstLine = true;
  384.     int linecount;
  385.     void myWriteLine(string msg, bool sameLine = false) {
  386.       if (firstLine) { firstLine = false; linecount = 1; }
  387.       else if (!sameLine) {
  388.         writeln "<br />";
  389.         linecount ++;
  390.       }
  391.       writeln msg;
  392.     }
  393.     if (linkToOverview) myWriteLine "<a href=\"$(urlWithParam(\"site\", \"main\"))\">Back to overview</a>&nbsp;|&nbsp;";
  394.     if (handleSpoilers) {
  395.       string spoiler-change;
  396.       if showSpoilers {
  397.         spoiler-change = "<a href=\"$(urlWithParam(\"spoilers\", null))\">Hide Spoilers</a>&nbsp;";
  398.       } else {
  399.         spoiler-change = "<a href=\"$(urlWithParam(\"spoilers\", \"visible\"))\">Always Show Spoilers</a>&nbsp;|";
  400.       }
  401.       myWriteLine ("$spoiler-change", true);
  402.     }
  403.     if (linkToList) myWriteLine ("<a href=\"$(urlWithParam(\"site\", \"overview\"))\">Go to post list</a> |", true);
  404.     if (linkToFullView) myWriteLine ("<a href=\"$(urlWithParams [(\"site\", \"full-html\"), (\"count\", \"50\")])\">
  405.      Go to full HTML view</a>&nbsp;|", true);
  406.     if (showRSS) myWriteLine ("<a href=\"$(urlWithParams [(\"site\", \"rss\"), (\"count\", string:null)])\">RSS</a>&nbsp;|", true);
  407.     myWriteLine ("<a href=\"$(urlWithParam(\"noTextPosts\", \"yes\"))\">No Text Posts</a>", true);
  408.     myWriteLine (additionalText, true);
  409.     if (showFilters) {
  410.       if (authormask) {
  411.         myWriteLine "Filter by author: $authormask (<a href=\"$(urlWithParam(\"author\", null))\">remove</a>)";
  412.       }
  413.       if (threadmask) {
  414.         myWriteLine "Filter by thread: $threadmask (<a href=\"$(urlWithParam(\"thread\", null))\">remove</a>)";
  415.       }
  416.       if (tagmask) {
  417.         myWriteLine "Filter by tag: $tagmask (<a href=\"$(urlWithParam(\"tag\", null))\">remove</a>)";
  418.       }
  419.       if (idmask) {
  420.         myWriteLine "Only show posts after $idmask (<a href=\"$(urlWithParam(\"id\", null))\">remove</a>)";
  421.       }
  422.       if (count != -1) {
  423.         myWriteLine "Only show $count posts (<a href=\"$(urlWithParam(\"count\", null))\">remove</a>)";
  424.       }
  425.       if (textmask) {
  426.         myWriteLine "Filter: show only image posts (<a href=\"$(urlWithParam(\"noTextPosts\", null))\">remove</a>)";
  427.       }
  428.     }
  429.     writeln "</div>";
  430.     if floatIt
  431.       for 0..linecount
  432.         writeln "<br style=\"clear: both; \" />";
  433.   }
  434.  
  435.   void writeFooter() {
  436.     writeln "<div style=\"color: #aaa; font-size: 70%; \">disclaimer: all copyrights remain with the respective owners. this site acts merely as a caching layer for mspaforums.com and tgchan.org;
  437.      the creator disclaims all liability for the site itself or the content displayed within. </div>";
  438.   }
  439.  
  440.   auto site = getParam ("site");
  441.   if (!site || site == "main") {
  442.     removeParam("id"); // cleanup
  443.     writeHTMLHeader();
  444.     writeln "<head><title>Thread Overview</title>";
  445.     writeln sharedCSS;
  446.     writeln sharedJS;
  447.     writeln "</head><body>";
  448.     onSuccess {
  449.       p \{
  450.         a.href "http://validator.w3.org/check?uri=referer"
  451.           $img #.src "http://www.w3.org/Icons/valid-xhtml10" #.alt "Valid XHTML 1.0 Transitional" #.height 31 #.width 88;
  452.         a.href "http://jigsaw.w3.org/css-validator/check/referer"
  453.           $img #.style "border:0;width:88px;height:31px" #.src "http://jigsaw.w3.org/css-validator/images/vcss" #.alt "Valid CSS!";
  454.         $img #.src "valid-rss-rogers.png" #.height 31 #.alt "RSS feeds validated";
  455.       }
  456.       writeFooter();
  457.       writeln "</body></html>";
  458.     }
  459.    
  460.     writeln "<div>";
  461.     writeSiteHeader(handleSpoilers => false, linkToOverview => false, linkToList => true, linkToFullView => true, showRSS => eval authormask || threadmask || tagmask);
  462.     writeln "</div>";
  463.    
  464.     writeln "<p>";
  465.     (string, string)[] postParams;
  466.     string getPostParam(string name, deflt = null) {
  467.       for auto par <- postParams
  468.         if (par[0] == name) return par[1];
  469.       return deflt;
  470.     }
  471.     if (getEnvVar "REQUEST_METHOD") == "POST" {
  472.       auto postText = string: join readfile 0;
  473.       // wtf some more
  474.       while (postText.find("&amp;") != -1) {
  475.         postText = postText.replace("&amp;", "&");
  476.       }
  477.       // writeln "Post mode <br />";
  478.       // writeln "$postText";
  479.       for auto part <- postText.split "&" {
  480.         (string name, string value) = slice(part, "=");
  481.         value = urldecode value;
  482.         postParams ~= (name, value);
  483.       }
  484.       auto action = getPostParam("action");
  485.       if (action == "add_tag") {
  486.         string tagname = filterTag getPostParam "tagname";
  487.         if (!tagname.length)
  488.           writeln "Tag invalid! debug $postParams";
  489.         else {
  490.           int threadId = getPostParam "threadId" #.atoi();
  491.          
  492.           db.exec("insert or replace into tags(thread_id, tag) values(?, ?)", threadId, tagname);
  493.         }
  494.       }
  495.       if (action == "rm_tag") {
  496.         string tagname = filterTag getPostParam "tagname";
  497.         if (!tagname.length)
  498.           writeln "Tag invalid! ";
  499.         else {
  500.           int threadId = getPostParam "threadId" #.atoi();
  501.          
  502.           db.exec("delete from tags where thread_id=? and tag=?", threadId, tagname);
  503.         }
  504.       }
  505.       if (action == "add_thread") {
  506.         string url = getPostParam "url";
  507.         string[] tags = getPostParam "tags" #.split "," #.map &strip #.eval[];
  508.         void addThread(string url) {
  509.           db.exec("insert or replace into page_urls (msgid, page_url) values(null, ?)", url);
  510.         }
  511.         void update(string[] urls) {
  512.           join readback("./mspa_update.sh", urls);
  513.         }
  514.         void setTags(string url) {
  515.           eval int id <- db.exec("select distinct thread_id from page_urls left outer join thread_ids where page_url = ? and page_urls.msgid = thread_ids.msgid", url);
  516.           if tags.length && tags[0] {
  517.             db.exec("delete from tags where thread_id = ?", id);
  518.             for auto tag <- tags
  519.               db.exec("insert or replace into tags(thread_id, tag) values(?, ?)", id, tag);
  520.           }
  521.         }
  522.         if (url.find("tgchan.org/wiki/")) {
  523.           auto links = string: url.downloadCached() #.betweens("href=\"", "\"") #.select \(string s) -> s.find "tgchan.org/kusaba/quest/" #.eval;
  524.           for auto link <- links addThread (link);
  525.           update links[];
  526.           for auto link <- links setTags (link);
  527.           writeln "$(links.length) threads added! ";
  528.         } else if (!url.startsWith("http://www.mspaforums.com/showthread.php?") && !url.startsWith "http://tgchan.org/kusaba/") {
  529.           writeln "Can only submit MSPA or tgchan threads/wiki pages! ";
  530.         } else {
  531.           if (auto base = url.between("", "/page")) url = base;
  532.           addThread (url);
  533.           update [url];
  534.           setTags (url);
  535.           writeln "Thread added!";
  536.         }
  537.       }
  538.     }
  539.     writeln "</p>";
  540.    
  541.     form #.action "?site=main" #.method "post" p \{
  542.       text "URL";
  543.       input #.type "text" #.name "url" #.size 40;
  544.       input #.type "submit" #.value "Add Thread";
  545.       br;
  546.       text "Tags";
  547.       input #.type "text" #.name "tags" #.size 28;
  548.       input #.type "hidden" #.name "action" #.value "add_thread";
  549.     }
  550.     p \{
  551.       writeln "Hint: You can add Tags to threads! To do this, use the + symbol next to the threads. Then, by clicking on the tag name, you can select only threads that have the same tag."; br;
  552.       writeln "This is useful if you want to follow a quest that spans multiple threads."; br;
  553.     }
  554.    
  555.     writeln "<table class=\"boxed\">";
  556.     onSuccess writeln "</table>";
  557.     tr \{
  558.       th #.class "first" "Thread";
  559.       th #.class "mid" "Starter";
  560.       th #.class "mid" "Posts";
  561.       th #.class "mid" "First post date";
  562.       th #.class "mid" "Last post date";
  563.       th #.class "last" "Tags";
  564.     }
  565.    
  566.     auto evenodd_iter = loop ["even", "odd"];
  567.     int formid;
  568.    
  569.     if (getParam("hax") == "yes") {
  570.       for (int msgid, ubyte[] data) <- db.exec "select msgid, content from content" {
  571.         auto pureText = removeSpoilers string: inflate data;
  572.         bool hasVisiblePics = eval pureText.find("img src=\"http") != -1;
  573.         if (hasVisiblePics) {
  574.           db.exec("insert or replace into image_post (msgid) values(?)", msgid);
  575.         } else {
  576.           db.exec("insert or replace into text_post (msgid) values(?)", msgid);
  577.         }
  578.       }
  579.       return;
  580.     }
  581.    
  582.     for (string thread, int thread_id, string threadname) <- db.exec ("
  583.      select page_url, A.thread_id, titles.title from (
  584.        select thread_ids.thread_id, thread_ids.msgid from thread_ids
  585.        left outer join tags on thread_ids.thread_id = tags.thread_id
  586.        $textmask-sql $tagmask-sql $threadmask-sql
  587.        group by thread_ids.thread_id
  588.      ) A
  589.      join page_urls B on A.msgid=B.msgid
  590.      join posts on A.msgid=posts.msgid
  591.      left outer join titles on A.thread_id=titles.thread_id
  592.      order by A.thread_id desc", tagmask, threadmask)
  593.     {
  594.       (thread, string bogus) = slice(thread, "/page");
  595.      
  596.       if (!threadname)
  597.         threadname = getThreadName thread;
  598.      
  599.       db.openStatementList();
  600.       eval (string threadStarter, int threadStarterId) <- db.exec("
  601.        select name, A.msgid from thread_ids A
  602.          join postnums B on A.msgid = B.msgid
  603.          join posts C on A.msgid = C.msgid
  604.          where A.thread_id=? and B.postnr=1", thread_id);
  605.      
  606.       if (!authormask || authormask == threadStarter) {
  607.         eval int posts <- db.exec("select count(*) from thread_ids join posts on thread_ids.msgid = posts.msgid $textmask-sql and thread_id=? $authormask-sql", thread_id, authormask);
  608.        
  609.         eval int maxPostNum <- db.exec("
  610.          select max(postnr) from thread_ids A
  611.            join posts on A.msgid = posts.msgid
  612.            join postnums B on A.msgid = B.msgid
  613.            where thread_id=? $authormask-sql", thread_id, authormask);
  614.         eval string firstDate <- db.exec ("select date from posts where msgid=? $authormask-sql", threadStarterId, authormask);
  615.         eval (string lastDate, int lastDateId) <- db.exec("
  616.          select posts.date, posts.msgid from thread_ids A
  617.          join postnums B on A.msgid = B.msgid
  618.          join posts on A.msgid = posts.msgid
  619.          where A.thread_id=? and B.postnr=? $authormask-sql", thread_id, maxPostNum, authormask);
  620.        
  621.         string[auto~] tags;
  622.         for string tag <- db.exec("select tag from tags where thread_id = ?", thread_id)
  623.           tags ~= tag.dup;
  624.         string genTag(string tag) {
  625.           string res = "<div class=\"inline box\"><a href=\"$(urlWithParam(\"tag\", \"$tag\"))\">$tag</a></div>";
  626.           /*string minus;
  627.           using scoped outputfn = \(string s) minus ~= s; {
  628.             b "-";
  629.             div #.class "hidden box inline" \{
  630.               form #.id "form$formid" #.method "post" #.action "#" \{
  631.                 input #.type "hidden" #.name "threadId" #.value "$thread_id");
  632.                 input #.type "hidden" #.name "tagname" #.value "$tag";
  633.                 input #.type "hidden" #.name "action" #.value "rm_tag";
  634.                 a #.href "javascript:submitForm('form$formid');" "Remove Tag";
  635.               }
  636.             }
  637.           }
  638.           res = "$res<div class=\"minustag inline box\" style=\"vertical-align: super; \">$minus</div>";*/
  639.           formid ++;
  640.           return res;
  641.         }
  642.         string tagstr = [for str <- tags extra &genTag: extra(str)].join ", ";
  643.        
  644.         string firstDateText = "$firstDate";
  645.         string lastDateText = "$lastDate";
  646.         if (thread_id >= 0) {
  647.           firstDateText = "<a href=\"http://www.mspaforums.com/showthread.php?$thread_id\">$firstDate</a>";
  648.           lastDateText = "<a href=\"http://www.mspaforums.com/showthread.php?$thread_id&amp;p=$lastDateId&amp;viewfull=1#post$lastDateId\">$lastDate</a>";
  649.         }
  650.        
  651.         eval string evenodd <- evenodd_iter;
  652.         // writeln "TEST <<$evenodd>>";
  653.         tr .class evenodd \{
  654.           td .class "first" a .href urlWithParam("thread", "$thread_id") threadname;
  655.           td .class "mid"   a .href urlWithParam("author", threadStarter) threadStarter;
  656.           td .class "mid"   "$posts";
  657.           td .class "mid"   firstDateText;
  658.           td .class "mid"   lastDateText;
  659.           td .class "last"  \{
  660.             text tagstr;
  661.             div .class "plustag inline box" \{
  662.               text "+";
  663.               div #.class "hidden box inline " #.style "position: absolute; " \{
  664.                 form #.id "form$formid" #.method "post" #.action "#" \{
  665.                   input #.type "text" #.name "tagname";
  666.                   input #.type "hidden" #.name "threadId" #.value "$thread_id";
  667.                   input #.type "hidden" #.name "action" #.value "add_tag";
  668.                   a #.href "javascript:submitForm('form$formid');" "Add Tag";
  669.                 }
  670.               }
  671.             }
  672.           }
  673.           formid ++;
  674.         }
  675.       }
  676.       db.finStatementList();
  677.     }
  678.     return;
  679.   }
  680.  
  681.   if (site == "overview") {
  682.     writeHTMLHeader(doctype => false);
  683.     std.cgi.head \{
  684.       title "Overview";
  685.       text sharedCSS;
  686.       text sharedJS;
  687.     }
  688.     writeln "<body>";
  689.     onSuccess {
  690.       writeFooter();
  691.       writeln "</body></html>";
  692.     }
  693.     writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => false, linkToFullView => true, floatIt => false);
  694.    
  695.     {
  696.       writeln "<table class=\"boxed\">";
  697.       tr \{
  698.         th.class "first" "Thread";
  699.         th.class "mid"   "Name";
  700.         th.class "mid"   "Date";
  701.         th.class "last"  "Post ID";
  702.       }
  703.      
  704.       auto evenodd_iter = loop ["even", "odd"];
  705.      
  706.       int iframe_id;
  707.       void writeRowFor(string page_url, int next_msg_id, msg_id, prev_msg_id, string name, string date, bool showSpoilers) {
  708.         eval string evenodd <- evenodd_iter;
  709.         string threadname = getThreadName page_url;
  710.         string spoilerpar;
  711.         if (showSpoilers) spoilerpar = "&amp;spoilers";
  712.         string next_post_link = "?site=rss&amp;id=$next_msg_id$spoilerpar";
  713.         string post_link = "?site=rss&amp;id=$msg_id$spoilerpar";
  714.         string prev_post_link = "?site=rss&amp;id=$prev_msg_id$spoilerpar";
  715.         tr.class evenodd \{
  716.           td.class "first" threadname;
  717.           td.class "mid"   a.href urlWithParam("author", name) name;
  718.           td.class "mid"   date;
  719.           td.class "last" \{
  720.             a.href "javascript: loadFrame('mewframe$iframe_id', 'mewdiv$iframe_id', '$post_link'); " "$msg_id";
  721.             div #.class "mewdiv" #.id "mewdiv$iframe_id" \{
  722.               table.class "mewtable boxed" \{
  723.                 tr.style "height: 20px; " \{
  724.                   td.onclick "javascript:loadFrame('mewframe$(iframe_id-1)', 'mewdiv$(iframe_id-1)', '$prev_post_link');" "&lt; Back";
  725.                   td.onclick "javascript:loadFrame('mewframe$(iframe_id+1)', 'mewdiv$(iframe_id+1)', '$next_post_link');" "Forth &gt;";
  726.                 }
  727.                 tr td.colspan 2 $ iframe #.class "mewframe" #.id "mewframe$iframe_id" #.src "" #.scrolling "auto" #.frameborder 0 "mew!";
  728.               }
  729.             }
  730.           }
  731.         }
  732.         writeln("");
  733.         iframe_id ++;
  734.       }
  735.       {
  736.         auto first = cat([true], loop [false]);
  737.         string page_url;
  738.         int msg_id = -1, prev_msg_id = -1;
  739.         string name, date;
  740.         bool aborted;
  741.         alias limit = 64;
  742.         string sqltext = "
  743.          select page_urls.page_url, posts.msgid, name, date from posts
  744.          join thread_ids on posts.msgid = thread_ids.msgid
  745.          join page_urls on posts.msgid = page_urls.msgid
  746.          join (
  747.            select thread_ids.thread_id, thread_ids.msgid from thread_ids
  748.            left outer join tags on thread_ids.thread_id = tags.thread_id
  749.            where 1 $tagmask-sql $threadmask-sql
  750.            group by thread_ids.thread_id
  751.          ) B on thread_ids.thread_id = B.thread_id
  752.          join date_codes on posts.msgid = date_codes.msgid
  753.          $textmask-sql $authormask-sql
  754.          order by date_codes.datecode asc";
  755.         for (int i, (string next_page_url, int next_msg_id, string next_name, string next_date)) <- zip(0..limit, db.exec!FOURSTRING!(string, int, string, string)(sqltext, tagmask, threadmask, authormask))
  756.         {
  757.           eval bool b <- first;
  758.           if !b {
  759.             writeRowFor (page_url, next_msg_id, msg_id, prev_msg_id, name, date, showSpoilers);
  760.             prev_msg_id = msg_id;
  761.           }
  762.           (page_url, msg_id, name, date) = (next_page_url.dup, next_msg_id, next_name.dup, next_date.dup);
  763.           if (i == limit - 1) aborted = true;
  764.         }
  765.         if (aborted) {
  766.           writeln "</table>";
  767.           writeln "<br />";
  768.           writeln "Rest omitted due to sanity checking. ";
  769.         } else {
  770.           writeRowFor (page_url, -1, msg_id, prev_msg_id, name, date, showSpoilers);
  771.           writeln "</table>";
  772.         }
  773.         // writeln ">> $sqltext";
  774.       }
  775.     }
  776.     return;
  777.   }
  778.  
  779.   alias base_url = "http://demented.no-ip.org/~feep/threadfeed.cgi";
  780.  
  781.   if (site == "rss") {
  782.     processCookie();
  783.     if auto id = getParam "id" {
  784.       bool spoilers = eval getParam ("spoilers");
  785.       writeHTMLHeader();
  786.       writeln "<head><title>Post view</title>";
  787.       writeln sharedCSS;
  788.       writeln sharedJS;
  789.       writeln "</head><body>";
  790.       onSuccess {
  791.         writeFooter();
  792.         writeln "</body></html>";
  793.       }
  794.       auto msgid = id.atoi;
  795.       eval int thread_id <- db.exec("select thread_id from thread_ids where msgid = ?", msgid);
  796.       string backlink = "|&nbsp;<a href=\"http://www.mspaforums.com/showthread.php?$thread_id&p=$msgid&viewfull=1#post$msgid\">MSPA forum link</a>";
  797.       writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => false, linkToFullView => false,
  798.                       showRSS => false, floatIt => false, showFilters => false, additionalText => backlink);
  799.       for ubyte[] data <- db.exec("select content from content where msgid = ?", msgid)
  800.       {
  801.         string idText = urlWithParam("id", "");
  802.         string content = reformatPost (string: inflate data, spoilers, idText, linkToAnchors => false, noNyud => noNyud);
  803.         writeln "$content";
  804.       }
  805.       return;
  806.     }
  807.     string sqlcode = "
  808.      select posts.msgid, postnr, A.thread_id, name, date, content from
  809.        (
  810.          select thread_ids.thread_id, msgid from thread_ids
  811.          left outer join tags on thread_ids.thread_id = tags.thread_id
  812.          where 1 $tagmask-sql $threadmask-sql
  813.          group by thread_ids.thread_id
  814.        ) A
  815.        join thread_ids on A.thread_id = thread_ids.thread_id
  816.        join posts on posts.msgid = thread_ids.msgid
  817.        join postnums C on C.msgid = thread_ids.msgid
  818.        join content D on D.msgid = thread_ids.msgid
  819.        join date_codes on date_codes.msgid = thread_ids.msgid
  820.        $textmask-sql $authormask-sql $idmask-sql
  821.        order by date_codes.datecode asc";
  822.     alias Tup = (int, int, int, string, string, string);
  823.     auto qit = db.exec!FIVESTRING!Tup(sqlcode, tagmask, threadmask, authormask, idmask);
  824.    
  825.     int lastMsgId;
  826.     for (int msgId, int postId, int threadId, string name, string date, string content) <- qit {
  827.       lastMsgId = msgId;
  828.     }
  829.     writeln "Content-type: application/rss+xml";
  830.     if (lastMsgId != 0)
  831.       writeln "Set-Cookie: idmask=from_$lastMsgId";
  832.     writeln "";
  833.     writeln "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>";
  834.     writeln "<rss version=\"2.0\" xmlns:atom=\"http://www.w3.org/2005/Atom\">";
  835.     onSuccess writeln "</rss>";
  836.     writeln "<channel>";
  837.     onSuccess writeln "</channel>";
  838.     writeln "<title>Thread Feed</title>";
  839.     writeln "<link>$base_url</link>";
  840.    
  841.     writeln "<description>RSS feed for MSPA and tgchan forums</description>";
  842.     // writeln "<atom:link href=\"$base_url?$query_string\" rel=\"self\" type=\"application/rss+xml\" />";
  843.    
  844.     qit = db.exec!FIVESTRING!Tup(sqlcode, tagmask, threadmask, authormask, idmask);
  845.     for (int msgId, int postId, int threadId, string name, string date, string content) <- qit
  846.     {
  847.       // parse the date
  848.       date.cleanupDate();
  849.       writeln "<item>";
  850.       onSuccess writeln "</item>";
  851.       writeln "<title>$name</title>";
  852.       writeln "<pubDate>$date</pubDate>";
  853.       writeln "<guid isPermaLink=\"false\">mspa_forum_post_id_on_feeps_rss_$msgId</guid>";
  854.       writeln "<link>$base_url$(urlWithParam(\"id\", \"\\$msgId\"))</link>"; // .replace("&amp;", "&"); /* not necessary for rss */
  855.       // writeln "<link>$base_url?id=$msgId</link>";
  856.     }
  857.     return;
  858.   }
  859.   if (site == "full-html") {
  860.     string sqlcode = "
  861.      select content, name, date, posts.msgid, thread_ids.thread_id from
  862.      (
  863.        select thread_ids.thread_id, msgid from thread_ids
  864.        left outer join tags on thread_ids.thread_id = tags.thread_id
  865.        where 1 $tagmask-sql $threadmask-sql
  866.        group by thread_ids.thread_id
  867.      ) A
  868.      join thread_ids on A.thread_id = thread_ids.thread_id
  869.      join posts on thread_ids.msgid = posts.msgid
  870.      join content B on posts.msgid = B.msgid
  871.      join date_codes on posts.msgid = date_codes.msgid
  872.      $textmask-sql $authormask-sql $idmask-sql
  873.      order by date_codes.datecode asc";
  874.     auto qit = db.exec!FIVESTRING!(ubyte[], string, string, int, int)(sqlcode, tagmask, threadmask, authormask, idmask);
  875.     if (getParam("funtimemode") == "yes") {
  876.       writeln "Content-type: text/html";
  877.       writeln "";
  878.       for (ubyte[] data, string name, string date, int postId, int threadId) <- qit {
  879.         auto text = string: inflate data #.dup;
  880.         // writeln "[$name]";
  881.         /*char[auto~] newtext;
  882.         while (text.length) {
  883.           void flush(int to) { newtext ~= text[0 .. to]; }
  884.           auto bpos = text.find("<b>");
  885.           auto bcpos = text.find("</b>");
  886.           if (bpos != -1 && bpos < bcpos) {
  887.             text = text[bpos + 3 .. $];
  888.           }
  889.           else if (bcpos != -1 && bcpos < bpos) {
  890.             flush bcpos;
  891.             text = text[bcpos + 4 .. $];
  892.           } else text = new char[] 0;
  893.         }
  894.         text = newtext[];*/
  895.         writeln string:text;
  896.       }
  897.       return;
  898.     }
  899.     writeHTMLHeader();
  900.     writeln "<head><title>Full HTML View</title>";
  901.     writeln sharedCSS;
  902.     writeln sharedJS;
  903.     writeln "</head><body>";
  904.     // writeln ">> $sqlcode";
  905.     onSuccess {
  906.       writeFooter();
  907.       writeln "</body></html>";
  908.     }
  909.     writeSiteHeader(handleSpoilers => true, linkToOverview => true, linkToList => true, linkToFullView => false, floatIt => false);
  910.     if (!authormask && !tagmask && !threadmask) {
  911.       writeln "Uh-oh! You loaded the Full-HTML view but no author, thread or tag mask is set! <br />
  912.      This means that I should by all rights dump my entire post database at your feet now. <br />
  913.      Since I'm a nice person and don't want to crash your system, I'm not going to do that. <br />
  914.      You should go back. There is nothing for you here. ";
  915.       return;
  916.     }
  917.     void loopBody(ubyte[] data, string name, date, int postId, threadId) {
  918.       string idText = urlWithParam("id", "");
  919.       string content = reformatPost (string: inflate data #.dup, showSpoilers, idText, linkToAnchors => true, noNyud => noNyud);
  920.       a.name "post$postId" "";
  921.       div.class "box_outer" \{
  922.         string backlink = "http://www.mspaforums.com/showthread.php?$threadId&amp;p=$postId&amp;viewfull=1#post$postId";
  923.         div.class "box_inner" \{
  924.           b "Name: ";
  925.           text name;
  926.           text "&nbsp;&nbsp;";
  927.           b "Date: ";
  928.           text date;
  929.           text "&nbsp;&nbsp;";
  930.           b \{ if (postId >= 0) a.href backlink "MSPA link"; } // otherwise non-mspa
  931.           a #.style "text-decoration: none; color: #cdc; " #.href urlWithParam("idmask", "from_$postId") "▼";
  932.         }
  933.         text content;
  934.       }
  935.     }
  936.     if (count == -1) {
  937.       for (ubyte[] data, string name, string date, int postId, int threadId) <- qit {
  938.         loopBody(data, name, date, postId, threadId);
  939.       }
  940.     } else {
  941.       int lastPostId = 0;
  942.       for (int k, (ubyte[] data, string name, string date, int postId, int threadId)) <- zip(0..count + 1, qit) {
  943.         if (k != count) loopBody(data, name, date, postId, threadId);
  944.         else lastPostId = postId;
  945.       }
  946.       if (lastPostId != 0) {
  947.         int left = 1;
  948.         int[auto~] restIds;
  949.         for (ubyte[] data, string name, string date, int postId, int threadId) <- qit { restIds ~= postId; }
  950.         left += restIds.length;
  951.         string extralink;
  952.         a .href urlWithParam("idmask", "from_$lastPostId") "Next";
  953.         if (restIds.length) {
  954.           int lastpage;
  955.           if (count < restIds.length) lastpage = restIds[$-count+1];
  956.           else lastpage = restIds[0];
  957.           text "($left posts left)";
  958.           a.href urlWithParam("idmask", "from_$lastpage") "Last Page";
  959.         }
  960.         br;
  961.       }
  962.     }
  963.     return;
  964.   }
  965. }
Advertisement
Add Comment
Please, Sign In to add comment