PluginProbe
HTTP Headers / 1.14.1
HTTP Headers v1.14.1
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.14.1, at http-headers.php

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