Example #1
0
 /**
  * Join all parameters into a URL path. This means all slashes are
  * normalized, removing all double slashes. If the first paramater has
  * a leading slash, the resulting string will also have a leading slash. If
  * it doesn't, the resulting string won't have one either.
  *
  * \param $parts
  *   Any number of string parameters (or an array)
  *
  * \return
  *   The resulting URL path
  *
  * \see URL::join_ext
  */
 static function join($parts = null)
 {
     $args = func_get_args();
     $num_args = func_num_args();
     /* Accept one single array argument too */
     if ($num_args == 1 && is_numeric_array($args[0])) {
         $args = $args[0];
     }
     assert('is_numeric_array($args)');
     $use_leading_slash = $args && is_string($args[0]) && str_has_prefix($args[0], '/');
     $use_trailing_slash = false;
     // decide later
     $list = array();
     while ($args) {
         $arg = array_shift($args);
         if (is_int($arg)) {
             $arg = (string) $arg;
         }
         assert('is_string($arg)');
         /* Check the last item for a trailing slash */
         if (!$args) {
             /* This is the last item */
             $use_trailing_slash = str_has_suffix($arg, '/');
         }
         /* Strip leading slashes */
         if (str_has_prefix($arg, '/')) {
             $arg = preg_replace('#^/*(.*)$#', '\\1', $arg);
         }
         /* Strip trailing slashes */
         if (str_has_suffix($arg, '/')) {
             $arg = preg_replace('#^(.*?)/+$#', '\\1', $arg);
         }
         /* Add to the list */
         $list[] = $arg;
     }
     /* Only non-whitespace strings are taken into account */
     $list = str_all_non_white($list);
     $joined = join('/', $list);
     /* Special case for empty results */
     if (strlen($joined) == '') {
         return $use_leading_slash || $use_trailing_slash ? '/' : '';
     }
     /* Leading slash */
     if ($use_leading_slash) {
         $joined = '/' . $joined;
     }
     /* Trailing slash */
     if ($use_trailing_slash) {
         $joined = $joined . '/';
     }
     return $joined;
 }
Example #2
0
 /**
  * Check if filename looks like a JPEG filename.
  *
  * \param $filename
  *   Filename to test.
  *
  * \return
  *   True if the filename looks like a JPEG filename, false otherwise.
  */
 private static function _filename_looks_like_jpeg($filename)
 {
     return str_has_suffix($filename, '.jpg') || str_has_suffix($filename, '.JPG') || str_has_suffix($filename, '.jpeg') || str_has_suffix($filename, '.JPEG');
 }
Example #3
0
 /**
  * \private
  *
  * Build a block level HTML element with the specified properties.
  *
  * \param $tag_name
  *   The name of the XHTML tag that should be created. Only a few
  *   block-level tags can be handled.
  *
  * \param $parsed_block
  *   A parsed block
  *
  * \return
  *   XHTML snippet as a string
  */
 function _build_html_block_element($tag_name, $parsed_block)
 {
     assert('is_string($tag_name); // tag name must be a string');
     assert('in_array($tag_name, explode(",", "p,h1,h2,h3,h4,h5,h6,div")); // invalid tag name');
     $attr = array();
     /* Class, id and language attributes can be copied. */
     foreach (array('class', 'id', 'lang') as $key) {
         if (!is_null($parsed_block[$key])) {
             $attr[$key] = $parsed_block[$key];
         }
     }
     /* The style attribute is a bit trickier, since the alignment and padding
      * specificiers must be incorporated as well. */
     $attr['style'] = '';
     if (!is_null($parsed_block['style'])) {
         $attr['style'] = $parsed_block['style'];
         /* Make sure the style declaration ends with a semicolon, so that we
          * can safely append other CSS snippets. */
         if (!str_has_suffix($attr['style'], ';')) {
             $attr['style'] .= ';';
         }
     }
     /* Alignment */
     if (!is_null($parsed_block['align'])) {
         $spec_to_value_mapping = array('<' => 'left', '>' => 'right', '=' => 'center', '<>' => 'justify');
         $attr['style'] .= sprintf(' text-align: %s;', $spec_to_value_mapping[$parsed_block['align']]);
     }
     /* Padding */
     if ($parsed_block['padding-left'] > 0) {
         $attr['style'] .= sprintf(' padding-left: %dem;', $parsed_block['padding-left']);
     }
     if ($parsed_block['padding-right'] > 0) {
         $attr['style'] .= sprintf(' padding-right: %dem;', $parsed_block['padding-right']);
     }
     /* Only include style attribute if needed */
     if (strlen($attr['style']) == 0) {
         array_unset_key($attr, 'style');
     }
     /* Generate the result */
     switch ($tag_name) {
         case 'p':
             $el = new AnewtXHTMLParagraph(null);
             break;
         case 'div':
             $el = new AnewtXHTMLDiv(null);
             break;
         case 'h1':
             $el = new AnewtXHTMLHeader1(null);
             break;
         case 'h2':
             $el = new AnewtXHTMLHeader2(null);
             break;
         case 'h3':
             $el = new AnewtXHTMLHeader3(null);
             break;
         case 'h4':
             $el = new AnewtXHTMLHeader4(null);
             break;
         case 'h5':
             $el = new AnewtXHTMLHeader5(null);
             break;
         case 'h6':
             $el = new AnewtXHTMLHeader6(null);
             break;
         default:
             assert('false; // unknown block tag name');
             break;
     }
     $el->append_child(ax_raw($parsed_block['markup']));
     $el->set_attributes($attr);
     return $el;
 }
