PluginProbe
HTTP Headers / 1.16.1
HTTP Headers v1.16.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.16.1, at http-headers.php

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