├── .gitignore ├── index.php ├── includes ├── index.php ├── admin │ ├── index.php │ ├── views │ │ ├── settings │ │ │ ├── enable-cors.php │ │ │ ├── page.php │ │ │ └── secret-key.php │ │ └── user-token-ui.php │ ├── class-simple-jwt-authentication-settings.php │ └── class-simple-jwt-authentication-profile.php ├── vendor │ ├── firebase │ │ └── php-jwt │ │ │ ├── .gitignore │ │ │ ├── src │ │ │ ├── ExpiredException.php │ │ │ ├── BeforeValidException.php │ │ │ ├── SignatureInvalidException.php │ │ │ └── JWT.php │ │ │ ├── tests │ │ │ ├── bootstrap.php │ │ │ ├── autoload.php.dist │ │ │ └── JWTTest.php │ │ │ ├── .travis.yml │ │ │ ├── composer.lock │ │ │ ├── phpunit.xml.dist │ │ │ ├── composer.json │ │ │ ├── run-tests.sh │ │ │ ├── LICENSE │ │ │ ├── package.xml │ │ │ └── README.md │ ├── donatj │ │ └── phpuseragentparser │ │ │ ├── .gitignore │ │ │ ├── phpunit.xml.dist │ │ │ ├── .travis.yml │ │ │ ├── composer.json │ │ │ ├── LICENSE.md │ │ │ ├── CONTRIBUTING.md │ │ │ ├── Tests │ │ │ ├── UserAgentParserTest.php │ │ │ └── user_agents.json │ │ │ ├── Tools │ │ │ └── user_agent_sorter.php │ │ │ ├── README.md │ │ │ └── Source │ │ │ └── UserAgentParser.php │ ├── autoload.php │ └── composer │ │ ├── autoload_classmap.php │ │ ├── autoload_namespaces.php │ │ ├── autoload_psr4.php │ │ ├── autoload_files.php │ │ ├── LICENSE │ │ ├── autoload_static.php │ │ ├── autoload_real.php │ │ ├── installed.json │ │ └── ClassLoader.php ├── class-simple-jwt-authentication-api.php └── class-simple-jwt-authentication-rest.php ├── languages ├── index.php ├── simple-jwt-authentication-sv_SE.mo ├── simple-jwt-authentication.pot └── simple-jwt-authentication-sv_SE.po ├── composer.json ├── uninstall.php ├── README.md ├── README.txt ├── simple-jwt-authentication.php ├── composer.lock └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /.DS_Store 2 | .history 3 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | array($vendorDir . '/firebase/php-jwt/src'), 10 | ); 11 | -------------------------------------------------------------------------------- /includes/vendor/composer/autoload_files.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/donatj/phpuseragentparser/Source/UserAgentParser.php', 10 | ); 11 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tests 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /includes/admin/views/settings/enable-cors.php: -------------------------------------------------------------------------------- 1 | value='1' /> 2 | ' . esc_html__( 'Defined in wp-config.php', 'simple-jwt-authentication' ) . ''; 5 | } 6 | ?> 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jonathan-dejong/simple-jwt-authentication", 3 | "type": "wordpress-plugin", 4 | "description": "Extends the WP REST API using JSON Web Tokens Authentication as an authentication method", 5 | "config": { 6 | "vendor-dir": "includes/vendor" 7 | }, 8 | "require": { 9 | "firebase/php-jwt": "^3.0", 10 | "donatj/phpuseragentparser": "*", 11 | "ramsey/uuid": "^3.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | 4 | php: 5 | - 5.3 6 | - 5.4 7 | - 5.5 8 | - 5.6 9 | - 7.0 10 | - 7.1 11 | - nightly 12 | - hhvm 13 | 14 | matrix: 15 | allow_failures: 16 | - php: nightly 17 | 18 | install: composer install --no-dev 19 | script: phpunit --coverage-clover=coverage.clover 20 | after_script: 21 | - wget https://scrutinizer-ci.com/ocular.phar 22 | # - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 23 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/tests/autoload.php.dist: -------------------------------------------------------------------------------- 1 | =5.2.0" 17 | }, 18 | "platform-dev": [] 19 | } 20 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | ./tests 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /includes/admin/views/settings/page.php: -------------------------------------------------------------------------------- 1 |
2 |

Simple JWT Authentication

3 | 8 |

9 |

10 | 11 | documentation', 'simple-jwt-authentication' ), 'https://github.com/jonathan-dejong/simple-jwt-authentication/wiki/Documentation' ); // phpcs:ignore ?> 12 |

13 |
14 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | =5.3.0" 10 | }, 11 | "require-dev": { 12 | "camspiers/json-pretty": "0.1.*", 13 | "phpunit/phpunit": ">=4.8", 14 | "donatj/drop": "*" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Jesse G. Donat", 19 | "email": "donatj@gmail.com", 20 | "homepage": "http://donatstudios.com", 21 | "role": "Developer" 22 | } 23 | ], 24 | "autoload": { 25 | "files": [ "Source/UserAgentParser.php" ] 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /includes/admin/views/settings/secret-key.php: -------------------------------------------------------------------------------- 1 | 14 | 15 | size="50" autocomplete="off" /> 16 |
17 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "firebase/php-jwt", 3 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 4 | "homepage": "https://github.com/firebase/php-jwt", 5 | "authors": [ 6 | { 7 | "name": "Neuman Vong", 8 | "email": "neuman+pear@twilio.com", 9 | "role": "Developer" 10 | }, 11 | { 12 | "name": "Anant Narayanan", 13 | "email": "anant@php.net", 14 | "role": "Developer" 15 | } 16 | ], 17 | "license": "BSD-3-Clause", 18 | "require": { 19 | "php": ">=5.3.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Firebase\\JWT\\": "src" 24 | } 25 | }, 26 | "minimum-stability": "dev" 27 | } 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-jwt-authentication 2 | 3 | ## Please note 4 | Due to having had a lot of changes in my personal and work life I find myself with very little time to maintain this plugin. 5 | I created this fork years ago because at the time the original author had abandoned his plugin and there wasn't any other options out there. 6 | Today, that's not the case and I'm glad! I've learned that there's now also a proper plugin with active development up on wordpress.org. 7 | 8 | For that reason I will recommend any newcomers here to instead use the plugin on wordpress.org found here: 9 | https://wordpress.org/plugins/simple-jwt-login/ 10 | 11 | **For everyone else, this plugin still works!** 12 | If you have issues with it due to WordPress core updates or the likes I will try to get it fixed. 13 | I still gladly accept PRs too with improvements. 14 | 15 | ## Documentation 16 | Extends the WP REST API using JSON Web Tokens Authentication as an authentication method. 17 | [Documentation](https://github.com/jonathan-dejong/simple-jwt-authentication/wiki/Documentation) 18 | -------------------------------------------------------------------------------- /includes/vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /includes/vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | __DIR__ . '/..' . '/donatj/phpuseragentparser/Source/UserAgentParser.php', 11 | ); 12 | 13 | public static $prefixLengthsPsr4 = array ( 14 | 'F' => 15 | array ( 16 | 'Firebase\\JWT\\' => 13, 17 | ), 18 | ); 19 | 20 | public static $prefixDirsPsr4 = array ( 21 | 'Firebase\\JWT\\' => 22 | array ( 23 | 0 => __DIR__ . '/..' . '/firebase/php-jwt/src', 24 | ), 25 | ); 26 | 27 | public static function getInitializer(ClassLoader $loader) 28 | { 29 | return \Closure::bind(function () use ($loader) { 30 | $loader->prefixLengthsPsr4 = ComposerStaticInit89ab54d4543c163039c2cf5235fd7e1a::$prefixLengthsPsr4; 31 | $loader->prefixDirsPsr4 = ComposerStaticInit89ab54d4543c163039c2cf5235fd7e1a::$prefixDirsPsr4; 32 | 33 | }, null, ClassLoader::class); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License 2 | =============== 3 | 4 | Copyright (c) 2013 Jesse G. Donat 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | ## Reporting Issues 4 | 5 | Issues can be reported via the [Github Issues](https://github.com/donatj/PhpUserAgent/issues) page. 6 | 7 | - **Detail is key**: If a browser is being misidentified, one or more sample user agent strings are key to getting it resolved. 8 | - **Missing Browser**: Is it modern? What is it being misidentified as? There are a lot of dead browsers out there that there is no reason to support. 9 | 10 | Please do not file any requests for OS version identification. It is not a desired feature. 11 | 12 | ## Pull Requests 13 | 14 | Pull requests are truly appreciated. While I try my best to stay on top of browsers hitting the market it is still a difficult task. 15 | 16 | - **Formatting**: Indentation **must** use tabs. Please try to match internal formatting and spacing to existing code. 17 | - **Tests**: If you're adding support for a new browser be sure to add test user agents for if at all possible ***every platform*** the browser is available on. Untested code will take much longer to be merged. 18 | - **Terseness**: Try to be terse. Be clever. Take up as little space as possible. The point of this project initially was to be smaller than the other guys. 19 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/run-tests.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/usr/bin/env bash 3 | gpg --fingerprint D8406D0D82947747293778314AA394086372C20A 4 | if [ $? -ne 0 ]; then 5 | echo -e "\033[33mDownloading PGP Public Key...\033[0m" 6 | gpg --recv-keys D8406D0D82947747293778314AA394086372C20A 7 | # Sebastian Bergmann 8 | gpg --fingerprint D8406D0D82947747293778314AA394086372C20A 9 | if [ $? -ne 0 ]; then 10 | echo -e "\033[31mCould not download PGP public key for verification\033[0m" 11 | exit 12 | fi 13 | fi 14 | 15 | # Let's grab the latest release and its signature 16 | if [ ! -f phpunit.phar ]; then 17 | wget https://phar.phpunit.de/phpunit.phar 18 | fi 19 | if [ ! -f phpunit.phar.asc ]; then 20 | wget https://phar.phpunit.de/phpunit.phar.asc 21 | fi 22 | 23 | # Verify before running 24 | gpg --verify phpunit.phar.asc phpunit.phar 25 | if [ $? -eq 0 ]; then 26 | echo 27 | echo -e "\033[33mBegin Unit Testing\033[0m" 28 | # Run the testing suite 29 | php --version 30 | php phpunit.phar --configuration phpunit.xml.dist 31 | else 32 | echo 33 | chmod -x phpunit.phar 34 | mv phpunit.phar /tmp/bad-phpunit.phar 35 | mv phpunit.phar.asc /tmp/bad-phpunit.phar.asc 36 | echo -e "\033[31mSignature did not match! PHPUnit has been moved to /tmp/bad-phpunit.phar\033[0m" 37 | exit 1 38 | fi 39 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/Tests/UserAgentParserTest.php: -------------------------------------------------------------------------------- 1 | $expected_result) { 12 | $result = parse_user_agent($ua); 13 | $this->assertSame($expected_result, $result, $ua . " failed!" ); 14 | } 15 | 16 | } 17 | 18 | function test_pase_user_agent_empty() { 19 | $expected = array( 20 | 'platform' => null, 21 | 'browser' => null, 22 | 'version' => null, 23 | ); 24 | 25 | $result = parse_user_agent(''); 26 | $this->assertSame($result, $expected); 27 | 28 | // $result = parse_user_agent('asdjkakljasdkljasdlkj'); 29 | // $this->assertEquals($result, $expected); 30 | 31 | $result = parse_user_agent('Mozilla (asdjkakljasdkljasdlkj) BlahBlah'); 32 | $this->assertSame($result, $expected); 33 | } 34 | 35 | 36 | /** 37 | * @expectedException \InvalidArgumentException 38 | */ 39 | function test_no_user_agent_exception() { 40 | unset($_SERVER['HTTP_USER_AGENT']); 41 | parse_user_agent(); 42 | } 43 | 44 | function test_global_user_agent(){ 45 | $_SERVER['HTTP_USER_AGENT'] = 'Test/1.0'; 46 | $this->assertSame(array('platform'=>null, 'browser' => 'Test', 'version' => '1.0'), parse_user_agent()); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | === Simple JWT Authentication === 2 | Contributors: jonathan-dejong 3 | Donate link: http://fancy.to/scbk86 4 | Tags: wp-rest, api, jwt, authentication, access 5 | Requires at least: 3.0.1 6 | Tested up to: 4.8 7 | Stable tag: 1.4.0 8 | License: GPLv3 or later 9 | License URI: http://www.gnu.org/licenses/gpl-3.0.html 10 | 11 | Easily extends the WP REST API using JSON Web Tokens Authentication as an authentication method. Including ways to revoke access for users at any time. 12 | 13 | == Description == 14 | 15 | 16 | 17 | == Installation == 18 | 19 | 1. Upload `simple-jwt-authentication` to the `/wp-content/plugins/` directory 20 | 2. Activate the plugin through the 'Plugins' menu in WordPress 21 | 3. Go to the settings page under Settings > Simple JWT Authentication to setup the plugin. 22 | 23 | == Changelog == 24 | = 1.4 = 25 | * Security fix - Not showing the secret key in WP admin if set as a constant. Thank you [JanThiel](https://github.com/JanThiel) for making me aware of this. 26 | * Added the user data to the expire filter to allow for user specific expire times. 27 | * Added a whole bunch of escaping and security improvements like nonces etc. Basically making the plugin follow WordPress-Extra standard instead of previous WordPress-Core. 28 | * Bugfix - Fixed some issues with the token UI in profile pages. Thanks to [gordielachance](https://github.com/gordielachance) for making me aware of this. 29 | 30 | = 1.3 = 31 | * Merged PR allowing to refresh a token. Thanks to Qazsero@github. 32 | 33 | = 1.0 = 34 | * Initial version. 35 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Neuman Vong 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials provided 14 | with the distribution. 15 | 16 | * Neither the name of Neuman Vong nor the names of other 17 | contributors may be used to endorse or promote products derived 18 | from this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /includes/class-simple-jwt-authentication-api.php: -------------------------------------------------------------------------------- 1 | &$val ) { 10 | $val['key'] = $key; 11 | } 12 | 13 | uasort($uas, function ( $a, $b ) { 14 | 15 | if($a['platform'] === null && $b['platform'] !== null) return 1; 16 | if($b['platform'] === null && $a['platform'] !== null) return -1; 17 | 18 | $desktop = array( 'Windows', 'Linux', 'Macintosh', 'Chrome OS' ); 19 | 20 | $ad = in_array($a['platform'], $desktop); 21 | $bd = in_array($b['platform'], $desktop); 22 | 23 | if( !$ad && $bd ) return 1; 24 | if( $ad && !$bd ) return -1; 25 | 26 | if( $ad ) { 27 | $result = strnatcasecmp($a['browser'], $b['browser']); 28 | if( $result == 0 ) { 29 | 30 | $result = strnatcasecmp($a['platform'], $b['platform']); 31 | 32 | if( $result == 0 ) { 33 | $result = compare_version($a['version'], $b['version']); 34 | } 35 | 36 | } 37 | } else { 38 | $result = strnatcasecmp($a['platform'], $b['platform']); 39 | if( $result == 0 ) { 40 | 41 | $result = strnatcasecmp($a['browser'], $b['browser']); 42 | 43 | if( $result == 0 ) { 44 | $result = compare_version($a['version'], $b['version']); 45 | } 46 | 47 | } 48 | } 49 | 50 | if( $result == 0 ) { 51 | $result = strnatcasecmp($a['key'], $b['key']); 52 | } 53 | 54 | return $result; 55 | }); 56 | 57 | foreach( $uas as &$val ) { 58 | unset($val['key']); 59 | } 60 | 61 | $jsonPretty = new Camspiers\JsonPretty\JsonPretty; 62 | echo $jsonPretty->prettify($uas); 63 | file_put_contents($jsonfile, $jsonPretty->prettify($uas)); 64 | 65 | 66 | function compare_version( $a, $b ) { 67 | $cmp_a = explode('.', $a); 68 | $cmp_b = explode('.', $b); 69 | 70 | $max = max(count($cmp_a), count($cmp_b)); 71 | 72 | $value = 0; 73 | 74 | for( $i = 0; $i < $max; $i++ ) { 75 | $aa = strtolower(isset($cmp_a[$i]) ? $cmp_a[$i] : '0'); 76 | $bb = strtolower(isset($cmp_b[$i]) ? $cmp_b[$i] : '0'); 77 | 78 | if( is_numeric($aa) && is_numeric($bb) ) { 79 | if( $aa != $bb ) { 80 | $value = ($aa > $bb ? 1 : -1); 81 | break; 82 | } 83 | } else if( $cmp = strcmp($aa, $bb) ) { 84 | $value = $cmp / abs($cmp); 85 | break; 86 | } 87 | } 88 | 89 | return $value; 90 | } -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | JWT 7 | pear.php.net 8 | A JWT encoder/decoder. 9 | A JWT encoder/decoder library for PHP. 10 | 11 | Neuman Vong 12 | lcfrs 13 | neuman+pear@twilio.com 14 | yes 15 | 16 | 17 | Firebase Operations 18 | firebase 19 | operations@firebase.com 20 | yes 21 | 22 | 2015-07-22 23 | 24 | 3.0.0 25 | 3.0.0 26 | 27 | 28 | beta 29 | beta 30 | 31 | BSD 3-Clause License 32 | 33 | Initial release with basic support for JWT encoding, decoding and signature verification. 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 5.1 47 | 48 | 49 | 1.7.0 50 | 51 | 52 | json 53 | 54 | 55 | hash 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 0.1.0 64 | 0.1.0 65 | 66 | 67 | beta 68 | beta 69 | 70 | 2015-04-01 71 | BSD 3-Clause License 72 | 73 | Initial release with basic support for JWT encoding, decoding and signature verification. 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /includes/vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInit89ab54d4543c163039c2cf5235fd7e1a::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | if ($useStaticLoader) { 51 | $includeFiles = Composer\Autoload\ComposerStaticInit89ab54d4543c163039c2cf5235fd7e1a::$files; 52 | } else { 53 | $includeFiles = require __DIR__ . '/autoload_files.php'; 54 | } 55 | foreach ($includeFiles as $fileIdentifier => $file) { 56 | composerRequire89ab54d4543c163039c2cf5235fd7e1a($fileIdentifier, $file); 57 | } 58 | 59 | return $loader; 60 | } 61 | } 62 | 63 | function composerRequire89ab54d4543c163039c2cf5235fd7e1a($fileIdentifier, $file) 64 | { 65 | if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { 66 | require $file; 67 | 68 | $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /simple-jwt-authentication.php: -------------------------------------------------------------------------------- 1 | plugin_name = 'simple-jwt-authentication'; 36 | $this->plugin_version = '1.4.0'; 37 | 38 | // Load all dependency files. 39 | $this->load_dependencies(); 40 | 41 | // Activation hook 42 | register_activation_hook( __FILE__, array( $this, 'activate' ) ); 43 | 44 | // Deactivation hook 45 | register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) ); 46 | 47 | // Localization 48 | add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) ); 49 | 50 | } 51 | 52 | /** 53 | * Loads all dependencies in our plugin. 54 | * 55 | * @since 1.0 56 | */ 57 | public function load_dependencies() { 58 | 59 | // Load all Composer dependencies 60 | $this->include_file( 'vendor/autoload.php' ); 61 | $this->include_file( 'class-simple-jwt-authentication-api.php' ); 62 | 63 | // Admin specific includes 64 | if ( is_admin() ) { 65 | $this->include_file( 'admin/class-simple-jwt-authentication-settings.php' ); 66 | $this->include_file( 'admin/class-simple-jwt-authentication-profile.php' ); 67 | } 68 | 69 | $this->include_file( 'class-simple-jwt-authentication-rest.php' ); 70 | 71 | } 72 | 73 | /** 74 | * Includes a single file located inside /includes. 75 | * 76 | * @param string $path relative path to /includes 77 | * @since 1.0 78 | */ 79 | private function include_file( $path ) { 80 | $plugin_name = $this->plugin_name; 81 | $plugin_version = $this->plugin_version; 82 | 83 | $includes_dir = trailingslashit( plugin_dir_path( __FILE__ ) . 'includes' ); 84 | if ( file_exists( $includes_dir . $path ) ) { 85 | include_once $includes_dir . $path; 86 | } 87 | } 88 | 89 | /** 90 | * The code that runs during plugin activation. 91 | * 92 | * @since 1.0 93 | */ 94 | public function activate() { 95 | 96 | } 97 | 98 | /** 99 | * The code that runs during plugin deactivation. 100 | * 101 | * @since 1.0 102 | */ 103 | public function deactivate() { 104 | 105 | } 106 | 107 | /** 108 | * Load the plugin text domain for translation. 109 | * 110 | * @since 1.0 111 | */ 112 | public function load_textdomain() { 113 | 114 | load_plugin_textdomain( 115 | 'simple-jwt-authentication', 116 | false, 117 | basename( dirname( __FILE__ ) ) . '/languages/' 118 | ); 119 | 120 | } 121 | 122 | } 123 | 124 | /** 125 | * Begins execution of the plugin. 126 | * 127 | * @since 1.0 128 | */ 129 | new Simple_Jwt_Authentication(); 130 | -------------------------------------------------------------------------------- /includes/admin/views/user-token-ui.php: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 19 | 20 | $token['uuid'], 26 | ), 27 | $current_url 28 | ), 29 | 'simple-jwt-ui-nonce', 30 | 'jwt_nonce' 31 | ); 32 | ?> 33 | 34 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
Platform %1$s. Browser: %2$s. Browser version: %3$s', 'simple-jwt-authentication' ), esc_html( $ua_info['platform'] ), esc_html( $ua_info['browser'] ), esc_html( $ua_info['version'] ) ); // phpcs:ignore ?> 40 | 41 |
46 | 47 | 48 |
57 | -------------------------------------------------------------------------------- /includes/vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "firebase/php-jwt", 4 | "version": "v3.0.0", 5 | "version_normalized": "3.0.0.0", 6 | "source": { 7 | "type": "git", 8 | "url": "https://github.com/firebase/php-jwt.git", 9 | "reference": "fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1" 10 | }, 11 | "dist": { 12 | "type": "zip", 13 | "url": "https://api.github.com/repos/firebase/php-jwt/zipball/fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1", 14 | "reference": "fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1", 15 | "shasum": "" 16 | }, 17 | "require": { 18 | "php": ">=5.3.0" 19 | }, 20 | "time": "2015-07-22T18:31:08+00:00", 21 | "type": "library", 22 | "installation-source": "dist", 23 | "autoload": { 24 | "psr-4": { 25 | "Firebase\\JWT\\": "src" 26 | } 27 | }, 28 | "notification-url": "https://packagist.org/downloads/", 29 | "license": [ 30 | "BSD-3-Clause" 31 | ], 32 | "authors": [ 33 | { 34 | "name": "Neuman Vong", 35 | "email": "neuman+pear@twilio.com", 36 | "role": "Developer" 37 | }, 38 | { 39 | "name": "Anant Narayanan", 40 | "email": "anant@php.net", 41 | "role": "Developer" 42 | } 43 | ], 44 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 45 | "homepage": "https://github.com/firebase/php-jwt" 46 | }, 47 | { 48 | "name": "donatj/phpuseragentparser", 49 | "version": "v0.7.0", 50 | "version_normalized": "0.7.0.0", 51 | "source": { 52 | "type": "git", 53 | "url": "https://github.com/donatj/PhpUserAgent.git", 54 | "reference": "73c819d4c7de034f1bfb7c7a36259192d7d7472a" 55 | }, 56 | "dist": { 57 | "type": "zip", 58 | "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/73c819d4c7de034f1bfb7c7a36259192d7d7472a", 59 | "reference": "73c819d4c7de034f1bfb7c7a36259192d7d7472a", 60 | "shasum": "" 61 | }, 62 | "require": { 63 | "php": ">=5.3.0" 64 | }, 65 | "require-dev": { 66 | "camspiers/json-pretty": "0.1.*", 67 | "donatj/drop": "*", 68 | "phpunit/phpunit": ">=4.8" 69 | }, 70 | "time": "2017-03-01T22:19:13+00:00", 71 | "type": "library", 72 | "installation-source": "dist", 73 | "autoload": { 74 | "files": [ 75 | "Source/UserAgentParser.php" 76 | ] 77 | }, 78 | "notification-url": "https://packagist.org/downloads/", 79 | "license": [ 80 | "MIT" 81 | ], 82 | "authors": [ 83 | { 84 | "name": "Jesse G. Donat", 85 | "email": "donatj@gmail.com", 86 | "homepage": "http://donatstudios.com", 87 | "role": "Developer" 88 | } 89 | ], 90 | "description": "Simple, streamlined PHP user-agent parser", 91 | "homepage": "http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT", 92 | "keywords": [ 93 | "browser", 94 | "browser detection", 95 | "parser", 96 | "user agent", 97 | "useragent" 98 | ] 99 | } 100 | ] 101 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/README.md: -------------------------------------------------------------------------------- 1 | # PHP User Agent Parser 2 | 3 | [![Latest Stable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/stable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Total Downloads](https://poser.pugx.org/donatj/phpuseragentparser/downloads.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![Latest Unstable Version](https://poser.pugx.org/donatj/phpuseragentparser/v/unstable.png)](https://packagist.org/packages/donatj/phpuseragentparser) [![License](https://poser.pugx.org/donatj/phpuseragentparser/license.png)](https://packagist.org/packages/donatj/phpuseragentparser) 4 | [![Build Status](https://travis-ci.org/donatj/PhpUserAgent.png?branch=master)](https://travis-ci.org/donatj/PhpUserAgent) 5 | [![HHVM Status](http://hhvm.h4cc.de/badge/donatj/phpuseragentparser.png?style=flat)](http://hhvm.h4cc.de/package/donatj/phpuseragentparser) 6 | 7 | ## What It Is 8 | 9 | A simple, streamlined PHP user-agent parser! 10 | 11 | Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 12 | 13 | 14 | ## Why Use This 15 | 16 | You have your choice in user-agent parsers. This one detects **all modern browsers** in a very light, quick, understandable fashion. 17 | It is less than 200 lines of code, and consists of just three regular expressions! 18 | It can also correctly identify exotic versions of IE others fail on. 19 | 20 | It offers 100% unit test coverage, is installable via Composer, and is very easy to use. 21 | 22 | ## What It Does Not Do 23 | 24 | ### OS Versions 25 | 26 | User-agent strings **are not** a reliable source of OS Version! 27 | 28 | - Many agents simply don't send the information. 29 | - Others provide varying levels of accuracy. 30 | - Parsing Windows versions alone almost nearly doubles the size of the code. 31 | 32 | I'm much more interested in keeping this thing *tiny* and accurate than adding niché features and would rather focus on things that can be **done well**. 33 | 34 | All that said, there is the start of a [branch to do it](https://github.com/donatj/PhpUserAgent/tree/os_version_detection) I created for a client if you want to poke it, I update it from time to time, but frankly if you need to *reliably detect OS Version*, using user-agent isn't the way to do it. I'd go with JavaScript. 35 | 36 | ## Requirements 37 | 38 | - PHP 5.3.0+ 39 | 40 | ## Installing 41 | 42 | PHP User Agent is available through Packagist via Composer. 43 | 44 | ```json 45 | { 46 | "require": { 47 | "donatj/phpuseragentparser": "*" 48 | } 49 | } 50 | ``` 51 | 52 | ## Sample Usage 53 | 54 | ```php 55 | $ua_info = parse_user_agent(); 56 | /* 57 | array( 58 | 'platform' => '[Detected Platform]', 59 | 'browser' => '[Detected Browser]', 60 | 'version' => '[Detected Browser Version]', 61 | ); 62 | */ 63 | ``` 64 | 65 | ## Currently Detected Platforms 66 | 67 | - Desktop 68 | - Windows 69 | - Linux 70 | - Macintosh 71 | - Chrome OS 72 | - Mobile 73 | - Android 74 | - iPhone 75 | - iPad / iPod Touch 76 | - Windows Phone OS 77 | - Kindle 78 | - Kindle Fire 79 | - BlackBerry 80 | - Playbook 81 | - Tizen 82 | - Console 83 | - Nintendo 3DS 84 | - New Nintendo 3DS 85 | - Nintendo Wii 86 | - Nintendo WiiU 87 | - PlayStation 3 88 | - PlayStation 4 89 | - PlayStation Vita 90 | - Xbox 360 91 | - Xbox One 92 | 93 | ## Currently Detected Browsers 94 | 95 | - Android Browser 96 | - BlackBerry Browser 97 | - Camino 98 | - Kindle / Silk 99 | - Firefox / IceWeasel / IceCat 100 | - Safari 101 | - Internet Explorer 102 | - IEMobile 103 | - Chrome 104 | - Opera 105 | - Midori 106 | - Vivaldi 107 | - TizenBrowser 108 | - SamsungBrowser 109 | - UC Browser 110 | - Lynx 111 | - Wget 112 | - Curl 113 | - Puffin 114 | 115 | 116 | 117 | More information is available at [Donat Studios](http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT). 118 | -------------------------------------------------------------------------------- /includes/admin/class-simple-jwt-authentication-settings.php: -------------------------------------------------------------------------------- 1 | plugin_name = $plugin_name; 21 | $this->plugin_version = $plugin_version; 22 | 23 | add_action( 'admin_menu', array( $this, 'add_admin_menu' ) ); 24 | add_action( 'admin_init', array( $this, 'settings_init' ) ); 25 | 26 | } 27 | 28 | 29 | /** 30 | * Adds the menu page to options. 31 | * 32 | * @since 1.0 33 | */ 34 | public function add_admin_menu() { 35 | add_options_page( 36 | 'Simple JWT Authentication', 37 | 'Simple JWT Authentication', 38 | 'manage_options', 39 | 'simple_jwt_authentication', 40 | array( $this, 'simple_jwt_authentication_options_page' ) 41 | ); 42 | 43 | } 44 | 45 | 46 | /** 47 | * Initialize all settings. 48 | * 49 | * @since 1.0 50 | */ 51 | public function settings_init() { 52 | register_setting( 'simple_jwt_authentication', 'simple_jwt_authentication_settings' ); 53 | 54 | add_settings_section( 55 | 'simple_jwt_authentication_section', 56 | __( 'Basic configuration', 'simple-jwt-authentication' ), 57 | array( $this, 'settings_section_callback' ), 58 | 'simple_jwt_authentication' 59 | ); 60 | 61 | add_settings_field( 62 | 'secret_key', 63 | __( 'Secret Key', 'simple-jwt-authentication' ), 64 | array( $this, 'settings_secret_callback' ), 65 | 'simple_jwt_authentication', 66 | 'simple_jwt_authentication_section' 67 | ); 68 | 69 | add_settings_field( 70 | 'enable_cors', 71 | // translators: %s is a link to CORS docs. 72 | sprintf( __( 'Enable %s', 'simple-jwt-authentication' ), 'CORS' ), 73 | array( $this, 'settings_cors_callback' ), 74 | 'simple_jwt_authentication', 75 | 'simple_jwt_authentication_section' 76 | ); 77 | 78 | } 79 | 80 | 81 | /** 82 | * Secret key field callback. 83 | * 84 | * @since 1.0 85 | */ 86 | public function settings_secret_callback() { 87 | $secret_key = Simple_Jwt_Authentication_Api::get_key(); 88 | $is_global = Simple_Jwt_Authentication_Api::is_global( 'SIMPLE_JWT_AUTHENTICATION_SECRET_KEY' ); 89 | include plugin_dir_path( __FILE__ ) . 'views/settings/secret-key.php'; 90 | 91 | } 92 | 93 | 94 | /** 95 | * Enable/disable cors field callback. 96 | * 97 | * @since 1.0 98 | */ 99 | public function settings_cors_callback() { 100 | $enable_cors = Simple_Jwt_Authentication_Api::get_cors(); 101 | $is_global = Simple_Jwt_Authentication_Api::is_global( 'SIMPLE_JWT_AUTHENTICATION_CORS_ENABLE' ); 102 | include plugin_dir_path( __FILE__ ) . 'views/settings/enable-cors.php'; 103 | 104 | } 105 | 106 | 107 | /** 108 | * Section callback. 109 | * 110 | * @since 1.0 111 | */ 112 | public function settings_section_callback() { 113 | echo sprintf( __( 'This is all you need to start using JWT authentication.
You can also specify these in wp-config.php instead using %1$s %2$s', 'simple-jwt-authentication' ), "

