summaryrefslogtreecommitdiff
path: root/lpf
diff options
context:
space:
mode:
Diffstat (limited to 'lpf')
-rw-r--r--lpf/ext/HTTP_Accept.class.php659
-rw-r--r--lpf/ext/README.txt11
-rw-r--r--lpf/lib/Controller.class.php30
-rw-r--r--lpf/lib/Mime.class.php45
-rw-r--r--lpf/lib/Model.class.php9
-rw-r--r--lpf/lib/Router.class.php110
-rw-r--r--lpf/lib/View.class.php135
-rw-r--r--lpf/views/pages/no-conf.html.php8
8 files changed, 1007 insertions, 0 deletions
diff --git a/lpf/ext/HTTP_Accept.class.php b/lpf/ext/HTTP_Accept.class.php
new file mode 100644
index 0000000..5efaa5d
--- /dev/null
+++ b/lpf/ext/HTTP_Accept.class.php
@@ -0,0 +1,659 @@
+<?php
+
+/**
+ * HTTP_Accept class for dealing with the HTTP 'Accept' header
+ *
+ * PHP versions 4 and 5
+ *
+ * LICENSE: This source file is subject to the MIT License.
+ * The full text of this license is available at the following URL:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * @category HTTP
+ * @package HTTP_Accept
+ * @author Kevin Locke <kwl7@cornell.edu>
+ * @copyright 2007 Kevin Locke
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @version SVN: $Id: HTTP_Accept.php 22 2007-10-06 18:46:45Z kevin $
+ * @link http://pear.php.net/package/HTTP_Accept
+ */
+
+/**
+ * HTTP_Accept class for dealing with the HTTP 'Accept' header
+ *
+ * This class is intended to be used to parse the HTTP Accept header into
+ * usable information and provide a simple API for dealing with that
+ * information.
+ *
+ * The parsing of this class is designed to follow RFC 2616 to the letter,
+ * any deviations from that standard are bugs and should be reported to the
+ * maintainer.
+ *
+ * Often the class will be used very simply as
+ * <code>
+ * <?php
+ * $accept = new HTTP_Accept($_SERVER['HTTP_ACCEPT']);
+ * if ($accept->getQuality("image/png") > $accept->getQuality("image/jpeg"))
+ * // Send PNG image
+ * else
+ * // Send JPEG image
+ * ?>
+ * </code>
+ *
+ * However, for browsers which do not accurately describe their preferences,
+ * it may be necessary to check if a MIME Type is explicitly listed in their
+ * Accept header, in addition to being preferred to another type
+ *
+ * <code>
+ * <?php
+ * if ($accept->isMatchExact("application/xhtml+xml"))
+ * // Client specifically asked for this type at some quality level
+ * ?>
+ * </code>
+ *
+ *
+ * @category HTTP
+ * @package HTTP_Accept
+ * @access public
+ * @link http://pear.php.net/package/HTTP_Accept
+ */
+class HTTP_Accept
+{
+ /**
+ * Array of types and their associated parameters, extensions, and quality
+ * factors, as represented in the Accept: header.
+ * Indexed by [type][subtype][index],
+ * and contains 'PARAMS', 'QUALITY', and 'EXTENSIONS' keys for the
+ * parameter set, quality factor, and extensions set respectively.
+ * Note: Since type, subtype, and parameters are case-insensitive
+ * (RFC 2045 5.1) they are stored as lower-case.
+ *
+ * @var array
+ * @access private
+ */
+ var $acceptedtypes = array();
+
+ /**
+ * Regular expression to match a token, as defined in RFC 2045
+ *
+ * @var string
+ * @access private
+ */
+ var $_matchtoken = '(?:[^[:cntrl:]()<>@,;:\\\\"\/\[\]?={} \t]+)';
+
+ /**
+ * Regular expression to match a quoted string, as defined in RFC 2045
+ *
+ * @var string
+ * @access private
+ */
+ var $_matchqstring = '(?:"[^\\\\"]*(?:\\\\.[^\\\\"]*)*")';
+
+ /**
+ * Constructs a new HTTP_Accept object
+ *
+ * Initializes the HTTP_Accept class with a given accept string
+ * or creates a new (empty) HTTP_Accept object if no string is given
+ *
+ * Note: The behavior is a little strange here to accomodate
+ * missing headers (to be interpreted as accept all) as well as
+ * new empty objects which should accept nothing. This means that
+ * HTTP_Accept("") will be different than HTTP_Accept()
+ *
+ * @access public
+ * @return object HTTP_Accept
+ * @param string $acceptstring The value of an Accept: header
+ * Will often be $_SERVER['HTTP_ACCEPT']
+ * Note: If get_magic_quotes_gpc is on,
+ * run stripslashes() on the string first
+ */
+ function HTTP_Accept()
+ {
+ if (func_num_args() == 0) {
+ // User wishes to create empty HTTP_Accept object
+ $this->acceptedtypes = array(
+ '*' => array(
+ '*' => array (
+ 0 => array(
+ 'PARAMS' => array(),
+ 'QUALITY' => 0,
+ 'EXTENSIONS' => array()
+ )
+ )
+ )
+ );
+ return;
+ }
+
+ $acceptstring = trim(func_get_arg(0));
+ if (empty($acceptstring)) {
+ // Accept header empty or not sent, interpret as "*/*"
+ $this->acceptedtypes = array(
+ '*' => array(
+ '*' => array (
+ 0 => array(
+ 'PARAMS' => array(),
+ 'QUALITY' => 1,
+ 'EXTENSIONS' => array()
+ )
+ )
+ )
+ );
+ return;
+ }
+
+ $matches = preg_match_all(
+ '/\s*('.$this->_matchtoken.')\/' . // typegroup/
+ '('.$this->_matchtoken.')' . // subtype
+ '((?:\s*;\s*'.$this->_matchtoken.'\s*' . // parameter
+ '(?:=\s*' . // optional =value
+ '(?:'.$this->_matchqstring.'|'.$this->_matchtoken.'))?)*)/', // value
+ $acceptstring, $acceptedtypes,
+ PREG_SET_ORDER);
+
+ if ($matches == 0) {
+ // Malformed Accept header
+ $this->acceptedtypes = array(
+ '*' => array(
+ '*' => array (
+ 0 => array(
+ 'PARAMS' => array(),
+ 'QUALITY' => 1,
+ 'EXTENSIONS' => array()
+ )
+ )
+ )
+ );
+ return;
+ }
+
+ foreach ($acceptedtypes as $accepted) {
+ $typefamily = strtolower($accepted[1]);
+ $subtype = strtolower($accepted[2]);
+
+ // */subtype is invalid according to grammar in section 14.1
+ // so we ignore it
+ if ($typefamily == '*' && $subtype != '*')
+ continue;
+
+ // Parse all arguments of the form "key=value"
+ $matches = preg_match_all('/;\s*('.$this->_matchtoken.')\s*' .
+ '(?:=\s*' .
+ '('.$this->_matchqstring.'|'.
+ $this->_matchtoken.'))?/',
+ $accepted[3], $args,
+ PREG_SET_ORDER);
+
+ $params = array();
+ $quality = -1;
+ $extensions = array();
+ foreach ($args as $arg) {
+ array_shift($arg);
+ if (!empty($arg[1])) {
+ // Strip quotes (Note: Can't use trim() in case "text\"")
+ $len = strlen($arg[1]);
+ if ($arg[1][0] == '"' && $arg[1][$len-1] == '"'
+ && $len > 1) {
+ $arg[1] = substr($arg[1], 1, $len-2);
+ $arg[1] = stripslashes($arg[1]);
+ }
+ } else if (!isset($arg[1])) {
+ $arg[1] = null;
+ }
+
+ // Everything before q=# is a parameter, after is an extension
+ if ($quality >= 0) {
+ $extensions[$arg[0]] = $arg[1];
+ } else if ($arg[0] == 'q') {
+ $quality = (float)$arg[1];
+
+ if ($quality < 0)
+ $quality = 0;
+ else if ($quality > 1)
+ $quality = 1;
+ } else {
+ $arg[0] = strtolower($arg[0]);
+ // Values required for parameters,
+ // assume empty-string for missing values
+ if (isset($arg[1]))
+ $params[$arg[0]] = $arg[1];
+ else
+ $params[$arg[0]] = "";
+ }
+ }
+
+ if ($quality < 0)
+ $quality = 1;
+ else if ($quality == 0)
+ continue;
+
+ if (!isset($this->acceptedtypes[$typefamily]))
+ $this->acceptedtypes[$typefamily] = array();
+ if (!isset($this->acceptedtypes[$typefamily][$subtype]))
+ $this->acceptedtypes[$typefamily][$subtype] = array();
+
+ $this->acceptedtypes[$typefamily][$subtype][] =
+ array('PARAMS' => $params,
+ 'QUALITY' => $quality,
+ 'EXTENSIONS' => $extensions);
+ }
+
+ if (!isset($this->acceptedtypes['*']))
+ $this->acceptedtypes['*'] = array();
+ if (!isset($this->acceptedtypes['*']['*']))
+ $this->acceptedtypes['*']['*'] = array(
+ 0 => array(
+ 'PARAMS' => array(),
+ 'QUALITY' => 0,
+ 'EXTENSIONS' => array()
+ )
+ );
+ }
+
+ /**
+ * Gets the accepted quality factor for a given MIME Type
+ *
+ * Note: If there are multiple best matches
+ * (e.g. "text/html;level=4;charset=utf-8" matching both "text/html;level=4"
+ * and "text/html;charset=utf-8"), it returns the lowest quality factor as
+ * a conservative estimate. Further, if the ambiguity is between parameters
+ * and extensions (e.g. "text/html;level=4;q=1;ext=foo" matching both
+ * "text/html;level=4" and "text/html;q=1;ext=foo") the parameters take
+ * precidence.
+ *
+ * Usage Note: If the quality factor for all supported media types is 0,
+ * RFC 2616 specifies that applications SHOULD send an HTTP 406 (not
+ * acceptable) response.
+ *
+ * @access public
+ * @return double the quality value for the given MIME Type
+ * Quality values are in the range [0,1] where 0 means
+ * "not accepted" and 1 is "perfect quality".
+ * @param string $mimetype The MIME Type to query ("text/html")
+ * @param array $params Parameters of Type to query ([level => 4])
+ * @param array $extensions Extension parameters to query
+ */
+ function getQuality($mimetype, $params = array(), $extensions = array())
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if ($params == null)
+ $params = array();
+ if ($extensions == null)
+ $extensions = array();
+
+ if (empty($this->acceptedtypes[$supertype])) {
+ if ($supertype == '*')
+ return 0;
+ else
+ return $this->getQuality("*/*", $params, $extensions);
+ }
+
+ if (empty($this->acceptedtypes[$supertype][$subtype])) {
+ if ($subtype == '*')
+ return $this->getQuality("*/*", $params, $extensions);
+ else
+ return $this->getQuality("$supertype/*", $params, $extensions);
+ }
+
+ $params = array_change_key_case($params, CASE_LOWER);
+
+ $matches = $this->_findBestMatchIndices($supertype, $subtype,
+ $params, $extensions);
+
+ if (count($matches) == 0) {
+ if ($subtype != '*')
+ return $this->getQuality("$supertype/*", $params, $extensions);
+ else if ($supertype != '*')
+ return $this->getQuality("*/*", $params, $extensions);
+ else
+ return 0;
+ }
+
+ $minquality = 1;
+ foreach ($matches as $match)
+ if ($this->acceptedtypes[$supertype][$subtype][$match]['QUALITY'] < $minquality)
+ $minquality = $this->acceptedtypes[$supertype][$subtype][$match]['QUALITY'];
+
+ return $minquality;
+ }
+
+ /**
+ * Determines if there is an exact match for the specified MIME Type
+ *
+ * @access public
+ * @return boolean true if there is an exact match to the given
+ * values, false otherwise.
+ * @param string $mimetype The MIME Type to query (e.g. "text/html")
+ * @param array $params Parameters of Type to query (e.g. [level => 4])
+ * @param array $extensions Extension parameters to query
+ */
+ function isMatchExact($mimetype, $params = array(), $extensions = array())
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if ($params == null)
+ $params = array();
+ if ($extensions == null)
+ $extensions = array();
+
+ return $this->_findExactMatchIndex($supertype, $subtype,
+ $params, $extensions) >= 0;
+ }
+
+ /**
+ * Gets a list of all MIME Types explicitly accepted, sorted by quality
+ *
+ * @access public
+ * @return array list of MIME Types explicitly accepted, sorted
+ * in decreasing order of quality factor
+ */
+ function getTypes()
+ {
+ $qvalues = array();
+ $types = array();
+ foreach ($this->acceptedtypes as $typefamily => $subtypes) {
+ if ($typefamily == '*')
+ continue;
+
+ foreach ($subtypes as $subtype => $variants) {
+ if ($subtype == '*')
+ continue;
+
+ $maxquality = 0;
+ foreach ($variants as $variant)
+ if ($variant['QUALITY'] > $maxquality)
+ $maxquality = $variant['QUALITY'];
+
+ if ($maxquality > 0) {
+ $qvalues[] = $maxquality;
+ $types[] = $typefamily.'/'.$subtype;
+ }
+ }
+ }
+
+ array_multisort($qvalues, SORT_DESC, SORT_NUMERIC,
+ $types, SORT_DESC, SORT_STRING);
+
+ return $types;
+ }
+
+ /**
+ * Gets the parameter sets for a given mime type, sorted by quality.
+ * Only parameter sets where the extensions set is empty will be returned.
+ *
+ * @access public
+ * @return array list of sets of name=>value parameter pairs
+ * in decreasing order of quality factor
+ * @param string $mimetype The MIME Type to query ("text/html")
+ */
+ function getParameterSets($mimetype)
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if (!isset($this->acceptedtypes[$supertype])
+ || !isset($this->acceptedtypes[$supertype][$subtype]))
+ return array();
+
+ $qvalues = array();
+ $paramsets = array();
+ foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) {
+ if (count($acceptedtype['EXTENSIONS']) == 0) {
+ $qvalues[] = $acceptedtype['QUALITY'];
+ $paramsets[] = $acceptedtype['PARAMS'];
+ }
+ }
+
+ array_multisort($qvalues, SORT_DESC, SORT_NUMERIC,
+ $paramsets, SORT_DESC, SORT_STRING);
+
+ return $paramsets;
+ }
+
+ /**
+ * Gets the extension sets for a given mime type, sorted by quality.
+ * Only extension sets where the parameter set is empty will be returned.
+ *
+ * @access public
+ * @return array list of sets of name=>value extension pairs
+ * in decreasing order of quality factor
+ * @param string $mimetype The MIME Type to query ("text/html")
+ */
+ function getExtensionSets($mimetype)
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if (!isset($this->acceptedtypes[$supertype])
+ || !isset($this->acceptedtypes[$supertype][$subtype]))
+ return array();
+
+ $qvalues = array();
+ $extensionsets = array();
+ foreach ($this->acceptedtypes[$supertype][$subtype] as $acceptedtype) {
+ if (count($acceptedtype['PARAMS']) == 0) {
+ $qvalues[] = $acceptedtype['QUALITY'];
+ $extensionsets[] = $acceptedtype['EXTENSIONS'];
+ }
+ }
+
+ array_multisort($qvalues, SORT_DESC, SORT_NUMERIC,
+ $extensionsets, SORT_DESC, SORT_STRING);
+
+ return $extensionsets;
+ }
+
+ /**
+ * Adds a type to the set of accepted types
+ *
+ * @access public
+ * @param string $mimetype The MIME Type to add (e.g. "text/html")
+ * @param double $quality The quality value for the given MIME Type
+ * Quality values are in the range [0,1] where
+ * 0 means "not accepted" and 1 is
+ * "perfect quality".
+ * @param array $params Parameters of the type to add (e.g. [level => 4])
+ * @param array $extensions Extension parameters of the type to add
+ */
+ function addType($mimetype, $quality = 1,
+ $params = array(), $extensions = array())
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if ($params == null)
+ $params = array();
+ if ($extensions == null)
+ $extensions = array();
+
+ $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions);
+
+ if ($index >= 0) {
+ $this->acceptedtypes[$supertype][$subtype][$index]['QUALITY'] = $quality;
+ } else {
+ if (!isset($this->acceptedtypes[$supertype]))
+ $this->acceptedtypes[$supertype] = array();
+ if (!isset($this->acceptedtypes[$supertype][$subtype]))
+ $this->acceptedtypes[$supertype][$subtype] = array();
+
+ $this->acceptedtypes[$supertype][$subtype][] =
+ array('PARAMS' => $params,
+ 'QUALITY' => $quality,
+ 'EXTENSIONS' => $extensions);
+ }
+ }
+
+ /**
+ * Removes a type from the set of accepted types
+ *
+ * @access public
+ * @param string $mimetype The MIME Type to remove (e.g. "text/html")
+ * @param array $params Parameters of the type to remove (e.g. [level => 4])
+ * @param array $extensions Extension parameters of the type to remove
+ */
+ function removeType($mimetype, $params = array(), $extensions = array())
+ {
+ $type = explode("/", $mimetype);
+ $supertype = strtolower($type[0]);
+ $subtype = strtolower($type[1]);
+
+ if ($params == null)
+ $params = array();
+ if ($extensions == null)
+ $extensions = array();
+
+ $index = $this->_findExactMatchIndex($supertype, $subtype, $params, $extensions);
+
+ if ($index >= 0) {
+ $this->acceptedtypes[$supertype][$subtype] =
+ array_merge(array_slice($this->acceptedtypes[$supertype][$subtype],
+ 0, $index),
+ array_slice($this->acceptedtypes[$supertype][$subtype],
+ $index+1));
+ }
+ }
+
+ /**
+ * Gets a string representation suitable for use in an HTTP Accept header
+ *
+ * @access public
+ * @return string a string representation of this object
+ */
+ function __toString()
+ {
+ $accepted = array();
+ $qvalues = array();
+ foreach ($this->acceptedtypes as $supertype => $subtypes) {
+ foreach ($subtypes as $subtype => $entries) {
+ foreach ($entries as $entry) {
+ $accepted[] = array('TYPE' => "$supertype/$subtype",
+ 'QUALITY' => $entry['QUALITY'],
+ 'PARAMS' => $entry['PARAMS'],
+ 'EXTENSIONS' => $entry['EXTENSIONS']);
+ $qvalues[] = $entry['QUALITY'];
+ }
+ }
+ }
+
+ array_multisort($qvalues, SORT_DESC, SORT_NUMERIC,
+ $accepted);
+
+ $str = "";
+ foreach ($accepted as $accept) {
+ // Skip the catchall value if it is 0, since this is implied
+ if ($accept['TYPE'] == '*/*' &&
+ $accept['QUALITY'] == 0 &&
+ count($accept['PARAMS']) == 0 &&
+ count($accept['EXTENSIONS'] == 0))
+ continue;
+
+ $str = $str.$accept['TYPE'].';';
+
+ foreach ($accept['PARAMS'] as $param => $value) {
+ if (preg_match('/^'.$this->_matchtoken.'$/', $value))
+ $str = $str.$param.'='.$value.';';
+ else
+ $str = $str.$param.'="'.addcslashes($value,'"\\').'";';
+ }
+
+ if ($accept['QUALITY'] < 1 || !empty($accept['EXTENSIONS']))
+ $str = $str.'q='.$accept['QUALITY'].';';
+
+ foreach ($accept['EXTENSIONS'] as $extension => $value) {
+ if (preg_match('/^'.$this->_matchtoken.'$/', $value))
+ $str = $str.$extension.'='.$value.';';
+ else
+ $str = $str.$extension.'="'.addcslashes($value,'"\\').'";';
+ }
+
+ $str[strlen($str)-1] = ',';
+ }
+
+ return rtrim($str, ',');
+ }
+
+ /**
+ * Finds the index of an exact match for the specified MIME Type
+ *
+ * @access private
+ * @return int the index of an exact match if found,
+ * -1 otherwise
+ * @param string $supertype The general MIME Type to find (e.g. "text")
+ * @param string $subtype The MIME subtype to find (e.g. "html")
+ * @param array $params Parameters of Type to find ([level => 4])
+ * @param array $extensions Extension parameters to find
+ */
+ function _findExactMatchIndex($supertype, $subtype, $params, $extensions)
+ {
+ if (empty($this->acceptedtypes[$supertype])
+ || empty($this->acceptedtypes[$supertype][$subtype]))
+ return -1;
+
+ $params = array_change_key_case($params, CASE_LOWER);
+
+ $parammatches = array();
+ foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch)
+ if ($typematch['PARAMS'] == $params
+ && $typematch['EXTENSIONS'] == $extensions)
+ return $index;
+
+ return -1;
+ }
+
+ /**
+ * Finds the indices of the best matches for the specified MIME Type
+ *
+ * A "match" in this context is an exact type match and no extraneous
+ * matches for parameters or extensions (so the best match for
+ * "text/html;level=4" may be "text/html" but not the other way around).
+ *
+ * "Best" is interpreted as the entries that match the most
+ * parameters and extensions (the sum of the number of matches)
+ *
+ * @access private
+ * @return array an array of the indices of the best matches
+ * (empty if no matches)
+ * @param string $supertype The general MIME Type to find (e.g. "text")
+ * @param string $subtype The MIME subtype to find (e.g. "html")
+ * @param array $params Parameters of Type to find ([level => 4])
+ * @param array $extensions Extension parameters to find
+ */
+ function _findBestMatchIndices($supertype, $subtype, $params, $extensions)
+ {
+ $bestmatches = array();
+ $bestlength = 0;
+
+ if (empty($this->acceptedtypes[$supertype])
+ || empty($this->acceptedtypes[$supertype][$subtype]))
+ return $bestmatches;
+
+ foreach ($this->acceptedtypes[$supertype][$subtype] as $index => $typematch) {
+ if (count(array_diff_assoc($typematch['PARAMS'], $params)) == 0
+ && count(array_diff_assoc($typematch['EXTENSIONS'],
+ $extensions)) == 0) {
+ $length = count($typematch['PARAMS'])
+ + count($typematch['EXTENSIONS']);
+
+ if ($length > $bestlength) {
+ $bestmatches = array($index);
+ $bestlength = $length;
+ } else if ($length == $bestlength) {
+ $bestmatches[] = $index;
+ }
+ }
+ }
+
+ return $bestmatches;
+ }
+}
+
+// vim: set ts=4 sts=4 sw=4 et:
+?>
diff --git a/lpf/ext/README.txt b/lpf/ext/README.txt
new file mode 100644
index 0000000..c8eddb6
--- /dev/null
+++ b/lpf/ext/README.txt
@@ -0,0 +1,11 @@
+These are class files that I've gathered from around the internet.
+
+I've renamed the files to follow a standard scheme.
+
+This is where each file came from:
+
+My Name : Original Name : From
+HTTP_Accept.class.php : HTTP_Accept.php : http://kevinlocke.name/programs/http_accept.php
+
+~ Luke Shumaker <http://lukeshu.ath.cx>
+Happy Hacking!
diff --git a/lpf/lib/Controller.class.php b/lpf/lib/Controller.class.php
new file mode 100644
index 0000000..05736ee
--- /dev/null
+++ b/lpf/lib/Controller.class.php
@@ -0,0 +1,30 @@
+<?php
+
+class Controller {
+ /**
+ * Show a $view, in the most appropriate format (according to file
+ * extension and HTTP Accept header). Pass the array $vars to the view.
+ */
+ protected function showView($view, $vars=null) {
+ if ($vars===null) { $vars = array(); }
+
+ $obj = new View($view);
+ $obj->show($vars);
+ }
+
+ // Here be default handlers ////////////////////////////////////////////
+
+ public function index($routed, $remainder) {
+ header('Content-type: text/plain');
+ echo " == Generic Controller Index == \n\n";
+ $routed_str = implode('/', $routed);
+ $remainder_str = implode('/', $remainder);
+ echo "Full path: $routed_str/$remainder_str\n";
+ echo "Controller path: $routed_str\n";
+ echo "Remainder path: $remainder_str\n";
+ }
+ public function http404($routed, $remainder) {
+ $this->showView('http404', array('routed'=>$routed,
+ 'remainder'=>$remainder));
+ }
+}
diff --git a/lpf/lib/Mime.class.php b/lpf/lib/Mime.class.php
new file mode 100644
index 0000000..f37c1eb
--- /dev/null
+++ b/lpf/lib/Mime.class.php
@@ -0,0 +1,45 @@
+<?php
+
+class Mime {
+ public static $mimes = array(
+ 'csv' => array(
+ 'text/csv', 'text/x-csv',
+ 'text/x-comma-separated-values',
+ 'text/comma-separated-values',
+ 'application/csv',
+ 'application/excel', 'application/vnd.msexcel'),
+ 'xhtml' => array('text/html', 'application/xhtml+xml'),
+ 'html' => array('text/html', 'application/xhtml+xml'),
+ 'bmp' => 'image/bmp',
+ 'gif' => 'image/gif',
+ 'jpeg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpg' => array('image/jpeg', 'image/pjpeg'),
+ 'jpe' => array('image/jpeg', 'image/pjpeg'),
+ 'png' => array('image/png', 'image/x-png'),
+ 'tiff' => 'image/tiff',
+ 'tif' => 'image/tiff',
+ 'css' => 'text/css',
+ 'htm' => 'text/html',
+ 'txt' => 'text/plain',
+ 'json' => array('application/json', 'text/json')
+ );
+
+ public static function ext2mime($ext) {
+ $mimes = self::$mimes;
+ $mime = $mimes[$ext];
+ if (!is_array($mime)) $mime = array($mime);
+ return $mime;
+ }
+ public static function mime2ext($my_mime) {
+ $ret = array();
+ foreach (self::mimes as $ext => $mime) {
+ if (is_array($mime)) {
+ $match = in_array($my_mime, $mime);
+ } else {
+ $match = $my_mime==$mime;
+ }
+ if ($match) $ret[] = $ext;
+ }
+ return $ret;
+ }
+} \ No newline at end of file
diff --git a/lpf/lib/Model.class.php b/lpf/lib/Model.class.php
new file mode 100644
index 0000000..0cce525
--- /dev/null
+++ b/lpf/lib/Model.class.php
@@ -0,0 +1,9 @@
+<?php
+require_once('Database.class.php');
+
+abstract class Model {
+ protected $db;
+ public function __construct() {
+ $this->db = Database::getInstance();
+ }
+}
diff --git a/lpf/lib/Router.class.php b/lpf/lib/Router.class.php
new file mode 100644
index 0000000..238e3f8
--- /dev/null
+++ b/lpf/lib/Router.class.php
@@ -0,0 +1,110 @@
+<?php
+
+require_once('Controller.class.php');
+
+class Router {
+ /**
+ * Array mapping URIs to controllers.
+ * A controller may register itself either by using
+ * Router::register($URI, $controller[, $function]);
+ * or by adding itself to the $ROUTER global.
+ *
+ * The default here just gives us a 404 handler.
+ */
+ private $routes = array('/*' => 'Http404');
+
+ /**
+ * Instantiate a router that looks for controllers in $controllerpath.
+ */
+ public function Router($controllerpath) {
+ // create a $ROUTES global that can be used to set up our
+ // $this->routes.
+ global $ROUTES;
+ $ROUTES = $this->routes;
+
+ // Split $controllerpath into directories, and load the
+ // controllers in each.
+ $dirs = explode(PATH_SEPARATOR, $controllerpath);
+ foreach ($dirs as $dir) {
+ // Find all files in $dir with the ext `.class.php'
+ $files = glob($dir.'/*.class.php');
+ foreach ($files as $file) {
+ // and include them
+ require_once($file);
+ }
+ }
+
+ $this->routes = $ROUTES;
+ unset($ROUTES);
+ }
+
+ /**
+ * Route the page at the relative URL $page to the appropriate
+ * controller, and call the appropriate function.
+ */
+ public function route($page) {
+ $parts = explode('/', $page);
+ $length = count($parts); // the # of segments in $controllerpart
+
+ // if $page ends in "/", strip that off
+ if ($parts[$length-1]=='') {
+ array_pop($parts);
+ $length--;
+ }
+
+ $controllerpart = implode('/', $parts);
+
+ // Keep shortening $controllerpart until it matches something in
+ // $this->routes. The shortest it will ever become is '/*'.
+ // If no key exists for '/*', that's an infinite loop.
+ // Fortunately, the default value of $this->routes directs '/*'
+ // to the Http404 controller.
+ while(!isset($this->routes[$controllerpart])) {
+ $some_parts = array_slice($parts, 0, $length);
+ $controllerpart = implode('/', $some_parts).'/*';
+ $length--;
+ }
+ $length++;
+
+ // Figure what function to call on what controller
+ // Grammar Nazi Warning: `what' or `which'?
+ $controller = $this->routes[$controllerpart];
+ if (strpos($controller, '->')===false) {
+ // imply function
+ $function = $parts[$length];
+ } else {
+ preg_match('/(.*)->(.*)/', $controller, $matches);
+ $controller = $matches[1];
+ $function = $matches[2];
+ }
+
+ // Default to the `index' function, provided by all controllers
+ if ($function=='') {
+ $function = 'index';
+ }
+
+ // We will pass these arrays to the function.
+ $routed = array_slice($parts, 0, $length);
+ $remainder = array_slice($parts, $length);
+
+ // Finally, run the controller
+ $obj = new $controller();
+ if (in_array($function, get_class_methods($obj))) {
+ call_user_func(array($obj, $function),
+ $routed, $remainder);
+ } else {
+ $obj->http404($routed, $remainder);
+ }
+ }
+
+ /**
+ * This is to allow controllers to register themselves to the router.
+ * If $function=='', then the function will be determined by the segment
+ * to the right of the last segment in $path
+ */
+ public static function register($path, $controller, $function='') {
+ $str = $controller.(($function=='')?'':'->'.$function);
+ global $ROUTES;
+ $ROUTES[$path] = $str;
+ }
+} \ No newline at end of file
diff --git a/lpf/lib/View.class.php b/lpf/lib/View.class.php
new file mode 100644
index 0000000..d7a21d3
--- /dev/null
+++ b/lpf/lib/View.class.php
@@ -0,0 +1,135 @@
+<?php
+
+require_once('Mime.class.php');
+require_once('HTTP_Accept.class.php');
+
+/**
+ * A class to handle loading and evaluating the appropriateness of different
+ * views.
+ *
+ * Depends on the state of the following variables/constants:
+ * PAGE_EXT
+ * VIEWPATH
+ * $_SERVER['HTTP_ACCEPT']
+ */
+class View {
+ private $extensions = array();
+ private $ext = '';
+ private $view = '';
+
+ /**
+ * Return the filename of the view file with extension $ext
+ */
+ private static function filename($view, $ext) {
+ return VIEWPATH."/pages/$view.$ext.php";
+ }
+
+ /**
+ * Choose between $extensions, which all have equal quality.
+ */
+ private function chooseBetweenEquals($extensions) {
+ if (count($extensions)<1) return false;
+ $pref = array('html');
+ foreach ($pref as $ext) {
+ if (in_array($ext, $extensions)) return $ext;
+ }
+ return $extensions[0]; // XXX: Worst. Solution. Ever.
+ }
+
+ /**
+ * Find the best available extension to use, based on file extension and
+ * HTTP 'Accept' headers.
+ */
+ private function chooseExtension($view) {
+ // Make a list of candidate extensions for this view.
+ $files = glob(self::filename($view, '*'));
+ $this->extensions = array();
+ $regex = '@'.preg_quote(VIEWPATH,'@').'[^.]*\.(.*)\.php$@';
+ foreach ($files as $file) {
+ $ext = preg_replace($regex, '$1', $file);
+ $this->extensions[] = $ext;
+ }
+ if (count($this->extensions)<1) return false;
+
+ if (PAGE_EXT != '') {
+ // First, do a check if we can just return requested
+ // file extension:
+ if (in_array(PAGE_EXT, $this->extensions)) {
+ return PAGE_EXT;
+ }
+
+ // Check for other extensions with the same mime type as
+ // the requested:
+ $accept_str = implode(', ', Mime::ext2mime(PAGE_EXT));
+ $extensions = $this->parseAccept($view, $accept_str);
+ if ($ext = $this->chooseBetweenEquals($extensions)) return $ext;
+ }
+
+ // Check for other extensions based on HTTP 'Accept' headers:
+ $accept_str = $_SERVER['HTTP_ACCEPT'];
+ $extensions = $this->parseAccept($view, $accept_str);
+ if ($ext = $this->chooseBetweenEquals($extensions)) return $ext;
+
+ // Well, all the good options failed, so let's see what we've
+ // got left:
+ $extensions = $this->extensions;
+ return $this->chooseBetweenEquals($extensions);
+
+ }
+
+ private function parseAccept($view, $accept_str) {
+ // $prefs is a associative array where the key is the file
+ // extension, and the value is how much we like that extension.
+ // Higher numbers are better.
+ $prefs = array();
+
+ $accept = new HTTP_Accept($accept_str);
+
+ // Loop through the candidate views, and record how much we
+ // like each.
+ foreach ($this->extensions as $ext) {
+ $mimes = Mime::ext2mime($ext);
+ foreach ($mimes as $mime) {
+ $quality = $accept->getQuality($mime);
+ if (isset($prefs[$ext])) {
+ $quality = max($prefs[$ext], $quality);
+ }
+ $prefs[$ext] = $quality;
+ }
+ }
+
+ // Create an array of all extensions tied for the top quality.
+ $ret = array();
+ $top_quality = 0.001;// minimum acceptable according to RFC 2616
+ foreach ($prefs as $ext => $quality) {
+ if ($quality > $top_quality) {
+ $top_quality = $quality;
+ $ret = array();
+ }
+ if ($quality == $top_quality) {
+ $ret[] = $ext;
+ }
+ }
+ return $ret;
+ }
+
+ public function __construct($view) {
+ $this->ext = $this->chooseExtension($view);
+ $this->view = $view;
+ }
+
+ public function show($vars) {
+ $file = self::filename($this->view, $this->ext);
+ $mimes = Mime::ext2mime($this->ext);
+
+ header('Content-type: '.$mimes[0]);
+
+ require_once(VIEWPATH.'/Template.class.php');
+ $vars['template'] = new Template();
+
+ global $VARS;
+ $VARS = $vars;
+ include($file);
+ unset($VARS);
+ }
+}
diff --git a/lpf/views/pages/no-conf.html.php b/lpf/views/pages/no-conf.html.php
new file mode 100644
index 0000000..1f4e3d3
--- /dev/null
+++ b/lpf/views/pages/no-conf.html.php
@@ -0,0 +1,8 @@
+<?php global $VARS;
+$t = $VARS['template'];
+
+$t->header('Message Manager');
+$t->paragraph('Awe shiz, dude, conf.php doesn\'t exist, you '.
+ 'need to go through the '.
+ '<a href="installer">installer</a>.');
+$t->footer();