From 9bd4845ad952c7c3d7ee72d2f8fe577d9d397204 Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Mon, 5 Mar 2018 22:42:48 +0000
Subject: [PATCH] Fix stack trace mangling bug! :D

---
 Actions/Report.php                     | 49 ++++++++++++++++----------
 utilities/SimpleXmlElementExtended.php | 26 ++++++++++++++
 utilities/transform.php                |  7 ++--
 3 files changed, 60 insertions(+), 22 deletions(-)
 create mode 100644 utilities/SimpleXmlElementExtended.php

diff --git a/Actions/Report.php b/Actions/Report.php
index 9a0989c..5d09b85 100644
--- a/Actions/Report.php
+++ b/Actions/Report.php
@@ -2,6 +2,8 @@
 
 namespace Sandpiper\Actions;
 
+use \DOMDocument; use \DOMElement; use \DOMCDATASection;
+
 use \Yosymfony\Toml\Toml;
 
 use \SBRL\Utilities\SimpleXmlWriter;
@@ -11,7 +13,7 @@ class Report extends \Sandpiper\AbstractAction
 	
 	public function __construct()
 	{
-		
+		libxml_disable_entity_loader(false);
 	}
 	
 	public function handle() {
@@ -30,7 +32,6 @@ class Report extends \Sandpiper\AbstractAction
 		
 		$place = null;
 		foreach($settings->get("places") as $current_place) {
-			var_dump($current_place);
 			if($current_place["key"] == $this->param_get("place_id", null)) {
 				$place = $current_place;
 				break;
@@ -44,7 +45,7 @@ class Report extends \Sandpiper\AbstractAction
 		$new_report->version = escape4xml($this->param_get("version", "?"));
 		$new_report->summary = escape4xml($this->param_get("summary", "(Unknown error)"));
 		$new_report->details = escape4xml($this->param_get("details", ""));
-		$new_report->stack_trace = escape4xml($this->get_post_body());
+		$new_report->stack_trace = $this->get_post_body();
 
 		$report_dir = ROOT_DIR . "/{$settings->get("data_dir")}/places/" . slugify($place["name"]);
 		$report_filename = "$report_dir/" . slugify($new_report->summary) . ".xml";
@@ -67,19 +68,27 @@ class Report extends \Sandpiper\AbstractAction
 			$writer->close();
 			file_put_contents($report_filename, $writer->render());
 		}
+		
+		// We have to use a DOMDocument here because SimpleXML mangled the 
+		// whitespace in the stack traces
+		$report_xml = new DOMDocument("1.0", "UTF-8");
+		$report_xml->preserveWhiteSpace = false;
+		$report_xml->formatOutput = true;
+		if(!$report_xml->loadXML(file_get_contents($report_filename), LIBXML_NONET))
+			exit("Error: Invalid XML generated when creating a new report file.\n");
+		
+		$report_node = new DOMElement("report");
+		$report_xml->getElementsByTagName("reports")->item(0)->appendChild($report_node);
+		
+		$report_node->appendChild(new DOMElement("timestamp", date(DATE_ATOM)));
+		$report_node->appendChild(new DOMElement("version", $new_report->version));
+		$report_node->appendChild(new DOMElement("details", $new_report->details));
+		$stack_trace_node = new DOMElement("stack_trace");
+		$report_node->appendChild($stack_trace_node);
+		$stack_trace_node->appendChild($report_xml->createCDATASection($new_report->stack_trace));
 
-		$report_xml = simplexml_load_file($report_filename);
-
-		if($report_xml === false)
-			exit("Error: invalid XML generated when creating a new report file.\n");
-
-		$report_node = $report_xml->reports->addChild("report");
-		$report_node->addChild("timestamp", date(DATE_ATOM));
-		$report_node->addChild("version", $new_report->version);
-		$report_node->addChild("details", $new_report->details);
-		$report_node->addChild("stack_trace", $new_report->stack_trace);
-
-		file_put_contents($report_filename, $report_xml->asXML());
+		// NOTE: When uploading with curl to test this, make sure to use --data-binary and not simply -d, as this mangles the newline characters.
+		file_put_contents($report_filename, $report_xml->saveXML());
 
 		// Update the place index
 		$place_index_filename = "$report_dir/index.xml";
@@ -88,13 +97,13 @@ class Report extends \Sandpiper\AbstractAction
 			$writer = new SimpleXmlWriter();
 			$writer->prettyprint = true;
 
-			$writer->add_xslt(ROOT_DIR . "/theme/place_index.xslt");
+			$writer->add_xslt("/theme/place_index.xslt");
 			$writer->open("errors");
 			$writer->close();
 			file_put_contents($place_index_filename, $writer->render());
 		}
 		$index_xml = simplexml_load_file($place_index_filename);
-
+		
 		$index_entry = null;
 		foreach($index_xml as $entry) {
 			if($entry->summary->__toString() != $new_report->summary)
@@ -112,8 +121,10 @@ class Report extends \Sandpiper\AbstractAction
 
 		$index_entry->report_count = intval($index_entry->report_count) + 1;
 		
-		file_put_contents($place_index_filename, $index_xml->asXML());
-		
+		// This is fine because we don't have to worry about the whitespace in the
+		// stack traces here
+		file_put_contents($place_index_filename, simplexml_asxml_pretty($index_xml));
+        
 		http_response_code(200);
 		exit("Report submitted successfully!\n");
 	}
diff --git a/utilities/SimpleXmlElementExtended.php b/utilities/SimpleXmlElementExtended.php
new file mode 100644
index 0000000..a44f910
--- /dev/null
+++ b/utilities/SimpleXmlElementExtended.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace External\Utilities;
+
+/**
+ * From https://stackoverflow.com/a/20511976/1460422
+ */
+class SimpleXMLElementExtended extends SimpleXMLElement {
+
+  /**
+   * Adds a child with $value inside CDATA
+   * @param string $name
+   * @param string $value
+   */
+  public function addChildWithCDATA($name, $value = NULL) {
+    $new_child = $this->addChild($name);
+
+    if ($new_child !== NULL) {
+      $node = dom_import_simplexml($new_child);
+      $no   = $node->ownerDocument;
+      $node->appendChild($no->createCDATASection($value));
+    }
+
+    return $new_child;
+  }
+}
diff --git a/utilities/transform.php b/utilities/transform.php
index df4a4e5..cb5ec3e 100644
--- a/utilities/transform.php
+++ b/utilities/transform.php
@@ -8,7 +8,6 @@
  * @return	string				The slugified string.
  */
 function slugify($string, $maxlength = 50) {
-	var_dump($string);
 	return substr(preg_replace([
 			'/\s+/',
 			'/[^a-z0-9\-_]/'
@@ -35,8 +34,10 @@ function escape4xml($string) {
  * @return	string				The pretty-printed XML representation of the provided SimpleXML node.
  */
 function simplexml_asxml_pretty($simplexml_node) {
-	$dom = dom_import_simplexml($simplexml_node);
-	$dom->preserveWhitespace = false;
+	libxml_disable_entity_loader(false);
+	$dom = new DomDocument;
+	$dom->preserveWhiteSpace = false;
 	$dom->formatOutput = true;
+	$dom->loadXML($simplexml_node->asXML(), LIBXML_NONET);
 	return $dom->saveXML();
 }
