', 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 |
--------------------------------------------------------------------------------