Example #4
0
 /**
  * Escape a field for embedding in an SQL query.
  *
  * This method does rigid sanity checking and throws errors when the
  * supplied value is not suitable for the specified field type.
  *
  * \param $field_type
  *   The field type (one of the \c ANEWT_DATABASE_SQL_FIELD_TYPE_* constants)
  * \param $value
  *   The value to escape
  *
  * \return
  *   The escaped value
  *
  * \see escape_field_array
  */
 private function escape_field($field_type, $value)
 {
     /* Escaping is not needed for NULL values. */
     if (is_null($value)) {
         return 'NULL';
     }
     /* The value is non-null. Perform very restrictive input sanitizing
      * based on the field type. */
     switch ($field_type) {
         case ANEWT_DATABASE_SQL_FIELD_TYPE_BOOLEAN:
             /* Integers: only accept 0 and 1 (no type juggling!) */
             if (is_int($value)) {
                 if ($value === 0) {
                     $value = false;
                 } elseif ($value === 1) {
                     $value = true;
                 }
             }
             /* Strings: only accept literal "0" and "1" (no type juggling!) */
             if (is_string($value)) {
                 if ($value === "0") {
                     $value = false;
                 } elseif ($value === "1") {
                     $value = true;
                 }
             }
             if (is_bool($value)) {
                 $value = $this->connection->escape_boolean($value);
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid boolean value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_INTEGER:
             if (is_int($value)) {
                 $value = (string) $value;
                 break;
             }
             if (is_string($value) && preg_match('/^-?\\d+$/', $value)) {
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid integer value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_FLOAT:
             /* FIXME: this does not accept .123 (without a leading zero) */
             if (is_string($value) && preg_match('/^-?\\d+(\\.\\d*)?$/', $value)) {
                 /* Enough checks done by the regex, no need to do any
                  * formatting/escaping */
                 break;
             } elseif (is_int($value) || is_float($value)) {
                 /* Locale-agnostic float formatting */
                 $value = number_format($value, 10, '.', '');
                 if (str_has_suffix($value, '.')) {
                     $value .= '0';
                 }
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid float value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_STRING:
             /* Accept integers and objects with a render() method. */
             if (is_int($value)) {
                 $value = (string) $value;
             } elseif (is_object($value) && method_exists($value, 'render')) {
                 $value = to_string($value);
             }
             /* From this point on only strings are accepted. */
             if (is_string($value)) {
                 $value = $this->connection->escape_string($value);
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid string value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_DATE:
             if ($value instanceof AnewtDateTimeAtom) {
                 $value = AnewtDateTime::sql_date($value);
             }
             if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2}$/', $value)) {
                 $value = $this->connection->escape_date($value);
                 break;
             }
             if (is_string($value) && strtoupper($value) == 'NOW') {
                 $value = 'NOW()';
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid date value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_TIME:
             if ($value instanceof AnewtDateTimeAtom) {
                 $value = AnewtDateTime::sql_time($value);
             }
             if (is_string($value) && preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $value)) {
                 $value = $this->connection->escape_time($value);
                 break;
             }
             if (is_string($value) && strtoupper($value) == 'NOW') {
                 $value = 'NOW()';
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid time value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_DATETIME:
         case ANEWT_DATABASE_SQL_FIELD_TYPE_TIMESTAMP:
             if ($value instanceof AnewtDateTimeAtom) {
                 $value = AnewtDateTime::sql($value);
             }
             if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/', $value)) {
                 $value = $this->connection->escape_datetime($value);
                 break;
             }
             if (is_string($value) && strtoupper($value) == 'NOW') {
                 $value = 'NOW()';
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid datetime or timestamp value: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_RAW:
             /* No checking, no escaping... use at your own risk! */
             break;
             /* The column and table type are mostly for internal usage, it's
              * a BAD idea to use user data for these fields! */
         /* The column and table type are mostly for internal usage, it's
          * a BAD idea to use user data for these fields! */
         case ANEWT_DATABASE_SQL_FIELD_TYPE_COLUMN:
             if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) {
                 $value = $this->connection->escape_column_name($value);
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid column name: "%s"', $value);
         case ANEWT_DATABASE_SQL_FIELD_TYPE_TABLE:
             if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) {
                 $value = $this->connection->escape_table_name($value);
                 break;
             }
             throw new AnewtDatabaseQueryException('Invalid table name: "%s"', $value);
         default:
             throw new AnewtDatabaseQueryException('Unsupported field type! Please file a bug.');
             break;
     }
     assert('is_string($value)');
     return $value;
 }
Example #5
0
 /**
  * Dispatch an URL to the correct handlers. This method does the actual
  * magic, such as url parsing, matching and command invocation. You can
  * optionally provide a custom url and tell the dispatcher that some parts
  * of the url should be skipped when handling this request.
  *
  * \param $url
  *   The url to dispatch (optional, defaults to null). Omit this value (or
  *   provide null) to use the current request url.
  * 
  * \param $skip_path_components
  *   The number of path components to skip when handling this request
  *   (optional, defaults to null). This is useful if you want to skip some
  *   prefix present in all urls, such as ~username. If you don't specify
  *   this parameter the value of <code>$_SERVER['PHP_SELF']</code> will be
  *   used to figure out how many components to skip.
  */
 function dispatch($url = null, $skip_path_components = null)
 {
     if (is_null($skip_path_components)) {
         /* Figure out the current script location. This is most likely
          * the script living in the document root calling this method. Use
          * the directory path component of this file to figure out how many
          * path components should be skipped. */
         $dir_url = dirname($_SERVER['PHP_SELF']);
         if ($dir_url == DIRECTORY_SEPARATOR) {
             $skip_path_components = 0;
         } else {
             $skip_path_components = count(explode('/', str_strip_prefix($dir_url, '/')));
         }
     }
     /* Initialization */
     $get_params = '';
     /* Use the current url if no explicit url was given */
     if (is_null($url)) {
         $url = Request::relative_url();
     }
     /* We need to strip off the GET parameters */
     $question_mark_pos = strpos($url, '?');
     if ($question_mark_pos !== false) {
         $get_params = substr($url, $question_mark_pos);
         $url = substr($url, 0, $question_mark_pos);
     }
     /* Sanity checks */
     assert('is_int($skip_path_components) && $skip_path_components >= 0');
     assert('is_string($url)');
     /* Force trailing slash for GET requests? */
     if (Request::is_get() && $this->_getdefault('force-trailing-slash', true) && $url != '/' && !str_has_suffix($url, '/') && preg_match('/^.*\\/[^.\\/]+$/', $url)) {
         redirect($url . '/' . $get_params, HTTP_STATUS_MOVED_PERMANENTLY);
     }
     /* Store the url so that it can be used later */
     $this->_set('url', $url);
     /* Split the url into smaller pieces */
     $url = str_strip_prefix($url, '/');
     $url = str_strip_suffix($url, '/');
     $components = split('/', $url);
     /* Skip some components if requested, and store the cut-off part in the
      * 'base-url' property. */
     if (count($components) < $skip_path_components) {
         $this->_handle_result(HTTP_STATUS_INTERNAL_SERVER_ERROR);
     }
     $base_url = sprintf('/%s/', join('/', array_slice($components, 0, $skip_path_components)));
     $this->_set('base-url', $base_url);
     $components = array_slice($components, $skip_path_components);
     /* Special case for top level urls */
     if (count($components) == 1 && strlen($components[0]) == 0) {
         $components = array();
     }
     /* Try all URL maps and see if they match the input url */
     $found_map = false;
     $command_name = null;
     $parameters = array();
     foreach ($this->urlmaps as $urlmap) {
         list($command_name, $patterns) = $urlmap;
         /* Check for valid parameters */
         $match = $this->_match_inputs_with_patterns($components, $patterns);
         /* This urlmap didn't match, try next one */
         if ($match === false) {
             continue;
             /* This urlmap matched! */
         } else {
             $parameters = $match;
             $found_map = true;
             break;
         }
     }
     /* No explicit map found, try an implicit map */
     if (!$found_map && $this->_getdefault('use-implicit-commands', true)) {
         $command_name = join('_', $components);
         $command_name = str_replace('-', '_', $command_name);
         /* The method must exist */
         $command = 'command_' . $command_name;
         $found_map = method_exists($this, $command);
     }
     /* As a last resort try the default handler, if one was set. There's no
      * need to check the availability of the method; set_default_command()
      * already did that. */
     if (!$found_map && $this->_isset('default-command')) {
         $command_name = $this->_get('default-command');
         $found_map = true;
     }
     /* Sanity check: is the method available? */
     $command = 'command_' . $command_name;
     if (!method_exists($this, $command)) {
         /* FIXME: it's not clear if this is desirable */
         /* We found a handler name but the method doesn't exist... */
         /* Trying the default handler, but remember which command
          * we wanted to access. */
         if (!$this->_isset('default_command')) {
             /* We give up. */
             $found_map = false;
         } else {
             $command = 'command_' . $this->_get('default-command');
         }
     }
     /* If we still don't have a command, we give up. Too bad... not found */
     if (!$found_map) {
         $this->_handle_result(HTTP_STATUS_NOT_FOUND);
         return false;
     }
     /* Store the command name for use by _handle_result() and possibly
      * pre_command(). */
     $this->_set('command', $command_name);
     /* If this piece of code is reached, a valid command has been found. Run
      * the pre-command hook, call the appropriate method and handle the
      * result. The pre_command() method may return HTTP_STATUS_NOT_FOUND or
      * HTTP_STATUS_FORBIDDEN as well, so we need to handle the status
      * carefully. */
     $status = $this->pre_command($parameters);
     /* The pre_command() method is not required to have a return value. If
      * it doesn't return anything, $status will be null at this point. If
      * that's the case we assume everything is alright. */
     if (is_null($status)) {
         $status = HTTP_STATUS_OK;
     }
     if ($status == HTTP_STATUS_OK) {
         /* The pre_command() method succeeded. Run the actual command and
          * keep track of the status. */
         $status = $this->{$command}($parameters);
     }
     /* Regardless of whether the actual command has been executed, the
      * result handler is invoked. Note: The post_command() is only invoked
      * if both the pre_command() and the actual command method return
      * HTTP_STATUS_OK (there's no danger of calling post_command() if no
      * real command has been executed). */
     $this->_handle_result($status);
 }
Example #6
0
 /**
  * (Really) dispatch an URL to the correct handlers.
  *
  * This method does the actual magic, such as URL parsing, matching and
  * command invocation. You can optionally provide a custom URL and tell the
  * dispatcher that some parts of the URL should be skipped when handling
  * this request.
  *
  * \param $url
  * \param $prefix
  * \see AnewtURLDispatcher::dispatch
  */
 private function real_dispatch($url = null, $prefix = null)
 {
     /* Use the current URL if no explicit url was given */
     if (is_null($url)) {
         $url = AnewtRequest::relative_url();
     }
     /* Figure out the right base location if no prefix was given. If the URL
      * starts with the PHP script name, we assume no htaccess file has been
      * setup to beautify the website URLs. In this case the relevant parts
      * of the URL are added after the PHP script name. Example URL of such
      * a setup is http://.../dispatch.php/a/b/c/. Otherwise, it is quite
      * likely a htaccess file is used to point all requests to a script that
      * invokes the dispatcher. We assume this script is placed in the
      * toplevel directory, so we use that directory as the prefix. */
     if (is_null($prefix)) {
         if (str_has_prefix($url, $_SERVER['SCRIPT_NAME'])) {
             $prefix = $_SERVER['SCRIPT_NAME'];
         } else {
             $prefix = dirname($_SERVER['SCRIPT_NAME']);
         }
     }
     assert('is_string($url)');
     assert('is_string($prefix)');
     /* Strip off the GET parameters from the URL */
     $get_params = '';
     $question_mark_pos = strpos($url, '?');
     if ($question_mark_pos !== false) {
         $get_params = substr($url, $question_mark_pos);
         $url = substr($url, 0, $question_mark_pos);
     }
     /* Redirect GET requests when trailing slash is required but missing */
     if (!str_has_suffix($url, '/') && $this->force_trailing_slash && AnewtRequest::is_get() && !preg_match('#^.*\\.[^\\/]*$#', $url)) {
         redirect(sprintf('%s/%s', $url, $get_params), HTTP_STATUS_MOVED_PERMANENTLY);
     }
     /* Strip off prefix and slashes */
     $this->request_url_full = $url;
     $url = str_strip_prefix($url, $prefix);
     $url = str_strip_prefix($url, '/');
     $url = str_strip_suffix($url, '/');
     $this->request_url = $url;
     /* Try to find a matching route and extract the parameters */
     $found_route = false;
     $command = null;
     $parameters = array();
     $url_parts = strlen($url) > 0 ? explode('/', $url) : array();
     foreach ($this->routes as $route) {
         $route_type = array_shift($route);
         $route_command = array_shift($route);
         $route_parameters = array();
         /* Type I: Routes using regular expression */
         if ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_REGEX) {
             list($pattern) = $route;
             /* Try both with and without trailing slash */
             if (preg_match($pattern, $url, $route_parameters) || preg_match($pattern, sprintf('%s/', $url), $route_parameters)) {
                 /* We don't care about $parameters[0] (it contains the full match) */
                 array_shift($route_parameters);
                 $route_parameters = array_map('urldecode', $route_parameters);
                 $command = $route_command;
                 $parameters = $route_parameters;
                 $found_route = true;
                 break;
             }
         } elseif ($route_type == ANEWT_URL_DISPATCHER_ROUTE_TYPE_URL_PARTS) {
             list($route_url, $additional_constraints) = $route;
             /* Route URL can be a string or an array */
             if (is_string($route_url)) {
                 $route_url = str_strip_prefix($route_url, '/');
                 $route_url = str_strip_suffix($route_url, '/');
                 $route_url_parts = strlen($route_url) > 0 ? explode('/', $route_url) : array();
             } elseif (is_numeric_array($route_url)) {
                 $route_url_parts = $route_url;
             } else {
                 throw new AnewtException('Invalid url route: %s', $route_url);
             }
             /* Match the URL parts against the route URL parts */
             if (count($url_parts) != count($route_url_parts)) {
                 continue;
             }
             $constraints = array_merge($this->url_part_constraints, $additional_constraints);
             for ($i = 0; $i < count($url_parts); $i++) {
                 /* If the URL starts with a ':' character it is
                  * a parameter... */
                 if ($route_url_parts[$i][0] === ':') {
                     $parameter_name = substr($route_url_parts[$i], 1);
                     $parameter_value = $url_parts[$i];
                     /* If there is a constraint for this parameter, the
                      * value must match the constraint. If not, this route
                      * cannot be used. */
                     if (array_key_exists($parameter_name, $constraints)) {
                         $pattern = $constraints[$parameter_name];
                         if (!preg_match($pattern, $parameter_value)) {
                             continue 2;
                         }
                     }
                     $route_parameters[$parameter_name] = urldecode($parameter_value);
                 } elseif ($url_parts[$i] !== $route_url_parts[$i]) {
                     continue 2;
                 }
             }
             /* If this code is reached, we found a matching route with all
              * the constraints on the URL parts satisfied. */
             $command = $route_command;
             $parameters = $route_parameters;
             $found_route = true;
             break;
         } else {
             assert('false; // not reached');
         }
     }
     /* If no route matches, try an automatic route. Only the first URL part
      * is considered for this. */
     if (!$found_route && $this->use_automatic_commands) {
         $url_parts = explode('/', $url, 2);
         if ($url_parts) {
             $command = array($this, sprintf('command_%s', $url_parts[0]));
             list($found_route, $error_message_to_ignore) = $this->is_valid_command($command);
         }
     }
     /* As a last resort try the default handler, if one was set. */
     $default_command = $this->default_command;
     if (!$found_route && !is_null($default_command)) {
         $command = $default_command;
         $command = $this->validate_command($command);
         $found_route = true;
     }
     /* If we still don't have a command, we give up. Too bad... not found */
     if (!$found_route) {
         throw new AnewtHTTPException(HTTP_STATUS_NOT_FOUND);
     }
     /* Check the command for validity. In most cases we already know the
      * command exists since that is already checked in the add_route_*()
      * methods or in the code above, except for lazily loaded commands, so
      * we try to load them and check for validity afterwards. */
     if (is_array($command) && is_string($command[0])) {
         $this->include_command_class($command[0]);
         $command = $this->validate_command($command);
     }
     /* Finally... run the command and the pre and post command hooks. */
     $this->pre_command($parameters);
     call_user_func($command, $parameters);
     $this->post_command($parameters);
 }
Example #7
0
 /**
  * Fills in the valus in the SQL template. This method will check all values
  * for correctness to avoid nasty SQL injection vulnerabilities.
  *
  * \param $args
  *   Array with values to use for substitution.
  *
  * \return
  *   The query containing all values, quoted correctly.
  */
 function fill($args = null)
 {
     /* We accept either:
      * - no parameters
      * - multiple scalar parameters
      * - 1 array parameter with scalar elements
      * - 1 associative array parameter
      * - 1 container parameter
      */
     $args = func_get_args();
     if ($this->named_mode) {
         if (count($args) != 1) {
             trigger_error('associative array or Container expected', E_USER_ERROR);
         }
         if ($args[0] instanceof Container) {
             $args = $args[0]->to_array();
         } elseif (is_array($args[0])) {
             $args = $args[0];
         } else {
             trigger_error('associative array or Container expected', E_USER_ERROR);
         }
         $numargs = count($this->named_placeholders);
     } else {
         if (count($args) == 1 && is_numeric_array($args[0])) {
             $args = $args[0];
         }
         assert('is_numeric_array($args)');
         if (count($args) != count($this->placeholders)) {
             trigger_error(sprintf('Incorrect number of parameters to SQLTemplate::fill(): expected %d, got %d', count($this->placeholders), count($args)), E_USER_ERROR);
         }
         $numargs = count($args);
     }
     /* Note: don't use foreach() here, because it copies the values in
      * memory and leaves the original values untouched! */
     for ($i = 0; $i < $numargs; $i++) {
         if ($this->named_mode) {
             $fieldtype = $this->named_placeholders[$i]['type'];
             $var = $this->named_placeholders[$i]['var'];
             if (!isset($args[$var])) {
                 $var = str_replace('-', '_', $var);
                 // Container replaces '-' with '_'
                 if (!array_key_exists($var, $args)) {
                     trigger_error(sprintf('SQLTemplate::fill(): missing expected parameter "%s"', $this->named_placeholders[$i]['var']), E_USER_ERROR);
                 }
             }
             $value = $args[$var];
             $argname = "`" . $var . "'";
         } else {
             $fieldtype = $this->placeholders[$i];
             $value =& $args[$i];
             $argname = $i + 1;
         }
         /* Handle NULL values here. Escaping is not needed for NULL values. */
         if (is_null($value)) {
             $value = 'NULL';
             if ($this->named_mode) {
                 $arglist[$i] = $value;
             }
             continue;
         }
         /* The value is non-null. Perform very restrictive input sanitizing
          * based on the field type. */
         switch ($fieldtype) {
             case ANEWT_DATABASE_TYPE_BOOLEAN:
                 /* Integers: only accept 0 and 1 (no type juggling!) */
                 if (is_int($value)) {
                     if ($value === 0) {
                         $value = false;
                     } elseif ($value === 1) {
                         $value = true;
                     }
                 }
                 /* Strings: only accept literal "0" and "1" (no type juggling!) */
                 if (is_string($value)) {
                     if ($value === "0") {
                         $value = false;
                     } elseif ($value === "1") {
                         $value = true;
                     }
                 }
                 if (is_bool($value)) {
                     $value = $this->db->backend->escape_boolean($value);
                     break;
                 }
                 trigger_error(sprintf('Invalid boolean value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_INTEGER:
                 if (is_int($value)) {
                     $value = (string) $value;
                     break;
                 }
                 if (is_string($value) && preg_match('/^-?\\d+$/', $value)) {
                     break;
                 }
                 trigger_error(sprintf('Invalid integer value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_FLOAT:
                 // FIXME: this does not accept .123 (without a leading zero)
                 if (is_string($value) && preg_match('/^-?\\d+(\\.\\d*)?$/', $value)) {
                     /* Enough checks done by the regex, no need to do any
                      * formatting/escaping */
                     break;
                     /* Locale-agnostic float formatting */
                 } elseif (is_int($value) || is_float($value)) {
                     $value = number_format($value, 10, '.', '');
                     if (str_has_suffix($value, '.')) {
                         $value .= '0';
                     }
                     break;
                 }
                 trigger_error(sprintf('Invalid float value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_STRING:
                 /* Accept integers and objects with a render() method. */
                 if (is_int($value)) {
                     $value = (string) $value;
                 } elseif (is_object($value) && method_exists($value, 'render')) {
                     $value = $value->render();
                 }
                 /* From this point on, only strings are accepted. */
                 if (is_string($value)) {
                     $value = $this->db->backend->escape_string($value);
                     break;
                 }
                 trigger_error(sprintf('Invalid string value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_DATE:
                 if ($value instanceof AnewtDateTimeAtom) {
                     $value = AnewtDateTime::sql_date($value);
                 }
                 if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2}$/', $value)) {
                     $value = $this->db->backend->escape_date($value);
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == "NOW") {
                     $value = "NOW()";
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == 'NOW') {
                     $value = 'NOW()';
                     break;
                 }
                 trigger_error(sprintf('Invalid date value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_TIME:
                 if ($value instanceof AnewtDateTimeAtom) {
                     $value = AnewtDateTime::sql_time($value);
                 }
                 if (is_string($value) && preg_match('/^\\d{2}:\\d{2}(:\\d{2})?$/', $value)) {
                     $value = $this->db->backend->escape_time($value);
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == "NOW") {
                     $value = "NOW()";
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == 'NOW') {
                     $value = 'NOW()';
                     break;
                 }
                 trigger_error(sprintf('Invalid time value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_DATETIME:
             case ANEWT_DATABASE_TYPE_TIMESTAMP:
                 if ($value instanceof AnewtDateTimeAtom) {
                     $value = AnewtDateTime::sql($value);
                 }
                 if (is_string($value) && preg_match('/^\\d{2,4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$/', $value)) {
                     $value = $this->db->backend->escape_datetime($value);
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == "NOW") {
                     $value = "NOW()";
                     break;
                 }
                 if (is_string($value) && strtoupper($value) == 'NOW') {
                     $value = 'NOW()';
                     break;
                 }
                 trigger_error(sprintf('Invalid datetime or timestamp value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_RAW:
                 /* No checking, no escaping... use at your own risk ;-) */
                 break;
                 /* The column and table type are mostly for internal usage, it's
                  * a BAD idea to use user data for these fields! */
             /* The column and table type are mostly for internal usage, it's
              * a BAD idea to use user data for these fields! */
             case ANEWT_DATABASE_TYPE_COLUMN:
                 if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) {
                     $value = $this->db->backend->escape_column_name($value);
                     break;
                 }
                 trigger_error(sprintf('Invalid column value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             case ANEWT_DATABASE_TYPE_TABLE:
                 if (is_string($value) && preg_match('/^([a-z0-9_-]+\\.)*[a-z0-9_-]+$/i', $value)) {
                     $value = $this->db->backend->escape_table_name($value);
                     break;
                 }
                 trigger_error(sprintf('Invalid table value: "%s" on argument %s', $value, $argname), E_USER_ERROR);
             default:
                 trigger_error('This is a bug! Fieldtype unknown', E_USER_ERROR);
                 break;
         }
         assert('is_string($value)');
         if ($this->named_mode) {
             $arglist[$i] = $value;
         }
     }
     /* Now that all supplied values are validated and escaped properly, we
      * can easily substitute them into the query template. The %s
      * placeholders were already prepared during initial parsing. */
     if ($this->named_mode) {
         $query = vsprintf($this->sql_template, $arglist);
     } else {
         $query = vsprintf($this->sql_template, $args);
     }
     return $query;
 }
Example #8
0
 /**
  * Build a URL from the passed parameters.
  *
  * You can provide a single path string, or an array of strings, in which
  * case each of the items in \c $path is a path component. The path
  * components will be concatenated, separated by slashes.
  *
  * All slashes are normalized. If the first path component has a leading
  * slash, the resulting string will also have a leading slash and if it
  * doesn't, the resulting string won't have one either. The same goes for
  * the trailing slash: if the last path component ends with a slash, the
  * resulting string will have one as well.
  *
  * If \c $parameters is passed, a HTTP query string will be appended to the
  * url using the this associatve array.
  *
  * Example:
  *
  * <code>$url = AnewtURL::join(array('/path/to', $file), array('foo' => 'bar'));</code>
  *
  * \param $path
  *   Single string or array of strings (each item is a path component of the url)
  *
  * \param $parameters
  *   Associative array used to build a query string (optional)
  *
  * \return
  *   The resulting URL path
  *
  * \see
  *   AnewtURL::parse
  */
 static function build($path, $parameters = null)
 {
     /* Input sanitizing */
     if (is_string($path)) {
         $path_components = array($path);
     } else {
         $path_components = $path;
     }
     if (is_null($parameters)) {
         $parameters = array();
     }
     assert('is_numeric_array($path_components);');
     assert('is_assoc_array($parameters);');
     /* Remove empty path components */
     $path_components = str_all_non_white($path_components);
     /* Leading and trailing slashes */
     if ($path_components) {
         /* Path is not empty */
         $use_leading_slash = str_has_prefix($path_components[0], '/');
         $use_trailing_slash = str_has_suffix($path_components[count($path_components) - 1], '/');
     } else {
         /* Path is empty */
         $use_leading_slash = false;
         $use_trailing_slash = false;
     }
     /* Loop over url parts and clean up */
     $first_part_seen = false;
     $path_components_clean = array();
     while ($path_components) {
         $part = array_shift($path_components);
         assert('is_string($part)');
         $part = str_strip_prefix($part, '/');
         $part = str_strip_suffix($part, '/');
         if (!strlen($part)) {
             continue;
         }
         /* Use url encoding, but the first path component may be something
          * like "http://...", which should not be url encoded. */
         if (!$first_part_seen && str_contains($part, '://')) {
             $first_part_seen = true;
             $part_encoded = $part;
         } else {
             $part_encoded = urlencode($part);
             /* Url decoding also escapes slashes and some other characters
              * we want to keep, since escaping those would disallow passing
              * path components like "/path/to/file". A slash cannot be used
              * in a filename anyway, so we special-case some characters. */
             $part_encoded = str_replace(array('%2F', '%7E'), array('/', '~'), $part_encoded);
         }
         $path_components_clean[] = $part_encoded;
     }
     /* Build url by joining all cleaned parts, and adding a leading and
      * trailing slash, if appropriate. */
     $url = join('/', $path_components_clean);
     if (strlen($url)) {
         /* Path is not empty */
         if ($use_leading_slash) {
             $url = sprintf('/%s', $url);
         }
         if ($use_trailing_slash) {
             $url = sprintf('%s/', $url);
         }
     } elseif ($use_leading_slash || $use_trailing_slash) {
         /* Path is empty, a slash is required */
         $url = '/';
     }
     /* Query parameters */
     assert('is_assoc_array($parameters)');
     $parameters_escaped = array();
     foreach ($parameters as $name => $value) {
         if (is_null($value)) {
             $parameters_escaped[] = urlencode($name);
         } else {
             assert('is_string($value);');
             $parameters_escaped[] = sprintf('%s=%s', urlencode($name), urlencode($value));
         }
     }
     if ($parameters_escaped) {
         $url = sprintf('%s?%s', $url, implode('&', $parameters_escaped));
     }
     return $url;
 }
 /**
  * Internal default handler preprocessor that looks for XML comments.
  *
  * \param $parser The parser instance
  * \param $data A string containing text data
  */
 private function _default_handler($parser, $data)
 {
     /* First flush the cdata buffer */
     $this->flush_data();
     /* Check if the data is comment */
     $comment_start = '<!--';
     $comment_end = '-->';
     if (str_has_prefix($data, $comment_start) && str_has_suffix($data, $comment_end)) {
         $comment = substr($data, strlen($comment_start));
         $comment = substr($comment, 0, strlen($comment) - strlen($comment_end));
         /* strip whitespace */
         $comment = trim($comment);
         $comment = preg_replace('/\\s+/', ' ', $comment);
         /* hand over control to the command handler */
         $this->handle_comment($comment);
         return;
     }
     /* hand over control to the default handler */
     $this->default_handler($data);
 }
Example #10
0
/**
 * Strip a suffix from a string.
 *
 * If the string didn't end with the given suffix, this function is a no-op: the
 * string is returned unaltered.
 *
 * \param $str
 *   The string to operate on.
 *
 * \param $suffix
 *   The suffix to remove.
 *
 * \return
 *   A string with the suffix removed, if found.
 */
function str_strip_suffix($str, $suffix)
{
    assert('is_string($str)');
    assert('is_string($suffix)');
    if (strlen($suffix) == 0) {
        return $str;
    }
    if (str_has_suffix($str, $suffix)) {
        $endpos = strlen($str) - strlen($suffix);
        return substr($str, 0, $endpos);
    }
    return $str;
}