define( 'SIMPLE_JWT_AUTHENTICATION_SECRET_KEY', YOURKEY );", "

define( 'SIMPLE_JWT_AUTHENTICATION_CORS_ENABLE', true );" ); // phpcs:ignore 114 | 115 | } 116 | 117 | 118 | /** 119 | * Settings form callback. 120 | * 121 | * @since 1.0 122 | */ 123 | public function simple_jwt_authentication_options_page() { 124 | include plugin_dir_path( __FILE__ ) . 'views/settings/page.php'; 125 | 126 | } 127 | 128 | 129 | } 130 | 131 | new Simple_Jwt_Authentication_Settings( $plugin_name, $plugin_version ); 132 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt) 2 | [![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt) 3 | [![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt) 4 | [![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt) 5 | 6 | PHP-JWT 7 | ======= 8 | A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should 9 | conform to the [current spec](http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-06) 10 | 11 | Installation 12 | ------------ 13 | 14 | Use composer to manage your dependencies and download PHP-JWT: 15 | 16 | ```bash 17 | composer require firebase/php-jwt 18 | ``` 19 | 20 | Example 21 | ------- 22 | ```php 23 | "http://example.org", 29 | "aud" => "http://example.com", 30 | "iat" => 1356999524, 31 | "nbf" => 1357000000 32 | ); 33 | 34 | /** 35 | * IMPORTANT: 36 | * You must specify supported algorithms for your application. See 37 | * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 38 | * for a list of spec-compliant algorithms. 39 | */ 40 | $jwt = JWT::encode($token, $key); 41 | $decoded = JWT::decode($jwt, $key, array('HS256')); 42 | 43 | print_r($decoded); 44 | 45 | /* 46 | NOTE: This will now be an object instead of an associative array. To get 47 | an associative array, you will need to cast it as such: 48 | */ 49 | 50 | $decoded_array = (array) $decoded; 51 | 52 | /** 53 | * You can add a leeway to account for when there is a clock skew times between 54 | * the signing and verifying servers. It is recommended that this leeway should 55 | * not be bigger than a few minutes. 56 | * 57 | * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef 58 | */ 59 | JWT::$leeway = 60; // $leeway in seconds 60 | $decoded = JWT::decode($jwt, $key, array('HS256')); 61 | 62 | ?> 63 | ``` 64 | 65 | Changelog 66 | --------- 67 | 68 | #### 3.0.0 / 2015-07-22 69 | - Minimum PHP version updated from `5.2.0` to `5.3.0`. 70 | - Add `\Firebase\JWT` namespace. See 71 | [#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to 72 | [@Dashron](https://github.com/Dashron)! 73 | - Require a non-empty key to decode and verify a JWT. See 74 | [#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to 75 | [@sjones608](https://github.com/sjones608)! 76 | - Cleaner documentation blocks in the code. See 77 | [#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to 78 | [@johanderuijter](https://github.com/johanderuijter)! 79 | 80 | #### 2.2.0 / 2015-06-22 81 | - Add support for adding custom, optional JWT headers to `JWT::encode()`. See 82 | [#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to 83 | [@mcocaro](https://github.com/mcocaro)! 84 | 85 | #### 2.1.0 / 2015-05-20 86 | - Add support for adding a leeway to `JWT:decode()` that accounts for clock skew 87 | between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)! 88 | - Add support for passing an object implementing the `ArrayAccess` interface for 89 | `$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)! 90 | 91 | #### 2.0.0 / 2015-04-01 92 | - **Note**: It is strongly recommended that you update to > v2.0.0 to address 93 | known security vulnerabilities in prior versions when both symmetric and 94 | asymmetric keys are used together. 95 | - Update signature for `JWT::decode(...)` to require an array of supported 96 | algorithms to use when verifying token signatures. 97 | 98 | 99 | Tests 100 | ----- 101 | Run the tests using phpunit: 102 | 103 | ```bash 104 | $ pear install PHPUnit 105 | $ phpunit --configuration phpunit.xml.dist 106 | PHPUnit 3.7.10 by Sebastian Bergmann. 107 | ..... 108 | Time: 0 seconds, Memory: 2.50Mb 109 | OK (5 tests, 5 assertions) 110 | ``` 111 | 112 | License 113 | ------- 114 | [3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause). 115 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "9a2ed9c13775e608cc90222d627a0479", 8 | "packages": [ 9 | { 10 | "name": "donatj/phpuseragentparser", 11 | "version": "v0.7.0", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/donatj/PhpUserAgent.git", 15 | "reference": "73c819d4c7de034f1bfb7c7a36259192d7d7472a" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/73c819d4c7de034f1bfb7c7a36259192d7d7472a", 20 | "reference": "73c819d4c7de034f1bfb7c7a36259192d7d7472a", 21 | "shasum": "" 22 | }, 23 | "require": { 24 | "php": ">=5.3.0" 25 | }, 26 | "require-dev": { 27 | "camspiers/json-pretty": "0.1.*", 28 | "donatj/drop": "*", 29 | "phpunit/phpunit": ">=4.8" 30 | }, 31 | "type": "library", 32 | "autoload": { 33 | "files": [ 34 | "Source/UserAgentParser.php" 35 | ] 36 | }, 37 | "notification-url": "https://packagist.org/downloads/", 38 | "license": [ 39 | "MIT" 40 | ], 41 | "authors": [ 42 | { 43 | "name": "Jesse G. Donat", 44 | "email": "donatj@gmail.com", 45 | "homepage": "http://donatstudios.com", 46 | "role": "Developer" 47 | } 48 | ], 49 | "description": "Simple, streamlined PHP user-agent parser", 50 | "homepage": "http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT", 51 | "keywords": [ 52 | "browser", 53 | "browser detection", 54 | "parser", 55 | "user agent", 56 | "useragent" 57 | ], 58 | "time": "2017-03-01T22:19:13+00:00" 59 | }, 60 | { 61 | "name": "firebase/php-jwt", 62 | "version": "v3.0.0", 63 | "source": { 64 | "type": "git", 65 | "url": "https://github.com/firebase/php-jwt.git", 66 | "reference": "fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1" 67 | }, 68 | "dist": { 69 | "type": "zip", 70 | "url": "https://api.github.com/repos/firebase/php-jwt/zipball/fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1", 71 | "reference": "fa8a06e96526eb7c0eeaa47e4f39be59d21f16e1", 72 | "shasum": "" 73 | }, 74 | "require": { 75 | "php": ">=5.3.0" 76 | }, 77 | "type": "library", 78 | "autoload": { 79 | "psr-4": { 80 | "Firebase\\JWT\\": "src" 81 | } 82 | }, 83 | "notification-url": "https://packagist.org/downloads/", 84 | "license": [ 85 | "BSD-3-Clause" 86 | ], 87 | "authors": [ 88 | { 89 | "name": "Neuman Vong", 90 | "email": "neuman+pear@twilio.com", 91 | "role": "Developer" 92 | }, 93 | { 94 | "name": "Anant Narayanan", 95 | "email": "anant@php.net", 96 | "role": "Developer" 97 | } 98 | ], 99 | "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", 100 | "homepage": "https://github.com/firebase/php-jwt", 101 | "time": "2015-07-22T18:31:08+00:00" 102 | } 103 | ], 104 | "packages-dev": [], 105 | "aliases": [], 106 | "minimum-stability": "stable", 107 | "stability-flags": [], 108 | "prefer-stable": false, 109 | "prefer-lowest": false, 110 | "platform": [], 111 | "platform-dev": [] 112 | } 113 | -------------------------------------------------------------------------------- /languages/simple-jwt-authentication.pot: -------------------------------------------------------------------------------- 1 | #, fuzzy 2 | msgid "" 3 | msgstr "" 4 | "Project-Id-Version: PACKAGE VERSION\n" 5 | "Report-Msgid-Bugs-To: \n" 6 | "POT-Creation-Date: 2017-10-03 07:43+0000\n" 7 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 8 | "Last-Translator: FULL NAME \n" 9 | "Language-Team: \n" 10 | "Language: \n" 11 | "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "X-Generator: Loco - https://localise.biz/" 16 | 17 | #: includes/class-simple-jwt-authentication-api.php:16 18 | #: includes/class-simple-jwt-authentication-rest.php:310 19 | msgid "Unknown" 20 | msgstr "" 21 | 22 | #: includes/class-simple-jwt-authentication-rest.php:97 23 | #: includes/class-simple-jwt-authentication-rest.php:263 24 | msgid "" 25 | "JWT is not configurated properly, please contact the admin. The key is " 26 | "missing." 27 | msgstr "" 28 | 29 | #: includes/class-simple-jwt-authentication-rest.php:236 30 | msgid "Authorization header not found." 31 | msgstr "" 32 | 33 | #: includes/class-simple-jwt-authentication-rest.php:251 34 | msgid "Authorization header malformed." 35 | msgstr "" 36 | 37 | #: includes/class-simple-jwt-authentication-rest.php:278 38 | msgid "The iss do not match with this server" 39 | msgstr "" 40 | 41 | #: includes/class-simple-jwt-authentication-rest.php:288 42 | msgid "User ID not found in the token" 43 | msgstr "" 44 | 45 | #: includes/class-simple-jwt-authentication-rest.php:300 46 | #: includes/class-simple-jwt-authentication-rest.php:324 47 | msgid "Token has been revoked." 48 | msgstr "" 49 | 50 | #: includes/class-simple-jwt-authentication-rest.php:413 51 | msgid "Error: Username or email not specified." 52 | msgstr "" 53 | 54 | #: includes/class-simple-jwt-authentication-rest.php:430 55 | msgid "Error: Invalid username." 56 | msgstr "" 57 | 58 | #: includes/class-simple-jwt-authentication-rest.php:449 59 | #: includes/class-simple-jwt-authentication-rest.php:457 60 | msgid "Error: Resetting password is not allowed." 61 | msgstr "" 62 | 63 | #: includes/class-simple-jwt-authentication-rest.php:506 64 | msgid "" 65 | "Success: an email for selecting a new password has been " 66 | "sent." 67 | msgstr "" 68 | 69 | #: includes/admin/class-simple-jwt-authentication-profile.php:44 70 | msgid "All tokens have been revoked." 71 | msgstr "" 72 | 73 | #: includes/admin/class-simple-jwt-authentication-profile.php:46 74 | #, php-format 75 | msgid "The token %s has been revoked." 76 | msgstr "" 77 | 78 | #: includes/admin/class-simple-jwt-authentication-profile.php:48 79 | msgid "All expired tokens have been removed." 80 | msgstr "" 81 | 82 | #: includes/admin/class-simple-jwt-authentication-settings.php:56 83 | msgid "Basic configuration" 84 | msgstr "" 85 | 86 | #: includes/admin/class-simple-jwt-authentication-settings.php:63 87 | msgid "Secret Key" 88 | msgstr "" 89 | 90 | #: includes/admin/class-simple-jwt-authentication-settings.php:71 91 | #, php-format 92 | msgid "Enable %s" 93 | msgstr "" 94 | 95 | #: includes/admin/class-simple-jwt-authentication-settings.php:112 96 | #, php-format 97 | msgid "" 98 | "This is all you need to start using JWT authentication.
You can also " 99 | "specify these in wp-config.php instead using %1$s %2$s" 100 | msgstr "" 101 | 102 | #: includes/admin/views/user-token-ui.php:1 103 | msgid "Simple JWT Authentication API Tokens" 104 | msgstr "" 105 | 106 | #: includes/admin/views/user-token-ui.php:5 107 | msgid "Token UUID" 108 | msgstr "" 109 | 110 | #: includes/admin/views/user-token-ui.php:6 111 | msgid "Expires" 112 | msgstr "" 113 | 114 | #: includes/admin/views/user-token-ui.php:7 115 | msgid "Last used" 116 | msgstr "" 117 | 118 | #: includes/admin/views/user-token-ui.php:8 119 | msgid "By IP" 120 | msgstr "" 121 | 122 | #: includes/admin/views/user-token-ui.php:9 123 | msgid "Browser" 124 | msgstr "" 125 | 126 | #: includes/admin/views/user-token-ui.php:30 127 | msgid "Lookup" 128 | msgstr "" 129 | 130 | #: includes/admin/views/user-token-ui.php:31 131 | #, php-format 132 | msgid "" 133 | "Platform %1$s. Browser: %2$s. " 134 | "Browser version: %3$s" 135 | msgstr "" 136 | 137 | #: includes/admin/views/user-token-ui.php:33 138 | msgid "Revokes this token from being used any further." 139 | msgstr "" 140 | 141 | #: includes/admin/views/user-token-ui.php:33 142 | msgid "Revoke" 143 | msgstr "" 144 | 145 | #: includes/admin/views/user-token-ui.php:39 146 | msgid "Doing this will require the user to login again on all devices." 147 | msgstr "" 148 | 149 | #: includes/admin/views/user-token-ui.php:39 150 | msgid "Revoke all tokens" 151 | msgstr "" 152 | 153 | #: includes/admin/views/user-token-ui.php:40 154 | msgid "Doing this will not affect logged in devices for this user." 155 | msgstr "" 156 | 157 | #: includes/admin/views/user-token-ui.php:40 158 | msgid "Remove all expired tokens" 159 | msgstr "" 160 | 161 | #: includes/admin/views/user-token-ui.php:45 162 | msgid "No tokens generated." 163 | msgstr "" 164 | 165 | #: includes/admin/views/settings/enable-cors.php:4 166 | #: includes/admin/views/settings/secret-key.php:4 167 | msgid "Defined in wp-config.php" 168 | msgstr "" 169 | 170 | #: includes/admin/views/settings/page.php:8 171 | msgid "Getting started" 172 | msgstr "" 173 | 174 | #: includes/admin/views/settings/page.php:10 175 | #, php-format 176 | msgid "" 177 | "To get started check out the documentation" 179 | msgstr "" 180 | 181 | #: includes/admin/views/settings/secret-key.php:6 182 | msgid "Should be a long string of letters, numbers and symbols." 183 | msgstr "" 184 | 185 | #. Name of the plugin 186 | msgid "Simple JWT Authentication" 187 | msgstr "" 188 | 189 | #. Description of the plugin 190 | msgid "" 191 | "Extends the WP REST API using JSON Web Tokens Authentication as an " 192 | "authentication method." 193 | msgstr "" 194 | 195 | #. URI of the plugin 196 | msgid "http://github.com/jonathan-dejong/simple-jwt-authentication" 197 | msgstr "" 198 | 199 | #. Author of the plugin 200 | msgid "Jonathan de Jong" 201 | msgstr "" 202 | 203 | #. Author URI of the plugin 204 | msgid "http://github.com/jonathan-dejong" 205 | msgstr "" 206 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/Source/UserAgentParser.php: -------------------------------------------------------------------------------- 1 | 7 | * @link https://github.com/donatj/PhpUserAgent 8 | * @link http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT 9 | * @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL 10 | * @throws \InvalidArgumentException on not having a proper user agent to parse. 11 | * @return string[] an array with browser, version and platform keys 12 | */ 13 | function parse_user_agent( $u_agent = null ) { 14 | if( is_null($u_agent) ) { 15 | if( isset($_SERVER['HTTP_USER_AGENT']) ) { 16 | $u_agent = $_SERVER['HTTP_USER_AGENT']; 17 | } else { 18 | throw new \InvalidArgumentException('parse_user_agent requires a user agent'); 19 | } 20 | } 21 | 22 | $platform = null; 23 | $browser = null; 24 | $version = null; 25 | 26 | $empty = array( 'platform' => $platform, 'browser' => $browser, 'version' => $version ); 27 | 28 | if( !$u_agent ) return $empty; 29 | 30 | if( preg_match('/\((.*?)\)/im', $u_agent, $parent_matches) ) { 31 | preg_match_all('/(?PBB\d+;|Android|CrOS|Tizen|iPhone|iPad|iPod|Linux|Macintosh|Windows(\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|X11|(New\ )?Nintendo\ (WiiU?|3?DS)|Xbox(\ One)?) 32 | (?:\ [^;]*)? 33 | (?:;|$)/imx', $parent_matches[1], $result, PREG_PATTERN_ORDER); 34 | 35 | $priority = array( 'Xbox One', 'Xbox', 'Windows Phone', 'Tizen', 'Android', 'CrOS', 'X11' ); 36 | 37 | $result['platform'] = array_unique($result['platform']); 38 | if( count($result['platform']) > 1 ) { 39 | if( $keys = array_intersect($priority, $result['platform']) ) { 40 | $platform = reset($keys); 41 | } else { 42 | $platform = $result['platform'][0]; 43 | } 44 | } elseif( isset($result['platform'][0]) ) { 45 | $platform = $result['platform'][0]; 46 | } 47 | } 48 | 49 | if( $platform == 'linux-gnu' || $platform == 'X11' ) { 50 | $platform = 'Linux'; 51 | } elseif( $platform == 'CrOS' ) { 52 | $platform = 'Chrome OS'; 53 | } 54 | 55 | preg_match_all('%(?PCamino|Kindle(\ Fire)?|Firefox|Iceweasel|IceCat|Safari|MSIE|Trident|AppleWebKit| 56 | TizenBrowser|Chrome|Vivaldi|IEMobile|Opera|OPR|Silk|Midori|Edge|CriOS|UCBrowser|Puffin|SamsungBrowser| 57 | Baiduspider|Googlebot|YandexBot|bingbot|Lynx|Version|Wget|curl| 58 | Valve\ Steam\ Tenfoot| 59 | NintendoBrowser|PLAYSTATION\ (\d|Vita)+) 60 | (?:\)?;?) 61 | (?:(?:[:/ ])(?P[0-9A-Z.]+)|/(?:[A-Z]*))%ix', 62 | $u_agent, $result, PREG_PATTERN_ORDER); 63 | 64 | // If nothing matched, return null (to avoid undefined index errors) 65 | if( !isset($result['browser'][0]) || !isset($result['version'][0]) ) { 66 | if( preg_match('%^(?!Mozilla)(?P[A-Z0-9\-]+)(/(?P[0-9A-Z.]+))?%ix', $u_agent, $result) ) { 67 | return array( 'platform' => $platform ?: null, 'browser' => $result['browser'], 'version' => isset($result['version']) ? $result['version'] ?: null : null ); 68 | } 69 | 70 | return $empty; 71 | } 72 | 73 | if( preg_match('/rv:(?P[0-9A-Z.]+)/si', $u_agent, $rv_result) ) { 74 | $rv_result = $rv_result['version']; 75 | } 76 | 77 | $browser = $result['browser'][0]; 78 | $version = $result['version'][0]; 79 | 80 | $lowerBrowser = array_map('strtolower', $result['browser']); 81 | 82 | $find = function ( $search, &$key, &$value = null ) use ( $lowerBrowser ) { 83 | $search = (array)$search; 84 | 85 | foreach( $search as $val ) { 86 | $xkey = array_search(strtolower($val), $lowerBrowser); 87 | if( $xkey !== false ) { 88 | $value = $val; 89 | $key = $xkey; 90 | 91 | return true; 92 | } 93 | } 94 | 95 | return false; 96 | }; 97 | 98 | $key = 0; 99 | $val = ''; 100 | if( $browser == 'Iceweasel' || strtolower($browser) == 'icecat' ) { 101 | $browser = 'Firefox'; 102 | } elseif( $find('Playstation Vita', $key) ) { 103 | $platform = 'PlayStation Vita'; 104 | $browser = 'Browser'; 105 | } elseif( $find(array( 'Kindle Fire', 'Silk' ), $key, $val) ) { 106 | $browser = $val == 'Silk' ? 'Silk' : 'Kindle'; 107 | $platform = 'Kindle Fire'; 108 | if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) { 109 | $version = $result['version'][array_search('Version', $result['browser'])]; 110 | } 111 | } elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) { 112 | $browser = 'NintendoBrowser'; 113 | $version = $result['version'][$key]; 114 | } elseif( $find('Kindle', $key, $platform) ) { 115 | $browser = $result['browser'][$key]; 116 | $version = $result['version'][$key]; 117 | } elseif( $find('OPR', $key) ) { 118 | $browser = 'Opera Next'; 119 | $version = $result['version'][$key]; 120 | } elseif( $find('Opera', $key, $browser) ) { 121 | $find('Version', $key); 122 | $version = $result['version'][$key]; 123 | } elseif( $find('Puffin', $key, $browser) ) { 124 | $version = $result['version'][$key]; 125 | if( strlen($version) > 3 ) { 126 | $part = substr($version, -2); 127 | if( ctype_upper($part) ) { 128 | $version = substr($version, 0, -2); 129 | 130 | $flags = array( 'IP' => 'iPhone', 'IT' => 'iPad', 'AP' => 'Android', 'AT' => 'Android', 'WP' => 'Windows Phone', 'WT' => 'Windows' ); 131 | if( isset($flags[$part]) ) { 132 | $platform = $flags[$part]; 133 | } 134 | } 135 | } 136 | } elseif( $find(array( 'IEMobile', 'Edge', 'Midori', 'Vivaldi', 'SamsungBrowser', 'Valve Steam Tenfoot', 'Chrome' ), $key, $browser) ) { 137 | $version = $result['version'][$key]; 138 | } elseif( $rv_result && $find('Trident', $key) ) { 139 | $browser = 'MSIE'; 140 | $version = $rv_result; 141 | } elseif( $find('UCBrowser', $key) ) { 142 | $browser = 'UC Browser'; 143 | $version = $result['version'][$key]; 144 | } elseif( $find('CriOS', $key) ) { 145 | $browser = 'Chrome'; 146 | $version = $result['version'][$key]; 147 | } elseif( $browser == 'AppleWebKit' ) { 148 | if( $platform == 'Android' && !($key = 0) ) { 149 | $browser = 'Android Browser'; 150 | } elseif( strpos($platform, 'BB') === 0 ) { 151 | $browser = 'BlackBerry Browser'; 152 | $platform = 'BlackBerry'; 153 | } elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) { 154 | $browser = 'BlackBerry Browser'; 155 | } else { 156 | $find('Safari', $key, $browser) || $find('TizenBrowser', $key, $browser); 157 | } 158 | 159 | $find('Version', $key); 160 | $version = $result['version'][$key]; 161 | } elseif( $pKey = preg_grep('/playstation \d/i', array_map('strtolower', $result['browser'])) ) { 162 | $pKey = reset($pKey); 163 | 164 | $platform = 'PlayStation ' . preg_replace('/[^\d]/i', '', $pKey); 165 | $browser = 'NetFront'; 166 | } 167 | 168 | return array( 'platform' => $platform ?: null, 'browser' => $browser ?: null, 'version' => $version ?: null ); 169 | } 170 | -------------------------------------------------------------------------------- /includes/admin/class-simple-jwt-authentication-profile.php: -------------------------------------------------------------------------------- 1 | plugin_name = $plugin_name; 37 | $this->plugin_version = $plugin_version; 38 | 39 | add_action( 'current_screen', array( $this, 'set_edited_user' ), 10 ); 40 | 41 | add_action( 'admin_notices', array( $this, 'admin_notices' ) ); 42 | add_action( 'edit_user_profile', array( $this, 'user_token_ui' ), 20 ); 43 | add_action( 'show_user_profile', array( $this, 'user_token_ui' ), 20 ); 44 | add_action( 'current_screen', array( $this, 'maybe_revoke_token' ), 15 ); 45 | add_action( 'show_user_profile', array( $this, 'maybe_revoke_token' ) ); 46 | add_action( 'current_screen', array( $this, 'maybe_revoke_all_tokens' ), 15 ); 47 | add_action( 'show_user_profile', array( $this, 'maybe_revoke_all_tokens' ) ); 48 | add_action( 'current_screen', array( $this, 'maybe_remove_expired_tokens' ), 15 ); 49 | add_action( 'show_user_profile', array( $this, 'maybe_remove_expired_tokens' ) ); 50 | 51 | } 52 | 53 | /** 54 | * Maybe show admin notice to the user. 55 | * 56 | * @return void 57 | */ 58 | public function admin_notices() { 59 | // Nonce validation for security. 60 | if ( ! isset( $_GET['jwt_nonce'] ) || ! wp_verify_nonce( $_GET['jwt_nonce'], 'simple-jwt-ui-nonce' ) ) { 61 | return; 62 | } 63 | 64 | if ( empty( $_GET['jwtupdated'] ) ) { 65 | return; 66 | } 67 | 68 | $class = 'notice-success'; 69 | 70 | if ( ! empty( $_GET['revoked'] ) && 'all' === $_GET['revoked'] ) { 71 | $message = __( 'All tokens have been revoked.', 'simple-jwt-authentication' ); 72 | } elseif ( ! empty( $_GET['revoked'] ) ) { 73 | // TranslatorS: %s is a token ID. 74 | $message = sprintf( __( 'The token %s has been revoked.', 'simple-jwt-authentication' ), $_GET['revoked'] ); 75 | } elseif ( ! empty( $_GET['removed'] ) ) { 76 | $message = __( 'All expired tokens have been removed.', 'simple-jwt-authentication' ); 77 | } 78 | echo sprintf( '

