PluginProbe
HTTP Headers / 1.15.0
HTTP Headers v1.15.0
1.19.5 trunk 1.0.0 1.1.0 1.1.1 1.1.2 1.10.0 1.10.1 1.10.2 1.10.3 1.10.4 1.10.5 1.11.0 1.12.0 1.12.1 1.12.2 1.13.0 1.13.1 1.13.2 1.13.3 1.13.4 1.14.0 1.14.1 1.14.2 1.15.0 All 60 releases
http-headers / http-headers.php

http-headers.php in HTTP Headers 1.15.0, at http-headers.php

1,432 lines 51.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: HTTP Headers
4 Plugin URI: https://zinoui.com/blog/http-headers-for-wordpress
5 Description: A plugin for HTTP headers management including security, access-control (CORS), caching, compression, and authentication.
6 Version: 1.15.0
7 Author: Dimitar Ivanov
8 Author URI: https://zinoui.com
9 License: GPLv2 or later
10 Text Domain: http-headers
11 */
12
13 /*
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License
16 as published by the Free Software Foundation; either version 2
17 of the License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/copyleft/gpl.html>.
26
27 Copyright (c) 2017-2020 Zino UI
28 */
29
30 if (!defined('ABSPATH')) {
31 exit;
32 }
33
34 $options = include dirname(__FILE__) . '/views/includes/options.inc.php';
35 foreach ($options as $option) {
36 if (get_option($option[0]) === false) {
37 add_option($option[0], $option[1], null, 'yes');
38 }
39 }
40
41 function build_csp_value($value) {
42 $csp = array();
43 foreach ($value as $key => $val)
44 {
45 if (is_array($val))
46 {
47 $source = NULL;
48 if (isset($val['source']))
49 {
50 $source = $val['source'];
51 unset($val['source']);
52 }
53 if (!empty($val))
54 {
55 $val = join(" ", array_keys($val));
56 if ($source)
57 {
58 $val .= " " . $source;
59 }
60 $csp[] = sprintf("%s %s", $key, $val);
61 } elseif ($source) {
62 $csp[] = sprintf("%s %s", $key, $source);
63 }
64 } else {
65 if (in_array($key, array('block-all-mixed-content', 'upgrade-insecure-requests')))
66 {
67 $csp[] = $key;
68 }
69 if (in_array($key, array('plugin-types', 'report-to')) && !empty($val))
70 {
71 $csp[] = sprintf("%s %s", $key, $val);
72 }
73 }
74 }
75
76 if (!$csp)
77 {
78 return NULL;
79 }
80
81 return join('; ', $csp);
82 }
83
84 function get_http_headers() {
85 $statuses = array();
86 $unset = array();
87 $headers = array();
88 $append = array();
89 if (get_option('hh_x_frame_options') == 1) {
90 $x_frame_options_value = strtoupper(get_option('hh_x_frame_options_value'));
91 if ($x_frame_options_value == 'ALLOW-FROM') {
92 $x_frame_options_value .= ' ' . get_option('hh_x_frame_options_domain');
93 }
94 $headers['X-Frame-Options'] = $x_frame_options_value;
95 }
96 if (get_option('hh_x_powered_by') == 1) {
97 if (get_option('hh_x_powered_by_option') == 'set') {
98 $headers['X-Powered-By'] = get_option('hh_x_powered_by_value');
99 } else {
100 $unset[] = 'X-Powered-By';
101 }
102 }
103 if (get_option('hh_x_xxs_protection') == 1) {
104 $headers['X-XSS-Protection'] = get_option('hh_x_xxs_protection_value');
105 if ($headers['X-XSS-Protection'] == '1; report=') {
106 $headers['X-XSS-Protection'] .= get_option('hh_x_xxs_protection_uri');
107 }
108 }
109 if (get_option('hh_x_content_type_options') == 1) {
110 $headers['X-Content-Type-Options'] = get_option('hh_x_content_type_options_value');
111 }
112 if (get_option('hh_x_download_options') == 1) {
113 $headers['X-Download-Options'] = get_option('hh_x_download_options_value');
114 }
115 if (get_option('hh_x_permitted_cross_domain_policies') == 1) {
116 $headers['X-Permitted-Cross-Domain-Policies'] = get_option('hh_x_permitted_cross_domain_policies_value');
117 }
118 if (get_option('hh_x_dns_prefetch_control') == 1) {
119 $headers['X-DNS-Prefetch-Control'] = get_option('hh_x_dns_prefetch_control_value');
120 }
121 if (get_option('hh_connection') == 1) {
122 $headers['Connection'] = get_option('hh_connection_value');
123 }
124 if (get_option('hh_pragma') == 1) {
125 $headers['Pragma'] = get_option('hh_pragma_value');
126 }
127 if (get_option('hh_age') == 1) {
128 $headers['Age'] = sprintf("%u", get_option('hh_age_value'));
129 }
130 if (get_option('hh_cache_control') == 1) {
131 $hh_cache_control_value = get_option('hh_cache_control_value', array());
132 $tmp = array();
133 foreach ($hh_cache_control_value as $k => $v) {
134 if (in_array($k, array('max-age', 's-maxage', 'stale-while-revalidate', 'stale-if-error'))) {
135 if (strlen($v) > 0) {
136 $tmp[] = sprintf("%s=%u", $k, $v);
137 }
138 } else {
139 $tmp[] = $k;
140 }
141 }
142 $hh_cache_control_value = join(', ', $tmp);
143 $headers['Cache-Control'] = $hh_cache_control_value;
144 }
145 if (get_option('hh_strict_transport_security') == 1) {
146 $hh_strict_transport_security = array();
147
148 $hh_strict_transport_security_max_age = get_option('hh_strict_transport_security_max_age');
149 if ($hh_strict_transport_security_max_age !== false)
150 {
151 $hh_strict_transport_security[] = sprintf('max-age=%u', get_option('hh_strict_transport_security_max_age'));
152 if (get_option('hh_strict_transport_security_sub_domains'))
153 {
154 $hh_strict_transport_security[] = 'includeSubDomains';
155 }
156 if (get_option('hh_strict_transport_security_preload'))
157 {
158 $hh_strict_transport_security[] = 'preload';
159 }
160 } else {
161 $hh_strict_transport_security = array(get_option('hh_strict_transport_security_value'));
162 }
163 $headers['Strict-Transport-Security'] = join('; ', $hh_strict_transport_security);
164 }
165 if (get_option('hh_x_ua_compatible') == 1) {
166 $headers['X-UA-Compatible'] = get_option('hh_x_ua_compatible_value');
167 }
168
169 if (get_option('hh_content_security_policy') == 1)
170 {
171 $value = get_option('hh_content_security_policy_value');
172 $csp = build_csp_value($value);
173 if ($csp)
174 {
175 $csp_report_only = get_option('hh_content_security_policy_report_only');
176 $headers['Content-Security-Policy'.($csp_report_only ? '-Report-Only' : NULL)] = $csp;
177 }
178 }
179
180 if (get_option('hh_access_control_allow_origin') == 1)
181 {
182 $value = get_option('hh_access_control_allow_origin_value');
183 switch ($value)
184 {
185 case 'origin':
186 $value = get_option('hh_access_control_allow_origin_url', array());
187 if (is_scalar($value))
188 {
189 $value = array($value);
190 }
191 break;
192 }
193 if (!empty($value))
194 {
195 $headers['Access-Control-Allow-Origin'] = $value;
196 }
197 }
198 if (get_option('hh_access_control_allow_credentials') == 1)
199 {
200 $headers['Access-Control-Allow-Credentials'] = get_option('hh_access_control_allow_credentials_value');
201 }
202 if (get_option('hh_access_control_max_age') == 1)
203 {
204 $value = get_option('hh_access_control_max_age_value');
205 if (!empty($value))
206 {
207 $headers['Access-Control-Max-Age'] = intval($value);
208 }
209 }
210 if (get_option('hh_access_control_allow_methods') == 1)
211 {
212 $value = get_option('hh_access_control_allow_methods_value');
213 if (!empty($value))
214 {
215 $headers['Access-Control-Allow-Methods'] = join(', ', array_keys($value));
216 }
217 }
218 if (get_option('hh_access_control_allow_headers') == 1)
219 {
220 $tmp = array();
221 $value = get_option('hh_access_control_allow_headers_value');
222 if (!empty($value))
223 {
224 $tmp = array_merge($tmp, array_keys($value));
225 }
226 $custom = get_option('hh_access_control_allow_headers_custom');
227 if (!empty($custom))
228 {
229 $tmp = array_merge($tmp, $custom);
230 }
231 if ($tmp)
232 {
233 $tmp = array_filter($tmp, 'trim');
234 $tmp = array_unique($tmp);
235 $headers['Access-Control-Allow-Headers'] = join(', ', $tmp);
236 }
237 }
238 if (get_option('hh_access_control_expose_headers') == 1)
239 {
240 $tmp = array();
241 $value = get_option('hh_access_control_expose_headers_value');
242 if (!empty($value))
243 {
244 $tmp = array_merge($tmp, array_keys($value));
245 }
246 $custom = get_option('hh_access_control_expose_headers_custom');
247 if (!empty($custom))
248 {
249 $tmp = array_merge($tmp, $custom);
250 }
251 if ($tmp)
252 {
253 $tmp = array_filter($tmp, 'trim');
254 $tmp = array_unique($tmp);
255 $headers['Access-Control-Expose-Headers'] = join(', ', $tmp);
256 }
257 }
258 if (get_option('hh_p3p') == 1)
259 {
260 $value = get_option('hh_p3p_value');
261 if (!empty($value))
262 {
263 $headers['P3P'] = 'CP="' . join(' ', array_keys($value)) . '"';
264 }
265 }
266 if (get_option('hh_referrer_policy') == 1) {
267 $headers['Referrer-Policy'] = get_option('hh_referrer_policy_value');
268 }
269 if (get_option('hh_cross_origin_resource_policy') == 1) {
270 $headers['Cross-Origin-Resource-Policy'] = get_option('hh_cross_origin_resource_policy_value');
271 }
272 if (get_option('hh_www_authenticate') == 1) {
273
274 switch (get_option('hh_www_authenticate_type')) {
275 case 'Basic':
276 if (!(isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])
277 && $_SERVER['PHP_AUTH_USER'] == get_option('hh_www_authenticate_user')
278 && $_SERVER['PHP_AUTH_PW'] == get_option('hh_www_authenticate_pswd'))) {
279 $headers['WWW-Authenticate'] = sprintf("Basic realm='%s'", get_option('hh_www_authenticate_realm'));
280 $statuses['HTTP/1.1'] = '401 Unauthorized';
281 }
282 break;
283 case 'Digest':
284 if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
285 $realm = get_option('hh_www_authenticate_realm');
286 $headers['WWW-Authenticate'] = sprintf("Digest realm='%s',qop='auth',nonce='%s',opaque='%s'",
287 $realm, uniqid(), md5($realm));
288 $statuses['HTTP/1.1'] = '401 Unauthorized';
289 }
290 break;
291 }
292 }
293 if (get_option('hh_vary') == 1)
294 {
295 $value = get_option('hh_vary_value');
296 if (!empty($value))
297 {
298 $append['Vary'] = join(', ', array_keys($value));
299 }
300 }
301
302 if (get_option('hh_expect_ct') == 1) {
303 $expect_ct_max_age = get_option('hh_expect_ct_max_age');
304 $expect_ct_report_uri = get_option('hh_expect_ct_report_uri');
305 if (!empty($expect_ct_report_uri) && !empty($expect_ct_max_age)) {
306
307 $expect_ct = array();
308 $expect_ct[] = sprintf("max-age=%u", $expect_ct_max_age);
309 if (get_option('hh_expect_ct_enforce') == 1) {
310 $expect_ct[] = "enforce";
311 }
312 $expect_ct[] = sprintf('report-uri="%s"', $expect_ct_report_uri);
313 $headers['Expect-CT'] = join(', ', $expect_ct);
314 }
315 }
316 if (get_option('hh_custom_headers') == 1) {
317 $custom_headers = get_option('hh_custom_headers_value');
318 if (isset($custom_headers['name'], $custom_headers['value']) && !empty($custom_headers['name'])) {
319 foreach ($custom_headers['name'] as $key => $name) {
320 $name = trim($name);
321 $value = trim($custom_headers['value'][$key]);
322 if (empty($name) || empty($value)) {
323 continue;
324 }
325 $headers[$name] = $value;
326 }
327 }
328 }
329 if (get_option('hh_report_to') == 1) {
330 $report_to = get_option('hh_report_to_value');
331 $tmp = array();
332 foreach ($report_to as $item)
333 {
334 $tmp[] = sprintf('{"url": "%s", "group": "%s", "max-age": %u%s}',
335 $item['url'], $item['group'], $item['max-age'], isset($item['includeSubDomains']) ? ', includeSubDomains' : NULL);
336 }
337 if ($tmp)
338 {
339 $headers['Report-To'] = join(', ', $tmp);
340 }
341 }
342 if (get_option('hh_feature_policy') == 1) {
343 $feature_policy_feature = get_option('hh_feature_policy_feature');
344 $feature_policy_value = get_option('hh_feature_policy_value');
345 $feature_policy_origin = get_option('hh_feature_policy_origin');
346 $tmp = array();
347 $feature_policy_feature = is_array($feature_policy_feature) ? $feature_policy_feature : array();
348 foreach (array_keys($feature_policy_feature) as $feature)
349 {
350 $value = NULL;
351 switch ($feature_policy_value[$feature])
352 {
353 case '*':
354 case "'none'":
355 $value = $feature_policy_value[$feature];
356 break;
357 case "'self'":
358 $value = $feature_policy_value[$feature];
359 if (!empty($feature_policy_origin[$feature]))
360 {
361 $value .= " " . $feature_policy_origin[$feature];
362 }
363 break;
364 case 'origin(s)':
365 $value = $feature_policy_origin[$feature];
366 break;
367 }
368
369 $tmp[] = sprintf("%s %s", $feature, $value);
370 }
371 if ($tmp)
372 {
373 $headers['Feature-Policy'] = join('; ', $tmp);
374 }
375 }
376
377 return array($headers, $statuses, $unset, $append);
378 }
379
380 function http_digest_parse($txt) {
381 $txt = stripslashes($txt);
382
383 $needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
384 $data = array();
385 $keys = implode('|', array_keys($needed_parts));
386
387 preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
388
389 foreach ($matches as $m) {
390 $data[$m[1]] = $m[3] ? $m[3] : $m[4];
391 unset($needed_parts[$m[1]]);
392 }
393
394 return $needed_parts ? false : $data;
395 }
396
397 function php_auth_digest() {
398 if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || get_option('hh_www_authenticate_user') != $data['username']) {
399 die('Wrong Credentials!');
400 }
401
402 $A1 = md5($data['username'] . ':' . get_option('hh_www_authenticate_realm') . ':' . get_option('hh_www_authenticate_pswd'));
403 $A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
404 $valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
405 if ($data['response'] != $valid_response) {
406 die('Wrong Credentials!');
407 }
408 }
409
410 function php_content_encoding() {
411 if (substr_count($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
412 ob_start('ob_gzhandler');
413 } else {
414 ob_start();
415 }
416 }
417
418 function php_cookie_security_directives() {
419 $lines = array();
420 if (get_option('hh_cookie_security') == 1) {
421 $value = get_option('hh_cookie_security_value', array());
422 if (isset($value['HttpOnly'])) {
423 $lines[] = 'session.cookie_httponly = on';
424 }
425 if (isset($value['Secure'])) {
426 $lines[] = 'session.cookie_secure = on';
427 }
428 if (isset($value['SameSite']) && in_array($value['SameSite'], array('None', 'Lax', 'Strict'))) {
429 $lines[] = sprintf('session.cookie_samesite = "%s"', $value['SameSite']);
430 }
431 }
432
433 return $lines;
434 }
435
436 function http_headers() {
437 if (get_option('hh_method') !== 'php') {
438 return;
439 }
440 // PHP method below
441 list($headers, $statuses, $unset, $append) = get_http_headers();
442 $isCors = false;
443 foreach ($headers as $key => $value) {
444 if ($key == 'Access-Control-Allow-Origin') {
445 if (isset($_SERVER['HTTP_ORIGIN'])) {
446 if (in_array($value, array('*', 'null'))) {
447 $isCors = true;
448 header(sprintf("%s: *", $key));
449 }
450
451 if (is_array($value) && in_array($_SERVER['HTTP_ORIGIN'], $value)) {
452 $isCors = true;
453 header(sprintf("%s: %s", $key, $_SERVER['HTTP_ORIGIN']));
454 header("Vary: Origin", false);
455 }
456 }
457 continue;
458 }
459 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
460 if ($isCors) {
461 header(sprintf("%s: %s", $key, $value));
462 }
463 continue;
464 }
465 header(sprintf("%s: %s", $key, $value));
466 }
467 foreach ($append as $key => $value) {
468 header(sprintf("%s: %s", $key, $value), false);
469 }
470 foreach ($unset as $header) {
471 if (function_exists('header_remove')) {
472 header_remove($header);
473 } else {
474 header("$header:");
475 }
476 }
477 foreach ($statuses as $key => $value) {
478 header(sprintf("%s %s", $key, $value));
479 exit;
480 }
481
482 if (get_option('hh_www_authenticate') == 1) {
483 php_auth_digest();
484 }
485
486 if (get_option('hh_content_encoding') == 1) {
487 php_content_encoding();
488 }
489 }
490
491 function http_headers_admin_add_page() {
492 add_options_page('HTTP Headers', 'HTTP Headers', 'manage_options', 'http-headers', 'http_headers_admin_page');
493 }
494
495 function http_headers_admin() {
496 register_setting('http-headers-mtd', 'hh_method');
497 register_setting('http-headers-xfo', 'hh_x_frame_options');
498 register_setting('http-headers-xfo', 'hh_x_frame_options_value');
499 register_setting('http-headers-xfo', 'hh_x_frame_options_domain');
500 register_setting('http-headers-xss', 'hh_x_xxs_protection');
501 register_setting('http-headers-xss', 'hh_x_xxs_protection_value');
502 register_setting('http-headers-xss', 'hh_x_xxs_protection_uri');
503 register_setting('http-headers-cto', 'hh_x_content_type_options');
504 register_setting('http-headers-cto', 'hh_x_content_type_options_value');
505 register_setting('http-headers-sts', 'hh_strict_transport_security');
506 register_setting('http-headers-sts', 'hh_strict_transport_security_value'); //obsolete
507 register_setting('http-headers-sts', 'hh_strict_transport_security_max_age');
508 register_setting('http-headers-sts', 'hh_strict_transport_security_sub_domains');
509 register_setting('http-headers-sts', 'hh_strict_transport_security_preload');
510 register_setting('http-headers-uac', 'hh_x_ua_compatible');
511 register_setting('http-headers-uac', 'hh_x_ua_compatible_value');
512 register_setting('http-headers-p3p', 'hh_p3p');
513 register_setting('http-headers-p3p', 'hh_p3p_value');
514 register_setting('http-headers-rp', 'hh_referrer_policy');
515 register_setting('http-headers-rp', 'hh_referrer_policy_value');
516 register_setting('http-headers-csp', 'hh_content_security_policy');
517 register_setting('http-headers-csp', 'hh_content_security_policy_value');
518 register_setting('http-headers-csp', 'hh_content_security_policy_report_only');
519 register_setting('http-headers-acao', 'hh_access_control_allow_origin');
520 register_setting('http-headers-acao', 'hh_access_control_allow_origin_value');
521 register_setting('http-headers-acao', 'hh_access_control_allow_origin_url');
522 register_setting('http-headers-acac', 'hh_access_control_allow_credentials');
523 register_setting('http-headers-acac', 'hh_access_control_allow_credentials_value');
524 register_setting('http-headers-acam', 'hh_access_control_allow_methods');
525 register_setting('http-headers-acam', 'hh_access_control_allow_methods_value');
526 register_setting('http-headers-acah', 'hh_access_control_allow_headers');
527 register_setting('http-headers-acah', 'hh_access_control_allow_headers_value');
528 register_setting('http-headers-acah', 'hh_access_control_allow_headers_custom');
529 register_setting('http-headers-aceh', 'hh_access_control_expose_headers');
530 register_setting('http-headers-aceh', 'hh_access_control_expose_headers_value');
531 register_setting('http-headers-aceh', 'hh_access_control_expose_headers_custom');
532 register_setting('http-headers-acma', 'hh_access_control_max_age');
533 register_setting('http-headers-acma', 'hh_access_control_max_age_value');
534 register_setting('http-headers-ce', 'hh_content_encoding');
535 register_setting('http-headers-ce', 'hh_content_encoding_module');
536 register_setting('http-headers-ce', 'hh_content_encoding_value');
537 register_setting('http-headers-ce', 'hh_content_encoding_ext');
538 register_setting('http-headers-vary', 'hh_vary');
539 register_setting('http-headers-vary', 'hh_vary_value');
540 register_setting('http-headers-xpb', 'hh_x_powered_by');
541 register_setting('http-headers-xpb', 'hh_x_powered_by_option');
542 register_setting('http-headers-xpb', 'hh_x_powered_by_value');
543 register_setting('http-headers-wwa', 'hh_www_authenticate');
544 register_setting('http-headers-wwa', 'hh_www_authenticate_type');
545 register_setting('http-headers-wwa', 'hh_www_authenticate_realm');
546 register_setting('http-headers-wwa', 'hh_www_authenticate_user');
547 register_setting('http-headers-wwa', 'hh_www_authenticate_pswd');
548 register_setting('http-headers-cc', 'hh_cache_control');
549 register_setting('http-headers-cc', 'hh_cache_control_value');
550 register_setting('http-headers-age', 'hh_age');
551 register_setting('http-headers-age', 'hh_age_value');
552 register_setting('http-headers-pra', 'hh_pragma');
553 register_setting('http-headers-pra', 'hh_pragma_value');
554 register_setting('http-headers-exp', 'hh_expires');
555 register_setting('http-headers-exp', 'hh_expires_value');
556 register_setting('http-headers-exp', 'hh_expires_type');
557 register_setting('http-headers-con', 'hh_connection');
558 register_setting('http-headers-con', 'hh_connection_value');
559 register_setting('http-headers-cose', 'hh_cookie_security');
560 register_setting('http-headers-cose', 'hh_cookie_security_value');
561 register_setting('http-headers-ect', 'hh_expect_ct');
562 register_setting('http-headers-ect', 'hh_expect_ct_max_age');
563 register_setting('http-headers-ect', 'hh_expect_ct_report_uri');
564 register_setting('http-headers-ect', 'hh_expect_ct_enforce');
565 register_setting('http-headers-tao', 'hh_timing_allow_origin');
566 register_setting('http-headers-tao', 'hh_timing_allow_origin_value');
567 register_setting('http-headers-tao', 'hh_timing_allow_origin_url');
568 register_setting('http-headers-che', 'hh_custom_headers');
569 register_setting('http-headers-che', 'hh_custom_headers_value');
570 register_setting('http-headers-xdo', 'hh_x_download_options');
571 register_setting('http-headers-xdo', 'hh_x_download_options_value');
572 register_setting('http-headers-xpcd', 'hh_x_permitted_cross_domain_policies');
573 register_setting('http-headers-xpcd', 'hh_x_permitted_cross_domain_policies_value');
574 register_setting('http-headers-xdpc', 'hh_x_dns_prefetch_control');
575 register_setting('http-headers-xdpc', 'hh_x_dns_prefetch_control_value');
576 register_setting('http-headers-rt', 'hh_report_to');
577 register_setting('http-headers-rt', 'hh_report_to_value');
578 register_setting('http-headers-fp', 'hh_feature_policy');
579 register_setting('http-headers-fp', 'hh_feature_policy_value');
580 register_setting('http-headers-fp', 'hh_feature_policy_feature');
581 register_setting('http-headers-fp', 'hh_feature_policy_origin');
582 register_setting('http-headers-csd', 'hh_clear_site_data');
583 register_setting('http-headers-csd', 'hh_clear_site_data_value');
584 register_setting('http-headers-cty', 'hh_content_type');
585 register_setting('http-headers-cty', 'hh_content_type_value');
586 register_setting('http-headers-corp', 'hh_cross_origin_resource_policy');
587 register_setting('http-headers-corp', 'hh_cross_origin_resource_policy_value');
588 }
589
590 function http_headers_option($option) {
591
592 include_once ABSPATH . 'wp-admin/includes/admin.php';
593
594 if (isset($_POST['hh_method']))
595 {
596 check_admin_referer('http-headers-mtd-options');
597 # When method is changed
598 http_headers_activate();
599
600 } elseif (get_option('hh_method') == 'htaccess') {
601 # When particular header is changed
602 switch (true) {
603 case array_key_exists('hh_www_authenticate', $_POST):
604 check_admin_referer('http-headers-wwa-options');
605 update_auth_credentials();
606 update_auth_directives();
607 break;
608 case array_key_exists('hh_content_encoding', $_POST):
609 check_admin_referer('http-headers-ce-options');
610 update_content_encoding_directives();
611 break;
612 case array_key_exists('hh_content_type', $_POST):
613 check_admin_referer('http-headers-cty-options');
614 update_content_type_directives();
615 break;
616 case array_key_exists('hh_expires', $_POST):
617 check_admin_referer('http-headers-exp-options');
618 update_expires_directives();
619 break;
620 case array_key_exists('hh_cookie_security', $_POST):
621 check_admin_referer('http-headers-cose-options');
622 update_cookie_security_directives();
623 break;
624 case array_key_exists('hh_timing_allow_origin', $_POST):
625 check_admin_referer('http-headers-tao-options');
626 update_timing_directives();
627 break;
628 case array_key_exists('option_page', $_POST) && strpos($_POST['option_page'], 'http-headers-') === 0:
629 check_admin_referer($_POST['option_page'].'-options');
630 update_headers_directives();
631 break;
632 }
633 }
634 }
635
636 function nginx_headers_directives() {
637 $lines = array();
638 list($headers, $statuses, $unset, $append) = get_http_headers();
639
640 foreach ($unset as $header) {
641 $lines[] = sprintf(' more_clear_headers "%s";', $header);
642 }
643 $cors = $cors_header = $cors_inner = $cors_footer = array();
644 $all = array();
645 foreach ($headers as $key => $value) {
646 if (in_array($key, array('WWW-Authenticate'))) {
647 continue;
648 }
649 if (in_array($key, array('X-Content-Type-Options'))) {
650 $all[] = sprintf('add_header %s %s always;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
651 continue;
652 }
653 if ($key == 'Access-Control-Allow-Origin' && is_array($value)) {
654 $cors_header[] = sprintf('if ($http_origin ~* ^(%s)$) {', str_replace('.', '\.', join('|', $value)));
655 $cors_footer[] = '}';
656 $cors_inner[] = ' add_header Access-Control-Allow-Origin "$http_origin";';
657 if (!in_array('*', $value))
658 {
659 $cors_inner[] = ' add_header Vary "Origin";';
660 }
661 continue;
662 }
663 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
664 $cors_inner[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
665 continue;
666 }
667 $lines[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
668 }
669 foreach ($append as $key => $value) {
670 $lines[] = sprintf(' add_header %s %s;', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
671 }
672 if (!empty($cors_inner))
673 {
674 $cors = array_merge(
675 $cors_header,
676 $cors_inner,
677 $cors_footer
678 );
679 }
680 if (!empty($lines)) {
681 $lines = array_merge(
682 $all,
683 $cors,
684 array('location ~* \.(php|html)$ {'),
685 $lines,
686 array('}')
687 );
688 }
689 return $lines;
690 }
691
692 function nginx_content_encoding_directives() {
693 $lines = array();
694 if (get_option('hh_content_encoding') == 1) {
695
696 $lines[] = 'gzip on;';
697
698 $content_encoding_value = get_option('hh_content_encoding_value');
699 if (!$content_encoding_value) {
700 $content_encoding_value = array();
701 }
702
703 $content_encoding_ext = get_option('hh_content_encoding_ext');
704 if (!$content_encoding_ext) {
705 $content_encoding_ext = array();
706 }
707 if (!empty($content_encoding_ext)) {
708 //$lines[] = sprintf('<FilesMatch "\.(%s)$">', join('|', array_keys($content_encoding_ext)));
709 }
710 if (!empty($content_encoding_value)) {
711 $lines[] = sprintf('gzip_types %s;', join(' ', array_keys($content_encoding_value)));
712 }
713 }
714 return $lines;
715 }
716
717 function nginx_content_type_directives() {
718 $lines = array();
719 if (get_option('hh_content_type') == 1) {
720 $values = get_option('hh_content_type_value', array());
721 foreach ($values as $ext => $media_type) {
722 $lines[] = sprintf("%s %s;", $media_type, $ext);
723 }
724 }
725
726 return $lines;
727 }
728
729 function nginx_expires_directives() {
730 $lines = array();
731 if (get_option('hh_expires') == 1) {
732
733 $types = get_option('hh_expires_type', array());
734 $values = get_option('hh_expires_value', array());
735
736 $lines[] = 'map $sent_http_content_type $expires {';
737 foreach ($types as $type => $whatever) {
738 list($base, $period, $suffix) = explode('_', $values[$type]);
739 if (in_array($base, array('access', 'modification'))) {
740 $lines[] = $type != 'default'
741 ? sprintf(' %s %u%s;', $type, $period, $suffix[0])
742 : sprintf(' default %u%s;', $period, $suffix[0]);
743 } elseif ($base == 'invalid') {
744 $lines[] = $type != 'default'
745 ? sprintf(' %s 0;', $type)
746 : sprintf(' default 0;');
747 }
748 }
749 $lines[] = '}';
750
751 $lines[] = 'expires $expires;';
752 }
753 return $lines;
754 }
755
756 function nginx_timing_directives() {
757 $lines = array();
758 if (get_option('hh_timing_allow_origin') == 1) {
759 $value = get_option('hh_timing_allow_origin_value');
760 switch ($value)
761 {
762 case 'origin':
763 $value = get_option('hh_timing_allow_origin_url');
764 break;
765 }
766 if (!empty($value))
767 {
768 $lines[] = 'location ~* \.(js|css|jpe?g|png|gif|eot|otf|svg|ttf|woff2?)$ {';
769 $lines[] = sprintf(' add_header Timing-Allow-Origin "%s";', $value);
770 $lines[] = '}';
771 }
772 }
773 return $lines;
774 }
775
776 function nginx_auth_directives() {
777 $lines = array();
778 if (get_option('hh_www_authenticate') == 1) {
779
780 $type = get_option('hh_www_authenticate_type');
781
782 $file = $type == 'Basic' ? '.hh-htpasswd' : '.hh-htdigest';
783
784 $lines[] = 'location ~ ^\.hh-ht(digest|passwd)$ {';
785 $lines[] = ' deny all;';
786 $lines[] = '}';
787
788 $lines[] = sprintf('location %s {', get_home_path());
789 if ($type == 'Basic') {
790 $lines[] = sprintf(' auth_basic "%s";', get_option('hh_www_authenticate_realm'));
791 $lines[] = sprintf(' auth_basic_user_file %s%s;', get_home_path(), $file);
792 } else {
793 $lines[] = sprintf(' auth_digest "%s";', get_option('hh_www_authenticate_realm'));
794 $lines[] = sprintf(' auth_digest_user_file %s%s;', get_home_path(), $file);
795 }
796 $lines[] = '}';
797 }
798 return $lines;
799 }
800
801 function nginx_auth_credentials() {
802 return apache_auth_credentials();
803 }
804
805 function nginx_cookie_security_directives() {
806 $lines = array();
807
808 //TODO
809
810 return $lines;
811 }
812
813 function nginx_check_requirements() {
814 //TODO scheduled for v2.0.0
815 return true;
816 }
817
818 function iis_headers_directives() {
819 //TODO scheduled for v2.0.0
820 }
821
822 function iis_content_encoding_directives() {
823 //TODO scheduled for v2.0.0
824 }
825
826 function iis_content_type_directives() {
827 //TODO scheduled for v2.0.0
828 }
829
830 function iis_expires_directives() {
831 //TODO scheduled for v2.0.0
832 }
833
834 function iis_timing_directives() {
835 //TODO scheduled for v2.0.0
836 }
837
838 function iis_auth_directives() {
839 //TODO scheduled for v2.0.0
840 }
841
842 function iis_auth_credentials() {
843 //TODO scheduled for v2.0.0
844 }
845
846 function iis_cookie_security_directives() {
847 //TODO scheduled for v2.0.0
848 }
849
850 function iis_check_requirements() {
851 //TODO scheduled for v2.0.0
852 return true;
853 }
854
855 function apache_headers_directives() {
856 $lines = array();
857 list($headers, $statuses, $unset, $append) = get_http_headers();
858
859 foreach ($unset as $header) {
860 $lines[] = sprintf(' Header always unset %s', $header);
861 $lines[] = sprintf(' Header unset %s', $header);
862 }
863 $all = array();
864 foreach ($headers as $key => $value) {
865 if (in_array($key, array('WWW-Authenticate'))) {
866 continue;
867 }
868 if (in_array($key, array('X-Content-Type-Options'))) {
869 $all[] = sprintf(' Header always set %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
870 continue;
871 }
872 if ($key == 'Strict-Transport-Security') {
873 $lines[] = sprintf(' Header set %s %s env=HTTPS', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
874 continue;
875 }
876 if ($key == 'Access-Control-Allow-Origin') {
877 $all[] = ' <IfModule mod_setenvif.c>';
878 if (!is_array($value)) {
879 if ($value) {
880 $value = array($value);
881 } else {
882 $value = array();
883 }
884 }
885 //$value[] = 'null';
886 if (is_array($value))
887 {
888 $all[] = sprintf(' SetEnvIf Origin "^(%s)$" CORS=$0', str_replace(array('.', '*'), array('\.', '\*'), join('|', $value)));
889 } else {
890 $all[] = ' SetEnvIf Origin "^(.+)$" CORS=$0';
891 }
892 $all[] = ' </IfModule>';
893 $all[] = ' Header set Access-Control-Allow-Origin %{CORS}e env=CORS';
894 if (!in_array('*', $value))
895 {
896 $all[] = ' Header append Vary "Origin" env=CORS';
897 }
898 continue;
899 }
900 if (in_array($key, array('Access-Control-Allow-Headers', 'Access-Control-Allow-Methods', 'Access-Control-Allow-Credentials', 'Access-Control-Max-Age', 'Access-Control-Expose-Headers'))) {
901 $all[] = sprintf(' Header set %s %s env=CORS', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
902 continue;
903 }
904 $lines[] = sprintf(' Header set %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
905 }
906 foreach ($append as $key => $value) {
907 $lines[] = sprintf(' Header append %s %s', $key, sprintf('%1$s%2$s%1$s', strpos($value, '"') === false ? '"' : "'", $value));
908 }
909 if (!empty($lines) || !empty($all)) {
910 $lines = array_merge(
911 array('<IfModule mod_headers.c>'),
912 $all,
913 array(' <FilesMatch "\.(php|html)$">'),
914 $lines,
915 array(' </FilesMatch>', '</IfModule>')
916 );
917 }
918 return $lines;
919 }
920
921 function apache_content_encoding_directives() {
922 $lines = array();
923 if (get_option('hh_content_encoding') == 1) {
924
925 $content_encoding_module = get_option('hh_content_encoding_module');
926
927 $module = 'mod_deflate.c';
928 $filter = 'DEFLATE';
929 $accept_encoding = 'gzip';
930
931 if ($content_encoding_module == 'brotli') {
932 $module = 'mod_brotli.c';
933 $filter = 'BROTLI_COMPRESS';
934 $accept_encoding = 'br';
935 }
936
937 $content_encoding_value = get_option('hh_content_encoding_value');
938 if (!$content_encoding_value) {
939 $content_encoding_value = array();
940 }
941
942 $content_encoding_ext = get_option('hh_content_encoding_ext');
943 if (!$content_encoding_ext) {
944 $content_encoding_ext = array();
945 }
946
947 $type = join('|', array_keys($content_encoding_value));
948 $ext = join('|', array_keys($content_encoding_ext));
949
950 if (!empty($type) && !empty($ext)) {
951 $expression = sprintf('(%%{CONTENT_TYPE} =~ m#^(%1$s)# || %%{REQUEST_FILENAME} =~ /.(%2$s)$/)', $type, $ext);
952 } elseif (!empty($type)) {
953 $expression = sprintf('%%{CONTENT_TYPE} =~ m#^(%1$s)#', $type);
954 } elseif (!empty($ext)) {
955 $expression = sprintf('%%{REQUEST_FILENAME} =~ /.(%1$s)$/', $ext);
956 }
957
958 if (isset($expression)) {
959 $lines[] = '<IfModule mod_filter.c>';
960 $lines[] = ' FilterDeclare HttpHeaders';
961 if (in_array($content_encoding_module, array('brotli', 'deflate'))) {
962 $lines[] = sprintf('<IfModule %s>', $module);
963 $lines[] = sprintf(' FilterProvider HttpHeaders %1$s "%%{HTTP:Accept-Encoding} =~ /%2$s/ && %3$s"', $filter, $accept_encoding, $expression);
964 $lines[] = ' </IfModule>';
965 } else {
966 $lines[] = ' <IfModule mod_deflate.c>';
967 $lines[] = ' <IfModule !mod_brotli.c>';
968 $lines[] = sprintf(' FilterProvider HttpHeaders DEFLATE "%%{HTTP:Accept-Encoding} =~ /gzip/ && %1$s"', $expression);
969 $lines[] = ' </IfModule>';
970 $lines[] = ' </IfModule>';
971 $lines[] = ' <IfModule mod_brotli.c>';
972 $lines[] = sprintf(' FilterProvider HttpHeaders BROTLI_COMPRESS "%%{HTTP:Accept-Encoding} =~ /br/ && %1$s"', $expression);
973 $lines[] = ' </IfModule>';
974 }
975 $lines[] = ' FilterChain HttpHeaders';
976 $lines[] = '</IfModule>';
977 }
978 }
979
980 return $lines;
981 }
982
983 function apache_expires_directives() {
984 $lines = array();
985 if (get_option('hh_expires') == 1) {
986
987 $types = get_option('hh_expires_type', array());
988 $values = get_option('hh_expires_value', array());
989
990 $lines[] = '<IfModule mod_expires.c>';
991 $lines[] = ' ExpiresActive On';
992 foreach ($types as $type => $whatever) {
993 list($base, $period, $suffix) = explode('_', $values[$type]);
994 if (in_array($base, array('access', 'modification'))) {
995 $lines[] = $type != 'default'
996 ? sprintf(' ExpiresByType %s "%s plus %u %s"', $type, $base, $period, $suffix)
997 : sprintf(' ExpiresDefault "%s plus %u %s"', $base, $period, $suffix);
998 } elseif ($base == 'invalid') {
999 $lines[] = $type != 'default'
1000 ? sprintf(' ExpiresByType %s A0', $type)
1001 : sprintf(' ExpiresDefault A0');
1002 }
1003 }
1004 $lines[] = '</IfModule>';
1005 }
1006
1007 return $lines;
1008 }
1009
1010 function apache_content_type_directives() {
1011 $lines = array();
1012 if (get_option('hh_content_type') == 1) {
1013 $values = get_option('hh_content_type_value', array());
1014 $lines[] = '<IfModule mod_mime.c>';
1015 foreach ($values as $ext => $media_type) {
1016 $lines[] = sprintf(" AddType %s .%s", $media_type, $ext);
1017 }
1018 $lines[] = '</IfModule>';
1019 }
1020
1021 return $lines;
1022 }
1023
1024 function apache_timing_directives() {
1025 $lines = array();
1026 if (get_option('hh_timing_allow_origin') == 1) {
1027 $value = get_option('hh_timing_allow_origin_value');
1028 switch ($value)
1029 {
1030 case 'origin':
1031 $value = get_option('hh_timing_allow_origin_url');
1032 break;
1033 }
1034 if (!empty($value))
1035 {
1036 $lines[] = '<IfModule mod_headers.c>';
1037 $lines[] = ' <FilesMatch "\\.(js|css|jpe?g|png|gif|eot|otf|svg|ttf|woff2?)$">';
1038 $lines[] = sprintf(' Header set Timing-Allow-Origin "%s"', $value);
1039 $lines[] = ' </FilesMatch>';
1040 $lines[] = '</IfModule>';
1041 }
1042 }
1043
1044 return $lines;
1045 }
1046
1047 function apache_auth_directives() {
1048 $lines = array();
1049 if (get_option('hh_www_authenticate') == 1) {
1050
1051 $type = get_option('hh_www_authenticate_type');
1052
1053 $file = $type == 'Basic' ? '.hh-htpasswd' : '.hh-htdigest';
1054
1055 $lines[] = '<FilesMatch "^\.hh-ht(digest|passwd)$">';
1056 $lines[] = ' <IfModule mod_authz_core.c>';
1057 $lines[] = ' Require all denied';
1058 $lines[] = ' </IfModule>';
1059 $lines[] = ' <IfModule !mod_authz_core.c>';
1060 $lines[] = ' Order deny,allow';
1061 $lines[] = ' Deny from all';
1062 $lines[] = ' </IfModule>';
1063 $lines[] = '</FilesMatch>';
1064 // no empty AuthName
1065 $realm = get_option('hh_www_authenticate_realm'); // AuthName
1066 $realm = ($realm == '') ? 'restricted area':$realm; // Empty => give fixed value
1067
1068 $lines[] = sprintf('<IfModule mod_auth_%s.c>', strtolower($type));
1069 $lines[] = sprintf(' AuthType %s', get_option('hh_www_authenticate_type'));
1070 $lines[] = sprintf(' AuthName "%s"', $realm);
1071 $lines[] = sprintf(' AuthUserFile "%s%s"', get_home_path(), $file);
1072 $lines[] = ' Require valid-user';
1073 $lines[] = '</IfModule>';
1074 }
1075
1076 return $lines;
1077 }
1078
1079 function apache_auth_credentials() {
1080 if (get_option('hh_www_authenticate') == 1) {
1081 $type = get_option('hh_www_authenticate_type');
1082 $usernames = get_option('hh_www_authenticate_user', array());
1083 $passwords = get_option('hh_www_authenticate_pswd', array());
1084 if (!is_array($usernames)) {
1085 $usernames = array($usernames);
1086 }
1087 if (!is_array($passwords)) {
1088 $passwords = array($passwords);
1089 }
1090 $realm = get_option('hh_www_authenticate_realm');
1091 $auth = array();
1092 switch ($type) {
1093 case 'Basic':
1094 $ht_file = get_home_path().'.hh-htpasswd';
1095 foreach ($usernames as $k => $user) {
1096 $auth[] = sprintf('%s:{SHA}%s', $user, base64_encode(sha1($passwords[$k], true)));
1097 }
1098 break;
1099 case 'Digest':
1100 $ht_file = get_home_path().'.hh-htdigest';
1101 foreach ($usernames as $k => $user) {
1102 $auth[] = sprintf('%s:%s:%s', $user, $realm, md5($user.':'.$realm.':'.$passwords[$k]));
1103 }
1104 break;
1105 }
1106 $auth = join("\n", $auth);
1107
1108 return compact('ht_file', 'auth');
1109 }
1110 return false;
1111 }
1112
1113 function apache_cookie_security_directives() {
1114 $lines = array();
1115 if (get_option('hh_cookie_security') == 1) {
1116 $value = get_option('hh_cookie_security_value', array());
1117 if (isset($value['HttpOnly'])) {
1118 $lines[] = 'php_flag session.cookie_httponly on';
1119 }
1120 if (isset($value['Secure'])) {
1121 $lines[] = 'php_flag session.cookie_secure on';
1122 }
1123 if (isset($value['SameSite']) && in_array($value['SameSite'], array('None', 'Lax', 'Strict'))) {
1124 $lines[] = sprintf('php_value session.cookie_samesite "%s"', $value['SameSite']);
1125 }
1126 }
1127
1128 return $lines;
1129 }
1130
1131 function apache_check_requirements() {
1132 return check_filename(get_home_path().'.htaccess');
1133 }
1134
1135 function update_headers_directives() {
1136 $lines = array();
1137 if (get_option('hh_method') == 'htaccess') {
1138 $lines = apache_headers_directives();
1139 }
1140
1141 return insert_with_markers(get_home_path().'.htaccess', "HttpHeaders", $lines);
1142 }
1143
1144 function update_content_encoding_directives() {
1145 $lines = array();
1146 if (get_option('hh_method') == 'htaccess') {
1147 $lines = apache_content_encoding_directives();
1148 }
1149
1150 return insert_with_markers(get_home_path().'.htaccess', "HttpHeadersCompression", $lines);
1151 }
1152
1153 function update_expires_directives() {
1154 $lines = array();
1155 if (get_option('hh_method') == 'htaccess') {
1156 $lines = apache_expires_directives();
1157 }
1158
1159 return insert_with_markers(get_home_path().'.htaccess', "HttpHeadersExpires", $lines);
1160 }
1161
1162 function update_content_type_directives() {
1163 $lines = array();
1164 if (get_option('hh_method') == 'htaccess') {
1165 $lines = apache_content_type_directives();
1166 }
1167
1168 return insert_with_markers(get_home_path().'.htaccess', "HttpHeadersContentType", $lines);
1169 }
1170
1171 function update_timing_directives() {
1172 $lines = array();
1173 if (get_option('hh_method') == 'htaccess') {
1174 $lines = apache_timing_directives();
1175 }
1176
1177 return insert_with_markers(get_home_path().'.htaccess', "HttpHeadersTiming", $lines);
1178 }
1179
1180 function update_auth_directives() {
1181 $lines = array();
1182 if (get_option('hh_method') == 'htaccess') {
1183 $lines = apache_auth_directives();
1184 }
1185
1186 return insert_with_markers(get_home_path().'.htaccess', "HttpHeadersAuth", $lines);
1187 }
1188
1189 function update_auth_credentials() {
1190 if (get_option('hh_method') == 'htaccess') {
1191 $credentials = apache_auth_credentials();
1192
1193 return @file_put_contents($credentials['ht_file'], $credentials['auth']);
1194 }
1195
1196 return false;
1197 }
1198
1199 function update_cookie_security_directives() {
1200 $lines = array();
1201 $is_apache = get_option('hh_method') == 'htaccess';
1202 $htaccess = get_home_path().'.htaccess';
1203 if (strpos(PHP_SAPI, 'cgi') !== false) {
1204 $filename = get_home_path().ini_get('user_ini.filename');
1205 $lines = php_cookie_security_directives();
1206 } elseif ($is_apache) {
1207 $filename = $htaccess;
1208 $lines = apache_cookie_security_directives();
1209 }
1210
1211 if (!$is_apache) {
1212 insert_with_markers($htaccess, "HttpHeadersCookieSecurity", array());
1213 }
1214
1215 return insert_with_markers($filename, "HttpHeadersCookieSecurity", $lines);
1216 }
1217
1218 function is_samesite_supported() {
1219 return version_compare(PHP_VERSION, '7.3.0', '>=');
1220 }
1221
1222 function http_headers_text_domain() {
1223 load_plugin_textdomain('http-headers', false, basename( dirname( __FILE__ ) ) . '/languages/');
1224 }
1225
1226 function http_headers_settings_link( $links ) {
1227 $url = get_admin_url() . 'options-general.php?page=http-headers';
1228 $settings_link = '<a href="' . $url . '">' . __('Settings', 'http-headers') . '</a>';
1229 array_unshift( $links, $settings_link );
1230 return $links;
1231 }
1232
1233 function http_headers_after_setup_theme() {
1234 add_filter('plugin_action_links_' . plugin_basename(__FILE__), 'http_headers_settings_link');
1235 }
1236
1237 function http_headers_enqueue($hook) {
1238 if ( 'http-headers.php' != $hook ) {
1239 # FIXME
1240 //return;
1241 }
1242
1243 wp_enqueue_script('http_headers_admin_scripts', plugin_dir_url( __FILE__ ) . 'assets/scripts.js', array(), '1.14.0', true);
1244 wp_localize_script('http_headers_admin_scripts', 'hh', array(
1245 'lbl_delete' => __('Delete', 'http-headers'),
1246 'lbl_value' => __('Value', 'http-headers'),
1247 ));
1248 wp_enqueue_style('http_headers_admin_styles', plugin_dir_url( __FILE__ ) . 'assets/styles.css');
1249 }
1250
1251 function http_headers_ajax_inspect() {
1252 check_ajax_referer('inspect');
1253 if (current_user_can('manage_options')) {
1254 include 'views/ajax-inspect.php';
1255 }
1256 wp_die();
1257 }
1258
1259 function http_headers_post_import() {
1260 check_admin_referer('import');
1261 global $wpdb;
1262 if (!(isset($_FILES['file']['tmp_name'])
1263 && is_uploaded_file($_FILES['file']['tmp_name'])
1264 && $_FILES['file']['error'] == UPLOAD_ERR_OK
1265 )) {
1266 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=100", get_admin_url()));
1267 exit;
1268 }
1269
1270 $string = @file_get_contents($_FILES['file']['tmp_name']);
1271 if ($string === false) {
1272 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=ERR&code=101", get_admin_url()));
1273 exit;
1274 }
1275
1276 $arr = preg_split('/;(\s+)?\n/', $string);
1277 foreach ($arr as $statement) {
1278 $statement = preg_replace("/(INSERT\s*INTO\s*)[\w\_]+options/", '${1}'.$wpdb->options, $statement);
1279 $wpdb->query($statement);
1280 }
1281
1282 wp_redirect(sprintf("%soptions-general.php?page=http-headers&tab=advanced&status=OK", get_admin_url()));
1283 exit;
1284 }
1285
1286 function http_headers_post_export() {
1287 check_admin_referer('export');
1288 global $wpdb;
1289 $options = include dirname(__FILE__) . '/views/includes/options.inc.php';
1290 $opts = array();
1291 foreach ($options as $option)
1292 {
1293 $opts[] = $option[0];
1294 }
1295 $statement = sprintf("SELECT * FROM %s WHERE option_name IN ('%s');", $wpdb->options, join("','", $opts));
1296 $results = $wpdb->get_results($statement, ARRAY_A);
1297 $sql = array();
1298
1299 $indexes = array();
1300 foreach ($options as $option)
1301 {
1302 foreach ($results as $item)
1303 {
1304 if ($item['option_name'] == $option[0])
1305 {
1306 $indexes[$option[0]] = 1;
1307
1308 $value = str_replace("'", "''", $item['option_value']);
1309 $query = array();
1310 $query[] = sprintf("INSERT INTO %s (option_id, option_name, option_value, autoload)", $wpdb->options);
1311 $query[] = sprintf("VALUES (NULL, '%s', '%s', '%s')", $item['option_name'], $value, $item['autoload']);
1312 $query[] = sprintf("ON DUPLICATE KEY UPDATE option_value = '%s', autoload = '%s';", $value, $item['autoload']);
1313 $sql[] = join("\n", $query);
1314 break;
1315 }
1316 }
1317
1318 if (!isset($indexes[$option[0]]))
1319 {
1320 $query = array();
1321 $query[] = sprintf("INSERT INTO %s (option_id, option_name, option_value, autoload)", $wpdb->options);
1322 $query[] = sprintf("VALUES (NULL, '%s', '%s', 'yes')", $option[0], $option[1]);
1323 $query[] = sprintf("ON DUPLICATE KEY UPDATE option_value = '%s', autoload = 'yes';", $option[1]);
1324 $sql[] = join("\n", $query);
1325 }
1326 }
1327
1328 $sql = join("\n\n", $sql);
1329 $length = function_exists('mb_strlen') ? mb_strlen($sql) : strlen($sql);
1330 $name = sprintf('WP-HTTP-Headers-%u.sql', time());
1331
1332 # Send headers
1333 header('Pragma: public');
1334 header('Expires: 0');
1335 header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
1336 header('Cache-Control: private', false);
1337 header('Content-Transfer-Encoding: binary');
1338 header('Content-Disposition: attachment; filename="'.$name.'";');
1339 header('Content-Type: application/sql');
1340 header('Content-Length: ' . $length);
1341
1342 echo $sql;
1343 exit;
1344 }
1345
1346 function check_filename($filename) {
1347 if (!is_file($filename)) {
1348 return -1;
1349 }
1350
1351 clearstatcache();
1352 if (!is_writable($filename)) {
1353 return -2;
1354 }
1355
1356 return true;
1357 }
1358
1359 function check_webserver_requirements() {
1360 $method = get_option('hh_method');
1361 if ($method == 'htaccess') {
1362 return apache_check_requirements();
1363 }
1364
1365 return true;
1366 }
1367
1368 function check_php_requirements() {
1369 if (strpos(PHP_SAPI, 'cgi') !== false) {
1370 // cgi, cgi-fcgi, fpm-fcgi
1371 return check_filename(get_home_path().ini_get('user_ini.filename'));
1372 }
1373
1374 return true;
1375 }
1376
1377 function http_headers_logout() {
1378 if (get_option('hh_clear_site_data') == 1) {
1379 $values = get_option('hh_clear_site_data_value', array());
1380 $tmp = array_keys($values);
1381 if ($tmp) {
1382 header(sprintf('Clear-Site-Data: "%s"', join('", "', $tmp)));
1383 }
1384 }
1385 }
1386
1387 function http_headers_activate() {
1388 update_headers_directives();
1389 update_auth_credentials();
1390 update_auth_directives();
1391 update_content_encoding_directives();
1392 update_content_type_directives();
1393 update_expires_directives();
1394 update_cookie_security_directives();
1395 update_timing_directives();
1396 }
1397
1398 function http_headers_deactivate() {
1399 $filename = get_home_path().'.htaccess';
1400
1401 insert_with_markers($filename, "HttpHeaders", array());
1402 insert_with_markers($filename, "HttpHeadersCompression", array());
1403 insert_with_markers($filename, "HttpHeadersContentType", array());
1404 insert_with_markers($filename, "HttpHeadersExpires", array());
1405 insert_with_markers($filename, "HttpHeadersTiming", array());
1406 insert_with_markers($filename, "HttpHeadersAuth", array());
1407 insert_with_markers($filename, "HttpHeadersCookieSecurity", array());
1408 }
1409
1410 register_activation_hook(__FILE__, 'http_headers_activate');
1411 register_deactivation_hook(__FILE__, 'http_headers_deactivate');
1412 add_action('wp_logout', 'http_headers_logout');
1413
1414 if ( is_admin() ){ // admin actions
1415 add_action('admin_menu', 'http_headers_admin_add_page');
1416 add_action('admin_init', 'http_headers_admin');
1417 add_action("added_option", 'http_headers_option');
1418 add_action("updated_option", 'http_headers_option');
1419 add_action('admin_enqueue_scripts', 'http_headers_enqueue');
1420 add_action('after_setup_theme', 'http_headers_after_setup_theme');
1421 add_action('plugins_loaded', 'http_headers_text_domain');
1422 add_action('wp_ajax_inspect', 'http_headers_ajax_inspect');
1423 add_action('admin_post_import', 'http_headers_post_import');
1424 add_action('admin_post_export', 'http_headers_post_export');
1425 } else {
1426 // non-admin enqueues, actions, and filters
1427 add_action('send_headers', 'http_headers');
1428 }
1429
1430 function http_headers_admin_page() {
1431 include 'views/index.php';
1432 }