b2evolution PHP Cross Reference Blogging Systems

Source: /inc/_core/_url.funcs.php - 906 lines - 23115 bytes - Summary - Text - Print

Description: URL manipulation functions This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}. See also {@link http://sourceforge.net/projects/evocms/}.

   1  <?php
   2  /**
   3   * URL manipulation functions
   4   *
   5   * This file is part of the b2evolution/evocms project - {@link http://b2evolution.net/}.
   6   * See also {@link http://sourceforge.net/projects/evocms/}.
   7   *
   8   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}.
   9   * Parts of this file are copyright (c)2006 by Daniel HAHLER - {@link http://daniel.hahler.de/}.
  10   *
  11   * @license http://b2evolution.net/about/license.html GNU General Public License (GPL)
  12   *
  13   * {@internal Open Source relicensing agreement:
  14   * Daniel HAHLER grants Francois PLANQUE the right to license
  15   * Daniel HAHLER's contributions to this file and the b2evolution project
  16   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  17   * }}
  18   *
  19   * @package evocore
  20   *
  21   * @author blueyed: Daniel HAHLER
  22   * @author Danny Ferguson
  23   *
  24   * @version $Id: _url.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  25   */
  26  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  27  
  28  
  29  /**
  30   * Check the validity of a given URL
  31   *
  32   * Checks allowed URI schemes and URL ban list.
  33   * URL can be empty.
  34   *
  35   * Note: We have a problem when trying to "antispam" a keyword which is already blacklisted
  36   * If that keyword appears in the URL... then the next page has a bad referer! :/
  37   *
  38   * {@internal This function gets tested in misc.funcs.simpletest.php.}}
  39   *
  40   * @param string Url to validate
  41   * @param string Context ("posting", "commenting")
  42   * @param boolean also do an antispam check on the url
  43   * @return mixed false (which means OK) or error message
  44   */
  45  function validate_url( $url, $context = 'posting', $antispam_check = true )
  46  {
  47      global $Debuglog, $debug;
  48  
  49      if( empty($url) )
  50      { // Empty URL, no problem
  51          return false;
  52      }
  53  
  54      $verbose = $debug || $context != 'commenting';
  55  
  56      $allowed_uri_schemes = get_allowed_uri_schemes( $context );
  57  
  58      // Validate URL structure
  59      if( $url[0] == '$' )
  60      {    // This is a 'special replace code' URL (used in footers)
  61           if( ! preg_match( '~\$([a-z_]+)\$~', $url ) )
  62          {
  63              return T_('Invalid URL $code$ format');
  64          }
  65      }
  66      elseif( preg_match( '~^\w+:~', $url ) )
  67      { // there's a scheme and therefor an absolute URL:
  68          if( substr($url, 0, 7) == 'mailto:' )
  69          { // mailto:link
  70              if( ! in_array( 'mailto', $allowed_uri_schemes ) )
  71              { // Scheme not allowed
  72                  $scheme = 'mailto:';
  73                  $Debuglog->add( 'URI scheme &laquo;'.$scheme.'&raquo; not allowed!', 'error' );
  74                  return $verbose
  75                      ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) )
  76                      : T_('URI scheme not allowed.');
  77              }
  78  
  79              preg_match( '~^(mailto):(.*?)(\?.*)?$~', $url, $match );
  80              if( ! $match )
  81              {
  82                  return $verbose
  83                      ? sprintf( T_('Invalid email link: %s.'), htmlspecialchars($url) )
  84                      : T_('Invalid email link.');
  85              }
  86        elseif( ! is_email($match[2]) )
  87              {
  88                  return $verbose
  89                      ? sprintf( T_('Supplied email address (%s) is invalid.'), htmlspecialchars($match[2]) )
  90                      : T_('Invalid email address.');
  91              }
  92          }
  93          elseif( substr($url, 0, 6) == 'clsid:' )
  94          { // clsid:link
  95              if( ! in_array( 'clsid', $allowed_uri_schemes ) )
  96              { // Scheme not allowed
  97                  $scheme = 'clsid:';
  98                  $Debuglog->add( 'URI scheme &laquo;'.$scheme.'&raquo; not allowed!', 'error' );
  99                  return $verbose
 100                      ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) )
 101                      : T_('URI scheme not allowed.');
 102              }
 103  
 104              if( ! preg_match( '~^(clsid):([a-fA-F0-9\-]+)$~', $url, $match) )
 105              {
 106                  return T_('Invalid class ID format');
 107              }
 108          }
 109          elseif( substr($url, 0, 11) == 'javascript:' )
 110          { // javascript:
 111              // Basically there could be anything here
 112              if( ! in_array( 'javascript', $allowed_uri_schemes ) )
 113              { // Scheme not allowed
 114                  $scheme = 'javascript:';
 115                  $Debuglog->add( 'URI scheme &laquo;'.$scheme.'&raquo; not allowed!', 'error' );
 116                  return $verbose
 117                      ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) )
 118                      : T_('URI scheme not allowed.');
 119              }
 120  
 121              preg_match( '~^(javascript):~', $url, $match );
 122          }
 123          else
 124          {
 125              // convert URL to IDN:
 126              $url = idna_encode($url);
 127  
 128              if( ! preg_match('~^           # start
 129                  ([a-z][a-z0-9+.\-]*)             # scheme
 130                  ://                              # authorize absolute URLs only ( // not present in clsid: -- problem? ; mailto: handled above)
 131                  (\w+(:\w+)?@)?                   # username or username and password (optional)
 132                  ( localhost |
 133                          [a-z0-9]([a-z0-9\-])*            # Don t allow anything too funky like entities
 134                          \.                               # require at least 1 dot
 135                          [a-z0-9]([a-z0-9.\-])+           # Don t allow anything too funky like entities
 136                  )
 137                  (:[0-9]+)?                       # optional port specification
 138                  .*                               # allow anything in the path (including spaces - used in FileManager - but no newlines).
 139                  $~ix', $url, $match) )
 140              { // Cannot validate URL structure
 141                  $Debuglog->add( 'URL &laquo;'.$url.'&raquo; does not match url pattern!', 'error' );
 142                  return $verbose
 143                      ? sprintf( T_('Invalid URL format (%s).'), htmlspecialchars($url) )
 144                      : T_('Invalid URL format.');
 145              }
 146  
 147              $scheme = strtolower($match[1]);
 148              if( ! in_array( $scheme, $allowed_uri_schemes ) )
 149              { // Scheme not allowed
 150                  $Debuglog->add( 'URI scheme &laquo;'.$scheme.'&raquo; not allowed!', 'error' );
 151                  return $verbose
 152                      ? sprintf( T_('URI scheme "%s" not allowed.'), htmlspecialchars($scheme) )
 153                      : T_('URI scheme not allowed.');
 154              }
 155          }
 156      }
 157      else
 158      { // URL is relative..
 159          if( $context == 'commenting' )
 160          {    // We do not allow relative URLs in comments
 161              return $verbose ? sprintf( T_('URL "%s" must be absolute.'), htmlspecialchars($url) ) : T_('URL must be absolute.');
 162          }
 163  
 164          $char = substr($url, 0, 1);
 165          if( $char != '/' && $char != '#' )
 166          { // must start with a slash or hash (for HTML anchors to the same page)
 167              return $verbose
 168                  ? sprintf( T_('URL "%s" must be a full path starting with "/" or an anchor starting with "#".'), htmlspecialchars($url) )
 169                  : T_('URL must be a full path starting with "/" or an anchor starting with "#".');
 170          }
 171      }
 172  
 173  
 174      if( $antispam_check )
 175      {    // Search for blocked keywords:
 176          if( $block = antispam_check($url) )
 177          {
 178              return $verbose
 179                  ? sprintf( T_('URL "%s" not allowed: blacklisted word "%s".'), htmlspecialchars($url), $block )
 180                  : T_('URL not allowed');
 181          }
 182      }
 183  
 184      return false; // OK
 185  }
 186  
 187  
 188  /**
 189   * Get allowed URI schemes for a given context.
 190   * @param string Context ("posting", "commenting")
 191   * @return array
 192   */
 193  function get_allowed_uri_schemes( $context = 'posting' )
 194  {
 195    /**
 196       * @var User
 197       */
 198      global $current_User;
 199  
 200      $schemes = array(
 201              'http',
 202              'https',
 203              'ftp',
 204              'gopher',
 205              'nntp',
 206              'news',
 207              'mailto',
 208              'irc',
 209              'aim',
 210              'icq'
 211          );
 212  
 213      if( $context == 'commenting' )
 214      {
 215          return $schemes;
 216      }
 217  
 218      if( !empty( $current_User ) )
 219      {    // Add additional permissions the current User may have:
 220  
 221          $Group = & $current_User->get_Group();
 222  
 223          if( $Group->perm_xhtml_javascript )
 224          {
 225              $schemes[] = 'javascript';
 226          }
 227  
 228          if( $Group->perm_xhtml_objects )
 229          {
 230              $schemes[] = 'clsid';
 231          }
 232  
 233      }
 234  
 235      return $schemes;
 236  }
 237  
 238  
 239  /**
 240   * Get the last HTTP status code received by the HTTP/HTTPS wrapper of PHP.
 241   *
 242   * @param array The $http_response_header array (by reference).
 243   * @return integer|boolean False if no HTTP status header could be found,
 244   *                         the HTTP status code otherwise.
 245   */
 246  function _http_wrapper_last_status( & $headers )
 247  {
 248      for( $i = count( $headers ) - 1; $i >= 0; --$i )
 249      {
 250          if( preg_match( '|^HTTP/\d+\.\d+ (\d+)|', $headers[$i], $matches ) )
 251          {
 252              return $matches[1];
 253          }
 254      }
 255  
 256      return false;
 257  }
 258  
 259  
 260  /**
 261   * Fetch remote page
 262   *
 263   * Attempt to retrieve a remote page using a HTTP GET request, first with
 264   * cURL, then fsockopen, then fopen.
 265   *
 266   * cURL gets skipped, if $max_size_kb is requested, since there appears to be no
 267   * method to control this.
 268   * {@internal (CURLOPT_READFUNCTION maybe? But it has not been called for me.. seems
 269   *            to affect sending, not fetching?!)}}
 270   *
 271   * @todo dh> Should we try remaining methods, if the previous one(s) failed?
 272   * @todo Tblue> Also allow HTTP POST.
 273   *
 274   * @param string URL
 275   * @param array Info (by reference)
 276   *        'error': holds error message, if any
 277   *        'status': HTTP status (e.g. 200 or 404)
 278   *        'used_method': Used method ("curl", "fopen", "fsockopen" or null if no method
 279   *                       is available)
 280   * @param integer Timeout (default: 15 seconds)
 281   * @param integer Maximum size in kB
 282   * @return string|false The remote page as a string; false in case of error
 283   */
 284  function fetch_remote_page( $url, & $info, $timeout = NULL, $max_size_kb = NULL )
 285  {
 286      $info = array(
 287          'error' => '',
 288          'status' => NULL,
 289          'mimetype' => NULL,
 290          'used_method' => NULL,
 291      );
 292  
 293      if( ! isset($timeout) )
 294          $timeout = 15;
 295  
 296      if( extension_loaded('curl') && ! $max_size_kb ) // dh> I could not find an option to support "maximum size" for curl (to abort during download => memory limit).
 297      {    // CURL:
 298          $info['used_method'] = 'curl';
 299  
 300          $ch = curl_init();
 301          curl_setopt( $ch, CURLOPT_URL, $url );
 302          curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true );
 303          curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout );
 304          curl_setopt( $ch, CURLOPT_TIMEOUT, $timeout );
 305          @curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, true ); // made silent due to possible errors with safe_mode/open_basedir(?)
 306          curl_setopt( $ch, CURLOPT_MAXREDIRS, 3 );
 307          $r = curl_exec( $ch );
 308  
 309          $info['mimetype'] = curl_getinfo( $ch, CURLINFO_CONTENT_TYPE );
 310          $info['status'] = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
 311          $info['error'] = curl_error( $ch );
 312          if( ( $errno = curl_errno( $ch ) ) )
 313          {
 314              $info['error'] .= ' (#'.$errno.')';
 315          }
 316          curl_close( $ch );
 317  
 318          return $r;
 319      }
 320  
 321      if( function_exists( 'fsockopen' ) ) // may have been disabled
 322      {    // FSOCKOPEN:
 323          $info['used_method'] = 'fsockopen';
 324  
 325          if ( ( $url_parsed = @parse_url( $url ) ) === false
 326               || ! isset( $url_parsed['host'] ) )
 327          {
 328              $info['error'] = 'Could not parse URL';
 329              return false;
 330          }
 331  
 332          $host = $url_parsed['host'];
 333          $port = empty( $url_parsed['port'] ) ? 80 : $url_parsed['port'];
 334          $path = empty( $url_parsed['path'] ) ? '/' : $url_parsed['path'];
 335          if( ! empty( $url_parsed['query'] ) )
 336          {
 337              $path .= '?'.$url_parsed['query'];
 338          }
 339  
 340          $out = 'GET '.$path.' HTTP/1.1'."\r\n";
 341          $out .= 'Host: '.$host;
 342          if( ! empty( $url_parsed['port'] ) )
 343          {    // we don't want to add :80 if not specified. remote end may not resolve it. (e-g b2evo multiblog does not)
 344              $out .= ':'.$port;
 345          }
 346          $out .= "\r\n".'Connection: Close'."\r\n\r\n";
 347  
 348          $fp = @fsockopen( $host, $port, $errno, $errstr, $timeout );
 349          if( ! $fp )
 350          {
 351              $info['error'] = $errstr.' (#'.$errno.')';
 352              return false;
 353          }
 354  
 355          // Send request:
 356          fwrite( $fp, $out );
 357  
 358          // Set timeout for data:
 359          if( function_exists( 'stream_set_timeout' ) )
 360          {
 361              stream_set_timeout( $fp, $timeout ); // PHP 4.3.0
 362          }
 363          else
 364          {
 365              socket_set_timeout( $fp, $timeout ); // PHP 4
 366          }
 367  
 368          // Read response:
 369          $r = '';
 370          // First line:
 371          $s = fgets( $fp );
 372          if( ! preg_match( '~^HTTP/\d+\.\d+ (\d+)~', $s, $match ) )
 373          {
 374              $info['error'] = 'Invalid response.';
 375              fclose( $fp );
 376              return false;
 377          }
 378  
 379          while( ! feof( $fp ) )
 380          {
 381              $r .= fgets( $fp );
 382              if( $max_size_kb && evo_bytes($r) >= $max_size_kb*1024 )
 383              {
 384                  $info['error'] = sprintf('Maximum size of %d kB reached.', $max_size_kb);
 385                  return false;
 386              }
 387          }
 388          fclose($fp);
 389  
 390          if ( ( $pos = strpos( $r, "\r\n\r\n" ) ) === false )
 391          {
 392              $info['error'] = 'Could not locate end of headers';
 393              return false;
 394          }
 395  
 396          // Remember headers to extract info at the end
 397          $headers = explode("\r\n", substr($r, 0, $pos));
 398  
 399          $info['status'] = $match[1];
 400          $r = substr( $r, $pos + 4 );
 401      }
 402      elseif( ini_get( 'allow_url_fopen' ) )
 403      {    // URL FOPEN:
 404          $info['used_method'] = 'fopen';
 405  
 406          $fp = @fopen( $url, 'r' );
 407          if( ! $fp )
 408          {
 409              if( isset( $http_response_header )
 410                  && ( $code = _http_wrapper_last_status( $http_response_header ) ) !== false )
 411              {    // fopen() returned false because it got a bad HTTP code:
 412                  $info['error'] = 'Invalid response';
 413                  $info['status'] = $code;
 414                  return '';
 415              }
 416  
 417              $info['error'] = 'fopen() failed';
 418              return false;
 419          }
 420          // Check just to be sure:
 421          else if ( ! isset( $http_response_header )
 422                    || ( $code = _http_wrapper_last_status( $http_response_header ) ) === false )
 423          {
 424              $info['error'] = 'Invalid response';
 425              return false;
 426          }
 427          else
 428          {
 429              // Used to get info at the end
 430              $headers = $http_response_header;
 431  
 432              // Retrieve contents
 433              $r = '';
 434              while( ! feof( $fp ) )
 435              {
 436                  $r .= fgets( $fp );
 437                  if( $max_size_kb && evo_bytes($r) >= $max_size_kb*1024 )
 438                  {
 439                      $info['error'] = sprintf('Maximum size of %d kB reached.', $max_size_kb);
 440                      return false;
 441                  }
 442              }
 443  
 444              $info['status'] = $code;
 445          }
 446          fclose( $fp );
 447      }
 448  
 449      // Extract mimetype info from the headers (for fsockopen/fopen)
 450      if( isset($r) )
 451      {
 452          foreach($headers as $header)
 453          {
 454              $header = strtolower($header);
 455              if( substr($header, 0, 13) == 'content-type:' )
 456              {
 457                  $info['mimetype'] = trim(substr($header, 13));
 458                  break; // only looking for mimetype
 459              }
 460          }
 461  
 462          return $r;
 463      }
 464  
 465      // All failed:
 466      $info['error'] = 'No method available to access URL!';
 467      return false;
 468  }
 469  
 470  
 471  /**
 472   * Get $url with the same protocol (http/https) as $other_url.
 473   *
 474   * @param string URL
 475   * @param string other URL (defaults to {@link $ReqHost})
 476   * @return string
 477   */
 478  function url_same_protocol( $url, $other_url = NULL )
 479  {
 480      if( is_null($other_url) )
 481      {
 482          global $ReqHost;
 483  
 484          $other_url = $ReqHost;
 485      }
 486  
 487      // change protocol of $url to same of admin ('https' <=> 'http')
 488      if( substr( $url, 0, 7 ) == 'http://' )
 489      {
 490          if( substr( $other_url, 0, 8 ) == 'https://' )
 491          {
 492              $url = 'https://'.substr( $url, 7 );
 493          }
 494      }
 495      elseif( substr( $url, 0, 8 ) == 'https://' )
 496      {
 497          if( substr( $other_url, 0, 7 ) == 'http://' )
 498          {
 499              $url = 'http://'.substr( $url, 8 );
 500          }
 501      }
 502  
 503      return $url;
 504  }
 505  
 506  
 507  /**
 508   * Add param(s) at the end of an URL, using either "?" or "&amp;" depending on existing url
 509   *
 510   * @param string existing url
 511   * @param string|array Params to add (string as-is) or array, which gets urlencoded.
 512   * @param string delimiter to use for more params
 513   */
 514  function url_add_param( $url, $param, $glue = '&amp;' )
 515  {
 516      if( empty($param) )
 517      {
 518          return $url;
 519      }
 520  
 521      if( ($anchor_pos = strpos($url, '#')) !== false )
 522      { // There's an "#anchor" in the URL
 523          $anchor = substr($url, $anchor_pos);
 524          $url = substr($url, 0, $anchor_pos);
 525      }
 526      else
 527      { // URL without "#anchor"
 528          $anchor = '';
 529      }
 530  
 531      // Handle array use case
 532      if( is_array($param) )
 533      { // list of key => value pairs
 534          $param_list = array();
 535          foreach( $param as $k => $v )
 536          {
 537              $param_list[] = get_param_urlencoded($k, $v, $glue);
 538          }
 539          $param = implode($glue, $param_list);
 540      }
 541  
 542      if( strpos($url, '?') !== false )
 543      { // There are already params in the URL
 544          $r = $url;
 545          if( substr($url, -1) != '?' )
 546          { // the "?" is not the last char
 547              $r .= $glue;
 548          }
 549          return $r.$param.$anchor;
 550      }
 551  
 552      // These are the first params
 553      return $url.'?'.$param.$anchor;
 554  }
 555  
 556  
 557  /**
 558   * Add a tail (starting with "/") at the end of an URL before any params (starting with "?")
 559   *
 560   * @param string existing url
 561   * @param string tail to add
 562   */
 563  function url_add_tail( $url, $tail )
 564  {
 565      $parts = explode( '?', $url );
 566      if( substr($parts[0], -1) == '/' )
 567      {
 568          $parts[0] = substr($parts[0], 0, -1);
 569      }
 570      if( isset($parts[1]) )
 571      {
 572          return $parts[0].$tail.'?'.$parts[1];
 573      }
 574  
 575      return $parts[0].$tail;
 576  }
 577  
 578  
 579  /**
 580   * Create a crumb param to be passed in action urls...
 581   *
 582   * @access public
 583   * @param string crumb_name
 584   */
 585  function url_crumb( $crumb_name )
 586  {
 587      return 'crumb_'.$crumb_name.'='.get_crumb($crumb_name);
 588  }
 589  
 590  
 591  /**
 592   * Get crumb via {@link $Session}.
 593   * @access public
 594   * @param string crumb_name
 595   * @return string
 596   */
 597  function get_crumb($crumb_name)
 598  {
 599      global $Session;
 600      return isset( $Session ) ? $Session->create_crumb( $crumb_name ) : '';
 601  }
 602  
 603  
 604  /**
 605   * Try to make $url relative to $target_url, if scheme, host, user and pass matches.
 606   *
 607   * This is useful for redirect_to params, to keep them short and avoid mod_security
 608   * rejecting the request as "Not Acceptable" (whole URL as param).
 609   *
 610   * @param string URL to handle
 611   * @param string URL where we want to make $url relative to
 612   * @return string
 613   */
 614  function url_rel_to_same_host( $url, $target_url )
 615  {
 616      // Prepend fake scheme to URLs starting with "//" (relative to current protocol), since
 617      // parse_url fails to handle them correctly otherwise (recognizes them as path-only)
 618      $mangled_url = substr($url, 0, 2) == '//' ? 'noprotocolscheme:'.$url : $url;
 619  
 620      if( substr($target_url, 0, 2) == '//' )
 621          $target_url = 'noprotocolscheme:'.$target_url;
 622  
 623  
 624      $parsed_url = @parse_url( $mangled_url );
 625      if( ! $parsed_url )
 626      { // invalid url
 627          return $url;
 628      }
 629      if( empty($parsed_url['scheme']) || empty($parsed_url['host']) )
 630      { // no protocol or host information
 631          return $url;
 632      }
 633  
 634      $target_url = @parse_url( $target_url );
 635      if( ! $target_url )
 636      { // invalid url
 637          return $url;
 638      }
 639      if( ! empty($target_url['scheme']) && $target_url['scheme'] != $parsed_url['scheme']
 640          && $parsed_url['scheme'] != 'noprotocolscheme' )
 641      { // scheme/protocol is different
 642          return $url;
 643      }
 644      if( ! empty($target_url['host']) )
 645      {
 646          if( empty($target_url['scheme']) || $target_url['host'] != $parsed_url['host'] )
 647          { // target has no scheme (but a host) or hosts differ
 648              return $url;
 649          }
 650  
 651          if( @$target_url['port'] != @$parsed_url['port'] )
 652              return $url;
 653          if( @$target_url['user'] != @$parsed_url['user'] )
 654              return $url;
 655          if( @$target_url['pass'] != @$parsed_url['pass'] )
 656              return $url;
 657      }
 658  
 659      // We can make the URL relative:
 660      $r = '';
 661      if( isset($parsed_url['path']) && strlen($parsed_url['path']) )
 662          $r .= $parsed_url['path'];
 663  
 664      if( isset($parsed_url['query']) && strlen($parsed_url['query']) )
 665          $r .= '?'.$parsed_url['query'];
 666  
 667      if( isset($parsed_url['fragment']) && strlen($parsed_url['fragment']) )
 668          $r .= '#'.$parsed_url['fragment'];
 669  
 670      return $r;
 671  }
 672  
 673  
 674  /**
 675   * Make an $url absolute according to $host, if it is not absolute yet.
 676   *
 677   * @param string URL
 678   * @param string Base (including protocol, e.g. 'http://example.com'); autodedected
 679   * @return string
 680   */
 681  function url_absolute( $url, $base = NULL )
 682  {
 683      load_funcs('_ext/_url_rel2abs.php');
 684  
 685      if( is_absolute_url($url) )
 686      {    // URL is already absolute
 687          return $url;
 688      }
 689  
 690      if( empty($base) )
 691      {    // Detect current page base
 692          global $Blog, $ReqHost, $base_tag_set, $baseurl;
 693  
 694          if( $base_tag_set )
 695          {    // <base> tag is set
 696              $base = $base_tag_set;
 697          }
 698          else
 699          {
 700              if( ! empty( $Blog ) )
 701              {    // Get original blog skin, not passed with 'tempskin' param
 702                  $SkinCache = & get_SkinCache();
 703                  if( ($Skin = $SkinCache->get_by_ID( $Blog->get_skin_ID(), false )) !== false )
 704                  {
 705                      $base = $Blog->get_local_skins_url().$Skin->folder.'/';
 706                  }
 707                  else
 708                  { // Skin not set:
 709                      $base = $Blog->gen_baseurl();
 710                  }
 711              }
 712              else
 713              {    // We are displaying a general page that is not specific to a blog:
 714                  $base = $ReqHost;
 715              }
 716          }
 717      }
 718  
 719      if( ($absurl = url_to_absolute($url, $base)) === false )
 720      {    // Return relative URL in case of error
 721          $absurl = $url;
 722      }
 723      return $absurl;
 724  }
 725  
 726  
 727  /**
 728   * Make links in $s absolute.
 729   *
 730   * It searches for "src" and "href" HTML tag attributes and makes the absolute.
 731   *
 732   * @uses url_absolute()
 733   * @param string content
 734   * @param string Hostname including scheme, e.g. http://example.com; defaults to $ReqHost
 735   * @return string
 736   */
 737  function make_rel_links_abs( $s, $host = NULL )
 738  {
 739      $s = preg_replace_callback( '~(<[^>]+?)\b((?:src|href)\s*=\s*)(["\'])?([^\\3]+?)(\\3)~i', create_function( '$m', '
 740          return $m[1].$m[2].$m[3].url_absolute($m[4], "'.$host.'").$m[5];' ), $s );
 741      return $s;
 742  }
 743  
 744  
 745  /**
 746   * Display an URL, constrained to a max length
 747   *
 748   * @param string
 749   * @param integer
 750   */
 751  function disp_url( $url, $max_length = NULL )
 752  {
 753      if( !empty($max_length) && evo_strlen($url) > $max_length )
 754      {
 755          $disp_url = htmlspecialchars(substr( $url, 0, $max_length-1 )).'&hellip;';
 756      }
 757      else
 758      {
 759          $disp_url = htmlspecialchars($url);
 760      }
 761      echo '<a href="'.$url.'">'.$disp_url.'</a>';
 762  }
 763  
 764  
 765  /**
 766   * Is a given URL absolute?
 767   * Note: "//foo/bar" is absolute - leaving the protocol out.
 768   *
 769   * @param string URL
 770   * @return boolean
 771   */
 772  function is_absolute_url( $url )
 773  {
 774      load_funcs('_ext/_url_rel2abs.php');
 775  
 776      if( ($parsed_url = split_url($url)) !== false )
 777      {
 778          if( !empty($parsed_url['scheme']) || !empty($parsed_url['host']) )
 779          {
 780              return true;
 781          }
 782      }
 783      return false;
 784  }
 785  
 786  
 787  /**
 788   * Compare two given URLs, if they are the same.
 789   * This converts all urlencoded chars (e.g. "%AA") to lowercase.
 790   * It appears that some webservers use lowercase for the chars (Apache),
 791   * while others use uppercase (lighttpd).
 792   * @return boolean
 793   */
 794  function is_same_url( $a, $b )
 795  {
 796      $a = preg_replace_callback('~%[0-9A-F]{2}~', create_function('$m', 'return strtolower($m[0]);'), $a);
 797      $b = preg_replace_callback('~%[0-9A-F]{2}~', create_function('$m', 'return strtolower($m[0]);'), $b);
 798      return $a == $b;
 799  }
 800  
 801  
 802  /**
 803   * IDNA-Encode URL to Punycode.
 804   * @param string URL
 805   * @return string Encoded URL (ASCII)
 806   */
 807  function idna_encode( $url )
 808  {
 809      global $evo_charset;
 810  
 811      $url_utf8 = convert_charset( $url, 'utf-8', $evo_charset );
 812  
 813      if( version_compare(PHP_VERSION, '5', '>=') )
 814      {
 815          load_class('_ext/idna/_idna_convert.class.php', 'idna_convert' );
 816          $IDNA = new idna_convert();
 817      }
 818      else
 819      {
 820          load_class('_ext/idna/_idna_convert.class.php4', 'Net_IDNA_php4' );
 821          $IDNA = new Net_IDNA_php4();
 822      }
 823  
 824      //echo '['.$url_utf8.'] ';
 825      $url = $IDNA->encode( $url_utf8 );
 826      /* if( $idna_error = $IDNA->get_last_error() )
 827      {
 828          echo $idna_error;
 829      } */
 830      // echo '['.$url.']<br>';
 831  
 832      return $url;
 833  }
 834  
 835  
 836  /**
 837   * Decode IDNA puny-code ("xn--..") to UTF-8 name.
 838   *
 839   * @param string
 840   * @return string The decoded puny-code ("xn--..") (UTF8!)
 841   */
 842  function idna_decode( $url )
 843  {
 844      if( version_compare(PHP_VERSION, '5', '>=') )
 845      {
 846          load_class('_ext/idna/_idna_convert.class.php', 'idna_convert' );
 847          $IDNA = new idna_convert();
 848      }
 849      else
 850      {
 851          load_class('_ext/idna/_idna_convert.class.php4', 'Net_IDNA_php4' );
 852          $IDNA = new Net_IDNA_php4();
 853      }
 854      return $IDNA->decode($url);
 855  }
 856  
 857  
 858  /**
 859   * Get disp urls for Frontoffice part OR ctrl urls for Backoffice
 860   *
 861   * @param string specific sub entry url
 862   * @param string additional params
 863   */
 864  function get_dispctrl_url( $dispctrl, $params = '' )
 865  {
 866      global $Blog;
 867  
 868      if( $params != '' )
 869      {
 870          $params = '&amp;'.$params;
 871      }
 872  
 873      if( is_admin_page() || empty( $Blog ) )
 874      {    // Backoffice part
 875          global $admin_url;
 876          return url_add_param( $admin_url, 'ctrl='.$dispctrl.$params );
 877      }
 878  
 879      return url_add_param( $Blog->gen_blogurl(), 'disp='.$dispctrl.$params );
 880  }
 881  
 882  
 883  /**
 884   * Get link tag
 885   *
 886   * @param string Url
 887   * @param string Link Text
 888   * @param string Link class
 889   * @param integer Max length of url when url is used as link text
 890   * @return string HTML link tag
 891   */
 892  function get_link_tag( $url, $text = '', $class='', $max_url_length = 50 )
 893  {
 894      if( empty( $text ) )
 895      { // Link text is empty, Use url
 896          $text = $url;
 897          if( strlen( $text ) > $max_url_length )
 898          { // Crop url text
 899              $text = substr( $text, 0, $max_url_length ).'&hellip;';
 900          }
 901      }
 902  
 903      return '<a class="'.$class.'" href="'.str_replace('&amp;', '&', $url ).'">'.$text.'</a>';
 904  }
 905  
 906  ?>

title

Description

title

Description

title

Description

title

title

Body