%2$s

', esc_attr( $class ), esc_html( $message ) ); 79 | 80 | } 81 | 82 | /** 83 | * Adds a token UI metabox to each user. 84 | * 85 | * @param object $user A WP_User object 86 | * @since 1.0 87 | */ 88 | public function user_token_ui( $user ) { 89 | if ( current_user_can( 'edit_users' ) ) { 90 | $tokens = get_user_meta( $user->ID, 'jwt_data', true ) ?: false; 91 | include plugin_dir_path( __FILE__ ) . 'views/user-token-ui.php'; 92 | 93 | } 94 | } 95 | 96 | /** 97 | * Check if we should revoke a token. 98 | * 99 | * @since 1.0 100 | */ 101 | public function maybe_revoke_token() { 102 | // Nonce validation for security. 103 | if ( ! isset( $_GET['jwt_nonce'] ) || ! wp_verify_nonce( $_GET['jwt_nonce'], 'simple-jwt-ui-nonce' ) ) { 104 | return; 105 | } 106 | if ( $this->user && current_user_can( 'edit_users' ) && ! empty( $_GET['revoke_token'] ) ) { 107 | $tokens = get_user_meta( $this->user->ID, 'jwt_data', true ) ?: false; 108 | $request_token = $_GET['revoke_token']; 109 | 110 | if ( $tokens ) { 111 | foreach ( $tokens as $key => $token ) { 112 | if ( $token['uuid'] === $_GET['revoke_token'] ) { 113 | unset( $tokens[ $key ] ); 114 | update_user_meta( $this->user->ID, 'jwt_data', $tokens ); 115 | break; 116 | } 117 | } 118 | } 119 | 120 | $current_url = get_home_url() . $_SERVER['REQUEST_URI']; 121 | $redirect_url = add_query_arg( 122 | array( 123 | 'jwtupdated' => 1, 124 | 'revoked' => $request_token, 125 | ), 126 | remove_query_arg( array( 'revoke_token' ), $current_url ) 127 | ); 128 | wp_safe_redirect( $redirect_url ); 129 | exit; 130 | } 131 | } 132 | 133 | /** 134 | * Check if we should revoke a token. 135 | * 136 | * @since 1.0 137 | */ 138 | public function maybe_revoke_all_tokens() { 139 | // Nonce validation for security. 140 | if ( ! isset( $_GET['jwt_nonce'] ) || ! wp_verify_nonce( $_GET['jwt_nonce'], 'simple-jwt-ui-nonce' ) ) { 141 | return; 142 | } 143 | 144 | if ( $this->user && current_user_can( 'edit_users' ) && ! empty( $_GET['revoke_all_tokens'] ) ) { 145 | delete_user_meta( $this->user->ID, 'jwt_data' ); 146 | $current_url = get_home_url() . $_SERVER['REQUEST_URI']; 147 | 148 | $redirect_url = add_query_arg( 149 | array( 150 | 'jwtupdated' => 1, 151 | 'revoked' => 'all', 152 | ), 153 | remove_query_arg( array( 'revoke_all_tokens' ), $current_url ) 154 | ); 155 | wp_safe_redirect( $redirect_url ); 156 | exit; 157 | 158 | } 159 | 160 | } 161 | 162 | /** 163 | * Check if we should revoke a token. 164 | * 165 | * @since 1.0 166 | */ 167 | public function maybe_remove_expired_tokens() { 168 | // Nonce validation for security. 169 | if ( ! isset( $_GET['jwt_nonce'] ) || ! wp_verify_nonce( $_GET['jwt_nonce'], 'simple-jwt-ui-nonce' ) ) { 170 | return; 171 | } 172 | 173 | if ( $this->user && current_user_can( 'edit_users' ) && ! empty( $_GET['remove_expired_tokens'] ) ) { 174 | 175 | $tokens = get_user_meta( $this->user->ID, 'jwt_data', true ) ?: false; 176 | if ( $tokens ) { 177 | foreach ( $tokens as $key => $token ) { 178 | if ( $token['expires'] < time() ) { 179 | unset( $tokens[ $key ] ); 180 | } 181 | } 182 | update_user_meta( $this->user->ID, 'jwt_data', $tokens ); 183 | } 184 | 185 | $current_url = get_home_url() . $_SERVER['REQUEST_URI']; 186 | $redirect_url = add_query_arg( 187 | array( 188 | 'jwtupdated' => 1, 189 | 'removed' => 'all', 190 | ), 191 | remove_query_arg( array( 'remove_expired_tokens' ), $current_url ) 192 | ); 193 | wp_safe_redirect( $redirect_url ); 194 | exit; 195 | } 196 | } 197 | 198 | /** 199 | * check if is edit user page, and if user exist from GET param 200 | * 201 | * @since 1.1.1 202 | */ 203 | public function set_edited_user() { 204 | // Nonce validation for security. 205 | if ( ! isset( $_GET['jwt_nonce'] ) || ! wp_verify_nonce( $_GET['jwt_nonce'], 'simple-jwt-ui-nonce' ) ) { 206 | return; 207 | } 208 | 209 | $user_id = ( ! empty( $_GET['user_id'] ) ) ? intval( $_GET['user_id'] ) : false; 210 | 211 | // If user ID was not set by $_GET check if we should get it from the current user. 212 | if ( false === $user_id ) { 213 | $current_screen = get_current_screen(); 214 | if ( 'profile' === $current_screen->id ) { 215 | $user_id = get_current_user_id(); 216 | } 217 | } 218 | 219 | // Bail if no user id can be found. 220 | if ( false === $user_id ) { 221 | return; 222 | } 223 | 224 | // Get user data and save to our parameter. 225 | $user = get_userdata( (int) $user_id ); 226 | if ( false !== $user ) { 227 | $user = $user->data; 228 | $this->user = $user; 229 | } 230 | } 231 | } 232 | new Simple_Jwt_Authentication_Profile( $plugin_name, $plugin_version ); 233 | -------------------------------------------------------------------------------- /languages/simple-jwt-authentication-sv_SE.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Simple JWT Authentication\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: 2017-10-03 07:43+0000\n" 6 | "PO-Revision-Date: 2017-10-03 07:45+0000\n" 7 | "Last-Translator: Jonathan de Jong \n" 8 | "Language-Team: Swedish\n" 9 | "Language: sv-SE\n" 10 | "Plural-Forms: nplurals=2; plural=n != 1\n" 11 | "MIME-Version: 1.0\n" 12 | "Content-Type: text/plain; charset=UTF-8\n" 13 | "Content-Transfer-Encoding: 8bit\n" 14 | "X-Generator: Loco - https://localise.biz/" 15 | 16 | #: includes/admin/class-simple-jwt-authentication-profile.php:44 17 | msgid "All tokens have been revoked." 18 | msgstr "Alla åtkomsttokens har upphävts." 19 | 20 | #: includes/admin/class-simple-jwt-authentication-profile.php:46 21 | #, php-format 22 | msgid "The token %s has been revoked." 23 | msgstr "Åtkomsttoken %s har upphävts." 24 | 25 | #: includes/admin/class-simple-jwt-authentication-profile.php:48 26 | msgid "All expired tokens have been removed." 27 | msgstr "Alla utgångna åtkomsttokens har tagits bort." 28 | 29 | #. Name of the plugin 30 | msgid "Simple JWT Authentication" 31 | msgstr "Simple JWT Authentication" 32 | 33 | #. Description of the plugin 34 | msgid "" 35 | "Extends the WP REST API using JSON Web Tokens Authentication as an " 36 | "authentication method." 37 | msgstr "" 38 | "Bygger ut WP REST API med JSON Web Tokens Authentication som ett sätt att " 39 | "autentisera användare." 40 | 41 | #. URI of the plugin 42 | msgid "http://github.com/jonathan-dejong/simple-jwt-authentication" 43 | msgstr "http://github.com/jonathan-dejong/simple-jwt-authentication" 44 | 45 | #. Author of the plugin 46 | msgid "Jonathan de Jong" 47 | msgstr "Jonathan de Jong" 48 | 49 | #. Author URI of the plugin 50 | msgid "http://github.com/jonathan-dejong" 51 | msgstr "https://github.com/jonathan-dejong" 52 | 53 | #: includes/class-simple-jwt-authentication-api.php:16 54 | #: includes/class-simple-jwt-authentication-rest.php:310 55 | msgid "Unknown" 56 | msgstr "Okänd" 57 | 58 | #: includes/class-simple-jwt-authentication-rest.php:97 59 | #: includes/class-simple-jwt-authentication-rest.php:263 60 | msgid "" 61 | "JWT is not configurated properly, please contact the admin. The key is " 62 | "missing." 63 | msgstr "" 64 | "JWT har inte konfigurerats korrekt. Vänligen kontakta administratör. Nyckeln " 65 | "saknas." 66 | 67 | #: includes/class-simple-jwt-authentication-rest.php:236 68 | msgid "Authorization header not found." 69 | msgstr "Auktoriseringsheadern saknas." 70 | 71 | #: includes/class-simple-jwt-authentication-rest.php:251 72 | msgid "Authorization header malformed." 73 | msgstr "Auktoriseringsheadern är felaktig." 74 | 75 | #: includes/class-simple-jwt-authentication-rest.php:278 76 | msgid "The iss do not match with this server" 77 | msgstr "iss matchar inte med den aktuella servern" 78 | 79 | #: includes/class-simple-jwt-authentication-rest.php:288 80 | msgid "User ID not found in the token" 81 | msgstr "Användarens ID hittades inte i åtkomsttoken" 82 | 83 | #: includes/class-simple-jwt-authentication-rest.php:300 84 | #: includes/class-simple-jwt-authentication-rest.php:324 85 | msgid "Token has been revoked." 86 | msgstr "Åtkomsttoken har upphävts" 87 | 88 | #: includes/class-simple-jwt-authentication-rest.php:413 89 | msgid "Error: Username or email not specified." 90 | msgstr "" 91 | "Fel: Användarnamn eller e-postadress måste specificeras." 92 | 93 | #: includes/class-simple-jwt-authentication-rest.php:430 94 | msgid "Error: Invalid username." 95 | msgstr "Fel: Ogiltigt användarnamn/e-postadress" 96 | 97 | #: includes/class-simple-jwt-authentication-rest.php:449 98 | #: includes/class-simple-jwt-authentication-rest.php:457 99 | msgid "Error: Resetting password is not allowed." 100 | msgstr "Fel: Återställning av lösenord är inte tillåtet." 101 | 102 | #: includes/class-simple-jwt-authentication-rest.php:506 103 | msgid "" 104 | "Success: an email for selecting a new password has been " 105 | "sent." 106 | msgstr "" 107 | "Succe: ett e-postmeddelande för att välja nytt lösenord har " 108 | "skickats." 109 | 110 | #: includes/admin/class-simple-jwt-authentication-settings.php:56 111 | msgid "Basic configuration" 112 | msgstr "Grundinställningar" 113 | 114 | #: includes/admin/class-simple-jwt-authentication-settings.php:63 115 | msgid "Secret Key" 116 | msgstr "Hemlig Nyckel" 117 | 118 | #: includes/admin/class-simple-jwt-authentication-settings.php:71 119 | #, php-format 120 | msgid "Enable %s" 121 | msgstr "Aktivera %s" 122 | 123 | #: includes/admin/class-simple-jwt-authentication-settings.php:112 124 | #, php-format 125 | msgid "" 126 | "This is all you need to start using JWT authentication.
You can also " 127 | "specify these in wp-config.php instead using %1$s %2$s" 128 | msgstr "" 129 | "Detta är allt som krävs för att börja använda JWT Autentisering.
Du " 130 | "kan även specificera dessa i wp-config.php istället med %1$s %2$s" 131 | 132 | #: includes/admin/views/user-token-ui.php:1 133 | msgid "Simple JWT Authentication API Tokens" 134 | msgstr "Simple JWT Authentication API Åtkomsttokens" 135 | 136 | #: includes/admin/views/user-token-ui.php:5 137 | msgid "Token UUID" 138 | msgstr "Åtkomsttoken UUID" 139 | 140 | #: includes/admin/views/user-token-ui.php:6 141 | msgid "Expires" 142 | msgstr "Utgår" 143 | 144 | #: includes/admin/views/user-token-ui.php:7 145 | msgid "Last used" 146 | msgstr "Senast använd" 147 | 148 | #: includes/admin/views/user-token-ui.php:8 149 | msgid "By IP" 150 | msgstr "Av IP" 151 | 152 | #: includes/admin/views/user-token-ui.php:9 153 | msgid "Browser" 154 | msgstr "Webbläsare" 155 | 156 | #: includes/admin/views/user-token-ui.php:30 157 | msgid "Lookup" 158 | msgstr "Slå upp" 159 | 160 | #: includes/admin/views/user-token-ui.php:31 161 | #, php-format 162 | msgid "" 163 | "Platform %1$s. Browser: %2$s. " 164 | "Browser version: %3$s" 165 | msgstr "" 166 | "Plattform %1$s. Webbläsare: %2$s. " 167 | "Webbläsarversion: %3$s" 168 | 169 | #: includes/admin/views/user-token-ui.php:33 170 | msgid "Revokes this token from being used any further." 171 | msgstr "Upphäver denna åtkomsttoken från att fortsätta användas." 172 | 173 | #: includes/admin/views/user-token-ui.php:33 174 | msgid "Revoke" 175 | msgstr "Upphäv" 176 | 177 | #: includes/admin/views/user-token-ui.php:39 178 | msgid "Doing this will require the user to login again on all devices." 179 | msgstr "Detta kräver att användaren loggar in igen på alla enheter." 180 | 181 | #: includes/admin/views/user-token-ui.php:39 182 | msgid "Revoke all tokens" 183 | msgstr "Upphäv alla åtkomsttokens" 184 | 185 | #: includes/admin/views/user-token-ui.php:40 186 | msgid "Doing this will not affect logged in devices for this user." 187 | msgstr "Detta påverkar inte inloggade enheter för den här användaren." 188 | 189 | #: includes/admin/views/user-token-ui.php:40 190 | msgid "Remove all expired tokens" 191 | msgstr "Ta bort alla utgångna åtkomsttokens" 192 | 193 | #: includes/admin/views/user-token-ui.php:45 194 | msgid "No tokens generated." 195 | msgstr "Inga åtkomsttokens har skapats." 196 | 197 | #: includes/admin/views/settings/enable-cors.php:4 198 | #: includes/admin/views/settings/secret-key.php:4 199 | msgid "Defined in wp-config.php" 200 | msgstr "Definierad i wp-config.php" 201 | 202 | #: includes/admin/views/settings/page.php:8 203 | msgid "Getting started" 204 | msgstr "Kom igång" 205 | 206 | #: includes/admin/views/settings/page.php:10 207 | #, php-format 208 | msgid "" 209 | "To get started check out the documentation" 211 | msgstr "" 212 | "Läs dokumentationen " 213 | "för att börja använda Simple JWT Authentication" 214 | 215 | #: includes/admin/views/settings/secret-key.php:6 216 | msgid "Should be a long string of letters, numbers and symbols." 217 | msgstr "Bör vara en lång sträng av bokstäver, siffror och symboler." 218 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/tests/JWTTest.php: -------------------------------------------------------------------------------- 1 | assertEquals(JWT::decode($msg, 'my_key', array('HS256')), 'abc'); 10 | } 11 | 12 | public function testDecodeFromPython() 13 | { 14 | $msg = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.Iio6aHR0cDovL2FwcGxpY2F0aW9uL2NsaWNreT9ibGFoPTEuMjMmZi5vbz00NTYgQUMwMDAgMTIzIg.E_U8X2YpMT5K1cEiT_3-IvBYfrdIFIeVYeOqre_Z5Cg'; 15 | $this->assertEquals( 16 | JWT::decode($msg, 'my_key', array('HS256')), 17 | '*:http://application/clicky?blah=1.23&f.oo=456 AC000 123' 18 | ); 19 | } 20 | 21 | public function testUrlSafeCharacters() 22 | { 23 | $encoded = JWT::encode('f?', 'a'); 24 | $this->assertEquals('f?', JWT::decode($encoded, 'a', array('HS256'))); 25 | } 26 | 27 | public function testMalformedUtf8StringsFail() 28 | { 29 | $this->setExpectedException('DomainException'); 30 | JWT::encode(pack('c', 128), 'a'); 31 | } 32 | 33 | public function testMalformedJsonThrowsException() 34 | { 35 | $this->setExpectedException('DomainException'); 36 | JWT::jsonDecode('this is not valid JSON string'); 37 | } 38 | 39 | public function testExpiredToken() 40 | { 41 | $this->setExpectedException('Firebase\JWT\ExpiredException'); 42 | $payload = array( 43 | "message" => "abc", 44 | "exp" => time() - 20); // time in the past 45 | $encoded = JWT::encode($payload, 'my_key'); 46 | JWT::decode($encoded, 'my_key', array('HS256')); 47 | } 48 | 49 | public function testBeforeValidTokenWithNbf() 50 | { 51 | $this->setExpectedException('Firebase\JWT\BeforeValidException'); 52 | $payload = array( 53 | "message" => "abc", 54 | "nbf" => time() + 20); // time in the future 55 | $encoded = JWT::encode($payload, 'my_key'); 56 | JWT::decode($encoded, 'my_key', array('HS256')); 57 | } 58 | 59 | public function testBeforeValidTokenWithIat() 60 | { 61 | $this->setExpectedException('Firebase\JWT\BeforeValidException'); 62 | $payload = array( 63 | "message" => "abc", 64 | "iat" => time() + 20); // time in the future 65 | $encoded = JWT::encode($payload, 'my_key'); 66 | JWT::decode($encoded, 'my_key', array('HS256')); 67 | } 68 | 69 | public function testValidToken() 70 | { 71 | $payload = array( 72 | "message" => "abc", 73 | "exp" => time() + JWT::$leeway + 20); // time in the future 74 | $encoded = JWT::encode($payload, 'my_key'); 75 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 76 | $this->assertEquals($decoded->message, 'abc'); 77 | } 78 | 79 | public function testValidTokenWithLeeway() 80 | { 81 | JWT::$leeway = 60; 82 | $payload = array( 83 | "message" => "abc", 84 | "exp" => time() - 20); // time in the past 85 | $encoded = JWT::encode($payload, 'my_key'); 86 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 87 | $this->assertEquals($decoded->message, 'abc'); 88 | JWT::$leeway = 0; 89 | } 90 | 91 | public function testExpiredTokenWithLeeway() 92 | { 93 | JWT::$leeway = 60; 94 | $payload = array( 95 | "message" => "abc", 96 | "exp" => time() - 70); // time far in the past 97 | $this->setExpectedException('Firebase\JWT\ExpiredException'); 98 | $encoded = JWT::encode($payload, 'my_key'); 99 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 100 | $this->assertEquals($decoded->message, 'abc'); 101 | JWT::$leeway = 0; 102 | } 103 | 104 | public function testValidTokenWithList() 105 | { 106 | $payload = array( 107 | "message" => "abc", 108 | "exp" => time() + 20); // time in the future 109 | $encoded = JWT::encode($payload, 'my_key'); 110 | $decoded = JWT::decode($encoded, 'my_key', array('HS256', 'HS512')); 111 | $this->assertEquals($decoded->message, 'abc'); 112 | } 113 | 114 | public function testValidTokenWithNbf() 115 | { 116 | $payload = array( 117 | "message" => "abc", 118 | "iat" => time(), 119 | "exp" => time() + 20, // time in the future 120 | "nbf" => time() - 20); 121 | $encoded = JWT::encode($payload, 'my_key'); 122 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 123 | $this->assertEquals($decoded->message, 'abc'); 124 | } 125 | 126 | public function testValidTokenWithNbfLeeway() 127 | { 128 | JWT::$leeway = 60; 129 | $payload = array( 130 | "message" => "abc", 131 | "nbf" => time() + 20); // not before in near (leeway) future 132 | $encoded = JWT::encode($payload, 'my_key'); 133 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 134 | $this->assertEquals($decoded->message, 'abc'); 135 | JWT::$leeway = 0; 136 | } 137 | 138 | public function testInvalidTokenWithNbfLeeway() 139 | { 140 | JWT::$leeway = 60; 141 | $payload = array( 142 | "message" => "abc", 143 | "nbf" => time() + 65); // not before too far in future 144 | $encoded = JWT::encode($payload, 'my_key'); 145 | $this->setExpectedException('Firebase\JWT\BeforeValidException'); 146 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 147 | JWT::$leeway = 0; 148 | } 149 | 150 | public function testValidTokenWithIatLeeway() 151 | { 152 | JWT::$leeway = 60; 153 | $payload = array( 154 | "message" => "abc", 155 | "iat" => time() + 20); // issued in near (leeway) future 156 | $encoded = JWT::encode($payload, 'my_key'); 157 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 158 | $this->assertEquals($decoded->message, 'abc'); 159 | JWT::$leeway = 0; 160 | } 161 | 162 | public function testInvalidTokenWithIatLeeway() 163 | { 164 | JWT::$leeway = 60; 165 | $payload = array( 166 | "message" => "abc", 167 | "iat" => time() + 65); // issued too far in future 168 | $encoded = JWT::encode($payload, 'my_key'); 169 | $this->setExpectedException('Firebase\JWT\BeforeValidException'); 170 | $decoded = JWT::decode($encoded, 'my_key', array('HS256')); 171 | JWT::$leeway = 0; 172 | } 173 | 174 | public function testInvalidToken() 175 | { 176 | $payload = array( 177 | "message" => "abc", 178 | "exp" => time() + 20); // time in the future 179 | $encoded = JWT::encode($payload, 'my_key'); 180 | $this->setExpectedException('Firebase\JWT\SignatureInvalidException'); 181 | $decoded = JWT::decode($encoded, 'my_key2', array('HS256')); 182 | } 183 | 184 | public function testNullKeyFails() 185 | { 186 | $payload = array( 187 | "message" => "abc", 188 | "exp" => time() + JWT::$leeway + 20); // time in the future 189 | $encoded = JWT::encode($payload, 'my_key'); 190 | $this->setExpectedException('InvalidArgumentException'); 191 | $decoded = JWT::decode($encoded, null, array('HS256')); 192 | } 193 | 194 | public function testEmptyKeyFails() 195 | { 196 | $payload = array( 197 | "message" => "abc", 198 | "exp" => time() + JWT::$leeway + 20); // time in the future 199 | $encoded = JWT::encode($payload, 'my_key'); 200 | $this->setExpectedException('InvalidArgumentException'); 201 | $decoded = JWT::decode($encoded, '', array('HS256')); 202 | } 203 | 204 | public function testRSEncodeDecode() 205 | { 206 | $privKey = openssl_pkey_new(array('digest_alg' => 'sha256', 207 | 'private_key_bits' => 1024, 208 | 'private_key_type' => OPENSSL_KEYTYPE_RSA)); 209 | $msg = JWT::encode('abc', $privKey, 'RS256'); 210 | $pubKey = openssl_pkey_get_details($privKey); 211 | $pubKey = $pubKey['key']; 212 | $decoded = JWT::decode($msg, $pubKey, array('RS256')); 213 | $this->assertEquals($decoded, 'abc'); 214 | } 215 | 216 | public function testKIDChooser() 217 | { 218 | $keys = array('1' => 'my_key', '2' => 'my_key2'); 219 | $msg = JWT::encode('abc', $keys['1'], 'HS256', '1'); 220 | $decoded = JWT::decode($msg, $keys, array('HS256')); 221 | $this->assertEquals($decoded, 'abc'); 222 | } 223 | 224 | public function testArrayAccessKIDChooser() 225 | { 226 | $keys = new ArrayObject(array('1' => 'my_key', '2' => 'my_key2')); 227 | $msg = JWT::encode('abc', $keys['1'], 'HS256', '1'); 228 | $decoded = JWT::decode($msg, $keys, array('HS256')); 229 | $this->assertEquals($decoded, 'abc'); 230 | } 231 | 232 | public function testNoneAlgorithm() 233 | { 234 | $msg = JWT::encode('abc', 'my_key'); 235 | $this->setExpectedException('DomainException'); 236 | JWT::decode($msg, 'my_key', array('none')); 237 | } 238 | 239 | public function testIncorrectAlgorithm() 240 | { 241 | $msg = JWT::encode('abc', 'my_key'); 242 | $this->setExpectedException('DomainException'); 243 | JWT::decode($msg, 'my_key', array('RS256')); 244 | } 245 | 246 | public function testMissingAlgorithm() 247 | { 248 | $msg = JWT::encode('abc', 'my_key'); 249 | $this->setExpectedException('DomainException'); 250 | JWT::decode($msg, 'my_key'); 251 | } 252 | 253 | public function testAdditionalHeaders() 254 | { 255 | $msg = JWT::encode('abc', 'my_key', 'HS256', null, array('cty' => 'test-eit;v=1')); 256 | $this->assertEquals(JWT::decode($msg, 'my_key', array('HS256')), 'abc'); 257 | } 258 | 259 | public function testInvalidSegmentCount() 260 | { 261 | $this->setExpectedException('UnexpectedValueException'); 262 | JWT::decode('brokenheader.brokenbody', 'my_key', array('HS256')); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /includes/vendor/firebase/php-jwt/src/JWT.php: -------------------------------------------------------------------------------- 1 | 18 | * @author Anant Narayanan 19 | * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD 20 | * @link https://github.com/firebase/php-jwt 21 | */ 22 | class JWT 23 | { 24 | 25 | /** 26 | * When checking nbf, iat or expiration times, 27 | * we want to provide some extra leeway time to 28 | * account for clock skew. 29 | */ 30 | public static $leeway = 0; 31 | 32 | public static $supported_algs = array( 33 | 'HS256' => array('hash_hmac', 'SHA256'), 34 | 'HS512' => array('hash_hmac', 'SHA512'), 35 | 'HS384' => array('hash_hmac', 'SHA384'), 36 | 'RS256' => array('openssl', 'SHA256'), 37 | ); 38 | 39 | /** 40 | * Decodes a JWT string into a PHP object. 41 | * 42 | * @param string $jwt The JWT 43 | * @param string|array|null $key The key, or map of keys. 44 | * If the algorithm used is asymmetric, this is the public key 45 | * @param array $allowed_algs List of supported verification algorithms 46 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 47 | * 48 | * @return object The JWT's payload as a PHP object 49 | * 50 | * @throws DomainException Algorithm was not provided 51 | * @throws UnexpectedValueException Provided JWT was invalid 52 | * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed 53 | * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' 54 | * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' 55 | * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim 56 | * 57 | * @uses jsonDecode 58 | * @uses urlsafeB64Decode 59 | */ 60 | public static function decode($jwt, $key, $allowed_algs = array()) 61 | { 62 | if (empty($key)) { 63 | throw new InvalidArgumentException('Key may not be empty'); 64 | } 65 | $tks = explode('.', $jwt); 66 | if (count($tks) != 3) { 67 | throw new UnexpectedValueException('Wrong number of segments'); 68 | } 69 | list($headb64, $bodyb64, $cryptob64) = $tks; 70 | if (null === ($header = JWT::jsonDecode(JWT::urlsafeB64Decode($headb64)))) { 71 | throw new UnexpectedValueException('Invalid header encoding'); 72 | } 73 | if (null === $payload = JWT::jsonDecode(JWT::urlsafeB64Decode($bodyb64))) { 74 | throw new UnexpectedValueException('Invalid claims encoding'); 75 | } 76 | $sig = JWT::urlsafeB64Decode($cryptob64); 77 | 78 | if (empty($header->alg)) { 79 | throw new DomainException('Empty algorithm'); 80 | } 81 | if (empty(self::$supported_algs[$header->alg])) { 82 | throw new DomainException('Algorithm not supported'); 83 | } 84 | if (!is_array($allowed_algs) || !in_array($header->alg, $allowed_algs)) { 85 | throw new DomainException('Algorithm not allowed'); 86 | } 87 | if (is_array($key) || $key instanceof \ArrayAccess) { 88 | if (isset($header->kid)) { 89 | $key = $key[$header->kid]; 90 | } else { 91 | throw new DomainException('"kid" empty, unable to lookup correct key'); 92 | } 93 | } 94 | 95 | // Check the signature 96 | if (!JWT::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { 97 | throw new SignatureInvalidException('Signature verification failed'); 98 | } 99 | 100 | // Check if the nbf if it is defined. This is the time that the 101 | // token can actually be used. If it's not yet that time, abort. 102 | if (isset($payload->nbf) && $payload->nbf > (time() + self::$leeway)) { 103 | throw new BeforeValidException( 104 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf) 105 | ); 106 | } 107 | 108 | // Check that this token has been created before 'now'. This prevents 109 | // using tokens that have been created for later use (and haven't 110 | // correctly used the nbf claim). 111 | if (isset($payload->iat) && $payload->iat > (time() + self::$leeway)) { 112 | throw new BeforeValidException( 113 | 'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat) 114 | ); 115 | } 116 | 117 | // Check if this token has expired. 118 | if (isset($payload->exp) && (time() - self::$leeway) >= $payload->exp) { 119 | throw new ExpiredException('Expired token'); 120 | } 121 | 122 | return $payload; 123 | } 124 | 125 | /** 126 | * Converts and signs a PHP object or array into a JWT string. 127 | * 128 | * @param object|array $payload PHP object or array 129 | * @param string $key The secret key. 130 | * If the algorithm used is asymmetric, this is the private key 131 | * @param string $alg The signing algorithm. 132 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 133 | * @param array $head An array with header elements to attach 134 | * 135 | * @return string A signed JWT 136 | * 137 | * @uses jsonEncode 138 | * @uses urlsafeB64Encode 139 | */ 140 | public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null) 141 | { 142 | $header = array('typ' => 'JWT', 'alg' => $alg); 143 | if ($keyId !== null) { 144 | $header['kid'] = $keyId; 145 | } 146 | if ( isset($head) && is_array($head) ) { 147 | $header = array_merge($head, $header); 148 | } 149 | $segments = array(); 150 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($header)); 151 | $segments[] = JWT::urlsafeB64Encode(JWT::jsonEncode($payload)); 152 | $signing_input = implode('.', $segments); 153 | 154 | $signature = JWT::sign($signing_input, $key, $alg); 155 | $segments[] = JWT::urlsafeB64Encode($signature); 156 | 157 | return implode('.', $segments); 158 | } 159 | 160 | /** 161 | * Sign a string with a given key and algorithm. 162 | * 163 | * @param string $msg The message to sign 164 | * @param string|resource $key The secret key 165 | * @param string $alg The signing algorithm. 166 | * Supported algorithms are 'HS256', 'HS384', 'HS512' and 'RS256' 167 | * 168 | * @return string An encrypted message 169 | * 170 | * @throws DomainException Unsupported algorithm was specified 171 | */ 172 | public static function sign($msg, $key, $alg = 'HS256') 173 | { 174 | if (empty(self::$supported_algs[$alg])) { 175 | throw new DomainException('Algorithm not supported'); 176 | } 177 | list($function, $algorithm) = self::$supported_algs[$alg]; 178 | switch($function) { 179 | case 'hash_hmac': 180 | return hash_hmac($algorithm, $msg, $key, true); 181 | case 'openssl': 182 | $signature = ''; 183 | $success = openssl_sign($msg, $signature, $key, $algorithm); 184 | if (!$success) { 185 | throw new DomainException("OpenSSL unable to sign data"); 186 | } else { 187 | return $signature; 188 | } 189 | } 190 | } 191 | 192 | /** 193 | * Verify a signature with the message, key and method. Not all methods 194 | * are symmetric, so we must have a separate verify and sign method. 195 | * 196 | * @param string $msg The original message (header and body) 197 | * @param string $signature The original signature 198 | * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key 199 | * @param string $alg The algorithm 200 | * 201 | * @return bool 202 | * 203 | * @throws DomainException Invalid Algorithm or OpenSSL failure 204 | */ 205 | private static function verify($msg, $signature, $key, $alg) 206 | { 207 | if (empty(self::$supported_algs[$alg])) { 208 | throw new DomainException('Algorithm not supported'); 209 | } 210 | 211 | list($function, $algorithm) = self::$supported_algs[$alg]; 212 | switch($function) { 213 | case 'openssl': 214 | $success = openssl_verify($msg, $signature, $key, $algorithm); 215 | if (!$success) { 216 | throw new DomainException("OpenSSL unable to verify data: " . openssl_error_string()); 217 | } else { 218 | return $signature; 219 | } 220 | case 'hash_hmac': 221 | default: 222 | $hash = hash_hmac($algorithm, $msg, $key, true); 223 | if (function_exists('hash_equals')) { 224 | return hash_equals($signature, $hash); 225 | } 226 | $len = min(self::safeStrlen($signature), self::safeStrlen($hash)); 227 | 228 | $status = 0; 229 | for ($i = 0; $i < $len; $i++) { 230 | $status |= (ord($signature[$i]) ^ ord($hash[$i])); 231 | } 232 | $status |= (self::safeStrlen($signature) ^ self::safeStrlen($hash)); 233 | 234 | return ($status === 0); 235 | } 236 | } 237 | 238 | /** 239 | * Decode a JSON string into a PHP object. 240 | * 241 | * @param string $input JSON string 242 | * 243 | * @return object Object representation of JSON string 244 | * 245 | * @throws DomainException Provided string was invalid JSON 246 | */ 247 | public static function jsonDecode($input) 248 | { 249 | if (version_compare(PHP_VERSION, '5.4.0', '>=') && !(defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { 250 | /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you 251 | * to specify that large ints (like Steam Transaction IDs) should be treated as 252 | * strings, rather than the PHP default behaviour of converting them to floats. 253 | */ 254 | $obj = json_decode($input, false, 512, JSON_BIGINT_AS_STRING); 255 | } else { 256 | /** Not all servers will support that, however, so for older versions we must 257 | * manually detect large ints in the JSON string and quote them (thus converting 258 | *them to strings) before decoding, hence the preg_replace() call. 259 | */ 260 | $max_int_length = strlen((string) PHP_INT_MAX) - 1; 261 | $json_without_bigints = preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input); 262 | $obj = json_decode($json_without_bigints); 263 | } 264 | 265 | if (function_exists('json_last_error') && $errno = json_last_error()) { 266 | JWT::handleJsonError($errno); 267 | } elseif ($obj === null && $input !== 'null') { 268 | throw new DomainException('Null result with non-null input'); 269 | } 270 | return $obj; 271 | } 272 | 273 | /** 274 | * Encode a PHP object into a JSON string. 275 | * 276 | * @param object|array $input A PHP object or array 277 | * 278 | * @return string JSON representation of the PHP object or array 279 | * 280 | * @throws DomainException Provided object could not be encoded to valid JSON 281 | */ 282 | public static function jsonEncode($input) 283 | { 284 | $json = json_encode($input); 285 | if (function_exists('json_last_error') && $errno = json_last_error()) { 286 | JWT::handleJsonError($errno); 287 | } elseif ($json === 'null' && $input !== null) { 288 | throw new DomainException('Null result with non-null input'); 289 | } 290 | return $json; 291 | } 292 | 293 | /** 294 | * Decode a string with URL-safe Base64. 295 | * 296 | * @param string $input A Base64 encoded string 297 | * 298 | * @return string A decoded string 299 | */ 300 | public static function urlsafeB64Decode($input) 301 | { 302 | $remainder = strlen($input) % 4; 303 | if ($remainder) { 304 | $padlen = 4 - $remainder; 305 | $input .= str_repeat('=', $padlen); 306 | } 307 | return base64_decode(strtr($input, '-_', '+/')); 308 | } 309 | 310 | /** 311 | * Encode a string with URL-safe Base64. 312 | * 313 | * @param string $input The string you want encoded 314 | * 315 | * @return string The base64 encode of what you passed in 316 | */ 317 | public static function urlsafeB64Encode($input) 318 | { 319 | return str_replace('=', '', strtr(base64_encode($input), '+/', '-_')); 320 | } 321 | 322 | /** 323 | * Helper method to create a JSON error. 324 | * 325 | * @param int $errno An error number from json_last_error() 326 | * 327 | * @return void 328 | */ 329 | private static function handleJsonError($errno) 330 | { 331 | $messages = array( 332 | JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', 333 | JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', 334 | JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON' 335 | ); 336 | throw new DomainException( 337 | isset($messages[$errno]) 338 | ? $messages[$errno] 339 | : 'Unknown JSON error: ' . $errno 340 | ); 341 | } 342 | 343 | /** 344 | * Get the number of bytes in cryptographic strings. 345 | * 346 | * @param string 347 | * 348 | * @return int 349 | */ 350 | private static function safeStrlen($str) 351 | { 352 | if (function_exists('mb_strlen')) { 353 | return mb_strlen($str, '8bit'); 354 | } 355 | return strlen($str); 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /includes/vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', $this->prefixesPsr0); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath.'\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 383 | $length = $this->prefixLengthsPsr4[$first][$search]; 384 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /includes/class-simple-jwt-authentication-rest.php: -------------------------------------------------------------------------------- 1 | plugin_name = $plugin_name; 33 | $this->plugin_version = $plugin_version; 34 | $this->api_version = 1; 35 | $this->namespace = $plugin_name . '/v' . $this->api_version; 36 | 37 | $this->init(); 38 | 39 | } 40 | 41 | 42 | /** 43 | * Initialize this class with hooks. 44 | * 45 | * @return void 46 | */ 47 | public function init() { 48 | add_action( 'rest_api_init', array( $this, 'add_api_routes' ) ); 49 | add_filter( 'rest_api_init', array( $this, 'add_cors_support' ) ); 50 | add_filter( 'rest_pre_dispatch', array( $this, 'rest_pre_dispatch' ), 10, 2 ); 51 | $this->gutenberg_compatibility(); 52 | } 53 | 54 | 55 | /** 56 | * Fix gutenberg compatiblity. 57 | * Make sure the JWT token is only looked for and applied if the user is not already logged in. 58 | * TODO: Look into if we really need to use cookies for this...? 59 | * 60 | * @return void 61 | */ 62 | public function gutenberg_compatibility() { 63 | // If logged in cookie exists bail early. 64 | foreach ( $_COOKIE as $name => $value ) { 65 | if ( 0 === strpos( $name, 'wordpress_logged_in_' ) ) { 66 | return; 67 | } 68 | } 69 | 70 | add_filter( 'determine_current_user', array( $this, 'determine_current_user' ), 10 ); 71 | } 72 | 73 | 74 | /** 75 | * Add the endpoints to the API 76 | */ 77 | public function add_api_routes() { 78 | register_rest_route( 79 | $this->namespace, 80 | 'token', 81 | array( 82 | 'methods' => 'POST', 83 | 'callback' => array( $this, 'generate_token' ), 84 | ) 85 | ); 86 | 87 | register_rest_route( 88 | $this->namespace, 89 | 'token/validate', 90 | array( 91 | 'methods' => 'POST', 92 | 'callback' => array( $this, 'validate_token' ), 93 | ) 94 | ); 95 | 96 | register_rest_route( 97 | $this->namespace, 98 | 'token/refresh', 99 | array( 100 | 'methods' => 'POST', 101 | 'callback' => array( $this, 'refresh_token' ), 102 | ) 103 | ); 104 | 105 | register_rest_route( 106 | $this->namespace, 107 | 'token/revoke', 108 | array( 109 | 'methods' => 'POST', 110 | 'callback' => array( $this, 'revoke_token' ), 111 | ) 112 | ); 113 | 114 | register_rest_route( 115 | $this->namespace, 116 | 'token/resetpassword', 117 | array( 118 | 'methods' => 'POST', 119 | 'callback' => array( $this, 'reset_password' ), 120 | ) 121 | ); 122 | } 123 | 124 | /** 125 | * Add CORs suppot to the request. 126 | */ 127 | public function add_cors_support() { 128 | $enable_cors = Simple_Jwt_Authentication_Api::get_cors(); 129 | if ( $enable_cors ) { 130 | $headers = apply_filters( 'jwt_auth_cors_allow_headers', 'Access-Control-Allow-Headers, Content-Type, Authorization' ); 131 | header( sprintf( 'Access-Control-Allow-Headers: %s', $headers ) ); 132 | } 133 | } 134 | 135 | /** 136 | * Creates a new UUID to track the token. Attempts to generate the UUID using 137 | * the WP built-in method first and falls back to ramsey/uuid 138 | * 139 | * @since 1.2 140 | * @return String UUID 141 | */ 142 | private function generate_uuid() { 143 | if ( function_exists( 'wp_generate_uuid4' ) ) { 144 | //Use the built in UUID generator 145 | return wp_generate_uuid4(); 146 | } else { 147 | //Old version of WP, use a different UUID generator 148 | return Uuid::uuid4()->toString(); 149 | } 150 | } 151 | 152 | /** 153 | * Get the user and password in the request body and generate a JWT 154 | * 155 | * @param object $request a WP REST request object 156 | * @since 1.0 157 | * @return mixed Either a WP_Error or current user data. 158 | */ 159 | public function generate_token( $request ) { 160 | $secret_key = Simple_Jwt_Authentication_Api::get_key(); 161 | $username = $request->get_param( 'username' ); 162 | $password = $request->get_param( 'password' ); 163 | 164 | /** First thing, check the secret key if not exist return a error*/ 165 | if ( ! $secret_key ) { 166 | return new WP_Error( 167 | 'jwt_auth_bad_config', 168 | __( 'JWT is not configurated properly, please contact the admin. The key is missing.', 'simple-jwt-authentication' ), 169 | array( 170 | 'status' => 403, 171 | ) 172 | ); 173 | } 174 | /** Try to authenticate the user with the passed credentials*/ 175 | $user = wp_authenticate( $username, $password ); 176 | 177 | /** If the authentication fails return a error*/ 178 | if ( is_wp_error( $user ) ) { 179 | $error_code = $user->get_error_code(); 180 | return new WP_Error( 181 | '[jwt_auth] ' . $error_code, 182 | $user->get_error_message( $error_code ), 183 | array( 184 | 'status' => 403, 185 | ) 186 | ); 187 | } 188 | 189 | // Valid credentials, the user exists create the according Token. 190 | $issued_at = time(); 191 | $not_before = apply_filters( 'jwt_auth_not_before', $issued_at ); 192 | $expire = apply_filters( 'jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at, $user ); 193 | $uuid = $this->generate_uuid(); 194 | 195 | $token = array( 196 | 'uuid' => $uuid, 197 | 'iss' => get_bloginfo( 'url' ), 198 | 'iat' => $issued_at, 199 | 'nbf' => $not_before, 200 | 'exp' => $expire, 201 | 'data' => array( 202 | 'user' => array( 203 | 'id' => $user->data->ID, 204 | ), 205 | ), 206 | ); 207 | 208 | // Let the user modify the token data before the sign. 209 | $token = JWT::encode( apply_filters( 'jwt_auth_token_before_sign', $token, $user ), $secret_key ); 210 | 211 | // Setup some user meta data we can use for our UI. 212 | $jwt_data = get_user_meta( $user->data->ID, 'jwt_data', true ) ?: array(); 213 | $user_ip = Simple_Jwt_Authentication_Api::get_ip(); 214 | $jwt_data[] = array( 215 | 'uuid' => $uuid, 216 | 'issued_at' => $issued_at, 217 | 'expires' => $expire, 218 | 'ip' => $user_ip, 219 | 'ua' => $_SERVER['HTTP_USER_AGENT'], 220 | 'last_used' => time(), 221 | ); 222 | update_user_meta( $user->data->ID, 'jwt_data', apply_filters( 'simple_jwt_auth_save_user_data', $jwt_data ) ); 223 | 224 | // The token is signed, now create the object with no sensible user data to the client. 225 | $data = array( 226 | 'token' => $token, 227 | 'user_id' => $user->data->ID, 228 | 'user_email' => $user->data->user_email, 229 | 'user_nicename' => $user->data->user_nicename, 230 | 'user_display_name' => $user->data->display_name, 231 | 'token_expires' => $expire, 232 | ); 233 | 234 | // Let the user modify the data before send it back. 235 | return apply_filters( 'jwt_auth_token_before_dispatch', $data, $user ); 236 | } 237 | 238 | /** 239 | * This is our Middleware to try to authenticate the user according to the 240 | * token send. 241 | * 242 | * @param (int|bool) $user Logged User ID 243 | * @since 1.0 244 | * @return (int|bool) 245 | */ 246 | public function determine_current_user( $user ) { 247 | /** 248 | * This hook only should run on the REST API requests to determine 249 | * if the user in the Token (if any) is valid, for any other 250 | * normal call ex. wp-admin/.* return the user. 251 | * 252 | * @since 1.2.3 253 | **/ 254 | $rest_api_slug = rest_get_url_prefix(); 255 | $valid_api_uri = strpos( $_SERVER['REQUEST_URI'], $rest_api_slug ); 256 | if ( ! $valid_api_uri ) { 257 | return $user; 258 | } 259 | 260 | /* 261 | * if the request URI is for validate the token don't do anything, 262 | * this avoid double calls to the validate_token function. 263 | */ 264 | $validate_uri = strpos( $_SERVER['REQUEST_URI'], 'token/validate' ); 265 | if ( $validate_uri > 0 ) { 266 | return $user; 267 | } 268 | $token = $this->validate_token( false ); 269 | 270 | if ( is_wp_error( $token ) ) { 271 | if ( $token->get_error_code() !== 'jwt_auth_no_auth_header' ) { 272 | // If there is a error, store it to show it after see rest_pre_dispatch 273 | $this->jwt_error = $token; 274 | return $user; 275 | } else { 276 | return $user; 277 | } 278 | } 279 | 280 | // Everything is ok, return the user ID stored in the token. 281 | return $token->data->user->id; 282 | } 283 | 284 | /** 285 | * Main validation function, this function try to get the Autentication 286 | * headers and decoded. 287 | * 288 | * @param bool $output 289 | * @since 1.0 290 | * @return WP_Error | Object 291 | */ 292 | public function validate_token( $output = true ) { 293 | /* 294 | * Looking for the HTTP_AUTHORIZATION header, if not present just 295 | * return the user. 296 | */ 297 | $header_name = defined( 'SIMPLE_JWT_AUTHENTICATION_HEADER_NAME' ) ? SIMPLE_JWT_AUTHENTICATION_HEADER_NAME : 'HTTP_AUTHORIZATION'; 298 | $auth = isset( $_SERVER[ $header_name ] ) ? $_SERVER[ $header_name ] : false; 299 | 300 | // Double check for different auth header string (server dependent) 301 | if ( ! $auth ) { 302 | $auth = isset( $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] ) ? $_SERVER['REDIRECT_HTTP_AUTHORIZATION'] : false; 303 | } 304 | 305 | if ( ! $auth ) { 306 | return new WP_Error( 307 | 'jwt_auth_no_auth_header', 308 | __( 'Authorization header not found.', 'simple-jwt-authentication' ), 309 | array( 310 | 'status' => 403, 311 | ) 312 | ); 313 | } 314 | 315 | /* 316 | * The HTTP_AUTHORIZATION is present verify the format 317 | * if the format is wrong return the user. 318 | */ 319 | list( $token ) = sscanf( $auth, 'Bearer %s' ); 320 | if ( ! $token ) { 321 | return new WP_Error( 322 | 'jwt_auth_bad_auth_header', 323 | __( 'Authorization header malformed.', 'simple-jwt-authentication' ), 324 | array( 325 | 'status' => 403, 326 | ) 327 | ); 328 | } 329 | 330 | // Get the Secret Key 331 | $secret_key = Simple_Jwt_Authentication_Api::get_key(); 332 | if ( ! $secret_key ) { 333 | return new WP_Error( 334 | 'jwt_auth_bad_config', 335 | __( 'JWT is not configurated properly, please contact the admin. The key is missing.', 'simple-jwt-authentication' ), 336 | array( 337 | 'status' => 403, 338 | ) 339 | ); 340 | } 341 | 342 | // Try to decode the token 343 | try { 344 | $token = JWT::decode( $token, $secret_key, array( 'HS256' ) ); 345 | // The Token is decoded now validate the iss 346 | if ( get_bloginfo( 'url' ) !== $token->iss ) { 347 | // The iss do not match, return error 348 | return new WP_Error( 349 | 'jwt_auth_bad_iss', 350 | __( 'The iss do not match with this server', 'simple-jwt-authentication' ), 351 | array( 352 | 'status' => 403, 353 | ) 354 | ); 355 | } 356 | // So far so good, validate the user id in the token. 357 | if ( ! isset( $token->data->user->id ) ) { 358 | return new WP_Error( 359 | 'jwt_auth_bad_request', 360 | __( 'User ID not found in the token', 'simple-jwt-authentication' ), 361 | array( 362 | 'status' => 403, 363 | ) 364 | ); 365 | } 366 | 367 | // Custom validation against an UUID on user meta data. 368 | $jwt_data = get_user_meta( $token->data->user->id, 'jwt_data', true ) ?: false; 369 | if ( false === $jwt_data ) { 370 | return new WP_Error( 371 | 'jwt_auth_token_revoked', 372 | __( 'Token has been revoked.', 'simple-jwt-authentication' ), 373 | array( 374 | 'status' => 403, 375 | ) 376 | ); 377 | } 378 | 379 | $valid_token = false; 380 | // Loop through and check wether we have the current token uuid in the users meta. 381 | foreach ( $jwt_data as $key => $token_data ) { 382 | if ( $token_data['uuid'] === $token->uuid ) { 383 | $user_ip = ! empty( $_SERVER['REMOTE_ADDR'] ) ? $_SERVER['REMOTE_ADDR'] : __( 'Unknown', 'simple-jwt-authentication' ); 384 | $jwt_data[ $key ]['last_used'] = time(); 385 | $jwt_data[ $key ]['ua'] = $_SERVER['HTTP_USER_AGENT']; 386 | $jwt_data[ $key ]['ip'] = $user_ip; 387 | $valid_token = true; 388 | break; 389 | } 390 | } 391 | 392 | // Found no valid token. Return error. 393 | if ( false === $valid_token ) { 394 | return new WP_Error( 395 | 'jwt_auth_token_revoked', 396 | __( 'Token has been revoked.', 'simple-jwt-authentication' ), 397 | array( 398 | 'status' => 403, 399 | ) 400 | ); 401 | } 402 | 403 | // Everything looks good return the decoded token if the $output is false 404 | if ( ! $output ) { 405 | return $token; 406 | } 407 | // If the output is true return an answer to the request to show it. 408 | return array( 409 | 'code' => 'jwt_auth_valid_token', 410 | 'data' => array( 411 | 'status' => 200, 412 | ), 413 | ); 414 | } catch ( Exception $e ) { 415 | // Something is wrong trying to decode the token, send back the error. 416 | return new WP_Error( 417 | 'jwt_auth_invalid_token', 418 | $e->getMessage(), 419 | array( 420 | 'status' => 403, 421 | ) 422 | ); 423 | } 424 | } 425 | 426 | 427 | /** 428 | * Get a JWT in the header and generate a JWT 429 | * 430 | * @return mixed Either a WP_Error or an object with a JWT token. 431 | */ 432 | public function refresh_token() { 433 | //Check if the token is valid and get user information 434 | $token = $this->validate_token( false ); 435 | 436 | if ( is_wp_error( $token ) ) { 437 | return $token; 438 | } 439 | 440 | // Get the Secret Key 441 | $secret_key = Simple_Jwt_Authentication_Api::get_key(); 442 | if ( ! $secret_key ) { 443 | return new WP_Error( 444 | 'jwt_auth_bad_config', 445 | __( 'JWT is not configurated properly, please contact the admin. The key is missing.', 'simple-jwt-authentication' ), 446 | array( 447 | 'status' => 403, 448 | ) 449 | ); 450 | } 451 | 452 | $user = new WP_User( $token->data->user->id ); 453 | 454 | // The user exists create the according Token. 455 | $issued_at = time(); 456 | $not_before = apply_filters( 'jwt_auth_not_before', $issued_at ); 457 | $expire = apply_filters( 'jwt_auth_expire', $issued_at + ( DAY_IN_SECONDS * 7 ), $issued_at, $user ); 458 | $uuid = wp_generate_uuid4(); 459 | 460 | $token = array( 461 | 'uuid' => $uuid, 462 | 'iss' => get_bloginfo( 'url' ), 463 | 'iat' => $issued_at, 464 | 'nbf' => $not_before, 465 | 'exp' => $expire, 466 | 'data' => array( 467 | 'user' => array( 468 | 'id' => $user->data->ID, 469 | ), 470 | ), 471 | ); 472 | 473 | // Let the user modify the token data before the sign. 474 | $token = JWT::encode( apply_filters( 'jwt_auth_token_before_sign', $token, $user ), $secret_key ); 475 | 476 | // Setup some user meta data we can use for our UI. 477 | $jwt_data = get_user_meta( $user->data->ID, 'jwt_data', true ) ?: array(); 478 | $user_ip = Simple_Jwt_Authentication_Api::get_ip(); 479 | $jwt_data[] = array( 480 | 'uuid' => $uuid, 481 | 'issued_at' => $issued_at, 482 | 'expires' => $expire, 483 | 'ip' => $user_ip, 484 | 'ua' => $_SERVER['HTTP_USER_AGENT'], 485 | 'last_used' => time(), 486 | ); 487 | update_user_meta( $user->data->ID, 'jwt_data', apply_filters( 'simple_jwt_auth_save_user_data', $jwt_data ) ); 488 | 489 | // The token is signed, now create the object with no sensible user data to the client. 490 | $data = array( 491 | 'token' => $token, 492 | 'user_id' => $user->data->ID, 493 | 'user_email' => $user->data->user_email, 494 | 'user_nicename' => $user->data->user_nicename, 495 | 'user_display_name' => $user->data->display_name, 496 | 'token_expires' => $expire, 497 | ); 498 | 499 | // Let the user modify the data before send it back. 500 | return apply_filters( 'jwt_auth_token_before_dispatch', $data, $user ); 501 | } 502 | 503 | 504 | /** 505 | * Check if we should revoke a token. 506 | * 507 | * @since 1.0 508 | */ 509 | public function revoke_token() { 510 | $token = $this->validate_token( false ); 511 | 512 | if ( is_wp_error( $token ) ) { 513 | if ( $token->get_error_code() !== 'jwt_auth_no_auth_header' ) { 514 | // If there is a error, store it to show it after see rest_pre_dispatch. 515 | $this->jwt_error = $token; 516 | return false; 517 | } else { 518 | return false; 519 | } 520 | } 521 | 522 | $tokens = get_user_meta( $token->data->user->id, 'jwt_data', true ) ?: false; 523 | $token_uuid = $token->uuid; 524 | 525 | if ( $tokens ) { 526 | foreach ( $tokens as $key => $token_data ) { 527 | if ( $token_data['uuid'] === $token_uuid ) { 528 | unset( $tokens[ $key ] ); 529 | update_user_meta( $token->data->user->id, 'jwt_data', $tokens ); 530 | return array( 531 | 'code' => 'jwt_auth_revoked_token', 532 | 'data' => array( 533 | 'status' => 200, 534 | ), 535 | ); 536 | } 537 | } 538 | } 539 | 540 | return array( 541 | 'code' => 'jwt_auth_no_token_to_revoke', 542 | 'data' => array( 543 | 'status' => 403, 544 | ), 545 | ); 546 | 547 | } 548 | 549 | 550 | /** 551 | * Endpoint for requesting a password reset link. 552 | * This is a slightly modified version of what WP core uses. 553 | * 554 | * @param object $request The request object that come in from WP Rest API. 555 | * @since 1.0 556 | */ 557 | public function reset_password( $request ) { 558 | $username = $request->get_param( 'username' ); 559 | if ( ! $username ) { 560 | return array( 561 | 'code' => 'jwt_auth_invalid_username', 562 | 'message' => __( 'Error: Username or email not specified.', 'simple-jwt-authentication' ), 563 | 'data' => array( 564 | 'status' => 403, 565 | ), 566 | ); 567 | } elseif ( strpos( $username, '@' ) ) { 568 | $user_data = get_user_by( 'email', trim( $username ) ); 569 | } else { 570 | $user_data = get_user_by( 'login', trim( $username ) ); 571 | } 572 | 573 | global $wpdb, $current_site; 574 | 575 | do_action( 'lostpassword_post' ); 576 | if ( ! $user_data ) { 577 | return array( 578 | 'code' => 'jwt_auth_invalid_username', 579 | 'message' => __( 'Error: Invalid username.', 'simple-jwt-authentication' ), 580 | 'data' => array( 581 | 'status' => 403, 582 | ), 583 | ); 584 | } 585 | 586 | // redefining user_login ensures we return the right case in the email 587 | $user_login = $user_data->user_login; 588 | $user_email = $user_data->user_email; 589 | 590 | do_action( 'retreive_password', $user_login ); // Misspelled and deprecated 591 | do_action( 'retrieve_password', $user_login ); 592 | 593 | $allow = apply_filters( 'allow_password_reset', true, $user_data->ID ); 594 | 595 | if ( ! $allow ) { 596 | return array( 597 | 'code' => 'jwt_auth_reset_password_not_allowed', 598 | 'message' => __( 'Error: Resetting password is not allowed.', 'simple-jwt-authentication' ), 599 | 'data' => array( 600 | 'status' => 403, 601 | ), 602 | ); 603 | } elseif ( is_wp_error( $allow ) ) { 604 | return array( 605 | 'code' => 'jwt_auth_reset_password_not_allowed', 606 | 'message' => __( 'Error: Resetting password is not allowed.', 'simple-jwt-authentication' ), 607 | 'data' => array( 608 | 'status' => 403, 609 | ), 610 | ); 611 | } 612 | 613 | $key = get_password_reset_key( $user_data ); 614 | 615 | $message = __( 'Someone requested that the password be reset for the following account:' ) . "\r\n\r\n"; 616 | $message .= network_home_url( '/' ) . "\r\n\r\n"; 617 | // translators: %s is the users login name. 618 | $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n"; 619 | $message .= __( 'If this was a mistake, just ignore this email and nothing will happen.' ) . "\r\n\r\n"; 620 | $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n"; 621 | $message .= '<' . network_site_url( "wp-login.php?action=rp&key=$key&login=" . rawurlencode( $user_login ), 'login' ) . ">\r\n"; 622 | 623 | if ( is_multisite() ) { 624 | $blogname = $GLOBALS['current_site']->site_name; 625 | } else { 626 | // The blogname option is escaped with esc_html on the way into the database in sanitize_option 627 | // we want to reverse this for the plain text arena of emails. 628 | $blogname = wp_specialchars_decode( get_option( 'blogname' ), ENT_QUOTES ); 629 | } 630 | // translators: %s is the sites name (blogname) 631 | $title = sprintf( __( '[%s] Password Reset' ), $blogname ); 632 | 633 | $title = apply_filters( 'retrieve_password_title', $title ); 634 | $message = apply_filters( 'retrieve_password_message', $message, $key ); 635 | 636 | if ( $message && ! wp_mail( $user_email, $title, $message ) ) { 637 | wp_die( __( 'The e-mail could not be sent.' ) . "
\n" . __( 'Possible reason: your host may have disabled the mail() function...' ) ); // phpcs:ignore 638 | } 639 | 640 | return array( 641 | 'code' => 'jwt_auth_password_reset', 642 | 'message' => __( 'Success: an email for selecting a new password has been sent.', 'simple-jwt-authentication' ), 643 | 'data' => array( 644 | 'status' => 200, 645 | ), 646 | ); 647 | } 648 | 649 | /** 650 | * Filter to hook the rest_pre_dispatch, if the is an error in the request 651 | * send it, if there is no error just continue with the current request. 652 | * 653 | * @param $request 654 | * @since 1.0 655 | */ 656 | public function rest_pre_dispatch( $request ) { 657 | if ( is_wp_error( $this->jwt_error ) ) { 658 | return $this->jwt_error; 659 | } 660 | return $request; 661 | } 662 | 663 | } 664 | new Simple_Jwt_Authentication_Rest( $plugin_name, $plugin_version ); 665 | -------------------------------------------------------------------------------- /includes/vendor/donatj/phpuseragentparser/Tests/user_agents.json: -------------------------------------------------------------------------------- 1 | { 2 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X; en; rv:1.8.1.11) Gecko\/20071128 Camino\/1.5.4": { 3 | "platform": "Macintosh", 4 | "browser": "Camino", 5 | "version": "1.5.4" 6 | }, 7 | "Mozilla\/5.0 (Macintosh; U; PPC Mac OS X Mach-O; it; rv:1.8.1.21) Gecko\/20090327 Camino\/1.6.7 (MultiLang) (like Firefox\/2.0.0.21pre)": { 8 | "platform": "Macintosh", 9 | "browser": "Camino", 10 | "version": "1.6.7" 11 | }, 12 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10.6; en; rv:1.9.0.18) Gecko\/2010021619 Camino\/2.0.2 (like Firefox\/3.0.18)": { 13 | "platform": "Macintosh", 14 | "browser": "Camino", 15 | "version": "2.0.2" 16 | }, 17 | "Mozilla\/5.0 (Macintosh; U; PPC Mac OS X 10.5; it; rv:1.9.0.19) Gecko\/2010111021 Camino\/2.0.6 (MultiLang) (like Firefox\/3.0.19)": { 18 | "platform": "Macintosh", 19 | "browser": "Camino", 20 | "version": "2.0.6" 21 | }, 22 | "Mozilla\/5.0 (Macintosh; U; PPC Mac OS X 10.5; de; rv:1.9.2.28) Gecko\/20120308 Camino\/2.1.2 (MultiLang) (like Firefox\/3.6.28)": { 23 | "platform": "Macintosh", 24 | "browser": "Camino", 25 | "version": "2.1.2" 26 | }, 27 | "Mozilla\/5.0 (X11; U; CrOS i686 0.9.128; en-US) AppleWebKit\/534.10 (KHTML, like Gecko) Chrome\/8.0.552.339": { 28 | "platform": "Chrome OS", 29 | "browser": "Chrome", 30 | "version": "8.0.552.339" 31 | }, 32 | "Mozilla\/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit\/536.11 (KHTML, like Gecko) Chrome\/20.0.1132.57 Safari\/536.11": { 33 | "platform": "Chrome OS", 34 | "browser": "Chrome", 35 | "version": "20.0.1132.57" 36 | }, 37 | "Mozilla\/5.0 (X11; CrOS i686 3912.101.0) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/27.0.1453.116 Safari\/537.36": { 38 | "platform": "Chrome OS", 39 | "browser": "Chrome", 40 | "version": "27.0.1453.116" 41 | }, 42 | "Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/535.19 (KHTML, like Gecko) Ubuntu\/12.04 Chromium\/18.0.1025.151 Chrome\/18.0.1025.151 Safari\/535.19": { 43 | "platform": "Linux", 44 | "browser": "Chrome", 45 | "version": "18.0.1025.151" 46 | }, 47 | "Mozilla\/5.0 (X11; Linux i686) AppleWebKit\/536.11 (KHTML, like Gecko) Chrome\/20.0.1132.57 Safari\/536.11": { 48 | "platform": "Linux", 49 | "browser": "Chrome", 50 | "version": "20.0.1132.57" 51 | }, 52 | "Mozilla\/5.0 (X11; Linux x86_64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/28.0.1500.52 Safari\/537.36": { 53 | "platform": "Linux", 54 | "browser": "Chrome", 55 | "version": "28.0.1500.52" 56 | }, 57 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10_6_1; en-US) AppleWebKit\/532.0 (KHTML, like Gecko) Chrome\/4.0.209.0 Safari\/532.0": { 58 | "platform": "Macintosh", 59 | "browser": "Chrome", 60 | "version": "4.0.209.0" 61 | }, 62 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_6_7) AppleWebKit\/535.1 (KHTML, like Gecko) Chrome\/14.0.813.0 Safari\/535.1": { 63 | "platform": "Macintosh", 64 | "browser": "Chrome", 65 | "version": "14.0.813.0" 66 | }, 67 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/27.0.1453.93 Safari\/537.36": { 68 | "platform": "Macintosh", 69 | "browser": "Chrome", 70 | "version": "27.0.1453.93" 71 | }, 72 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit\/534.7 (KHTML, like Gecko) Chrome\/7.0.517.41 Safari\/534.7": { 73 | "platform": "Windows", 74 | "browser": "Chrome", 75 | "version": "7.0.517.41" 76 | }, 77 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit\/534.13 (KHTML, like Gecko) Chrome\/9.0.597.98 Safari\/534.13": { 78 | "platform": "Windows", 79 | "browser": "Chrome", 80 | "version": "9.0.597.98" 81 | }, 82 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit\/534.16 (KHTML, like Gecko) Chrome\/10.0.648.114 Safari\/534.16": { 83 | "platform": "Windows", 84 | "browser": "Chrome", 85 | "version": "10.0.648.114" 86 | }, 87 | "Mozilla\/5.0 (Windows NT 6.2; WOW64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/30.0.1599.17 Safari\/537.36": { 88 | "platform": "Windows", 89 | "browser": "Chrome", 90 | "version": "30.0.1599.17" 91 | }, 92 | "Mozilla\/5.0 (Windows NT 6.4; WOW64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/36.0.1985.143 Safari\/537.36 Edge\/12.0": { 93 | "platform": "Windows", 94 | "browser": "Edge", 95 | "version": "12.0" 96 | }, 97 | "Mozilla\/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/42.0.2311.135 Safari\/537.36 Edge\/12.10240": { 98 | "platform": "Windows", 99 | "browser": "Edge", 100 | "version": "12.10240" 101 | }, 102 | "Mozilla\/5.0 (X11; U; Linux i386; en-US; rv:1.7.5) Gecko\/20041109 Firefox\/1.0": { 103 | "platform": "Linux", 104 | "browser": "Firefox", 105 | "version": "1.0" 106 | }, 107 | "Mozilla\/5.0 (X11; U; Linux i686; en-US; rv:1.8.1) Gecko\/20061024 Iceweasel\/2.0 (Debian-2.0+dfsg-1)": { 108 | "platform": "Linux", 109 | "browser": "Firefox", 110 | "version": "2.0" 111 | }, 112 | "Mozilla\/5.0 (X11; U; Linux sparc64; es-PY; rv:5.0) Gecko\/20100101 IceCat\/5.0 (like Firefox\/5.0; Debian-6.0.1)": { 113 | "platform": "Linux", 114 | "browser": "Firefox", 115 | "version": "5.0" 116 | }, 117 | "Mozilla\/5.0 (X11; Linux i686; rv:7.0.1) Gecko\/20111106 IceCat\/7.0.1": { 118 | "platform": "Linux", 119 | "browser": "Firefox", 120 | "version": "7.0.1" 121 | }, 122 | "Mozilla\/5.0 (X11; Ubuntu; Linux x86_64; rv:12.0) Gecko\/20100101 Firefox\/12.0": { 123 | "platform": "Linux", 124 | "browser": "Firefox", 125 | "version": "12.0" 126 | }, 127 | "Mozilla\/5.0 (X11; Ubuntu; Linux i686; rv:14.0) Gecko\/20100101 Firefox\/14.0.1": { 128 | "platform": "Linux", 129 | "browser": "Firefox", 130 | "version": "14.0.1" 131 | }, 132 | "Mozilla\/5.0 (X11; Linux x86_64; rv:17.0) Gecko\/20121201 icecat\/17.0.1": { 133 | "platform": "Linux", 134 | "browser": "Firefox", 135 | "version": "17.0.1" 136 | }, 137 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko\/20100401 Firefox\/3.6.3": { 138 | "platform": "Macintosh", 139 | "browser": "Firefox", 140 | "version": "3.6.3" 141 | }, 142 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10.6; rv:11.0) Gecko\/20100101 Firefox\/11.0": { 143 | "platform": "Macintosh", 144 | "browser": "Firefox", 145 | "version": "11.0" 146 | }, 147 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10.6; rv:25.0) Gecko\/20100101 Firefox\/25.0": { 148 | "platform": "Macintosh", 149 | "browser": "Firefox", 150 | "version": "25.0" 151 | }, 152 | "Mozilla\/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.1.5) Gecko\/20091102 Firefox\/3.5.5 (.NET CLR 3.5.21022)": { 153 | "platform": "Windows", 154 | "browser": "Firefox", 155 | "version": "3.5.5" 156 | }, 157 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.15) Gecko\/20110303 Firefox\/3.6.15 FirePHP\/0.5": { 158 | "platform": "Windows", 159 | "browser": "Firefox", 160 | "version": "3.6.15" 161 | }, 162 | "Mozilla\/5.0 (Windows NT 6.1; Win64; x64; rv:25.0) Gecko\/20100101 Firefox\/25.0": { 163 | "platform": "Windows", 164 | "browser": "Firefox", 165 | "version": "25.0" 166 | }, 167 | "Mozilla\/5.0 (X11; U; Linux i686; de-DE; rv:1.8.1.9) Gecko\/20071103 Midori\/0.0.10": { 168 | "platform": "Linux", 169 | "browser": "Midori", 170 | "version": "0.0.10" 171 | }, 172 | "Mozilla\/5.0 (X11; U; Linux i686; de) AppleWebKit\/523+ (KHTML like Gecko) midori\/0.1": { 173 | "platform": "Linux", 174 | "browser": "Midori", 175 | "version": "0.1" 176 | }, 177 | "Midori\/0.3 (X11; Linux; U; en-us) WebKit\/531.2+": { 178 | "platform": "Linux", 179 | "browser": "Midori", 180 | "version": "0.3" 181 | }, 182 | "Mozilla\/5.0 (X11; Linux) AppleWebKit\/537.6 (KHTML, like Gecko) Chrome\/18.0.1025.133 Safari\/537.6 Midori\/0.4": { 183 | "platform": "Linux", 184 | "browser": "Midori", 185 | "version": "0.4" 186 | }, 187 | "Mozilla\/5.0 (X11; Linux; rv:2.0.1) Gecko\/20100101 Firefox\/4.0.1 Midori\/0.4": { 188 | "platform": "Linux", 189 | "browser": "Midori", 190 | "version": "0.4" 191 | }, 192 | "Mozilla\/5.0 (X11; Linux) AppleWebKit\/535.22 (KHTML, like Gecko) Chrome\/18.0.1025.133 Safari\/535.22 Midori\/0.5": { 193 | "platform": "Linux", 194 | "browser": "Midori", 195 | "version": "0.5" 196 | }, 197 | "Midori\/0.3 (Windows; Windows; U; en-en) WebKit\/532+ ": { 198 | "platform": "Windows", 199 | "browser": "Midori", 200 | "version": "0.3" 201 | }, 202 | "Mozilla\/4.0 (compatible;MSIE 6.0;Windows 98;Q312461)": { 203 | "platform": "Windows", 204 | "browser": "MSIE", 205 | "version": "6.0" 206 | }, 207 | "Mozilla\/4.0 (compatible; U; MSIE 6.0; Windows NT 5.1)": { 208 | "platform": "Windows", 209 | "browser": "MSIE", 210 | "version": "6.0" 211 | }, 212 | "Mozilla\/4.0 (Compatible; Windows NT 5.1; MSIE 6.0) (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)": { 213 | "platform": "Windows", 214 | "browser": "MSIE", 215 | "version": "6.0" 216 | }, 217 | "Mozilla\/4.0 (MSIE 6.0; Windows NT 5.0)": { 218 | "platform": "Windows", 219 | "browser": "MSIE", 220 | "version": "6.0" 221 | }, 222 | "Mozilla\/4.0 (MSIE 6.0; Windows NT 5.1)": { 223 | "platform": "Windows", 224 | "browser": "MSIE", 225 | "version": "6.0" 226 | }, 227 | "Mozilla\/4.0 (Windows; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)": { 228 | "platform": "Windows", 229 | "browser": "MSIE", 230 | "version": "6.0" 231 | }, 232 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla\/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; .NET4.0C; .NET4.0E)": { 233 | "platform": "Windows", 234 | "browser": "MSIE", 235 | "version": "7.0" 236 | }, 237 | "Mozilla\/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident\/4.0; Mozilla\/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; InfoPath.3; Tablet PC 2.0)": { 238 | "platform": "Windows", 239 | "browser": "MSIE", 240 | "version": "8.0" 241 | }, 242 | "Mozilla\/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident\/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; Tablet PC 2.0)": { 243 | "platform": "Windows", 244 | "browser": "MSIE", 245 | "version": "8.0" 246 | }, 247 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident\/5.0)": { 248 | "platform": "Windows", 249 | "browser": "MSIE", 250 | "version": "9.0" 251 | }, 252 | "Mozilla\/5.0 (Windows NT 6.3; Trident\/7.0; rv:11.0) like Gecko": { 253 | "platform": "Windows", 254 | "browser": "MSIE", 255 | "version": "11.0" 256 | }, 257 | "Mozilla\/5.0 (Windows NT 6.3; Win64, x64; Trident\/7.0; Touch; rv:11.0) like Gecko": { 258 | "platform": "Windows", 259 | "browser": "MSIE", 260 | "version": "11.0" 261 | }, 262 | "Mozilla\/5.0 (Windows NT 6.3; WOW64; Trident\/7.0; Touch; rv:11.0) like Gecko": { 263 | "platform": "Windows", 264 | "browser": "MSIE", 265 | "version": "11.0" 266 | }, 267 | "Opera\/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; Edition MacAppStore; en) Presto\/2.10.229 Version\/11.61": { 268 | "platform": "Macintosh", 269 | "browser": "Opera", 270 | "version": "11.61" 271 | }, 272 | "Mozilla\/4.0 (compatible; MSIE 8.0; Windows NT 6.1; de) Opera 11.01": { 273 | "platform": "Windows", 274 | "browser": "Opera", 275 | "version": "11.01" 276 | }, 277 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko\/20101213 Opera\/9.80 (Windows NT 6.1; U; zh-tw) Presto\/2.7.62 Version\/11.01": { 278 | "platform": "Windows", 279 | "browser": "Opera", 280 | "version": "11.01" 281 | }, 282 | "Mozilla\/5.0 (Windows NT 6.1; U; nl; rv:1.9.1.6) Gecko\/20091201 Firefox\/3.5.6 Opera 11.01": { 283 | "platform": "Windows", 284 | "browser": "Opera", 285 | "version": "11.01" 286 | }, 287 | "Opera\/9.80 (Windows NT 5.1; U; cs) Presto\/2.7.62 Version\/11.01": { 288 | "platform": "Windows", 289 | "browser": "Opera", 290 | "version": "11.01" 291 | }, 292 | "Opera\/9.80 (Windows NT 6.0; U; en) Presto\/2.8.99 Version\/11.10": { 293 | "platform": "Windows", 294 | "browser": "Opera", 295 | "version": "11.10" 296 | }, 297 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/28.0.1500.20 Safari\/537.36 OPR\/15.0.1147.18 (Edition Next)": { 298 | "platform": "Macintosh", 299 | "browser": "Opera Next", 300 | "version": "15.0.1147.18" 301 | }, 302 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/30.0.1599.66 Safari\/537.36 OPR\/17.0.1241.36 (Edition Next)": { 303 | "platform": "Macintosh", 304 | "browser": "Opera Next", 305 | "version": "17.0.1241.36" 306 | }, 307 | "Mozilla\/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit\/531.22.7 (KHTML, like Gecko) Version\/4.0.5 Safari\/531.22.7": { 308 | "platform": "Macintosh", 309 | "browser": "Safari", 310 | "version": "4.0.5" 311 | }, 312 | "Mozilla\/5.0 (Macintosh; U; PPC Mac OS X 10_4_11; en) AppleWebKit\/533.18.1 (KHTML, like Gecko) Version\/4.1.2 Safari\/533.18.5": { 313 | "platform": "Macintosh", 314 | "browser": "Safari", 315 | "version": "4.1.2" 316 | }, 317 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us) AppleWebKit\/533.16 (KHTML, like Gecko) Version\/5.0 Safari\/533.16": { 318 | "platform": "Macintosh", 319 | "browser": "Safari", 320 | "version": "5.0" 321 | }, 322 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_9) AppleWebKit\/537.71 (KHTML, like Gecko) Version\/7.0 Safari\/537.71": { 323 | "platform": "Macintosh", 324 | "browser": "Safari", 325 | "version": "7.0" 326 | }, 327 | "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10_10) AppleWebKit\/600.1.25 (KHTML, like Gecko) Version\/8.0 Safari\/600.1.25": { 328 | "platform": "Macintosh", 329 | "browser": "Safari", 330 | "version": "8.0" 331 | }, 332 | "Mozilla\/5.0 (Windows; U; Windows NT 6.1; tr-TR) AppleWebKit\/533.20.25 (KHTML, like Gecko) Version\/5.0.4 Safari\/533.20.27": { 333 | "platform": "Windows", 334 | "browser": "Safari", 335 | "version": "5.0.4" 336 | }, 337 | "Mozilla\/5.0 (X11; U; X11; en-US; Valve Steam Tenfoot\/1352503901; ) AppleWebKit\/535.15 (KHTML, like Gecko) Chrome\/18.0.989.0 Safari\/535.11": { 338 | "platform": "Linux", 339 | "browser": "Valve Steam Tenfoot", 340 | "version": "1352503901" 341 | }, 342 | "Mozilla\/5.0 (Macintosh; U; MacOS X 10_11_3; en-US; Valve Steam Tenfoot\/1456366706; ) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/47.0.2526.80 Safari\/537.36": { 343 | "platform": "Macintosh", 344 | "browser": "Valve Steam Tenfoot", 345 | "version": "1456366706" 346 | }, 347 | "Mozilla\/5.0 (Windows; U; Windows NT 6.2; en-US; Valve Steam Tenfoot\/1451690000; ) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/47.0.2526.49 Safari\/537.36": { 348 | "platform": "Windows", 349 | "browser": "Valve Steam Tenfoot", 350 | "version": "1451690000" 351 | }, 352 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10.6; en; rv:1.9.0.18) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/40.0.2214.89 Vivaldi\/1.0.83.38 Safari\/537.36": { 353 | "platform": "Macintosh", 354 | "browser": "Vivaldi", 355 | "version": "1.0.83.38" 356 | }, 357 | "Mozilla\/5.0 (Windows NT 6.3; WOW64) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/40.0.2214.89 Vivaldi\/1.0.83.38 Safari\/537.36": { 358 | "platform": "Windows", 359 | "browser": "Vivaldi", 360 | "version": "1.0.83.38" 361 | }, 362 | "Wget\/1.12 (linux-gnu)": { 363 | "platform": "Linux", 364 | "browser": "Wget", 365 | "version": "1.12" 366 | }, 367 | "Mozilla\/5.0 (Linux; U; Android 2.2; en-us; SGH-T959 Build\/FROYO) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1": { 368 | "platform": "Android", 369 | "browser": "Android Browser", 370 | "version": "4.0" 371 | }, 372 | "Mozilla\/5.0 (Linux; U; Android 3.2.1; hu-hu; Transformer TF101 Build\/HTK75) AppleWebKit\/534.13 (KHTML, like Gecko) Version\/4.0 Safari\/534.13": { 373 | "platform": "Android", 374 | "browser": "Android Browser", 375 | "version": "4.0" 376 | }, 377 | "Mozilla\/5.0 (Linux; U; Android Itfunz2.1_Eclipse_Terminator_II_Final; zh-cn; Milestone Build\/SHOLS_U2_02.36.0) AppleWebKit\/530.17 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/530.17": { 378 | "platform": "Android", 379 | "browser": "Android Browser", 380 | "version": "4.0" 381 | }, 382 | "Mozilla\/5.0 (Linux; Android 4.2.2; de-at; SAMSUNG GT-I9195\/I9195XXUAMF6 Build\/JDQ39) AppleWebKit\/535.19 (KHTML, like Gecko) Version\/1.0 Chrome\/18.0.1025.308 Mobile Safari\/535.19": { 383 | "platform": "Android", 384 | "browser": "Chrome", 385 | "version": "18.0.1025.308" 386 | }, 387 | "Mozilla\/5.0 (X11; U; Linux x86_64; en-gb) AppleWebKit\/534.35 (KHTML, like Gecko) Chrome\/11.0.696.65 Safari\/534.35 Puffin\/2.9174AP": { 388 | "platform": "Android", 389 | "browser": "Puffin", 390 | "version": "2.9174" 391 | }, 392 | "Mozilla\/5.0 (X11; U; Linux x86_64; en-us) AppleWebKit\/534.35 (KHTML, like Gecko) Chrome\/11.0.696.65 Safari\/534.35 Puffin\/2.9174AT": { 393 | "platform": "Android", 394 | "browser": "Puffin", 395 | "version": "2.9174" 396 | }, 397 | "Mozilla\/5.0 (Linux; Android 7.1.1; Nexus 5X Build\/N4F26O; en-us) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/42.0.2311.135 Mobile Safari\/537.36 Puffin\/6.0.7.15747AP": { 398 | "platform": "Android", 399 | "browser": "Puffin", 400 | "version": "6.0.7.15747" 401 | }, 402 | "Mozilla\/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925F Build\/LRX22G) AppleWebKit\/537.36 (KHTML, like Gecko) SamsungBrowser\/4.0 Chrome\/44.0.2403.133 Mobile Safari\/537.36": { 403 | "platform": "Android", 404 | "browser": "SamsungBrowser", 405 | "version": "4.0" 406 | }, 407 | "Mozilla\/5.0 (Linux; Android 5.0.2; SAMSUNG SM-G925K Build\/LRX22G) AppleWebKit\/537.36 (KHTML, like Gecko) SamsungBrowser\/4.0 Chrome\/44.0.2403.133 Mobile VR Safari\/537.36": { 408 | "platform": "Android", 409 | "browser": "SamsungBrowser", 410 | "version": "4.0" 411 | }, 412 | "Mozilla\/5.0 (Linux; U; Android 5.1.1; en-US; SM-G925F Build\/LMY47X) AppleWebKit\/534.30 (KHTML, like Gecko) Version\/4.0 UCBrowser\/10.7.5.658 U3\/0.8.0 Mobile Safari\/534.30": { 413 | "platform": "Android", 414 | "browser": "UC Browser", 415 | "version": "10.7.5.658" 416 | }, 417 | "Mozilla\/5.0 (BlackBerry; U; BlackBerry 9780; en) AppleWebKit\/534.8+ (KHTML, like Gecko) Version\/6.0.0.723 Mobile Safari\/534.8+": { 418 | "platform": "BlackBerry", 419 | "browser": "BlackBerry Browser", 420 | "version": "6.0.0.723" 421 | }, 422 | "Mozilla\/5.0 (BB10; Touch) AppleWebKit\/537.10+ (KHTML, like Gecko) Version\/10.0.9.2372 Mobile Safari\/537.10+": { 423 | "platform": "BlackBerry", 424 | "browser": "BlackBerry Browser", 425 | "version": "10.0.9.2372" 426 | }, 427 | "Mozilla\/5.0 (iPad; CPU OS 5_1_1 like Mac OS X) AppleWebKit\/534.46 (KHTML, like Gecko) CriOS\/29.0.1547.11 Mobile\/9B206 Safari\/7534.48.3": { 428 | "platform": "iPad", 429 | "browser": "Chrome", 430 | "version": "29.0.1547.11" 431 | }, 432 | "Mozilla\/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit\/537.51.1 (KHTML, like Gecko) CriOS\/30.0.1599.12 Mobile\/11A465 Safari\/8536.25": { 433 | "platform": "iPad", 434 | "browser": "Chrome", 435 | "version": "30.0.1599.12" 436 | }, 437 | "Mozilla\/5.0 (X11; U; Linux x86_64; en-AU) AppleWebKit\/534.35 (KHTML, like Gecko) Chrome\/11.0.696.65 Safari\/534.35 Puffin\/3.9174IT": { 438 | "platform": "iPad", 439 | "browser": "Puffin", 440 | "version": "3.9174" 441 | }, 442 | "Mozilla\/5.0 (iPad; U; CPU iPhone OS 3_2 like Mac OS X; en-us) AppleWebKit\/531.21.10 (KHTML, like Gecko) Version\/4.0.4 Mobile\/7B314 Safari\/531.21.10": { 443 | "platform": "iPad", 444 | "browser": "Safari", 445 | "version": "4.0.4" 446 | }, 447 | "Mozilla\/5.0 (iPad; U; CPU OS 3_2 like Mac OS X; en-us) AppleWebKit\/531.21.10 (KHTML, like Gecko) Version\/4.0.4 Mobile\/7B334b Safari\/531.21.10": { 448 | "platform": "iPad", 449 | "browser": "Safari", 450 | "version": "4.0.4" 451 | }, 452 | "Mozilla\/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit\/534.46.0 (KHTML, like Gecko) CriOS\/19.0.1084.60 Mobile\/9B206 Safari\/7534.48.3": { 453 | "platform": "iPhone", 454 | "browser": "Chrome", 455 | "version": "19.0.1084.60" 456 | }, 457 | "Mozilla\/5.0 (iPhone; CPU iPhone OS 7_0_2 like Mac OS X) AppleWebKit\/537.51.1 (KHTML, like Gecko) CriOS\/30.0.1599.12 Mobile\/11A501 Safari\/8536.25": { 458 | "platform": "iPhone", 459 | "browser": "Chrome", 460 | "version": "30.0.1599.12" 461 | }, 462 | "Mozilla\/5.0 (iPod; U; CPU iPhone OS 6_1 like Mac OS X; en-HK) AppleWebKit\/534.35 (KHTML, like Gecko) Chrome\/11.0.696.65 Safari\/534.35 Puffin\/3.9174IP Mobile": { 463 | "platform": "iPhone", 464 | "browser": "Puffin", 465 | "version": "3.9174" 466 | }, 467 | "Mozilla\/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit\/420+ (KHTML, like Gecko) Version\/3.0 Mobile\/1C25 Safari\/419.3": { 468 | "platform": "iPhone", 469 | "browser": "Safari", 470 | "version": "3.0" 471 | }, 472 | "Mozilla\/5.0 (iPhone; U; CPU iPhone OS 4_2_6 like Mac OS X; en-us) AppleWebKit\/533.17.9 (KHTML, like Gecko) Version\/5.0.2 Mobile\/8E200 Safari\/6533.18.5": { 473 | "platform": "iPhone", 474 | "browser": "Safari", 475 | "version": "5.0.2" 476 | }, 477 | "Mozilla\/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit\/420.1 (KHTML, like Gecko) Version\/3.0 Mobile\/3A101a Safari\/419.3": { 478 | "platform": "iPod", 479 | "browser": "Safari", 480 | "version": "3.0" 481 | }, 482 | "Mozilla\/5.0 (iPod; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit\/534.46 (KHTML, like Gecko) Version\/5.1 Mobile\/9A405 Safari\/7534.48.3": { 483 | "platform": "iPod", 484 | "browser": "Safari", 485 | "version": "5.1" 486 | }, 487 | "Mozilla\/5.0 (iPod touch; CPU iPhone OS 7_0_3 like Mac OS X) AppleWebKit\/537.51.1 (KHTML, like Gecko) Version\/7.0 Mobile\/11B511 Safari\/9537.53": { 488 | "platform": "iPod", 489 | "browser": "Safari", 490 | "version": "7.0" 491 | }, 492 | "Mozilla\/4.0 (compatible; Linux 2.6.10) NetFront\/3.3 Kindle\/1.0 (screen 600x800)": { 493 | "platform": "Kindle", 494 | "browser": "Kindle", 495 | "version": "1.0" 496 | }, 497 | "Mozilla\/5.0 (Linux; U; en-US) AppleWebKit\/528.5+ (KHTML, like Gecko, Safari\/528.5+) Version\/4.0 Kindle\/3.0 (screen 600x800; rotate)": { 498 | "platform": "Kindle", 499 | "browser": "Kindle", 500 | "version": "3.0" 501 | }, 502 | "Mozilla\/5.0 (Linux; U; en-US) AppleWebKit\/528.5+ (KHTML, like Gecko, Safari\/538.5+) Version\/4.0 Kindle\/3.0 (screen 600x800; rotate)": { 503 | "platform": "Kindle", 504 | "browser": "Kindle", 505 | "version": "3.0" 506 | }, 507 | "Mozilla\/5.0 (Linux; U; Android 2.3.4; en-us; Kindle Fire Build\/GINGERBREAD) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1": { 508 | "platform": "Kindle Fire", 509 | "browser": "Kindle", 510 | "version": "4.0" 511 | }, 512 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk\/1.0.13.81_10003810) AppleWebKit\/533.16 (KHTML, like Gecko) Version\/5.0 Safari\/533.16 Silk-Accelerated=true": { 513 | "platform": "Kindle Fire", 514 | "browser": "Silk", 515 | "version": "1.0.13.81" 516 | }, 517 | "Mozilla\/5.0 (Linux; U; Android 2.3.4; en-us; Silk\/1.1.0-84) AppleWebKit\/533.1 (KHTML, like Gecko) Version\/4.0 Mobile Safari\/533.1 Silk-Accelerated=false": { 518 | "platform": "Kindle Fire", 519 | "browser": "Silk", 520 | "version": "1.1.0" 521 | }, 522 | "Mozilla\/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk\/1.1.0-80) AppleWebKit\/533.16 (KHTML, like Gecko) Version\/5.0 Safari\/533.16 Silk-Accelerated=true": { 523 | "platform": "Kindle Fire", 524 | "browser": "Silk", 525 | "version": "1.1.0" 526 | }, 527 | "Mozilla\/5.0 (Linux; U; Android 4.0.3; en-us; KFTT Build\/IML74K) AppleWebKit\/535.19 (KHTML, like Gecko) Silk\/2.1 Mobile Safari\/535.19 Silk-Accelerated=true": { 528 | "platform": "Kindle Fire", 529 | "browser": "Silk", 530 | "version": "2.1" 531 | }, 532 | "Mozilla\/5.0 (Linux; U; en-us; KFTT Build\/IML74K) AppleWebKit\/535.19 (KHTML, like Gecko) Silk\/2.2 Safari\/535.19 Silk-Accelerated=true": { 533 | "platform": "Kindle Fire", 534 | "browser": "Silk", 535 | "version": "2.2" 536 | }, 537 | "Mozilla\/5.0 (Linux; U; Android 4.2.2; en-us; KFTHWI Build\/JDQ39) AppleWebKit\/537.36 (KHTML, like Gecko) Silk\/3.22 like Chrome\/34.0.1847.137 Mobile Safari\/537.36": { 538 | "platform": "Kindle Fire", 539 | "browser": "Silk", 540 | "version": "3.22" 541 | }, 542 | "Mozilla\/5.0 (Linux; U; Android 4.2.2; en-us; KFTHWI Build\/JDQ39) AppleWebKit\/537.36 (KHTML, like Gecko) Silk\/3.22 like Chrome\/34.0.1847.137 Safari\/537.36": { 543 | "platform": "Kindle Fire", 544 | "browser": "Silk", 545 | "version": "3.22" 546 | }, 547 | "Mozilla\/5.0 (X11; Linux x86_64; U; en-us) AppleWebKit\/537.36 (KHTML, like Gecko) Silk\/3.22 like Chrome\/34.0.1847.137 Safari\/537.36": { 548 | "platform": "Kindle Fire", 549 | "browser": "Silk", 550 | "version": "3.22" 551 | }, 552 | "Mozilla\/5.0 (New Nintendo 3DS like iPhone) AppleWebKit\/536.30 (KHTML, like Gecko) NX\/3.0.0.5.8 Mobile NintendoBrowser\/1.0.9934.JP": { 553 | "platform": "New Nintendo 3DS", 554 | "browser": "NintendoBrowser", 555 | "version": "1.0.9934.JP" 556 | }, 557 | "Mozilla\/5.0 (Nintendo 3DS; U; ; en) Version\/1.7498.US": { 558 | "platform": "Nintendo 3DS", 559 | "browser": "NintendoBrowser", 560 | "version": "1.7498.US" 561 | }, 562 | "Mozilla\/5.0 (Nintendo 3DS; U; ; en) Version\/1.7552.EU": { 563 | "platform": "Nintendo 3DS", 564 | "browser": "NintendoBrowser", 565 | "version": "1.7552.EU" 566 | }, 567 | "Bunjalloo\/0.7.6(Nintendo DS;U;en)": { 568 | "platform": "Nintendo DS", 569 | "browser": "Bunjalloo", 570 | "version": "0.7.6" 571 | }, 572 | "Opera\/9.00 (Nintendo Wii; U; ; 1038-58; Wii Shop Channel\/1.0; en)": { 573 | "platform": "Nintendo Wii", 574 | "browser": "Opera", 575 | "version": "9.00" 576 | }, 577 | "Opera\/9.30 (Nintendo Wii; U; ; 2071; Wii Shop Channel\/1.0; en)": { 578 | "platform": "Nintendo Wii", 579 | "browser": "Opera", 580 | "version": "9.30" 581 | }, 582 | "Mozilla\/5.0 (Nintendo WiiU) AppleWebKit\/534.52 (KHTML, like Gecko) NX\/2.1.0.8.21 NintendoBrowser\/1.0.0.7494.US": { 583 | "platform": "Nintendo WiiU", 584 | "browser": "NintendoBrowser", 585 | "version": "1.0.0.7494.US" 586 | }, 587 | "Mozilla\/5.0 (PlayBook; U; RIM Tablet OS 2.0.1; en-US) AppleWebKit\/535.8+ (KHTML, like Gecko) Version\/7.2.0.1 Safari\/535.8+": { 588 | "platform": "PlayBook", 589 | "browser": "BlackBerry Browser", 590 | "version": "7.2.0.1" 591 | }, 592 | "Mozilla\/4.0 (PS3 (PlayStation 3); 1.00)": { 593 | "platform": "PlayStation 3", 594 | "browser": "NetFront", 595 | "version": "1.00" 596 | }, 597 | "Mozilla\/5.0 (PLAYSTATION 3; 1.00)": { 598 | "platform": "PlayStation 3", 599 | "browser": "NetFront", 600 | "version": "1.00" 601 | }, 602 | "Mozilla\/5.0 (PLAYSTATION 3; 3.55)": { 603 | "platform": "PlayStation 3", 604 | "browser": "NetFront", 605 | "version": "3.55" 606 | }, 607 | "Mozilla\/5.0 (PLAYSTATION 3 4.20) AppleWebKit\/531.22.8 (KHTML, like Gecko)": { 608 | "platform": "PlayStation 3", 609 | "browser": "NetFront", 610 | "version": "4.20" 611 | }, 612 | "Mozilla\/5.0 (PlayStation 4 1.50) AppleWebKit\/536.26 (KHTML, like Gecko)": { 613 | "platform": "PlayStation 4", 614 | "browser": "NetFront", 615 | "version": "1.50" 616 | }, 617 | "Mozilla\/5.0 (Playstation Vita 1.50) AppleWebKit\/531.22.8 (KHTML, like Gecko) Silk\/3.2": { 618 | "platform": "PlayStation Vita", 619 | "browser": "Browser", 620 | "version": "1.50" 621 | }, 622 | "Mozilla\/5.0 (PlayStation Vita 1.69) AppleWebKit\/531.22.8 (KHTML, like Gecko) Silk\/3.2": { 623 | "platform": "PlayStation Vita", 624 | "browser": "Browser", 625 | "version": "1.69" 626 | }, 627 | "Mozilla\/5.0 (Linux; Tizen 2.3; SAMSUNG SM-Z130H) AppleWebKit\/537.3 (KHTML, like Gecko) SamsungBrowser\/1.0 ": { 628 | "platform": "Tizen", 629 | "browser": "SamsungBrowser", 630 | "version": "1.0" 631 | }, 632 | "Mozilla\/5.0 (Linux; U; Tizen 2.0; en-us) AppleWebKit\/537.1 (KHTML, like Gecko) Mobile TizenBrowser\/2": { 633 | "platform": "Tizen", 634 | "browser": "TizenBrowser", 635 | "version": "2" 636 | }, 637 | "Mozilla\/5.0 (Windows Phone 10.0; Android 4.2.1; Nokia; Lumia 520) AppleWebKit\/537.36 (KHTML, like Gecko) Chrome\/42.0.2311.135 Mobile Safari\/537.36 Edge\/12.10130": { 638 | "platform": "Windows Phone", 639 | "browser": "Edge", 640 | "version": "12.10130" 641 | }, 642 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident\/3.1; IEMobile\/7.0) Asus;Galaxy6": { 643 | "platform": "Windows Phone", 644 | "browser": "IEMobile", 645 | "version": "7.0" 646 | }, 647 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident\/3.1; IEMobile\/7.0) LG;LG-E900h)": { 648 | "platform": "Windows Phone", 649 | "browser": "IEMobile", 650 | "version": "7.0" 651 | }, 652 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.0; Trident\/3.1; IEMobile\/7.0; LG; GW910)": { 653 | "platform": "Windows Phone", 654 | "browser": "IEMobile", 655 | "version": "7.0" 656 | }, 657 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0)": { 658 | "platform": "Windows Phone", 659 | "browser": "IEMobile", 660 | "version": "9.0" 661 | }, 662 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0; SAMSUNG; SGH-i917)": { 663 | "platform": "Windows Phone", 664 | "browser": "IEMobile", 665 | "version": "9.0" 666 | }, 667 | "Mozilla\/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident\/6.0; IEMobile\/10.0; ARM; Touch; NOKIA; Lumia 820)": { 668 | "platform": "Windows Phone", 669 | "browser": "IEMobile", 670 | "version": "10.0" 671 | }, 672 | "Mozilla\/5.0 (compatible; MSIE 10.0; Windows Phone 8.0; Trident\/6.0; IEMobile\/10.0; ARM; Touch; rv:11; NOKIA; Lumia 920) like Gecko": { 673 | "platform": "Windows Phone", 674 | "browser": "IEMobile", 675 | "version": "10.0" 676 | }, 677 | "Mozilla\/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident\/7.0; Touch; rv:11.0; IEMobile\/11.0; NOKIA; Lumia 520) like iPhone OS 7_0_3 Mac OS X AppleWebKit\/537 (KHTML, like Gecko) Mobile Safari\/537": { 678 | "platform": "Windows Phone", 679 | "browser": "IEMobile", 680 | "version": "11.0" 681 | }, 682 | "Mozilla\/5.0 (Mobile; Windows Phone 8.1; Android 4.0; ARM; Trident\/7.0; Touch; rv:11.0; IEMobile\/11.0; NOKIA; Lumia 920; Vodafone ES) like iPhone OS 7_0_3 Mac OS X AppleWebkit\/537 (KHTML, like Gecko) Mobile Safari\/537": { 683 | "platform": "Windows Phone", 684 | "browser": "IEMobile", 685 | "version": "11.0" 686 | }, 687 | "Mozilla\/5.0 (Windows Phone 8.1; ARM; Trident\/7.0;Touch; rv:11.0; IEMobile\/11.0; Microsoft; Virtual) like Gecko": { 688 | "platform": "Windows Phone", 689 | "browser": "IEMobile", 690 | "version": "11.0" 691 | }, 692 | "Mozilla\/5.0 (Windows Phone 8.1; ARM; Trident\/7.0; Touch; rv:11; IEMobile\/11.0) like Android 4.1.2; compatible) like iPhone OS 7_0_3 Mac OS X WebKit\/537.36 (KHTML, like Gecko) Chrome\/32.0.1700.99 Mobile Safari\/537.36": { 693 | "platform": "Windows Phone", 694 | "browser": "IEMobile", 695 | "version": "11.0" 696 | }, 697 | "Mozilla\/5.0 (Windows Phone 8.1; ARM; Trident\/7.0; Touch; rv:11; IEMobile\/11.0) like Android 4.1.2; compatible) like iPhone OS 7_0_3 MacWebKit\/537.36 (KHTML, like Gecko) Chrome\/32.0.1700.99 Mobile Safari \/537.36": { 698 | "platform": "Windows Phone", 699 | "browser": "IEMobile", 700 | "version": "11.0" 701 | }, 702 | "Mozilla\/5.0 (Windows Phone 8.1; ARM; Trident\/7.0; Touch; rv:11; IEMobile\/11.0; NOKIA; Lumia 928) like Gecko": { 703 | "platform": "Windows Phone", 704 | "browser": "IEMobile", 705 | "version": "11.0" 706 | }, 707 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows Phone OS 7.5; Trident\/3.1; IEMobile\/7.0; Xbox)": { 708 | "platform": "Xbox", 709 | "browser": "IEMobile", 710 | "version": "7.0" 711 | }, 712 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident\/5.0; Xbox), or Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0; Xbox)": { 713 | "platform": "Xbox", 714 | "browser": "IEMobile", 715 | "version": "9.0" 716 | }, 717 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/9.0; Xbox)": { 718 | "platform": "Xbox", 719 | "browser": "IEMobile", 720 | "version": "9.0" 721 | }, 722 | "Mozilla\/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident\/3.1; Xbox)": { 723 | "platform": "Xbox", 724 | "browser": "MSIE", 725 | "version": "7.0" 726 | }, 727 | "Mozilla\/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident\/5.0; Xbox)": { 728 | "platform": "Xbox", 729 | "browser": "MSIE", 730 | "version": "9.0" 731 | }, 732 | "Mozilla\/5.0 (compatible; MSIE 10.0; Windows Phone OS 7.5; Trident\/5.0; IEMobile\/10.0; Xbox; Xbox One)": { 733 | "platform": "Xbox One", 734 | "browser": "IEMobile", 735 | "version": "10.0" 736 | }, 737 | "Mozilla\/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Trident\/6.0; Xbox; Xbox One)": { 738 | "platform": "Xbox One", 739 | "browser": "MSIE", 740 | "version": "10.0" 741 | }, 742 | "AdsBot-Google (+http:\/\/www.google.com\/adsbot.html)": { 743 | "platform": null, 744 | "browser": "AdsBot-Google", 745 | "version": null 746 | }, 747 | "Mozilla\/5.0 (compatible; Baiduspider\/2.0; +http:\/\/www.baidu.com\/search\/spider.html)": { 748 | "platform": null, 749 | "browser": "Baiduspider", 750 | "version": "2.0" 751 | }, 752 | "Mozilla\/5.0 (compatible; bingbot\/2.0; +http:\/\/www.bing.com\/bingbot.htm)": { 753 | "platform": null, 754 | "browser": "bingbot", 755 | "version": "2.0" 756 | }, 757 | "curl\/7.19.7 (universal-apple-darwin10.0) libcurl\/7.19.7 OpenSSL\/0.9.8r zlib\/1.2.3": { 758 | "platform": null, 759 | "browser": "curl", 760 | "version": "7.19.7" 761 | }, 762 | "facebookexternalhit\/1.1 (+http:\/\/www.facebook.com\/externalhit_uatext.php)": { 763 | "platform": null, 764 | "browser": "facebookexternalhit", 765 | "version": "1.1" 766 | }, 767 | "FeedValidator\/1.3": { 768 | "platform": null, 769 | "browser": "FeedValidator", 770 | "version": "1.3" 771 | }, 772 | "Googlebot\/1.3": { 773 | "platform": null, 774 | "browser": "Googlebot", 775 | "version": "1.3" 776 | }, 777 | "Mozilla\/5.0 (compatible; Googlebot\/2.1; +http:\/\/www.google.com\/bot.html)": { 778 | "platform": null, 779 | "browser": "Googlebot", 780 | "version": "2.1" 781 | }, 782 | "Googlebot-Image\/1.0": { 783 | "platform": null, 784 | "browser": "Googlebot-Image", 785 | "version": "1.0" 786 | }, 787 | "Googlebot-Video\/1.0": { 788 | "platform": null, 789 | "browser": "Googlebot-Video", 790 | "version": "1.0" 791 | }, 792 | "Lynx\/2.8.6rel.4 libwww-FM\/2.14 SSL-MM\/1.4.1 OpenSSL\/0.9.7l Lynxlet\/0.7.0": { 793 | "platform": null, 794 | "browser": "Lynx", 795 | "version": "2.8.6rel.4" 796 | }, 797 | "msnbot-media\/1.1 (+http:\/\/search.msn.com\/msnbot.htm)": { 798 | "platform": null, 799 | "browser": "msnbot-media", 800 | "version": "1.1" 801 | }, 802 | "WordPress\/3.7.1; http:\/\/wordpress.com": { 803 | "platform": null, 804 | "browser": "WordPress", 805 | "version": "3.7.1" 806 | }, 807 | "Mozilla\/5.0 (compatible; YandexBot\/3.0; +http:\/\/yandex.com\/bots)": { 808 | "platform": null, 809 | "browser": "YandexBot", 810 | "version": "3.0" 811 | } 812 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | {one line to give the program's name and a brief idea of what it does.} 635 | Copyright (C) {year} {name of author} 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | {project} Copyright (C) {year} {fullname} 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------