[ Index ]

PHP Cross Reference of phpBB-3.1.10-deutsch

title

Body

[close]

/includes/ -> functions_download.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  /**
  15  * @ignore
  16  */
  17  if (!defined('IN_PHPBB'))
  18  {
  19      exit;
  20  }
  21  
  22  /**
  23  * A simplified function to deliver avatars
  24  * The argument needs to be checked before calling this function.
  25  */
  26  function send_avatar_to_browser($file, $browser)
  27  {
  28      global $config, $phpbb_root_path;
  29  
  30      $prefix = $config['avatar_salt'] . '_';
  31      $image_dir = $config['avatar_path'];
  32  
  33      // Adjust image_dir path (no trailing slash)
  34      if (substr($image_dir, -1, 1) == '/' || substr($image_dir, -1, 1) == '\\')
  35      {
  36          $image_dir = substr($image_dir, 0, -1) . '/';
  37      }
  38      $image_dir = str_replace(array('../', '..\\', './', '.\\'), '', $image_dir);
  39  
  40      if ($image_dir && ($image_dir[0] == '/' || $image_dir[0] == '\\'))
  41      {
  42          $image_dir = '';
  43      }
  44      $file_path = $phpbb_root_path . $image_dir . '/' . $prefix . $file;
  45  
  46      if ((@file_exists($file_path) && @is_readable($file_path)) && !headers_sent())
  47      {
  48          header('Cache-Control: public');
  49  
  50          $image_data = @getimagesize($file_path);
  51          header('Content-Type: ' . image_type_to_mime_type($image_data[2]));
  52  
  53          if ((strpos(strtolower($browser), 'msie') !== false) && !phpbb_is_greater_ie_version($browser, 7))
  54          {
  55              header('Content-Disposition: attachment; ' . header_filename($file));
  56  
  57              if (strpos(strtolower($browser), 'msie 6.0') !== false)
  58              {
  59                  header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
  60              }
  61              else
  62              {
  63                  header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
  64              }
  65          }
  66          else
  67          {
  68              header('Content-Disposition: inline; ' . header_filename($file));
  69              header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
  70          }
  71  
  72          $size = @filesize($file_path);
  73          if ($size)
  74          {
  75              header("Content-Length: $size");
  76          }
  77  
  78          if (@readfile($file_path) == false)
  79          {
  80              $fp = @fopen($file_path, 'rb');
  81  
  82              if ($fp !== false)
  83              {
  84                  while (!feof($fp))
  85                  {
  86                      echo fread($fp, 8192);
  87                  }
  88                  fclose($fp);
  89              }
  90          }
  91  
  92          flush();
  93      }
  94      else
  95      {
  96          header('HTTP/1.0 404 Not Found');
  97      }
  98  }
  99  
 100  /**
 101  * Wraps an url into a simple html page. Used to display attachments in IE.
 102  * this is a workaround for now; might be moved to template system later
 103  * direct any complaints to 1 Microsoft Way, Redmond
 104  */
 105  function wrap_img_in_html($src, $title)
 106  {
 107      echo '<!DOCTYPE html>';
 108      echo '<html>';
 109      echo '<head>';
 110      echo '<meta charset="utf-8">';
 111      echo '<meta http-equiv="X-UA-Compatible" content="IE=edge">';
 112      echo '<title>' . $title . '</title>';
 113      echo '</head>';
 114      echo '<body>';
 115      echo '<div>';
 116      echo '<img src="' . $src . '" alt="' . $title . '" />';
 117      echo '</div>';
 118      echo '</body>';
 119      echo '</html>';
 120  }
 121  
 122  /**
 123  * Send file to browser
 124  */
 125  function send_file_to_browser($attachment, $upload_dir, $category)
 126  {
 127      global $user, $db, $config, $phpbb_root_path;
 128  
 129      $filename = $phpbb_root_path . $upload_dir . '/' . $attachment['physical_filename'];
 130  
 131      if (!@file_exists($filename))
 132      {
 133          send_status_line(404, 'Not Found');
 134          trigger_error('ERROR_NO_ATTACHMENT');
 135      }
 136  
 137      // Correct the mime type - we force application/octetstream for all files, except images
 138      // Please do not change this, it is a security precaution
 139      if ($category != ATTACHMENT_CATEGORY_IMAGE || strpos($attachment['mimetype'], 'image') !== 0)
 140      {
 141          $attachment['mimetype'] = (strpos(strtolower($user->browser), 'msie') !== false || strpos(strtolower($user->browser), 'opera') !== false) ? 'application/octetstream' : 'application/octet-stream';
 142      }
 143  
 144      if (@ob_get_length())
 145      {
 146          @ob_end_clean();
 147      }
 148  
 149      // Now send the File Contents to the Browser
 150      $size = @filesize($filename);
 151  
 152      // To correctly display further errors we need to make sure we are using the correct headers for both (unsetting content-length may not work)
 153  
 154      // Check if headers already sent or not able to get the file contents.
 155      if (headers_sent() || !@file_exists($filename) || !@is_readable($filename))
 156      {
 157          // PHP track_errors setting On?
 158          if (!empty($php_errormsg))
 159          {
 160              send_status_line(500, 'Internal Server Error');
 161              trigger_error($user->lang['UNABLE_TO_DELIVER_FILE'] . '<br />' . sprintf($user->lang['TRACKED_PHP_ERROR'], $php_errormsg));
 162          }
 163  
 164          send_status_line(500, 'Internal Server Error');
 165          trigger_error('UNABLE_TO_DELIVER_FILE');
 166      }
 167  
 168      // Make sure the database record for the filesize is correct
 169      if ($size > 0 && $size != $attachment['filesize'] && strpos($attachment['physical_filename'], 'thumb_') === false)
 170      {
 171          // Update database record
 172          $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
 173              SET filesize = ' . (int) $size . '
 174              WHERE attach_id = ' . (int) $attachment['attach_id'];
 175          $db->sql_query($sql);
 176      }
 177  
 178      // Now the tricky part... let's dance
 179      header('Cache-Control: public');
 180  
 181      // Send out the Headers. Do not set Content-Disposition to inline please, it is a security measure for users using the Internet Explorer.
 182      header('Content-Type: ' . $attachment['mimetype']);
 183  
 184      if (phpbb_is_greater_ie_version($user->browser, 7))
 185      {
 186          header('X-Content-Type-Options: nosniff');
 187      }
 188  
 189      if ($category == ATTACHMENT_CATEGORY_FLASH && request_var('view', 0) === 1)
 190      {
 191          // We use content-disposition: inline for flash files and view=1 to let it correctly play with flash player 10 - any other disposition will fail to play inline
 192          header('Content-Disposition: inline');
 193      }
 194      else
 195      {
 196          if (empty($user->browser) || ((strpos(strtolower($user->browser), 'msie') !== false) && !phpbb_is_greater_ie_version($user->browser, 7)))
 197          {
 198              header('Content-Disposition: attachment; ' . header_filename(htmlspecialchars_decode($attachment['real_filename'])));
 199              if (empty($user->browser) || (strpos(strtolower($user->browser), 'msie 6.0') !== false))
 200              {
 201                  header('Expires: ' . gmdate('D, d M Y H:i:s', time()) . ' GMT');
 202              }
 203          }
 204          else
 205          {
 206              header('Content-Disposition: ' . ((strpos($attachment['mimetype'], 'image') === 0) ? 'inline' : 'attachment') . '; ' . header_filename(htmlspecialchars_decode($attachment['real_filename'])));
 207              if (phpbb_is_greater_ie_version($user->browser, 7) && (strpos($attachment['mimetype'], 'image') !== 0))
 208              {
 209                  header('X-Download-Options: noopen');
 210              }
 211          }
 212      }
 213  
 214      // Close the db connection before sending the file etc.
 215      file_gc(false);
 216  
 217      if (!set_modified_headers($attachment['filetime'], $user->browser))
 218      {
 219          // We make sure those have to be enabled manually by defining a constant
 220          // because of the potential disclosure of full attachment path
 221          // in case support for features is absent in the webserver software.
 222          if (defined('PHPBB_ENABLE_X_ACCEL_REDIRECT') && PHPBB_ENABLE_X_ACCEL_REDIRECT)
 223          {
 224              // X-Accel-Redirect - http://wiki.nginx.org/XSendfile
 225              header('X-Accel-Redirect: ' . $user->page['root_script_path'] . $upload_dir . '/' . $attachment['physical_filename']);
 226              exit;
 227          }
 228          else if (defined('PHPBB_ENABLE_X_SENDFILE') && PHPBB_ENABLE_X_SENDFILE && !phpbb_http_byte_range($size))
 229          {
 230              // X-Sendfile - http://blog.lighttpd.net/articles/2006/07/02/x-sendfile
 231              // Lighttpd's X-Sendfile does not support range requests as of 1.4.26
 232              // and always requires an absolute path.
 233              header('X-Sendfile: ' . dirname(__FILE__) . "/../$upload_dir/{$attachment['physical_filename']}");
 234              exit;
 235          }
 236  
 237          if ($size)
 238          {
 239              header("Content-Length: $size");
 240          }
 241  
 242          // Try to deliver in chunks
 243          @set_time_limit(0);
 244  
 245          $fp = @fopen($filename, 'rb');
 246  
 247          if ($fp !== false)
 248          {
 249              // Deliver file partially if requested
 250              if ($range = phpbb_http_byte_range($size))
 251              {
 252                  fseek($fp, $range['byte_pos_start']);
 253  
 254                  send_status_line(206, 'Partial Content');
 255                  header('Content-Range: bytes ' . $range['byte_pos_start'] . '-' . $range['byte_pos_end'] . '/' . $range['bytes_total']);
 256                  header('Content-Length: ' . $range['bytes_requested']);
 257              }
 258  
 259              while (!feof($fp))
 260              {
 261                  echo fread($fp, 8192);
 262              }
 263              fclose($fp);
 264          }
 265          else
 266          {
 267              @readfile($filename);
 268          }
 269  
 270          flush();
 271      }
 272  
 273      exit;
 274  }
 275  
 276  /**
 277  * Get a browser friendly UTF-8 encoded filename
 278  */
 279  function header_filename($file)
 280  {
 281      global $request;
 282  
 283      $user_agent = $request->header('User-Agent');
 284  
 285      // There be dragons here.
 286      // Not many follows the RFC...
 287      if (strpos($user_agent, 'MSIE') !== false || strpos($user_agent, 'Konqueror') !== false)
 288      {
 289          return "filename=" . rawurlencode($file);
 290      }
 291  
 292      // follow the RFC for extended filename for the rest
 293      return "filename*=UTF-8''" . rawurlencode($file);
 294  }
 295  
 296  /**
 297  * Check if downloading item is allowed
 298  */
 299  function download_allowed()
 300  {
 301      global $config, $user, $db, $request;
 302  
 303      if (!$config['secure_downloads'])
 304      {
 305          return true;
 306      }
 307  
 308      $url = htmlspecialchars_decode($request->header('Referer'));
 309  
 310      if (!$url)
 311      {
 312          return ($config['secure_allow_empty_referer']) ? true : false;
 313      }
 314  
 315      // Split URL into domain and script part
 316      $url = @parse_url($url);
 317  
 318      if ($url === false)
 319      {
 320          return ($config['secure_allow_empty_referer']) ? true : false;
 321      }
 322  
 323      $hostname = $url['host'];
 324      unset($url);
 325  
 326      $allowed = ($config['secure_allow_deny']) ? false : true;
 327      $iplist = array();
 328  
 329      if (($ip_ary = @gethostbynamel($hostname)) !== false)
 330      {
 331          foreach ($ip_ary as $ip)
 332          {
 333              if ($ip)
 334              {
 335                  $iplist[] = $ip;
 336              }
 337          }
 338      }
 339  
 340      // Check for own server...
 341      $server_name = $user->host;
 342  
 343      // Forcing server vars is the only way to specify/override the protocol
 344      if ($config['force_server_vars'] || !$server_name)
 345      {
 346          $server_name = $config['server_name'];
 347      }
 348  
 349      if (preg_match('#^.*?' . preg_quote($server_name, '#') . '.*?$#i', $hostname))
 350      {
 351          $allowed = true;
 352      }
 353  
 354      // Get IP's and Hostnames
 355      if (!$allowed)
 356      {
 357          $sql = 'SELECT site_ip, site_hostname, ip_exclude
 358              FROM ' . SITELIST_TABLE;
 359          $result = $db->sql_query($sql);
 360  
 361          while ($row = $db->sql_fetchrow($result))
 362          {
 363              $site_ip = trim($row['site_ip']);
 364              $site_hostname = trim($row['site_hostname']);
 365  
 366              if ($site_ip)
 367              {
 368                  foreach ($iplist as $ip)
 369                  {
 370                      if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_ip, '#')) . '$#i', $ip))
 371                      {
 372                          if ($row['ip_exclude'])
 373                          {
 374                              $allowed = ($config['secure_allow_deny']) ? false : true;
 375                              break 2;
 376                          }
 377                          else
 378                          {
 379                              $allowed = ($config['secure_allow_deny']) ? true : false;
 380                          }
 381                      }
 382                  }
 383              }
 384  
 385              if ($site_hostname)
 386              {
 387                  if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($site_hostname, '#')) . '$#i', $hostname))
 388                  {
 389                      if ($row['ip_exclude'])
 390                      {
 391                          $allowed = ($config['secure_allow_deny']) ? false : true;
 392                          break;
 393                      }
 394                      else
 395                      {
 396                          $allowed = ($config['secure_allow_deny']) ? true : false;
 397                      }
 398                  }
 399              }
 400          }
 401          $db->sql_freeresult($result);
 402      }
 403  
 404      return $allowed;
 405  }
 406  
 407  /**
 408  * Check if the browser has the file already and set the appropriate headers-
 409  * @returns false if a resend is in order.
 410  */
 411  function set_modified_headers($stamp, $browser)
 412  {
 413      global $request;
 414  
 415      // let's see if we have to send the file at all
 416      $last_load     =  $request->header('If-Modified-Since') ? strtotime(trim($request->header('If-Modified-Since'))) : false;
 417  
 418      if (strpos(strtolower($browser), 'msie 6.0') === false && !phpbb_is_greater_ie_version($browser, 7))
 419      {
 420          if ($last_load !== false && $last_load >= $stamp)
 421          {
 422              send_status_line(304, 'Not Modified');
 423              // seems that we need those too ... browsers
 424              header('Cache-Control: public');
 425              header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
 426              return true;
 427          }
 428          else
 429          {
 430              header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $stamp) . ' GMT');
 431          }
 432      }
 433      return false;
 434  }
 435  
 436  /**
 437  * Garbage Collection
 438  *
 439  * @param bool $exit        Whether to die or not.
 440  *
 441  * @return null
 442  */
 443  function file_gc($exit = true)
 444  {
 445      global $cache, $db;
 446  
 447      if (!empty($cache))
 448      {
 449          $cache->unload();
 450      }
 451  
 452      $db->sql_close();
 453  
 454      if ($exit)
 455      {
 456          exit;
 457      }
 458  }
 459  
 460  /**
 461  * HTTP range support (RFC 2616 Section 14.35)
 462  *
 463  * Allows browsers to request partial file content
 464  * in case a download has been interrupted.
 465  *
 466  * @param int $filesize        the size of the file in bytes we are about to deliver
 467  *
 468  * @return mixed        false if the whole file has to be delivered
 469  *                    associative array on success
 470  */
 471  function phpbb_http_byte_range($filesize)
 472  {
 473      // Only call find_range_request() once.
 474      static $request_array;
 475  
 476      if (!$filesize)
 477      {
 478          return false;
 479      }
 480  
 481      if (!isset($request_array))
 482      {
 483          $request_array = phpbb_find_range_request();
 484      }
 485  
 486      return (empty($request_array)) ? false : phpbb_parse_range_request($request_array, $filesize);
 487  }
 488  
 489  /**
 490  * Searches for HTTP range request in request headers.
 491  *
 492  * @return mixed        false if no request found
 493  *                    array of strings containing the requested ranges otherwise
 494  *                    e.g. array(0 => '0-0', 1 => '123-125')
 495  */
 496  function phpbb_find_range_request()
 497  {
 498      global $request;
 499  
 500      $value = $request->header('Range');
 501  
 502      // Make sure range request starts with "bytes="
 503      if (strpos($value, 'bytes=') === 0)
 504      {
 505          // Strip leading 'bytes='
 506          // Multiple ranges can be separated by a comma
 507          return explode(',', substr($value, 6));
 508      }
 509  
 510      return false;
 511  }
 512  
 513  /**
 514  * Analyses a range request array.
 515  *
 516  * A range request can contain multiple ranges,
 517  * we however only handle the first request and
 518  * only support requests from a given byte to the end of the file.
 519  *
 520  * @param array    $request_array    array of strings containing the requested ranges
 521  * @param int    $filesize        the full size of the file in bytes that has been requested
 522  *
 523  * @return mixed        false if the whole file has to be delivered
 524  *                    associative array on success
 525  *                        byte_pos_start        the first byte position, can be passed to fseek()
 526  *                        byte_pos_end        the last byte position
 527  *                        bytes_requested        the number of bytes requested
 528  *                        bytes_total            the full size of the file
 529  */
 530  function phpbb_parse_range_request($request_array, $filesize)
 531  {
 532      // Go through all ranges
 533      foreach ($request_array as $range_string)
 534      {
 535          $range = explode('-', trim($range_string));
 536  
 537          // "-" is invalid, "0-0" however is valid and means the very first byte.
 538          if (sizeof($range) != 2 || $range[0] === '' && $range[1] === '')
 539          {
 540              continue;
 541          }
 542  
 543          if ($range[0] === '')
 544          {
 545              // Return last $range[1] bytes.
 546  
 547              if (!$range[1])
 548              {
 549                  continue;
 550              }
 551  
 552              if ($range[1] >= $filesize)
 553              {
 554                  return false;
 555              }
 556  
 557              $first_byte_pos    = $filesize - (int) $range[1];
 558              $last_byte_pos    = $filesize - 1;
 559          }
 560          else
 561          {
 562              // Return bytes from $range[0] to $range[1]
 563  
 564              $first_byte_pos    = (int) $range[0];
 565              $last_byte_pos    = (int) $range[1];
 566  
 567              if ($last_byte_pos && $last_byte_pos < $first_byte_pos)
 568              {
 569                  // The requested range contains 0 bytes.
 570                  continue;
 571              }
 572  
 573              if ($first_byte_pos >= $filesize)
 574              {
 575                  // Requested range not satisfiable
 576                  return false;
 577              }
 578  
 579              // Adjust last-byte-pos if it is absent or greater than the content.
 580              if ($range[1] === '' || $last_byte_pos >= $filesize)
 581              {
 582                  $last_byte_pos = $filesize - 1;
 583              }
 584          }
 585  
 586          // We currently do not support range requests that end before the end of the file
 587          if ($last_byte_pos != $filesize - 1)
 588          {
 589              continue;
 590          }
 591  
 592          return array(
 593              'byte_pos_start'    => $first_byte_pos,
 594              'byte_pos_end'        => $last_byte_pos,
 595              'bytes_requested'    => $last_byte_pos - $first_byte_pos + 1,
 596              'bytes_total'        => $filesize,
 597          );
 598      }
 599  }
 600  
 601  /**
 602  * Increments the download count of all provided attachments
 603  *
 604  * @param \phpbb\db\driver\driver_interface $db The database object
 605  * @param array|int $ids The attach_id of each attachment
 606  *
 607  * @return null
 608  */
 609  function phpbb_increment_downloads($db, $ids)
 610  {
 611      if (!is_array($ids))
 612      {
 613          $ids = array($ids);
 614      }
 615  
 616      $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
 617          SET download_count = download_count + 1
 618          WHERE ' . $db->sql_in_set('attach_id', $ids);
 619      $db->sql_query($sql);
 620  }
 621  
 622  /**
 623  * Handles authentication when downloading attachments from a post or topic
 624  *
 625  * @param \phpbb\db\driver\driver_interface $db The database object
 626  * @param \phpbb\auth\auth $auth The authentication object
 627  * @param int $topic_id The id of the topic that we are downloading from
 628  *
 629  * @return null
 630  */
 631  function phpbb_download_handle_forum_auth($db, $auth, $topic_id)
 632  {
 633      $sql_array = array(
 634          'SELECT'    => 't.topic_visibility, t.forum_id, f.forum_name, f.forum_password, f.parent_id',
 635          'FROM'        => array(
 636              TOPICS_TABLE => 't',
 637              FORUMS_TABLE => 'f',
 638          ),
 639          'WHERE'    => 't.topic_id = ' . (int) $topic_id . '
 640              AND t.forum_id = f.forum_id',
 641      );
 642  
 643      $sql = $db->sql_build_query('SELECT', $sql_array);
 644      $result = $db->sql_query($sql);
 645      $row = $db->sql_fetchrow($result);
 646      $db->sql_freeresult($result);
 647  
 648      if ($row && $row['topic_visibility'] != ITEM_APPROVED && !$auth->acl_get('m_approve', $row['forum_id']))
 649      {
 650          send_status_line(404, 'Not Found');
 651          trigger_error('ERROR_NO_ATTACHMENT');
 652      }
 653      else if ($row && $auth->acl_get('u_download') && $auth->acl_get('f_download', $row['forum_id']))
 654      {
 655          if ($row['forum_password'])
 656          {
 657              // Do something else ... ?
 658              login_forum_box($row);
 659          }
 660      }
 661      else
 662      {
 663          send_status_line(403, 'Forbidden');
 664          trigger_error('SORRY_AUTH_VIEW_ATTACH');
 665      }
 666  }
 667  
 668  /**
 669  * Handles authentication when downloading attachments from PMs
 670  *
 671  * @param \phpbb\db\driver\driver_interface $db The database object
 672  * @param \phpbb\auth\auth $auth The authentication object
 673  * @param int $user_id The user id
 674  * @param int $msg_id The id of the PM that we are downloading from
 675  *
 676  * @return null
 677  */
 678  function phpbb_download_handle_pm_auth($db, $auth, $user_id, $msg_id)
 679  {
 680      if (!$auth->acl_get('u_pm_download'))
 681      {
 682          send_status_line(403, 'Forbidden');
 683          trigger_error('SORRY_AUTH_VIEW_ATTACH');
 684      }
 685  
 686      $allowed = phpbb_download_check_pm_auth($db, $user_id, $msg_id);
 687  
 688      if (!$allowed)
 689      {
 690          send_status_line(403, 'Forbidden');
 691          trigger_error('ERROR_NO_ATTACHMENT');
 692      }
 693  }
 694  
 695  /**
 696  * Checks whether a user can download from a particular PM
 697  *
 698  * @param \phpbb\db\driver\driver_interface $db The database object
 699  * @param int $user_id The user id
 700  * @param int $msg_id The id of the PM that we are downloading from
 701  *
 702  * @return bool Whether the user is allowed to download from that PM or not
 703  */
 704  function phpbb_download_check_pm_auth($db, $user_id, $msg_id)
 705  {
 706      // Check if the attachment is within the users scope...
 707      $sql = 'SELECT msg_id
 708          FROM ' . PRIVMSGS_TO_TABLE . '
 709          WHERE msg_id = ' . (int) $msg_id . '
 710              AND (
 711                  user_id = ' . (int) $user_id . '
 712                  OR author_id = ' . (int) $user_id . '
 713              )';
 714      $result = $db->sql_query_limit($sql, 1);
 715      $allowed = (bool) $db->sql_fetchfield('msg_id');
 716      $db->sql_freeresult($result);
 717  
 718      return $allowed;
 719  }
 720  
 721  /**
 722  * Check if the browser is internet explorer version 7+
 723  *
 724  * @param string $user_agent    User agent HTTP header
 725  * @param int $version IE version to check against
 726  *
 727  * @return bool true if internet explorer version is greater than $version
 728  */
 729  function phpbb_is_greater_ie_version($user_agent, $version)
 730  {
 731      if (preg_match('/msie (\d+)/', strtolower($user_agent), $matches))
 732      {
 733          $ie_version = (int) $matches[1];
 734          return ($ie_version > $version);
 735      }
 736      else
 737      {
 738          return false;
 739      }
 740  }


Generated: Sun Feb 19 19:52:41 2017 Cross-referenced by PHPXref 0.7.1