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

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

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