diff options
| author | 2014-01-29 21:26:19 +0100 | |
|---|---|---|
| committer | 2014-01-29 21:26:19 +0100 | |
| commit | 165eb57459a152b3cc6aa3fd15ca990c3d908829 (patch) | |
| tree | 640ddacbc6eabc985646dd65b0578f369ea92321 /lib | |
| parent | 75096e6a39fe5d34d3951991f296f616e62a9fd8 (diff) | |
| parent | c053825ff8f9792e692c101585481129b006937b (diff) | |
Sortie de la version 0.70.7.0
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/.htaccess | 3 | ||||
| -rw-r--r-- | lib/JSON.php | 933 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/ActionController.php (renamed from lib/minz/ActionController.php) | 4 | ||||
| -rw-r--r-- | lib/Minz/ActionException.php | 9 | ||||
| -rw-r--r-- | lib/Minz/BadConfigurationException.php | 9 | ||||
| -rw-r--r-- | lib/Minz/Cache.php (renamed from lib/minz/Minz_Cache.php) | 6 | ||||
| -rw-r--r-- | lib/Minz/Configuration.php | 340 | ||||
| -rw-r--r-- | lib/Minz/ControllerNotActionControllerException.php | 9 | ||||
| -rw-r--r-- | lib/Minz/ControllerNotExistException.php | 9 | ||||
| -rw-r--r-- | lib/Minz/CurrentPagePaginationException.php | 8 | ||||
| -rw-r--r-- | lib/Minz/Dispatcher.php (renamed from lib/minz/Dispatcher.php) | 79 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Error.php (renamed from lib/minz/Error.php) | 40 | ||||
| -rw-r--r-- | lib/Minz/Exception.php | 16 | ||||
| -rw-r--r-- | lib/Minz/FileNotExistException.php | 8 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/FrontController.php (renamed from lib/minz/FrontController.php) | 97 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Helper.php (renamed from lib/minz/Helper.php) | 2 | ||||
| -rw-r--r-- | lib/Minz/Log.php (renamed from lib/minz/Minz_Log.php) | 62 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Model.php (renamed from lib/minz/Model.php) | 2 | ||||
| -rw-r--r-- | lib/Minz/ModelArray.php | 81 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/ModelPdo.php (renamed from lib/minz/dao/Model_pdo.php) | 49 | ||||
| -rw-r--r-- | lib/Minz/PDOConnectionException.php | 9 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Paginator.php (renamed from lib/minz/Paginator.php) | 2 | ||||
| -rw-r--r-- | lib/Minz/PermissionDeniedException.php | 8 | ||||
| -rw-r--r-- | lib/Minz/Request.php (renamed from lib/minz/Request.php) | 30 | ||||
| -rw-r--r-- | lib/Minz/Response.php (renamed from lib/minz/Response.php) | 2 | ||||
| -rw-r--r-- | lib/Minz/RouteNotFoundException.php | 16 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Router.php (renamed from lib/minz/Router.php) | 34 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Session.php (renamed from lib/minz/Session.php) | 49 | ||||
| -rw-r--r-- | lib/Minz/Translate.php (renamed from lib/minz/Translate.php) | 6 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/Url.php (renamed from lib/minz/Url.php) | 33 | ||||
| -rw-r--r--[-rwxr-xr-x] | lib/Minz/View.php (renamed from lib/minz/View.php) | 44 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie.php | 30 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/File.php | 1 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Misc.php | 4 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Parser.php | 31 | ||||
| -rw-r--r-- | lib/SimplePie/SimplePie/Sanitize.php | 53 | ||||
| -rw-r--r-- | lib/SimplePie_autoloader.php | 86 | ||||
| -rw-r--r-- | lib/http-conditional.php | 4 | ||||
| -rw-r--r-- | lib/lib_opml.php | 121 | ||||
| -rw-r--r-- | lib/lib_rss.php | 350 | ||||
| -rwxr-xr-x | lib/minz/Configuration.php | 250 | ||||
| -rwxr-xr-x | lib/minz/dao/Model_array.php | 122 | ||||
| -rwxr-xr-x | lib/minz/dao/Model_txt.php | 84 | ||||
| -rw-r--r-- | lib/minz/exceptions/MinzException.php | 94 | ||||
| -rw-r--r-- | lib/password_compat.php | 279 |
45 files changed, 2406 insertions, 1102 deletions
diff --git a/lib/.htaccess b/lib/.htaccess new file mode 100644 index 000000000..9e768397d --- /dev/null +++ b/lib/.htaccess @@ -0,0 +1,3 @@ +Order Allow,Deny +Deny from all +Satisfy all diff --git a/lib/JSON.php b/lib/JSON.php new file mode 100644 index 000000000..8dc8a6f01 --- /dev/null +++ b/lib/JSON.php @@ -0,0 +1,933 @@ +<?php +/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ +/** + * Converts to and from JSON format. + * + * JSON (JavaScript Object Notation) is a lightweight data-interchange + * format. It is easy for humans to read and write. It is easy for machines + * to parse and generate. It is based on a subset of the JavaScript + * Programming Language, Standard ECMA-262 3rd Edition - December 1999. + * This feature can also be found in Python. JSON is a text format that is + * completely language independent but uses conventions that are familiar + * to programmers of the C-family of languages, including C, C++, C#, Java, + * JavaScript, Perl, TCL, and many others. These properties make JSON an + * ideal data-interchange language. + * + * This package provides a simple encoder and decoder for JSON notation. It + * is intended for use with client-side Javascript applications that make + * use of HTTPRequest to perform server communication functions - data can + * be encoded into JSON notation for use in a client-side javascript, or + * decoded from incoming Javascript requests. JSON format is native to + * Javascript, and can be directly eval()'ed with no further parsing + * overhead + * + * All strings should be in ASCII or UTF-8 format! + * + * LICENSE: Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: Redistributions of source code must retain the + * above copyright notice, this list of conditions and the following + * disclaimer. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @category + * @package Services_JSON + * @author Michal Migurski <mike-json@teczno.com> + * @author Matt Knapp <mdknapp[at]gmail[dot]com> + * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com> + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_USE_TO_JSON', 64); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * <code> + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * </code> + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects + * It serializes the return value from the toJSON call rather + * than the object it'self, toJSON can return associative arrays, + * strings or numbers, if you return an object, make sure it does + * not have a toJSON method, otherwise an error will occur. + */ + function Services_JSON($use = 0) + { + $this->use = $use; + $this->_mb_strlen = function_exists('mb_strlen'); + $this->_mb_convert_encoding = function_exists('mb_convert_encoding'); + $this->_mb_substr = function_exists('mb_substr'); + } + // private - cache the mbstring lookup results.. + var $_mb_strlen = false; + var $_mb_substr = false; + var $_mb_convert_encoding = false; + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if($this->_mb_convert_encoding) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch($this->strlen8($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format (and sends JSON Header) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + header('Content-type: application/json'); + return $this->encodeUnsafe($var); + } + /** + * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!) + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encodeUnsafe($var) + { + // see bug #16908 - regarding numeric locale printing + $lc = setlocale(LC_NUMERIC, 0); + setlocale(LC_NUMERIC, 'C'); + $ret = $this->_encode($var); + setlocale(LC_NUMERIC, $lc); + return $ret; + + } + /** + * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function _encode($var) + { + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = $this->strlen8($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+1 >= $strlen_var) { + $c += 1; + $ascii .= '?'; + break; + } + + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + if ($c+2 >= $strlen_var) { + $c += 2; + $ascii .= '?'; + break; + } + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + @ord($var{$c + 1}), + @ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + if ($c+3 >= $strlen_var) { + $c += 3; + $ascii .= '?'; + break; + } + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + if ($c+4 >= $strlen_var) { + $c += 4; + $ascii .= '?'; + break; + } + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + if ($c+5 >= $strlen_var) { + $c += 5; + $ascii .= '?'; + break; + } + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, '_encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + + // support toJSON methods. + if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) { + // this may end up allowing unlimited recursion + // so we check the return value to make sure it's not got the same method. + $recode = $var->toJSON(); + + if (method_exists($recode, 'toJSON')) { + + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(class_name($var). + " toJSON returned an object with a toJSON method."); + + } + + return $this->_encode( $recode ); + } + + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->_encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->_encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = $this->substr8($str, 0, 1); + $chrs = $this->substr8($str, 1, -1); + $utf8 = ''; + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2))) + . chr(hexdec($this->substr8($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= $this->substr8($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = $this->substr8($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = $this->strlen8($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = $this->substr8($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = $this->substr8($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B")); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } + + /** + * Calculates length of string in bytes + * @param string + * @return integer length + */ + function strlen8( $str ) + { + if ( $this->_mb_strlen ) { + return mb_strlen( $str, "8bit" ); + } + return strlen( $str ); + } + + /** + * Returns part of a string, interpreting $start and $length as number of bytes. + * @param string + * @param integer start + * @param integer length + * @return integer length + */ + function substr8( $string, $start, $length=false ) + { + if ( $length === false ) { + $length = $this->strlen8( $string ) - $start; + } + if ( $this->_mb_substr ) { + return mb_substr( $string, $start, $length, "8bit" ); + } + return substr( $string, $start, $length ); + } + +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} diff --git a/lib/minz/ActionController.php b/lib/Minz/ActionController.php index ab9389dbd..409d9611f 100755..100644 --- a/lib/minz/ActionController.php +++ b/lib/Minz/ActionController.php @@ -7,7 +7,7 @@ /** * La classe ActionController représente le contrôleur de l'application */ -class ActionController { +class Minz_ActionController { protected $router; protected $view; @@ -18,7 +18,7 @@ class ActionController { */ public function __construct ($router) { $this->router = $router; - $this->view = new View (); + $this->view = new Minz_View (); $this->view->attributeParams (); } diff --git a/lib/Minz/ActionException.php b/lib/Minz/ActionException.php new file mode 100644 index 000000000..c566a076f --- /dev/null +++ b/lib/Minz/ActionException.php @@ -0,0 +1,9 @@ +<?php +class Minz_ActionException extends Minz_Exception { + public function __construct ($controller_name, $action_name, $code = self::ERROR) { + $message = '`' . $action_name . '` cannot be invoked on `' + . $controller_name . '`'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/Minz/BadConfigurationException.php b/lib/Minz/BadConfigurationException.php new file mode 100644 index 000000000..a7b77d687 --- /dev/null +++ b/lib/Minz/BadConfigurationException.php @@ -0,0 +1,9 @@ +<?php +class Minz_BadConfigurationException extends Minz_Exception { + public function __construct ($part_missing, $code = self::ERROR) { + $message = '`' . $part_missing + . '` in the configuration file is missing or is misconfigured'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/minz/Minz_Cache.php b/lib/Minz/Cache.php index 6848e3350..fcb627eb2 100644 --- a/lib/minz/Minz_Cache.php +++ b/lib/Minz/Cache.php @@ -35,7 +35,7 @@ class Minz_Cache { * Setteurs */ public function _fileName () { - $file = md5 (Request::getURI ()); + $file = md5 (Minz_Request::getURI ()); $this->file = CACHE_PATH . '/'.$file; } @@ -43,7 +43,7 @@ class Minz_Cache { public function _expire () { if ($this->exist ()) { $this->expire = filemtime ($this->file) - + Configuration::delayCache (); + + Minz_Configuration::delayCache (); } } @@ -52,7 +52,7 @@ class Minz_Cache { * @return true si activé, false sinon */ public static function isEnabled () { - return Configuration::cacheEnabled () && self::$enabled; + return Minz_Configuration::cacheEnabled () && self::$enabled; } /** diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php new file mode 100644 index 000000000..572b9984d --- /dev/null +++ b/lib/Minz/Configuration.php @@ -0,0 +1,340 @@ +<?php +/** + * MINZ - Copyright 2011 Marien Fressinaud + * Sous licence AGPL3 <http://www.gnu.org/licenses/> +*/ + +/** + * La classe Configuration permet de gérer la configuration de l'application + */ +class Minz_Configuration { + const CONF_PATH_NAME = '/config.php'; + + /** + * VERSION est la version actuelle de MINZ + */ + const VERSION = '1.3.1.freshrss'; // version spéciale FreshRSS + + /** + * valeurs possibles pour l'"environment" + * SILENT rend l'application muette (pas de log) + * PRODUCTION est recommandée pour une appli en production + * (log les erreurs critiques) + * DEVELOPMENT log toutes les erreurs + */ + const SILENT = 0; + const PRODUCTION = 1; + const DEVELOPMENT = 2; + + /** + * définition des variables de configuration + * $salt une chaîne de caractères aléatoires (obligatoire) + * $environment gère le niveau d'affichage pour log et erreurs + * $use_url_rewriting indique si on utilise l'url_rewriting + * $base_url le chemin de base pour accéder à l'application + * $title le nom de l'application + * $language la langue par défaut de l'application + * $cacheEnabled permet de savoir si le cache doit être activé + * $delayCache la limite de cache + * $db paramètres pour la base de données (tableau) + * - host le serveur de la base + * - user nom d'utilisateur + * - password mot de passe de l'utilisateur + * - base le nom de la base de données + */ + private static $salt = ''; + private static $environment = Minz_Configuration::PRODUCTION; + private static $base_url = ''; + private static $use_url_rewriting = false; + private static $title = ''; + private static $language = 'en'; + private static $cache_enabled = false; + private static $delay_cache = 3600; + private static $default_user = ''; + private static $allow_anonymous = false; + private static $auth_type = 'none'; + + private static $db = array ( + 'type' => 'mysql', + 'host' => '', + 'user' => '', + 'password' => '', + 'base' => '', + 'prefix' => '', + ); + + /* + * Getteurs + */ + public static function salt () { + return self::$salt; + } + public static function environment ($str = false) { + $env = self::$environment; + + if ($str) { + switch (self::$environment) { + case self::SILENT: + $env = 'silent'; + break; + case self::DEVELOPMENT: + $env = 'development'; + break; + case self::PRODUCTION: + default: + $env = 'production'; + } + } + + return $env; + } + public static function baseUrl () { + return self::$base_url; + } + public static function useUrlRewriting () { + return self::$use_url_rewriting; + } + public static function title () { + return self::$title; + } + public static function language () { + return self::$language; + } + public static function cacheEnabled () { + return self::$cache_enabled; + } + public static function delayCache () { + return self::$delay_cache; + } + public static function dataBase () { + return self::$db; + } + public static function defaultUser () { + return self::$default_user; + } + public static function isAdmin($currentUser) { + return $currentUser === self::$default_user; + } + public static function allowAnonymous() { + return self::$allow_anonymous; + } + public static function authType() { + return self::$auth_type; + } + public static function needsLogin() { + return self::$auth_type !== 'none'; + } + public static function canLogIn() { + return self::$auth_type === 'form' || self::$auth_type === 'persona'; + } + + public static function _allowAnonymous($allow = false) { + self::$allow_anonymous = ((bool)$allow) && self::canLogIn(); + } + public static function _authType($value) { + $value = strtolower($value); + switch ($value) { + case 'form': + case 'http_auth': + case 'persona': + case 'none': + self::$auth_type = $value; + break; + } + self::_allowAnonymous(self::$allow_anonymous); + } + + /** + * Initialise les variables de configuration + * @exception Minz_FileNotExistException si le CONF_PATH_NAME n'existe pas + * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté + */ + public static function init () { + try { + self::parseFile (); + self::setReporting (); + } catch (Minz_FileNotExistException $e) { + throw $e; + } catch (Minz_BadConfigurationException $e) { + throw $e; + } + } + + public static function writeFile() { + $ini_array = array( + 'general' => array( + 'environment' => self::environment(true), + 'use_url_rewriting' => self::$use_url_rewriting, + 'salt' => self::$salt, + 'base_url' => self::$base_url, + 'title' => self::$title, + 'default_user' => self::$default_user, + 'allow_anonymous' => self::$allow_anonymous, + 'auth_type' => self::$auth_type, + ), + 'db' => self::$db, + ); + @rename(DATA_PATH . self::CONF_PATH_NAME, DATA_PATH . self::CONF_PATH_NAME . '.bak.php'); + $result = file_put_contents(DATA_PATH . self::CONF_PATH_NAME, "<?php\n return " . var_export($ini_array, true) . ';'); + if (function_exists('opcache_invalidate')) { + opcache_invalidate(DATA_PATH . self::CONF_PATH_NAME); //Clear PHP 5.5+ cache for include + } + return (bool)$result; + } + + /** + * Parse un fichier de configuration + * @exception Minz_PermissionDeniedException si le CONF_PATH_NAME n'est pas accessible + * @exception Minz_BadConfigurationException si CONF_PATH_NAME mal formaté + */ + private static function parseFile () { + $ini_array = include(DATA_PATH . self::CONF_PATH_NAME); + + if (!is_array($ini_array)) { + throw new Minz_PermissionDeniedException ( + DATA_PATH . self::CONF_PATH_NAME, + Minz_Exception::ERROR + ); + } + + // [general] est obligatoire + if (!isset ($ini_array['general'])) { + throw new Minz_BadConfigurationException ( + '[general]', + Minz_Exception::ERROR + ); + } + $general = $ini_array['general']; + + // salt est obligatoire + if (!isset ($general['salt'])) { + if (isset($general['sel_application'])) { //v0.6 + $general['salt'] = $general['sel_application']; + } else { + throw new Minz_BadConfigurationException ( + 'salt', + Minz_Exception::ERROR + ); + } + } + self::$salt = $general['salt']; + + if (isset ($general['environment'])) { + switch ($general['environment']) { + case 'silent': + self::$environment = Minz_Configuration::SILENT; + break; + case 'development': + self::$environment = Minz_Configuration::DEVELOPMENT; + break; + case 'production': + self::$environment = Minz_Configuration::PRODUCTION; + break; + default: + if ($general['environment'] >= 0 && + $general['environment'] <= 2) { + // fallback 0.7-beta + self::$environment = $general['environment']; + } else { + throw new Minz_BadConfigurationException ( + 'environment', + Minz_Exception::ERROR + ); + } + } + + } + if (isset ($general['base_url'])) { + self::$base_url = $general['base_url']; + } + if (isset ($general['use_url_rewriting'])) { + self::$use_url_rewriting = $general['use_url_rewriting']; + } + + if (isset ($general['title'])) { + self::$title = $general['title']; + } + if (isset ($general['language'])) { + self::$language = $general['language']; + } + if (isset ($general['cache_enabled'])) { + self::$cache_enabled = $general['cache_enabled']; + if (CACHE_PATH === false && self::$cache_enabled) { + throw new FileNotExistException ( + 'CACHE_PATH', + Minz_Exception::ERROR + ); + } + } + if (isset ($general['delay_cache'])) { + self::$delay_cache = inval($general['delay_cache']); + } + if (isset ($general['default_user'])) { + self::$default_user = $general['default_user']; + } + if (isset ($general['auth_type'])) { + self::_authType($general['auth_type']); + } + if (isset ($general['allow_anonymous'])) { + self::$allow_anonymous = ((bool)($general['allow_anonymous'])) && ($general['allow_anonymous'] !== 'no'); + } + + // Base de données + if (isset ($ini_array['db'])) { + $db = $ini_array['db']; + if (empty($db['host'])) { + throw new Minz_BadConfigurationException ( + 'host', + Minz_Exception::ERROR + ); + } + if (empty($db['user'])) { + throw new Minz_BadConfigurationException ( + 'user', + Minz_Exception::ERROR + ); + } + if (!isset ($db['password'])) { + throw new Minz_BadConfigurationException ( + 'password', + Minz_Exception::ERROR + ); + } + if (empty($db['base'])) { + throw new Minz_BadConfigurationException ( + 'base', + Minz_Exception::ERROR + ); + } + + if (!empty($db['type'])) { + self::$db['type'] = $db['type']; + } + self::$db['host'] = $db['host']; + self::$db['user'] = $db['user']; + self::$db['password'] = $db['password']; + self::$db['base'] = $db['base']; + if (isset($db['prefix'])) { + self::$db['prefix'] = $db['prefix']; + } + } + } + + private static function setReporting() { + switch (self::$environment) { + case self::PRODUCTION: + error_reporting(E_ALL); + ini_set('display_errors','Off'); + ini_set('log_errors', 'On'); + break; + case self::DEVELOPMENT: + error_reporting(E_ALL); + ini_set('display_errors','On'); + ini_set('log_errors', 'On'); + break; + case self::SILENT: + error_reporting(0); + break; + } + } +} diff --git a/lib/Minz/ControllerNotActionControllerException.php b/lib/Minz/ControllerNotActionControllerException.php new file mode 100644 index 000000000..535a1377e --- /dev/null +++ b/lib/Minz/ControllerNotActionControllerException.php @@ -0,0 +1,9 @@ +<?php +class Minz_ControllerNotActionControllerException extends Minz_Exception { + public function __construct ($controller_name, $code = self::ERROR) { + $message = 'Controller `' . $controller_name + . '` isn\'t instance of ActionController'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/Minz/ControllerNotExistException.php b/lib/Minz/ControllerNotExistException.php new file mode 100644 index 000000000..523867d11 --- /dev/null +++ b/lib/Minz/ControllerNotExistException.php @@ -0,0 +1,9 @@ +<?php +class Minz_ControllerNotExistException extends Minz_Exception { + public function __construct ($controller_name, $code = self::ERROR) { + $message = 'Controller `' . $controller_name + . '` doesn\'t exist'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/Minz/CurrentPagePaginationException.php b/lib/Minz/CurrentPagePaginationException.php new file mode 100644 index 000000000..74214d879 --- /dev/null +++ b/lib/Minz/CurrentPagePaginationException.php @@ -0,0 +1,8 @@ +<?php +class Minz_CurrentPagePaginationException extends Minz_Exception { + public function __construct ($page) { + $message = 'Page number `' . $page . '` doesn\'t exist'; + + parent::__construct ($message, self::ERROR); + } +} diff --git a/lib/minz/Dispatcher.php b/lib/Minz/Dispatcher.php index 0cfdd8e75..71dfe8ac6 100644 --- a/lib/minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -9,8 +9,8 @@ * déterminée dans la Request * C'est un singleton */ -class Dispatcher { - const CONTROLLERS_PATH_NAME = '/controllers'; +class Minz_Dispatcher { + const CONTROLLERS_PATH_NAME = '/Controllers'; /* singleton */ private static $instance = null; @@ -22,8 +22,8 @@ class Dispatcher { * Récupère l'instance du Dispatcher */ public static function getInstance ($router) { - if (is_null (self::$instance)) { - self::$instance = new Dispatcher ($router); + if (self::$instance === null) { + self::$instance = new Minz_Dispatcher ($router); } return self::$instance; } @@ -38,44 +38,51 @@ class Dispatcher { /** * Lance le controller indiqué dans Request * Remplit le body de Response à partir de la Vue - * @exception MinzException + * @exception Minz_Exception */ - public function run () { + public function run ($ob = true) { $cache = new Minz_Cache(); // Le ob_start est dupliqué : sans ça il y a un bug sous Firefox // ici on l'appelle avec 'ob_gzhandler', après sans. // Vraisemblablement la compression fonctionne mais c'est sale // J'ignore les effets de bord :( - ob_start ('ob_gzhandler'); + if ($ob) { + ob_start ('ob_gzhandler'); + } if (Minz_Cache::isEnabled () && !$cache->expired ()) { - ob_start (); + if ($ob) { + ob_start (); + } $cache->render (); - $text = ob_get_clean(); + if ($ob) { + $text = ob_get_clean(); + } } else { - while (Request::$reseted) { - Request::$reseted = false; + $text = ''; //TODO: Clean this code + while (Minz_Request::$reseted) { + Minz_Request::$reseted = false; try { - $this->createController ( - Request::controllerName () - . 'Controller' - ); - + $this->createController ('FreshRSS_' . Minz_Request::controllerName () . '_Controller'); $this->controller->init (); $this->controller->firstAction (); $this->launchAction ( - Request::actionName () + Minz_Request::actionName () . 'Action' ); $this->controller->lastAction (); - if (!Request::$reseted) { - ob_start (); + if (!Minz_Request::$reseted) { + if ($ob) { + ob_start (); + } $this->controller->view ()->build (); - $text = ob_get_clean(); + if ($ob) { + $text = ob_get_clean(); + } } - } catch (MinzException $e) { + } catch (Minz_Exception $e) { throw $e; } } @@ -85,14 +92,12 @@ class Dispatcher { } } - Response::setBody ($text); + Minz_Response::setBody ($text); } /** * Instancie le Controller * @param $controller_name le nom du controller à instancier - * @exception FileNotExistException le fichier correspondant au - * > controller n'existe pas * @exception ControllerNotExistException le controller n'existe pas * @exception ControllerNotActionControllerException controller n'est * > pas une instance de ActionController @@ -101,26 +106,18 @@ class Dispatcher { $filename = APP_PATH . self::CONTROLLERS_PATH_NAME . '/' . $controller_name . '.php'; - if (!file_exists ($filename)) { - throw new FileNotExistException ( - $filename, - MinzException::ERROR - ); - } - require_once ($filename); - if (!class_exists ($controller_name)) { - throw new ControllerNotExistException ( + throw new Minz_ControllerNotExistException ( $controller_name, - MinzException::ERROR + Minz_Exception::ERROR ); } $this->controller = new $controller_name ($this->router); - if (! ($this->controller instanceof ActionController)) { - throw new ControllerNotActionControllerException ( + if (! ($this->controller instanceof Minz_ActionController)) { + throw new Minz_ControllerNotActionControllerException ( $controller_name, - MinzException::ERROR + Minz_Exception::ERROR ); } } @@ -129,18 +126,18 @@ class Dispatcher { * Lance l'action sur le controller du dispatcher * @param $action_name le nom de l'action * @exception ActionException si on ne peut pas exécuter l'action sur - * > le controller + * le controller */ private function launchAction ($action_name) { - if (!Request::$reseted) { + if (!Minz_Request::$reseted) { if (!is_callable (array ( $this->controller, $action_name ))) { - throw new ActionException ( + throw new Minz_ActionException ( get_class ($this->controller), $action_name, - MinzException::ERROR + Minz_Exception::ERROR ); } call_user_func (array ( diff --git a/lib/minz/Error.php b/lib/Minz/Error.php index 0e8c2f60b..337ab6c0a 100755..100644 --- a/lib/minz/Error.php +++ b/lib/Minz/Error.php @@ -1,5 +1,5 @@ <?php -/** +/** * MINZ - Copyright 2011 Marien Fressinaud * Sous licence AGPL3 <http://www.gnu.org/licenses/> */ @@ -7,7 +7,7 @@ /** * La classe Error permet de lancer des erreurs HTTP */ -class Error { +class Minz_Error { public function __construct () { } /** @@ -21,28 +21,28 @@ class Error { */ public static function error ($code = 404, $logs = array (), $redirect = false) { $logs = self::processLogs ($logs); - $error_filename = APP_PATH . '/controllers/errorController.php'; - + $error_filename = APP_PATH . '/Controllers/errorController.php'; + if (file_exists ($error_filename)) { $params = array ( 'code' => $code, 'logs' => $logs ); - - Response::setHeader ($code); + + Minz_Response::setHeader ($code); if ($redirect) { - Request::forward (array ( + Minz_Request::forward (array ( 'c' => 'error' ), true); } else { - Request::forward (array ( + Minz_Request::forward (array ( 'c' => 'error', 'params' => $params ), false); } } else { $text = '<h1>An error occured</h1>'."\n"; - + if (!empty ($logs)) { $text .= '<ul>'."\n"; foreach ($logs as $log) { @@ -50,14 +50,14 @@ class Error { } $text .= '</ul>'."\n"; } - - Response::setHeader ($code); - Response::setBody ($text); - Response::send (); + + Minz_Response::setHeader ($code); + Minz_Response::setBody ($text); + Minz_Response::send (); exit (); } } - + /** * Permet de retourner les logs de façon à n'avoir que * ceux que l'on veut réellement @@ -66,12 +66,12 @@ class Error { * > en fonction de l'environment */ private static function processLogs ($logs) { - $env = Configuration::environment (); + $env = Minz_Configuration::environment (); $logs_ok = array (); $error = array (); $warning = array (); $notice = array (); - + if (isset ($logs['error'])) { $error = $logs['error']; } @@ -81,14 +81,14 @@ class Error { if (isset ($logs['notice'])) { $notice = $logs['notice']; } - - if ($env == Configuration::PRODUCTION) { + + if ($env == Minz_Configuration::PRODUCTION) { $logs_ok = $error; } - if ($env == Configuration::DEVELOPMENT) { + if ($env == Minz_Configuration::DEVELOPMENT) { $logs_ok = array_merge ($error, $warning, $notice); } - + return $logs_ok; } } diff --git a/lib/Minz/Exception.php b/lib/Minz/Exception.php new file mode 100644 index 000000000..b5e71e0d8 --- /dev/null +++ b/lib/Minz/Exception.php @@ -0,0 +1,16 @@ +<?php +class Minz_Exception extends Exception { + const ERROR = 0; + const WARNING = 10; + const NOTICE = 20; + + public function __construct ($message, $code = self::ERROR) { + if ($code != Minz_Exception::ERROR + && $code != Minz_Exception::WARNING + && $code != Minz_Exception::NOTICE) { + $code = Minz_Exception::ERROR; + } + + parent::__construct ($message, $code); + } +} diff --git a/lib/Minz/FileNotExistException.php b/lib/Minz/FileNotExistException.php new file mode 100644 index 000000000..f8dfbdf66 --- /dev/null +++ b/lib/Minz/FileNotExistException.php @@ -0,0 +1,8 @@ +<?php +class Minz_FileNotExistException extends Minz_Exception { + public function __construct ($file_name, $code = self::ERROR) { + $message = 'File not found: `' . $file_name.'`'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/minz/FrontController.php b/lib/Minz/FrontController.php index d48d43d04..80eda8877 100755..100644 --- a/lib/minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -2,109 +2,81 @@ # ***** BEGIN LICENSE BLOCK ***** # MINZ - a free PHP Framework like Zend Framework # Copyright (C) 2011 Marien Fressinaud -# +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. -# +# # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. -# +# # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. # # ***** END LICENSE BLOCK ***** /** - * La classe FrontController est le noyau du framework, elle lance l'application + * La classe FrontController est le Dispatcher du framework, elle lance l'application * Elle est appelée en général dans le fichier index.php à la racine du serveur */ -class FrontController { +class Minz_FrontController { protected $dispatcher; protected $router; - + + private $useOb = true; + /** * Constructeur * Initialise le router et le dispatcher */ public function __construct () { - $this->loadLib (); - if (LOG_PATH === false) { - $this->killApp ('Path doesn\'t exist : LOG_PATH'); + $this->killApp ('Path not found: LOG_PATH'); } - + try { - Configuration::init (); + Minz_Configuration::init (); - Request::init (); + Minz_Request::init (); - $this->router = new Router (); + $this->router = new Minz_Router (); $this->router->init (); - } catch (RouteNotFoundException $e) { + } catch (Minz_RouteNotFoundException $e) { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - Error::error ( + Minz_Error::error ( 404, array ('error' => array ($e->getMessage ())) ); - } catch (MinzException $e) { + } catch (Minz_Exception $e) { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); $this->killApp ($e->getMessage ()); } - - $this->dispatcher = Dispatcher::getInstance ($this->router); - } - - /** - * Inclue les fichiers de la librairie - */ - private function loadLib () { - require ('ActionController.php'); - require ('Minz_Cache.php'); - require ('Configuration.php'); - require ('Dispatcher.php'); - require ('Error.php'); - require ('Helper.php'); - require ('Minz_Log.php'); - require ('Model.php'); - require ('Paginator.php'); - require ('Request.php'); - require ('Response.php'); - require ('Router.php'); - require ('Session.php'); - require ('Translate.php'); - require ('Url.php'); - require ('View.php'); - - require ('dao/Model_pdo.php'); - require ('dao/Model_txt.php'); - require ('dao/Model_array.php'); - - require ('exceptions/MinzException.php'); + + $this->dispatcher = Minz_Dispatcher::getInstance ($this->router); } - + /** * Démarre l'application (lance le dispatcher et renvoie la réponse */ public function run () { try { - $this->dispatcher->run (); - Response::send (); - } catch (MinzException $e) { + $this->dispatcher->run ($this->useOb); + Minz_Response::send (); + } catch (Minz_Exception $e) { try { Minz_Log::record ($e->getMessage (), Minz_Log::ERROR); - } catch (PermissionDeniedException $e) { + } catch (Minz_PermissionDeniedException $e) { $this->killApp ($e->getMessage ()); } - if ($e instanceof FileNotExistException || - $e instanceof ControllerNotExistException || - $e instanceof ControllerNotActionControllerException || - $e instanceof ActionException) { - Error::error ( + if ($e instanceof Minz_FileNotExistException || + $e instanceof Minz_ControllerNotExistException || + $e instanceof Minz_ControllerNotActionControllerException || + $e instanceof Minz_ActionException) { + Minz_Error::error ( 404, array ('error' => array ($e->getMessage ())), true @@ -114,7 +86,7 @@ class FrontController { } } } - + /** * Permet d'arrêter le programme en urgence */ @@ -124,4 +96,15 @@ class FrontController { } exit ('### Application problem ###<br />'."\n".$txt); } + + public function useOb() { + return $this->useOb; + } + + /** + * Use ob_start('ob_gzhandler') or not. + */ + public function _useOb($ob) { + return $this->useOb = (bool)$ob; + } } diff --git a/lib/minz/Helper.php b/lib/Minz/Helper.php index 4f64ba218..b058211d3 100755..100644 --- a/lib/minz/Helper.php +++ b/lib/Minz/Helper.php @@ -7,7 +7,7 @@ /** * La classe Helper représente une aide pour des tâches récurrentes */ -class Helper { +class Minz_Helper { /** * Annule les effets des magic_quotes pour une variable donnée * @param $var variable à traiter (tableau ou simple variable) diff --git a/lib/minz/Minz_Log.php b/lib/Minz/Log.php index 153870435..e710aad4a 100644 --- a/lib/minz/Minz_Log.php +++ b/lib/Minz/Log.php @@ -1,5 +1,5 @@ <?php -/** +/** * MINZ - Copyright 2011 Marien Fressinaud * Sous licence AGPL3 <http://www.gnu.org/licenses/> */ @@ -12,12 +12,14 @@ class Minz_Log { * Les différents niveau de log * ERROR erreurs bloquantes de l'application * WARNING erreurs pouvant géner le bon fonctionnement, mais non bloquantes - * NOTICE messages d'informations, affichés pour le déboggage + * NOTICE erreurs mineures ou messages d'informations + * DEBUG Informations affichées pour le déboggage */ - const ERROR = 0; - const WARNING = 10; - const NOTICE = 20; - + const ERROR = 2; + const WARNING = 4; + const NOTICE = 8; + const DEBUG = 16; + /** * Enregistre un message dans un fichier de log spécifique * Message non loggué si @@ -29,15 +31,15 @@ class Minz_Log { * @param $file_name fichier de log, par défaut LOG_PATH/application.log */ public static function record ($information, $level, $file_name = null) { - $env = Configuration::environment (); - - if (! ($env == Configuration::SILENT - || ($env == Configuration::PRODUCTION - && ($level == Minz_Log::WARNING || $level == Minz_Log::NOTICE)))) { - if (is_null ($file_name)) { - $file_name = LOG_PATH . '/application.log'; + $env = Minz_Configuration::environment (); + + if (! ($env === Minz_Configuration::SILENT + || ($env === Minz_Configuration::PRODUCTION + && ($level >= Minz_Log::NOTICE)))) { + if ($file_name === null) { + $file_name = LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log'; } - + switch ($level) { case Minz_Log::ERROR : $level_label = 'error'; @@ -48,27 +50,19 @@ class Minz_Log { case Minz_Log::NOTICE : $level_label = 'notice'; break; + case Minz_Log::DEBUG : + $level_label = 'debug'; + break; default : $level_label = 'unknown'; } - - if ($env == Configuration::PRODUCTION) { - $file = @fopen ($file_name, 'a'); - } else { - $file = fopen ($file_name, 'a'); - } - - if ($file !== false) { - $log = '[' . date('r') . ']'; - $log .= ' [' . $level_label . ']'; - $log .= ' --- ' . $information . "\n"; - fwrite ($file, $log); - fclose ($file); - } else { - throw new PermissionDeniedException ( - $file_name, - MinzException::ERROR - ); + + $log = '[' . date('r') . ']' + . ' [' . $level_label . ']' + . ' --- ' . $information . "\n"; + + if (file_put_contents($file_name, $log, FILE_APPEND | LOCK_EX) === false) { + throw new Minz_PermissionDeniedException($file_name, Minz_Exception::ERROR); } } } @@ -83,7 +77,7 @@ class Minz_Log { $msg_get = str_replace("\n", '', '$_GET content : ' . print_r($_GET, true)); $msg_post = str_replace("\n", '', '$_POST content : ' . print_r($_POST, true)); - self::record($msg_get, Minz_Log::NOTICE, $file_name); - self::record($msg_post, Minz_Log::NOTICE, $file_name); + self::record($msg_get, Minz_Log::DEBUG, $file_name); + self::record($msg_post, Minz_Log::DEBUG, $file_name); } } diff --git a/lib/minz/Model.php b/lib/Minz/Model.php index 37fc19ed1..adbaba942 100755..100644 --- a/lib/minz/Model.php +++ b/lib/Minz/Model.php @@ -7,6 +7,6 @@ /** * La classe Model représente un modèle de l'application (représentation MVC) */ -class Model { +class Minz_Model { } diff --git a/lib/Minz/ModelArray.php b/lib/Minz/ModelArray.php new file mode 100644 index 000000000..ff23dbc83 --- /dev/null +++ b/lib/Minz/ModelArray.php @@ -0,0 +1,81 @@ +<?php +/** + * MINZ - Copyright 2011 Marien Fressinaud + * Sous licence AGPL3 <http://www.gnu.org/licenses/> +*/ + +/** + * La classe Model_array représente le modèle interragissant avec les fichiers de type texte gérant des tableaux php + */ +class Minz_ModelArray { + /** + * $filename est le nom du fichier + */ + protected $filename; + + /** + * Ouvre le fichier indiqué, charge le tableau dans $array et le $filename + * @param $filename le nom du fichier à ouvrir contenant un tableau + * Remarque : $array sera obligatoirement un tableau + */ + public function __construct ($filename) { + $this->filename = $filename; + } + + protected function loadArray() { + if (!file_exists($this->filename)) { + throw new Minz_FileNotExistException($this->filename, Minz_Exception::WARNING); + } + elseif (($handle = $this->getLock()) === false) { + throw new Minz_PermissionDeniedException($this->filename); + } else { + $data = include($this->filename); + $this->releaseLock($handle); + + if ($data === false) { + throw new Minz_PermissionDeniedException($this->filename); + } elseif (!is_array($data)) { + $data = array(); + } + return $data; + } + } + + /** + * Sauve le tableau $array dans le fichier $filename + **/ + protected function writeArray($array) { + if (file_put_contents($this->filename, "<?php\n return " . var_export($array, true) . ';', LOCK_EX) === false) { + throw new Minz_PermissionDeniedException($this->filename); + } + if (function_exists('opcache_invalidate')) { + opcache_invalidate($this->filename); //Clear PHP 5.5+ cache for include + } + return true; + } + + private function getLock() { + $handle = fopen($this->filename, 'r'); + if ($handle === false) { + return false; + } + + $count = 50; + while (!flock($handle, LOCK_SH) && $count > 0) { + $count--; + usleep(1000); + } + + if ($count > 0) { + return $handle; + } else { + fclose($handle); + return false; + } + } + + private function releaseLock($handle) { + flock($handle, LOCK_UN); + fclose($handle); + } +} diff --git a/lib/minz/dao/Model_pdo.php b/lib/Minz/ModelPdo.php index a91a4fa00..831df13a2 100755..100644 --- a/lib/minz/dao/Model_pdo.php +++ b/lib/Minz/ModelPdo.php @@ -1,5 +1,5 @@ <?php -/** +/** * MINZ - Copyright 2011 Marien Fressinaud * Sous licence AGPL3 <http://www.gnu.org/licenses/> */ @@ -8,7 +8,7 @@ * La classe Model_sql représente le modèle interragissant avec les bases de données * Seul la connexion MySQL est prise en charge pour le moment */ -class Model_pdo { +class Minz_ModelPdo { /** * Partage la connexion à la base de données entre toutes les instances. @@ -23,7 +23,7 @@ class Model_pdo { protected $bd; protected $prefix; - + /** * Créé la connexion à la base de données à l'aide des variables * HOST, BASE, USER et PASS définies dans le fichier de configuration @@ -35,7 +35,7 @@ class Model_pdo { return; } - $db = Configuration::dataBase (); + $db = Minz_Configuration::dataBase (); $driver_options = null; try { @@ -49,9 +49,7 @@ class Model_pdo { PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8' ); } elseif($type == 'sqlite') { - $string = $type - . ':/' . PUBLIC_PATH - . '/data/' . $db['base'] . '.sqlite'; //TODO: DEBUG UTF-8 http://www.siteduzero.com/forum/sujet/sqlite-connexion-utf-8-18797 + $string = $type . ':/' . DATA_PATH . $db['base'] . '.sqlite'; //TODO: DEBUG UTF-8 http://www.siteduzero.com/forum/sujet/sqlite-connexion-utf-8-18797 } $this->bd = new FreshPDO ( @@ -62,21 +60,50 @@ class Model_pdo { ); self::$sharedBd = $this->bd; - $this->prefix = $db['prefix']; + $this->prefix = $db['prefix'] . Minz_Session::param('currentUser', '_') . '_'; self::$sharedPrefix = $this->prefix; } catch (Exception $e) { - throw new PDOConnectionException ( + throw new Minz_PDOConnectionException ( $string, - $db['user'], MinzException::WARNING + $db['user'], Minz_Exception::ERROR ); } } + + public function beginTransaction() { + $this->bd->beginTransaction(); + } + public function commit() { + $this->bd->commit(); + } + public function rollBack() { + $this->bd->rollBack(); + } + + public function size($all = false) { + $db = Minz_Configuration::dataBase (); + $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema = ?'; + $values = array ($db['base']); + if (!$all) { + $sql .= ' AND table_name LIKE ?'; + $values[] = $this->prefix . '%'; + } + $stm = $this->bd->prepare ($sql); + $stm->execute ($values); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); + return $res[0]; + } + + public static function clean() { + self::$sharedBd = null; + self::$sharedPrefix = ''; + } } class FreshPDO extends PDO { private static function check($statement) { if (preg_match('/^(?:UPDATE|INSERT|DELETE)/i', $statement)) { - touch(PUBLIC_PATH . '/data/touch.txt'); + invalidateHttpCache(); } } diff --git a/lib/Minz/PDOConnectionException.php b/lib/Minz/PDOConnectionException.php new file mode 100644 index 000000000..faf2e0fe9 --- /dev/null +++ b/lib/Minz/PDOConnectionException.php @@ -0,0 +1,9 @@ +<?php +class Minz_PDOConnectionException extends Minz_Exception { + public function __construct ($string_connection, $user, $code = self::ERROR) { + $message = 'Access to database is denied for `' . $user . '`' + . ' (`' . $string_connection . '`)'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/minz/Paginator.php b/lib/Minz/Paginator.php index 1a8376e75..5858e76a5 100755..100644 --- a/lib/minz/Paginator.php +++ b/lib/Minz/Paginator.php @@ -7,7 +7,7 @@ /** * La classe Paginator permet de gérer la pagination de l'application facilement */ -class Paginator { +class Minz_Paginator { /** * $items tableau des éléments à afficher/gérer */ diff --git a/lib/Minz/PermissionDeniedException.php b/lib/Minz/PermissionDeniedException.php new file mode 100644 index 000000000..61be530d3 --- /dev/null +++ b/lib/Minz/PermissionDeniedException.php @@ -0,0 +1,8 @@ +<?php +class Minz_PermissionDeniedException extends Minz_Exception { + public function __construct ($file_name, $code = self::ERROR) { + $message = 'Permission is denied for `' . $file_name.'`'; + + parent::__construct ($message, $code); + } +} diff --git a/lib/minz/Request.php b/lib/Minz/Request.php index ffddbe6ad..d4e1355d7 100644 --- a/lib/minz/Request.php +++ b/lib/Minz/Request.php @@ -7,7 +7,7 @@ /** * Request représente la requête http */ -class Request { +class Minz_Request { private static $controller_name = ''; private static $action_name = ''; private static $params = array (); @@ -29,15 +29,18 @@ class Request { public static function params () { return self::$params; } + static function htmlspecialchars_utf8 ($p) { + return htmlspecialchars($p, ENT_COMPAT, 'UTF-8'); + } public static function param ($key, $default = false, $specialchars = false) { if (isset (self::$params[$key])) { $p = self::$params[$key]; if(is_object($p) || $specialchars) { return $p; } elseif(is_array($p)) { - return array_map('htmlspecialchars', $p); //TODO: Should use explicit UTF-8 + return array_map('self::htmlspecialchars_utf8', $p); } else { - return htmlspecialchars($p, ENT_NOQUOTES, 'UTF-8'); + return self::htmlspecialchars_utf8($p); } } else { return $default; @@ -93,7 +96,14 @@ class Request { * @return la base de l'url */ public static function getBaseUrl () { - return Configuration::baseUrl (); + $defaultBaseUrl = Minz_Configuration::baseUrl(); + if (!empty($defaultBaseUrl)) { + return $defaultBaseUrl; + } elseif (isset($_SERVER['REQUEST_URI'])) { + return dirname($_SERVER['REQUEST_URI']) . '/'; + } else { + return '/'; + } } /** @@ -121,10 +131,10 @@ class Request { * > sinon, le dispatcher recharge en interne */ public static function forward ($url = array (), $redirect = false) { - $url = Url::checkUrl ($url); + $url = Minz_Url::checkUrl ($url); if ($redirect) { - header ('Location: ' . Url::display ($url, 'php')); + header ('Location: ' . Minz_Url::display ($url, 'php')); exit (); } else { self::$reseted = true; @@ -182,9 +192,9 @@ class Request { */ private static function magicQuotesOff () { if (get_magic_quotes_gpc ()) { - $_GET = Helper::stripslashes_r ($_GET); - $_POST = Helper::stripslashes_r ($_POST); - $_COOKIE = Helper::stripslashes_r ($_COOKIE); + $_GET = Minz_Helper::stripslashes_r ($_GET); + $_POST = Minz_Helper::stripslashes_r ($_POST); + $_COOKIE = Minz_Helper::stripslashes_r ($_COOKIE); } } @@ -192,5 +202,3 @@ class Request { return !empty ($_POST) || !empty ($_FILES); } } - - diff --git a/lib/minz/Response.php b/lib/Minz/Response.php index fcf53c5b1..f8ea3d946 100644 --- a/lib/minz/Response.php +++ b/lib/Minz/Response.php @@ -7,7 +7,7 @@ /** * Response représente la requête http renvoyée à l'utilisateur */ -class Response { +class Minz_Response { private static $header = 'HTTP/1.0 200 OK'; private static $body = ''; diff --git a/lib/Minz/RouteNotFoundException.php b/lib/Minz/RouteNotFoundException.php new file mode 100644 index 000000000..dc4f6fbad --- /dev/null +++ b/lib/Minz/RouteNotFoundException.php @@ -0,0 +1,16 @@ +<?php +class Minz_RouteNotFoundException extends Minz_Exception { + private $route; + + public function __construct ($route, $code = self::ERROR) { + $this->route = $route; + + $message = 'Route `' . $route . '` not found'; + + parent::__construct ($message, $code); + } + + public function route () { + return $this->route; + } +} diff --git a/lib/minz/Router.php b/lib/Minz/Router.php index c5d6f5baa..1ccd72597 100755..100644 --- a/lib/minz/Router.php +++ b/lib/Minz/Router.php @@ -8,7 +8,7 @@ * La classe Router gère le routage de l'application * Les routes sont définies dans APP_PATH.'/configuration/routes.php' */ -class Router { +class Minz_Router { const ROUTES_PATH_NAME = '/configuration/routes.php'; private $routes = array (); @@ -19,7 +19,7 @@ class Router { * et que l'on utilise l'url rewriting */ public function __construct () { - if (Configuration::useUrlRewriting ()) { + if (Minz_Configuration::useUrlRewriting ()) { if (file_exists (APP_PATH . self::ROUTES_PATH_NAME)) { $routes = include ( APP_PATH . self::ROUTES_PATH_NAME @@ -34,9 +34,9 @@ class Router { $routes ); } else { - throw new FileNotExistException ( + throw new Minz_FileNotExistException ( self::ROUTES_PATH_NAME, - MinzException::ERROR + Minz_Exception::ERROR ); } } @@ -51,10 +51,10 @@ class Router { public function init () { $url = array (); - if (Configuration::useUrlRewriting ()) { + if (Minz_Configuration::useUrlRewriting ()) { try { $url = $this->buildWithRewriting (); - } catch (RouteNotFoundException $e) { + } catch (Minz_RouteNotFoundException $e) { throw $e; } } else { @@ -63,10 +63,10 @@ class Router { $url['params'] = array_merge ( $url['params'], - Request::fetchPOST () + Minz_Request::fetchPOST () ); - Request::forward ($url); + Minz_Request::forward ($url); } /** @@ -77,15 +77,15 @@ class Router { public function buildWithoutRewriting () { $url = array (); - $url['c'] = Request::fetchGET ( + $url['c'] = Minz_Request::fetchGET ( 'c', - Request::defaultControllerName () + Minz_Request::defaultControllerName () ); - $url['a'] = Request::fetchGET ( + $url['a'] = Minz_Request::fetchGET ( 'a', - Request::defaultActionName () + Minz_Request::defaultActionName () ); - $url['params'] = Request::fetchGET (); + $url['params'] = Minz_Request::fetchGET (); // post-traitement unset ($url['params']['c']); @@ -103,7 +103,7 @@ class Router { */ public function buildWithRewriting () { $url = array (); - $uri = Request::getURI (); + $uri = Minz_Request::getURI (); $find = false; foreach ($this->routes as $route) { @@ -121,14 +121,14 @@ class Router { } if (!$find && $uri != '/') { - throw new RouteNotFoundException ( + throw new Minz_RouteNotFoundException ( $uri, - MinzException::ERROR + Minz_Exception::ERROR ); } // post-traitement - $url = Url::checkUrl ($url); + $url = Minz_Url::checkUrl ($url); return $url; } diff --git a/lib/minz/Session.php b/lib/Minz/Session.php index f9c9c6754..ddabc4658 100755..100644 --- a/lib/minz/Session.php +++ b/lib/Minz/Session.php @@ -4,42 +4,39 @@ * La classe Session gère la session utilisateur * C'est un singleton */ -class Session { +class Minz_Session { /** * $session stocke les variables de session */ - private static $session = array (); - + private static $session = array (); //TODO: Try to avoid having another local copy + /** - * Initialise la session + * Initialise la session, avec un nom + * Le nom de session est utilisé comme nom pour les cookies et les URLs (i.e. PHPSESSID). + * Il ne doit contenir que des caractères alphanumériques ; il doit être court et descriptif */ - public static function init () { + public static function init ($name) { // démarre la session - session_name (md5 (Configuration::selApplication ())); + session_name ($name); + session_set_cookie_params (0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirname($_SERVER['REQUEST_URI'])), null, false, true); session_start (); - + if (isset ($_SESSION)) { self::$session = $_SESSION; } } - - + + /** * Permet de récupérer une variable de session * @param $p le paramètre à récupérer * @return la valeur de la variable de session, false si n'existe pas */ public static function param ($p, $default = false) { - if (isset (self::$session[$p])) { - $return = self::$session[$p]; - } else { - $return = $default; - } - - return $return; + return isset(self::$session[$p]) ? self::$session[$p] : $default; } - - + + /** * Permet de créer ou mettre à jour une variable de session * @param $p le paramètre à créer ou modifier @@ -52,27 +49,23 @@ class Session { } else { $_SESSION[$p] = $v; self::$session[$p] = $v; - - if($p == 'language') { - // reset pour remettre à jour le fichier de langue à utiliser - Translate::reset (); - } } } - - + + /** * Permet d'effacer une session * @param $force si à false, n'efface pas le paramètre de langue */ public static function unset_session ($force = false) { $language = self::param ('language'); - - session_unset (); + + session_destroy(); self::$session = array (); - + if (!$force) { self::_param ('language', $language); + Minz_Translate::reset (); } } } diff --git a/lib/minz/Translate.php b/lib/Minz/Translate.php index e8cbe4852..e14f783f7 100644 --- a/lib/minz/Translate.php +++ b/lib/Minz/Translate.php @@ -8,7 +8,7 @@ * La classe Translate se charge de la traduction * Utilise les fichiers du répertoire /app/i18n/ */ -class Translate { +class Minz_Translate { /** * $language est la langue à afficher */ @@ -25,8 +25,8 @@ class Translate { * l'enregistre dans $translates */ public static function init () { - $l = Configuration::language (); - self::$language = Session::param ('language', $l); + $l = Minz_Configuration::language (); + self::$language = Minz_Session::param ('language', $l); $l_path = APP_PATH . '/i18n/' . self::$language . '.php'; diff --git a/lib/minz/Url.php b/lib/Minz/Url.php index ce051ebd9..17f1ddece 100755..100644 --- a/lib/minz/Url.php +++ b/lib/Minz/Url.php @@ -3,7 +3,7 @@ /** * La classe Url permet de gérer les URL à travers MINZ */ -class Url { +class Minz_Url { /** * Affiche une Url formatée selon que l'on utilise l'url_rewriting ou non * si oui, on cherche dans la table de routage la correspondance pour formater @@ -17,28 +17,31 @@ class Url { * @return l'url formatée */ public static function display ($url = array (), $encodage = 'html', $absolute = false) { - $url = self::checkUrl ($url); + $isArray = is_array($url); + + if ($isArray) { + $url = self::checkUrl ($url); + } $url_string = ''; if ($absolute) { - if (is_array ($url) && isset ($url['protocol'])) { + if ($isArray && isset ($url['protocol'])) { $protocol = $url['protocol']; } elseif (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { $protocol = 'https:'; } else { $protocol = 'http:'; } - $url_string = $protocol . '//' . Request::getDomainName () . Request::getBaseUrl (); - } - else { - $url_string = '.'; + $url_string = $protocol . '//' . Minz_Request::getDomainName () . Minz_Request::getBaseUrl (); + } else { + $url_string = $isArray ? '.' : PUBLIC_RELATIVE; } - if (is_array ($url)) { - $router = new Router (); + if ($isArray) { + $router = new Minz_Router (); - if (Configuration::useUrlRewriting ()) { + if (Minz_Configuration::useUrlRewriting ()) { $url_string .= $router->printUriRewrited ($url); } else { $url_string .= self::printUri ($url, $encodage); @@ -67,13 +70,13 @@ class Url { } if (isset ($url['c']) - && $url['c'] != Request::defaultControllerName ()) { + && $url['c'] != Minz_Request::defaultControllerName ()) { $uri .= $separator . 'c=' . $url['c']; $separator = $and; } if (isset ($url['a']) - && $url['a'] != Request::defaultActionName ()) { + && $url['a'] != Minz_Request::defaultActionName ()) { $uri .= $separator . 'a=' . $url['a']; $separator = $and; } @@ -98,10 +101,10 @@ class Url { if (is_array ($url)) { if (!isset ($url['c'])) { - $url_checked['c'] = Request::defaultControllerName (); + $url_checked['c'] = Minz_Request::defaultControllerName (); } if (!isset ($url['a'])) { - $url_checked['a'] = Request::defaultActionName (); + $url_checked['a'] = Minz_Request::defaultActionName (); } if (!isset ($url['params'])) { $url_checked['params'] = array (); @@ -125,5 +128,5 @@ function _url ($controller, $action) { $params[$args[$i]] = $args[$i + 1]; } - return Url::display (array ('c' => $controller, 'a' => $action, 'params' => $params)); + return Minz_Url::display (array ('c' => $controller, 'a' => $action, 'params' => $params)); } diff --git a/lib/minz/View.php b/lib/Minz/View.php index fd92762b3..e170bd406 100755..100644 --- a/lib/minz/View.php +++ b/lib/Minz/View.php @@ -7,13 +7,13 @@ /** * La classe View représente la vue de l'application */ -class View { +class Minz_View { const VIEWS_PATH_NAME = '/views'; const LAYOUT_PATH_NAME = '/layout'; const LAYOUT_FILENAME = '/layout.phtml'; private $view_filename = ''; - private $use_layout = false; + private $use_layout = null; private static $title = ''; private static $styles = array (); @@ -28,22 +28,19 @@ class View { public function __construct () { $this->view_filename = APP_PATH . self::VIEWS_PATH_NAME . '/' - . Request::controllerName () . '/' - . Request::actionName () . '.phtml'; + . Minz_Request::controllerName () . '/' + . Minz_Request::actionName () . '.phtml'; - if (file_exists (APP_PATH - . self::LAYOUT_PATH_NAME - . self::LAYOUT_FILENAME)) { - $this->use_layout = true; - } - - self::$title = Configuration::title (); + self::$title = Minz_Configuration::title (); } /** * Construit la vue */ public function build () { + if ($this->use_layout === null) { //TODO: avoid file_exists and require views to be explicit + $this->use_layout = file_exists (APP_PATH . self::LAYOUT_PATH_NAME . self::LAYOUT_FILENAME); + } if ($this->use_layout) { $this->buildLayout (); } else { @@ -66,10 +63,8 @@ class View { * Affiche la Vue en elle-même */ public function render () { - if (file_exists ($this->view_filename)) { - include ($this->view_filename); - } else { - Minz_Log::record ('File doesn\'t exist : `' + if ((include($this->view_filename)) === false) { + Minz_Log::record ('File not found: `' . $this->view_filename . '`', Minz_Log::NOTICE); } @@ -84,10 +79,8 @@ class View { . self::LAYOUT_PATH_NAME . '/' . $part . '.phtml'; - if (file_exists ($fic_partial)) { - include ($fic_partial); - } else { - Minz_Log::record ('File doesn\'t exist : `' + if ((include($fic_partial)) === false) { + Minz_Log::record ('File not found: `' . $fic_partial . '`', Minz_Log::WARNING); } @@ -102,10 +95,8 @@ class View { . '/views/helpers/' . $helper . '.phtml'; - if (file_exists ($fic_helper)) { - include ($fic_helper); - } else { - Minz_Log::record ('File doesn\'t exist : `' + if ((include($fic_helper)) === false) {; + Minz_Log::record ('File not found: `' . $fic_helper . '`', Minz_Log::WARNING); } @@ -150,8 +141,9 @@ class View { $styles .= '<!--[if ' . $cond . ']>'; } - $styles .= '<link rel="stylesheet" media="' . $style['media'] - . '" href="' . $style['url'] . '" />'; + $styles .= '<link rel="stylesheet" ' . + ($style['media'] === 'all' ? '' : 'media="' . $style['media'] . '" ') . + 'href="' . $style['url'] . '" />'; if ($cond) { $styles .= '<![endif]-->'; @@ -231,7 +223,7 @@ class View { self::$params[$key] = $value; } public function attributeParams () { - foreach (View::$params as $key => $value) { + foreach (Minz_View::$params as $key => $value) { $this->$key = $value; } } diff --git a/lib/SimplePie/SimplePie.php b/lib/SimplePie/SimplePie.php index 9e532023a..f02037c10 100644 --- a/lib/SimplePie/SimplePie.php +++ b/lib/SimplePie/SimplePie.php @@ -602,6 +602,13 @@ class SimplePie public $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); /** + * @var array Stores the default attributes to add to differet tags by add_attributes(). + * @see SimplePie::add_attributes() + * @access private + */ + public $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); //FreshRSS + + /** * @var array Stores the default tags to be stripped by strip_htmltags(). * @see SimplePie::strip_htmltags() * @access private @@ -1073,6 +1080,7 @@ class SimplePie $this->strip_comments(false); $this->strip_htmltags(false); $this->strip_attributes(false); + $this->add_attributes(false); $this->set_image_handler(false); } } @@ -1119,6 +1127,15 @@ class SimplePie $this->sanitize->strip_attributes($attribs); } + public function add_attributes($attribs = '') + { + if ($attribs === '') + { + $attribs = $this->add_attributes; + } + $this->sanitize->add_attributes($attribs); + } + /** * Set the output encoding * @@ -1296,7 +1313,7 @@ class SimplePie // First check to see if input has been overridden. if ($this->input_encoding !== false) { - $encodings[] = $this->input_encoding; + $encodings[] = strtoupper($this->input_encoding); } $application_types = array('application/xml', 'application/xml-dtd', 'application/xml-external-parsed-entity'); @@ -1313,18 +1330,18 @@ class SimplePie } else { - $encodings[] = ''; //Let the DOM parser decide first + $encodings[] = ''; //FreshRSS: Let the DOM parser decide first } } elseif (in_array($sniffed, $text_types) || substr($sniffed, 0, 5) === 'text/' && substr($sniffed, -4) === '+xml') { if (isset($headers['content-type']) && preg_match('/;\x20?charset=([^;]*)/i', $headers['content-type'], $charset)) { - $encodings[] = $charset[1]; + $encodings[] = strtoupper($charset[1]); } else { - $encodings[] = ''; + $encodings[] = ''; //FreshRSS: Let the DOM parser decide first } $encodings[] = 'US-ASCII'; } @@ -1347,13 +1364,14 @@ class SimplePie foreach ($encodings as $encoding) { // Change the encoding to UTF-8 (as we always use UTF-8 internally) - if ($utf8_data = (empty($encoding) || $encoding === 'UTF-8') ? $this->raw_data : $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) + if ($utf8_data = (empty($encoding) || $encoding === 'UTF-8') ? $this->raw_data : //FreshRSS + $this->registry->call('Misc', 'change_encoding', array($this->raw_data, $encoding, 'UTF-8'))) { // Create new parser $parser = $this->registry->create('Parser'); // If it's parsed fine - if ($parser->parse($utf8_data, 'UTF-8')) + if ($parser->parse($utf8_data, empty($encoding) ? '' : 'UTF-8')) //FreshRSS { $this->data = $parser->get_data(); if (!($this->get_type() & ~SIMPLEPIE_TYPE_NONE)) diff --git a/lib/SimplePie/SimplePie/File.php b/lib/SimplePie/SimplePie/File.php index 063ad955e..cf926cf5a 100644 --- a/lib/SimplePie/SimplePie/File.php +++ b/lib/SimplePie/SimplePie/File.php @@ -77,6 +77,7 @@ class SimplePie_File $this->useragent = $useragent; if (preg_match('/^http(s)?:\/\//i', $url)) { + syslog(LOG_INFO, 'SimplePie GET ' . $url); //FreshRSS if ($useragent === null) { $useragent = ini_get('user_agent'); diff --git a/lib/SimplePie/SimplePie/Misc.php b/lib/SimplePie/SimplePie/Misc.php index 621f2c062..347520303 100644 --- a/lib/SimplePie/SimplePie/Misc.php +++ b/lib/SimplePie/SimplePie/Misc.php @@ -79,6 +79,10 @@ class SimplePie_Misc public static function absolutize_url($relative, $base) { + if (substr($relative, 0, 2) === '//') //FreshRSS: disable absolutize_url for "//www.example.net" which will pick HTTP or HTTPS automatically + { + return $relative; + } $iri = SimplePie_IRI::absolutize(new SimplePie_IRI($base), $relative); if ($iri === false) { diff --git a/lib/SimplePie/SimplePie/Parser.php b/lib/SimplePie/SimplePie/Parser.php index 72878c25a..bd6c4efd8 100644 --- a/lib/SimplePie/SimplePie/Parser.php +++ b/lib/SimplePie/SimplePie/Parser.php @@ -77,6 +77,8 @@ class SimplePie_Parser public function parse(&$data, $encoding) { + $xmlEncoding = ''; + if (!empty($encoding)) { // Use UTF-8 if we get passed US-ASCII, as every US-ASCII character is a UTF-8 character @@ -121,6 +123,7 @@ class SimplePie_Parser $declaration = $this->registry->create('XML_Declaration_Parser', array(substr($data, 5, $pos - 5))); if ($declaration->parse()) { + $xmlEncoding = strtoupper($declaration->encoding); //FreshRSS $data = substr($data, $pos + 2); $data = '<?xml version="' . $declaration->version . '" encoding="' . $encoding . '" standalone="' . (($declaration->standalone) ? 'yes' : 'no') . '"?>' . $data; } @@ -132,18 +135,24 @@ class SimplePie_Parser } } - try - { - $dom = new DOMDocument(); - $dom->recover = true; - $dom->strictErrorChecking = false; - $dom->loadXML($data); - $this->encoding = $encoding = $dom->encoding = 'UTF-8'; - $data = $dom->saveXML(); - //file_put_contents('/home/alex/public_html/alexandre.alapetite.fr/prive/FreshRSS/log/parser.log', date('c') . ' ' . 'OK' . "\n", FILE_APPEND); - } - catch (Exception $e) + if ($xmlEncoding === '' || $xmlEncoding === 'UTF-8') //FreshRSS: case of no explicit HTTP encoding, and lax UTF-8 { + try + { + $dom = new DOMDocument(); + $dom->recover = true; + $dom->strictErrorChecking = false; + $dom->loadXML($data); + $this->encoding = $encoding = $dom->encoding = 'UTF-8'; + $data2 = $dom->saveXML(); + if (strlen($data2) > (strlen($data) / 2.0)) + { + $data = $data2; + } + } + catch (Exception $e) + { + } } $return = true; diff --git a/lib/SimplePie/SimplePie/Sanitize.php b/lib/SimplePie/SimplePie/Sanitize.php index 83a274ced..0974c150d 100644 --- a/lib/SimplePie/SimplePie/Sanitize.php +++ b/lib/SimplePie/SimplePie/Sanitize.php @@ -62,6 +62,7 @@ class SimplePie_Sanitize var $strip_htmltags = array('base', 'blink', 'body', 'doctype', 'embed', 'font', 'form', 'frame', 'frameset', 'html', 'iframe', 'input', 'marquee', 'meta', 'noscript', 'object', 'param', 'script', 'style'); var $encode_instead_of_strip = false; var $strip_attributes = array('bgsound', 'class', 'expr', 'id', 'style', 'onclick', 'onerror', 'onfinish', 'onmouseover', 'onmouseout', 'onfocus', 'onblur', 'lowsrc', 'dynsrc'); + var $add_attributes = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none')); //FreshRSS var $strip_comments = false; var $output_encoding = 'UTF-8'; var $enable_cache = true; @@ -179,6 +180,25 @@ class SimplePie_Sanitize } } + public function add_attributes($attribs = array('audio' => array('preload' => 'none'), 'iframe' => array('sandbox' => 'allow-scripts allow-same-origin'), 'video' => array('preload' => 'none'))) + { + if ($attribs) + { + if (is_array($attribs)) + { + $this->add_attributes = $attribs; + } + else + { + $this->add_attributes = explode(',', $attribs); + } + } + else + { + $this->add_attributes = false; + } + } + public function strip_comments($strip = false) { $this->strip_comments = (bool) $strip; @@ -255,10 +275,11 @@ class SimplePie_Sanitize $document->loadHTML($data); restore_error_handler(); + $xpath = new DOMXPath($document); //FreshRSS + // Strip comments if ($this->strip_comments) { - $xpath = new DOMXPath($document); $comments = $xpath->query('//comment()'); foreach ($comments as $comment) @@ -274,7 +295,7 @@ class SimplePie_Sanitize { foreach ($this->strip_htmltags as $tag) { - $this->strip_tag($tag, $document, $type); + $this->strip_tag($tag, $document, $xpath, $type); } } @@ -282,7 +303,15 @@ class SimplePie_Sanitize { foreach ($this->strip_attributes as $attrib) { - $this->strip_attr($attrib, $document); + $this->strip_attr($attrib, $xpath); + } + } + + if ($this->add_attributes) + { + foreach ($this->add_attributes as $tag => $valuePairs) + { + $this->add_attr($tag, $valuePairs, $document); } } @@ -452,9 +481,8 @@ class SimplePie_Sanitize } } - protected function strip_tag($tag, $document, $type) + protected function strip_tag($tag, $document, $xpath, $type) { - $xpath = new DOMXPath($document); $elements = $xpath->query('body//' . $tag); if ($this->encode_instead_of_strip) { @@ -537,9 +565,8 @@ class SimplePie_Sanitize } } - protected function strip_attr($attrib, $document) + protected function strip_attr($attrib, $xpath) { - $xpath = new DOMXPath($document); $elements = $xpath->query('//*[@' . $attrib . ']'); foreach ($elements as $element) @@ -547,4 +574,16 @@ class SimplePie_Sanitize $element->removeAttribute($attrib); } } + + protected function add_attr($tag, $valuePairs, $document) + { + $elements = $document->getElementsByTagName($tag); + foreach ($elements as $element) + { + foreach ($valuePairs as $attrib => $value) + { + $element->setAttribute($attrib, $value); + } + } + } } diff --git a/lib/SimplePie_autoloader.php b/lib/SimplePie_autoloader.php deleted file mode 100644 index 3f67635b0..000000000 --- a/lib/SimplePie_autoloader.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -/** - * SimplePie - * - * A PHP-Based RSS and Atom Feed Framework. - * Takes the hard work out of managing a complete RSS/Atom solution. - * - * Copyright (c) 2004-2009, Ryan Parman, Geoffrey Sneddon, Ryan McCue, and contributors - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, are - * permitted provided that the following conditions are met: - * - * * Redistributions of source code must retain the above copyright notice, this list of - * conditions and the following disclaimer. - * - * * Redistributions in binary form must reproduce the above copyright notice, this list - * of conditions and the following disclaimer in the documentation and/or other materials - * provided with the distribution. - * - * * Neither the name of the SimplePie Team nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS - * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS - * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * @package SimplePie - * @version 1.3.1 - * @copyright 2004-2012 Ryan Parman, Geoffrey Sneddon, Ryan McCue - * @author Ryan Parman - * @author Geoffrey Sneddon - * @author Ryan McCue - * @link http://simplepie.org/ SimplePie - * @license http://www.opensource.org/licenses/bsd-license.php BSD License - */ - - -// autoloader -spl_autoload_register(array(new SimplePie_Autoloader(), 'autoload')); - -if (!class_exists('SimplePie')) -{ - trigger_error('Autoloader not registered properly', E_USER_ERROR); -} - -/** - * Autoloader class - * - * @package SimplePie - * @subpackage API - */ -class SimplePie_Autoloader -{ - /** - * Constructor - */ - public function __construct() - { - $this->path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'SimplePie'; - } - - /** - * Autoloader - * - * @param string $class The name of the class to attempt to load. - */ - public function autoload($class) - { - // Only load the class if it starts with "SimplePie" - if (strpos($class, 'SimplePie') !== 0) - { - return; - } - - $filename = $this->path . DIRECTORY_SEPARATOR . str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; - include $filename; - } -} diff --git a/lib/http-conditional.php b/lib/http-conditional.php index 6684fd6ac..59fbef41f 100644 --- a/lib/http-conditional.php +++ b/lib/http-conditional.php @@ -35,7 +35,7 @@ ... //Rest of the script, just as you would do normally. ?> - Version 1.7 beta, 2013-11-03, http://alexandre.alapetite.fr/doc-alex/php-http-304/ + Version 1.7 beta, 2013-12-02, http://alexandre.alapetite.fr/doc-alex/php-http-304/ ------------------------------------------------------------------ Written by Alexandre Alapetite, http://alexandre.alapetite.fr/cv/ @@ -124,7 +124,7 @@ function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMod if ($feedMode) {//Special RSS/ATOM global $clientCacheDate; - $clientCacheDate=strtotime($dateCacheClient); + $clientCacheDate=@strtotime($dateCacheClient); $cachePrivacy=0; } diff --git a/lib/lib_opml.php b/lib/lib_opml.php new file mode 100644 index 000000000..1b5517d7f --- /dev/null +++ b/lib/lib_opml.php @@ -0,0 +1,121 @@ +<?php +function opml_export ($cats) { + $txt = ''; + + foreach ($cats as $cat) { + $txt .= '<outline text="' . $cat['name'] . '">' . "\n"; + + foreach ($cat['feeds'] as $feed) { + $txt .= "\t" . '<outline text="' . $feed->name () . '" type="rss" xmlUrl="' . $feed->url () . '" htmlUrl="' . $feed->website () . '" description="' . htmlspecialchars($feed->description(), ENT_COMPAT, 'UTF-8') . '" />' . "\n"; + } + + $txt .= '</outline>' . "\n"; + } + + return $txt; +} + +function opml_import ($xml) { + $xml = html_only_entity_decode($xml); //!\ Assume UTF-8 + + $dom = new DOMDocument(); + $dom->recover = true; + $dom->strictErrorChecking = false; + $dom->loadXML($xml); + $dom->encoding = 'UTF-8'; + + $opml = simplexml_import_dom($dom); + + if (!$opml) { + throw new FreshRSS_Opml_Exception (); + } + + $catDAO = new FreshRSS_CategoryDAO(); + $catDAO->checkDefault(); + $defCat = $catDAO->getDefault(); + + $categories = array (); + $feeds = array (); + + foreach ($opml->body->outline as $outline) { + if (!isset ($outline['xmlUrl'])) { + // Catégorie + $title = ''; + + if (isset ($outline['text'])) { + $title = (string) $outline['text']; + } elseif (isset ($outline['title'])) { + $title = (string) $outline['title']; + } + + if ($title) { + // Permet d'éviter les soucis au niveau des id : + // ceux-ci sont générés en fonction de la date, + // un flux pourrait être dans une catégorie X avec l'id Y + // alors qu'il existe déjà la catégorie X mais avec l'id Z + // Y ne sera pas ajouté et le flux non plus vu que l'id + // de sa catégorie n'exisera pas + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); + $catDAO = new FreshRSS_CategoryDAO (); + $cat = $catDAO->searchByName ($title); + if ($cat === false) { + $cat = new FreshRSS_Category ($title); + $values = array ( + 'name' => $cat->name (), + 'color' => $cat->color () + ); + $cat->_id ($catDAO->addCategory ($values)); + } + + $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); + } + } else { + // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut + $feeds[] = getFeed ($outline, $defCat->id()); + } + } + + return array ($categories, $feeds); +} + +/** + * import all feeds of a given outline tag + */ +function getFeedsOutline ($outline, $cat_id) { + $feeds = array (); + + foreach ($outline->children () as $child) { + if (isset ($child['xmlUrl'])) { + $feeds[] = getFeed ($child, $cat_id); + } else { + $feeds = array_merge( + $feeds, + getFeedsOutline ($child, $cat_id) + ); + } + } + + return $feeds; +} + +function getFeed ($outline, $cat_id) { + $url = (string) $outline['xmlUrl']; + $url = htmlspecialchars($url, ENT_COMPAT, 'UTF-8'); + $title = ''; + if (isset ($outline['text'])) { + $title = (string) $outline['text']; + } elseif (isset ($outline['title'])) { + $title = (string) $outline['title']; + } + $title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8'); + $feed = new FreshRSS_Feed ($url); + $feed->_category ($cat_id); + $feed->_name ($title); + if (isset($outline['htmlUrl'])) { + $feed->_website(htmlspecialchars((string)$outline['htmlUrl'], ENT_COMPAT, 'UTF-8')); + } + if (isset($outline['description'])) { + $feed->_description(sanitizeHTML((string)$outline['description'])); + } + return $feed; +} diff --git a/lib/lib_rss.php b/lib/lib_rss.php index a27994e94..1bb117ab6 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -1,212 +1,187 @@ <?php -// vérifie qu'on est connecté -function is_logged () { - return Session::param ('mail') != false; +if (!function_exists('json_decode')) { + require_once('JSON.php'); + function json_decode($var) { + $JSON = new Services_JSON; + return (array)($JSON->decode($var)); + } } -// vérifie que le système d'authentification est configuré -function login_is_conf ($conf) { - return $conf->mailLogin () != false; +if (!function_exists('json_encode')) { + require_once('JSON.php'); + function json_encode($var) { + $JSON = new Services_JSON; + return $JSON->encodeUnsafe($var); + } } -// tiré de Shaarli de Seb Sauvage -function small_hash ($txt) { - $t = rtrim (base64_encode (hash ('crc32', $txt, true)), '='); - $t = str_replace ('+', '-', $t); // Get rid of characters which need encoding in URLs. - $t = str_replace ('/', '_', $t); - $t = str_replace ('=', '@', $t); - - return $t; +//<Auto-loading> +function classAutoloader($class) { + if (strpos($class, 'FreshRSS') === 0) { + $components = explode('_', $class); + switch (count($components)) { + case 1: + include(APP_PATH . '/' . $components[0] . '.php'); + return; + case 2: + include(APP_PATH . '/Models/' . $components[1] . '.php'); + return; + case 3: //Controllers, Exceptions + include(APP_PATH . '/' . $components[2] . 's/' . $components[1] . $components[2] . '.php'); + return; + } + } elseif (strpos($class, 'Minz') === 0) { + include(LIB_PATH . '/' . str_replace('_', '/', $class) . '.php'); + } elseif (strpos($class, 'SimplePie') === 0) { + include(LIB_PATH . '/SimplePie/' . str_replace('_', '/', $class) . '.php'); + } } -function timestamptodate ($t, $hour = true) { - $month = Translate::t (date('M', $t)); - if ($hour) { - $date = Translate::t ('format_date_hour', $month); +spl_autoload_register('classAutoloader'); +//</Auto-loading> + +function checkUrl($url) { + if (empty ($url)) { + return ''; + } + if (!preg_match ('#^https?://#i', $url)) { + $url = 'http://' . $url; + } + if (filter_var($url, FILTER_VALIDATE_URL) || + (version_compare(PHP_VERSION, '5.3.3', '<') && (strpos($url, '-') > 0) && //PHP bug #51192 + ($url === filter_var($url, FILTER_SANITIZE_URL)))) { + return $url; } else { - $date = Translate::t ('format_date', $month); + return false; } - - return date ($date, $t); } -function sortEntriesByDate ($entry1, $entry2) { - return $entry2->date (true) - $entry1->date (true); -} -function sortReverseEntriesByDate ($entry1, $entry2) { - return $entry1->date (true) - $entry2->date (true); +// tiré de Shaarli de Seb Sauvage //Format RFC 4648 base64url +function small_hash ($txt) { + $t = rtrim (base64_encode (hash ('crc32', $txt, true)), '='); + return strtr ($t, '+/', '-_'); } -function get_domain ($url) { - return parse_url($url, PHP_URL_HOST); +function formatNumber($n, $precision = 0) { + return str_replace(' ', ' ', //Espace insécable //TODO: remplacer par une espace _fine_ insécable + number_format($n, $precision, '.', ' ')); //number_format does not seem to be Unicode-compatible } -function opml_export ($cats) { - $txt = ''; - - foreach ($cats as $cat) { - $txt .= '<outline text="' . $cat['name'] . '">' . "\n"; - - foreach ($cat['feeds'] as $feed) { - $txt .= "\t" . '<outline text="' . cleanText ($feed->name ()) . '" type="rss" xmlUrl="' . htmlentities ($feed->url (), ENT_COMPAT, 'UTF-8') . '" htmlUrl="' . htmlentities ($feed->website (), ENT_COMPAT, 'UTF-8') . '" />' . "\n"; - } - - $txt .= '</outline>' . "\n"; +function formatBytes($bytes, $precision = 2, $system = 'IEC') { + if ($system === 'IEC') { + $base = 1024; + $units = array('B', 'KiB', 'MiB', 'GiB', 'TiB'); + } elseif ($system === 'SI') { + $base = 1000; + $units = array('B', 'KB', 'MB', 'GB', 'TB'); } - - return $txt; + $bytes = max(intval($bytes), 0); + $pow = $bytes === 0 ? 0 : floor(log($bytes) / log($base)); + $pow = min($pow, count($units) - 1); + $bytes /= pow($base, $pow); + return formatNumber($bytes, $precision) . ' ' . $units[$pow]; } -function cleanText ($text) { - return preg_replace ('/&[\w]+;/', '', $text); -} - -function opml_import ($xml) { - $opml = @simplexml_load_string ($xml); - - if (!$opml) { - throw new OpmlException (); - } - - $catDAO = new CategoryDAO(); - $catDAO->checkDefault(); - $defCat = $catDAO->getDefault(); - - $categories = array (); - $feeds = array (); - - foreach ($opml->body->outline as $outline) { - if (!isset ($outline['xmlUrl'])) { - // Catégorie - $title = ''; - - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; - } - - if ($title) { - // Permet d'éviter les soucis au niveau des id : - // ceux-ci sont générés en fonction de la date, - // un flux pourrait être dans une catégorie X avec l'id Y - // alors qu'il existe déjà la catégorie X mais avec l'id Z - // Y ne sera pas ajouté et le flux non plus vu que l'id - // de sa catégorie n'exisera pas - $catDAO = new CategoryDAO (); - $cat = $catDAO->searchByName ($title); - if ($cat === false) { - $cat = new Category ($title); - } - $categories[] = $cat; - - $feeds = array_merge ($feeds, getFeedsOutline ($outline, $cat->id ())); - } - } else { - // Flux rss sans catégorie, on récupère l'ajoute dans la catégorie par défaut - $feeds[] = getFeed ($outline, $defCat->id()); - } +function timestamptodate ($t, $hour = true) { + $month = Minz_Translate::t (date('M', $t)); + if ($hour) { + $date = Minz_Translate::t ('format_date_hour', $month); + } else { + $date = Minz_Translate::t ('format_date', $month); } - return array ($categories, $feeds); + return @date ($date, $t); } -/** - * import all feeds of a given outline tag - */ -function getFeedsOutline ($outline, $cat_id) { - $feeds = array (); - - foreach ($outline->children () as $child) { - if (isset ($child['xmlUrl'])) { - $feeds[] = getFeed ($child, $cat_id); +function html_only_entity_decode($text) { + static $htmlEntitiesOnly = null; + if ($htmlEntitiesOnly === null) { + if (version_compare(PHP_VERSION, '5.3.4') >= 0) { + $htmlEntitiesOnly = array_flip(array_diff( + get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES, 'UTF-8'), //Decode HTML entities + get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES, 'UTF-8') //Preserve XML entities + )); } else { - $feeds = array_merge( - $feeds, - getFeedsOutline ($child, $cat_id) - ); + $htmlEntitiesOnly = array_map('utf8_encode', array_flip(array_diff( + get_html_translation_table(HTML_ENTITIES, ENT_NOQUOTES), //Decode HTML entities + get_html_translation_table(HTML_SPECIALCHARS, ENT_NOQUOTES) //Preserve XML entities + ))); } } + return strtr($text, $htmlEntitiesOnly); +} - return $feeds; +function customSimplePie() { + $simplePie = new SimplePie(); + $simplePie->set_useragent(Minz_Translate::t('freshrss') . '/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ') ' . SIMPLEPIE_NAME . '/' . SIMPLEPIE_VERSION); + $simplePie->set_cache_location(CACHE_PATH); + $simplePie->set_cache_duration(1500); + $simplePie->strip_htmltags(array( + 'base', 'blink', 'body', 'doctype', 'embed', + 'font', 'form', 'frame', 'frameset', 'html', + 'link', 'input', 'marquee', 'meta', 'noscript', + 'object', 'param', 'plaintext', 'script', 'style', + )); + $simplePie->strip_attributes(array_merge($simplePie->strip_attributes, array( + 'autoplay', 'onload', 'onunload', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', + 'onmouseover', 'onmousemove', 'onmouseout', 'onfocus', 'onblur', + 'onkeypress', 'onkeydown', 'onkeyup', 'onselect', 'onchange', 'seamless'))); + $simplePie->add_attributes(array( + 'img' => array('lazyload' => ''), //http://www.w3.org/TR/resource-priorities/ + 'audio' => array('preload' => 'none'), + 'iframe' => array('postpone' => '', 'sandbox' => 'allow-scripts allow-same-origin'), + 'video' => array('postpone' => '', 'preload' => 'none'), + )); + $simplePie->set_url_replacements(array( + 'a' => 'href', + 'area' => 'href', + 'audio' => 'src', + 'blockquote' => 'cite', + 'del' => 'cite', + 'form' => 'action', + 'iframe' => 'src', + 'img' => array( + 'longdesc', + 'src' + ), + 'input' => 'src', + 'ins' => 'cite', + 'q' => 'cite', + 'source' => 'src', + 'track' => 'src', + 'video' => array( + 'poster', + 'src', + ), + )); + return $simplePie; } -function getFeed ($outline, $cat_id) { - $url = (string) $outline['xmlUrl']; - $title = ''; - if (isset ($outline['text'])) { - $title = (string) $outline['text']; - } elseif (isset ($outline['title'])) { - $title = (string) $outline['title']; +function sanitizeHTML($data, $base = '') { + static $simplePie = null; + if ($simplePie == null) { + $simplePie = customSimplePie(); + $simplePie->init(); } - $feed = new Feed ($url); - $feed->_category ($cat_id); - $feed->_name ($title); - return $feed; + return html_only_entity_decode($simplePie->sanitize->sanitize($data, SIMPLEPIE_CONSTRUCT_HTML, $base)); } - /* permet de récupérer le contenu d'un article pour un flux qui n'est pas complet */ function get_content_by_parsing ($url, $path) { + require_once (LIB_PATH . '/lib_phpQuery.php'); + + syslog(LOG_INFO, 'FreshRSS GET ' . $url); $html = file_get_contents ($url); if ($html) { $doc = phpQuery::newDocument ($html); $content = $doc->find ($path); - $content->find ('*')->removeAttr ('style') - ->removeAttr ('id') - ->removeAttr ('class') - ->removeAttr ('onload') - ->removeAttr ('target'); - $content->removeAttr ('style') - ->removeAttr ('id') - ->removeAttr ('class') - ->removeAttr ('onload') - ->removeAttr ('target'); - return $content->__toString (); + return sanitizeHTML($content->__toString(), $url); } else { throw new Exception (); } } -/* Télécharge le favicon d'un site, le place sur le serveur et retourne l'URL */ -function dowload_favicon ($website, $id) { - $url = 'http://g.etfv.co/' . $website; - $favicons_dir = PUBLIC_PATH . '/data/favicons'; - $dest = $favicons_dir . '/' . $id . '.ico'; - $favicon_url = '/data/favicons/' . $id . '.ico'; - - if (!is_dir ($favicons_dir)) { - if (!mkdir ($favicons_dir, 0755, true)) { - return $url; - } - } - - if (!file_exists ($dest)) { - $c = curl_init ($url); - curl_setopt ($c, CURLOPT_HEADER, false); - curl_setopt ($c, CURLOPT_RETURNTRANSFER, true); - curl_setopt ($c, CURLOPT_BINARYTRANSFER, true); - $imgRaw = curl_exec ($c); - - if (curl_getinfo ($c, CURLINFO_HTTP_CODE) == 200) { - $file = fopen ($dest, 'w'); - if ($file === false) { - return $url; - } - - fwrite ($file, $imgRaw); - fclose ($file); - } else { - return $url; - } - - curl_close ($c); - } - - return $favicon_url; -} - /** * Add support of image lazy loading * Move content from src attribute to data-original @@ -214,8 +189,47 @@ function dowload_favicon ($website, $id) { */ function lazyimg($content) { return preg_replace( - '/<img([^<]+)src=([\'"])([^"\']*)([\'"])([^<]*)>/i', - '<img$1src="' . Url::display('/data/grey.gif') . '" data-original="$3"$5>', + '/<img([^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', + '<img$1src="' . Minz_Url::display('/themes/icons/grey.gif') . '" data-original="$2"$3>', $content ); } + +function lazyIframe($content) { + return preg_replace( + '/<iframe([^>]+?)src=[\'"]([^"\']+)[\'"]([^>]*)>/i', + '<iframe$1src="about:blank" data-original="$2"$3>', + $content + ); +} + +function uTimeString() { + $t = @gettimeofday(); + return $t['sec'] . str_pad($t['usec'], 6, '0'); +} + +function uSecString() { + $t = @gettimeofday(); + return str_pad($t['usec'], 6, '0'); +} + +function invalidateHttpCache() { + touch(LOG_PATH . '/' . Minz_Session::param('currentUser', '_') . '.log'); + Minz_Session::_param('touch', uTimeString()); +} + +function usernameFromPath($userPath) { + if (preg_match('%/([a-z0-9]{1,16})_user\.php$%', $userPath, $matches)) { + return $matches[1]; + } else { + return ''; + } +} + +function listUsers() { + return array_map('usernameFromPath', glob(DATA_PATH . '/*_user.php')); +} + +function httpAuthUser() { + return isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'] : ''; +} diff --git a/lib/minz/Configuration.php b/lib/minz/Configuration.php deleted file mode 100755 index b296ec378..000000000 --- a/lib/minz/Configuration.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * La classe Configuration permet de gérer la configuration de l'application - */ -class Configuration { - const CONF_PATH_NAME = '/configuration/application.ini'; - - /** - * VERSION est la version actuelle de MINZ - */ - const VERSION = '1.3.1.freshrss'; // version spéciale FreshRSS - - /** - * valeurs possibles pour l'"environment" - * SILENT rend l'application muette (pas de log) - * PRODUCTION est recommandée pour une appli en production - * (log les erreurs critiques) - * DEVELOPMENT log toutes les erreurs - */ - const SILENT = 0; - const PRODUCTION = 1; - const DEVELOPMENT = 2; - - /** - * définition des variables de configuration - * $sel_application une chaîne de caractères aléatoires (obligatoire) - * $environment gère le niveau d'affichage pour log et erreurs - * $use_url_rewriting indique si on utilise l'url_rewriting - * $base_url le chemin de base pour accéder à l'application - * $title le nom de l'application - * $language la langue par défaut de l'application - * $cacheEnabled permet de savoir si le cache doit être activé - * $delayCache la limite de cache - * $db paramètres pour la base de données (tableau) - * - host le serveur de la base - * - user nom d'utilisateur - * - password mot de passe de l'utilisateur - * - base le nom de la base de données - */ - private static $sel_application = ''; - private static $environment = Configuration::PRODUCTION; - private static $base_url = ''; - private static $use_url_rewriting = false; - private static $title = ''; - private static $language = 'en'; - private static $cache_enabled = false; - private static $delay_cache = 3600; - - private static $db = array ( - 'host' => false, - 'user' => false, - 'password' => false, - 'base' => false - ); - - /* - * Getteurs - */ - public static function selApplication () { - return self::$sel_application; - } - public static function environment () { - return self::$environment; - } - public static function baseUrl () { - return self::$base_url; - } - public static function useUrlRewriting () { - return self::$use_url_rewriting; - } - public static function title () { - return self::$title; - } - public static function language () { - return self::$language; - } - public static function cacheEnabled () { - return self::$cache_enabled; - } - public static function delayCache () { - return self::$delay_cache; - } - public static function dataBase () { - return self::$db; - } - - /** - * Initialise les variables de configuration - * @exception FileNotExistException si le CONF_PATH_NAME n'existe pas - * @exception BadConfigurationException si CONF_PATH_NAME mal formaté - */ - public static function init () { - try { - self::parseFile (); - self::setReporting (); - } catch (FileNotExistException $e) { - throw $e; - } catch (BadConfigurationException $e) { - throw $e; - } - } - - /** - * Parse un fichier de configuration de type ".ini" - * @exception FileNotExistException si le CONF_PATH_NAME n'existe pas - * @exception BadConfigurationException si CONF_PATH_NAME mal formaté - */ - private static function parseFile () { - if (!file_exists (APP_PATH . self::CONF_PATH_NAME)) { - throw new FileNotExistException ( - APP_PATH . self::CONF_PATH_NAME, - MinzException::ERROR - ); - } - - $ini_array = parse_ini_file ( - APP_PATH . self::CONF_PATH_NAME, - true - ); - - if (!$ini_array) { - throw new PermissionDeniedException ( - APP_PATH . self::CONF_PATH_NAME, - MinzException::ERROR - ); - } - - // [general] est obligatoire - if (!isset ($ini_array['general'])) { - throw new BadConfigurationException ( - '[general]', - MinzException::ERROR - ); - } - $general = $ini_array['general']; - - - // sel_application est obligatoire - if (!isset ($general['sel_application'])) { - throw new BadConfigurationException ( - 'sel_application', - MinzException::ERROR - ); - } - self::$sel_application = $general['sel_application']; - - if (isset ($general['environment'])) { - switch ($general['environment']) { - case 'silent': - self::$environment = Configuration::SILENT; - break; - case 'development': - self::$environment = Configuration::DEVELOPMENT; - break; - case 'production': - self::$environment = Configuration::PRODUCTION; - break; - default: - throw new BadConfigurationException ( - 'environment', - MinzException::ERROR - ); - } - - } - if (isset ($general['base_url'])) { - self::$base_url = $general['base_url']; - } - if (isset ($general['use_url_rewriting'])) { - self::$use_url_rewriting = $general['use_url_rewriting']; - } - - if (isset ($general['title'])) { - self::$title = $general['title']; - } - if (isset ($general['language'])) { - self::$language = $general['language']; - } - if (isset ($general['cache_enabled'])) { - self::$cache_enabled = $general['cache_enabled']; - if (CACHE_PATH === false && self::$cache_enabled) { - throw new FileNotExistException ( - 'CACHE_PATH', - MinzException::ERROR - ); - } - } - if (isset ($general['delay_cache'])) { - self::$delay_cache = $general['delay_cache']; - } - - // Base de données - $db = false; - if (isset ($ini_array['db'])) { - $db = $ini_array['db']; - } - if ($db) { - if (!isset ($db['host'])) { - throw new BadConfigurationException ( - 'host', - MinzException::ERROR - ); - } - if (!isset ($db['user'])) { - throw new BadConfigurationException ( - 'user', - MinzException::ERROR - ); - } - if (!isset ($db['password'])) { - throw new BadConfigurationException ( - 'password', - MinzException::ERROR - ); - } - if (!isset ($db['base'])) { - throw new BadConfigurationException ( - 'base', - MinzException::ERROR - ); - } - - self::$db['type'] = isset ($db['type']) ? $db['type'] : 'mysql'; - self::$db['host'] = $db['host']; - self::$db['user'] = $db['user']; - self::$db['password'] = $db['password']; - self::$db['base'] = $db['base']; - self::$db['prefix'] = isset ($db['prefix']) ? $db['prefix'] : ''; - } - } - - private static function setReporting () { - if (self::environment () == self::DEVELOPMENT) { - error_reporting (E_ALL); - ini_set ('display_errors','On'); - ini_set('log_errors', 'On'); - } elseif (self::environment () == self::PRODUCTION) { - error_reporting(E_ALL); - ini_set('display_errors','Off'); - ini_set('log_errors', 'On'); - } else { - error_reporting(0); - } - } -} diff --git a/lib/minz/dao/Model_array.php b/lib/minz/dao/Model_array.php deleted file mode 100755 index 0b9ccf071..000000000 --- a/lib/minz/dao/Model_array.php +++ /dev/null @@ -1,122 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * La classe Model_array représente le modèle interragissant avec les fichiers de type texte gérant des tableaux php - */ -class Model_array extends Model_txt { - /** - * $array Le tableau php contenu dans le fichier $nameFile - */ - protected $array = array (); - - /** - * Ouvre le fichier indiqué, charge le tableau dans $array et le $nameFile - * @param $nameFile le nom du fichier à ouvrir contenant un tableau - * Remarque : $array sera obligatoirement un tableau - */ - public function __construct ($nameFile) { - parent::__construct ($nameFile); - - if (!$this->getLock ('read')) { - throw new PermissionDeniedException ($this->filename); - } else { - $this->array = include ($this->filename); - $this->releaseLock (); - - if (!is_array ($this->array)) { - $this->array = array (); - } - - $this->array = $this->decodeArray ($this->array); - } - } - - /** - * Écrit un tableau dans le fichier $nameFile - * @param $array le tableau php à enregistrer - **/ - public function writeFile ($array) { - if (!$this->getLock ('write')) { - throw new PermissionDeniedException ($this->namefile); - } else { - $this->erase (); - - $this->writeLine ('<?php'); - $this->writeLine ('return ', false); - $this->writeArray ($array); - $this->writeLine (';'); - - $this->releaseLock (); - } - } - - private function writeArray ($array, $profondeur = 0) { - $tab = ''; - for ($i = 0; $i < $profondeur; $i++) { - $tab .= "\t"; - } - $this->writeLine ('array ('); - - foreach ($array as $key => $value) { - if (is_int ($key)) { - $this->writeLine ($tab . "\t" . $key . ' => ', false); - } else { - $this->writeLine ($tab . "\t" . '\'' . $key . '\'' . ' => ', false); - } - - if (is_array ($value)) { - $this->writeArray ($value, $profondeur + 1); - $this->writeLine (','); - } else { - if (is_numeric ($value)) { - $this->writeLine ($value . ','); - } else { - $this->writeLine ('\'' . addslashes ($value) . '\','); - } - } - } - - $this->writeLine ($tab . ')', false); - } - - private function decodeArray ($array) { - $new_array = array (); - - foreach ($array as $key => $value) { - if (is_array ($value)) { - $new_array[$key] = $this->decodeArray ($value); - } else { - $new_array[$key] = stripslashes ($value); - } - } - - return $new_array; - } - - private function getLock ($type) { - if ($type == 'write') { - $lock = LOCK_EX; - } else { - $lock = LOCK_SH; - } - - $count = 1; - while (!flock ($this->file, $lock) && $count <= 50) { - $count++; - } - - if ($count >= 50) { - return false; - } else { - return true; - } - } - - private function releaseLock () { - flock ($this->file, LOCK_UN); - } -} diff --git a/lib/minz/dao/Model_txt.php b/lib/minz/dao/Model_txt.php deleted file mode 100755 index aed653068..000000000 --- a/lib/minz/dao/Model_txt.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -/** - * MINZ - Copyright 2011 Marien Fressinaud - * Sous licence AGPL3 <http://www.gnu.org/licenses/> -*/ - -/** - * La classe Model_txt représente le modèle interragissant avec les fichiers de type texte - */ -class Model_txt { - /** - * $file représente le fichier à ouvrir - */ - protected $file; - - /** - * $filename est le nom du fichier - */ - protected $filename; - - /** - * Ouvre un fichier dans $file - * @param $nameFile nom du fichier à ouvrir - * @param $mode mode d'ouverture du fichier ('a+' par défaut) - * @exception FileNotExistException si le fichier n'existe pas - * > ou ne peux pas être ouvert - */ - public function __construct ($nameFile, $mode = 'a+') { - $this->filename = $nameFile; - if (!file_exists($this->filename)) { - throw new FileNotExistException ( - $this->filename, - MinzException::WARNING - ); - } - - $this->file = @fopen ($this->filename, $mode); - - if (!$this->file) { - throw new PermissionDeniedException ( - $this->filename, - MinzException::WARNING - ); - } - } - - /** - * Lit une ligne de $file - * @return une ligne du fichier - */ - public function readLine () { - return fgets ($this->file); - } - - /** - * Écrit une ligne dans $file - * @param $line la ligne à écrire - */ - public function writeLine ($line, $newLine = true) { - $char = ''; - if ($newLine) { - $char = "\n"; - } - - fwrite ($this->file, $line . $char); - } - - /** - * Efface le fichier $file - * @return true en cas de succès, false sinon - */ - public function erase () { - return ftruncate ($this->file, 0); - } - - /** - * Ferme $file - */ - public function __destruct () { - if (isset ($this->file)) { - fclose ($this->file); - } - } -} diff --git a/lib/minz/exceptions/MinzException.php b/lib/minz/exceptions/MinzException.php deleted file mode 100644 index 4568c4da8..000000000 --- a/lib/minz/exceptions/MinzException.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -class MinzException extends Exception { - const ERROR = 0; - const WARNING = 10; - const NOTICE = 20; - - public function __construct ($message, $code = self::ERROR) { - if ($code != MinzException::ERROR - && $code != MinzException::WARNING - && $code != MinzException::NOTICE) { - $code = MinzException::ERROR; - } - - parent::__construct ($message, $code); - } -} - -class PermissionDeniedException extends MinzException { - public function __construct ($file_name, $code = self::ERROR) { - $message = 'Permission is denied for `' . $file_name.'`'; - - parent::__construct ($message, $code); - } -} -class FileNotExistException extends MinzException { - public function __construct ($file_name, $code = self::ERROR) { - $message = 'File doesn\'t exist : `' . $file_name.'`'; - - parent::__construct ($message, $code); - } -} -class BadConfigurationException extends MinzException { - public function __construct ($part_missing, $code = self::ERROR) { - $message = '`' . $part_missing - . '` in the configuration file is missing or is misconfigured'; - - parent::__construct ($message, $code); - } -} -class ControllerNotExistException extends MinzException { - public function __construct ($controller_name, $code = self::ERROR) { - $message = 'Controller `' . $controller_name - . '` doesn\'t exist'; - - parent::__construct ($message, $code); - } -} -class ControllerNotActionControllerException extends MinzException { - public function __construct ($controller_name, $code = self::ERROR) { - $message = 'Controller `' . $controller_name - . '` isn\'t instance of ActionController'; - - parent::__construct ($message, $code); - } -} -class ActionException extends MinzException { - public function __construct ($controller_name, $action_name, $code = self::ERROR) { - $message = '`' . $action_name . '` cannot be invoked on `' - . $controller_name . '`'; - - parent::__construct ($message, $code); - } -} -class RouteNotFoundException extends MinzException { - private $route; - - public function __construct ($route, $code = self::ERROR) { - $this->route = $route; - - $message = 'Route `' . $route . '` not found'; - - parent::__construct ($message, $code); - } - - public function route () { - return $this->route; - } -} -class PDOConnectionException extends MinzException { - public function __construct ($string_connection, $user, $code = self::ERROR) { - $message = 'Access to database is denied for `' . $user . '`' - . ' (`' . $string_connection . '`)'; - - parent::__construct ($message, $code); - } -} -class CurrentPagePaginationException extends MinzException { - public function __construct ($page) { - $message = 'Page number `' . $page . '` doesn\'t exist'; - - parent::__construct ($message, self::ERROR); - } -} diff --git a/lib/password_compat.php b/lib/password_compat.php new file mode 100644 index 000000000..e8ec02885 --- /dev/null +++ b/lib/password_compat.php @@ -0,0 +1,279 @@ +<?php +/** + * A Compatibility library with PHP 5.5's simplified password hashing API. + * + * @author Anthony Ferrara <ircmaxell@php.net> + * @license http://www.opensource.org/licenses/mit-license.html MIT License + * @copyright 2012 The Authors + */ + +namespace { + +if (!defined('PASSWORD_DEFAULT')) { + + define('PASSWORD_BCRYPT', 1); + define('PASSWORD_DEFAULT', PASSWORD_BCRYPT); + + /** + * Hash the password using the specified algorithm + * + * @param string $password The password to hash + * @param int $algo The algorithm to use (Defined by PASSWORD_* constants) + * @param array $options The options for the algorithm to use + * + * @return string|false The hashed password, or false on error. + */ + function password_hash($password, $algo, array $options = array()) { + if (!function_exists('crypt')) { + trigger_error("Crypt must be loaded for password_hash to function", E_USER_WARNING); + return null; + } + if (!is_string($password)) { + trigger_error("password_hash(): Password must be a string", E_USER_WARNING); + return null; + } + if (!is_int($algo)) { + trigger_error("password_hash() expects parameter 2 to be long, " . gettype($algo) . " given", E_USER_WARNING); + return null; + } + $resultLength = 0; + switch ($algo) { + case PASSWORD_BCRYPT: + // Note that this is a C constant, but not exposed to PHP, so we don't define it here. + $cost = 10; + if (isset($options['cost'])) { + $cost = $options['cost']; + if ($cost < 4 || $cost > 31) { + trigger_error(sprintf("password_hash(): Invalid bcrypt cost parameter specified: %d", $cost), E_USER_WARNING); + return null; + } + } + // The length of salt to generate + $raw_salt_len = 16; + // The length required in the final serialization + $required_salt_len = 22; + $hash_format = sprintf("$2y$%02d$", $cost); + // The expected length of the final crypt() output + $resultLength = 60; + break; + default: + trigger_error(sprintf("password_hash(): Unknown password hashing algorithm: %s", $algo), E_USER_WARNING); + return null; + } + $salt_requires_encoding = false; + if (isset($options['salt'])) { + switch (gettype($options['salt'])) { + case 'NULL': + case 'boolean': + case 'integer': + case 'double': + case 'string': + $salt = (string) $options['salt']; + break; + case 'object': + if (method_exists($options['salt'], '__tostring')) { + $salt = (string) $options['salt']; + break; + } + case 'array': + case 'resource': + default: + trigger_error('password_hash(): Non-string salt parameter supplied', E_USER_WARNING); + return null; + } + if (PasswordCompat\binary\_strlen($salt) < $required_salt_len) { + trigger_error(sprintf("password_hash(): Provided salt is too short: %d expecting %d", PasswordCompat\binary\_strlen($salt), $required_salt_len), E_USER_WARNING); + return null; + } elseif (0 == preg_match('#^[a-zA-Z0-9./]+$#D', $salt)) { + $salt_requires_encoding = true; + } + } else { + $buffer = ''; + $buffer_valid = false; + if (function_exists('mcrypt_create_iv') && !defined('PHALANGER')) { + $buffer = mcrypt_create_iv($raw_salt_len, MCRYPT_DEV_URANDOM); + if ($buffer) { + $buffer_valid = true; + } + } + if (!$buffer_valid && function_exists('openssl_random_pseudo_bytes')) { + $buffer = openssl_random_pseudo_bytes($raw_salt_len); + if ($buffer) { + $buffer_valid = true; + } + } + if (!$buffer_valid && @is_readable('/dev/urandom')) { + $f = fopen('/dev/urandom', 'r'); + $read = PasswordCompat\binary\_strlen($buffer); + while ($read < $raw_salt_len) { + $buffer .= fread($f, $raw_salt_len - $read); + $read = PasswordCompat\binary\_strlen($buffer); + } + fclose($f); + if ($read >= $raw_salt_len) { + $buffer_valid = true; + } + } + if (!$buffer_valid || PasswordCompat\binary\_strlen($buffer) < $raw_salt_len) { + $bl = PasswordCompat\binary\_strlen($buffer); + for ($i = 0; $i < $raw_salt_len; $i++) { + if ($i < $bl) { + $buffer[$i] = $buffer[$i] ^ chr(mt_rand(0, 255)); + } else { + $buffer .= chr(mt_rand(0, 255)); + } + } + } + $salt = $buffer; + $salt_requires_encoding = true; + } + if ($salt_requires_encoding) { + // encode string with the Base64 variant used by crypt + $base64_digits = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + $bcrypt64_digits = + './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + + $base64_string = base64_encode($salt); + $salt = strtr(rtrim($base64_string, '='), $base64_digits, $bcrypt64_digits); + } + $salt = PasswordCompat\binary\_substr($salt, 0, $required_salt_len); + + $hash = $hash_format . $salt; + + $ret = crypt($password, $hash); + + if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != $resultLength) { + return false; + } + + return $ret; + } + + /** + * Get information about the password hash. Returns an array of the information + * that was used to generate the password hash. + * + * array( + * 'algo' => 1, + * 'algoName' => 'bcrypt', + * 'options' => array( + * 'cost' => 10, + * ), + * ) + * + * @param string $hash The password hash to extract info from + * + * @return array The array of information about the hash. + */ + function password_get_info($hash) { + $return = array( + 'algo' => 0, + 'algoName' => 'unknown', + 'options' => array(), + ); + if (PasswordCompat\binary\_substr($hash, 0, 4) == '$2y$' && PasswordCompat\binary\_strlen($hash) == 60) { + $return['algo'] = PASSWORD_BCRYPT; + $return['algoName'] = 'bcrypt'; + list($cost) = sscanf($hash, "$2y$%d$"); + $return['options']['cost'] = $cost; + } + return $return; + } + + /** + * Determine if the password hash needs to be rehashed according to the options provided + * + * If the answer is true, after validating the password using password_verify, rehash it. + * + * @param string $hash The hash to test + * @param int $algo The algorithm used for new password hashes + * @param array $options The options array passed to password_hash + * + * @return boolean True if the password needs to be rehashed. + */ + function password_needs_rehash($hash, $algo, array $options = array()) { + $info = password_get_info($hash); + if ($info['algo'] != $algo) { + return true; + } + switch ($algo) { + case PASSWORD_BCRYPT: + $cost = isset($options['cost']) ? $options['cost'] : 10; + if ($cost != $info['options']['cost']) { + return true; + } + break; + } + return false; + } + + /** + * Verify a password against a hash using a timing attack resistant approach + * + * @param string $password The password to verify + * @param string $hash The hash to verify against + * + * @return boolean If the password matches the hash + */ + function password_verify($password, $hash) { + if (!function_exists('crypt')) { + trigger_error("Crypt must be loaded for password_verify to function", E_USER_WARNING); + return false; + } + $ret = crypt($password, $hash); + if (!is_string($ret) || PasswordCompat\binary\_strlen($ret) != PasswordCompat\binary\_strlen($hash) || PasswordCompat\binary\_strlen($ret) <= 13) { + return false; + } + + $status = 0; + for ($i = 0; $i < PasswordCompat\binary\_strlen($ret); $i++) { + $status |= (ord($ret[$i]) ^ ord($hash[$i])); + } + + return $status === 0; + } +} + +} + +namespace PasswordCompat\binary { + /** + * Count the number of bytes in a string + * + * We cannot simply use strlen() for this, because it might be overwritten by the mbstring extension. + * In this case, strlen() will count the number of *characters* based on the internal encoding. A + * sequence of bytes might be regarded as a single multibyte character. + * + * @param string $binary_string The input string + * + * @internal + * @return int The number of bytes + */ + function _strlen($binary_string) { + if (function_exists('mb_strlen')) { + return mb_strlen($binary_string, '8bit'); + } + return strlen($binary_string); + } + + /** + * Get a substring based on byte limits + * + * @see _strlen() + * + * @param string $binary_string The input string + * @param int $start + * @param int $length + * + * @internal + * @return string The substring + */ + function _substr($binary_string, $start, $length) { + if (function_exists('mb_substr')) { + return mb_substr($binary_string, $start, $length, '8bit'); + } + return substr($binary_string, $start, $length); + } + +} |
