changeset 134:b6b4a58c7625

Using .inc.php rather than just .inc for include files.
author Tom Fredrik Blenning <bfg@bfgconsult.no>
date Sun, 22 Jan 2023 19:22:00 +0100
parents 00255ca89459
children 2fe6713ccd64
files CacheTimeCheck.inc CacheTimeCheck.inc.php FileCacheSet.inc FileCacheSet.inc.php Flag.inc Flag.inc.php Http.inc Http.inc.php InputParser.inc InputParser.inc.php Language.inc Language.inc.php Logger.inc Logger.inc.php OnlineBufferValidator.inc OnlineBufferValidator.inc.php OnlineURIValidator.inc OnlineURIValidator.inc.php OnlineValidator.inc OnlineValidator.inc.php Options.inc Options.inc.php Page.inc Page.inc.php ScriptIncludeCache.inc ScriptIncludeCache.inc.php Sitemap.inc Sitemap.inc.php StatusCodes.inc StatusCodes.inc.php Validator.inc Validator.inc.php common-functions.inc common-functions.inc.php constants.inc constants.inc.php filters.inc filters.inc.php flag.php index.php sitemap.php
diffstat 41 files changed, 2490 insertions(+), 2490 deletions(-) [+]
line wrap: on
line diff
--- a/CacheTimeCheck.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,86 +0,0 @@
-<?php
-$baseDir = dirname(__FILE__);
-
-include_once 'common-functions.inc';
-include_once 'FileCacheSet.inc';
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->cache_time("${baseDir}/FileCacheSet.inc");
-
-function toGMTime($time) {
-  return gmdate('D, d M Y H:i:s', $time) . ' GMT';
-}
-
-/**
- * Checks if a HTTP_IF_MODIFIED_SINCE header is set, if this file is
- * newer, set a Last-Modified header, otherwise abort with an 304
- * status code
- */
-function CheckHttpModified($cache)
-{
-  if (DEBUG_LEVEL >= VERBOSITY_DEBUG)
-    var_dump($_SERVER);
-
-  $gmdate_mod = toGMTime($cache->getNewest());
-
-  if (array_key_exists('REDIRECT_URL', $_SERVER) && $_SERVER['REDIRECT_URL'] == '/sitemap.xml') {
-    //print_r($gmdate_mod);
-  }
-
-  foreach($cache->cacheControl() as $header => $value) {
-    header ("${header}: ${value}");
-  }
-  if(array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
-    $HTTP_IF_MODIFIED_SINCE = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
-    $if_modified_since = preg_replace('/;.*$/', '', $HTTP_IF_MODIFIED_SINCE);
-
-    if (strtotime($if_modified_since) >= $cache->getNewest()) {
-      header("HTTP/1.0 304 Not Modified");
-      return false;
-    }
-  }
-
-  foreach($cache->cacheControl() as $header => $value) {
-    header ("${header}: ${value}");
-  }
-  header("Last-Modified: $gmdate_mod");
-  return true;
-}
-
-
-
-/**
- * CacheTimeCheck provides a set of functions to enable generating a
- * correct time for the latest update for a given file.
- *
- * @author Tom Fredrik Blenning Klaussen
- */
-class CacheTimeCheck extends FileCacheSet
-{
-  function __construct($filename = False)
-  {
-    parent::__construct();
-    if ($filename)
-      $this->cache_time($filename);
-    $this->cache_time(__FILE__);
-    $this->max_age=0;
-  }
-
-  public function addParent($cache)
-  {
-    parent::addParent($cache);
-  }
-
-  /**
-   * Convenience function to load a file, and add it to the cacheset
-   *
-   * @param $path path of the file
-   * @return the contents of the file
-   */
-  function loadFile($path)
-  {
-    $this->cache_time($path);
-    return loadFile($path);
-  }
-
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/CacheTimeCheck.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,86 @@
+<?php
+$baseDir = dirname(__FILE__);
+
+include_once 'common-functions.inc.php';
+include_once 'FileCacheSet.inc.php';
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->cache_time("${baseDir}/FileCacheSet.inc.php");
+
+function toGMTime($time) {
+  return gmdate('D, d M Y H:i:s', $time) . ' GMT';
+}
+
+/**
+ * Checks if a HTTP_IF_MODIFIED_SINCE header is set, if this file is
+ * newer, set a Last-Modified header, otherwise abort with an 304
+ * status code
+ */
+function CheckHttpModified($cache)
+{
+  if (DEBUG_LEVEL >= VERBOSITY_DEBUG)
+    var_dump($_SERVER);
+
+  $gmdate_mod = toGMTime($cache->getNewest());
+
+  if (array_key_exists('REDIRECT_URL', $_SERVER) && $_SERVER['REDIRECT_URL'] == '/sitemap.xml') {
+    //print_r($gmdate_mod);
+  }
+
+  foreach($cache->cacheControl() as $header => $value) {
+    header ("${header}: ${value}");
+  }
+  if(array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) {
+    $HTTP_IF_MODIFIED_SINCE = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+    $if_modified_since = preg_replace('/;.*$/', '', $HTTP_IF_MODIFIED_SINCE);
+
+    if (strtotime($if_modified_since) >= $cache->getNewest()) {
+      header("HTTP/1.0 304 Not Modified");
+      return false;
+    }
+  }
+
+  foreach($cache->cacheControl() as $header => $value) {
+    header ("${header}: ${value}");
+  }
+  header("Last-Modified: $gmdate_mod");
+  return true;
+}
+
+
+
+/**
+ * CacheTimeCheck provides a set of functions to enable generating a
+ * correct time for the latest update for a given file.
+ *
+ * @author Tom Fredrik Blenning Klaussen
+ */
+class CacheTimeCheck extends FileCacheSet
+{
+  function __construct($filename = False)
+  {
+    parent::__construct();
+    if ($filename)
+      $this->cache_time($filename);
+    $this->cache_time(__FILE__);
+    $this->max_age=0;
+  }
+
+  public function addParent($cache)
+  {
+    parent::addParent($cache);
+  }
+
+  /**
+   * Convenience function to load a file, and add it to the cacheset
+   *
+   * @param $path path of the file
+   * @return the contents of the file
+   */
+  function loadFile($path)
+  {
+    $this->cache_time($path);
+    return loadFile($path);
+  }
+
+}
+?>
--- a/FileCacheSet.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-<?php
-$baseDir = dirname(__FILE__);
-
-include_once 'Logger.inc';
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->cache_time("${baseDir}/Logger.inc");
-
-class Cache {
-  private $max_age=0;
-  function setMaxAge($seconds)
-  {
-    $this->max_age=$seconds;
-  }
-
-  function cacheControl() {
-    if ($this->max_age > 0) {
-      return array("expires" => toGMTime(time()+$this->max_age), "cache-control" => "public, max-age=$this->max_age");
-    }
-    else {
-      return array("expires" => 0, "cache-control" => "public, max-age=0, no-cache");
-    }
-  }
-
-};
-
-/**
- * Caches a set of file with timestamps
- */
-class FileCacheSet extends Cache {
-  private $newest = 0;
-  private $files = array();
-  private $parentCaches = array();
-
-  /**
-   * Constructs a FileCacheSet object
-   *
-   * @param $parent optional linked parent cache to sync with
-   */
-  protected function __construct($parent = null) {
-    if ($parent) {
-      $this->addParent($parent);
-    }
-  }
-
-  /**
-   * Links up a parent cache
-   *
-   * @param $parent cache we should sync with
-   */
-  protected function addParent($parent)
-  {
-    array_push($this->parentCaches, $parent);
-  }
-
-  /**
-   * List a set of files which contributes to this pages cacheset.
-   *
-   * @param $humanReadable If the timestamp should be human readable.
-   *
-   * @return an associative array of file, and timestamp
-   */
-  function cacheSet($humanReadable = False)
-  {
-    $retVal = array();
-    foreach ($this->parentCaches as $parent) {
-      $retVal = array_merge($retVal, $parent->cacheSet($humanReadable));
-    }
-    foreach($this->files as $file) {
-      $mtime = filemtime($file);
-      if ($humanReadable)
-	$mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
-
-      $retVal[$file] = $mtime;
-    }
-    return $retVal;
-  }
-
-  /**
-   * Include a file in the cacheset
-   *
-   * @param $path the path of the file
-   */
-  function cache_time($path)
-  {
-    if (!file_exists($path)) {
-      Logger::warn("${path} does not exist");
-      errorPage("Resource is not available", 404);
-    }
-
-    array_push($this->files, $path);
-    $mtime = filemtime($path);
-    if ($mtime > $this->newest) {
-      $this->newest = $mtime;
-    }
-  }
-
-  /**
-   * Find the newest member of the combined cacheset
-   *
-   * @return timestamp of newest member
-   */
-  function getNewest()
-  {
-    $newest = 0;
-    foreach ($this->parentCaches as $parent) {
-      $newest = max($newest, $parent->getNewest());
-    }
-    $newest = max($newest, $this->newest);
-    return $newest;
-  }
-}
-
-/**
- * Singleton class, keeps track of all scriptfile includes
- *
- * This class is typically used as a parent class of another cache
- */
-class ScriptIncludeCache extends FileCacheSet
-{
-  private static $myInstance = 0;
-
-  protected function __construct($filename = False)
-  {
-    parent::__construct();
-    if ($filename)
-      $this->cache_time($filename);
-    $this->cache_time(__FILE__);
-  }
-
-  /**
-   * Generates a singleton instance of this CacheTimeCheck
-   *
-   * @param $filename an optional file to include in the cacheset
-   *
-   * @return a CacheTimeCheck object
-   */
-  public static function instance($filename = False)
-  {
-    if (! self::$myInstance)
-      self::$myInstance = new self($filename);
-    elseif ($filename)
-      self::$myInstance->cache_time($filename);
-    return self::$myInstance;
-  }
-
-  /**
-   * Convenience function to include a file, and add it to the cacheset.
-   *
-   * @param $path path of the file
-   * @param $basedir a directory to prepend to the path
-   */
-  function includeOnce($path, $basedir = false)
-  {
-    if ($basedir)
-      $path = $basedir . "/" . $path;
-    $this->cache_time($path);
-    include_once($path);
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileCacheSet.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,160 @@
+<?php
+$baseDir = dirname(__FILE__);
+
+include_once 'Logger.inc.php';
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->cache_time("${baseDir}/Logger.inc.php");
+
+class Cache {
+  private $max_age=0;
+  function setMaxAge($seconds)
+  {
+    $this->max_age=$seconds;
+  }
+
+  function cacheControl() {
+    if ($this->max_age > 0) {
+      return array("expires" => toGMTime(time()+$this->max_age), "cache-control" => "public, max-age=$this->max_age");
+    }
+    else {
+      return array("expires" => 0, "cache-control" => "public, max-age=0, no-cache");
+    }
+  }
+
+};
+
+/**
+ * Caches a set of file with timestamps
+ */
+class FileCacheSet extends Cache {
+  private $newest = 0;
+  private $files = array();
+  private $parentCaches = array();
+
+  /**
+   * Constructs a FileCacheSet object
+   *
+   * @param $parent optional linked parent cache to sync with
+   */
+  protected function __construct($parent = null) {
+    if ($parent) {
+      $this->addParent($parent);
+    }
+  }
+
+  /**
+   * Links up a parent cache
+   *
+   * @param $parent cache we should sync with
+   */
+  protected function addParent($parent)
+  {
+    array_push($this->parentCaches, $parent);
+  }
+
+  /**
+   * List a set of files which contributes to this pages cacheset.
+   *
+   * @param $humanReadable If the timestamp should be human readable.
+   *
+   * @return an associative array of file, and timestamp
+   */
+  function cacheSet($humanReadable = False)
+  {
+    $retVal = array();
+    foreach ($this->parentCaches as $parent) {
+      $retVal = array_merge($retVal, $parent->cacheSet($humanReadable));
+    }
+    foreach($this->files as $file) {
+      $mtime = filemtime($file);
+      if ($humanReadable)
+	$mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
+
+      $retVal[$file] = $mtime;
+    }
+    return $retVal;
+  }
+
+  /**
+   * Include a file in the cacheset
+   *
+   * @param $path the path of the file
+   */
+  function cache_time($path)
+  {
+    if (!file_exists($path)) {
+      Logger::warn("${path} does not exist");
+      errorPage("Resource is not available", 404);
+    }
+
+    array_push($this->files, $path);
+    $mtime = filemtime($path);
+    if ($mtime > $this->newest) {
+      $this->newest = $mtime;
+    }
+  }
+
+  /**
+   * Find the newest member of the combined cacheset
+   *
+   * @return timestamp of newest member
+   */
+  function getNewest()
+  {
+    $newest = 0;
+    foreach ($this->parentCaches as $parent) {
+      $newest = max($newest, $parent->getNewest());
+    }
+    $newest = max($newest, $this->newest);
+    return $newest;
+  }
+}
+
+/**
+ * Singleton class, keeps track of all scriptfile includes
+ *
+ * This class is typically used as a parent class of another cache
+ */
+class ScriptIncludeCache extends FileCacheSet
+{
+  private static $myInstance = 0;
+
+  protected function __construct($filename = False)
+  {
+    parent::__construct();
+    if ($filename)
+      $this->cache_time($filename);
+    $this->cache_time(__FILE__);
+  }
+
+  /**
+   * Generates a singleton instance of this CacheTimeCheck
+   *
+   * @param $filename an optional file to include in the cacheset
+   *
+   * @return a CacheTimeCheck object
+   */
+  public static function instance($filename = False)
+  {
+    if (! self::$myInstance)
+      self::$myInstance = new self($filename);
+    elseif ($filename)
+      self::$myInstance->cache_time($filename);
+    return self::$myInstance;
+  }
+
+  /**
+   * Convenience function to include a file, and add it to the cacheset.
+   *
+   * @param $path path of the file
+   * @param $basedir a directory to prepend to the path
+   */
+  function includeOnce($path, $basedir = false)
+  {
+    if ($basedir)
+      $path = $basedir . "/" . $path;
+    $this->cache_time($path);
+    include_once($path);
+  }
+}
+?>
\ No newline at end of file
--- a/Flag.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,93 +0,0 @@
-<?php
-/**
- * @file
- * Displays a flag, in an active or disabled state, depending on parameters
- */
-
-include_once 'CacheTimeCheck.inc';
-
-/// @cond
-$scriptCache = ScriptIncludeCache::instance(__FILE__);
-$scriptCache->includeOnce('Language.inc');
-$scriptCache->includeOnce('common-functions.inc');
-$scriptCache->includeOnce('Page.inc');
-/// @endcond
-
-/**
- * Functionality for generating a flag based on state options
- */
-class Flag extends Page
-{
-  private $active;
-  private $lang;
-
-  /**
-   * Constructs a flag object
-   *
-   * @param $masterCache link this objects cache to this masterCache
-   */
-  function __construct($masterCache)
-  {
-    $this->active = $_GET['active'];
-    $lang = $_GET['lang'];
-
-    if(!$lang) {
-      $lang = "no";
-      $langs = Language::accepted();
-      foreach ($langs as $l => $val) {
-	if (file_exists($l)) {
-	  $lang = $l;
-	  break;
-	}
-      }
-    }
-
-
-    $this->lang = $lang;
-    foreach ( [ 'img', '../img' ] as $dir) {
-      $this->name = "${dir}/flag-${lang}";
-      if ($this->active)
-	$this->name .= "-active";
-      $this->name .= ".png";
-      if (file_exists($this->name))
-	break;
-    }
-
-    $cache = new CacheTimeCheck($this->name);
-    $cache->setMaxAge(30*86400);
-    $cache->addParent($masterCache);
-    $this->setCache($cache);
-  }
-
-  function cacheCheck()
-  {
-    return Cacheable::YES;
-  }
-
-  function mayCompress()
-  {
-    return false;
-  }
-
-  function mayValidate()
-  {
-    return false;
-  }
-
-  /**
-   * Produce the actual content
-   */
-  function generateContent()
-  {
-    $flag = loadFile($this->name);
-
-    if (floatval($flag) < 0) {
-      errorPage('Resource not found', 404);
-    }
-    else {
-      $flag = new PageContent($flag);
-      $flag->setHeader('Content-Type', 'image/png');
-      return $flag;
-    }
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Flag.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @file
+ * Displays a flag, in an active or disabled state, depending on parameters
+ */
+
+include_once 'CacheTimeCheck.inc.php';
+
+/// @cond
+$scriptCache = ScriptIncludeCache::instance(__FILE__);
+$scriptCache->includeOnce('Language.inc.php');
+$scriptCache->includeOnce('common-functions.inc.php');
+$scriptCache->includeOnce('Page.inc.php');
+/// @endcond
+
+/**
+ * Functionality for generating a flag based on state options
+ */
+class Flag extends Page
+{
+  private $active;
+  private $lang;
+
+  /**
+   * Constructs a flag object
+   *
+   * @param $masterCache link this objects cache to this masterCache
+   */
+  function __construct($masterCache)
+  {
+    $this->active = $_GET['active'];
+    $lang = $_GET['lang'];
+
+    if(!$lang) {
+      $lang = "no";
+      $langs = Language::accepted();
+      foreach ($langs as $l => $val) {
+	if (file_exists($l)) {
+	  $lang = $l;
+	  break;
+	}
+      }
+    }
+
+
+    $this->lang = $lang;
+    foreach ( [ 'img', '../img' ] as $dir) {
+      $this->name = "${dir}/flag-${lang}";
+      if ($this->active)
+	$this->name .= "-active";
+      $this->name .= ".png";
+      if (file_exists($this->name))
+	break;
+    }
+
+    $cache = new CacheTimeCheck($this->name);
+    $cache->setMaxAge(30*86400);
+    $cache->addParent($masterCache);
+    $this->setCache($cache);
+  }
+
+  function cacheCheck()
+  {
+    return Cacheable::YES;
+  }
+
+  function mayCompress()
+  {
+    return false;
+  }
+
+  function mayValidate()
+  {
+    return false;
+  }
+
+  /**
+   * Produce the actual content
+   */
+  function generateContent()
+  {
+    $flag = loadFile($this->name);
+
+    if (floatval($flag) < 0) {
+      errorPage('Resource not found', 404);
+    }
+    else {
+      $flag = new PageContent($flag);
+      $flag->setHeader('Content-Type', 'image/png');
+      return $flag;
+    }
+  }
+}
--- a/Http.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,173 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-/// @endcond
-
-/**
- * Thrown if a request has gone bad
- */
-class RequestException extends RuntimeException
-{
-  private $_info;
-
-  /**
-   * Constructs a RequestException
-   *
-   * @param $info an info array as defined in http_get, if a key with
-   * name error exist, this will be the error text for the
-   * RuntimeException
-   */
-  function __construct($info)
-  {
-    $this->_info = $info;
-    if (array_key_exists('error', $info)) {
-      parent::__construct($info['error']);
-    }
-  }
-
-  /**
-   * Get the info object associated with this RequestException
-   *
-   * @return $info an info array as defined in http_get
-   */
-  function info()
-  {
-    return $this->_info;
-  }
-}
-
-/**
- * Http contains a set of functions for retrieving http information
- */
-class Http
-{
-  /**
-   * Gets the content of a page
-   *
-   * This mimics the file_content with a uri parameter which is often
-   * disabled due to security reasons
-   *
-   * @param $uri The uri to fetch
-   */
-  static function get_uri_contents($uri)
-  {
-    $crl = curl_init();
-    $timeout = 5;
-
-    curl_setopt ($crl, CURLOPT_URL, $uri);
-    curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1);
-    curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout);
-    $ret = curl_exec($crl);
-    curl_close($crl);
-    return $ret;
-  }
-
-  /**
-   * Splits an http header response into an associative array
-   *
-   * @param $response The headers to parse
-   * @return an Associative array, the key '' refers to the HTTP status header
-   */
-  static function headersToArray($response)
-  {
-    $headers = array();
-    $response = trim($response);
-    $str = explode("\n", $response);
-    $headers[''] = trim($str[0]);
-    foreach($str as $kv) {
-      $p = strpos($kv, ":");
-      if ($p) {
-	$key = substr($kv, 0, $p);
-	$value = trim(substr($kv, $p + 1));
-	$headers[$key] = $value;
-      }
-    }
-    return $headers;
-  }
-
-  /**
-   * Queries a URL for headers
-   *
-   * @param $url the url to query
-   * @param $timeout float number of seconds to wait before timeout
-   * @return an associative array of all headers returned
-   */
-  static function getHeaders($url, $timeout = 1)
-  {
-    //Workaround when getHeaders doesn't work
-    if (false) {
-      $response = http_head($url, array("timeout" => $timeout), $info);
-      if (array_key_exists('error', $info) && $info['error']) {
-	throw new RequestException($info);
-      }
-      return self::headersToArray($response);
-    }
-    elseif (true) {
-      $rp = get_headers($url);
-      $response=array('' =>  array_shift ($rp));
-      foreach($rp as $kv) {
-	if (!str_contains($kv, ':')) {
-	  if (!preg_match("/^HTTP\/\S+\s(\d+)/", $response[''], $re)) {
-	    throw new Exception('Uninteligble header');
-	  }
-	  //FIXME: We should do something with this,
-	  //but now we just assume we have a redirect
-	  $retCode=intval($re[1]);
-	  $response=array('' =>  $kv);
-	}
-	else {
-	  $tmp=array_map('trim', explode(':', $kv, 2));
-	  $response[$tmp[0]]=$tmp[1];
-	}
-      }
-      return $response;
-    }
-    else {
-      return array('' => 'HTTP/1.1 200 OK');
-    }
-
-  }
-
-  /**
-   * Performs a post to an URI for headers
-   *
-   * @param $uri the uri to query
-   * @param $params an associative array of params to post
-   * @param $timeout float number of seconds to wait before timeout
-   * @return an associative array of all headers returned
-   */
-  static function postHeaders($uri, $params, $timeout = 1)
-  {
-    $crl = curl_init();
-
-    $descriptorspec = array(
-			    0 => array("pipe", "r"),
-			    1 => array("pipe", "w"),
-			    //2 => array("file", "/tmp/error-output.txt", "a")
-			    );
-
-    //We use tac, since it buffers, and we don't care about the output
-    //being reordered.
-    $process = proc_open('tac | tac', $descriptorspec, $pipes);
-
-    curl_setopt ($crl, CURLOPT_URL, $uri);
-    curl_setopt ($crl, CURLOPT_WRITEHEADER, $pipes[0]);
-    curl_setopt ($crl, CURLOPT_NOBODY, true);
-    curl_setopt ($crl, CURLOPT_POST, true);
-    curl_setopt ($crl, CURLOPT_POSTFIELDS, $params);
-
-    curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1);
-    curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout);
-    $ret = curl_exec($crl);
-    curl_close($crl);
-
-    fclose($pipes[0]);
-    $buf = fread($pipes[1], 8192);
-
-    return self::headersToArray($buf);
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Http.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,173 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+/// @endcond
+
+/**
+ * Thrown if a request has gone bad
+ */
+class RequestException extends RuntimeException
+{
+  private $_info;
+
+  /**
+   * Constructs a RequestException
+   *
+   * @param $info an info array as defined in http_get, if a key with
+   * name error exist, this will be the error text for the
+   * RuntimeException
+   */
+  function __construct($info)
+  {
+    $this->_info = $info;
+    if (array_key_exists('error', $info)) {
+      parent::__construct($info['error']);
+    }
+  }
+
+  /**
+   * Get the info object associated with this RequestException
+   *
+   * @return $info an info array as defined in http_get
+   */
+  function info()
+  {
+    return $this->_info;
+  }
+}
+
+/**
+ * Http contains a set of functions for retrieving http information
+ */
+class Http
+{
+  /**
+   * Gets the content of a page
+   *
+   * This mimics the file_content with a uri parameter which is often
+   * disabled due to security reasons
+   *
+   * @param $uri The uri to fetch
+   */
+  static function get_uri_contents($uri)
+  {
+    $crl = curl_init();
+    $timeout = 5;
+
+    curl_setopt ($crl, CURLOPT_URL, $uri);
+    curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1);
+    curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout);
+    $ret = curl_exec($crl);
+    curl_close($crl);
+    return $ret;
+  }
+
+  /**
+   * Splits an http header response into an associative array
+   *
+   * @param $response The headers to parse
+   * @return an Associative array, the key '' refers to the HTTP status header
+   */
+  static function headersToArray($response)
+  {
+    $headers = array();
+    $response = trim($response);
+    $str = explode("\n", $response);
+    $headers[''] = trim($str[0]);
+    foreach($str as $kv) {
+      $p = strpos($kv, ":");
+      if ($p) {
+	$key = substr($kv, 0, $p);
+	$value = trim(substr($kv, $p + 1));
+	$headers[$key] = $value;
+      }
+    }
+    return $headers;
+  }
+
+  /**
+   * Queries a URL for headers
+   *
+   * @param $url the url to query
+   * @param $timeout float number of seconds to wait before timeout
+   * @return an associative array of all headers returned
+   */
+  static function getHeaders($url, $timeout = 1)
+  {
+    //Workaround when getHeaders doesn't work
+    if (false) {
+      $response = http_head($url, array("timeout" => $timeout), $info);
+      if (array_key_exists('error', $info) && $info['error']) {
+	throw new RequestException($info);
+      }
+      return self::headersToArray($response);
+    }
+    elseif (true) {
+      $rp = get_headers($url);
+      $response=array('' =>  array_shift ($rp));
+      foreach($rp as $kv) {
+	if (!str_contains($kv, ':')) {
+	  if (!preg_match("/^HTTP\/\S+\s(\d+)/", $response[''], $re)) {
+	    throw new Exception('Uninteligble header');
+	  }
+	  //FIXME: We should do something with this,
+	  //but now we just assume we have a redirect
+	  $retCode=intval($re[1]);
+	  $response=array('' =>  $kv);
+	}
+	else {
+	  $tmp=array_map('trim', explode(':', $kv, 2));
+	  $response[$tmp[0]]=$tmp[1];
+	}
+      }
+      return $response;
+    }
+    else {
+      return array('' => 'HTTP/1.1 200 OK');
+    }
+
+  }
+
+  /**
+   * Performs a post to an URI for headers
+   *
+   * @param $uri the uri to query
+   * @param $params an associative array of params to post
+   * @param $timeout float number of seconds to wait before timeout
+   * @return an associative array of all headers returned
+   */
+  static function postHeaders($uri, $params, $timeout = 1)
+  {
+    $crl = curl_init();
+
+    $descriptorspec = array(
+			    0 => array("pipe", "r"),
+			    1 => array("pipe", "w"),
+			    //2 => array("file", "/tmp/error-output.txt", "a")
+			    );
+
+    //We use tac, since it buffers, and we don't care about the output
+    //being reordered.
+    $process = proc_open('tac | tac', $descriptorspec, $pipes);
+
+    curl_setopt ($crl, CURLOPT_URL, $uri);
+    curl_setopt ($crl, CURLOPT_WRITEHEADER, $pipes[0]);
+    curl_setopt ($crl, CURLOPT_NOBODY, true);
+    curl_setopt ($crl, CURLOPT_POST, true);
+    curl_setopt ($crl, CURLOPT_POSTFIELDS, $params);
+
+    curl_setopt ($crl, CURLOPT_RETURNTRANSFER, 1);
+    curl_setopt ($crl, CURLOPT_CONNECTTIMEOUT, $timeout);
+    $ret = curl_exec($crl);
+    curl_close($crl);
+
+    fclose($pipes[0]);
+    $buf = fread($pipes[1], 8192);
+
+    return self::headersToArray($buf);
+  }
+}
+?>
\ No newline at end of file
--- a/InputParser.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,368 +0,0 @@
-<?php
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Page.inc', dirname(__FILE__));
-/// @endcond
-
-/**
- * Functionality for translating an XML configuration document into a webpage
- */
-class InputParser extends Page
-{
-  private $options;
-  private $master;
-
-  /**
-   * Constructs a new InputParser object
-   *
-   * @param $name name of file to read configuration from
-   *
-   * @param $masterCache CacheTimeCheck cache object to use as parent
-   * for this inputParsercache
-   */
-  function __construct($name, $masterCache) {
-    $this->master = new DOMDocument();
-    $cache = new CacheTimeCheck($name);
-    $cache->addParent($masterCache);
-    parent::setCache($cache);
-    $this->master->load($name);
-
-    $this->options = new Options($this->master);
-    $this->options->setCache($cache);
-    $base=basePath();
-    $base=$_SERVER['DOCUMENT_ROOT'];
-    $this->options->setBasePath($base);
-    $this->options->setCacheable(true);
-
-    $this->options->setUrlParams(array('name', 'lang'));
-
-    if(array_key_exists('lang', $_GET) && $_GET['lang']) {
-      $this->options->setLang($_GET['lang']);
-    }
-    else {
-      if (count($this->options->getAcceptedLanguages()) > 1)
-	$this->addVariant('Accept-Language');
-      $this->options->setLang($this->options->getDefaultLang());
-    }
-
-    if(array_key_exists('name', $_GET) && $_GET['name']) {
-      $this->options->setName($_GET['name']);
-    }
-    else {
-      $pathToAnalyze=$_SERVER['PATH_TRANSLATED'];
-      $prefix=$_SERVER['SCRIPT_FILENAME'];
-      if (substr($pathToAnalyze, 0, strlen($prefix)) == $prefix) {
-	$pathToAnalyze = substr($pathToAnalyze, strlen($prefix));
-      }
-      preg_match('/\/([^\/]*)$/', $pathToAnalyze, $groups);
-      if ($groups[1]) {
-	$this->options->setName($groups[1]);
-      }
-    }
-
-
-    $params = $this->master->getElementsByTagName("param");
-
-    foreach ($params as $param) {
-      if ($param->getAttribute("type") == "input") {
-	$doc = self::getInput($this->master, $param, $this->options);
-
-	$parent = $param->parentNode;
-	foreach ($doc->firstChild->childNodes as $child) {
-	  $clonedChild = $this->master->importNode($child, true);
-	  $parent->insertBefore($clonedChild, $param);
-	}
-	$parent->removeChild($param);
-      }
-    }
-    $this->master = self::getFiles($this->master, $this->options);
-    self::removeCommentsFromDOM($this->master);
-    $cache->setMaxAge(0);
-  }
-
-  /**
-   * Removes all comments from the DOM
-   *
-   * @param $dom A DOMDocument for processing
-   */
-  static function removeCommentsFromDOM($dom)
-  {
-    $xpath = new DOMXPath($dom);
-    foreach ($xpath->query('//comment()') as $comment) {
-      $comment->parentNode->removeChild($comment);
-    }
-  }
-
-  function cacheCheck()
-  {
-    return $this->options->getCacheable();
-  }
-
-  /**
-   * Generate an appropriate response for this page, eg. 302 NOT
-   * MODIFIED or the actual page
-   */
-  function generateContent()
-  {
-    //We may need to set and check the contenttype and replace
-    //saveHTML with saveXML
-    $retVal = new PageContent(options::replacePlaceholders($this->master->saveXML()));
-    $retVal->setHeader('Content-Language', $this->options->getLang());
-    return $retVal;
-  }
-
-  /**
-   * Extracts data from a @<param@> tag.
-   *
-   * @param $param the param tag
-   *
-   * @return if the type is array, return an array, otherwise return a
-   * scalar
-   */
-  static function getParam($param)
-  {
-    $param_type = $param->getAttribute("type");
-    $param_value;
-    if (! $param_type)
-      $param_type = "scalar";
-
-    if($param_type == "scalar") {
-      $param_subst = $param->getAttribute("subst");
-      $param_value = $param->getAttribute("value");
-      if ($param_subst) {
-	/*
-	  $param_value=preg_replace("/name/", $name, $param_subst);
-	  $param_value=preg_replace('/lang/', $lang, $param_value);
-	*/
-      }
-    }
-    elseif($param_type == "array") {
-      $params = $param->getElementsByTagName("param");
-      $param_value = array();
-      foreach ($param->childNodes as $param) {
-	if ($param->nodeType == XML_ELEMENT_NODE) {
-	  array_push($param_value, self::getParam($param));
-	}
-      }
-    }
-    else {
-      throw new UnexpectedValueException("Unknown parameter type '$param_type'");
-    }
-    return $param_value;
-  }
-
-  /**
-   * This is the last processing stage for generating a file.
-   *
-   * @param $doc the document to be worked upon
-   * @param $options an Options object for this file.
-   *
-   * @return This is the same as the input document, fully processed
-   */
-  static function getFiles($doc, $options) {
-    $lang = $options->getLang();
-    $conf = $options->getName();
-
-    $toRemove = array();
-
-    $topLevelTags = $doc->getElementsByTagName("toplevel");
-    foreach ($topLevelTags as $topLevel) {
-      $topLevel->parentNode->removeChild($topLevel);
-    }
-
-    $valueDict = array();
-    $fragments = array();
-    $setters = $doc->getElementsByTagName("set");
-    foreach ($setters as $setTag) {
-      $key = $setTag->getAttribute("id");
-      $type = $setTag->getAttribute("type");
-      if ($type == "fragment") {
-	$fragments[$key] = $setTag;
-      }
-      else {
-	$value = $setTag->getAttribute("value");
-	if ($key && $value) {
-	  $valueDict[$key] = $value;
-	}
-      }
-      //We need to iterate in the opposite direction when removing,
-      //so best shifting.
-      array_unshift($toRemove, $setTag);
-    }
-
-    $params = $doc->getElementsByTagName("param");
-    foreach ($params as $param) {
-      if ($param->getAttribute("type")=="input_config") {
-	$id = $param->getAttribute("id");
-	if (array_key_exists($id, $valueDict)) {
-	  $value = $valueDict[$id];
-	  $tmp = new DOMDocument();
-
-	  $tmp->loadXml("<xml>${value}</xml>");
-	  $parent = $param->parentNode;
-	  $parent->insertBefore(new DOMText($tmp->textContent), $param);
-	  //We need to iterate in the opposite direction when removing,
-	  //so best shifting.
-	  array_unshift($toRemove, $param);
-	}
-	elseif (array_key_exists($id, $fragments)) {
-	  $fragment = $fragments[$id];
-
-	  $cloneFragment = $fragment->cloneNode(true);
-	  $insNode = $param;
-	  for ($i = $cloneFragment->childNodes->length - 1; $i >= 0; $i--) {
-	    $child = $cloneFragment->childNodes->item($i);
-	    $child = $child->parentNode->removeChild($child);
-	    $insNode = $insNode->parentNode->insertBefore($child, $insNode);
-	  }
-
-	  //We need to iterate in the opposite direction when removing,
-	  //so best shifting.
-	  array_unshift($toRemove, $param);
-	}
-      }
-    }
-
-    foreach($toRemove as $param) {
-      $parent = $param->parentNode;
-      $parent->removeChild($param);
-    }
-
-    $body = getElementByTagName($doc,"html");
-    $files = $body->getElementsByTagName("file");
-
-    $toRemove = array();
-
-    foreach ($files as $file) {
-      $script = $file->getAttribute("script");
-      if ($script) {
-	$options->setCacheable(false);
-	$src="";
-	$cwd = getcwd();
-
-	$matches=array();
-	preg_match('/(.*\/)/', $script, $matches);
-	$dirname=$matches[0];
-	preg_match('/([^\/]*)$/', $script, $matches);
-	$filename=$matches[0];
-	chdir("${lang}/${dirname}");
-	$pipe=popen("php ${filename}","r");
-	$file_content = stream_get_contents($pipe);
-	chdir("${cwd}");
-      }
-      else {
-	$src = $file->getAttribute("src");
-	$fname = $options->getBasePath() . "/${lang}/${src}";
-	if (!file_exists($fname) ) {
-	  $fname = $options->getBasePath() . "/common/${src}";
-	}
-	$file_content = $options->getCache()->loadFile($fname);
-      }
-      if(floatval($file_content)<0) {
-	errorPage("Resource not found '${lang}/${src}'");
-      }
-
-      $filters = $file->getElementsByTagName("filter");
-      foreach($filters as $filter) {
-	$func = $filter->getAttribute("function");
-	$params = $filter->getElementsByTagName("param");
-	$callString = "\$file_content = ${func}(\$file_content, \$options";
-	$param_values = array();
-	$i = 0;
-	foreach ($filter->childNodes as $param) {
-	  if ($param->nodeType == XML_ELEMENT_NODE)
-	    {
-	      $param_value[$i] = self::getParam($param);
-	      $callString .= ",\$param_value[$i]";
-	      $i++;
-	    }
-	}
-	$callString .= ");";
-	eval($callString);
-      }
-      $ndoc = $doc->createDocumentFragment();
-
-      $parent = $file->parentNode;
-
-      $ndoc->appendXML($file_content);
-      $parent->insertBefore($ndoc, $file);
-
-      //We need to iterate in the opposite direction when removing,
-      //so best shifting.
-      array_unshift($toRemove, $file);
-    }
-    foreach($toRemove as $param) {
-      $parent = $param->parentNode;
-      $parent->removeChild($param);
-    }
-
-    return $doc;
-  }
-
-  /**
-   * Follows all include directives recursively for the specified
-   * $param an generates an xml file.
-   *
-   * This function may be used to generate a file which has all the
-   * necessary information to determine wether or not we may cache.
-   *
-   * @param $master The master document to be processed
-   * @param $param the input tag to resolve
-   * @param $options the options object for this file
-   */
-  function getInput($master, $param, $options)
-  {
-    $lang = $options->getLang();
-    $name = $param->getAttribute("id");
-    $conf = $options->getName();
-    if (!$conf)
-      $conf = $param->getAttribute("default");
-
-    $confFile = $options->getBasePath() . "/${lang}/${conf}.xml";
-    if (! file_exists($confFile) ) {
-      $confFile = $options->getBasePath() . "/common/${conf}.xml";
-    }
-
-    $options->getCache()->cache_time($confFile);
-    $doc = new DOMDocument();
-    $doc->load($confFile);
-
-    $toplevel = $doc->getElementsByTagName("toplevel");
-
-    if(! $toplevel->length) {
-      errorPage("Resource '${conf}' is not available", 500);
-    }
-
-    $includes = $doc->getElementsByTagName("include");
-    $recurse = 0;
-
-    while($includes->length > 0) {
-      if(++$recurse > MAX_RECURSE) {
-	errorPage('Recursion limit exceeded', 500);
-      }
-      foreach ($includes as $include) {
-	$src = $include->getAttribute("src");
-	$subdoc = new DOMDocument();
-	$subfile = $options->getBasePath() . "/${lang}/${src}";
-	if (! file_exists($subfile) ) {
-	  $subfile = $options->getBasePath() . "/common/${src}";
-	}
-	$subdoc->load("$subfile");
-	$options->getCache()->cache_time($subfile);
-	$parent = $include->parentNode;
-	$xml = getElementByTagName($subdoc,"xml");
-	foreach($xml->childNodes as $child) {
-	  $text = $subdoc->saveHTML($child);
-	  $clonedChild = $doc->importNode($child,true);
-	  $parent->insertBefore($clonedChild,$include);
-	}
-	$parent->removeChild($include);
-      }
-      $includes = $doc->getElementsByTagName("include");
-    }
-
-    return $doc;
-  }
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/InputParser.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,368 @@
+<?php
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Page.inc.php', dirname(__FILE__));
+/// @endcond
+
+/**
+ * Functionality for translating an XML configuration document into a webpage
+ */
+class InputParser extends Page
+{
+  private $options;
+  private $master;
+
+  /**
+   * Constructs a new InputParser object
+   *
+   * @param $name name of file to read configuration from
+   *
+   * @param $masterCache CacheTimeCheck cache object to use as parent
+   * for this inputParsercache
+   */
+  function __construct($name, $masterCache) {
+    $this->master = new DOMDocument();
+    $cache = new CacheTimeCheck($name);
+    $cache->addParent($masterCache);
+    parent::setCache($cache);
+    $this->master->load($name);
+
+    $this->options = new Options($this->master);
+    $this->options->setCache($cache);
+    $base=basePath();
+    $base=$_SERVER['DOCUMENT_ROOT'];
+    $this->options->setBasePath($base);
+    $this->options->setCacheable(true);
+
+    $this->options->setUrlParams(array('name', 'lang'));
+
+    if(array_key_exists('lang', $_GET) && $_GET['lang']) {
+      $this->options->setLang($_GET['lang']);
+    }
+    else {
+      if (count($this->options->getAcceptedLanguages()) > 1)
+	$this->addVariant('Accept-Language');
+      $this->options->setLang($this->options->getDefaultLang());
+    }
+
+    if(array_key_exists('name', $_GET) && $_GET['name']) {
+      $this->options->setName($_GET['name']);
+    }
+    else {
+      $pathToAnalyze=$_SERVER['PATH_TRANSLATED'];
+      $prefix=$_SERVER['SCRIPT_FILENAME'];
+      if (substr($pathToAnalyze, 0, strlen($prefix)) == $prefix) {
+	$pathToAnalyze = substr($pathToAnalyze, strlen($prefix));
+      }
+      preg_match('/\/([^\/]*)$/', $pathToAnalyze, $groups);
+      if ($groups[1]) {
+	$this->options->setName($groups[1]);
+      }
+    }
+
+
+    $params = $this->master->getElementsByTagName("param");
+
+    foreach ($params as $param) {
+      if ($param->getAttribute("type") == "input") {
+	$doc = self::getInput($this->master, $param, $this->options);
+
+	$parent = $param->parentNode;
+	foreach ($doc->firstChild->childNodes as $child) {
+	  $clonedChild = $this->master->importNode($child, true);
+	  $parent->insertBefore($clonedChild, $param);
+	}
+	$parent->removeChild($param);
+      }
+    }
+    $this->master = self::getFiles($this->master, $this->options);
+    self::removeCommentsFromDOM($this->master);
+    $cache->setMaxAge(0);
+  }
+
+  /**
+   * Removes all comments from the DOM
+   *
+   * @param $dom A DOMDocument for processing
+   */
+  static function removeCommentsFromDOM($dom)
+  {
+    $xpath = new DOMXPath($dom);
+    foreach ($xpath->query('//comment()') as $comment) {
+      $comment->parentNode->removeChild($comment);
+    }
+  }
+
+  function cacheCheck()
+  {
+    return $this->options->getCacheable();
+  }
+
+  /**
+   * Generate an appropriate response for this page, eg. 302 NOT
+   * MODIFIED or the actual page
+   */
+  function generateContent()
+  {
+    //We may need to set and check the contenttype and replace
+    //saveHTML with saveXML
+    $retVal = new PageContent(options::replacePlaceholders($this->master->saveXML()));
+    $retVal->setHeader('Content-Language', $this->options->getLang());
+    return $retVal;
+  }
+
+  /**
+   * Extracts data from a @<param@> tag.
+   *
+   * @param $param the param tag
+   *
+   * @return if the type is array, return an array, otherwise return a
+   * scalar
+   */
+  static function getParam($param)
+  {
+    $param_type = $param->getAttribute("type");
+    $param_value;
+    if (! $param_type)
+      $param_type = "scalar";
+
+    if($param_type == "scalar") {
+      $param_subst = $param->getAttribute("subst");
+      $param_value = $param->getAttribute("value");
+      if ($param_subst) {
+	/*
+	  $param_value=preg_replace("/name/", $name, $param_subst);
+	  $param_value=preg_replace('/lang/', $lang, $param_value);
+	*/
+      }
+    }
+    elseif($param_type == "array") {
+      $params = $param->getElementsByTagName("param");
+      $param_value = array();
+      foreach ($param->childNodes as $param) {
+	if ($param->nodeType == XML_ELEMENT_NODE) {
+	  array_push($param_value, self::getParam($param));
+	}
+      }
+    }
+    else {
+      throw new UnexpectedValueException("Unknown parameter type '$param_type'");
+    }
+    return $param_value;
+  }
+
+  /**
+   * This is the last processing stage for generating a file.
+   *
+   * @param $doc the document to be worked upon
+   * @param $options an Options object for this file.
+   *
+   * @return This is the same as the input document, fully processed
+   */
+  static function getFiles($doc, $options) {
+    $lang = $options->getLang();
+    $conf = $options->getName();
+
+    $toRemove = array();
+
+    $topLevelTags = $doc->getElementsByTagName("toplevel");
+    foreach ($topLevelTags as $topLevel) {
+      $topLevel->parentNode->removeChild($topLevel);
+    }
+
+    $valueDict = array();
+    $fragments = array();
+    $setters = $doc->getElementsByTagName("set");
+    foreach ($setters as $setTag) {
+      $key = $setTag->getAttribute("id");
+      $type = $setTag->getAttribute("type");
+      if ($type == "fragment") {
+	$fragments[$key] = $setTag;
+      }
+      else {
+	$value = $setTag->getAttribute("value");
+	if ($key && $value) {
+	  $valueDict[$key] = $value;
+	}
+      }
+      //We need to iterate in the opposite direction when removing,
+      //so best shifting.
+      array_unshift($toRemove, $setTag);
+    }
+
+    $params = $doc->getElementsByTagName("param");
+    foreach ($params as $param) {
+      if ($param->getAttribute("type")=="input_config") {
+	$id = $param->getAttribute("id");
+	if (array_key_exists($id, $valueDict)) {
+	  $value = $valueDict[$id];
+	  $tmp = new DOMDocument();
+
+	  $tmp->loadXml("<xml>${value}</xml>");
+	  $parent = $param->parentNode;
+	  $parent->insertBefore(new DOMText($tmp->textContent), $param);
+	  //We need to iterate in the opposite direction when removing,
+	  //so best shifting.
+	  array_unshift($toRemove, $param);
+	}
+	elseif (array_key_exists($id, $fragments)) {
+	  $fragment = $fragments[$id];
+
+	  $cloneFragment = $fragment->cloneNode(true);
+	  $insNode = $param;
+	  for ($i = $cloneFragment->childNodes->length - 1; $i >= 0; $i--) {
+	    $child = $cloneFragment->childNodes->item($i);
+	    $child = $child->parentNode->removeChild($child);
+	    $insNode = $insNode->parentNode->insertBefore($child, $insNode);
+	  }
+
+	  //We need to iterate in the opposite direction when removing,
+	  //so best shifting.
+	  array_unshift($toRemove, $param);
+	}
+      }
+    }
+
+    foreach($toRemove as $param) {
+      $parent = $param->parentNode;
+      $parent->removeChild($param);
+    }
+
+    $body = getElementByTagName($doc,"html");
+    $files = $body->getElementsByTagName("file");
+
+    $toRemove = array();
+
+    foreach ($files as $file) {
+      $script = $file->getAttribute("script");
+      if ($script) {
+	$options->setCacheable(false);
+	$src="";
+	$cwd = getcwd();
+
+	$matches=array();
+	preg_match('/(.*\/)/', $script, $matches);
+	$dirname=$matches[0];
+	preg_match('/([^\/]*)$/', $script, $matches);
+	$filename=$matches[0];
+	chdir("${lang}/${dirname}");
+	$pipe=popen("php ${filename}","r");
+	$file_content = stream_get_contents($pipe);
+	chdir("${cwd}");
+      }
+      else {
+	$src = $file->getAttribute("src");
+	$fname = $options->getBasePath() . "/${lang}/${src}";
+	if (!file_exists($fname) ) {
+	  $fname = $options->getBasePath() . "/common/${src}";
+	}
+	$file_content = $options->getCache()->loadFile($fname);
+      }
+      if(floatval($file_content)<0) {
+	errorPage("Resource not found '${lang}/${src}'");
+      }
+
+      $filters = $file->getElementsByTagName("filter");
+      foreach($filters as $filter) {
+	$func = $filter->getAttribute("function");
+	$params = $filter->getElementsByTagName("param");
+	$callString = "\$file_content = ${func}(\$file_content, \$options";
+	$param_values = array();
+	$i = 0;
+	foreach ($filter->childNodes as $param) {
+	  if ($param->nodeType == XML_ELEMENT_NODE)
+	    {
+	      $param_value[$i] = self::getParam($param);
+	      $callString .= ",\$param_value[$i]";
+	      $i++;
+	    }
+	}
+	$callString .= ");";
+	eval($callString);
+      }
+      $ndoc = $doc->createDocumentFragment();
+
+      $parent = $file->parentNode;
+
+      $ndoc->appendXML($file_content);
+      $parent->insertBefore($ndoc, $file);
+
+      //We need to iterate in the opposite direction when removing,
+      //so best shifting.
+      array_unshift($toRemove, $file);
+    }
+    foreach($toRemove as $param) {
+      $parent = $param->parentNode;
+      $parent->removeChild($param);
+    }
+
+    return $doc;
+  }
+
+  /**
+   * Follows all include directives recursively for the specified
+   * $param an generates an xml file.
+   *
+   * This function may be used to generate a file which has all the
+   * necessary information to determine wether or not we may cache.
+   *
+   * @param $master The master document to be processed
+   * @param $param the input tag to resolve
+   * @param $options the options object for this file
+   */
+  function getInput($master, $param, $options)
+  {
+    $lang = $options->getLang();
+    $name = $param->getAttribute("id");
+    $conf = $options->getName();
+    if (!$conf)
+      $conf = $param->getAttribute("default");
+
+    $confFile = $options->getBasePath() . "/${lang}/${conf}.xml";
+    if (! file_exists($confFile) ) {
+      $confFile = $options->getBasePath() . "/common/${conf}.xml";
+    }
+
+    $options->getCache()->cache_time($confFile);
+    $doc = new DOMDocument();
+    $doc->load($confFile);
+
+    $toplevel = $doc->getElementsByTagName("toplevel");
+
+    if(! $toplevel->length) {
+      errorPage("Resource '${conf}' is not available", 500);
+    }
+
+    $includes = $doc->getElementsByTagName("include");
+    $recurse = 0;
+
+    while($includes->length > 0) {
+      if(++$recurse > MAX_RECURSE) {
+	errorPage('Recursion limit exceeded', 500);
+      }
+      foreach ($includes as $include) {
+	$src = $include->getAttribute("src");
+	$subdoc = new DOMDocument();
+	$subfile = $options->getBasePath() . "/${lang}/${src}";
+	if (! file_exists($subfile) ) {
+	  $subfile = $options->getBasePath() . "/common/${src}";
+	}
+	$subdoc->load("$subfile");
+	$options->getCache()->cache_time($subfile);
+	$parent = $include->parentNode;
+	$xml = getElementByTagName($subdoc,"xml");
+	foreach($xml->childNodes as $child) {
+	  $text = $subdoc->saveHTML($child);
+	  $clonedChild = $doc->importNode($child,true);
+	  $parent->insertBefore($clonedChild,$include);
+	}
+	$parent->removeChild($include);
+      }
+      $includes = $doc->getElementsByTagName("include");
+    }
+
+    return $doc;
+  }
+}
+?>
--- a/Language.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-<?php
-/**
- * Functionality for determining language use
- */
-class Language {
-  /**
-   * Extracts the accepted languages from the GET query, sorted by
-   * preference(q-value).
-   *
-   * @return associative array of language codes with q value
-   */
-  static function accepted() {
-    $langs = array();
-
-    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-      // break up string into pieces (languages and q factors)
-      preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
-
-      if (count($lang_parse[1])) {
-	// create a list like "en" => 0.8
-	$langs = array_combine($lang_parse[1], $lang_parse[4]);
-
-	// set default to 1 for any without q factor
-	foreach ($langs as $lang => $val) {
-	  if ($val === '') $langs[$lang] = 1;
-	}
-
-	// sort list based on value
-	arsort($langs, SORT_NUMERIC);
-      }
-    }
-    return $langs;
-  }
-
-  /**
-   * From the list of desired languages, pick the best which we can serve.
-   *
-   * @param $default what to choose if no match could be found.
-   */
-  static function prefer($default)
-  {
-    $language = $default;
-    $langs = self::accepted();
-    if ($langs) {
-      foreach ($langs as $l => $val) {
-	if (file_exists($l)) {
-	  $language = $l;
-	  break;
-	}
-      }
-    }
-    return $language;
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Language.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,55 @@
+<?php
+/**
+ * Functionality for determining language use
+ */
+class Language {
+  /**
+   * Extracts the accepted languages from the GET query, sorted by
+   * preference(q-value).
+   *
+   * @return associative array of language codes with q value
+   */
+  static function accepted() {
+    $langs = array();
+
+    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+      // break up string into pieces (languages and q factors)
+      preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);
+
+      if (count($lang_parse[1])) {
+	// create a list like "en" => 0.8
+	$langs = array_combine($lang_parse[1], $lang_parse[4]);
+
+	// set default to 1 for any without q factor
+	foreach ($langs as $lang => $val) {
+	  if ($val === '') $langs[$lang] = 1;
+	}
+
+	// sort list based on value
+	arsort($langs, SORT_NUMERIC);
+      }
+    }
+    return $langs;
+  }
+
+  /**
+   * From the list of desired languages, pick the best which we can serve.
+   *
+   * @param $default what to choose if no match could be found.
+   */
+  static function prefer($default)
+  {
+    $language = $default;
+    $langs = self::accepted();
+    if ($langs) {
+      foreach ($langs as $l => $val) {
+	if (file_exists($l)) {
+	  $language = $l;
+	  break;
+	}
+      }
+    }
+    return $language;
+  }
+}
+?>
\ No newline at end of file
--- a/Logger.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-<?php
-/**
- * Common access point for logging
- */
-class Logger {
-  /**
-   * All logging goes through this function
-   *
-   * @param $level the severity level of this message
-   * @param $msg message to be logged
-   */
-  static function msg($level, $msg)
-  {
-    if (DEBUG_LEVEL >= $level) {
-      print $msg;
-      if(ABORT_ON_LOG)
-	exit;
-    }
-  }
-
-  /**
-   * Convenience function, logs a message with level VERBOSITY_WARNING
-   *
-   * @param $msg message to be logged
-   */
-  static function warn($msg)
-  {
-    self::msg(VERBOSITY_WARNING, $msg);
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Logger.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,31 @@
+<?php
+/**
+ * Common access point for logging
+ */
+class Logger {
+  /**
+   * All logging goes through this function
+   *
+   * @param $level the severity level of this message
+   * @param $msg message to be logged
+   */
+  static function msg($level, $msg)
+  {
+    if (DEBUG_LEVEL >= $level) {
+      print $msg;
+      if(ABORT_ON_LOG)
+	exit;
+    }
+  }
+
+  /**
+   * Convenience function, logs a message with level VERBOSITY_WARNING
+   *
+   * @param $msg message to be logged
+   */
+  static function warn($msg)
+  {
+    self::msg(VERBOSITY_WARNING, $msg);
+  }
+}
+?>
\ No newline at end of file
--- a/OnlineBufferValidator.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,39 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Http.inc', $baseDir);
-$cache->includeOnce('OnlineValidator.inc', $baseDir);
-/// @endcond
-
-
-/**
- * Defines a validator which uses an online validator and a buffer
- * with the content
- */
-class OnlineBufferValidator extends OnlineValidator
-{
-
-  private $buffer;
-
-  /**
-   * Constructs an OnlineURIValidator
-   *
-   * @param $buffer The buffer to validate
-   */
-  function __construct($buffer)
-  {
-    $this->buffer = $buffer;
-  }
-
-  function check()
-  {
-    $params = array( 'fragment' => $this->buffer);
-
-    $headers = Http::postHeaders($this->validator_url , $params, 2);
-    return $headers['X-W3C-Validator-Status'] === "Valid";
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OnlineBufferValidator.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,39 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Http.inc.php', $baseDir);
+$cache->includeOnce('OnlineValidator.inc.php', $baseDir);
+/// @endcond
+
+
+/**
+ * Defines a validator which uses an online validator and a buffer
+ * with the content
+ */
+class OnlineBufferValidator extends OnlineValidator
+{
+
+  private $buffer;
+
+  /**
+   * Constructs an OnlineURIValidator
+   *
+   * @param $buffer The buffer to validate
+   */
+  function __construct($buffer)
+  {
+    $this->buffer = $buffer;
+  }
+
+  function check()
+  {
+    $params = array( 'fragment' => $this->buffer);
+
+    $headers = Http::postHeaders($this->validator_url , $params, 2);
+    return $headers['X-W3C-Validator-Status'] === "Valid";
+  }
+}
+?>
\ No newline at end of file
--- a/OnlineURIValidator.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Http.inc', $baseDir);
-$cache->includeOnce('OnlineValidator.inc', $baseDir);
-/// @endcond
-
-/**
- * Defines a validator which uses an online validator and a URI to the
- * content
- */
-class OnlineURIValidator extends OnlineValidator
-{
-  private $uri;
-
-  /**
-   * Constructs an OnlineURIValidator
-   *
-   * @param $uri The uri to validate
-   */
-  function __construct($uri)
-  {
-    $this->uri = $uri;
-  }
-
-  function check()
-  {
-    $request = urlencode($this->uri);
-    $query= '?uri=' . $request;
-
-    $headers = Http::getHeaders($this->validator_url . $query, 5);
-    return $headers['X-W3C-Validator-Status'] === "Valid";
-  }
-
-  /**
-   * Gets the URI for this validator
-   *
-   * @return string with the URI
-   */
-  function getUri()
-  {
-    return $this->uri;
-  }
-
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OnlineURIValidator.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,49 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Http.inc.php', $baseDir);
+$cache->includeOnce('OnlineValidator.inc.php', $baseDir);
+/// @endcond
+
+/**
+ * Defines a validator which uses an online validator and a URI to the
+ * content
+ */
+class OnlineURIValidator extends OnlineValidator
+{
+  private $uri;
+
+  /**
+   * Constructs an OnlineURIValidator
+   *
+   * @param $uri The uri to validate
+   */
+  function __construct($uri)
+  {
+    $this->uri = $uri;
+  }
+
+  function check()
+  {
+    $request = urlencode($this->uri);
+    $query= '?uri=' . $request;
+
+    $headers = Http::getHeaders($this->validator_url . $query, 5);
+    return $headers['X-W3C-Validator-Status'] === "Valid";
+  }
+
+  /**
+   * Gets the URI for this validator
+   *
+   * @return string with the URI
+   */
+  function getUri()
+  {
+    return $this->uri;
+  }
+
+}
+?>
\ No newline at end of file
--- a/OnlineValidator.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Validator.inc', $baseDir);
-/// @endcond
-
-
-/**
- * Defines a validator which uses an online validator
- */
-abstract class OnlineValidator extends Validator
-{
-  /**
-   * The url where the validator is located
-   */
-  protected $validator_url = 'http://validator.w3.org/check';
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/OnlineValidator.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,21 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Validator.inc.php', $baseDir);
+/// @endcond
+
+
+/**
+ * Defines a validator which uses an online validator
+ */
+abstract class OnlineValidator extends Validator
+{
+  /**
+   * The url where the validator is located
+   */
+  protected $validator_url = 'http://validator.w3.org/check';
+}
+?>
\ No newline at end of file
--- a/Options.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +0,0 @@
-<?php
-/**
- */
-abstract class Cacheable {
-  const NO = 0;
-  const YES = 1;
-  const UNDETERMINED = -1;
-}
-
-/**
- * Contains alle configurable parameters, and "globals"
- *
- * @author Tom Fredrik Blenning Klaussen
- */
-class Options
-{
-  private $defaultLang;
-  private $lang;
-  private $name;
-  private $acceptedLanguages = array();
-  private $inputDefaults = array();
-  private $cache;
-  private $urlParams = array();
-  private $basePath;
-  private $flagUrl;
-  private $baseUrl;
-  private $cacheable = Cacheable::YES;
-
-  /**
-   * Gets the default language
-   *
-   * @return two letter code for the language
-   */
-  function getDefaultLang()
-  {
-    return $this->defaultLang;
-  }
-
-  /**
-   * Gets the selected language
-   *
-   * @return two letter code for the language
-   */
-  function getLang()
-  {
-    return $this->lang;
-  }
-
-  /**
-   *  Sets wether or not this page may be cached
-   *
-   * @param $cacheable Cacheable
-   */
-  function setCacheable($cacheable)
-  {
-    $this->cacheable = $cacheable;
-  }
-
-  /**
-   * Gets wether or not this page may be cached
-   *
-   * @return Cacheable, default is YES
-   */
-  function getCacheable()
-  {
-    return $this->cacheable;
-  }
-
-  /**
-   * Get the base url, or if non set, extracts it from _SERVER
-   *
-   * @return the baseurl for the scripts
-   */
-  function getBaseUrl()
-  {
-    if ($this->baseUrl)
-      return $this->baseUrl;
-
-    $request = $_SERVER['REQUEST_URI'];
-
-    $l = strpos($request, '?');
-    $base = ($l) ? substr($request, 0 , $l) : $request;
-    $base = rtrim($base, '/');
-
-    return "//" . $_SERVER['HTTP_HOST'] . $base;
-  }
-
-  /**
-   * Replaces placeholder variables, with actual values.
-   *
-   * Currently supported values:
-   * @li \%HOST\%
-   *
-   * @param $value string to replace values in
-   * @return the processed string
-   */
-  static function replacePlaceholders($value)
-  {
-    $value = preg_replace('/%HOST%/', $_SERVER['HTTP_HOST'], $value);
-    return $value;
-  }
-
-  /**
-   * Sets the base url where scripts are located
-   *
-   * @param $baseUrl the url where scripts are located
-   */
-  function setBaseUrl($baseUrl)
-  {
-    $baseUrl = self::replacePlaceholders($baseUrl);
-    $this->baseUrl = $baseUrl;
-  }
-
-  /**
-   * Sets the url for the flag script
-   *
-   * @param $flagUrl for flag script
-   */
-  function setFlagUrl($flagUrl)
-  {
-    $flagUrl = self::replacePlaceholders($flagUrl);
-    $this->flagUrl = $flagUrl;
-  }
-
-  /**
-   * Gets the url for the flag script, or autogenerate if not set
-   *
-   * @return url for flag script
-   */
-  function getFlagUrl()
-  {
-    if ($this->flagUrl)
-      return $this->flagUrl;
-
-    return $this->getBaseUrl() . "/flag.php";
-  }
-
-  /**
-   * Sets the selected language
-   *
-   * @param $lang two letter code for the language
-   */
-  function setLang($lang)
-  {
-    $this->lang = $lang;
-  }
-
-  /**
-   * Gets the path where the scripts are located
-   *
-   * @return path where scripts are located
-   */
-  function getBasePath()
-  {
-    return $this->basePath;
-  }
-
-  /**
-   * Sets the path where the scripts are located
-   *
-   * @param $basePath path where scripts are located
-   */
-  function setBasePath($basePath)
-  {
-    $this->basePath = $basePath;
-  }
-
-  /**
-   * Sets a set of urlparameters
-   *
-   * @param $urlParams list of parameters to get from the URL
-   */
-  function setUrlParams($urlParams)
-  {
-    foreach($urlParams as $key) {
-      $value = array_key_exists($key, $_GET) ? $_GET[$key] : '';
-      $this->urlParams[$key] = $value;
-    }
-  }
-
-  /**
-   * Gets the default language
-   *
-   * @return associative array of key and value for the url parameters
-   */
-  function getUrlParams()
-  {
-    return $this->urlParams;
-  }
-
-  /**
-   * Sets the name(identity) for this page
-   *
-   * @param $name name(identity)
-   */
-  function setName($name)
-  {
-    $this->name = $name;
-  }
-
-  /**
-   * Gets the name(identity) for this page
-   *
-   * @return name(identity)
-   */
-  function getName()
-  {
-    return $this->name;
-  }
-
-  /**
-   * Sets a cache object
-   *
-   * @param $cache CacheTimeCheck object
-   */
-  function setCache($cache)
-  {
-    $this->cache = $cache;
-  }
-
-  /**
-   * Gets the cache object
-   *
-   * @return cache object
-   */
-  function getCache()
-  {
-    return $this->cache;
-  }
-
-  /**
-   * A list of languages which this configuration supports.
-   *
-   * @return array of two letter language codes
-   */
-  function getAcceptedLanguages()
-  {
-    return $this->acceptedLanguages;
-  }
-
-  /**
-   * Gets the default value associated whith the key
-   *
-   * @param $key as specified in master xml file.
-   * @return associated default
-   */
-  function getInputDefault($key)
-  {
-    return $this->inputDefaults[$key];
-  }
-
-  /**
-   * Constructs an options object
-   *
-   * This contstructor will consume any tag with the type option, and
-   * extract values from any tag with type input
-   *
-   * @include master.xml
-   *
-   * @param $baseDocument An open xml file
-   */
-  function __construct($baseDocument)
-  {
-    $params = $baseDocument->getElementsByTagName("param");
-    $toRemove = array();
-    foreach ($params as $param) {
-      if ($param->getAttribute("type") == "option") {
-	$id = $param->getAttribute("id");
-	if ($id == "lang") {
-	  $this->defaultLang = $param->getAttribute("default");
-	  $accepts = $param->getElementsByTagName("accept_value");
-	  foreach($accepts as $accept) {
-	    foreach($accept->childNodes as $child) {
-	      array_push($this->acceptedLanguages, $child->nodeValue);
-	    }
-	  }
-	}
-	elseif ($id == "baseUrl") {
-	  $value = $param->getAttribute("value");
-	  if($value)
-	    $this->setBaseUrl($value);
-	}
-	elseif ($id == "flagUrl") {
-	  $value = $param->getAttribute("value");
-	  if($value)
-	    $this->setFlagUrl($value);
-	}
-	else {
-	  warn("Invalid option: ${id}");
-	}
-	//We need to iterate in the opposite direction when removing,
-	//so best shifting.
-	array_unshift($toRemove, $param);
-      }
-      elseif ($param->getAttribute("type") == "input") {
-	$id = $param->getAttribute("id");
-	$default = $param->getAttribute("default");
-	$this->inputDefaults[$id] = $default;
-      }
-    }
-    foreach($toRemove as $param) {
-      $parent = $param->parentNode;
-      $parent->removeChild($param);
-    }
-  }
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Options.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,307 @@
+<?php
+/**
+ */
+abstract class Cacheable {
+  const NO = 0;
+  const YES = 1;
+  const UNDETERMINED = -1;
+}
+
+/**
+ * Contains alle configurable parameters, and "globals"
+ *
+ * @author Tom Fredrik Blenning Klaussen
+ */
+class Options
+{
+  private $defaultLang;
+  private $lang;
+  private $name;
+  private $acceptedLanguages = array();
+  private $inputDefaults = array();
+  private $cache;
+  private $urlParams = array();
+  private $basePath;
+  private $flagUrl;
+  private $baseUrl;
+  private $cacheable = Cacheable::YES;
+
+  /**
+   * Gets the default language
+   *
+   * @return two letter code for the language
+   */
+  function getDefaultLang()
+  {
+    return $this->defaultLang;
+  }
+
+  /**
+   * Gets the selected language
+   *
+   * @return two letter code for the language
+   */
+  function getLang()
+  {
+    return $this->lang;
+  }
+
+  /**
+   *  Sets wether or not this page may be cached
+   *
+   * @param $cacheable Cacheable
+   */
+  function setCacheable($cacheable)
+  {
+    $this->cacheable = $cacheable;
+  }
+
+  /**
+   * Gets wether or not this page may be cached
+   *
+   * @return Cacheable, default is YES
+   */
+  function getCacheable()
+  {
+    return $this->cacheable;
+  }
+
+  /**
+   * Get the base url, or if non set, extracts it from _SERVER
+   *
+   * @return the baseurl for the scripts
+   */
+  function getBaseUrl()
+  {
+    if ($this->baseUrl)
+      return $this->baseUrl;
+
+    $request = $_SERVER['REQUEST_URI'];
+
+    $l = strpos($request, '?');
+    $base = ($l) ? substr($request, 0 , $l) : $request;
+    $base = rtrim($base, '/');
+
+    return "//" . $_SERVER['HTTP_HOST'] . $base;
+  }
+
+  /**
+   * Replaces placeholder variables, with actual values.
+   *
+   * Currently supported values:
+   * @li \%HOST\%
+   *
+   * @param $value string to replace values in
+   * @return the processed string
+   */
+  static function replacePlaceholders($value)
+  {
+    $value = preg_replace('/%HOST%/', $_SERVER['HTTP_HOST'], $value);
+    return $value;
+  }
+
+  /**
+   * Sets the base url where scripts are located
+   *
+   * @param $baseUrl the url where scripts are located
+   */
+  function setBaseUrl($baseUrl)
+  {
+    $baseUrl = self::replacePlaceholders($baseUrl);
+    $this->baseUrl = $baseUrl;
+  }
+
+  /**
+   * Sets the url for the flag script
+   *
+   * @param $flagUrl for flag script
+   */
+  function setFlagUrl($flagUrl)
+  {
+    $flagUrl = self::replacePlaceholders($flagUrl);
+    $this->flagUrl = $flagUrl;
+  }
+
+  /**
+   * Gets the url for the flag script, or autogenerate if not set
+   *
+   * @return url for flag script
+   */
+  function getFlagUrl()
+  {
+    if ($this->flagUrl)
+      return $this->flagUrl;
+
+    return $this->getBaseUrl() . "/flag.php";
+  }
+
+  /**
+   * Sets the selected language
+   *
+   * @param $lang two letter code for the language
+   */
+  function setLang($lang)
+  {
+    $this->lang = $lang;
+  }
+
+  /**
+   * Gets the path where the scripts are located
+   *
+   * @return path where scripts are located
+   */
+  function getBasePath()
+  {
+    return $this->basePath;
+  }
+
+  /**
+   * Sets the path where the scripts are located
+   *
+   * @param $basePath path where scripts are located
+   */
+  function setBasePath($basePath)
+  {
+    $this->basePath = $basePath;
+  }
+
+  /**
+   * Sets a set of urlparameters
+   *
+   * @param $urlParams list of parameters to get from the URL
+   */
+  function setUrlParams($urlParams)
+  {
+    foreach($urlParams as $key) {
+      $value = array_key_exists($key, $_GET) ? $_GET[$key] : '';
+      $this->urlParams[$key] = $value;
+    }
+  }
+
+  /**
+   * Gets the default language
+   *
+   * @return associative array of key and value for the url parameters
+   */
+  function getUrlParams()
+  {
+    return $this->urlParams;
+  }
+
+  /**
+   * Sets the name(identity) for this page
+   *
+   * @param $name name(identity)
+   */
+  function setName($name)
+  {
+    $this->name = $name;
+  }
+
+  /**
+   * Gets the name(identity) for this page
+   *
+   * @return name(identity)
+   */
+  function getName()
+  {
+    return $this->name;
+  }
+
+  /**
+   * Sets a cache object
+   *
+   * @param $cache CacheTimeCheck object
+   */
+  function setCache($cache)
+  {
+    $this->cache = $cache;
+  }
+
+  /**
+   * Gets the cache object
+   *
+   * @return cache object
+   */
+  function getCache()
+  {
+    return $this->cache;
+  }
+
+  /**
+   * A list of languages which this configuration supports.
+   *
+   * @return array of two letter language codes
+   */
+  function getAcceptedLanguages()
+  {
+    return $this->acceptedLanguages;
+  }
+
+  /**
+   * Gets the default value associated whith the key
+   *
+   * @param $key as specified in master xml file.
+   * @return associated default
+   */
+  function getInputDefault($key)
+  {
+    return $this->inputDefaults[$key];
+  }
+
+  /**
+   * Constructs an options object
+   *
+   * This contstructor will consume any tag with the type option, and
+   * extract values from any tag with type input
+   *
+   * @include master.xml
+   *
+   * @param $baseDocument An open xml file
+   */
+  function __construct($baseDocument)
+  {
+    $params = $baseDocument->getElementsByTagName("param");
+    $toRemove = array();
+    foreach ($params as $param) {
+      if ($param->getAttribute("type") == "option") {
+	$id = $param->getAttribute("id");
+	if ($id == "lang") {
+	  $this->defaultLang = $param->getAttribute("default");
+	  $accepts = $param->getElementsByTagName("accept_value");
+	  foreach($accepts as $accept) {
+	    foreach($accept->childNodes as $child) {
+	      array_push($this->acceptedLanguages, $child->nodeValue);
+	    }
+	  }
+	}
+	elseif ($id == "baseUrl") {
+	  $value = $param->getAttribute("value");
+	  if($value)
+	    $this->setBaseUrl($value);
+	}
+	elseif ($id == "flagUrl") {
+	  $value = $param->getAttribute("value");
+	  if($value)
+	    $this->setFlagUrl($value);
+	}
+	else {
+	  warn("Invalid option: ${id}");
+	}
+	//We need to iterate in the opposite direction when removing,
+	//so best shifting.
+	array_unshift($toRemove, $param);
+      }
+      elseif ($param->getAttribute("type") == "input") {
+	$id = $param->getAttribute("id");
+	$default = $param->getAttribute("default");
+	$this->inputDefaults[$id] = $default;
+      }
+    }
+    foreach($toRemove as $param) {
+      $parent = $param->parentNode;
+      $parent->removeChild($param);
+    }
+  }
+}
+?>
--- a/Page.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,231 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('OnlineBufferValidator.inc', $baseDir);
-$cache->includeOnce('Options.inc', $baseDir);
-/// @endcond
-
-/**
- * Container for headers and content, used for compounding these
- */
-class PageContent
-{
-  /// The headers for the page
-  public $headers = array();
-  /// The content of the page
-  public $content;
-
-  /**
-   * Constructs a PageContent object containing only content
-   *
-   * @param $content The content of this page
-   */
-  function __construct($content = "")
-  {
-    $this->content = $content;
-  }
-
-  /**
-   * Sets a header
-   *
-   * @param $headername Name of the header
-   * @param $value Value for the header
-   */
-  function setHeader($headername, $value)
-  {
-    $this->headers[$headername] = $value;
-  }
-
-  /**
-   * Returns a string representation of this object, containing only
-   * the content
-   *
-   * @return the content of the page
-   */
-  function __toString()
-  {
-    return $this->content;
-  }
-}
-
-/**
- * Master class for generating a page
- */
-abstract class Page
-{
-  private $cache;
-  private $variants = array();
-
-  function addVariant($value)
-  {
-    array_push($this->variants, $value);
-  }
-
-  function getVariants()
-  {
-    return $this->variants;
-  }
-
-  /**
-   * Constructs a page
-   *
-   * @param $cache optionally sets a cache
-   */
-  function __construct($cache = null)
-  {
-    $this->setCache($cache);
-  }
-
-  /**
-   * Set the cache
-   *
-   * @param $cache The cache object
-   */
-  protected function setCache($cache)
-  {
-    $this->cache = $cache;
-  }
-
-  /**
-   * Get the cache
-   *
-   * @return The cache object
-   */
-  protected function getCache()
-  {
-    return $this->cache;
-  }
-
-  /**
-   * Decide wether or not this page may be compressed.
-   *
-   * Normally this is a check for http headers, but some pages
-   * eg. pictures may not want to be compressed, and may override this
-   * function.
-   *
-   * @return bool if this page may be compressed
-   */
-  function mayCompress()
-  {
-    if (COMPRESSION_DISABLED)
-      return false;
-    //We want to add the variant even if we don't serve with compression.
-    $this->addVariant('Accept-Encoding');
-    if (!array_key_exists('HTTP_ACCEPT_ENCODING', $_SERVER))
-      return false;
-    return (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'));
-  }
-
-  /**
-   * Decide wether or not this page may be validated.
-   *
-   * Normally this is a check for the option novalidate, but this may
-   * be overridden
-   *
-   * @return bool if this page may be validated
-   */
-  function mayValidate()
-  {
-    if (!VALIDATE)
-      return false;
-    if (array_key_exists('novalidate', $_GET))
-      return !$_GET['novalidate'];
-    if (!array_key_exists('HTTP_USER_AGENT', $_SERVER))
-      return false;
-    //UserAgent should be W3C_Validator/1.3
-    return !startswith($_SERVER['HTTP_USER_AGENT'], 'W3C');
-  }
-
-
-  /**
-   * Turns on compression for this page
-   *
-   * @note This may not be reversed
-   */
-  function startCompression()
-  {
-    ob_start("ob_gzhandler");
-  }
-
-  /**
-   * Generates the actual content of the page
-   *
-   * @return the content buffer
-   */
-  abstract function generateContent();
-
-  /**
-   * Finishes all necessary processing to determine the cacheset of this page.
-   *
-   * @return bool if this page may be cached
-   */
-  abstract function cacheCheck();
-
-  /**
-   * Generates an appropriate response to the request.
-   *
-   * Eg. 304 NOT CHANGED, error message or the actual content
-   */
-  function genPage()
-  {
-    $cacheable = $this->cacheCheck();
-    if ($cacheable == Cacheable::YES) {
-      if (!CheckHttpModified($this->cache))
-	return false;
-    }
-    $res = $this->generateContent();
-    if ($cacheable == Cacheable::UNDETERMINED) {
-      $cacheable = $this->cacheCheck();
-      if ($cacheable == Cacheable::YES) {
-	if (!CheckHttpModified($this->cache))
-	  return false;
-      }
-    }
-
-    if ($this->mayValidate()) {
-      /*
-      $request = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
-      $validator = new OnlineUriValidator($request);
-      */
-      $validator = new OnlineBufferValidator($res);
-      if (!$validator->check())
-	throw new LogicException('The page could be generated, but contained errors');
-    }
-    if ($this->mayCompress()) {
-      $this->startCompression();
-    }
-    $t = gettype($res);
-    if ($t === "string") {
-      $res = new PageContent($res);
-    }
-    elseif (get_class($res) !== "PageContent") {
-      throw new InvalidArgumentException("generateContent returned an unexpected type");
-    }
-    if ($variants = $this->getVariants()) {
-      $res->setHeader('Vary', join(",", $variants));
-    }
-    return $res;
-  }
-
-  /**
-   * Displays the result from genPage.
-   *
-   * Printing headers and content.
-   */
-  function display()
-  {
-    $res = $this->genPage();
-    if ($res) {
-      foreach ($res->headers as $header => $value) {
-	header("${header}: ${value}");
-      }
-      print $res;
-    }
-  }
-
-
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Page.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,231 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('OnlineBufferValidator.inc.php', $baseDir);
+$cache->includeOnce('Options.inc.php', $baseDir);
+/// @endcond
+
+/**
+ * Container for headers and content, used for compounding these
+ */
+class PageContent
+{
+  /// The headers for the page
+  public $headers = array();
+  /// The content of the page
+  public $content;
+
+  /**
+   * Constructs a PageContent object containing only content
+   *
+   * @param $content The content of this page
+   */
+  function __construct($content = "")
+  {
+    $this->content = $content;
+  }
+
+  /**
+   * Sets a header
+   *
+   * @param $headername Name of the header
+   * @param $value Value for the header
+   */
+  function setHeader($headername, $value)
+  {
+    $this->headers[$headername] = $value;
+  }
+
+  /**
+   * Returns a string representation of this object, containing only
+   * the content
+   *
+   * @return the content of the page
+   */
+  function __toString()
+  {
+    return $this->content;
+  }
+}
+
+/**
+ * Master class for generating a page
+ */
+abstract class Page
+{
+  private $cache;
+  private $variants = array();
+
+  function addVariant($value)
+  {
+    array_push($this->variants, $value);
+  }
+
+  function getVariants()
+  {
+    return $this->variants;
+  }
+
+  /**
+   * Constructs a page
+   *
+   * @param $cache optionally sets a cache
+   */
+  function __construct($cache = null)
+  {
+    $this->setCache($cache);
+  }
+
+  /**
+   * Set the cache
+   *
+   * @param $cache The cache object
+   */
+  protected function setCache($cache)
+  {
+    $this->cache = $cache;
+  }
+
+  /**
+   * Get the cache
+   *
+   * @return The cache object
+   */
+  protected function getCache()
+  {
+    return $this->cache;
+  }
+
+  /**
+   * Decide wether or not this page may be compressed.
+   *
+   * Normally this is a check for http headers, but some pages
+   * eg. pictures may not want to be compressed, and may override this
+   * function.
+   *
+   * @return bool if this page may be compressed
+   */
+  function mayCompress()
+  {
+    if (COMPRESSION_DISABLED)
+      return false;
+    //We want to add the variant even if we don't serve with compression.
+    $this->addVariant('Accept-Encoding');
+    if (!array_key_exists('HTTP_ACCEPT_ENCODING', $_SERVER))
+      return false;
+    return (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip'));
+  }
+
+  /**
+   * Decide wether or not this page may be validated.
+   *
+   * Normally this is a check for the option novalidate, but this may
+   * be overridden
+   *
+   * @return bool if this page may be validated
+   */
+  function mayValidate()
+  {
+    if (!VALIDATE)
+      return false;
+    if (array_key_exists('novalidate', $_GET))
+      return !$_GET['novalidate'];
+    if (!array_key_exists('HTTP_USER_AGENT', $_SERVER))
+      return false;
+    //UserAgent should be W3C_Validator/1.3
+    return !startswith($_SERVER['HTTP_USER_AGENT'], 'W3C');
+  }
+
+
+  /**
+   * Turns on compression for this page
+   *
+   * @note This may not be reversed
+   */
+  function startCompression()
+  {
+    ob_start("ob_gzhandler");
+  }
+
+  /**
+   * Generates the actual content of the page
+   *
+   * @return the content buffer
+   */
+  abstract function generateContent();
+
+  /**
+   * Finishes all necessary processing to determine the cacheset of this page.
+   *
+   * @return bool if this page may be cached
+   */
+  abstract function cacheCheck();
+
+  /**
+   * Generates an appropriate response to the request.
+   *
+   * Eg. 304 NOT CHANGED, error message or the actual content
+   */
+  function genPage()
+  {
+    $cacheable = $this->cacheCheck();
+    if ($cacheable == Cacheable::YES) {
+      if (!CheckHttpModified($this->cache))
+	return false;
+    }
+    $res = $this->generateContent();
+    if ($cacheable == Cacheable::UNDETERMINED) {
+      $cacheable = $this->cacheCheck();
+      if ($cacheable == Cacheable::YES) {
+	if (!CheckHttpModified($this->cache))
+	  return false;
+      }
+    }
+
+    if ($this->mayValidate()) {
+      /*
+      $request = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
+      $validator = new OnlineUriValidator($request);
+      */
+      $validator = new OnlineBufferValidator($res);
+      if (!$validator->check())
+	throw new LogicException('The page could be generated, but contained errors');
+    }
+    if ($this->mayCompress()) {
+      $this->startCompression();
+    }
+    $t = gettype($res);
+    if ($t === "string") {
+      $res = new PageContent($res);
+    }
+    elseif (get_class($res) !== "PageContent") {
+      throw new InvalidArgumentException("generateContent returned an unexpected type");
+    }
+    if ($variants = $this->getVariants()) {
+      $res->setHeader('Vary', join(",", $variants));
+    }
+    return $res;
+  }
+
+  /**
+   * Displays the result from genPage.
+   *
+   * Printing headers and content.
+   */
+  function display()
+  {
+    $res = $this->genPage();
+    if ($res) {
+      foreach ($res->headers as $header => $value) {
+	header("${header}: ${value}");
+      }
+      print $res;
+    }
+  }
+
+
+}
+?>
--- a/ScriptIncludeCache.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<?php
-/**
- * @file
- *
- * Since ScriptIncludeCache is defined in FileCacheSet.inc, we simply
- * include it here to provide a common class include interface
- */
-include_once 'FileCacheSet.inc';
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ScriptIncludeCache.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,9 @@
+<?php
+/**
+ * @file
+ *
+ * Since ScriptIncludeCache is defined in FileCacheSet.inc.php, we simply
+ * include it here to provide a common class include interface
+ */
+include_once 'FileCacheSet.inc.php';
+?>
\ No newline at end of file
--- a/Sitemap.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Http.inc', $baseDir);
-$cache->includeOnce('Page.inc', $baseDir);
-$cache->includeOnce('common-functions.inc', $baseDir);
-$cache->includeOnce('CacheTimeCheck.inc', $baseDir);
-/// @endcond
-
-class SimpleCache extends Cache {
-  private $time;
-
-  function __construct($time = 0) {
-    $this->time = $time;
-  }
-  function getNewest() {
-    return $this->time;
-  }
-};
-
-function cmp_length_lex($a, $b)
-{
-  if ($a == $b) {
-    return 0;
-  }
-  $la = strlen($a);
-  $lb = strlen($b);
-  if ($la == $lb) {
-    return ($la < $lb) ? -1 : 1;
-  }
-  return ($a < $b) ? -1 : 1;
-}
-
-
-/**
- * Functionality for generating a sitemap
- */
-class Sitemap extends Page
-{
-  private $master;
-  private $options;
-
-  /**
-   * Constructs a sitemap object from a master document
-   *
-   * @param $path location of master document
-   */
-  function __construct($path) {
-    $this->master = new DOMDocument();
-    $this->master->load($path);
-
-    $this->options = new Options($this->master);
-    $this->lastmod=0;
-  }
-
-  function cacheCheck()
-  {
-    if ($this->lastmod == 0)
-      return Cacheable::UNDETERMINED;
-    else
-      return Cacheable::YES;
-  }
-
-  function mayValidate()
-  {
-    return false;
-  }
-
-  private function processDir($dir, $lang, $acceptedLanguages, $baseurl) {
-    $urls = array();
-
-    $base=basePath();
-    $base=$_SERVER['DOCUMENT_ROOT'];
-
-    if ($handle = opendir($base . "/${dir}")) {
-      while (false !== ($entry = readdir($handle))) {
-	if (endsWith($entry, '.xml')) {
-	  $fentry = $base . "/${dir}/${entry}";
-	  $doc = new DOMDocument();
-
-	  if (file_exists($fentry)) {
-	    $doc->load($fentry);
-
-	    $opts = array();
-	    if (count($acceptedLanguages) > 1) {
-	      $opts['lang'] = $lang;
-	    }
-
-	    $toplevel = $doc->getElementsByTagName("toplevel");
-
-	    if($toplevel->length) {
-	      $name = substr($entry, 0, -4);
-
-	      if ($name != $this->options->getInputDefault('name')) {
-		$opts['name'] = $name;
-	      }
-
-	      $optstring = genUrl($opts, array(), array('lang', 'name'));
-	      $location = "${baseurl}${optstring}";
-	      array_push($urls, $location);
-	    }
-	  }
-	}
-      }
-      closedir($handle);
-    }
-    return $urls;
-  }
-
-  function generateContent() {
-
-    /// The final output variable
-    $out = '<?xml version="1.0" encoding="UTF-8"?>';
-    $out .= "\n";
-    $out .= '<?xml-stylesheet type="text/xsl" href="/css/gss.xsl"?>';
-    $out .= "\n";
-    $out .= '<urlset
-      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
-      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
-            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
-';
-
-    $proto= $_SERVER["REQUEST_SCHEME"];
-    if (array_key_exists('HTTP_X_FORWARDED_PROTO', $_SERVER)) {
-      $proto=$_SERVER['HTTP_X_FORWARDED_PROTO'];
-    }
-    $base = $proto ."://". $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
-    $l = strrpos($base, '/');
-    if ($l)
-      $base = substr($base, 0, $l);
-
-    $acceptedLanguages = $this->options->getAcceptedLanguages();
-
-    $urls = array();
-
-    foreach($this->options->getAcceptedLanguages() as $lang) {
-      $urls=array_merge($urls,
-			$this->processDir($lang, $lang,
-					  $acceptedLanguages, $base)
-			);
-      $urls=array_merge($urls,
-			$this->processDir("common", $lang,
-					  $acceptedLanguages, $base)
-			);
-    }
-
-    usort($urls, "cmp_length_lex");
-
-    foreach($urls as $location) {
-      $headers = Http::getHeaders($location, 5);
-
-      $location = htmlentities($location);
-
-      if (array_key_exists('Last-Modified', $headers)) {
-	$lastmod = $headers["Last-Modified"];
-      }
-
-      $n = StatusCodes::codeFromHeader($headers['']);
-
-      if ($n == StatusCodes::HTTP_OK) {
-	if (isset($lastmod)) {
-
-	  $lastmod = strtotime($lastmod);
-	  if ($lastmod > $this->lastmod) {
-	    $this->lastmod = $lastmod;
-	  }
-	  $lastmod = date(DateTime::W3C, $lastmod);
-	}
-
-	$out .= "<url>\n";
-	$out .= "<loc>${location}</loc>\n";
-	if (isset($lastmod)) {
-	  $out .= "<lastmod>${lastmod}</lastmod>\n";
-	}
-	$out .= "</url>\n";
-      }
-    }
-
-    $out .= "</urlset>";
-
-    $res = new PageContent($out);
-    $cache=new SimpleCache($this->lastmod);
-    $cache->setMaxAge(86400);
-    $this->setCache($cache);
-    $res->setHeader('Content-type', 'application/xml');
-    return $res;
-  }
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Sitemap.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,193 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('Http.inc.php', $baseDir);
+$cache->includeOnce('Page.inc.php', $baseDir);
+$cache->includeOnce('common-functions.inc.php', $baseDir);
+$cache->includeOnce('CacheTimeCheck.inc.php', $baseDir);
+/// @endcond
+
+class SimpleCache extends Cache {
+  private $time;
+
+  function __construct($time = 0) {
+    $this->time = $time;
+  }
+  function getNewest() {
+    return $this->time;
+  }
+};
+
+function cmp_length_lex($a, $b)
+{
+  if ($a == $b) {
+    return 0;
+  }
+  $la = strlen($a);
+  $lb = strlen($b);
+  if ($la == $lb) {
+    return ($la < $lb) ? -1 : 1;
+  }
+  return ($a < $b) ? -1 : 1;
+}
+
+
+/**
+ * Functionality for generating a sitemap
+ */
+class Sitemap extends Page
+{
+  private $master;
+  private $options;
+
+  /**
+   * Constructs a sitemap object from a master document
+   *
+   * @param $path location of master document
+   */
+  function __construct($path) {
+    $this->master = new DOMDocument();
+    $this->master->load($path);
+
+    $this->options = new Options($this->master);
+    $this->lastmod=0;
+  }
+
+  function cacheCheck()
+  {
+    if ($this->lastmod == 0)
+      return Cacheable::UNDETERMINED;
+    else
+      return Cacheable::YES;
+  }
+
+  function mayValidate()
+  {
+    return false;
+  }
+
+  private function processDir($dir, $lang, $acceptedLanguages, $baseurl) {
+    $urls = array();
+
+    $base=basePath();
+    $base=$_SERVER['DOCUMENT_ROOT'];
+
+    if ($handle = opendir($base . "/${dir}")) {
+      while (false !== ($entry = readdir($handle))) {
+	if (endsWith($entry, '.xml')) {
+	  $fentry = $base . "/${dir}/${entry}";
+	  $doc = new DOMDocument();
+
+	  if (file_exists($fentry)) {
+	    $doc->load($fentry);
+
+	    $opts = array();
+	    if (count($acceptedLanguages) > 1) {
+	      $opts['lang'] = $lang;
+	    }
+
+	    $toplevel = $doc->getElementsByTagName("toplevel");
+
+	    if($toplevel->length) {
+	      $name = substr($entry, 0, -4);
+
+	      if ($name != $this->options->getInputDefault('name')) {
+		$opts['name'] = $name;
+	      }
+
+	      $optstring = genUrl($opts, array(), array('lang', 'name'));
+	      $location = "${baseurl}${optstring}";
+	      array_push($urls, $location);
+	    }
+	  }
+	}
+      }
+      closedir($handle);
+    }
+    return $urls;
+  }
+
+  function generateContent() {
+
+    /// The final output variable
+    $out = '<?xml version="1.0" encoding="UTF-8"?>';
+    $out .= "\n";
+    $out .= '<?xml-stylesheet type="text/xsl" href="/css/gss.xsl"?>';
+    $out .= "\n";
+    $out .= '<urlset
+      xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
+            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
+';
+
+    $proto= $_SERVER["REQUEST_SCHEME"];
+    if (array_key_exists('HTTP_X_FORWARDED_PROTO', $_SERVER)) {
+      $proto=$_SERVER['HTTP_X_FORWARDED_PROTO'];
+    }
+    $base = $proto ."://". $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
+    $l = strrpos($base, '/');
+    if ($l)
+      $base = substr($base, 0, $l);
+
+    $acceptedLanguages = $this->options->getAcceptedLanguages();
+
+    $urls = array();
+
+    foreach($this->options->getAcceptedLanguages() as $lang) {
+      $urls=array_merge($urls,
+			$this->processDir($lang, $lang,
+					  $acceptedLanguages, $base)
+			);
+      $urls=array_merge($urls,
+			$this->processDir("common", $lang,
+					  $acceptedLanguages, $base)
+			);
+    }
+
+    usort($urls, "cmp_length_lex");
+
+    foreach($urls as $location) {
+      $headers = Http::getHeaders($location, 5);
+
+      $location = htmlentities($location);
+
+      if (array_key_exists('Last-Modified', $headers)) {
+	$lastmod = $headers["Last-Modified"];
+      }
+
+      $n = StatusCodes::codeFromHeader($headers['']);
+
+      if ($n == StatusCodes::HTTP_OK) {
+	if (isset($lastmod)) {
+
+	  $lastmod = strtotime($lastmod);
+	  if ($lastmod > $this->lastmod) {
+	    $this->lastmod = $lastmod;
+	  }
+	  $lastmod = date(DateTime::W3C, $lastmod);
+	}
+
+	$out .= "<url>\n";
+	$out .= "<loc>${location}</loc>\n";
+	if (isset($lastmod)) {
+	  $out .= "<lastmod>${lastmod}</lastmod>\n";
+	}
+	$out .= "</url>\n";
+      }
+    }
+
+    $out .= "</urlset>";
+
+    $res = new PageContent($out);
+    $cache=new SimpleCache($this->lastmod);
+    $cache->setMaxAge(86400);
+    $this->setCache($cache);
+    $res->setHeader('Content-type', 'application/xml');
+    return $res;
+  }
+}
+?>
--- a/StatusCodes.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +0,0 @@
-<?php
-/**
- * StatusCodes provides named constants for
- * HTTP protocol status codes. Written for the
- * Recess Framework (http://www.recessframework.com/)
- *
- * @author Kris Jordan
- * @author Tom Fredrik Blenning Klaussen
- * @copyright MIT
- */
-class StatusCodes
-{
-  // [Informational 1xx]
-  /**
-   * <a href="http://httpstatus.es/100">HTTP_CONTINUE</a>
-   */
-  const HTTP_CONTINUE = 100;
-  /**
-   * <a href="http://httpstatus.es/101">HTTP_SWITCHING_PROTOCOLS</a>
-   */
-  const HTTP_SWITCHING_PROTOCOLS = 101;
-
-  // [Successful 2xx]
-  /**
-   * <a href="http://httpstatus.es/200">HTTP_OK</a>
-   */
-  const HTTP_OK = 200;
-  /**
-   * <a href="http://httpstatus.es/201">HTTP_CREATED</a>
-   */
-  const HTTP_CREATED = 201;
-  /**
-   * <a href="http://httpstatus.es/202">HTTP_ACCEPTED</a>
-   */
-  const HTTP_ACCEPTED = 202;
-  /**
-   * <a href="http://httpstatus.es/203">HTTP_ACCEPTED</a>
-   */
-  const HTTP_NONAUTHORITATIVE_INFORMATION = 203;
-  /**
-   * <a href="http://httpstatus.es/204">HTTP_NO_CONTENT</a>
-   */
-  const HTTP_NO_CONTENT = 204;
-  /**
-   * <a href="http://httpstatus.es/205">HTTP_RESET_CONTENT</a>
-   */
-  const HTTP_RESET_CONTENT = 205;
-  /**
-   * <a href="http://httpstatus.es/206">HTTP_PARTIAL_CONTENT</a>
-   */
-  const HTTP_PARTIAL_CONTENT = 206;
-
-  // [Redirection 3xx]
-  /**
-   * <a href="http://httpstatus.es/300">HTTP_MULTIPLE_CHOICES</a>
-   */
-  const HTTP_MULTIPLE_CHOICES = 300;
-  /**
-   * <a href="http://httpstatus.es/301">HTTP_MOVED_PERMANENTLY</a>
-   */
-  const HTTP_MOVED_PERMANENTLY = 301;
-  /**
-   * <a href="http://httpstatus.es/302">HTTP_FOUND</a>
-   */
-  const HTTP_FOUND = 302;
-  /**
-   * <a href="http://httpstatus.es/303">HTTP_SEE_OTHER</a>
-   */
-  const HTTP_SEE_OTHER = 303;
-  /**
-   * <a href="http://httpstatus.es/304">HTTP_NOT_MODIFIED</a>
-   */
-  const HTTP_NOT_MODIFIED = 304;
-  /**
-   * <a href="http://httpstatus.es/305">HTTP_USE_PROXY</a>
-   */
-  const HTTP_USE_PROXY = 305;
-  /**
-   * <a href="http://httpstatus.es/306">HTTP_UNUSED</a>
-   */
-  const HTTP_UNUSED = 306;
-  /**
-   * <a href="http://httpstatus.es/307">HTTP_TEMPORARY_REDIRECT</a>
-   */
-  const HTTP_TEMPORARY_REDIRECT = 307;
-
-  // [Client Error 4xx]
-  /**
-   * Defines the beginning of errorCodes
-   * @private
-   */
-  const errorCodesBeginAt = 400;
-  /**
-   * <a href="http://httpstatus.es/400">HTTP_BAD_REQUEST</a>
-   */
-  const HTTP_BAD_REQUEST = 400;
-  /**
-   * <a href="http://httpstatus.es/401">HTTP_UNAUTHORIZED</a>
-   */
-  const HTTP_UNAUTHORIZED = 401;
-  /**
-   * <a href="http://httpstatus.es/402">HTTP_PAYMENT_REQUIRED</a>
-   */
-  const HTTP_PAYMENT_REQUIRED = 402;
-  /**
-   * <a href="http://httpstatus.es/403">HTTP_FORBIDDEN</a>
-   */
-  const HTTP_FORBIDDEN = 403;
-  /**
-   * <a href="http://httpstatus.es/404">HTTP_NOT_FOUND</a>
-   */
-  const HTTP_NOT_FOUND = 404;
-  /**
-   * <a href="http://httpstatus.es/405">HTTP_METHOD_NOT_ALLOWED</a>
-   */
-  const HTTP_METHOD_NOT_ALLOWED = 405;
-  /**
-   * <a href="http://httpstatus.es/406">HTTP_NOT_ACCEPTABLE</a>
-   */
-  const HTTP_NOT_ACCEPTABLE = 406;
-  /**
-   * <a href="http://httpstatus.es/407">HTTP_PROXY_AUTHENTICATION_REQUIRED</a>
-   */
-  const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
-  /**
-   * <a href="http://httpstatus.es/408">HTTP_REQUEST_TIMEOUT</a>
-   */
-  const HTTP_REQUEST_TIMEOUT = 408;
-  /**
-   * <a href="http://httpstatus.es/409">HTTP_CONFLICT</a>
-   */
-  const HTTP_CONFLICT = 409;
-  /**
-   * <a href="http://httpstatus.es/410">HTTP_GONE</a>
-   */
-  const HTTP_GONE = 410;
-  /**
-   * <a href="http://httpstatus.es/411">HTTP_LENGTH_REQUIRED</a>
-   */
-  const HTTP_LENGTH_REQUIRED = 411;
-  /**
-   * <a href="http://httpstatus.es/412">HTTP_PRECONDITION_FAILED</a>
-   */
-  const HTTP_PRECONDITION_FAILED = 412;
-  /**
-   * <a href="http://httpstatus.es/413">HTTP_REQUEST_ENTITY_TOO_LARGE</a>
-   */
-  const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
-  /**
-   * <a href="http://httpstatus.es/414">HTTP_REQUEST_URI_TOO_LONG</a>
-   */
-  const HTTP_REQUEST_URI_TOO_LONG = 414;
-  /**
-   * <a href="http://httpstatus.es/415">HTTP_UNSUPPORTED_MEDIA_TYPE</a>
-   */
-  const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
-  /**
-   * <a href="http://httpstatus.es/416">HTTP_REQUESTED_RANGE_NOT_SATISFIABLE</a>
-   */
-  const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
-  /**
-   * <a href="http://httpstatus.es/417">HTTP_EXPECTATION_FAILED</a>
-   */
-  const HTTP_EXPECTATION_FAILED = 417;
-
-  // [Server Error 5xx]
-  /**
-   * <a href="http://httpstatus.es/500">HTTP_INTERNAL_SERVER_ERROR</a>
-   */
-  const HTTP_INTERNAL_SERVER_ERROR = 500;
-  /**
-   * <a href="http://httpstatus.es/501">HTTP_NOT_IMPLEMENTED</a>
-   */
-  const HTTP_NOT_IMPLEMENTED = 501;
-  /**
-   * <a href="http://httpstatus.es/502">HTTP_BAD_GATEWAY</a>
-   */
-  const HTTP_BAD_GATEWAY = 502;
-  /**
-   * <a href="http://httpstatus.es/503">HTTP_SERVICE_UNAVAILABLE</a>
-   */
-  const HTTP_SERVICE_UNAVAILABLE = 503;
-  /**
-   * <a href="http://httpstatus.es/504">HTTP_GATEWAY_TIMEOUT</a>
-   */
-  const HTTP_GATEWAY_TIMEOUT = 504;
-  /**
-   * <a href="http://httpstatus.es/505">HTTP_VERSION_NOT_SUPPORTED</a>
-   */
-  const HTTP_VERSION_NOT_SUPPORTED = 505;
-
-  private static $messages =
-    array(
-	  // [Informational 1xx]
-	  100=>'100 Continue',
-	  101=>'101 Switching Protocols',
-
-	  // [Successful 2xx]
-	  200=>'200 OK',
-	  201=>'201 Created',
-	  202=>'202 Accepted',
-	  203=>'203 Non-Authoritative Information',
-	  204=>'204 No Content',
-	  205=>'205 Reset Content',
-	  206=>'206 Partial Content',
-
-	  // [Redirection 3xx]
-	  300=>'300 Multiple Choices',
-	  301=>'301 Moved Permanently',
-	  302=>'302 Found',
-	  303=>'303 See Other',
-	  304=>'304 Not Modified',
-	  305=>'305 Use Proxy',
-	  306=>'306 (Unused)',
-	  307=>'307 Temporary Redirect',
-
-	  // [Client Error 4xx]
-	  400=>'400 Bad Request',
-	  401=>'401 Unauthorized',
-	  402=>'402 Payment Required',
-	  403=>'403 Forbidden',
-	  404=>'404 Not Found',
-	  405=>'405 Method Not Allowed',
-	  406=>'406 Not Acceptable',
-	  407=>'407 Proxy Authentication Required',
-	  408=>'408 Request Timeout',
-	  409=>'409 Conflict',
-	  410=>'410 Gone',
-	  411=>'411 Length Required',
-	  412=>'412 Precondition Failed',
-	  413=>'413 Request Entity Too Large',
-	  414=>'414 Request-URI Too Long',
-	  415=>'415 Unsupported Media Type',
-	  416=>'416 Requested Range Not Satisfiable',
-	  417=>'417 Expectation Failed',
-
-	  // [Server Error 5xx]
-	  500=>'500 Internal Server Error',
-	  501=>'501 Not Implemented',
-	  502=>'502 Bad Gateway',
-	  503=>'503 Service Unavailable',
-	  504=>'504 Gateway Timeout',
-	  505=>'505 HTTP Version Not Supported'
-	  );
-
-  /**
-   * Get the header for the specified code.
-   *
-   * @param $code Http status code
-   * @return A textual representation of the header
-   */
-  public static function httpHeaderFor($code)
-  {
-    return 'HTTP/1.1 ' . self::$messages[$code];
-  }
-
-  /**
-   * Get a canonical status message for the specified code
-   *
-   * @param $code Http status code
-   * @return Text for the specified code
-   */
-  public static function getMessageForCode($code)
-  {
-    return self::$messages[$code];
-  }
-
-  /**
-   * Checks if the specified code is an error code.
-   *
-   * @param $code Http status code
-   * @return bool Answer
-   */
-  public static function isError($code)
-  {
-    return is_numeric($code) && $code >= self::HTTP_BAD_REQUEST;
-  }
-
-  /**
-   * Can the specified status code have a body?
-   *
-   * @param $code Http status code
-   * @return bool Answer
-   */
-  public static function canHaveBody($code)
-  {
-    return
-      // True if not in 100s
-      ($code < self::HTTP_CONTINUE || $code >= self::HTTP_OK)
-      && // and not 204 NO CONTENT
-      $code != self::HTTP_NO_CONTENT
-      && // and not 304 NOT MODIFIED
-      $code != self::HTTP_NOT_MODIFIED;
-  }
-
-  /**
-   * Extract the numeric code from a header
-   *
-   * @param $header an http top header
-   */
-  public static function codeFromHeader($header)
-  {
-    $matches = array();
-    preg_match('/HTTP\/\S+\s(\d+)/', $header, $matches);
-    if (count($matches) < 1)
-      throw new InvalidArgumentException("Not an http header");
-    $n = $matches[1];
-    return $n;
-  }
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/StatusCodes.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,311 @@
+<?php
+/**
+ * StatusCodes provides named constants for
+ * HTTP protocol status codes. Written for the
+ * Recess Framework (http://www.recessframework.com/)
+ *
+ * @author Kris Jordan
+ * @author Tom Fredrik Blenning Klaussen
+ * @copyright MIT
+ */
+class StatusCodes
+{
+  // [Informational 1xx]
+  /**
+   * <a href="http://httpstatus.es/100">HTTP_CONTINUE</a>
+   */
+  const HTTP_CONTINUE = 100;
+  /**
+   * <a href="http://httpstatus.es/101">HTTP_SWITCHING_PROTOCOLS</a>
+   */
+  const HTTP_SWITCHING_PROTOCOLS = 101;
+
+  // [Successful 2xx]
+  /**
+   * <a href="http://httpstatus.es/200">HTTP_OK</a>
+   */
+  const HTTP_OK = 200;
+  /**
+   * <a href="http://httpstatus.es/201">HTTP_CREATED</a>
+   */
+  const HTTP_CREATED = 201;
+  /**
+   * <a href="http://httpstatus.es/202">HTTP_ACCEPTED</a>
+   */
+  const HTTP_ACCEPTED = 202;
+  /**
+   * <a href="http://httpstatus.es/203">HTTP_ACCEPTED</a>
+   */
+  const HTTP_NONAUTHORITATIVE_INFORMATION = 203;
+  /**
+   * <a href="http://httpstatus.es/204">HTTP_NO_CONTENT</a>
+   */
+  const HTTP_NO_CONTENT = 204;
+  /**
+   * <a href="http://httpstatus.es/205">HTTP_RESET_CONTENT</a>
+   */
+  const HTTP_RESET_CONTENT = 205;
+  /**
+   * <a href="http://httpstatus.es/206">HTTP_PARTIAL_CONTENT</a>
+   */
+  const HTTP_PARTIAL_CONTENT = 206;
+
+  // [Redirection 3xx]
+  /**
+   * <a href="http://httpstatus.es/300">HTTP_MULTIPLE_CHOICES</a>
+   */
+  const HTTP_MULTIPLE_CHOICES = 300;
+  /**
+   * <a href="http://httpstatus.es/301">HTTP_MOVED_PERMANENTLY</a>
+   */
+  const HTTP_MOVED_PERMANENTLY = 301;
+  /**
+   * <a href="http://httpstatus.es/302">HTTP_FOUND</a>
+   */
+  const HTTP_FOUND = 302;
+  /**
+   * <a href="http://httpstatus.es/303">HTTP_SEE_OTHER</a>
+   */
+  const HTTP_SEE_OTHER = 303;
+  /**
+   * <a href="http://httpstatus.es/304">HTTP_NOT_MODIFIED</a>
+   */
+  const HTTP_NOT_MODIFIED = 304;
+  /**
+   * <a href="http://httpstatus.es/305">HTTP_USE_PROXY</a>
+   */
+  const HTTP_USE_PROXY = 305;
+  /**
+   * <a href="http://httpstatus.es/306">HTTP_UNUSED</a>
+   */
+  const HTTP_UNUSED = 306;
+  /**
+   * <a href="http://httpstatus.es/307">HTTP_TEMPORARY_REDIRECT</a>
+   */
+  const HTTP_TEMPORARY_REDIRECT = 307;
+
+  // [Client Error 4xx]
+  /**
+   * Defines the beginning of errorCodes
+   * @private
+   */
+  const errorCodesBeginAt = 400;
+  /**
+   * <a href="http://httpstatus.es/400">HTTP_BAD_REQUEST</a>
+   */
+  const HTTP_BAD_REQUEST = 400;
+  /**
+   * <a href="http://httpstatus.es/401">HTTP_UNAUTHORIZED</a>
+   */
+  const HTTP_UNAUTHORIZED = 401;
+  /**
+   * <a href="http://httpstatus.es/402">HTTP_PAYMENT_REQUIRED</a>
+   */
+  const HTTP_PAYMENT_REQUIRED = 402;
+  /**
+   * <a href="http://httpstatus.es/403">HTTP_FORBIDDEN</a>
+   */
+  const HTTP_FORBIDDEN = 403;
+  /**
+   * <a href="http://httpstatus.es/404">HTTP_NOT_FOUND</a>
+   */
+  const HTTP_NOT_FOUND = 404;
+  /**
+   * <a href="http://httpstatus.es/405">HTTP_METHOD_NOT_ALLOWED</a>
+   */
+  const HTTP_METHOD_NOT_ALLOWED = 405;
+  /**
+   * <a href="http://httpstatus.es/406">HTTP_NOT_ACCEPTABLE</a>
+   */
+  const HTTP_NOT_ACCEPTABLE = 406;
+  /**
+   * <a href="http://httpstatus.es/407">HTTP_PROXY_AUTHENTICATION_REQUIRED</a>
+   */
+  const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+  /**
+   * <a href="http://httpstatus.es/408">HTTP_REQUEST_TIMEOUT</a>
+   */
+  const HTTP_REQUEST_TIMEOUT = 408;
+  /**
+   * <a href="http://httpstatus.es/409">HTTP_CONFLICT</a>
+   */
+  const HTTP_CONFLICT = 409;
+  /**
+   * <a href="http://httpstatus.es/410">HTTP_GONE</a>
+   */
+  const HTTP_GONE = 410;
+  /**
+   * <a href="http://httpstatus.es/411">HTTP_LENGTH_REQUIRED</a>
+   */
+  const HTTP_LENGTH_REQUIRED = 411;
+  /**
+   * <a href="http://httpstatus.es/412">HTTP_PRECONDITION_FAILED</a>
+   */
+  const HTTP_PRECONDITION_FAILED = 412;
+  /**
+   * <a href="http://httpstatus.es/413">HTTP_REQUEST_ENTITY_TOO_LARGE</a>
+   */
+  const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+  /**
+   * <a href="http://httpstatus.es/414">HTTP_REQUEST_URI_TOO_LONG</a>
+   */
+  const HTTP_REQUEST_URI_TOO_LONG = 414;
+  /**
+   * <a href="http://httpstatus.es/415">HTTP_UNSUPPORTED_MEDIA_TYPE</a>
+   */
+  const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+  /**
+   * <a href="http://httpstatus.es/416">HTTP_REQUESTED_RANGE_NOT_SATISFIABLE</a>
+   */
+  const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+  /**
+   * <a href="http://httpstatus.es/417">HTTP_EXPECTATION_FAILED</a>
+   */
+  const HTTP_EXPECTATION_FAILED = 417;
+
+  // [Server Error 5xx]
+  /**
+   * <a href="http://httpstatus.es/500">HTTP_INTERNAL_SERVER_ERROR</a>
+   */
+  const HTTP_INTERNAL_SERVER_ERROR = 500;
+  /**
+   * <a href="http://httpstatus.es/501">HTTP_NOT_IMPLEMENTED</a>
+   */
+  const HTTP_NOT_IMPLEMENTED = 501;
+  /**
+   * <a href="http://httpstatus.es/502">HTTP_BAD_GATEWAY</a>
+   */
+  const HTTP_BAD_GATEWAY = 502;
+  /**
+   * <a href="http://httpstatus.es/503">HTTP_SERVICE_UNAVAILABLE</a>
+   */
+  const HTTP_SERVICE_UNAVAILABLE = 503;
+  /**
+   * <a href="http://httpstatus.es/504">HTTP_GATEWAY_TIMEOUT</a>
+   */
+  const HTTP_GATEWAY_TIMEOUT = 504;
+  /**
+   * <a href="http://httpstatus.es/505">HTTP_VERSION_NOT_SUPPORTED</a>
+   */
+  const HTTP_VERSION_NOT_SUPPORTED = 505;
+
+  private static $messages =
+    array(
+	  // [Informational 1xx]
+	  100=>'100 Continue',
+	  101=>'101 Switching Protocols',
+
+	  // [Successful 2xx]
+	  200=>'200 OK',
+	  201=>'201 Created',
+	  202=>'202 Accepted',
+	  203=>'203 Non-Authoritative Information',
+	  204=>'204 No Content',
+	  205=>'205 Reset Content',
+	  206=>'206 Partial Content',
+
+	  // [Redirection 3xx]
+	  300=>'300 Multiple Choices',
+	  301=>'301 Moved Permanently',
+	  302=>'302 Found',
+	  303=>'303 See Other',
+	  304=>'304 Not Modified',
+	  305=>'305 Use Proxy',
+	  306=>'306 (Unused)',
+	  307=>'307 Temporary Redirect',
+
+	  // [Client Error 4xx]
+	  400=>'400 Bad Request',
+	  401=>'401 Unauthorized',
+	  402=>'402 Payment Required',
+	  403=>'403 Forbidden',
+	  404=>'404 Not Found',
+	  405=>'405 Method Not Allowed',
+	  406=>'406 Not Acceptable',
+	  407=>'407 Proxy Authentication Required',
+	  408=>'408 Request Timeout',
+	  409=>'409 Conflict',
+	  410=>'410 Gone',
+	  411=>'411 Length Required',
+	  412=>'412 Precondition Failed',
+	  413=>'413 Request Entity Too Large',
+	  414=>'414 Request-URI Too Long',
+	  415=>'415 Unsupported Media Type',
+	  416=>'416 Requested Range Not Satisfiable',
+	  417=>'417 Expectation Failed',
+
+	  // [Server Error 5xx]
+	  500=>'500 Internal Server Error',
+	  501=>'501 Not Implemented',
+	  502=>'502 Bad Gateway',
+	  503=>'503 Service Unavailable',
+	  504=>'504 Gateway Timeout',
+	  505=>'505 HTTP Version Not Supported'
+	  );
+
+  /**
+   * Get the header for the specified code.
+   *
+   * @param $code Http status code
+   * @return A textual representation of the header
+   */
+  public static function httpHeaderFor($code)
+  {
+    return 'HTTP/1.1 ' . self::$messages[$code];
+  }
+
+  /**
+   * Get a canonical status message for the specified code
+   *
+   * @param $code Http status code
+   * @return Text for the specified code
+   */
+  public static function getMessageForCode($code)
+  {
+    return self::$messages[$code];
+  }
+
+  /**
+   * Checks if the specified code is an error code.
+   *
+   * @param $code Http status code
+   * @return bool Answer
+   */
+  public static function isError($code)
+  {
+    return is_numeric($code) && $code >= self::HTTP_BAD_REQUEST;
+  }
+
+  /**
+   * Can the specified status code have a body?
+   *
+   * @param $code Http status code
+   * @return bool Answer
+   */
+  public static function canHaveBody($code)
+  {
+    return
+      // True if not in 100s
+      ($code < self::HTTP_CONTINUE || $code >= self::HTTP_OK)
+      && // and not 204 NO CONTENT
+      $code != self::HTTP_NO_CONTENT
+      && // and not 304 NOT MODIFIED
+      $code != self::HTTP_NOT_MODIFIED;
+  }
+
+  /**
+   * Extract the numeric code from a header
+   *
+   * @param $header an http top header
+   */
+  public static function codeFromHeader($header)
+  {
+    $matches = array();
+    preg_match('/HTTP\/\S+\s(\d+)/', $header, $matches);
+    if (count($matches) < 1)
+      throw new InvalidArgumentException("Not an http header");
+    $n = $matches[1];
+    return $n;
+  }
+}
+?>
\ No newline at end of file
--- a/Validator.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-<?php
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-$cache = ScriptIncludeCache::instance(__FILE__);
-/// @endcond
-
-/**
- * This is the base class for all validators
- */
-abstract class Validator
-{
-  /**
-   * Performs the check if input is valid
-   *
-   * @return bool if the check went through
-   */
-  abstract function check();
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Validator.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,21 @@
+<?php
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+$cache = ScriptIncludeCache::instance(__FILE__);
+/// @endcond
+
+/**
+ * This is the base class for all validators
+ */
+abstract class Validator
+{
+  /**
+   * Performs the check if input is valid
+   *
+   * @return bool if the check went through
+   */
+  abstract function check();
+}
+?>
\ No newline at end of file
--- a/common-functions.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,224 +0,0 @@
-<?php
-/**
- * @file
- * Functionality which doesn't belong anywhere else
- */
-include_once 'ScriptIncludeCache.inc';
-
-/// @cond
-$baseDir = dirname(__FILE__);
-
-$cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('StatusCodes.inc', $baseDir);
-/// @endcond
-
-/**
- * Generates a representation for an array of key => value pairs
- *
- * @note Behaviour is undefined if value is a composite structure.
- *
- * @param $map the input array
- * @return a string representation that may be eval'ed
- */
-function repMapString($map)
-{
-  $opt = 'array(';
-  $start = True;
-
-  foreach($map as $param => $value) {
-    if ($start) {
-      $start = False;
-      $opt .= "\"${param}\" => \"${value}\"";
-    }
-    else {
-      $opt .= ", \"${param}\" => \"${value}\"";
-    }
-  }
-  $opt .= ')';
-  return $opt;
-}
-
-/**
- * Get the location on the server where the top level script is
- * located
- *
- * @return directory
- */
-function basePath()
-{
-  $l = strrpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['SCRIPT_NAME']);
-  return substr($_SERVER['SCRIPT_FILENAME'], 0, $l);
-}
-
-/**
- * Loads a file
- *
- * @param $sFilename name of the file to load
- * @param $sCharset the character encoding of the file
- *
- * @todo make this function throw instead of returning codes
- *
- * @return the contents of the file, or a status code (-3 if file does
- * not exists, if file could not be opened -2)
- */
-function loadFile($sFilename, $sCharset = 'UTF-8')
-{
-  if (floatval(phpversion()) >= 4.3) {
-    if (!file_exists($sFilename))
-      return -3;
-    $sData = file_get_contents($sFilename);
-  }
-  else {
-    if (!file_exists($sFilename))
-      return -3;
-    $rHandle = fopen($sFilename, 'r');
-    if (!$rHandle)
-      return -2;
-
-    $sData = '';
-    while(!feof($rHandle))
-      $sData .= fread($rHandle, filesize($sFilename));
-    fclose($rHandle);
-  }
-  if ($sEncoding = mb_detect_encoding($sData, 'auto', true) != $sCharset) {
-    if ($sEncoding != 1) {
-      $sData = mb_convert_encoding($sData, $sCharset, $sEncoding);
-    }
-  }
-  return $sData;
-}
-
-/**
- * Generate a status page and exit
- *
- * @param $errorText the text to be displayed in the body
- * @param $errorCode the status code to be served
- */
-function errorPage($errorText, $errorCode = 403)
-{
-  header(StatusCodes::httpHeaderFor($errorCode));
-  print '<?xml version="1.0" encoding="UTF-8"?>';
-  print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml"><head>';
-
-  print '<title>' . StatusCodes::httpHeaderFor($errorCode) . '</title>';
-  print '</head><body>';
-  print "<div id=\"page\"><h1>${errorText}</h1></div>";
-  print '</body></html>';
-
-  exit;
-}
-
-/**
- * Generates an URL for the specified parameters
- *
- * @param $urlParams an associative array of the values already set
- * @param $keys a set of values to override $urlParams
- *
- * @param $nonQueryParams a list of keys, where values should be in
- * the URL, rather than the query part, note that the order is
- * important
- */
-function genUrl($urlParams, $keys = array(), $nonQueryParams = array()) {
-  $out = '';
-  $first = 1;
-  $new_params = $urlParams;
-  foreach($keys as $param => $val) {
-    $new_params[$param] = $val;
-  }
-
-  $numEncParams=0;
-  foreach($nonQueryParams as $nqp) {
-    if (array_key_exists($nqp, $new_params)) {
-      ++$numEncParams;
-      $val = $new_params[$nqp];
-      if ($val)
-	$out .= "/${val}";
-      unset($new_params[$nqp]);
-    }
-  }
-  if ($numEncParams<count($nonQueryParams)) {
-	$out .= '/';
-  }
-
-  foreach($new_params as $param => $val) {
-    if ($val) {
-      if($first) {
-	$first = 0;
-	$out .= "?";
-      }
-      else
-	$out .= "&amp;";
-      $out .= urlencode($param) . '=' . urlencode($val);
-    }
-  }
-
-  return $out;
-}
-
-/**
- * Retrieves a single subelement
- * @throw Exception if number of elements are different from 1
- *
- * @todo Throw more specific exception
- *
- * @param $obj the xml element to search in
- * @param $name the name of the element to search for
- */
-function getElementByTagName($obj, $name) {
-  $elems = $obj->getElementsByTagName($name);
-  if ($elems->length != 1) {
-    throw new UnexpectedValueException("More than one tag with name \"${name}\"");
-  }
-  $elem = $elems->item(0);
-  return $elem;
-}
-
-/**
- * Checks if one string start with another string
- *
- * @param $haystack the string to search
- * @param $needle the string to search for
- *
- * @return bool if match
- */
-function startswith($haystack, $needle)
-{
-    return strpos($haystack, $needle) === 0;
-}
-
-/**
- * Checks if one string ends with another string
- *
- * @param $haystack the string to search
- * @param $needle the string to search for
- *
- * @return bool if match
- */
-function endsWith($haystack, $needle)
-{
-  $l = strlen($haystack) - strlen($needle);
-  return strrpos($haystack, $needle) === $l;
-}
-
-/**
- * Generates the query part of an URI
- *
- * @param $opts an associative array of options
- * @return a string that can be used for the query part of an URI
- */
-function opttostring($opts)
-{
-  $str = '';
-  foreach (array_keys($opts) as $key) {
-    $value = $opts[$key];
-    if ($str) {
-      $str .= "&${key}=${value}";
-    }
-    else {
-      $str = "?${key}=${value}";
-    }
-  }
-  return $str;
-}
-?>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common-functions.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,224 @@
+<?php
+/**
+ * @file
+ * Functionality which doesn't belong anywhere else
+ */
+include_once 'ScriptIncludeCache.inc.php';
+
+/// @cond
+$baseDir = dirname(__FILE__);
+
+$cache = ScriptIncludeCache::instance(__FILE__);
+$cache->includeOnce('StatusCodes.inc.php', $baseDir);
+/// @endcond
+
+/**
+ * Generates a representation for an array of key => value pairs
+ *
+ * @note Behaviour is undefined if value is a composite structure.
+ *
+ * @param $map the input array
+ * @return a string representation that may be eval'ed
+ */
+function repMapString($map)
+{
+  $opt = 'array(';
+  $start = True;
+
+  foreach($map as $param => $value) {
+    if ($start) {
+      $start = False;
+      $opt .= "\"${param}\" => \"${value}\"";
+    }
+    else {
+      $opt .= ", \"${param}\" => \"${value}\"";
+    }
+  }
+  $opt .= ')';
+  return $opt;
+}
+
+/**
+ * Get the location on the server where the top level script is
+ * located
+ *
+ * @return directory
+ */
+function basePath()
+{
+  $l = strrpos($_SERVER['SCRIPT_FILENAME'], $_SERVER['SCRIPT_NAME']);
+  return substr($_SERVER['SCRIPT_FILENAME'], 0, $l);
+}
+
+/**
+ * Loads a file
+ *
+ * @param $sFilename name of the file to load
+ * @param $sCharset the character encoding of the file
+ *
+ * @todo make this function throw instead of returning codes
+ *
+ * @return the contents of the file, or a status code (-3 if file does
+ * not exists, if file could not be opened -2)
+ */
+function loadFile($sFilename, $sCharset = 'UTF-8')
+{
+  if (floatval(phpversion()) >= 4.3) {
+    if (!file_exists($sFilename))
+      return -3;
+    $sData = file_get_contents($sFilename);
+  }
+  else {
+    if (!file_exists($sFilename))
+      return -3;
+    $rHandle = fopen($sFilename, 'r');
+    if (!$rHandle)
+      return -2;
+
+    $sData = '';
+    while(!feof($rHandle))
+      $sData .= fread($rHandle, filesize($sFilename));
+    fclose($rHandle);
+  }
+  if ($sEncoding = mb_detect_encoding($sData, 'auto', true) != $sCharset) {
+    if ($sEncoding != 1) {
+      $sData = mb_convert_encoding($sData, $sCharset, $sEncoding);
+    }
+  }
+  return $sData;
+}
+
+/**
+ * Generate a status page and exit
+ *
+ * @param $errorText the text to be displayed in the body
+ * @param $errorCode the status code to be served
+ */
+function errorPage($errorText, $errorCode = 403)
+{
+  header(StatusCodes::httpHeaderFor($errorCode));
+  print '<?xml version="1.0" encoding="UTF-8"?>';
+  print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"><head>';
+
+  print '<title>' . StatusCodes::httpHeaderFor($errorCode) . '</title>';
+  print '</head><body>';
+  print "<div id=\"page\"><h1>${errorText}</h1></div>";
+  print '</body></html>';
+
+  exit;
+}
+
+/**
+ * Generates an URL for the specified parameters
+ *
+ * @param $urlParams an associative array of the values already set
+ * @param $keys a set of values to override $urlParams
+ *
+ * @param $nonQueryParams a list of keys, where values should be in
+ * the URL, rather than the query part, note that the order is
+ * important
+ */
+function genUrl($urlParams, $keys = array(), $nonQueryParams = array()) {
+  $out = '';
+  $first = 1;
+  $new_params = $urlParams;
+  foreach($keys as $param => $val) {
+    $new_params[$param] = $val;
+  }
+
+  $numEncParams=0;
+  foreach($nonQueryParams as $nqp) {
+    if (array_key_exists($nqp, $new_params)) {
+      ++$numEncParams;
+      $val = $new_params[$nqp];
+      if ($val)
+	$out .= "/${val}";
+      unset($new_params[$nqp]);
+    }
+  }
+  if ($numEncParams<count($nonQueryParams)) {
+	$out .= '/';
+  }
+
+  foreach($new_params as $param => $val) {
+    if ($val) {
+      if($first) {
+	$first = 0;
+	$out .= "?";
+      }
+      else
+	$out .= "&amp;";
+      $out .= urlencode($param) . '=' . urlencode($val);
+    }
+  }
+
+  return $out;
+}
+
+/**
+ * Retrieves a single subelement
+ * @throw Exception if number of elements are different from 1
+ *
+ * @todo Throw more specific exception
+ *
+ * @param $obj the xml element to search in
+ * @param $name the name of the element to search for
+ */
+function getElementByTagName($obj, $name) {
+  $elems = $obj->getElementsByTagName($name);
+  if ($elems->length != 1) {
+    throw new UnexpectedValueException("More than one tag with name \"${name}\"");
+  }
+  $elem = $elems->item(0);
+  return $elem;
+}
+
+/**
+ * Checks if one string start with another string
+ *
+ * @param $haystack the string to search
+ * @param $needle the string to search for
+ *
+ * @return bool if match
+ */
+function startswith($haystack, $needle)
+{
+    return strpos($haystack, $needle) === 0;
+}
+
+/**
+ * Checks if one string ends with another string
+ *
+ * @param $haystack the string to search
+ * @param $needle the string to search for
+ *
+ * @return bool if match
+ */
+function endsWith($haystack, $needle)
+{
+  $l = strlen($haystack) - strlen($needle);
+  return strrpos($haystack, $needle) === $l;
+}
+
+/**
+ * Generates the query part of an URI
+ *
+ * @param $opts an associative array of options
+ * @return a string that can be used for the query part of an URI
+ */
+function opttostring($opts)
+{
+  $str = '';
+  foreach (array_keys($opts) as $key) {
+    $value = $opts[$key];
+    if ($str) {
+      $str .= "&${key}=${value}";
+    }
+    else {
+      $str = "?${key}=${value}";
+    }
+  }
+  return $str;
+}
+?>
\ No newline at end of file
--- a/constants.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-<?php
-/**
- * @file
- * Definitions of constants
- */
-define('VERBOSITY_NONE', 0);
-define('VERBOSITY_ERROR', 1);
-define('VERBOSITY_WARNING', 10);
-define('VERBOSITY_DEBUG', 100);
-
-define('DEBUG_LEVEL', VERBOSITY_NONE);
-define('COMPRESSION_DISABLED', 0);
-
-define('DUMP', 0);
-define('MAX_RECURSE', 50);
-define('CACHING', 1);
-define('VALIDATE', 0);
-
-define('ABORT_ON_LOG', FALSE);
-
-
-if (DEBUG_LEVEL >= VERBOSITY_WARNING) {
-  error_reporting(E_ALL);
-  ini_set("display_errors", 1);
-  ini_set('display_startup_errors', 1);
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/constants.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @file
+ * Definitions of constants
+ */
+define('VERBOSITY_NONE', 0);
+define('VERBOSITY_ERROR', 1);
+define('VERBOSITY_WARNING', 10);
+define('VERBOSITY_DEBUG', 100);
+
+define('DEBUG_LEVEL', VERBOSITY_NONE);
+define('COMPRESSION_DISABLED', 0);
+
+define('DUMP', 0);
+define('MAX_RECURSE', 50);
+define('CACHING', 1);
+define('VALIDATE', 0);
+
+define('ABORT_ON_LOG', FALSE);
+
+
+if (DEBUG_LEVEL >= VERBOSITY_WARNING) {
+  error_reporting(E_ALL);
+  ini_set("display_errors", 1);
+  ini_set('display_startup_errors', 1);
+}
+?>
--- a/filters.inc	Sun Jan 22 19:15:55 2023 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-<?php
-/**
- * @file
- * Filters which may be used from xml
- */
-
-/**
- * A configuration function for generating an active status in a list
- * item corresponding to the currently active 'name'
- *
- * @param $input the string to be processed
- * @param $options Options for this file
- */
-function activeNav($input, $options)
-{
-  $name = $options->getName();
-  if (!$name)
-    $name = $options->getInputDefault('name');
-  $lang = $options->getLang();
-  $pattern = "/<li id=\"${name}\"\s?([^>]*)>/is";
-  $replacement = "<li id=\"${name}\" class=\"active\" $1>";
-  $output = preg_replace($pattern, $replacement, $input);
-
-  $pattern = '/<li id="([^"]+)"\s?([^>]*)>(.*?)<\/li>/is';
-  $replacement = "<li id=\"\$1\" \$2><a href=\"%URL-$1%\">\$3</a></li>";
-
-  $output = preg_replace_callback($pattern,
-	function($matches) use ($options){
-          $opt = $options->getUrlParams();
-          $baseUrl = $options->getBaseUrl();
-          $g=genUrl($opt, array("name" => $matches[1]), array("lang", "name") );
-	  return "<li id=\"$matches[1]\" $matches[2]><a href=\"$baseUrl" . $g . "\">$matches[3]</a></li>";
-	},
-	$output);
-
-  return $output;
-}
-
-/**
- * A configuration function for generating a language bar.
- *
- * @param $input the string to be processed
- * @param $options Options for this file
- * @param $languages array of alternative languages
- */
-function addLangBar($input, $options, $languages)
-{
-  $name = $options->getName();
-  $lang = $options->getLang();
-  $langbar='<ul id="language-select">';
-
-  foreach($languages as $l) {
-    $active = ($l == $lang) ? 0 : 1;
-    $langbar.= "
-	    <li class=\"${l}\">";
-    if ($active)
-      $langbar .= '<a href="'.genUrl($options->getUrlParams(), array( 'lang' => $l), array('lang', 'name') ) . '">';
-
-    $flagUrl = $options->getFlagUrl();
-
-    $langbar .= "
-	      <img src=\"${flagUrl}?lang=${l}&amp;active=${active}\" width=\"20\" height=\"16\" alt=\"Norsk versjon - inaktiv\" title=\"Norsk\"/>";
-    if ($active)
-      $langbar .= "</a>";
-
-    $langbar .= "
-	    </li>
-";
-
-  }
-  $langbar.='</ul>';
-  $pattern = '/<ul id="language-select"\/>/';
-  $replacement = $langbar;
-  $output = preg_replace($pattern, $replacement, $input);
-
-  return $output;
-}
-?>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/filters.inc.php	Sun Jan 22 19:22:00 2023 +0100
@@ -0,0 +1,78 @@
+<?php
+/**
+ * @file
+ * Filters which may be used from xml
+ */
+
+/**
+ * A configuration function for generating an active status in a list
+ * item corresponding to the currently active 'name'
+ *
+ * @param $input the string to be processed
+ * @param $options Options for this file
+ */
+function activeNav($input, $options)
+{
+  $name = $options->getName();
+  if (!$name)
+    $name = $options->getInputDefault('name');
+  $lang = $options->getLang();
+  $pattern = "/<li id=\"${name}\"\s?([^>]*)>/is";
+  $replacement = "<li id=\"${name}\" class=\"active\" $1>";
+  $output = preg_replace($pattern, $replacement, $input);
+
+  $pattern = '/<li id="([^"]+)"\s?([^>]*)>(.*?)<\/li>/is';
+  $replacement = "<li id=\"\$1\" \$2><a href=\"%URL-$1%\">\$3</a></li>";
+
+  $output = preg_replace_callback($pattern,
+	function($matches) use ($options){
+          $opt = $options->getUrlParams();
+          $baseUrl = $options->getBaseUrl();
+          $g=genUrl($opt, array("name" => $matches[1]), array("lang", "name") );
+	  return "<li id=\"$matches[1]\" $matches[2]><a href=\"$baseUrl" . $g . "\">$matches[3]</a></li>";
+	},
+	$output);
+
+  return $output;
+}
+
+/**
+ * A configuration function for generating a language bar.
+ *
+ * @param $input the string to be processed
+ * @param $options Options for this file
+ * @param $languages array of alternative languages
+ */
+function addLangBar($input, $options, $languages)
+{
+  $name = $options->getName();
+  $lang = $options->getLang();
+  $langbar='<ul id="language-select">';
+
+  foreach($languages as $l) {
+    $active = ($l == $lang) ? 0 : 1;
+    $langbar.= "
+	    <li class=\"${l}\">";
+    if ($active)
+      $langbar .= '<a href="'.genUrl($options->getUrlParams(), array( 'lang' => $l), array('lang', 'name') ) . '">';
+
+    $flagUrl = $options->getFlagUrl();
+
+    $langbar .= "
+	      <img src=\"${flagUrl}?lang=${l}&amp;active=${active}\" width=\"20\" height=\"16\" alt=\"Norsk versjon - inaktiv\" title=\"Norsk\"/>";
+    if ($active)
+      $langbar .= "</a>";
+
+    $langbar .= "
+	    </li>
+";
+
+  }
+  $langbar.='</ul>';
+  $pattern = '/<ul id="language-select"\/>/';
+  $replacement = $langbar;
+  $output = preg_replace($pattern, $replacement, $input);
+
+  return $output;
+}
+?>
--- a/flag.php	Sun Jan 22 19:15:55 2023 +0100
+++ b/flag.php	Sun Jan 22 19:22:00 2023 +0100
@@ -3,12 +3,12 @@
  * @file
  * Displays a flag, in an active or disabled state, depending on parameters
  */
-include_once 'constants.inc';
-include_once 'CacheTimeCheck.inc';
+include_once 'constants.inc.php';
+include_once 'CacheTimeCheck.inc.php';
 
 /// @cond
 $scriptCache = ScriptIncludeCache::instance(__FILE__);
-$scriptCache->includeOnce('Flag.inc');
+$scriptCache->includeOnce('Flag.inc.php');
 
 try {
   $flag = new Flag($scriptCache);
--- a/index.php	Sun Jan 22 19:15:55 2023 +0100
+++ b/index.php	Sun Jan 22 19:22:00 2023 +0100
@@ -3,20 +3,20 @@
  * @file
  * Main access point for webpages
  */
-include_once 'constants.inc';
+include_once 'constants.inc.php';
 
-include_once 'CacheTimeCheck.inc';
+include_once 'CacheTimeCheck.inc.php';
 
 /// @cond
 $baseDir = dirname(__FILE__);
 $scriptCache = ScriptIncludeCache::instance(__FILE__);
 
-$scriptCache->includeOnce('Language.inc', $baseDir);
-$scriptCache->includeOnce('Options.inc', $baseDir);
-$scriptCache->includeOnce('common-functions.inc', $baseDir);
-$scriptCache->includeOnce('filters.inc', $baseDir);
-$scriptCache->includeOnce('InputParser.inc', $baseDir);
-$scriptCache->includeOnce('Logger.inc', $baseDir);
+$scriptCache->includeOnce('Language.inc.php', $baseDir);
+$scriptCache->includeOnce('Options.inc.php', $baseDir);
+$scriptCache->includeOnce('common-functions.inc.php', $baseDir);
+$scriptCache->includeOnce('filters.inc.php', $baseDir);
+$scriptCache->includeOnce('InputParser.inc.php', $baseDir);
+$scriptCache->includeOnce('Logger.inc.php', $baseDir);
 
 if (DEBUG_LEVEL >= VERBOSITY_DEBUG) {
   var_dump($_SERVER);
--- a/sitemap.php	Sun Jan 22 19:15:55 2023 +0100
+++ b/sitemap.php	Sun Jan 22 19:22:00 2023 +0100
@@ -3,14 +3,14 @@
  * @file
  * Generates a sitemap
  */
-include_once 'constants.inc';
+include_once 'constants.inc.php';
 
-include_once 'ScriptIncludeCache.inc';
+include_once 'ScriptIncludeCache.inc.php';
 
 /// @cond
 $baseDir = dirname(__FILE__);
 $cache = ScriptIncludeCache::instance(__FILE__);
-$cache->includeOnce('Sitemap.inc', dirname(__FILE__));
+$cache->includeOnce('Sitemap.inc.php', dirname(__FILE__));
 
 try {
   $base=basePath();