Source for file browser.php

Documentation is available at browser.php

  1. <?php
  2.  
  3. /**
  4. * @copyright Copyright (c) 2002-2009 Marco Von Ballmoos
  5. * @author Marco Von Ballmoos
  6. * @filesource
  7. * @package webcore
  8. * @subpackage util
  9. * @version 3.2.0
  10. * @since 2.2.1
  11. */
  12.  
  13. /****************************************************************************
  14.  
  15. Copyright (c) 2002-2009 Marco Von Ballmoos
  16.  
  17. This file is part of earthli WebCore.
  18.  
  19. earthli WebCore is free software; you can redistribute it and/or modify
  20. it under the terms of the GNU General Public License as published by
  21. the Free Software Foundation; either version 2 of the License, or
  22. (at your option) any later version.
  23.  
  24. earthli WebCore is distributed in the hope that it will be useful,
  25. but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  27. GNU General Public License for more details.
  28.  
  29. You should have received a copy of the GNU General Public License
  30. along with earthli WebCore; if not, write to the Free Software
  31. Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  32.  
  33. For more information about the earthli WebCore, visit:
  34.  
  35. http://www.earthli.com/software/webcore
  36.  
  37. ****************************************************************************/
  38.  
  39. /**
  40. * Does the browser support DHTML?
  41. */
  42. define ('Browser_DHTML', 1);
  43.  
  44. /**
  45. * Does the browser support PNG images with alpha-transparency?
  46. */
  47. define ('Browser_alpha_PNG', 2);
  48.  
  49. /**
  50. * Does the browser support CSS Level 1?
  51. */
  52. define ('Browser_CSS_1', 3);
  53.  
  54. /**
  55. * Does the browser support CSS Level 2?
  56. */
  57. define ('Browser_CSS_2', 4);
  58.  
  59. /**
  60. * Does the browser support CSS Tables?
  61. */
  62. define ('Browser_CSS_Tables', 5);
  63.  
  64. /**
  65. * Does the browser support Javascript?
  66. */
  67. define ('Browser_JavaScript', 6);
  68.  
  69. /**
  70. * Does the browser support setting/getting cookies?
  71. */
  72. define ('Browser_cookie', 7);
  73.  
  74. /**
  75. * Are HTML anchors in a form submission allowed?
  76. * Determines whether the "action" of a {@link FORM} can contain
  77. * an HTML anchor (#) or not.
  78. */
  79. define ('Browser_anchors_in_posts', 8);
  80.  
  81. /**
  82. * Does the browser support DOM level 2?
  83. */
  84. define ('Browser_DOM_2', 9);
  85.  
  86. /**
  87. * Does the browser accept full HTML in newsfeeds?
  88. */
  89. define ('Browser_extended_HTML_newsfeeds', 10);
  90.  
  91. /**
  92. * Browser uses the Netscape 4 renderer.
  93. */
  94. define ('Browser_netscape_4', 'netscape_4');
  95.  
  96. /**
  97. * Browser uses the Gecko renderer.
  98. * @see BROWSER::gecko_date()
  99. */
  100. define ('Browser_gecko', 'gecko');
  101.  
  102. /**
  103. * Browser uses the KHTML renderer.
  104. * As of this writing, this is used by Safari and Omniweb 4.5 on the Mac and
  105. * Konqueror on Linux.
  106. */
  107. define ('Browser_khtml', 'khtml');
  108.  
  109. /**
  110. * Browser uses the Opera renderer.
  111. */
  112. define ('Browser_opera', 'opera');
  113.  
  114. /**
  115. * Browser uses the newer Opera renderer; version numbers changed from
  116. * browsers identified as {@link Browser_opera}.
  117. */
  118. define ('Browser_presto', 'presto');
  119.  
  120. /**
  121. * Browser uses the WebTV renderer.
  122. */
  123. define ('Browser_webtv', 'webtv');
  124.  
  125. /**
  126. * Browser uses the Internet Explorer renderer.
  127. */
  128. define ('Browser_ie', 'ie');
  129.  
  130. /**
  131. * Browser uses the iCab renderer.
  132. * Mac OS X only.
  133. */
  134. define ('Browser_icab', 'icab');
  135.  
  136. /**
  137. * Browser uses the Omniweb renderer.
  138. * Mac OS X only.
  139. */
  140. define ('Browser_omniweb', 'omniweb');
  141.  
  142. /**
  143. * Browser uses a text renderer.
  144. */
  145. define ('Browser_text', 'lynx');
  146.  
  147. /**
  148. * Browser is a search or other robot.
  149. */
  150. define ('Browser_robot', 'robot');
  151.  
  152. /**
  153. * Browser is a newsfeed reader.
  154. */
  155. define ('Browser_newsreader', 'newsreader');
  156.  
  157. /**
  158. * Browser is a previewer, like the bot Facebook uses to generate previews for linked URLs.
  159. */
  160. define ('Browser_previewer', 'previewer');
  161.  
  162. /**
  163. * Browser is running on Win32.
  164. */
  165. define ('Browser_os_windows', 'windows');
  166.  
  167. /**
  168. * Browser is running on MacOS (Classic or OS X).
  169. */
  170. define ('Browser_os_mac', 'macos');
  171.  
  172. /**
  173. * Browser is running on Linux (some form).
  174. */
  175. define ('Browser_os_linux', 'linux');
  176.  
  177. /**
  178. * Browser element could not be determined.
  179. * @access private
  180. */
  181. define ('Browser_unknown', 'Unknown');
  182.  
  183. /**
  184. * Encapsulates all information about the client.
  185. * The class reads the values from the current session, by default, but can be
  186. * loaded using {@link load_from_string()}. Use {@link supports()} and {@link }
  187. * is()} to determine the capabilities of the underlying renderer.
  188. *
  189. * The user agent detection mechanism makes a distinction between the name of
  190. * the browser and the technology used to render content. The browser name (e.
  191. * g. MSN) is available through {@link name()} and {@link version()}, while the
  192. * underlying renderer (e.g. IE 6.0) is available through ({@link }
  193. * renderer_name()} and {@link renderer_version()}). Use {@link system_id()} to
  194. * get a formatted operating system name or {@link system_name()} and {@link }
  195. * system_version()} to access the information directly.
  196. * @see is()
  197. * @see supports()
  198. * @package webcore
  199. * @subpackage util
  200. * @version 3.2.0
  201. * @since 2.2.1
  202. */
  203. class BROWSER
  204. {
  205. /**
  206. * User-agent string received in HTTP request.
  207. * Set this property with {@link load_from_server()} or {@link load_from_string()}.
  208. * @var string
  209. */
  210. public $user_agent_string;
  211.  
  212. /**
  213. * Calls {@link load_from_server()} by default.
  214. */
  215. public function __construct ()
  216. {
  217. $this->load_from_server ();
  218. }
  219.  
  220. /**
  221. * Read the user agent from the server environment.
  222. * Called automatically from the constructor.
  223. * @see load_from_string()
  224. */
  225. public function load_from_server ()
  226. {
  227. $this->load_from_string (read_array_index ($_SERVER, 'HTTP_USER_AGENT'));
  228. }
  229.  
  230. /**
  231. * Read from a given user agent.
  232. * Used primarily for testing.
  233. * @see load_from_server()
  234. */
  235. public function load_from_string ($s)
  236. {
  237. $this->user_agent_string = $s;
  238. $tables = $this->_make_user_agent_parse_tables ();
  239. $parser = $this->_make_user_agent_parser ($tables);
  240. $this->_ua = $parser->make_properties_from ($s);
  241. }
  242.  
  243. /**
  244. * Resolve the domain name of the browser.
  245. * Performs a reverse name lookup of the {@link ip_address()}.
  246. * @return string
  247. */
  248. public function domain ()
  249. {
  250. return @gethostbyaddr ($this->ip_address ());
  251. }
  252.  
  253. /**
  254. * The actual ip address of the browser.
  255. * Will resolve the proxy forward, if one is present in the HTTP header.
  256. * @see domain()
  257. * @return string
  258. */
  259. public function ip_address ()
  260. {
  261. $forwarded = read_array_index ($_SERVER, 'HTTP_X_FORWARDED_FOR');
  262. $remote_addr = read_array_index ($_SERVER, 'REMOTE_ADDR');
  263. if ($forwarded)
  264. {
  265. return $forwarded;
  266. }
  267. else if ($remote_addr)
  268. {
  269. return $remote_addr;
  270. }
  271.  
  272. return read_array_index ($_SERVER, 'REMOTE_HOST');
  273. }
  274.  
  275. /**
  276. * Return a description as HTML.
  277. * @return string
  278. */
  279. public function description_as_html ()
  280. {
  281. return $this->_description_as_text (false);
  282. }
  283.  
  284. /**
  285. * Return a description as plain text.
  286. * @return string
  287. */
  288. public function description_as_plain_text ()
  289. {
  290. return $this->_description_as_text (true);
  291. }
  292.  
  293. /**
  294. * Identifies the technology used by this browser.
  295. * This is often different than the name, since many browsers employ embedded
  296. * renderers like Gecko, or are rebranded like Opera Composer browsers.
  297. * @return string
  298. */
  299. public function renderer_name ()
  300. {
  301. return $this->_ua->renderer_name;
  302. }
  303.  
  304. /**
  305. * Identifies the version of the technology used by this browser.
  306. * This is the number used internall to identify whether a feature is supported.
  307. * @see BROWSER::supports()
  308. * @return string
  309. */
  310. public function renderer_version ()
  311. {
  312. return $this->_ua->renderer_version;
  313. }
  314.  
  315. /**
  316. * Name of the browser.
  317. * Not necessarily the same as the renderer name. AOL uses the IE engine or the
  318. * Gecko engine, depending on version. MSN uses the IE engine.
  319. * @return string
  320. */
  321. public function name ()
  322. {
  323. return $this->_ua->name;
  324. }
  325.  
  326. /**
  327. * Version of the browser.
  328. * Not necessarily the same as the renderer version. Largely useless identifier
  329. * for determining feature support, but nice to use when showing a user which
  330. * browser they are running. e.g. displaying 'MSN 7.0' instead of 'IE 5.5sp1'.
  331. * @return string
  332. */
  333. public function version ()
  334. {
  335. return $this->_ua->version;
  336. }
  337.  
  338. /**
  339. * Name of the icon to use.
  340. * Use this location with {@link CONTEXT::icon_as_html()} to render the icon in
  341. * a WebCore application.
  342. * @return string
  343. */
  344. public function icon_name ()
  345. {
  346. $n = strtolower ($this->name ());
  347.  
  348. if (strpos ($n, 'opera') !== false)
  349. {
  350. $Result = 'opera1_t';
  351. }
  352. elseif (strpos ($n, 'firefox') !== false)
  353. {
  354. $Result = 'firefox';
  355. }
  356. elseif (strpos ($n, 'omniweb') !== false)
  357. {
  358. $Result = 'omniweb5';
  359. }
  360. elseif (strpos ($n, 'safari') !== false)
  361. {
  362. $Result = 'safari';
  363. }
  364. elseif (strpos ($n, 'camino') !== false)
  365. {
  366. $Result = 'camino';
  367. }
  368. elseif (strpos ($n, 'internet explorer') !== false)
  369. {
  370. $Result = 'ie';
  371. }
  372. elseif (strpos ($n, 'shiira') !== false)
  373. {
  374. $Result = 'shiira';
  375. }
  376. elseif (strpos ($n, 'chrome') !== false)
  377. {
  378. $Result = 'chrome';
  379. }
  380. else
  381. {
  382. $Result = '';
  383. }
  384.  
  385. return $Result;
  386. }
  387.  
  388. /**
  389. * Specific name of the operating system.
  390. * @return string
  391. */
  392. public function system_name ()
  393. {
  394. return $this->_ua->system_name;
  395. }
  396.  
  397. /**
  398. * Operating system version.
  399. * This is specific, like Windows NT 5.1 (Windows 2000) will return 5.1, not 2000.
  400. * @return string
  401. */
  402. public function system_version ()
  403. {
  404. return $this->_ua->system_version;
  405. }
  406.  
  407. /**
  408. * Calculated system name.
  409. * The most likely operating system derived from the user agent string.
  410. * @return string
  411. */
  412. public function calculated_system_name ()
  413. {
  414. return $this->_ua->calculated_system_name;
  415. }
  416.  
  417. /**
  418. * Fully formatted operating system id.
  419. * Returns as much information about the operating system as possible,
  420. * favoring the interpreted system name because it's generally capitalized
  421. * better.
  422. * <ul>
  423. * <li>'Windows 5.1' will return 'Windows 5.1 (Windows 2000)'</li>
  424. * <li>'Linux i686 ... Debian/1.2.0' will return 'Debian 1.2.0 (Linux)'</li>
  425. * </ul>
  426. * @return string
  427. */
  428. public function system_id ()
  429. {
  430. if (isset ($this->_ua->system_name))
  431. {
  432. $Result = $this->_ua->system_name;
  433. if ($this->_ua->system_version)
  434. {
  435. $Result .= ' ' . $this->_ua->system_version;
  436. }
  437. if (strcasecmp ($this->_ua->system_name, $this->_ua->calculated_system_name) != 0)
  438. {
  439. $Result .= ' (' . $this->_ua->calculated_system_name . ')';
  440. }
  441. return $Result;
  442. }
  443.  
  444. return $this->_ua->calculated_system_name;
  445. }
  446.  
  447. /**
  448. * Build date of the client, if built with Gecko.
  449. * Gecko is the mozilla browser technology. Conforming user agent strings include
  450. * the build date of the Gecko component. (Can be empty)
  451. * @return DATE_TIME
  452. */
  453. public function gecko_date ()
  454. {
  455. return $this->_ua->gecko_date;
  456. }
  457.  
  458. /**
  459. * Does the client match the given code?
  460. * The code can be an operating system (like {@link Browser_os_mac}) or
  461. * a browser identifier (like {@link Browser_os_opera}).
  462. * @param string $code
  463. * @return boolean
  464. */
  465. public function is ($code)
  466. {
  467. return (isset ($this->_ua->renderer_id) && ($code == $this->_ua->renderer_id))
  468. || (isset ($this->_ua->os_id) && ($code == $this->_ua->os_id));
  469. }
  470.  
  471. /**
  472. * Is the requested functionality supported?
  473. * See the browser functionality constants.
  474. * @param integer $code
  475. * @return boolean
  476. */
  477. public function supports ($code)
  478. {
  479. switch ($code)
  480. {
  481. case Browser_DHTML:
  482. return (($this->is (Browser_gecko)) ||
  483. ($this->is (Browser_opera) && ($this->_ua->major_version >= 7)) ||
  484. ($this->is (Browser_presto)) ||
  485. ($this->is (Browser_ie) && $this->_ua->major_version >= 5) ||
  486. ($this->is (Browser_khtml)));
  487.  
  488. case Browser_alpha_PNG:
  489. return (($this->is (Browser_gecko)) ||
  490. ($this->is (Browser_presto)) ||
  491. ($this->is (Browser_opera) && (($this->_ua->major_version >= 6) ||
  492. ($this->is (Browser_os_mac) && ($this->_ua->major_version >= 5)))) ||
  493. ($this->is (Browser_ie) && (($this->is (Browser_os_mac) && ($this->_ua->major_version >= 5)) ||
  494. ($this->is (Browser_os_windows) && ($this->_ua->major_version >= 7)))) ||
  495. ($this->is (Browser_omniweb) && ($this->_ua->major_version >= 4)) ||
  496. ($this->is (Browser_khtml)));
  497.  
  498. case Browser_CSS_1:
  499. return (($this->is (Browser_gecko)) ||
  500. ($this->is (Browser_presto)) ||
  501. ($this->is (Browser_opera) && ($this->_ua->major_version >= 4)) ||
  502. ($this->is (Browser_ie) && ($this->_ua->major_version >= 5)) ||
  503. ($this->is (Browser_khtml)));
  504.  
  505. case Browser_CSS_2:
  506. return (($this->is (Browser_gecko)) ||
  507. ($this->is (Browser_presto)) ||
  508. ($this->is (Browser_opera) && ($this->_ua->major_version >= 7)) ||
  509. ($this->is (Browser_khtml)) ||
  510. ($this->is (Browser_omniweb) && (($this->_ua->major_version >= 5) || (($this->_ua->major_version >= 4) && ($this->_ua->minor_version >= 5)))));
  511. case Browser_CSS_Tables:
  512. return (($this->is (Browser_opera) && ($this->_ua->major_version >= 7)) ||
  513. ($this->is (Browser_presto)) ||
  514. ($this->is (Browser_omniweb) && (($this->_ua->major_version >= 5) || (($this->_ua->major_version >= 4) && ($this->_ua->minor_version >= 5)))));
  515.  
  516. case Browser_JavaScript:
  517. return $this->_ua->renderer_name != Browser_unknown;
  518.  
  519. case Browser_cookie:
  520. return $this->_ua->renderer_name != Browser_unknown;
  521.  
  522. case Browser_anchors_in_posts:
  523. return ! ($this->is (Browser_ie) && $this->is (Browser_os_mac));
  524.  
  525. case Browser_DOM_2:
  526. return $this->is (Browser_opera) && ($this->_ua->major_version >= 9) ||
  527. $this->is (Browser_presto);
  528.  
  529. case Browser_extended_HTML_newsfeeds:
  530. return true;
  531. }
  532.  
  533. return false;
  534. }
  535.  
  536. /**
  537. * Build a human-readable description.
  538. * @param boolean $text_only May include HTML tags if not set.
  539. * @return string
  540. * @access private
  541. */
  542. protected function _description_as_text ($text_only = false)
  543. {
  544. $app_name = $this->name () . ' ' . $this->version ();
  545. $eng_name = $this->renderer_name () . ' ' . $this->renderer_version ();
  546.  
  547. if ($app_name == $eng_name)
  548. {
  549. $Result = $app_name;
  550. }
  551. else
  552. {
  553. $Result = "$app_name - $eng_name";
  554. }
  555.  
  556. if ($this->is (Browser_gecko))
  557. {
  558. $gd = $this->gecko_date ();
  559. if ($gd)
  560. {
  561. $t = $gd->formatter ();
  562. $t->type = Date_time_format_date_only;
  563. if ($text_only)
  564. {
  565. $t->clear_flags ();
  566. }
  567. $Result .= ' (Released ' . $gd->format ($t) . ')';
  568. }
  569. }
  570.  
  571. return $Result;
  572. }
  573.  
  574. /**
  575. * @return USER_AGENT_PARSE_TABLES
  576. * @access private
  577. */
  578. protected function _make_user_agent_parse_tables ()
  579. {
  580. return new USER_AGENT_PARSE_TABLES ();
  581. }
  582.  
  583. /**
  584. * @return USER_AGENT_PARSER
  585. * @access private
  586. */
  587. protected function _make_user_agent_parser ($tables)
  588. {
  589. return new USER_AGENT_PARSER ($tables);
  590. }
  591. }
  592.  
  593. /**
  594. * Raw information container for client properties.
  595. * Used by the {@link BROWSER} to determine client properties and capabilities.
  596. * The user agent tracks both the client name and version ({@link $name} and
  597. * {@link $version}) and the rendering technology ({@link $renderer_name} and
  598. * {@link $renderer_version}) for that client. Can be created from a string by
  599. * the {@link USER_AGENT_PARSER}.
  600. * @package webcore
  601. * @subpackage util
  602. * @version 3.2.0
  603. * @since 2.6.0
  604. */
  605. class USER_AGENT_PROPERTIES
  606. {
  607. /**
  608. * Name of the client application.
  609. * @var string
  610. */
  611. public $name = Browser_unknown;
  612.  
  613. /**
  614. * Version of the client application.
  615. * @var string
  616. */
  617. public $version = '';
  618.  
  619. /**
  620. * Name of the rendering technology.
  621. * @var string
  622. */
  623. public $renderer_name = Browser_unknown;
  624.  
  625. /**
  626. * @var string
  627. */
  628. public $renderer_version = '';
  629.  
  630. /**
  631. * @var string
  632. */
  633. public $renderer_id = Browser_unknown;
  634.  
  635. /**
  636. * @var string
  637. */
  638. public $system_name = Browser_unknown;
  639.  
  640. /**
  641. * @var string
  642. */
  643. public $system_version = '';
  644.  
  645. /**
  646. * @var string
  647. */
  648. public $calculated_system_name = Browser_unknown;
  649.  
  650. /**
  651. * @var string
  652. */
  653. public $os_id = '';
  654.  
  655. /**
  656. * @var DATE_TIME
  657. */
  658. public $gecko_date;
  659.  
  660. /**
  661. * @var boolean
  662. */
  663. public $is_robot;
  664.  
  665. /**
  666. * @var integer
  667. */
  668. public $major_version = 0;
  669.  
  670. /**
  671. * @var integer
  672. */
  673. public $minor_version = 0;
  674.  
  675. /**
  676. * @var integer
  677. */
  678. public $build_number = 0;
  679.  
  680. public function __construct ()
  681. {
  682. $this->gecko_date = new DATE_TIME ();
  683. $this->gecko_date->clear ();
  684. }
  685. }
  686.  
  687. /**
  688. * Creates a {@link USER_AGENT_PROPERTIES} object from a string.
  689. * Used by the {@link BROWSER} to generate a set of properties from the user
  690. * agents passed in by PHP.
  691. * @package webcore
  692. * @subpackage util
  693. * @version 3.2.0
  694. * @since 2.7.0
  695. * @access private
  696. */
  697. class USER_AGENT_PARSER
  698. {
  699. /**
  700. * @param USER_AGENT_PARSE_TABLES $tables
  701. */
  702. public function __construct ($tables)
  703. {
  704. $this->_tables = $tables;
  705. }
  706.  
  707. public function make_properties_from ($s)
  708. {
  709. $Result = new USER_AGENT_PROPERTIES ();
  710.  
  711. $parts = null; // Compiler warning
  712. preg_match_all ('/([a-zA-Z]|[a-zA-Z]+[0-9]+|[a-zA-Z]+[ 0-9]+[a-zA-Z]|[a-zA-Z][ \-&a-zA-Z]*[a-zA-Z])[-\/: ]?[vV]?([0-9][0-9a-z]*([\.-][0-9][0-9a-z]*)*)/', $s, $parts);
  713.  
  714. $ids = $parts [1];
  715. $vers = $parts [2];
  716.  
  717. $ignored_ids = $this->_tables->ignored_ids ();
  718. $system_ids = $this->_tables->system_ids ();
  719. $renderers = $this->_tables->renderer_ids ();
  720.  
  721. $continue_processing = true;
  722. $index = 0;
  723. $current_renderer = null;
  724. $browser_is_final = false;
  725.  
  726. while ($continue_processing && ($index < sizeof ($ids)))
  727. {
  728. $ver = $this->_extract_version ($vers [$index]);
  729. $id = strtolower ($ids [$index]);
  730. // Remove the trailing version marker if needed
  731. if (strcasecmp(substr($id, -2), ' v') == 0)
  732. {
  733. $id = substr($id, 0, -2);
  734. $ids [$index] = substr($ids [$index], 0, -2);
  735. }
  736.  
  737. // Don't bother processing ids only one character long
  738. if (strlen($id) > 1)
  739. {
  740. if (isset ($renderers [$id]))
  741. {
  742. $renderer = $renderers [$id];
  743. if (empty ($current_renderer) || ($current_renderer->renderer_can_be_overridden ()))
  744. {
  745. if ($renderer->is_mozilla_gecko ($ver))
  746. {
  747. $current_renderer = $renderers [Browser_gecko];
  748. }
  749. else
  750. {
  751. $current_renderer = $renderer;
  752. }
  753. if ($id == Browser_gecko)
  754. {
  755. $Result->gecko_date = $this->_determine_gecko_date ($ver);
  756. }
  757. else
  758. {
  759. $current_version = $ver;
  760. }
  761. }
  762. }
  763. if (! isset ($ignored_ids [$id]) && !isset($system_ids[$id]))
  764. {
  765. if (!$browser_is_final && (empty ($current_renderer) || $current_renderer->browser_can_be_overridden ()))
  766. {
  767. $Result->version = $ver;
  768. if (isset ($renderers [$id]))
  769. {
  770. $renderer = $renderers [$id];
  771. if ($renderer->is_mozilla_gecko ($ver))
  772. {
  773. $Result->name = 'Mozilla';
  774. }
  775. else
  776. {
  777. $Result->name = $renderer->display_name;
  778. }
  779. if ($current_renderer->precedence == User_agent_final_browser_temporary_renderer)
  780. {
  781. $browser_is_final = true;
  782. }
  783. $continue_processing = $renderer->continue_processing_ids ();
  784. }
  785. else
  786. {
  787. $Result->name = $ids [$index]; // Use the id in original case
  788. }
  789. }
  790. }
  791. if (isset ($system_ids [$id]))
  792. {
  793. $Result->system_name = $system_ids [$id];
  794. $Result->system_version = $ver;
  795. }
  796. }
  797. $index += 1;
  798. }
  799.  
  800. if (! empty ($current_renderer))
  801. {
  802. $Result->renderer_id = $current_renderer->id;
  803. $Result->renderer_name = $current_renderer->technology_name;
  804. $Result->renderer_version = $current_version;
  805. @list ($Result->major_version, $Result->minor_version, $Result->build_number) = explode ('.', $Result->renderer_version);
  806.  
  807. if (! $current_renderer->browser_can_be_overridden ())
  808. {
  809. $Result->name = $current_renderer->display_name;
  810. $Result->version = $Result->renderer_version;
  811. }
  812. }
  813.  
  814. $this->_determine_os ($Result, $s);
  815. $this->_determine_robot ($Result);
  816.  
  817. return $Result;
  818. }
  819.  
  820. /**
  821. * Pull out the version from the given string.
  822. * @param string $version
  823. * @return true
  824. * @access private
  825. */
  826. protected function _extract_version ($version)
  827. {
  828. $Result = $version;
  829. if (($Result [0] == 'v') || ($Result [0] == 'V'))
  830. {
  831. $Result = substr ($Result, 1);
  832. }
  833. return $Result;
  834. }
  835.  
  836. /**
  837. * Read the gecko publication date from the version number.
  838. * @param string $version
  839. * @return DATE_TIME
  840. * @access private
  841. */
  842. protected function _determine_gecko_date ($version)
  843. {
  844. $parts = null; // Compiler warning
  845. preg_match ('/([0-9]{4})([0-9]{2})([0-9]{2})/', $version, $parts);
  846. if (sizeof ($parts))
  847. {
  848. return new DATE_TIME (mktime (0, 0, 0, $parts [2], $parts [3], $parts [1]));
  849. }
  850. return null;
  851. }
  852.  
  853. /**
  854. * Determine the operating system.
  855. * Since most user agents don't specify an OS with version (Linux is a
  856. * standout here), we examine the user agent in a non version-specific way.
  857. * @param USER_AGENT_PROPERTIES $props
  858. * @param string $raw_data User agent to examine, in lower-case format.
  859. * @access private
  860. */
  861. protected function _determine_os ($props, $raw_data)
  862. {
  863. $raw_data = strtolower ($raw_data);
  864. $props->calculated_system_name = Browser_unknown;
  865. $oss = $this->_tables->os_ids ();
  866. while (($props->calculated_system_name == Browser_unknown) && (list ($key, $value) = each ($oss)))
  867. {
  868. if ($key)
  869. {
  870. $keys = explode (',', $key);
  871. $match = true;
  872. foreach ($keys as $key)
  873. {
  874. $match = $match && ($this->_contains ($raw_data, $key));
  875. }
  876. if ($match)
  877. {
  878. $props->calculated_system_name = $value;
  879. }
  880. }
  881. }
  882.  
  883. /* If the actual system has not been set through other means, use the
  884. * calculated system.
  885. */
  886.  
  887. if ($props->system_name == Browser_unknown)
  888. {
  889. $props->system_name = $props->calculated_system_name;
  890. }
  891.  
  892. /* Set the standard OS id, if one exists. */
  893.  
  894. if ($this->_contains ($props->calculated_system_name, 'Windows'))
  895. {
  896. $props->os_id = Browser_os_windows;
  897. }
  898. if ($this->_contains ($props->calculated_system_name, 'MacOS'))
  899. {
  900. $props->os_id = Browser_os_mac;
  901. }
  902. if ($this->_contains ($props->calculated_system_name, 'Linux'))
  903. {
  904. $props->os_id = Browser_os_linux;
  905. }
  906. }
  907.  
  908. /**
  909. * Check if the client is a search engine or robot.
  910. * Only checks if the {@link $renderer_id} has not already been set.
  911. * @param USER_AGENT_PROPERTIES $props
  912. * @access private
  913. */
  914. protected function _determine_robot ($props)
  915. {
  916. if ($props->renderer_id == Browser_unknown)
  917. {
  918. $robot_names = $this->_tables->robot_names ();
  919. foreach ($robot_names as $n)
  920. {
  921. if ($this->_contains (strtolower ($props->name), $n))
  922. {
  923. $props->renderer_id = Browser_robot;
  924. }
  925. }
  926. }
  927. }
  928.  
  929. /**
  930. * @param string $haystack
  931. * @param string $needle
  932. * @return bool
  933. * @access private
  934. */
  935. protected function _contains ($haystack, $needle)
  936. {
  937. return strpos ($haystack, $needle) !== false;
  938. }
  939. }
  940.  
  941. /**
  942. * Used by the {@link USER_AGENT_PARSER}.
  943. * The tables contain browser- and user agent-specific information built up from
  944. * a database of known user agents.
  945. * @package webcore
  946. * @subpackage util
  947. * @version 3.2.0
  948. * @since 2.7.0
  949. * @access private
  950. */
  951. class USER_AGENT_PARSE_TABLES
  952. {
  953. /**
  954. * A list of the known rendering technologies.
  955. * Each id maps to the rendering technology to record (retrieved later with the 'is' function),
  956. * a pretty-printed name for the renderer and a level. The level determines which renderers override
  957. * others if more than one renderer is specified. That is, almost every user-agent on the planet
  958. * returns mozilla x.x, but that renderer declaration is overridden by any other one that comes along.
  959. * IE is the next weakest because many user agents spoof as IE as well. If either or both of these are
  960. * specified, they are recorded, but if any other renderer is specified, that one is used instead.
  961. * @see USER_AGENT_RENDERER_INFO
  962. * @return array[string,USER_AGENT_RENDERER_INFO]
  963. */
  964. public function renderer_ids ()
  965. {
  966. return array (
  967. 'mozilla' => new USER_AGENT_RENDERER_INFO (Browser_netscape_4, 'Netscape 4.x', User_agent_temporary_renderer),
  968. 'msie' => new USER_AGENT_RENDERER_INFO (Browser_ie, 'Trident (IE)', User_agent_temporary_renderer, 'Internet Explorer'),
  969. 'rv' => new USER_AGENT_RENDERER_INFO (Browser_gecko, 'Gecko', User_agent_temporary_renderer, 'Mozilla'),
  970. 'gecko' => new USER_AGENT_RENDERER_INFO (Browser_gecko, 'Gecko', User_agent_temporary_renderer, 'Mozilla'),
  971. 'shiira' => new USER_AGENT_RENDERER_INFO (Browser_khtml, 'Webcore', User_agent_final_browser_abort, 'Shiira'),
  972. 'applewebkit' => new USER_AGENT_RENDERER_INFO (Browser_khtml, 'Webcore', User_agent_final_renderer),
  973. 'netscape6' => new USER_AGENT_RENDERER_INFO (Browser_gecko, 'Netscape', User_agent_final_browser),
  974. 'chrome' => new USER_AGENT_RENDERER_INFO (Browser_khtml, 'Google Chrome', User_agent_final_browser_abort),
  975. 'opera mini' => new USER_AGENT_RENDERER_INFO (Browser_opera, 'Presto (Opera)', User_agent_final_browser_temporary_renderer, 'Opera Mini'),
  976. 'opera' => new USER_AGENT_RENDERER_INFO (Browser_opera, 'Presto (Opera)', User_agent_temporary_renderer, 'Opera'),
  977. 'presto' => new USER_AGENT_RENDERER_INFO (Browser_presto, 'Presto (Opera)', User_agent_final_renderer, 'Opera'),
  978. 'version' => new USER_AGENT_RENDERER_INFO (Browser_presto, 'Presto (Opera)', User_agent_final_browser, 'Opera'),
  979. 'konqueror' => new USER_AGENT_RENDERER_INFO (Browser_khtml, 'KHTML', User_agent_final_browser, 'Konqueror'),
  980. 'omniweb' => new USER_AGENT_RENDERER_INFO (Browser_omniweb, 'OmniWeb', User_agent_final_browser),
  981. 'webtv' => new USER_AGENT_RENDERER_INFO (Browser_webtv, 'WebTV', User_agent_final_browser),
  982. 'googlebot' => new USER_AGENT_RENDERER_INFO (Browser_robot, 'Google Robot', User_agent_final_browser),
  983. 'msnbot' => new USER_AGENT_RENDERER_INFO (Browser_robot, 'MSN Robot', User_agent_final_browser),
  984. 'yahooseeker' => new USER_AGENT_RENDERER_INFO (Browser_robot, 'Yahoo Robot', User_agent_final_browser),
  985. 'googlebot-image' => new USER_AGENT_RENDERER_INFO (Browser_robot, 'Google Robot', User_agent_final_browser),
  986. 'lynx' => new USER_AGENT_RENDERER_INFO (Browser_text, 'Text', User_agent_final_browser, 'Lynx'),
  987. 'icab' => new USER_AGENT_RENDERER_INFO (Browser_icab, 'iCab', User_agent_final_browser),
  988. 'applesyndication' => new USER_AGENT_RENDERER_INFO (Browser_newsreader, 'Safari Newsreader', User_agent_final_browser),
  989. 'yahoofeedseeker' => new USER_AGENT_RENDERER_INFO (Browser_newsreader, 'Yahoo Newsreader', User_agent_final_browser),
  990. 'newsgatoronline' => new USER_AGENT_RENDERER_INFO (Browser_newsreader, 'NewsGator', User_agent_final_browser),
  991. 'bloglines' => new USER_AGENT_RENDERER_INFO (Browser_newsreader, 'Bloglines', User_agent_final_browser),
  992. 'facebookexternalhit' => new USER_AGENT_RENDERER_INFO (Browser_previewer, 'Facebook Preview', User_agent_final_browser),
  993. );
  994. }
  995.  
  996. /**
  997. * A list of ids known to be spurious or system ids.
  998. * The algorithm always uses the last non-ignored id as the browser id; this
  999. * list determines which ids are ignored.
  1000. * @return array[string,boolean]
  1001. */
  1002. public function ignored_ids ()
  1003. {
  1004. return array ('windows' => 1,
  1005. 'windows nt' => 1,
  1006. 'win 9x' => 1,
  1007. 'linux' => 1,
  1008. 'debian' => 1,
  1009. 'suse' => 1,
  1010. 'amigaos' => 1,
  1011. 'gecko' => 1, // gecko build date
  1012. 'rv' => 1, // gecko version number
  1013. 'libwww-fm' => 1, // lynx
  1014. 'openssl' => 1, // lynx
  1015. 'ssl-mm' => 1, // lynx
  1016. 'ycomp' => 1, // IE plugin
  1017. 'sbcydsl' => 1, // IE plugin
  1018. 'hotbar' => 1, // IE plugin
  1019. 'yplus' => 1, // IE plugin
  1020. 'net clr' => 1, // msn
  1021. 'i386-unknown-freebsd' => 1,
  1022. 'libcurl' => 1,
  1023. 'r1' => 1,
  1024. 'ssl' => 1,
  1025. 'debian package' => 1,
  1026. 'winfx runtime' => 1, // windows vista
  1027. 'avalon' => 1, // windows vista
  1028. 'tablet pc' => 1, // windows vista
  1029. 'sl commerce client' => 1, // windows vista
  1030. 'cldc' => 1, // Nokia phone
  1031. 'midp' => 1, // Nokia phone
  1032. 'views' => 1, // Newsfeed readers
  1033. 'users' => 1, // Newsfeed readers
  1034. 'ipv' => 1,
  1035. 'ssl' => 1,
  1036. 'linux i' => 1,
  1037. );
  1038. }
  1039.  
  1040. /**
  1041. * A list of systems known to provide version info in the user agent.
  1042. * @return array[string,string]
  1043. */
  1044. public function system_ids ()
  1045. {
  1046. return array ('windows nt' => 'Windows NT',
  1047. 'win 9x' => 'Windows 9x',
  1048. 'linux' => 'Linux',
  1049. 'debian' => 'Debian',
  1050. 'amigaos' => 'AmigaOS',
  1051. 'debian package' => 'Debian',
  1052. 'suse' => 'SUSE',
  1053. 'series80' => 'Series 80',
  1054. 'winnt' => 'Windows NT',
  1055. 'freebsd' => 'FreeBSD',
  1056. );
  1057. }
  1058.  
  1059. /**
  1060. * A list of names that are commonly robots.
  1061. * Since there are so many different robot clients, we look for common names
  1062. * in the renderer/browser name to mark unknown browsers as robots.
  1063. * @return array[string,boolean]
  1064. */
  1065. public function robot_names ()
  1066. {
  1067. return array ('crawler',
  1068. 'spider',
  1069. 'bot',
  1070. 'robot'
  1071. );
  1072. }
  1073.  
  1074. /**
  1075. * A mapping of user agent fragments to platform ids.
  1076. * The platform id is a nicely formatted, standardized name for the operating system. This
  1077. * array maps the different user agent platform ids onto these standard ones. (e.g. 'nt 4'
  1078. * and 'nt4' both map onto 'Windows NT 4.x').
  1079. * @return array[string,string]
  1080. */
  1081. public function os_ids ()
  1082. {
  1083. return array ('win,nt 6.0' => 'Windows Vista',
  1084. 'win,nt 5.1' => 'Windows XP',
  1085. 'win,nt 5' => 'Windows 2000',
  1086. 'win,2000' => 'Windows 2000',
  1087. 'win,98' => 'Windows 98',
  1088. 'win,95' => 'Windows 95',
  1089. 'win,nt 4' => 'Windows NT 4.x',
  1090. 'win,nt4' => 'Windows NT 4.x',
  1091. 'win,nt 3' => 'Windows NT 3.x',
  1092. 'win,nt' => 'Windows NT',
  1093. 'win,16' => 'Windows 3.x',
  1094. 'win' => 'Windows',
  1095. 'mac,68k' => 'MacOS 68k',
  1096. 'mac,68000' => 'MacOS 68k',
  1097. 'mac,os x' => 'Mac OS X',
  1098. 'mac,ppc' => 'MacOS PPC',
  1099. 'mac,powerpc' => 'MacOS PPC',
  1100. 'macintosh' => 'MacOS',
  1101. 'applesyndication' => 'Mac OS X', // Safari newsreader
  1102. 'linux' => 'Linux',
  1103. 'series80' => 'Series 80',
  1104. 'amigaos' => 'AmigaOS',
  1105. 'beos' => 'BeOS',
  1106. 'os/2' => 'OS/2',
  1107. 'webtv' => 'WebTV',
  1108. 'sunos' => 'Sun/Solaris',
  1109. 'irix' => 'Irix',
  1110. 'hpux' => 'HP Unix',
  1111. 'aix' => 'AIX',
  1112. 'dec' => 'DEC-Alpha',
  1113. 'alpha' => 'DEC-Alpha',
  1114. 'osf1' => 'DEC-Alpha',
  1115. 'ultrix' => 'DEC-Alpha',
  1116. 'sco' => 'SCO',
  1117. 'unix_sv' => 'SCO',
  1118. 'vax' => 'VMS',
  1119. 'openvms' => 'VMS',
  1120. 'sinix' => 'Sinix',
  1121. 'reliantunix' => 'Reliant/Unix',
  1122. 'freebsd' => 'FreeBSD',
  1123. 'openbsd' => 'OpenBSD',
  1124. 'netbsd' => 'NetBSD',
  1125. 'bsd' => 'BSD',
  1126. 'unix_system_v' => 'UnixWare',
  1127. 'ncr' => 'MPRAS',
  1128. 'x11' => 'Unix'
  1129. );
  1130. }
  1131. }
  1132.  
  1133. /**
  1134. * Marks a potential renderer and browser.
  1135. * A {@link USER_AGENT_RENDERER_INFO} marked as such will be used for the
  1136. * renderer and browser only if no other renderers or browsers are detected.
  1137. * @see User_agent_final_renderer
  1138. * @see User_agent_final_browser
  1139. */
  1140. define ('User_agent_temporary_renderer', 1);
  1141.  
  1142. /**
  1143. * Marks a renderer that is not necessarily the final browser.
  1144. * A {@link USER_AGENT_RENDERER_INFO} marked as such will be used for the
  1145. * renderer, but can be replaced as browser by an ensuing name/version pair.
  1146. * @see User_agent_temporary_renderer
  1147. * @see User_agent_final_browser
  1148. */
  1149. define ('User_agent_final_renderer', 2);
  1150.  
  1151. /**
  1152. * Marks a renderer that cannot be replaced.
  1153. * A {@link USER_AGENT_RENDERER_INFO} marked as such will be used for both
  1154. * renderer and browser information if detected in the user agent.
  1155. * @see User_agent_final_renderer
  1156. * @see User_agent_final_browser
  1157. */
  1158. define ('User_agent_final_browser', 3);
  1159.  
  1160. /**
  1161. * Marks a final browser name (skips remaining entries).
  1162. * Used only for special cases where a browser sticks its name in front of
  1163. * another valid browser name (that cannot be added to the ignored ids).
  1164. * The "Shiira" browser is such a case as it adds the "Safari" at the end of
  1165. * the user agent.
  1166. */
  1167. define ('User_agent_final_browser_abort', 4);
  1168.  
  1169. /**
  1170. * Marks a final browser name, but overridable renderer.
  1171. * The browser name is definite, but the renderer can still be overridden. The
  1172. * "Opera Mini" browser works this way in that it places the renderer name and
  1173. * version at the end.
  1174. */
  1175. define ('User_agent_final_browser_temporary_renderer', 5);
  1176.  
  1177. /**
  1178. * Properties for a registered renderer.
  1179. * The {@link USER_AGENT_PROPERTIES::_renderer_ids()} function returns an array
  1180. * of these to use during detection.
  1181. * @package webcore
  1182. * @subpackage util
  1183. * @version 3.2.0
  1184. * @since 2.7.0
  1185. * @access private
  1186. */
  1187. class USER_AGENT_RENDERER_INFO
  1188. {
  1189. /**
  1190. * Identifier for the renderer.
  1191. * May be {@link Browser_gecko}, {@link Browser_opera} or any of the other
  1192. * browser constants.
  1193. * @var string
  1194. */
  1195. public $id;
  1196.  
  1197. /**
  1198. * Code name for the rendering technology.
  1199. * May be the same as {@link $display_name}.
  1200. * @var string
  1201. */
  1202. public $technology_name;
  1203.  
  1204. /**
  1205. * Replacement for the name found in the user agent.
  1206. * Often the renderer is identified by a non-user-friendly id (e.g. MSIE)
  1207. * instead of the name of the browser. This is the name to use for the browser
  1208. * if no other name is given.
  1209. */
  1210. public $display_name;
  1211.  
  1212. /**
  1213. * Defines the precedence for this renderer during detection.
  1214. * Uses {@link User_agent_temporary_renderer}, {@link }
  1215. * User_agent_final_renderer}, {@link User_agent_final_browser}.
  1216. * @var integer
  1217. */
  1218. public $precedence;
  1219.  
  1220. /**
  1221. * Create a description for a renderer.
  1222. * Uses {@link $technology_name} if {@link $display_name} is empty.
  1223. * @param string $id Value for the {@link $id}.
  1224. * @param string $tech Value for the {@link $technology_name}.
  1225. * @param string $disp Value for the {@link $display_name}.
  1226. * @param integer $prec Value for the {@link $precedence}.
  1227. */
  1228. public function __construct ($id, $tech, $prec, $disp = '')
  1229. {
  1230. $this->id = $id;
  1231. $this->technology_name = $tech;
  1232. if ($disp)
  1233. {
  1234. $this->display_name = $disp;
  1235. }
  1236. else
  1237. {
  1238. $this->display_name = $tech;
  1239. }
  1240. $this->precedence = $prec;
  1241. }
  1242.  
  1243. /**
  1244. * @return boolean
  1245. */
  1246. public function renderer_can_be_overridden ()
  1247. {
  1248. return ($this->precedence == User_agent_temporary_renderer) || ($this->precedence == User_agent_final_browser_temporary_renderer);
  1249. }
  1250.  
  1251. /**
  1252. * @return boolean
  1253. */
  1254. public function browser_can_be_overridden ()
  1255. {
  1256. return ($this->precedence != User_agent_final_browser);
  1257. }
  1258.  
  1259. /**
  1260. * @return boolean
  1261. */
  1262. public function continue_processing_ids ()
  1263. {
  1264. return $this->precedence != User_agent_final_browser_abort;
  1265. }
  1266.  
  1267. /**
  1268. * Return <code>True</code> if the browser is Mozilla.
  1269. * If the {@link $id} is {@link Browser_netscape_4}, but the version is
  1270. * greater than 4, it's a Mozilla browser, not Netscape 4 (they use the same
  1271. * technology id).
  1272. * @param string $ver
  1273. * @return boolean
  1274. */
  1275. public function is_mozilla_gecko ($ver)
  1276. {
  1277. return ($this->id == Browser_netscape_4) && ($ver [0] > 4);
  1278. }
  1279. }
  1280.  
  1281. ?>