Defender Security – Malware Scanner, Login Security & Firewall - Version 2.2.4

Version Description

Download this release

Release Info

Developer hoang1213
Plugin Icon 128x128 Defender Security – Malware Scanner, Login Security & Firewall
Version 2.2.4
Comparing to
See all releases

Code changes from version 2.1.5 to 2.2.4

Files changed (117) hide show
  1. app/behavior/activator-free.php +40 -19
  2. app/behavior/blacklist-free.php +0 -36
  3. app/behavior/endpoint.php +29 -0
  4. app/behavior/utils.php +154 -0
  5. app/behavior/wpmudev.php +72 -0
  6. app/component/cli.php +51 -0
  7. app/component/data-factory.php +184 -0
  8. app/component/public-suffix.php +8796 -0
  9. app/controller.php +3 -3
  10. app/controller/dashboard.php +156 -119
  11. app/controller/debug.php +39 -10
  12. app/controller/gdpr.php +1 -1
  13. app/module/advanced-tools.php +4 -2
  14. app/module/advanced-tools/component/auth-api.php +53 -56
  15. app/module/advanced-tools/component/auth-listener.php +459 -0
  16. app/module/advanced-tools/component/mask-api.php +78 -11
  17. app/module/advanced-tools/component/mask-login-listener.php +236 -0
  18. app/module/advanced-tools/controller/main.php +62 -628
  19. app/module/advanced-tools/controller/rest.php +116 -0
  20. app/module/advanced-tools/model/auth-settings.php +82 -30
  21. app/module/advanced-tools/model/mask-settings.php +52 -10
  22. app/module/advanced-tools/view/login/disabled.php +5 -5
  23. app/module/advanced-tools/view/login/otp.php +4 -4
  24. app/module/advanced-tools/view/main-free.php +6 -6
  25. app/module/advanced-tools/view/main.php +1 -297
  26. app/module/audit.php +2 -0
  27. app/module/audit/controller/main-free.php +38 -10
  28. app/module/audit/view/main-free.php +1 -0
  29. app/module/hardener.php +24 -18
  30. app/module/hardener/component/change-admin-service.php +2 -0
  31. app/module/hardener/component/change-admin.php +28 -19
  32. app/module/hardener/component/db-prefix-service.php +67 -34
  33. app/module/hardener/component/db-prefix.php +35 -21
  34. app/module/hardener/component/disable-file-editor.php +38 -29
  35. app/module/hardener/component/disable-trackback.php +23 -17
  36. app/module/hardener/component/disable-xml-rpc-service.php +2 -0
  37. app/module/hardener/component/disable-xml-rpc.php +25 -23
  38. app/module/hardener/component/hide-error-service.php +20 -41
  39. app/module/hardener/component/hide-error.php +18 -11
  40. app/module/hardener/component/login-duration.php +30 -90
  41. app/module/hardener/component/php-version-service.php +34 -38
  42. app/module/hardener/component/php-version.php +25 -2
  43. app/module/hardener/component/prevent-enum-users-service.php +47 -0
  44. app/module/hardener/component/prevent-enum-users.php +125 -0
  45. app/module/hardener/component/prevent-php-service.php +46 -17
  46. app/module/hardener/component/prevent-php.php +56 -33
  47. app/module/hardener/component/protect-information-service.php +49 -16
  48. app/module/hardener/component/protect-information.php +25 -9
  49. app/module/hardener/component/security-key-service.php +28 -18
  50. app/module/hardener/component/security-key.php +88 -26
  51. app/module/hardener/component/servers/apache-service.php +136 -255
  52. app/module/hardener/component/sh-content-security-service.php +173 -0
  53. app/module/hardener/component/sh-content-security.php +331 -0
  54. app/module/hardener/component/sh-content-type-options-service.php +78 -0
  55. app/module/hardener/component/sh-content-type-options.php +121 -0
  56. app/module/hardener/component/sh-feature-policy-service.php +96 -0
  57. app/module/hardener/component/sh-feature-policy.php +175 -0
  58. app/module/hardener/component/sh-referrer-policy-service.php +97 -0
  59. app/module/hardener/component/sh-referrer-policy.php +132 -0
  60. app/module/hardener/component/sh-strict-transport-service.php +112 -0
  61. app/module/hardener/component/sh-strict-transport.php +149 -0
  62. app/module/hardener/component/sh-x-frame-service.php +102 -0
  63. app/module/hardener/component/sh-x-frame.php +125 -0
  64. app/module/hardener/component/sh-xss-protection-service.php +86 -0
  65. app/module/hardener/component/sh-xss-protection.php +128 -0
  66. app/module/hardener/component/wp-rest-api-service.php +51 -0
  67. app/module/hardener/component/wp-rest-api.php +120 -0
  68. app/module/hardener/component/wp-version.php +25 -5
  69. app/module/hardener/controller/main.php +125 -217
  70. app/module/hardener/controller/rest.php +197 -0
  71. app/module/hardener/model/settings.php +166 -74
  72. app/module/hardener/rule-service.php +61 -5
  73. app/module/hardener/rule.php +41 -46
  74. app/module/hardener/view/email/notification.php +2 -2
  75. app/module/hardener/view/main.php +2 -4
  76. app/module/hardener/view/tweaks/csp/debug-bar.php +21 -0
  77. app/module/hardener/view/tweaks/csp/notification-bar.php +16 -0
  78. app/module/ip-lockout.php +2 -0
  79. app/module/ip-lockout/component/ip-api.php +38 -26
  80. app/module/ip-lockout/component/login-listener.php +185 -0
  81. app/module/ip-lockout/component/login-protection-api.php +98 -61
  82. app/module/ip-lockout/component/logs-table.php +10 -10
  83. app/module/ip-lockout/component/notfound-listener.php +175 -0
  84. app/module/ip-lockout/controller/main.php +242 -900
  85. app/module/ip-lockout/controller/rest.php +385 -0
  86. app/module/ip-lockout/model/ip-model.php +38 -0
  87. app/module/ip-lockout/model/log-model.php +152 -1
  88. app/module/ip-lockout/model/settings.php +272 -85
  89. app/module/ip-lockout/view/emails/404-ban.php +660 -659
  90. app/module/ip-lockout/view/emails/404-lockout.php +660 -659
  91. app/module/ip-lockout/view/emails/login-lockout.php +27 -26
  92. app/module/ip-lockout/view/emails/login-username-ban.php +26 -25
  93. app/module/ip-lockout/view/emails/report.php +26 -25
  94. app/module/ip-lockout/view/locked.php +69 -64
  95. app/module/ip-lockout/view/main.php +1 -0
  96. app/module/scan.php +2 -0
  97. app/module/scan/behavior/core-result.php +30 -337
  98. app/module/scan/component/data-factory.php +97 -0
  99. app/module/scan/component/scan-api.php +120 -100
  100. app/module/scan/controller/main.php +61 -714
  101. app/module/scan/controller/rest.php +377 -0
  102. app/module/scan/model/result-item.php +9 -9
  103. app/module/scan/model/scan.php +31 -11
  104. app/module/scan/model/settings.php +106 -55
  105. app/module/scan/view/automation-free.php +1 -1
  106. app/module/scan/view/email-template.php +632 -631
  107. app/module/scan/view/main.php +1 -0
  108. app/module/scan/view/pro-feature.php +3 -3
  109. app/module/setting.php +2 -0
  110. app/module/setting/component/backup-settings.php +242 -0
  111. app/module/setting/controller/main.php +53 -112
  112. app/module/setting/controller/rest.php +114 -0
  113. app/module/setting/model/settings.php +28 -11
  114. app/module/setting/view/main.php +1 -0
  115. app/view/debug.php +79 -18
  116. app/view/main.php +1 -0
  117. assets/app/advanced-tools.js +1 -0
app/behavior/activator-free.php CHANGED
@@ -16,15 +16,18 @@ class Activator_Free extends Behavior {
16
  if ( ! Utils::instance()->checkPermission() ) {
17
  return;
18
  }
19
-
20
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'activateModule' ) ) {
21
  return;
22
  }
23
-
24
- $activator = HTTP_Helper::retrieve_post( 'activator' );
25
  $activated = array();
26
  if ( count( $activator ) ) {
27
- foreach ( $activator as $item ) {
 
 
 
28
  switch ( $item ) {
29
  case 'activate_scan':
30
  //start a new scan
@@ -33,8 +36,8 @@ class Activator_Free extends Behavior {
33
  break;
34
  case 'activate_lockout':
35
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
36
- $settings->detect_404 = 1;
37
- $settings->login_protection = 1;
38
  $activated[] = $item;
39
  $settings->save();
40
  break;
@@ -45,15 +48,15 @@ class Activator_Free extends Behavior {
45
  }
46
  }
47
  }
48
-
49
- $cache = WP_Helper::getCache();
50
- $cache->set( 'wdf_isActivated', 1, 0 );
51
-
52
  wp_send_json_success( array(
53
- 'activated' => $activated
 
54
  ) );
55
  }
56
-
57
  /**
58
  * Check if we should show activator screen
59
  * @return bool
@@ -61,13 +64,31 @@ class Activator_Free extends Behavior {
61
  public function isShowActivator() {
62
  $cache = WP_Helper::getCache();
63
  if ( $cache->get( 'wdf_isActivated', false ) == 1 ) {
64
- return false;
65
  }
66
- //alread has data, just return
67
- if ( get_site_option( 'wp_defender' ) != false ) {
68
- return false;
69
  }
70
-
71
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
  }
16
  if ( ! Utils::instance()->checkPermission() ) {
17
  return;
18
  }
19
+
20
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'activateModule' ) ) {
21
  return;
22
  }
23
+
24
+ $activator = $_POST;
25
  $activated = array();
26
  if ( count( $activator ) ) {
27
+ foreach ( $activator as $item => $status ) {
28
+ if ( $status != true ) {
29
+ continue;
30
+ }
31
  switch ( $item ) {
32
  case 'activate_scan':
33
  //start a new scan
36
  break;
37
  case 'activate_lockout':
38
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
39
+ $settings->detect_404 = true;
40
+ $settings->login_protection = true;
41
  $activated[] = $item;
42
  $settings->save();
43
  break;
48
  }
49
  }
50
  }
51
+
52
+ update_site_option( 'wp_defender_free_is_activated', 1 );
53
+
 
54
  wp_send_json_success( array(
55
+ 'activated' => $activated,
56
+ //'message' => __( "" )
57
  ) );
58
  }
59
+
60
  /**
61
  * Check if we should show activator screen
62
  * @return bool
64
  public function isShowActivator() {
65
  $cache = WP_Helper::getCache();
66
  if ( $cache->get( 'wdf_isActivated', false ) == 1 ) {
67
+ return 0;
68
  }
69
+ if ( get_site_transient( 'wp_defender_free_is_activated' ) == 1 ) {
70
+ return 0;
 
71
  }
72
+
73
+ if ( get_site_option( 'wp_defender_free_is_activated' ) == 1 ) {
74
+ return 0;
75
+ }
76
+
77
+ $keys = [
78
+ 'wp_defender',
79
+ 'wd_scan_settings',
80
+ 'wd_hardener_settings',
81
+ 'wd_audit_settings',
82
+ 'wd_2auth_settings',
83
+ 'wd_masking_login_settings'
84
+ ];
85
+ foreach ( $keys as $key ) {
86
+ $option = get_site_option( $key );
87
+ if ( is_array( $option ) ) {
88
+ return 0;
89
+ }
90
+ }
91
+
92
+ return 1;
93
  }
94
  }
app/behavior/blacklist-free.php CHANGED
@@ -9,41 +9,5 @@ use Hammer\Base\Behavior;
9
  use WP_Defender\Component\Error_Code;
10
 
11
  class Blacklist_Free extends Behavior {
12
- public function renderBlacklistWidget() {
13
- $this->_renderFree();
14
- }
15
 
16
- private function _renderFree() {
17
- ?>
18
- <div class="sui-box">
19
- <div class="sui-box-header">
20
- <h3 class="sui-box-title">
21
- <i class="sui-icon-target" aria-hidden="true"></i>
22
- <?php _e( "Blacklist Monitor", "defender-security" ) ?>
23
- </h3>
24
- <div class="sui-actions-left">
25
- <span class="sui-tag sui-tag-pro"><?php _e( "Pro", "defender-security" ) ?></span>
26
- </div>
27
- </div>
28
- <div class="sui-box-body sui-upsell-items">
29
- <div class="sui-box-settings-row no-margin-bottom">
30
- <p>
31
- <?php _e( "Automatically check if you’re on Google’s blacklist every 6 hours. If something’s wrong, we’ll let you know via email.", "defender-security" ) ?>
32
- </p>
33
- </div>
34
- <div class="sui-box-settings-row sui-upsell-row">
35
- <img class="sui-image sui-upsell-image"
36
- src="<?php echo wp_defender()->getPluginUrl() . 'assets/img/dashboard-blacklist.svg' ?>">
37
- <div class="sui-upsell-notice">
38
- <p>
39
- <?php
40
- printf( __( "Blacklist Monitor is a Pro feature, included as part of a WPMU DEV monthly membership. <a target='_blank' href='%s'>Learn more</a>.", "defender-security" ), Utils::instance()->campaignURL( 'defender_dash_blacklist_upgrade_button' ) )
41
- ?>
42
- </p>
43
- </div>
44
- </div>
45
- </div>
46
- </div>
47
- <?php
48
- }
49
  }
9
  use WP_Defender\Component\Error_Code;
10
 
11
  class Blacklist_Free extends Behavior {
 
 
 
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
app/behavior/endpoint.php ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Behavior;
7
+
8
+ use Hammer\Helper\WP_Helper;
9
+
10
+ class Endpoint extends \Hammer\Base\Behavior {
11
+
12
+ /**
13
+ * This will contains the endpoints of current session
14
+ * @return array
15
+ */
16
+ public function getAllAvailableEndpoints( $module ) {
17
+ $endpoints = (array) WP_Helper::getArrayCache()->get( 'endpoints' );
18
+
19
+ return isset( $endpoints[ $module ] ) ? $endpoints[ $module ] : array();
20
+ }
21
+
22
+ /**
23
+ * a quick helper for static class
24
+ * @return Endpoint
25
+ */
26
+ public static function instance() {
27
+ return new Endpoint();
28
+ }
29
+ }
app/behavior/utils.php CHANGED
@@ -8,6 +8,7 @@ namespace WP_Defender\Behavior;
8
  use Hammer\Base\Behavior;
9
  use Hammer\Helper\Log_Helper;
10
  use Hammer\Helper\WP_Helper;
 
11
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
12
  use WP_Defender\Module\Hardener\Model\Settings;
13
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
@@ -677,6 +678,33 @@ class Utils extends Behavior {
677
  return false;
678
  }
679
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
680
  /**
681
  * @param null $result
682
  *
@@ -1066,4 +1094,130 @@ class Utils extends Behavior {
1066
 
1067
  return $country_array;
1068
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1069
  }
8
  use Hammer\Base\Behavior;
9
  use Hammer\Helper\Log_Helper;
10
  use Hammer\Helper\WP_Helper;
11
+ use WP_Defender\Component\Error_Code;
12
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
13
  use WP_Defender\Module\Hardener\Model\Settings;
14
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
678
  return false;
679
  }
680
 
681
+ /**
682
+ * We will need to convert mo translate into json for frontend can read
683
+ *
684
+ * @param $handle
685
+ */
686
+ public function createTranslationJson( $handle ) {
687
+ $locale = determine_locale();
688
+ $mo_file = "wpdef-{$locale}.mo";
689
+ $mo_path = wp_defender()->getPluginPath() . 'languages/' . $mo_file;
690
+ $json_path = wp_defender()->getPluginPath() . 'languages/' . "wpdef-{$locale}-{$handle}.json";
691
+ if ( file_exists( $json_path ) ) {
692
+ //already there
693
+ //return;
694
+ }
695
+ if ( ! file_exists( $mo_path ) ) {
696
+ //no translation found
697
+ return;
698
+ }
699
+ //import from mo
700
+ $translations = new \Gettext\Translations();
701
+ \Gettext\Extractors\Mo::fromFile( $mo_path, $translations );
702
+ $translations->setDomain( 'messages' );
703
+ $translations->setLanguage( get_locale() );
704
+ //export to json
705
+ \Gettext\Generators\Jed::toFile( $translations, $json_path );
706
+ }
707
+
708
  /**
709
  * @param null $result
710
  *
1094
 
1095
  return $country_array;
1096
  }
1097
+
1098
+ /**
1099
+ * @param $dir
1100
+ *
1101
+ * @return bool|void|\WP_Error
1102
+ */
1103
+ public function removeDir( $dir ) {
1104
+ if ( ! is_dir( $dir ) ) {
1105
+ return;
1106
+ }
1107
+ $it = new \RecursiveDirectoryIterator( $dir, \RecursiveDirectoryIterator::SKIP_DOTS );
1108
+ $files = new \RecursiveIteratorIterator( $it,
1109
+ \RecursiveIteratorIterator::CHILD_FIRST );
1110
+ foreach ( $files as $file ) {
1111
+ if ( $file->isDir() ) {
1112
+ $res = @rmdir( $file->getRealPath() );
1113
+ } else {
1114
+ $res = @unlink( $file->getRealPath() );
1115
+ }
1116
+ if ( $res == false ) {
1117
+ return new \WP_Error( Error_Code::NOT_WRITEABLE, __( "Defender doesn't have enough permission to remove this file", "defender-security" ) );
1118
+ }
1119
+ }
1120
+ $res = @rmdir( $dir );
1121
+ if ( $res == false ) {
1122
+ return new \WP_Error( Error_Code::NOT_WRITEABLE, __( "Defender doesn't have enough permission to remove this file", "defender-security" ) );
1123
+ }
1124
+
1125
+ return true;
1126
+ }
1127
+
1128
+ public function parseDomain( $domain ) {
1129
+ if ( ! filter_var( $domain, FILTER_VALIDATE_DOMAIN ) ) {
1130
+ return false;
1131
+ }
1132
+ $suffix = $this->getDomainSuffix( $domain );
1133
+ if ( $suffix == false ) {
1134
+ return false;
1135
+ }
1136
+ $host = parse_url( $domain, PHP_URL_HOST );
1137
+ $host_without_tld = str_replace( $suffix, '', $host );
1138
+ //remove righter . if any
1139
+ $host_without_tld = rtrim( $host_without_tld, '.' );
1140
+ $parts = explode( '.', $host_without_tld );
1141
+ if ( count( $parts ) == 1 ) {
1142
+ return [
1143
+ 'host' => $host,
1144
+ 'tld' => $suffix
1145
+ ];
1146
+ }
1147
+ //parse to get the root & subdomain
1148
+ $domain = array_pop( $parts );
1149
+
1150
+ return [
1151
+ 'host' => $host,
1152
+ 'tld' => $suffix,
1153
+ 'subdomain' => str_replace( $domain, '', $host_without_tld ),
1154
+ ];
1155
+ }
1156
+
1157
+ private function getDomainSuffix( $domain ) {
1158
+ $tlds = include dirname( __DIR__ ) . '/component/public-suffix.php';
1159
+ //whitelist development
1160
+ $tlds['localhost'] = 1;
1161
+ $parts = explode( '.', $domain );
1162
+ $parts = array_reverse( $parts );
1163
+ $suffix = '';
1164
+ $list = [];
1165
+ $length = 0;
1166
+ foreach ( $parts as $part ) {
1167
+ $suffix = rtrim( $part . '.' . $suffix, '.' );
1168
+ $notAllow = '!' . $suffix;
1169
+ if ( isset( $tlds[ $notAllow ] ) ) {
1170
+ //this wont be here
1171
+ continue;
1172
+ }
1173
+ if ( isset( $tlds[ $suffix ] ) ) {
1174
+ if ( $length > strlen( $suffix ) ) {
1175
+ //put at last
1176
+ $list[] = $suffix;
1177
+ } else {
1178
+ array_unshift( $list, $suffix );
1179
+ }
1180
+ }
1181
+ };
1182
+ if ( empty( $list ) ) {
1183
+ return false;
1184
+ }
1185
+
1186
+ //the lenghter will be use
1187
+ return $list[0];
1188
+ }
1189
+
1190
+ public function log( $log, $group = null ) {
1191
+ if ( ! defined( 'DEFENDER_DEBUG' ) ) {
1192
+ return;
1193
+ }
1194
+ $log_path = self::getDefUploadDir();
1195
+ $log_name = hash( 'sha256', network_home_url() . $group . SECURE_AUTH_SALT );
1196
+ $log_path = $log_path . '/' . $log_name;
1197
+
1198
+ $log = sprintf( '%s - %s' . PHP_EOL, date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ), $log );
1199
+ file_put_contents( $log_path, $log, FILE_APPEND );
1200
+ }
1201
+
1202
+ public function read_log( $group = null ) {
1203
+ if ( ! defined( 'DEFENDER_DEBUG' ) ) {
1204
+ return;
1205
+ }
1206
+ $log_path = self::getDefUploadDir();
1207
+ $log_name = hash( 'sha256', network_home_url() . $group . SECURE_AUTH_SALT );
1208
+ $log_path = $log_path . '/' . $log_name;
1209
+ $text = file( $log_path );
1210
+
1211
+ return implode( array_reverse( $text ), PHP_EOL );
1212
+ }
1213
+
1214
+ public function clear_log( $group = null ) {
1215
+ if ( ! defined( 'DEFENDER_DEBUG' ) ) {
1216
+ return;
1217
+ }
1218
+ $log_path = self::getDefUploadDir();
1219
+ $log_name = hash( 'sha256', network_home_url() . $group . SECURE_AUTH_SALT );
1220
+ $log_path = $log_path . '/' . $log_name;
1221
+ @unlink( $log_path );
1222
+ }
1223
  }
app/behavior/wpmudev.php ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Behavior;
7
+
8
+ use Hammer\Base\Behavior;
9
+
10
+ /**
11
+ * This class contains everything relate to WPMUDEV
12
+ * Class WPMUDEV
13
+ * @package WP_Defender\Behavior
14
+ * @since 2.2
15
+ */
16
+ class WPMUDEV extends Behavior {
17
+ /**
18
+ * @param $campaign
19
+ *
20
+ * @return string
21
+ */
22
+ public function campaignURL( $campaign ) {
23
+ $url = "https://premium.wpmudev.org/project/wp-defender/?utm_source=defender&utm_medium=plugin&utm_campaign=" . $campaign;
24
+
25
+ return $url;
26
+ }
27
+
28
+ /**
29
+ * Get whitelabel status from Dev Dashboard
30
+ * Properties
31
+ * - hide_branding
32
+ * - hero_image
33
+ * - footer_text
34
+ * - change_footer
35
+ * - hide_doc_link
36
+ *
37
+ * @return mixed
38
+ */
39
+ public function whiteLabelStatus() {
40
+ if ( \WP_Defender\Behavior\Utils::instance()->getAPIKey() ) {
41
+ $site = \WPMUDEV_Dashboard::$site;
42
+ if ( is_object( $site ) ) {
43
+ $info = $site->get_wpmudev_branding( array() );
44
+ return $info;
45
+ }
46
+ } else {
47
+ return [
48
+ 'hide_branding' => false,
49
+ 'hero_image' => '',
50
+ 'footer_text' => '',
51
+ 'change_footer' => false,
52
+ 'hide_doc_link' => false
53
+ ];
54
+ }
55
+ }
56
+
57
+ /**
58
+ * a quick helper for static class
59
+ * @return WPMUDEV
60
+ */
61
+ public static function instance() {
62
+ return new WPMUDEV();
63
+ }
64
+
65
+ /**
66
+ * Return the highcontrast css class if it is
67
+ * @return string
68
+ */
69
+ public function maybeHighContrast() {
70
+ return \WP_Defender\Module\Setting\Model\Settings::instance()->high_contrast_mode;
71
+ }
72
+ }
app/component/cli.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace WP_Defender\Component;
4
+
5
+ use WP_Defender\Module\Scan\Component\Scan_Api;
6
+
7
+ class Cli {
8
+
9
+ public function scan() {
10
+ echo 'Check if there is a scan ongoing...' . PHP_EOL;
11
+ $model = Scan_Api::getActiveScan();
12
+ if ( ! is_object( $model ) ) {
13
+ echo 'No active scan, create one now...' . PHP_EOL;
14
+ Scan_Api::createScan();
15
+ } else {
16
+ echo 'Found active scan, process...' . PHP_EOL;
17
+ }
18
+ echo sprintf( 'Total core files: %d' . PHP_EOL, count( Scan_Api::getCoreFiles() ) );
19
+ echo sprintf( 'Total content files: %d' . PHP_EOL, count( Scan_Api::getContentFiles() ) );
20
+ echo '=============================================' . PHP_EOL;
21
+ $is_done = false;
22
+ while ( $is_done == false ) {
23
+ Scan_Api::releaseLock();
24
+ $memory = ( memory_get_peak_usage( true ) / 1024 / 1024 ) . PHP_EOL;
25
+ echo 'Memory: ' . $memory . ' MB';
26
+ if ( $memory > 256 ) {
27
+ break;
28
+ }
29
+ $is_done = Scan_Api::processActiveScan();
30
+ $progress = Scan_Api::getScanProgress();
31
+ echo 'Scanning at ' . $progress . PHP_EOL;
32
+ gc_collect_cycles();
33
+ }
34
+ if ( $is_done ) {
35
+ $model = Scan_Api::getLastScan();
36
+ \WP_CLI::log( sprintf( 'Found %s issues. Please go to %s for more info.' . PHP_EOL, count( $model->getItems() ), network_admin_url( 'admin.php?page=wdf-scan&view=issues' ) ) );
37
+ \WP_CLI::success( 'Scan done.' );
38
+ } else {
39
+ \WP_CLI::log( 'Run the command wp defender scan again to continue process the scan.' );
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @param $args
45
+ */
46
+ public function scan_a_file( $args ) {
47
+ $file = ABSPATH . $args[0];
48
+
49
+ $this->scan();
50
+ }
51
+ }
app/component/data-factory.php ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Component;
7
+
8
+ use WP_Defender\Behavior\Utils;
9
+ use WP_Defender\Module\Advanced_Tools\Component\Mask_Api;
10
+ use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
11
+ use WP_Defender\Module\Advanced_Tools\Model\Mask_Settings;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\IP_Lockout\Model\Log_Model;
14
+
15
+ class Data_Factory {
16
+ public static function buildData() {
17
+ if ( ! Utils::instance()->checkPermission() ) {
18
+ return [];
19
+ }
20
+
21
+ return [
22
+ 'security_tweaks' => self::buildTweaksData(),
23
+ 'scan' => self::buildScanData(),
24
+ 'blacklist' => self::buildBlacklistData(),
25
+ 'ip_lockout' => self::buildIpLockoutData(),
26
+ 'audit' => self::buildAuditData(),
27
+ 'report' => self::buildReportData(),
28
+ 'advanced_tools' => self::buildAToolsData(),
29
+ ];
30
+ }
31
+
32
+ /**
33
+ * @return array
34
+ */
35
+ public static function buildAToolsData() {
36
+ return [
37
+ 'two_factors' => [
38
+ 'enabled' => Auth_Settings::instance()->enabled,
39
+ 'useable' => count( Auth_Settings::instance()->user_roles ) > 0
40
+ ],
41
+ 'mask_login' => [
42
+ 'enabled' => Mask_Settings::instance()->enabled,
43
+ 'useable' => strlen( Mask_Settings::instance()->mask_url ) > 0,
44
+ 'login_url' => Mask_Api::getNewLoginUrl()
45
+ ],
46
+ 'nonces' => [
47
+ 'updateSettings' => wp_create_nonce( 'updateSettings' )
48
+ ],
49
+ 'endpoints' => [
50
+ 'updateSettings' => 'wp-defender/v1/advanced-tools/updateSettings'
51
+ ]
52
+ ];
53
+ }
54
+
55
+ /**
56
+ * @return array
57
+ */
58
+ public static function buildReportData() {
59
+ if ( ! class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
60
+ return [
61
+ 'scan' => \WP_Defender\Module\Scan\Model\Settings::instance()->report ? \WP_Defender\Module\Scan\Model\Settings::instance()->frequency : - 1,
62
+ 'ip_lockout' => \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->report ? \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->report_frequency : - 1,
63
+ 'audit' => - 1
64
+ ];
65
+ } else {
66
+ return [
67
+ 'scan' => \WP_Defender\Module\Scan\Model\Settings::instance()->report ? \WP_Defender\Module\Scan\Model\Settings::instance()->frequency : - 1,
68
+ 'ip_lockout' => \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->report ? \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->report_frequency : - 1,
69
+ 'audit' => \WP_Defender\Module\Audit\Model\Settings::instance()->notification ? \WP_Defender\Module\Audit\Model\Settings::instance()->frequency : - 1
70
+ ];
71
+ }
72
+ }
73
+
74
+ /**
75
+ * @return array
76
+ */
77
+ public static function buildAuditData() {
78
+ if ( ! class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
79
+ //free version
80
+ return [];
81
+ }
82
+ $setting = \WP_Defender\Module\Audit\Model\Settings::instance();
83
+
84
+ return [
85
+ 'enabled' => $setting->enabled,
86
+ 'report' => $setting->notification,
87
+ 'nonces' => [
88
+ 'summary' => wp_create_nonce( 'summary' ),
89
+ 'updateSettings' => wp_create_nonce( 'updateSettings' )
90
+ ],
91
+ 'endpoints' => [
92
+ 'summary' => 'wp-defender/v1/audit/summary',
93
+ 'updateSettings' => 'wp-defender/v1/audit/updateSettings'
94
+ ]
95
+ ];
96
+ }
97
+
98
+ /**
99
+ * @return array
100
+ */
101
+ private static function buildIpLockoutData() {
102
+ $summaryData = Log_Model::getSummary();
103
+ $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
104
+
105
+ return [
106
+ 'nonces' => [
107
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
108
+ ],
109
+ 'endpoints' => [
110
+ 'updateSettings' => 'wp-defender/v1/lockout/updateSettings'
111
+ ],
112
+ 'summary' => [
113
+ 'ip' => [
114
+ 'week' => $summaryData['loginLockoutThisWeek'],
115
+ ],
116
+ 'nf' => [
117
+ 'week' => $summaryData['lockout404ThisWeek'],
118
+ ],
119
+ 'lastLockout' => $summaryData['lastLockout']
120
+ ],
121
+ 'notification' => $settings->login_lockout_notification && $settings->ip_lockout_notification,
122
+ 'enabled' => $settings->login_protection || $settings->detect_404
123
+ ];
124
+ }
125
+
126
+ /**
127
+ * @return array
128
+ */
129
+ private static function buildBlacklistData() {
130
+ return [
131
+ 'nonces' => [
132
+ 'toggleBlacklistWidget' => wp_create_nonce( 'toggleBlacklistWidget' ),
133
+ 'blacklistWidgetStatus' => wp_create_nonce( 'blacklistWidgetStatus' )
134
+ ],
135
+ 'endpoints' => [
136
+ 'toggleBlacklistWidget' => 'wp-defender/v1/toggleBlacklistWidget',
137
+ 'blacklistWidgetStatus' => 'wp-defender/v1/blacklistWidgetStatus'
138
+ ]
139
+ ];
140
+ }
141
+
142
+ /**
143
+ * @return array
144
+ */
145
+ private static function buildTweaksData() {
146
+ $rules = Settings::instance()->getTweaksAsArray( 'issues' );
147
+ $resolved = Settings::instance()->getTweaksAsArray( 'fixed' );
148
+ $ignored = Settings::instance()->getTweaksAsArray( 'ignore' );
149
+ $total = count( Settings::instance()->getDefinedRules() );
150
+
151
+ return [
152
+ 'rules' => array_slice( $rules, 0, 5 ),
153
+ 'count' => [
154
+ 'issues' => count( $rules ),
155
+ 'resolved' => count( $resolved ) + count( $ignored ),
156
+ 'total' => $total
157
+ ],
158
+ ];
159
+ }
160
+
161
+ /**
162
+ * @return array
163
+ */
164
+ private static function buildScanData() {
165
+ $settings = \WP_Defender\Module\Scan\Model\Settings::instance();
166
+
167
+ return array_merge( \WP_Defender\Module\Scan\Component\Data_Factory::buildLiteData(), [
168
+ 'nonces' => [
169
+ 'newScan' => wp_create_nonce( 'newScan' ),
170
+ 'processScan' => wp_create_nonce( 'processScan' ),
171
+ 'cancelScan' => wp_create_nonce( 'cancelScan' )
172
+ ],
173
+ 'endpoints' => [
174
+ 'newScan' => 'wp-defender/v1/scan/newScan',
175
+ 'processScan' => 'wp-defender/v1/scan/processScan',
176
+ 'cancelScan' => 'wp-defender/v1/scan/cancelScan',
177
+ ],
178
+ 'report' => [
179
+ 'enabled' => $settings->report,
180
+ 'frequency' => $settings->frequency
181
+ ]
182
+ ] );
183
+ }
184
+ }
app/component/public-suffix.php ADDED
@@ -0,0 +1,8796 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ return array (
3
+ 'ac' => 1,
4
+ 'com.ac' => 1,
5
+ 'edu.ac' => 1,
6
+ 'gov.ac' => 1,
7
+ 'net.ac' => 1,
8
+ 'mil.ac' => 1,
9
+ 'org.ac' => 1,
10
+ 'ad' => 1,
11
+ 'nom.ad' => 1,
12
+ 'ae' => 1,
13
+ 'co.ae' => 1,
14
+ 'net.ae' => 1,
15
+ 'org.ae' => 1,
16
+ 'sch.ae' => 1,
17
+ 'ac.ae' => 1,
18
+ 'gov.ae' => 1,
19
+ 'mil.ae' => 1,
20
+ 'aero' => 1,
21
+ 'accident-investigation.aero' => 1,
22
+ 'accident-prevention.aero' => 1,
23
+ 'aerobatic.aero' => 1,
24
+ 'aeroclub.aero' => 1,
25
+ 'aerodrome.aero' => 1,
26
+ 'agents.aero' => 1,
27
+ 'aircraft.aero' => 1,
28
+ 'airline.aero' => 1,
29
+ 'airport.aero' => 1,
30
+ 'air-surveillance.aero' => 1,
31
+ 'airtraffic.aero' => 1,
32
+ 'air-traffic-control.aero' => 1,
33
+ 'ambulance.aero' => 1,
34
+ 'amusement.aero' => 1,
35
+ 'association.aero' => 1,
36
+ 'author.aero' => 1,
37
+ 'ballooning.aero' => 1,
38
+ 'broker.aero' => 1,
39
+ 'caa.aero' => 1,
40
+ 'cargo.aero' => 1,
41
+ 'catering.aero' => 1,
42
+ 'certification.aero' => 1,
43
+ 'championship.aero' => 1,
44
+ 'charter.aero' => 1,
45
+ 'civilaviation.aero' => 1,
46
+ 'club.aero' => 1,
47
+ 'conference.aero' => 1,
48
+ 'consultant.aero' => 1,
49
+ 'consulting.aero' => 1,
50
+ 'control.aero' => 1,
51
+ 'council.aero' => 1,
52
+ 'crew.aero' => 1,
53
+ 'design.aero' => 1,
54
+ 'dgca.aero' => 1,
55
+ 'educator.aero' => 1,
56
+ 'emergency.aero' => 1,
57
+ 'engine.aero' => 1,
58
+ 'engineer.aero' => 1,
59
+ 'entertainment.aero' => 1,
60
+ 'equipment.aero' => 1,
61
+ 'exchange.aero' => 1,
62
+ 'express.aero' => 1,
63
+ 'federation.aero' => 1,
64
+ 'flight.aero' => 1,
65
+ 'freight.aero' => 1,
66
+ 'fuel.aero' => 1,
67
+ 'gliding.aero' => 1,
68
+ 'government.aero' => 1,
69
+ 'groundhandling.aero' => 1,
70
+ 'group.aero' => 1,
71
+ 'hanggliding.aero' => 1,
72
+ 'homebuilt.aero' => 1,
73
+ 'insurance.aero' => 1,
74
+ 'journal.aero' => 1,
75
+ 'journalist.aero' => 1,
76
+ 'leasing.aero' => 1,
77
+ 'logistics.aero' => 1,
78
+ 'magazine.aero' => 1,
79
+ 'maintenance.aero' => 1,
80
+ 'media.aero' => 1,
81
+ 'microlight.aero' => 1,
82
+ 'modelling.aero' => 1,
83
+ 'navigation.aero' => 1,
84
+ 'parachuting.aero' => 1,
85
+ 'paragliding.aero' => 1,
86
+ 'passenger-association.aero' => 1,
87
+ 'pilot.aero' => 1,
88
+ 'press.aero' => 1,
89
+ 'production.aero' => 1,
90
+ 'recreation.aero' => 1,
91
+ 'repbody.aero' => 1,
92
+ 'res.aero' => 1,
93
+ 'research.aero' => 1,
94
+ 'rotorcraft.aero' => 1,
95
+ 'safety.aero' => 1,
96
+ 'scientist.aero' => 1,
97
+ 'services.aero' => 1,
98
+ 'show.aero' => 1,
99
+ 'skydiving.aero' => 1,
100
+ 'software.aero' => 1,
101
+ 'student.aero' => 1,
102
+ 'trader.aero' => 1,
103
+ 'trading.aero' => 1,
104
+ 'trainer.aero' => 1,
105
+ 'union.aero' => 1,
106
+ 'workinggroup.aero' => 1,
107
+ 'works.aero' => 1,
108
+ 'af' => 1,
109
+ 'gov.af' => 1,
110
+ 'com.af' => 1,
111
+ 'org.af' => 1,
112
+ 'net.af' => 1,
113
+ 'edu.af' => 1,
114
+ 'ag' => 1,
115
+ 'com.ag' => 1,
116
+ 'org.ag' => 1,
117
+ 'net.ag' => 1,
118
+ 'co.ag' => 1,
119
+ 'nom.ag' => 1,
120
+ 'ai' => 1,
121
+ 'off.ai' => 1,
122
+ 'com.ai' => 1,
123
+ 'net.ai' => 1,
124
+ 'org.ai' => 1,
125
+ 'al' => 1,
126
+ 'com.al' => 1,
127
+ 'edu.al' => 1,
128
+ 'gov.al' => 1,
129
+ 'mil.al' => 1,
130
+ 'net.al' => 1,
131
+ 'org.al' => 1,
132
+ 'am' => 1,
133
+ 'co.am' => 1,
134
+ 'com.am' => 1,
135
+ 'commune.am' => 1,
136
+ 'net.am' => 1,
137
+ 'org.am' => 1,
138
+ 'ao' => 1,
139
+ 'ed.ao' => 1,
140
+ 'gv.ao' => 1,
141
+ 'og.ao' => 1,
142
+ 'co.ao' => 1,
143
+ 'pb.ao' => 1,
144
+ 'it.ao' => 1,
145
+ 'aq' => 1,
146
+ 'ar' => 1,
147
+ 'com.ar' => 1,
148
+ 'edu.ar' => 1,
149
+ 'gob.ar' => 1,
150
+ 'gov.ar' => 1,
151
+ 'int.ar' => 1,
152
+ 'mil.ar' => 1,
153
+ 'musica.ar' => 1,
154
+ 'net.ar' => 1,
155
+ 'org.ar' => 1,
156
+ 'tur.ar' => 1,
157
+ 'arpa' => 1,
158
+ 'e164.arpa' => 1,
159
+ 'in-addr.arpa' => 1,
160
+ 'ip6.arpa' => 1,
161
+ 'iris.arpa' => 1,
162
+ 'uri.arpa' => 1,
163
+ 'urn.arpa' => 1,
164
+ 'as' => 1,
165
+ 'gov.as' => 1,
166
+ 'asia' => 1,
167
+ 'at' => 1,
168
+ 'ac.at' => 1,
169
+ 'co.at' => 1,
170
+ 'gv.at' => 1,
171
+ 'or.at' => 1,
172
+ 'au' => 1,
173
+ 'com.au' => 1,
174
+ 'net.au' => 1,
175
+ 'org.au' => 1,
176
+ 'edu.au' => 1,
177
+ 'gov.au' => 1,
178
+ 'asn.au' => 1,
179
+ 'id.au' => 1,
180
+ 'info.au' => 1,
181
+ 'conf.au' => 1,
182
+ 'oz.au' => 1,
183
+ 'act.au' => 1,
184
+ 'nsw.au' => 1,
185
+ 'nt.au' => 1,
186
+ 'qld.au' => 1,
187
+ 'sa.au' => 1,
188
+ 'tas.au' => 1,
189
+ 'vic.au' => 1,
190
+ 'wa.au' => 1,
191
+ 'act.edu.au' => 1,
192
+ 'catholic.edu.au' => 1,
193
+ 'eq.edu.au' => 1,
194
+ 'nsw.edu.au' => 1,
195
+ 'nt.edu.au' => 1,
196
+ 'qld.edu.au' => 1,
197
+ 'sa.edu.au' => 1,
198
+ 'tas.edu.au' => 1,
199
+ 'vic.edu.au' => 1,
200
+ 'wa.edu.au' => 1,
201
+ 'qld.gov.au' => 1,
202
+ 'sa.gov.au' => 1,
203
+ 'tas.gov.au' => 1,
204
+ 'vic.gov.au' => 1,
205
+ 'wa.gov.au' => 1,
206
+ 'education.tas.edu.au' => 1,
207
+ 'schools.nsw.edu.au' => 1,
208
+ 'aw' => 1,
209
+ 'com.aw' => 1,
210
+ 'ax' => 1,
211
+ 'az' => 1,
212
+ 'com.az' => 1,
213
+ 'net.az' => 1,
214
+ 'int.az' => 1,
215
+ 'gov.az' => 1,
216
+ 'org.az' => 1,
217
+ 'edu.az' => 1,
218
+ 'info.az' => 1,
219
+ 'pp.az' => 1,
220
+ 'mil.az' => 1,
221
+ 'name.az' => 1,
222
+ 'pro.az' => 1,
223
+ 'biz.az' => 1,
224
+ 'ba' => 1,
225
+ 'com.ba' => 1,
226
+ 'edu.ba' => 1,
227
+ 'gov.ba' => 1,
228
+ 'mil.ba' => 1,
229
+ 'net.ba' => 1,
230
+ 'org.ba' => 1,
231
+ 'bb' => 1,
232
+ 'biz.bb' => 1,
233
+ 'co.bb' => 1,
234
+ 'com.bb' => 1,
235
+ 'edu.bb' => 1,
236
+ 'gov.bb' => 1,
237
+ 'info.bb' => 1,
238
+ 'net.bb' => 1,
239
+ 'org.bb' => 1,
240
+ 'store.bb' => 1,
241
+ 'tv.bb' => 1,
242
+ '*.bd' => 1,
243
+ 'be' => 1,
244
+ 'ac.be' => 1,
245
+ 'bf' => 1,
246
+ 'gov.bf' => 1,
247
+ 'bg' => 1,
248
+ 'a.bg' => 1,
249
+ 'b.bg' => 1,
250
+ 'c.bg' => 1,
251
+ 'd.bg' => 1,
252
+ 'e.bg' => 1,
253
+ 'f.bg' => 1,
254
+ 'g.bg' => 1,
255
+ 'h.bg' => 1,
256
+ 'i.bg' => 1,
257
+ 'j.bg' => 1,
258
+ 'k.bg' => 1,
259
+ 'l.bg' => 1,
260
+ 'm.bg' => 1,
261
+ 'n.bg' => 1,
262
+ 'o.bg' => 1,
263
+ 'p.bg' => 1,
264
+ 'q.bg' => 1,
265
+ 'r.bg' => 1,
266
+ 's.bg' => 1,
267
+ 't.bg' => 1,
268
+ 'u.bg' => 1,
269
+ 'v.bg' => 1,
270
+ 'w.bg' => 1,
271
+ 'x.bg' => 1,
272
+ 'y.bg' => 1,
273
+ 'z.bg' => 1,
274
+ '0.bg' => 1,
275
+ '1.bg' => 1,
276
+ '2.bg' => 1,
277
+ '3.bg' => 1,
278
+ '4.bg' => 1,
279
+ '5.bg' => 1,
280
+ '6.bg' => 1,
281
+ '7.bg' => 1,
282
+ '8.bg' => 1,
283
+ '9.bg' => 1,
284
+ 'bh' => 1,
285
+ 'com.bh' => 1,
286
+ 'edu.bh' => 1,
287
+ 'net.bh' => 1,
288
+ 'org.bh' => 1,
289
+ 'gov.bh' => 1,
290
+ 'bi' => 1,
291
+ 'co.bi' => 1,
292
+ 'com.bi' => 1,
293
+ 'edu.bi' => 1,
294
+ 'or.bi' => 1,
295
+ 'org.bi' => 1,
296
+ 'biz' => 1,
297
+ 'bj' => 1,
298
+ 'asso.bj' => 1,
299
+ 'barreau.bj' => 1,
300
+ 'gouv.bj' => 1,
301
+ 'bm' => 1,
302
+ 'com.bm' => 1,
303
+ 'edu.bm' => 1,
304
+ 'gov.bm' => 1,
305
+ 'net.bm' => 1,
306
+ 'org.bm' => 1,
307
+ 'bn' => 1,
308
+ 'com.bn' => 1,
309
+ 'edu.bn' => 1,
310
+ 'gov.bn' => 1,
311
+ 'net.bn' => 1,
312
+ 'org.bn' => 1,
313
+ 'bo' => 1,
314
+ 'com.bo' => 1,
315
+ 'edu.bo' => 1,
316
+ 'gob.bo' => 1,
317
+ 'int.bo' => 1,
318
+ 'org.bo' => 1,
319
+ 'net.bo' => 1,
320
+ 'mil.bo' => 1,
321
+ 'tv.bo' => 1,
322
+ 'web.bo' => 1,
323
+ 'academia.bo' => 1,
324
+ 'agro.bo' => 1,
325
+ 'arte.bo' => 1,
326
+ 'blog.bo' => 1,
327
+ 'bolivia.bo' => 1,
328
+ 'ciencia.bo' => 1,
329
+ 'cooperativa.bo' => 1,
330
+ 'democracia.bo' => 1,
331
+ 'deporte.bo' => 1,
332
+ 'ecologia.bo' => 1,
333
+ 'economia.bo' => 1,
334
+ 'empresa.bo' => 1,
335
+ 'indigena.bo' => 1,
336
+ 'industria.bo' => 1,
337
+ 'info.bo' => 1,
338
+ 'medicina.bo' => 1,
339
+ 'movimiento.bo' => 1,
340
+ 'musica.bo' => 1,
341
+ 'natural.bo' => 1,
342
+ 'nombre.bo' => 1,
343
+ 'noticias.bo' => 1,
344
+ 'patria.bo' => 1,
345
+ 'politica.bo' => 1,
346
+ 'profesional.bo' => 1,
347
+ 'plurinacional.bo' => 1,
348
+ 'pueblo.bo' => 1,
349
+ 'revista.bo' => 1,
350
+ 'salud.bo' => 1,
351
+ 'tecnologia.bo' => 1,
352
+ 'tksat.bo' => 1,
353
+ 'transporte.bo' => 1,
354
+ 'wiki.bo' => 1,
355
+ 'br' => 1,
356
+ '9guacu.br' => 1,
357
+ 'abc.br' => 1,
358
+ 'adm.br' => 1,
359
+ 'adv.br' => 1,
360
+ 'agr.br' => 1,
361
+ 'aju.br' => 1,
362
+ 'am.br' => 1,
363
+ 'anani.br' => 1,
364
+ 'aparecida.br' => 1,
365
+ 'arq.br' => 1,
366
+ 'art.br' => 1,
367
+ 'ato.br' => 1,
368
+ 'b.br' => 1,
369
+ 'barueri.br' => 1,
370
+ 'belem.br' => 1,
371
+ 'bhz.br' => 1,
372
+ 'bio.br' => 1,
373
+ 'blog.br' => 1,
374
+ 'bmd.br' => 1,
375
+ 'boavista.br' => 1,
376
+ 'bsb.br' => 1,
377
+ 'campinagrande.br' => 1,
378
+ 'campinas.br' => 1,
379
+ 'caxias.br' => 1,
380
+ 'cim.br' => 1,
381
+ 'cng.br' => 1,
382
+ 'cnt.br' => 1,
383
+ 'com.br' => 1,
384
+ 'contagem.br' => 1,
385
+ 'coop.br' => 1,
386
+ 'cri.br' => 1,
387
+ 'cuiaba.br' => 1,
388
+ 'curitiba.br' => 1,
389
+ 'def.br' => 1,
390
+ 'ecn.br' => 1,
391
+ 'eco.br' => 1,
392
+ 'edu.br' => 1,
393
+ 'emp.br' => 1,
394
+ 'eng.br' => 1,
395
+ 'esp.br' => 1,
396
+ 'etc.br' => 1,
397
+ 'eti.br' => 1,
398
+ 'far.br' => 1,
399
+ 'feira.br' => 1,
400
+ 'flog.br' => 1,
401
+ 'floripa.br' => 1,
402
+ 'fm.br' => 1,
403
+ 'fnd.br' => 1,
404
+ 'fortal.br' => 1,
405
+ 'fot.br' => 1,
406
+ 'foz.br' => 1,
407
+ 'fst.br' => 1,
408
+ 'g12.br' => 1,
409
+ 'ggf.br' => 1,
410
+ 'goiania.br' => 1,
411
+ 'gov.br' => 1,
412
+ 'ac.gov.br' => 1,
413
+ 'al.gov.br' => 1,
414
+ 'am.gov.br' => 1,
415
+ 'ap.gov.br' => 1,
416
+ 'ba.gov.br' => 1,
417
+ 'ce.gov.br' => 1,
418
+ 'df.gov.br' => 1,
419
+ 'es.gov.br' => 1,
420
+ 'go.gov.br' => 1,
421
+ 'ma.gov.br' => 1,
422
+ 'mg.gov.br' => 1,
423
+ 'ms.gov.br' => 1,
424
+ 'mt.gov.br' => 1,
425
+ 'pa.gov.br' => 1,
426
+ 'pb.gov.br' => 1,
427
+ 'pe.gov.br' => 1,
428
+ 'pi.gov.br' => 1,
429
+ 'pr.gov.br' => 1,
430
+ 'rj.gov.br' => 1,
431
+ 'rn.gov.br' => 1,
432
+ 'ro.gov.br' => 1,
433
+ 'rr.gov.br' => 1,
434
+ 'rs.gov.br' => 1,
435
+ 'sc.gov.br' => 1,
436
+ 'se.gov.br' => 1,
437
+ 'sp.gov.br' => 1,
438
+ 'to.gov.br' => 1,
439
+ 'gru.br' => 1,
440
+ 'imb.br' => 1,
441
+ 'ind.br' => 1,
442
+ 'inf.br' => 1,
443
+ 'jab.br' => 1,
444
+ 'jampa.br' => 1,
445
+ 'jdf.br' => 1,
446
+ 'joinville.br' => 1,
447
+ 'jor.br' => 1,
448
+ 'jus.br' => 1,
449
+ 'leg.br' => 1,
450
+ 'lel.br' => 1,
451
+ 'londrina.br' => 1,
452
+ 'macapa.br' => 1,
453
+ 'maceio.br' => 1,
454
+ 'manaus.br' => 1,
455
+ 'maringa.br' => 1,
456
+ 'mat.br' => 1,
457
+ 'med.br' => 1,
458
+ 'mil.br' => 1,
459
+ 'morena.br' => 1,
460
+ 'mp.br' => 1,
461
+ 'mus.br' => 1,
462
+ 'natal.br' => 1,
463
+ 'net.br' => 1,
464
+ 'niteroi.br' => 1,
465
+ '*.nom.br' => 1,
466
+ 'not.br' => 1,
467
+ 'ntr.br' => 1,
468
+ 'odo.br' => 1,
469
+ 'ong.br' => 1,
470
+ 'org.br' => 1,
471
+ 'osasco.br' => 1,
472
+ 'palmas.br' => 1,
473
+ 'poa.br' => 1,
474
+ 'ppg.br' => 1,
475
+ 'pro.br' => 1,
476
+ 'psc.br' => 1,
477
+ 'psi.br' => 1,
478
+ 'pvh.br' => 1,
479
+ 'qsl.br' => 1,
480
+ 'radio.br' => 1,
481
+ 'rec.br' => 1,
482
+ 'recife.br' => 1,
483
+ 'ribeirao.br' => 1,
484
+ 'rio.br' => 1,
485
+ 'riobranco.br' => 1,
486
+ 'riopreto.br' => 1,
487
+ 'salvador.br' => 1,
488
+ 'sampa.br' => 1,
489
+ 'santamaria.br' => 1,
490
+ 'santoandre.br' => 1,
491
+ 'saobernardo.br' => 1,
492
+ 'saogonca.br' => 1,
493
+ 'sjc.br' => 1,
494
+ 'slg.br' => 1,
495
+ 'slz.br' => 1,
496
+ 'sorocaba.br' => 1,
497
+ 'srv.br' => 1,
498
+ 'taxi.br' => 1,
499
+ 'tc.br' => 1,
500
+ 'teo.br' => 1,
501
+ 'the.br' => 1,
502
+ 'tmp.br' => 1,
503
+ 'trd.br' => 1,
504
+ 'tur.br' => 1,
505
+ 'tv.br' => 1,
506
+ 'udi.br' => 1,
507
+ 'vet.br' => 1,
508
+ 'vix.br' => 1,
509
+ 'vlog.br' => 1,
510
+ 'wiki.br' => 1,
511
+ 'zlg.br' => 1,
512
+ 'bs' => 1,
513
+ 'com.bs' => 1,
514
+ 'net.bs' => 1,
515
+ 'org.bs' => 1,
516
+ 'edu.bs' => 1,
517
+ 'gov.bs' => 1,
518
+ 'bt' => 1,
519
+ 'com.bt' => 1,
520
+ 'edu.bt' => 1,
521
+ 'gov.bt' => 1,
522
+ 'net.bt' => 1,
523
+ 'org.bt' => 1,
524
+ 'bv' => 1,
525
+ 'bw' => 1,
526
+ 'co.bw' => 1,
527
+ 'org.bw' => 1,
528
+ 'by' => 1,
529
+ 'gov.by' => 1,
530
+ 'mil.by' => 1,
531
+ 'com.by' => 1,
532
+ 'of.by' => 1,
533
+ 'bz' => 1,
534
+ 'com.bz' => 1,
535
+ 'net.bz' => 1,
536
+ 'org.bz' => 1,
537
+ 'edu.bz' => 1,
538
+ 'gov.bz' => 1,
539
+ 'ca' => 1,
540
+ 'ab.ca' => 1,
541
+ 'bc.ca' => 1,
542
+ 'mb.ca' => 1,
543
+ 'nb.ca' => 1,
544
+ 'nf.ca' => 1,
545
+ 'nl.ca' => 1,
546
+ 'ns.ca' => 1,
547
+ 'nt.ca' => 1,
548
+ 'nu.ca' => 1,
549
+ 'on.ca' => 1,
550
+ 'pe.ca' => 1,
551
+ 'qc.ca' => 1,
552
+ 'sk.ca' => 1,
553
+ 'yk.ca' => 1,
554
+ 'gc.ca' => 1,
555
+ 'cat' => 1,
556
+ 'cc' => 1,
557
+ 'cd' => 1,
558
+ 'gov.cd' => 1,
559
+ 'cf' => 1,
560
+ 'cg' => 1,
561
+ 'ch' => 1,
562
+ 'ci' => 1,
563
+ 'org.ci' => 1,
564
+ 'or.ci' => 1,
565
+ 'com.ci' => 1,
566
+ 'co.ci' => 1,
567
+ 'edu.ci' => 1,
568
+ 'ed.ci' => 1,
569
+ 'ac.ci' => 1,
570
+ 'net.ci' => 1,
571
+ 'go.ci' => 1,
572
+ 'asso.ci' => 1,
573
+ 'aéroport.ci' => 1,
574
+ 'int.ci' => 1,
575
+ 'presse.ci' => 1,
576
+ 'md.ci' => 1,
577
+ 'gouv.ci' => 1,
578
+ '*.ck' => 1,
579
+ '!www.ck' => 1,
580
+ 'cl' => 1,
581
+ 'gov.cl' => 1,
582
+ 'gob.cl' => 1,
583
+ 'co.cl' => 1,
584
+ 'mil.cl' => 1,
585
+ 'cm' => 1,
586
+ 'co.cm' => 1,
587
+ 'com.cm' => 1,
588
+ 'gov.cm' => 1,
589
+ 'net.cm' => 1,
590
+ 'cn' => 1,
591
+ 'ac.cn' => 1,
592
+ 'com.cn' => 1,
593
+ 'edu.cn' => 1,
594
+ 'gov.cn' => 1,
595
+ 'net.cn' => 1,
596
+ 'org.cn' => 1,
597
+ 'mil.cn' => 1,
598
+ '公司.cn' => 1,
599
+ '网络.cn' => 1,
600
+ '網絡.cn' => 1,
601
+ 'ah.cn' => 1,
602
+ 'bj.cn' => 1,
603
+ 'cq.cn' => 1,
604
+ 'fj.cn' => 1,
605
+ 'gd.cn' => 1,
606
+ 'gs.cn' => 1,
607
+ 'gz.cn' => 1,
608
+ 'gx.cn' => 1,
609
+ 'ha.cn' => 1,
610
+ 'hb.cn' => 1,
611
+ 'he.cn' => 1,
612
+ 'hi.cn' => 1,
613
+ 'hl.cn' => 1,
614
+ 'hn.cn' => 1,
615
+ 'jl.cn' => 1,
616
+ 'js.cn' => 1,
617
+ 'jx.cn' => 1,
618
+ 'ln.cn' => 1,
619
+ 'nm.cn' => 1,
620
+ 'nx.cn' => 1,
621
+ 'qh.cn' => 1,
622
+ 'sc.cn' => 1,
623
+ 'sd.cn' => 1,
624
+ 'sh.cn' => 1,
625
+ 'sn.cn' => 1,
626
+ 'sx.cn' => 1,
627
+ 'tj.cn' => 1,
628
+ 'xj.cn' => 1,
629
+ 'xz.cn' => 1,
630
+ 'yn.cn' => 1,
631
+ 'zj.cn' => 1,
632
+ 'hk.cn' => 1,
633
+ 'mo.cn' => 1,
634
+ 'tw.cn' => 1,
635
+ 'co' => 1,
636
+ 'arts.co' => 1,
637
+ 'com.co' => 1,
638
+ 'edu.co' => 1,
639
+ 'firm.co' => 1,
640
+ 'gov.co' => 1,
641
+ 'info.co' => 1,
642
+ 'int.co' => 1,
643
+ 'mil.co' => 1,
644
+ 'net.co' => 1,
645
+ 'nom.co' => 1,
646
+ 'org.co' => 1,
647
+ 'rec.co' => 1,
648
+ 'web.co' => 1,
649
+ 'com' => 1,
650
+ 'coop' => 1,
651
+ 'cr' => 1,
652
+ 'ac.cr' => 1,
653
+ 'co.cr' => 1,
654
+ 'ed.cr' => 1,
655
+ 'fi.cr' => 1,
656
+ 'go.cr' => 1,
657
+ 'or.cr' => 1,
658
+ 'sa.cr' => 1,
659
+ 'cu' => 1,
660
+ 'com.cu' => 1,
661
+ 'edu.cu' => 1,
662
+ 'org.cu' => 1,
663
+ 'net.cu' => 1,
664
+ 'gov.cu' => 1,
665
+ 'inf.cu' => 1,
666
+ 'cv' => 1,
667
+ 'cw' => 1,
668
+ 'com.cw' => 1,
669
+ 'edu.cw' => 1,
670
+ 'net.cw' => 1,
671
+ 'org.cw' => 1,
672
+ 'cx' => 1,
673
+ 'gov.cx' => 1,
674
+ 'cy' => 1,
675
+ 'ac.cy' => 1,
676
+ 'biz.cy' => 1,
677
+ 'com.cy' => 1,
678
+ 'ekloges.cy' => 1,
679
+ 'gov.cy' => 1,
680
+ 'ltd.cy' => 1,
681
+ 'name.cy' => 1,
682
+ 'net.cy' => 1,
683
+ 'org.cy' => 1,
684
+ 'parliament.cy' => 1,
685
+ 'press.cy' => 1,
686
+ 'pro.cy' => 1,
687
+ 'tm.cy' => 1,
688
+ 'cz' => 1,
689
+ 'de' => 1,
690
+ 'dj' => 1,
691
+ 'dk' => 1,
692
+ 'dm' => 1,
693
+ 'com.dm' => 1,
694
+ 'net.dm' => 1,
695
+ 'org.dm' => 1,
696
+ 'edu.dm' => 1,
697
+ 'gov.dm' => 1,
698
+ 'do' => 1,
699
+ 'art.do' => 1,
700
+ 'com.do' => 1,
701
+ 'edu.do' => 1,
702
+ 'gob.do' => 1,
703
+ 'gov.do' => 1,
704
+ 'mil.do' => 1,
705
+ 'net.do' => 1,
706
+ 'org.do' => 1,
707
+ 'sld.do' => 1,
708
+ 'web.do' => 1,
709
+ 'dz' => 1,
710
+ 'com.dz' => 1,
711
+ 'org.dz' => 1,
712
+ 'net.dz' => 1,
713
+ 'gov.dz' => 1,
714
+ 'edu.dz' => 1,
715
+ 'asso.dz' => 1,
716
+ 'pol.dz' => 1,
717
+ 'art.dz' => 1,
718
+ 'ec' => 1,
719
+ 'com.ec' => 1,
720
+ 'info.ec' => 1,
721
+ 'net.ec' => 1,
722
+ 'fin.ec' => 1,
723
+ 'k12.ec' => 1,
724
+ 'med.ec' => 1,
725
+ 'pro.ec' => 1,
726
+ 'org.ec' => 1,
727
+ 'edu.ec' => 1,
728
+ 'gov.ec' => 1,
729
+ 'gob.ec' => 1,
730
+ 'mil.ec' => 1,
731
+ 'edu' => 1,
732
+ 'ee' => 1,
733
+ 'edu.ee' => 1,
734
+ 'gov.ee' => 1,
735
+ 'riik.ee' => 1,
736
+ 'lib.ee' => 1,
737
+ 'med.ee' => 1,
738
+ 'com.ee' => 1,
739
+ 'pri.ee' => 1,
740
+ 'aip.ee' => 1,
741
+ 'org.ee' => 1,
742
+ 'fie.ee' => 1,
743
+ 'eg' => 1,
744
+ 'com.eg' => 1,
745
+ 'edu.eg' => 1,
746
+ 'eun.eg' => 1,
747
+ 'gov.eg' => 1,
748
+ 'mil.eg' => 1,
749
+ 'name.eg' => 1,
750
+ 'net.eg' => 1,
751
+ 'org.eg' => 1,
752
+ 'sci.eg' => 1,
753
+ '*.er' => 1,
754
+ 'es' => 1,
755
+ 'com.es' => 1,
756
+ 'nom.es' => 1,
757
+ 'org.es' => 1,
758
+ 'gob.es' => 1,
759
+ 'edu.es' => 1,
760
+ 'et' => 1,
761
+ 'com.et' => 1,
762
+ 'gov.et' => 1,
763
+ 'org.et' => 1,
764
+ 'edu.et' => 1,
765
+ 'biz.et' => 1,
766
+ 'name.et' => 1,
767
+ 'info.et' => 1,
768
+ 'net.et' => 1,
769
+ 'eu' => 1,
770
+ 'fi' => 1,
771
+ 'aland.fi' => 1,
772
+ '*.fj' => 1,
773
+ '*.fk' => 1,
774
+ 'fm' => 1,
775
+ 'fo' => 1,
776
+ 'fr' => 1,
777
+ 'asso.fr' => 1,
778
+ 'com.fr' => 1,
779
+ 'gouv.fr' => 1,
780
+ 'nom.fr' => 1,
781
+ 'prd.fr' => 1,
782
+ 'tm.fr' => 1,
783
+ 'aeroport.fr' => 1,
784
+ 'avocat.fr' => 1,
785
+ 'avoues.fr' => 1,
786
+ 'cci.fr' => 1,
787
+ 'chambagri.fr' => 1,
788
+ 'chirurgiens-dentistes.fr' => 1,
789
+ 'experts-comptables.fr' => 1,
790
+ 'geometre-expert.fr' => 1,
791
+ 'greta.fr' => 1,
792
+ 'huissier-justice.fr' => 1,
793
+ 'medecin.fr' => 1,
794
+ 'notaires.fr' => 1,
795
+ 'pharmacien.fr' => 1,
796
+ 'port.fr' => 1,
797
+ 'veterinaire.fr' => 1,
798
+ 'ga' => 1,
799
+ 'gb' => 1,
800
+ 'gd' => 1,
801
+ 'ge' => 1,
802
+ 'com.ge' => 1,
803
+ 'edu.ge' => 1,
804
+ 'gov.ge' => 1,
805
+ 'org.ge' => 1,
806
+ 'mil.ge' => 1,
807
+ 'net.ge' => 1,
808
+ 'pvt.ge' => 1,
809
+ 'gf' => 1,
810
+ 'gg' => 1,
811
+ 'co.gg' => 1,
812
+ 'net.gg' => 1,
813
+ 'org.gg' => 1,
814
+ 'gh' => 1,
815
+ 'com.gh' => 1,
816
+ 'edu.gh' => 1,
817
+ 'gov.gh' => 1,
818
+ 'org.gh' => 1,
819
+ 'mil.gh' => 1,
820
+ 'gi' => 1,
821
+ 'com.gi' => 1,
822
+ 'ltd.gi' => 1,
823
+ 'gov.gi' => 1,
824
+ 'mod.gi' => 1,
825
+ 'edu.gi' => 1,
826
+ 'org.gi' => 1,
827
+ 'gl' => 1,
828
+ 'co.gl' => 1,
829
+ 'com.gl' => 1,
830
+ 'edu.gl' => 1,
831
+ 'net.gl' => 1,
832
+ 'org.gl' => 1,
833
+ 'gm' => 1,
834
+ 'gn' => 1,
835
+ 'ac.gn' => 1,
836
+ 'com.gn' => 1,
837
+ 'edu.gn' => 1,
838
+ 'gov.gn' => 1,
839
+ 'org.gn' => 1,
840
+ 'net.gn' => 1,
841
+ 'gov' => 1,
842
+ 'gp' => 1,
843
+ 'com.gp' => 1,
844
+ 'net.gp' => 1,
845
+ 'mobi.gp' => 1,
846
+ 'edu.gp' => 1,
847
+ 'org.gp' => 1,
848
+ 'asso.gp' => 1,
849
+ 'gq' => 1,
850
+ 'gr' => 1,
851
+ 'com.gr' => 1,
852
+ 'edu.gr' => 1,
853
+ 'net.gr' => 1,
854
+ 'org.gr' => 1,
855
+ 'gov.gr' => 1,
856
+ 'gs' => 1,
857
+ 'gt' => 1,
858
+ 'com.gt' => 1,
859
+ 'edu.gt' => 1,
860
+ 'gob.gt' => 1,
861
+ 'ind.gt' => 1,
862
+ 'mil.gt' => 1,
863
+ 'net.gt' => 1,
864
+ 'org.gt' => 1,
865
+ 'gu' => 1,
866
+ 'com.gu' => 1,
867
+ 'edu.gu' => 1,
868
+ 'gov.gu' => 1,
869
+ 'guam.gu' => 1,
870
+ 'info.gu' => 1,
871
+ 'net.gu' => 1,
872
+ 'org.gu' => 1,
873
+ 'web.gu' => 1,
874
+ 'gw' => 1,
875
+ 'gy' => 1,
876
+ 'co.gy' => 1,
877
+ 'com.gy' => 1,
878
+ 'edu.gy' => 1,
879
+ 'gov.gy' => 1,
880
+ 'net.gy' => 1,
881
+ 'org.gy' => 1,
882
+ 'hk' => 1,
883
+ 'com.hk' => 1,
884
+ 'edu.hk' => 1,
885
+ 'gov.hk' => 1,
886
+ 'idv.hk' => 1,
887
+ 'net.hk' => 1,
888
+ 'org.hk' => 1,
889
+ '公司.hk' => 1,
890
+ '教育.hk' => 1,
891
+ '敎育.hk' => 1,
892
+ '政府.hk' => 1,
893
+ '個人.hk' => 1,
894
+ '个人.hk' => 1,
895
+ '箇人.hk' => 1,
896
+ '網络.hk' => 1,
897
+ '网络.hk' => 1,
898
+ '组織.hk' => 1,
899
+ '網絡.hk' => 1,
900
+ '网絡.hk' => 1,
901
+ '组织.hk' => 1,
902
+ '組織.hk' => 1,
903
+ '組织.hk' => 1,
904
+ 'hm' => 1,
905
+ 'hn' => 1,
906
+ 'com.hn' => 1,
907
+ 'edu.hn' => 1,
908
+ 'org.hn' => 1,
909
+ 'net.hn' => 1,
910
+ 'mil.hn' => 1,
911
+ 'gob.hn' => 1,
912
+ 'hr' => 1,
913
+ 'iz.hr' => 1,
914
+ 'from.hr' => 1,
915
+ 'name.hr' => 1,
916
+ 'com.hr' => 1,
917
+ 'ht' => 1,
918
+ 'com.ht' => 1,
919
+ 'shop.ht' => 1,
920
+ 'firm.ht' => 1,
921
+ 'info.ht' => 1,
922
+ 'adult.ht' => 1,
923
+ 'net.ht' => 1,
924
+ 'pro.ht' => 1,
925
+ 'org.ht' => 1,
926
+ 'med.ht' => 1,
927
+ 'art.ht' => 1,
928
+ 'coop.ht' => 1,
929
+ 'pol.ht' => 1,
930
+ 'asso.ht' => 1,
931
+ 'edu.ht' => 1,
932
+ 'rel.ht' => 1,
933
+ 'gouv.ht' => 1,
934
+ 'perso.ht' => 1,
935
+ 'hu' => 1,
936
+ 'co.hu' => 1,
937
+ 'info.hu' => 1,
938
+ 'org.hu' => 1,
939
+ 'priv.hu' => 1,
940
+ 'sport.hu' => 1,
941
+ 'tm.hu' => 1,
942
+ '2000.hu' => 1,
943
+ 'agrar.hu' => 1,
944
+ 'bolt.hu' => 1,
945
+ 'casino.hu' => 1,
946
+ 'city.hu' => 1,
947
+ 'erotica.hu' => 1,
948
+ 'erotika.hu' => 1,
949
+ 'film.hu' => 1,
950
+ 'forum.hu' => 1,
951
+ 'games.hu' => 1,
952
+ 'hotel.hu' => 1,
953
+ 'ingatlan.hu' => 1,
954
+ 'jogasz.hu' => 1,
955
+ 'konyvelo.hu' => 1,
956
+ 'lakas.hu' => 1,
957
+ 'media.hu' => 1,
958
+ 'news.hu' => 1,
959
+ 'reklam.hu' => 1,
960
+ 'sex.hu' => 1,
961
+ 'shop.hu' => 1,
962
+ 'suli.hu' => 1,
963
+ 'szex.hu' => 1,
964
+ 'tozsde.hu' => 1,
965
+ 'utazas.hu' => 1,
966
+ 'video.hu' => 1,
967
+ 'id' => 1,
968
+ 'ac.id' => 1,
969
+ 'biz.id' => 1,
970
+ 'co.id' => 1,
971
+ 'desa.id' => 1,
972
+ 'go.id' => 1,
973
+ 'mil.id' => 1,
974
+ 'my.id' => 1,
975
+ 'net.id' => 1,
976
+ 'or.id' => 1,
977
+ 'ponpes.id' => 1,
978
+ 'sch.id' => 1,
979
+ 'web.id' => 1,
980
+ 'ie' => 1,
981
+ 'gov.ie' => 1,
982
+ 'il' => 1,
983
+ 'ac.il' => 1,
984
+ 'co.il' => 1,
985
+ 'gov.il' => 1,
986
+ 'idf.il' => 1,
987
+ 'k12.il' => 1,
988
+ 'muni.il' => 1,
989
+ 'net.il' => 1,
990
+ 'org.il' => 1,
991
+ 'im' => 1,
992
+ 'ac.im' => 1,
993
+ 'co.im' => 1,
994
+ 'com.im' => 1,
995
+ 'ltd.co.im' => 1,
996
+ 'net.im' => 1,
997
+ 'org.im' => 1,
998
+ 'plc.co.im' => 1,
999
+ 'tt.im' => 1,
1000
+ 'tv.im' => 1,
1001
+ 'in' => 1,
1002
+ 'co.in' => 1,
1003
+ 'firm.in' => 1,
1004
+ 'net.in' => 1,
1005
+ 'org.in' => 1,
1006
+ 'gen.in' => 1,
1007
+ 'ind.in' => 1,
1008
+ 'nic.in' => 1,
1009
+ 'ac.in' => 1,
1010
+ 'edu.in' => 1,
1011
+ 'res.in' => 1,
1012
+ 'gov.in' => 1,
1013
+ 'mil.in' => 1,
1014
+ 'info' => 1,
1015
+ 'int' => 1,
1016
+ 'eu.int' => 1,
1017
+ 'io' => 1,
1018
+ 'com.io' => 1,
1019
+ 'iq' => 1,
1020
+ 'gov.iq' => 1,
1021
+ 'edu.iq' => 1,
1022
+ 'mil.iq' => 1,
1023
+ 'com.iq' => 1,
1024
+ 'org.iq' => 1,
1025
+ 'net.iq' => 1,
1026
+ 'ir' => 1,
1027
+ 'ac.ir' => 1,
1028
+ 'co.ir' => 1,
1029
+ 'gov.ir' => 1,
1030
+ 'id.ir' => 1,
1031
+ 'net.ir' => 1,
1032
+ 'org.ir' => 1,
1033
+ 'sch.ir' => 1,
1034
+ 'ایران.ir' => 1,
1035
+ 'ايران.ir' => 1,
1036
+ 'is' => 1,
1037
+ 'net.is' => 1,
1038
+ 'com.is' => 1,
1039
+ 'edu.is' => 1,
1040
+ 'gov.is' => 1,
1041
+ 'org.is' => 1,
1042
+ 'int.is' => 1,
1043
+ 'it' => 1,
1044
+ 'gov.it' => 1,
1045
+ 'edu.it' => 1,
1046
+ 'abr.it' => 1,
1047
+ 'abruzzo.it' => 1,
1048
+ 'aosta-valley.it' => 1,
1049
+ 'aostavalley.it' => 1,
1050
+ 'bas.it' => 1,
1051
+ 'basilicata.it' => 1,
1052
+ 'cal.it' => 1,
1053
+ 'calabria.it' => 1,
1054
+ 'cam.it' => 1,
1055
+ 'campania.it' => 1,
1056
+ 'emilia-romagna.it' => 1,
1057
+ 'emiliaromagna.it' => 1,
1058
+ 'emr.it' => 1,
1059
+ 'friuli-v-giulia.it' => 1,
1060
+ 'friuli-ve-giulia.it' => 1,
1061
+ 'friuli-vegiulia.it' => 1,
1062
+ 'friuli-venezia-giulia.it' => 1,
1063
+ 'friuli-veneziagiulia.it' => 1,
1064
+ 'friuli-vgiulia.it' => 1,
1065
+ 'friuliv-giulia.it' => 1,
1066
+ 'friulive-giulia.it' => 1,
1067
+ 'friulivegiulia.it' => 1,
1068
+ 'friulivenezia-giulia.it' => 1,
1069
+ 'friuliveneziagiulia.it' => 1,
1070
+ 'friulivgiulia.it' => 1,
1071
+ 'fvg.it' => 1,
1072
+ 'laz.it' => 1,
1073
+ 'lazio.it' => 1,
1074
+ 'lig.it' => 1,
1075
+ 'liguria.it' => 1,
1076
+ 'lom.it' => 1,
1077
+ 'lombardia.it' => 1,
1078
+ 'lombardy.it' => 1,
1079
+ 'lucania.it' => 1,
1080
+ 'mar.it' => 1,
1081
+ 'marche.it' => 1,
1082
+ 'mol.it' => 1,
1083
+ 'molise.it' => 1,
1084
+ 'piedmont.it' => 1,
1085
+ 'piemonte.it' => 1,
1086
+ 'pmn.it' => 1,
1087
+ 'pug.it' => 1,
1088
+ 'puglia.it' => 1,
1089
+ 'sar.it' => 1,
1090
+ 'sardegna.it' => 1,
1091
+ 'sardinia.it' => 1,
1092
+ 'sic.it' => 1,
1093
+ 'sicilia.it' => 1,
1094
+ 'sicily.it' => 1,
1095
+ 'taa.it' => 1,
1096
+ 'tos.it' => 1,
1097
+ 'toscana.it' => 1,
1098
+ 'trentin-sud-tirol.it' => 1,
1099
+ 'trentin-süd-tirol.it' => 1,
1100
+ 'trentin-sudtirol.it' => 1,
1101
+ 'trentin-südtirol.it' => 1,
1102
+ 'trentin-sued-tirol.it' => 1,
1103
+ 'trentin-suedtirol.it' => 1,
1104
+ 'trentino-a-adige.it' => 1,
1105
+ 'trentino-aadige.it' => 1,
1106
+ 'trentino-alto-adige.it' => 1,
1107
+ 'trentino-altoadige.it' => 1,
1108
+ 'trentino-s-tirol.it' => 1,
1109
+ 'trentino-stirol.it' => 1,
1110
+ 'trentino-sud-tirol.it' => 1,
1111
+ 'trentino-süd-tirol.it' => 1,
1112
+ 'trentino-sudtirol.it' => 1,
1113
+ 'trentino-südtirol.it' => 1,
1114
+ 'trentino-sued-tirol.it' => 1,
1115
+ 'trentino-suedtirol.it' => 1,
1116
+ 'trentino.it' => 1,
1117
+ 'trentinoa-adige.it' => 1,
1118
+ 'trentinoaadige.it' => 1,
1119
+ 'trentinoalto-adige.it' => 1,
1120
+ 'trentinoaltoadige.it' => 1,
1121
+ 'trentinos-tirol.it' => 1,
1122
+ 'trentinostirol.it' => 1,
1123
+ 'trentinosud-tirol.it' => 1,
1124
+ 'trentinosüd-tirol.it' => 1,
1125
+ 'trentinosudtirol.it' => 1,
1126
+ 'trentinosüdtirol.it' => 1,
1127
+ 'trentinosued-tirol.it' => 1,
1128
+ 'trentinosuedtirol.it' => 1,
1129
+ 'trentinsud-tirol.it' => 1,
1130
+ 'trentinsüd-tirol.it' => 1,
1131
+ 'trentinsudtirol.it' => 1,
1132
+ 'trentinsüdtirol.it' => 1,
1133
+ 'trentinsued-tirol.it' => 1,
1134
+ 'trentinsuedtirol.it' => 1,
1135
+ 'tuscany.it' => 1,
1136
+ 'umb.it' => 1,
1137
+ 'umbria.it' => 1,
1138
+ 'val-d-aosta.it' => 1,
1139
+ 'val-daosta.it' => 1,
1140
+ 'vald-aosta.it' => 1,
1141
+ 'valdaosta.it' => 1,
1142
+ 'valle-aosta.it' => 1,
1143
+ 'valle-d-aosta.it' => 1,
1144
+ 'valle-daosta.it' => 1,
1145
+ 'valleaosta.it' => 1,
1146
+ 'valled-aosta.it' => 1,
1147
+ 'valledaosta.it' => 1,
1148
+ 'vallee-aoste.it' => 1,
1149
+ 'vallée-aoste.it' => 1,
1150
+ 'vallee-d-aoste.it' => 1,
1151
+ 'vallée-d-aoste.it' => 1,
1152
+ 'valleeaoste.it' => 1,
1153
+ 'valléeaoste.it' => 1,
1154
+ 'valleedaoste.it' => 1,
1155
+ 'valléedaoste.it' => 1,
1156
+ 'vao.it' => 1,
1157
+ 'vda.it' => 1,
1158
+ 'ven.it' => 1,
1159
+ 'veneto.it' => 1,
1160
+ 'ag.it' => 1,
1161
+ 'agrigento.it' => 1,
1162
+ 'al.it' => 1,
1163
+ 'alessandria.it' => 1,
1164
+ 'alto-adige.it' => 1,
1165
+ 'altoadige.it' => 1,
1166
+ 'an.it' => 1,
1167
+ 'ancona.it' => 1,
1168
+ 'andria-barletta-trani.it' => 1,
1169
+ 'andria-trani-barletta.it' => 1,
1170
+ 'andriabarlettatrani.it' => 1,
1171
+ 'andriatranibarletta.it' => 1,
1172
+ 'ao.it' => 1,
1173
+ 'aosta.it' => 1,
1174
+ 'aoste.it' => 1,
1175
+ 'ap.it' => 1,
1176
+ 'aq.it' => 1,
1177
+ 'aquila.it' => 1,
1178
+ 'ar.it' => 1,
1179
+ 'arezzo.it' => 1,
1180
+ 'ascoli-piceno.it' => 1,
1181
+ 'ascolipiceno.it' => 1,
1182
+ 'asti.it' => 1,
1183
+ 'at.it' => 1,
1184
+ 'av.it' => 1,
1185
+ 'avellino.it' => 1,
1186
+ 'ba.it' => 1,
1187
+ 'balsan-sudtirol.it' => 1,
1188
+ 'balsan-südtirol.it' => 1,
1189
+ 'balsan-suedtirol.it' => 1,
1190
+ 'balsan.it' => 1,
1191
+ 'bari.it' => 1,
1192
+ 'barletta-trani-andria.it' => 1,
1193
+ 'barlettatraniandria.it' => 1,
1194
+ 'belluno.it' => 1,
1195
+ 'benevento.it' => 1,
1196
+ 'bergamo.it' => 1,
1197
+ 'bg.it' => 1,
1198
+ 'bi.it' => 1,
1199
+ 'biella.it' => 1,
1200
+ 'bl.it' => 1,
1201
+ 'bn.it' => 1,
1202
+ 'bo.it' => 1,
1203
+ 'bologna.it' => 1,
1204
+ 'bolzano-altoadige.it' => 1,
1205
+ 'bolzano.it' => 1,
1206
+ 'bozen-sudtirol.it' => 1,
1207
+ 'bozen-südtirol.it' => 1,
1208
+ 'bozen-suedtirol.it' => 1,
1209
+ 'bozen.it' => 1,
1210
+ 'br.it' => 1,
1211
+ 'brescia.it' => 1,
1212
+ 'brindisi.it' => 1,
1213
+ 'bs.it' => 1,
1214
+ 'bt.it' => 1,
1215
+ 'bulsan-sudtirol.it' => 1,
1216
+ 'bulsan-südtirol.it' => 1,
1217
+ 'bulsan-suedtirol.it' => 1,
1218
+ 'bulsan.it' => 1,
1219
+ 'bz.it' => 1,
1220
+ 'ca.it' => 1,
1221
+ 'cagliari.it' => 1,
1222
+ 'caltanissetta.it' => 1,
1223
+ 'campidano-medio.it' => 1,
1224
+ 'campidanomedio.it' => 1,
1225
+ 'campobasso.it' => 1,
1226
+ 'carbonia-iglesias.it' => 1,
1227
+ 'carboniaiglesias.it' => 1,
1228
+ 'carrara-massa.it' => 1,
1229
+ 'carraramassa.it' => 1,
1230
+ 'caserta.it' => 1,
1231
+ 'catania.it' => 1,
1232
+ 'catanzaro.it' => 1,
1233
+ 'cb.it' => 1,
1234
+ 'ce.it' => 1,
1235
+ 'cesena-forli.it' => 1,
1236
+ 'cesena-forlì.it' => 1,
1237
+ 'cesenaforli.it' => 1,
1238
+ 'cesenaforlì.it' => 1,
1239
+ 'ch.it' => 1,
1240
+ 'chieti.it' => 1,
1241
+ 'ci.it' => 1,
1242
+ 'cl.it' => 1,
1243
+ 'cn.it' => 1,
1244
+ 'co.it' => 1,
1245
+ 'como.it' => 1,
1246
+ 'cosenza.it' => 1,
1247
+ 'cr.it' => 1,
1248
+ 'cremona.it' => 1,
1249
+ 'crotone.it' => 1,
1250
+ 'cs.it' => 1,
1251
+ 'ct.it' => 1,
1252
+ 'cuneo.it' => 1,
1253
+ 'cz.it' => 1,
1254
+ 'dell-ogliastra.it' => 1,
1255
+ 'dellogliastra.it' => 1,
1256
+ 'en.it' => 1,
1257
+ 'enna.it' => 1,
1258
+ 'fc.it' => 1,
1259
+ 'fe.it' => 1,
1260
+ 'fermo.it' => 1,
1261
+ 'ferrara.it' => 1,
1262
+ 'fg.it' => 1,
1263
+ 'fi.it' => 1,
1264
+ 'firenze.it' => 1,
1265
+ 'florence.it' => 1,
1266
+ 'fm.it' => 1,
1267
+ 'foggia.it' => 1,
1268
+ 'forli-cesena.it' => 1,
1269
+ 'forlì-cesena.it' => 1,
1270
+ 'forlicesena.it' => 1,
1271
+ 'forlìcesena.it' => 1,
1272
+ 'fr.it' => 1,
1273
+ 'frosinone.it' => 1,
1274
+ 'ge.it' => 1,
1275
+ 'genoa.it' => 1,
1276
+ 'genova.it' => 1,
1277
+ 'go.it' => 1,
1278
+ 'gorizia.it' => 1,
1279
+ 'gr.it' => 1,
1280
+ 'grosseto.it' => 1,
1281
+ 'iglesias-carbonia.it' => 1,
1282
+ 'iglesiascarbonia.it' => 1,
1283
+ 'im.it' => 1,
1284
+ 'imperia.it' => 1,
1285
+ 'is.it' => 1,
1286
+ 'isernia.it' => 1,
1287
+ 'kr.it' => 1,
1288
+ 'la-spezia.it' => 1,
1289
+ 'laquila.it' => 1,
1290
+ 'laspezia.it' => 1,
1291
+ 'latina.it' => 1,
1292
+ 'lc.it' => 1,
1293
+ 'le.it' => 1,
1294
+ 'lecce.it' => 1,
1295
+ 'lecco.it' => 1,
1296
+ 'li.it' => 1,
1297
+ 'livorno.it' => 1,
1298
+ 'lo.it' => 1,
1299
+ 'lodi.it' => 1,
1300
+ 'lt.it' => 1,
1301
+ 'lu.it' => 1,
1302
+ 'lucca.it' => 1,
1303
+ 'macerata.it' => 1,
1304
+ 'mantova.it' => 1,
1305
+ 'massa-carrara.it' => 1,
1306
+ 'massacarrara.it' => 1,
1307
+ 'matera.it' => 1,
1308
+ 'mb.it' => 1,
1309
+ 'mc.it' => 1,
1310
+ 'me.it' => 1,
1311
+ 'medio-campidano.it' => 1,
1312
+ 'mediocampidano.it' => 1,
1313
+ 'messina.it' => 1,
1314
+ 'mi.it' => 1,
1315
+ 'milan.it' => 1,
1316
+ 'milano.it' => 1,
1317
+ 'mn.it' => 1,
1318
+ 'mo.it' => 1,
1319
+ 'modena.it' => 1,
1320
+ 'monza-brianza.it' => 1,
1321
+ 'monza-e-della-brianza.it' => 1,
1322
+ 'monza.it' => 1,
1323
+ 'monzabrianza.it' => 1,
1324
+ 'monzaebrianza.it' => 1,
1325
+ 'monzaedellabrianza.it' => 1,
1326
+ 'ms.it' => 1,
1327
+ 'mt.it' => 1,
1328
+ 'na.it' => 1,
1329
+ 'naples.it' => 1,
1330
+ 'napoli.it' => 1,
1331
+ 'no.it' => 1,
1332
+ 'novara.it' => 1,
1333
+ 'nu.it' => 1,
1334
+ 'nuoro.it' => 1,
1335
+ 'og.it' => 1,
1336
+ 'ogliastra.it' => 1,
1337
+ 'olbia-tempio.it' => 1,
1338
+ 'olbiatempio.it' => 1,
1339
+ 'or.it' => 1,
1340
+ 'oristano.it' => 1,
1341
+ 'ot.it' => 1,
1342
+ 'pa.it' => 1,
1343
+ 'padova.it' => 1,
1344
+ 'padua.it' => 1,
1345
+ 'palermo.it' => 1,
1346
+ 'parma.it' => 1,
1347
+ 'pavia.it' => 1,
1348
+ 'pc.it' => 1,
1349
+ 'pd.it' => 1,
1350
+ 'pe.it' => 1,
1351
+ 'perugia.it' => 1,
1352
+ 'pesaro-urbino.it' => 1,
1353
+ 'pesarourbino.it' => 1,
1354
+ 'pescara.it' => 1,
1355
+ 'pg.it' => 1,
1356
+ 'pi.it' => 1,
1357
+ 'piacenza.it' => 1,
1358
+ 'pisa.it' => 1,
1359
+ 'pistoia.it' => 1,
1360
+ 'pn.it' => 1,
1361
+ 'po.it' => 1,
1362
+ 'pordenone.it' => 1,
1363
+ 'potenza.it' => 1,
1364
+ 'pr.it' => 1,
1365
+ 'prato.it' => 1,
1366
+ 'pt.it' => 1,
1367
+ 'pu.it' => 1,
1368
+ 'pv.it' => 1,
1369
+ 'pz.it' => 1,
1370
+ 'ra.it' => 1,
1371
+ 'ragusa.it' => 1,
1372
+ 'ravenna.it' => 1,
1373
+ 'rc.it' => 1,
1374
+ 're.it' => 1,
1375
+ 'reggio-calabria.it' => 1,
1376
+ 'reggio-emilia.it' => 1,
1377
+ 'reggiocalabria.it' => 1,
1378
+ 'reggioemilia.it' => 1,
1379
+ 'rg.it' => 1,
1380
+ 'ri.it' => 1,
1381
+ 'rieti.it' => 1,
1382
+ 'rimini.it' => 1,
1383
+ 'rm.it' => 1,
1384
+ 'rn.it' => 1,
1385
+ 'ro.it' => 1,
1386
+ 'roma.it' => 1,
1387
+ 'rome.it' => 1,
1388
+ 'rovigo.it' => 1,
1389
+ 'sa.it' => 1,
1390
+ 'salerno.it' => 1,
1391
+ 'sassari.it' => 1,
1392
+ 'savona.it' => 1,
1393
+ 'si.it' => 1,
1394
+ 'siena.it' => 1,
1395
+ 'siracusa.it' => 1,
1396
+ 'so.it' => 1,
1397
+ 'sondrio.it' => 1,
1398
+ 'sp.it' => 1,
1399
+ 'sr.it' => 1,
1400
+ 'ss.it' => 1,
1401
+ 'suedtirol.it' => 1,
1402
+ 'südtirol.it' => 1,
1403
+ 'sv.it' => 1,
1404
+ 'ta.it' => 1,
1405
+ 'taranto.it' => 1,
1406
+ 'te.it' => 1,
1407
+ 'tempio-olbia.it' => 1,
1408
+ 'tempioolbia.it' => 1,
1409
+ 'teramo.it' => 1,
1410
+ 'terni.it' => 1,
1411
+ 'tn.it' => 1,
1412
+ 'to.it' => 1,
1413
+ 'torino.it' => 1,
1414
+ 'tp.it' => 1,
1415
+ 'tr.it' => 1,
1416
+ 'trani-andria-barletta.it' => 1,
1417
+ 'trani-barletta-andria.it' => 1,
1418
+ 'traniandriabarletta.it' => 1,
1419
+ 'tranibarlettaandria.it' => 1,
1420
+ 'trapani.it' => 1,
1421
+ 'trento.it' => 1,
1422
+ 'treviso.it' => 1,
1423
+ 'trieste.it' => 1,
1424
+ 'ts.it' => 1,
1425
+ 'turin.it' => 1,
1426
+ 'tv.it' => 1,
1427
+ 'ud.it' => 1,
1428
+ 'udine.it' => 1,
1429
+ 'urbino-pesaro.it' => 1,
1430
+ 'urbinopesaro.it' => 1,
1431
+ 'va.it' => 1,
1432
+ 'varese.it' => 1,
1433
+ 'vb.it' => 1,
1434
+ 'vc.it' => 1,
1435
+ 've.it' => 1,
1436
+ 'venezia.it' => 1,
1437
+ 'venice.it' => 1,
1438
+ 'verbania.it' => 1,
1439
+ 'vercelli.it' => 1,
1440
+ 'verona.it' => 1,
1441
+ 'vi.it' => 1,
1442
+ 'vibo-valentia.it' => 1,
1443
+ 'vibovalentia.it' => 1,
1444
+ 'vicenza.it' => 1,
1445
+ 'viterbo.it' => 1,
1446
+ 'vr.it' => 1,
1447
+ 'vs.it' => 1,
1448
+ 'vt.it' => 1,
1449
+ 'vv.it' => 1,
1450
+ 'je' => 1,
1451
+ 'co.je' => 1,
1452
+ 'net.je' => 1,
1453
+ 'org.je' => 1,
1454
+ '*.jm' => 1,
1455
+ 'jo' => 1,
1456
+ 'com.jo' => 1,
1457
+ 'org.jo' => 1,
1458
+ 'net.jo' => 1,
1459
+ 'edu.jo' => 1,
1460
+ 'sch.jo' => 1,
1461
+ 'gov.jo' => 1,
1462
+ 'mil.jo' => 1,
1463
+ 'name.jo' => 1,
1464
+ 'jobs' => 1,
1465
+ 'jp' => 1,
1466
+ 'ac.jp' => 1,
1467
+ 'ad.jp' => 1,
1468
+ 'co.jp' => 1,
1469
+ 'ed.jp' => 1,
1470
+ 'go.jp' => 1,
1471
+ 'gr.jp' => 1,
1472
+ 'lg.jp' => 1,
1473
+ 'ne.jp' => 1,
1474
+ 'or.jp' => 1,
1475
+ 'aichi.jp' => 1,
1476
+ 'akita.jp' => 1,
1477
+ 'aomori.jp' => 1,
1478
+ 'chiba.jp' => 1,
1479
+ 'ehime.jp' => 1,
1480
+ 'fukui.jp' => 1,
1481
+ 'fukuoka.jp' => 1,
1482
+ 'fukushima.jp' => 1,
1483
+ 'gifu.jp' => 1,
1484
+ 'gunma.jp' => 1,
1485
+ 'hiroshima.jp' => 1,
1486
+ 'hokkaido.jp' => 1,
1487
+ 'hyogo.jp' => 1,
1488
+ 'ibaraki.jp' => 1,
1489
+ 'ishikawa.jp' => 1,
1490
+ 'iwate.jp' => 1,
1491
+ 'kagawa.jp' => 1,
1492
+ 'kagoshima.jp' => 1,
1493
+ 'kanagawa.jp' => 1,
1494
+ 'kochi.jp' => 1,
1495
+ 'kumamoto.jp' => 1,
1496
+ 'kyoto.jp' => 1,
1497
+ 'mie.jp' => 1,
1498
+ 'miyagi.jp' => 1,
1499
+ 'miyazaki.jp' => 1,
1500
+ 'nagano.jp' => 1,
1501
+ 'nagasaki.jp' => 1,
1502
+ 'nara.jp' => 1,
1503
+ 'niigata.jp' => 1,
1504
+ 'oita.jp' => 1,
1505
+ 'okayama.jp' => 1,
1506
+ 'okinawa.jp' => 1,
1507
+ 'osaka.jp' => 1,
1508
+ 'saga.jp' => 1,
1509
+ 'saitama.jp' => 1,
1510
+ 'shiga.jp' => 1,
1511
+ 'shimane.jp' => 1,
1512
+ 'shizuoka.jp' => 1,
1513
+ 'tochigi.jp' => 1,
1514
+ 'tokushima.jp' => 1,
1515
+ 'tokyo.jp' => 1,
1516
+ 'tottori.jp' => 1,
1517
+ 'toyama.jp' => 1,
1518
+ 'wakayama.jp' => 1,
1519
+ 'yamagata.jp' => 1,
1520
+ 'yamaguchi.jp' => 1,
1521
+ 'yamanashi.jp' => 1,
1522
+ '栃木.jp' => 1,
1523
+ '愛知.jp' => 1,
1524
+ '愛媛.jp' => 1,
1525
+ '兵庫.jp' => 1,
1526
+ '熊本.jp' => 1,
1527
+ '茨城.jp' => 1,
1528
+ '北海道.jp' => 1,
1529
+ '千葉.jp' => 1,
1530
+ '和歌山.jp' => 1,
1531
+ '長崎.jp' => 1,
1532
+ '長野.jp' => 1,
1533
+ '新潟.jp' => 1,
1534
+ '青森.jp' => 1,
1535
+ '静岡.jp' => 1,
1536
+ '東京.jp' => 1,
1537
+ '石川.jp' => 1,
1538
+ '埼玉.jp' => 1,
1539
+ '三重.jp' => 1,
1540
+ '京都.jp' => 1,
1541
+ '佐賀.jp' => 1,
1542
+ '大分.jp' => 1,
1543
+ '大阪.jp' => 1,
1544
+ '奈良.jp' => 1,
1545
+ '宮城.jp' => 1,
1546
+ '宮崎.jp' => 1,
1547
+ '富山.jp' => 1,
1548
+ '山口.jp' => 1,
1549
+ '山形.jp' => 1,
1550
+ '山梨.jp' => 1,
1551
+ '岩手.jp' => 1,
1552
+ '岐阜.jp' => 1,
1553
+ '岡山.jp' => 1,
1554
+ '島根.jp' => 1,
1555
+ '広島.jp' => 1,
1556
+ '徳島.jp' => 1,
1557
+ '沖縄.jp' => 1,
1558
+ '滋賀.jp' => 1,
1559
+ '神奈川.jp' => 1,
1560
+ '福井.jp' => 1,
1561
+ '福岡.jp' => 1,
1562
+ '福島.jp' => 1,
1563
+ '秋田.jp' => 1,
1564
+ '群馬.jp' => 1,
1565
+ '香川.jp' => 1,
1566
+ '高知.jp' => 1,
1567
+ '鳥取.jp' => 1,
1568
+ '鹿児島.jp' => 1,
1569
+ '*.kawasaki.jp' => 1,
1570
+ '*.kitakyushu.jp' => 1,
1571
+ '*.kobe.jp' => 1,
1572
+ '*.nagoya.jp' => 1,
1573
+ '*.sapporo.jp' => 1,
1574
+ '*.sendai.jp' => 1,
1575
+ '*.yokohama.jp' => 1,
1576
+ '!city.kawasaki.jp' => 1,
1577
+ '!city.kitakyushu.jp' => 1,
1578
+ '!city.kobe.jp' => 1,
1579
+ '!city.nagoya.jp' => 1,
1580
+ '!city.sapporo.jp' => 1,
1581
+ '!city.sendai.jp' => 1,
1582
+ '!city.yokohama.jp' => 1,
1583
+ 'aisai.aichi.jp' => 1,
1584
+ 'ama.aichi.jp' => 1,
1585
+ 'anjo.aichi.jp' => 1,
1586
+ 'asuke.aichi.jp' => 1,
1587
+ 'chiryu.aichi.jp' => 1,
1588
+ 'chita.aichi.jp' => 1,
1589
+ 'fuso.aichi.jp' => 1,
1590
+ 'gamagori.aichi.jp' => 1,
1591
+ 'handa.aichi.jp' => 1,
1592
+ 'hazu.aichi.jp' => 1,
1593
+ 'hekinan.aichi.jp' => 1,
1594
+ 'higashiura.aichi.jp' => 1,
1595
+ 'ichinomiya.aichi.jp' => 1,
1596
+ 'inazawa.aichi.jp' => 1,
1597
+ 'inuyama.aichi.jp' => 1,
1598
+ 'isshiki.aichi.jp' => 1,
1599
+ 'iwakura.aichi.jp' => 1,
1600
+ 'kanie.aichi.jp' => 1,
1601
+ 'kariya.aichi.jp' => 1,
1602
+ 'kasugai.aichi.jp' => 1,
1603
+ 'kira.aichi.jp' => 1,
1604
+ 'kiyosu.aichi.jp' => 1,
1605
+ 'komaki.aichi.jp' => 1,
1606
+ 'konan.aichi.jp' => 1,
1607
+ 'kota.aichi.jp' => 1,
1608
+ 'mihama.aichi.jp' => 1,
1609
+ 'miyoshi.aichi.jp' => 1,
1610
+ 'nishio.aichi.jp' => 1,
1611
+ 'nisshin.aichi.jp' => 1,
1612
+ 'obu.aichi.jp' => 1,
1613
+ 'oguchi.aichi.jp' => 1,
1614
+ 'oharu.aichi.jp' => 1,
1615
+ 'okazaki.aichi.jp' => 1,
1616
+ 'owariasahi.aichi.jp' => 1,
1617
+ 'seto.aichi.jp' => 1,
1618
+ 'shikatsu.aichi.jp' => 1,
1619
+ 'shinshiro.aichi.jp' => 1,
1620
+ 'shitara.aichi.jp' => 1,
1621
+ 'tahara.aichi.jp' => 1,
1622
+ 'takahama.aichi.jp' => 1,
1623
+ 'tobishima.aichi.jp' => 1,
1624
+ 'toei.aichi.jp' => 1,
1625
+ 'togo.aichi.jp' => 1,
1626
+ 'tokai.aichi.jp' => 1,
1627
+ 'tokoname.aichi.jp' => 1,
1628
+ 'toyoake.aichi.jp' => 1,
1629
+ 'toyohashi.aichi.jp' => 1,
1630
+ 'toyokawa.aichi.jp' => 1,
1631
+ 'toyone.aichi.jp' => 1,
1632
+ 'toyota.aichi.jp' => 1,
1633
+ 'tsushima.aichi.jp' => 1,
1634
+ 'yatomi.aichi.jp' => 1,
1635
+ 'akita.akita.jp' => 1,
1636
+ 'daisen.akita.jp' => 1,
1637
+ 'fujisato.akita.jp' => 1,
1638
+ 'gojome.akita.jp' => 1,
1639
+ 'hachirogata.akita.jp' => 1,
1640
+ 'happou.akita.jp' => 1,
1641
+ 'higashinaruse.akita.jp' => 1,
1642
+ 'honjo.akita.jp' => 1,
1643
+ 'honjyo.akita.jp' => 1,
1644
+ 'ikawa.akita.jp' => 1,
1645
+ 'kamikoani.akita.jp' => 1,
1646
+ 'kamioka.akita.jp' => 1,
1647
+ 'katagami.akita.jp' => 1,
1648
+ 'kazuno.akita.jp' => 1,
1649
+ 'kitaakita.akita.jp' => 1,
1650
+ 'kosaka.akita.jp' => 1,
1651
+ 'kyowa.akita.jp' => 1,
1652
+ 'misato.akita.jp' => 1,
1653
+ 'mitane.akita.jp' => 1,
1654
+ 'moriyoshi.akita.jp' => 1,
1655
+ 'nikaho.akita.jp' => 1,
1656
+ 'noshiro.akita.jp' => 1,
1657
+ 'odate.akita.jp' => 1,
1658
+ 'oga.akita.jp' => 1,
1659
+ 'ogata.akita.jp' => 1,
1660
+ 'semboku.akita.jp' => 1,
1661
+ 'yokote.akita.jp' => 1,
1662
+ 'yurihonjo.akita.jp' => 1,
1663
+ 'aomori.aomori.jp' => 1,
1664
+ 'gonohe.aomori.jp' => 1,
1665
+ 'hachinohe.aomori.jp' => 1,
1666
+ 'hashikami.aomori.jp' => 1,
1667
+ 'hiranai.aomori.jp' => 1,
1668
+ 'hirosaki.aomori.jp' => 1,
1669
+ 'itayanagi.aomori.jp' => 1,
1670
+ 'kuroishi.aomori.jp' => 1,
1671
+ 'misawa.aomori.jp' => 1,
1672
+ 'mutsu.aomori.jp' => 1,
1673
+ 'nakadomari.aomori.jp' => 1,
1674
+ 'noheji.aomori.jp' => 1,
1675
+ 'oirase.aomori.jp' => 1,
1676
+ 'owani.aomori.jp' => 1,
1677
+ 'rokunohe.aomori.jp' => 1,
1678
+ 'sannohe.aomori.jp' => 1,
1679
+ 'shichinohe.aomori.jp' => 1,
1680
+ 'shingo.aomori.jp' => 1,
1681
+ 'takko.aomori.jp' => 1,
1682
+ 'towada.aomori.jp' => 1,
1683
+ 'tsugaru.aomori.jp' => 1,
1684
+ 'tsuruta.aomori.jp' => 1,
1685
+ 'abiko.chiba.jp' => 1,
1686
+ 'asahi.chiba.jp' => 1,
1687
+ 'chonan.chiba.jp' => 1,
1688
+ 'chosei.chiba.jp' => 1,
1689
+ 'choshi.chiba.jp' => 1,
1690
+ 'chuo.chiba.jp' => 1,
1691
+ 'funabashi.chiba.jp' => 1,
1692
+ 'futtsu.chiba.jp' => 1,
1693
+ 'hanamigawa.chiba.jp' => 1,
1694
+ 'ichihara.chiba.jp' => 1,
1695
+ 'ichikawa.chiba.jp' => 1,
1696
+ 'ichinomiya.chiba.jp' => 1,
1697
+ 'inzai.chiba.jp' => 1,
1698
+ 'isumi.chiba.jp' => 1,
1699
+ 'kamagaya.chiba.jp' => 1,
1700
+ 'kamogawa.chiba.jp' => 1,
1701
+ 'kashiwa.chiba.jp' => 1,
1702
+ 'katori.chiba.jp' => 1,
1703
+ 'katsuura.chiba.jp' => 1,
1704
+ 'kimitsu.chiba.jp' => 1,
1705
+ 'kisarazu.chiba.jp' => 1,
1706
+ 'kozaki.chiba.jp' => 1,
1707
+ 'kujukuri.chiba.jp' => 1,
1708
+ 'kyonan.chiba.jp' => 1,
1709
+ 'matsudo.chiba.jp' => 1,
1710
+ 'midori.chiba.jp' => 1,
1711
+ 'mihama.chiba.jp' => 1,
1712
+ 'minamiboso.chiba.jp' => 1,
1713
+ 'mobara.chiba.jp' => 1,
1714
+ 'mutsuzawa.chiba.jp' => 1,
1715
+ 'nagara.chiba.jp' => 1,
1716
+ 'nagareyama.chiba.jp' => 1,
1717
+ 'narashino.chiba.jp' => 1,
1718
+ 'narita.chiba.jp' => 1,
1719
+ 'noda.chiba.jp' => 1,
1720
+ 'oamishirasato.chiba.jp' => 1,
1721
+ 'omigawa.chiba.jp' => 1,
1722
+ 'onjuku.chiba.jp' => 1,
1723
+ 'otaki.chiba.jp' => 1,
1724
+ 'sakae.chiba.jp' => 1,
1725
+ 'sakura.chiba.jp' => 1,
1726
+ 'shimofusa.chiba.jp' => 1,
1727
+ 'shirako.chiba.jp' => 1,
1728
+ 'shiroi.chiba.jp' => 1,
1729
+ 'shisui.chiba.jp' => 1,
1730
+ 'sodegaura.chiba.jp' => 1,
1731
+ 'sosa.chiba.jp' => 1,
1732
+ 'tako.chiba.jp' => 1,
1733
+ 'tateyama.chiba.jp' => 1,
1734
+ 'togane.chiba.jp' => 1,
1735
+ 'tohnosho.chiba.jp' => 1,
1736
+ 'tomisato.chiba.jp' => 1,
1737
+ 'urayasu.chiba.jp' => 1,
1738
+ 'yachimata.chiba.jp' => 1,
1739
+ 'yachiyo.chiba.jp' => 1,
1740
+ 'yokaichiba.chiba.jp' => 1,
1741
+ 'yokoshibahikari.chiba.jp' => 1,
1742
+ 'yotsukaido.chiba.jp' => 1,
1743
+ 'ainan.ehime.jp' => 1,
1744
+ 'honai.ehime.jp' => 1,
1745
+ 'ikata.ehime.jp' => 1,
1746
+ 'imabari.ehime.jp' => 1,
1747
+ 'iyo.ehime.jp' => 1,
1748
+ 'kamijima.ehime.jp' => 1,
1749
+ 'kihoku.ehime.jp' => 1,
1750
+ 'kumakogen.ehime.jp' => 1,
1751
+ 'masaki.ehime.jp' => 1,
1752
+ 'matsuno.ehime.jp' => 1,
1753
+ 'matsuyama.ehime.jp' => 1,
1754
+ 'namikata.ehime.jp' => 1,
1755
+ 'niihama.ehime.jp' => 1,
1756
+ 'ozu.ehime.jp' => 1,
1757
+ 'saijo.ehime.jp' => 1,
1758
+ 'seiyo.ehime.jp' => 1,
1759
+ 'shikokuchuo.ehime.jp' => 1,
1760
+ 'tobe.ehime.jp' => 1,
1761
+ 'toon.ehime.jp' => 1,
1762
+ 'uchiko.ehime.jp' => 1,
1763
+ 'uwajima.ehime.jp' => 1,
1764
+ 'yawatahama.ehime.jp' => 1,
1765
+ 'echizen.fukui.jp' => 1,
1766
+ 'eiheiji.fukui.jp' => 1,
1767
+ 'fukui.fukui.jp' => 1,
1768
+ 'ikeda.fukui.jp' => 1,
1769
+ 'katsuyama.fukui.jp' => 1,
1770
+ 'mihama.fukui.jp' => 1,
1771
+ 'minamiechizen.fukui.jp' => 1,
1772
+ 'obama.fukui.jp' => 1,
1773
+ 'ohi.fukui.jp' => 1,
1774
+ 'ono.fukui.jp' => 1,
1775
+ 'sabae.fukui.jp' => 1,
1776
+ 'sakai.fukui.jp' => 1,
1777
+ 'takahama.fukui.jp' => 1,
1778
+ 'tsuruga.fukui.jp' => 1,
1779
+ 'wakasa.fukui.jp' => 1,
1780
+ 'ashiya.fukuoka.jp' => 1,
1781
+ 'buzen.fukuoka.jp' => 1,
1782
+ 'chikugo.fukuoka.jp' => 1,
1783
+ 'chikuho.fukuoka.jp' => 1,
1784
+ 'chikujo.fukuoka.jp' => 1,
1785
+ 'chikushino.fukuoka.jp' => 1,
1786
+ 'chikuzen.fukuoka.jp' => 1,
1787
+ 'chuo.fukuoka.jp' => 1,
1788
+ 'dazaifu.fukuoka.jp' => 1,
1789
+ 'fukuchi.fukuoka.jp' => 1,
1790
+ 'hakata.fukuoka.jp' => 1,
1791
+ 'higashi.fukuoka.jp' => 1,
1792
+ 'hirokawa.fukuoka.jp' => 1,
1793
+ 'hisayama.fukuoka.jp' => 1,
1794
+ 'iizuka.fukuoka.jp' => 1,
1795
+ 'inatsuki.fukuoka.jp' => 1,
1796
+ 'kaho.fukuoka.jp' => 1,
1797
+ 'kasuga.fukuoka.jp' => 1,
1798
+ 'kasuya.fukuoka.jp' => 1,
1799
+ 'kawara.fukuoka.jp' => 1,
1800
+ 'keisen.fukuoka.jp' => 1,
1801
+ 'koga.fukuoka.jp' => 1,
1802
+ 'kurate.fukuoka.jp' => 1,
1803
+ 'kurogi.fukuoka.jp' => 1,
1804
+ 'kurume.fukuoka.jp' => 1,
1805
+ 'minami.fukuoka.jp' => 1,
1806
+ 'miyako.fukuoka.jp' => 1,
1807
+ 'miyama.fukuoka.jp' => 1,
1808
+ 'miyawaka.fukuoka.jp' => 1,
1809
+ 'mizumaki.fukuoka.jp' => 1,
1810
+ 'munakata.fukuoka.jp' => 1,
1811
+ 'nakagawa.fukuoka.jp' => 1,
1812
+ 'nakama.fukuoka.jp' => 1,
1813
+ 'nishi.fukuoka.jp' => 1,
1814
+ 'nogata.fukuoka.jp' => 1,
1815
+ 'ogori.fukuoka.jp' => 1,
1816
+ 'okagaki.fukuoka.jp' => 1,
1817
+ 'okawa.fukuoka.jp' => 1,
1818
+ 'oki.fukuoka.jp' => 1,
1819
+ 'omuta.fukuoka.jp' => 1,
1820
+ 'onga.fukuoka.jp' => 1,
1821
+ 'onojo.fukuoka.jp' => 1,
1822
+ 'oto.fukuoka.jp' => 1,
1823
+ 'saigawa.fukuoka.jp' => 1,
1824
+ 'sasaguri.fukuoka.jp' => 1,
1825
+ 'shingu.fukuoka.jp' => 1,
1826
+ 'shinyoshitomi.fukuoka.jp' => 1,
1827
+ 'shonai.fukuoka.jp' => 1,
1828
+ 'soeda.fukuoka.jp' => 1,
1829
+ 'sue.fukuoka.jp' => 1,
1830
+ 'tachiarai.fukuoka.jp' => 1,
1831
+ 'tagawa.fukuoka.jp' => 1,
1832
+ 'takata.fukuoka.jp' => 1,
1833
+ 'toho.fukuoka.jp' => 1,
1834
+ 'toyotsu.fukuoka.jp' => 1,
1835
+ 'tsuiki.fukuoka.jp' => 1,
1836
+ 'ukiha.fukuoka.jp' => 1,
1837
+ 'umi.fukuoka.jp' => 1,
1838
+ 'usui.fukuoka.jp' => 1,
1839
+ 'yamada.fukuoka.jp' => 1,
1840
+ 'yame.fukuoka.jp' => 1,
1841
+ 'yanagawa.fukuoka.jp' => 1,
1842
+ 'yukuhashi.fukuoka.jp' => 1,
1843
+ 'aizubange.fukushima.jp' => 1,
1844
+ 'aizumisato.fukushima.jp' => 1,
1845
+ 'aizuwakamatsu.fukushima.jp' => 1,
1846
+ 'asakawa.fukushima.jp' => 1,
1847
+ 'bandai.fukushima.jp' => 1,
1848
+ 'date.fukushima.jp' => 1,
1849
+ 'fukushima.fukushima.jp' => 1,
1850
+ 'furudono.fukushima.jp' => 1,
1851
+ 'futaba.fukushima.jp' => 1,
1852
+ 'hanawa.fukushima.jp' => 1,
1853
+ 'higashi.fukushima.jp' => 1,
1854
+ 'hirata.fukushima.jp' => 1,
1855
+ 'hirono.fukushima.jp' => 1,
1856
+ 'iitate.fukushima.jp' => 1,
1857
+ 'inawashiro.fukushima.jp' => 1,
1858
+ 'ishikawa.fukushima.jp' => 1,
1859
+ 'iwaki.fukushima.jp' => 1,
1860
+ 'izumizaki.fukushima.jp' => 1,
1861
+ 'kagamiishi.fukushima.jp' => 1,
1862
+ 'kaneyama.fukushima.jp' => 1,
1863
+ 'kawamata.fukushima.jp' => 1,
1864
+ 'kitakata.fukushima.jp' => 1,
1865
+ 'kitashiobara.fukushima.jp' => 1,
1866
+ 'koori.fukushima.jp' => 1,
1867
+ 'koriyama.fukushima.jp' => 1,
1868
+ 'kunimi.fukushima.jp' => 1,
1869
+ 'miharu.fukushima.jp' => 1,
1870
+ 'mishima.fukushima.jp' => 1,
1871
+ 'namie.fukushima.jp' => 1,
1872
+ 'nango.fukushima.jp' => 1,
1873
+ 'nishiaizu.fukushima.jp' => 1,
1874
+ 'nishigo.fukushima.jp' => 1,
1875
+ 'okuma.fukushima.jp' => 1,
1876
+ 'omotego.fukushima.jp' => 1,
1877
+ 'ono.fukushima.jp' => 1,
1878
+ 'otama.fukushima.jp' => 1,
1879
+ 'samegawa.fukushima.jp' => 1,
1880
+ 'shimogo.fukushima.jp' => 1,
1881
+ 'shirakawa.fukushima.jp' => 1,
1882
+ 'showa.fukushima.jp' => 1,
1883
+ 'soma.fukushima.jp' => 1,
1884
+ 'sukagawa.fukushima.jp' => 1,
1885
+ 'taishin.fukushima.jp' => 1,
1886
+ 'tamakawa.fukushima.jp' => 1,
1887
+ 'tanagura.fukushima.jp' => 1,
1888
+ 'tenei.fukushima.jp' => 1,
1889
+ 'yabuki.fukushima.jp' => 1,
1890
+ 'yamato.fukushima.jp' => 1,
1891
+ 'yamatsuri.fukushima.jp' => 1,
1892
+ 'yanaizu.fukushima.jp' => 1,
1893
+ 'yugawa.fukushima.jp' => 1,
1894
+ 'anpachi.gifu.jp' => 1,
1895
+ 'ena.gifu.jp' => 1,
1896
+ 'gifu.gifu.jp' => 1,
1897
+ 'ginan.gifu.jp' => 1,
1898
+ 'godo.gifu.jp' => 1,
1899
+ 'gujo.gifu.jp' => 1,
1900
+ 'hashima.gifu.jp' => 1,
1901
+ 'hichiso.gifu.jp' => 1,
1902
+ 'hida.gifu.jp' => 1,
1903
+ 'higashishirakawa.gifu.jp' => 1,
1904
+ 'ibigawa.gifu.jp' => 1,
1905
+ 'ikeda.gifu.jp' => 1,
1906
+ 'kakamigahara.gifu.jp' => 1,
1907
+ 'kani.gifu.jp' => 1,
1908
+ 'kasahara.gifu.jp' => 1,
1909
+ 'kasamatsu.gifu.jp' => 1,
1910
+ 'kawaue.gifu.jp' => 1,
1911
+ 'kitagata.gifu.jp' => 1,
1912
+ 'mino.gifu.jp' => 1,
1913
+ 'minokamo.gifu.jp' => 1,
1914
+ 'mitake.gifu.jp' => 1,
1915
+ 'mizunami.gifu.jp' => 1,
1916
+ 'motosu.gifu.jp' => 1,
1917
+ 'nakatsugawa.gifu.jp' => 1,
1918
+ 'ogaki.gifu.jp' => 1,
1919
+ 'sakahogi.gifu.jp' => 1,
1920
+ 'seki.gifu.jp' => 1,
1921
+ 'sekigahara.gifu.jp' => 1,
1922
+ 'shirakawa.gifu.jp' => 1,
1923
+ 'tajimi.gifu.jp' => 1,
1924
+ 'takayama.gifu.jp' => 1,
1925
+ 'tarui.gifu.jp' => 1,
1926
+ 'toki.gifu.jp' => 1,
1927
+ 'tomika.gifu.jp' => 1,
1928
+ 'wanouchi.gifu.jp' => 1,
1929
+ 'yamagata.gifu.jp' => 1,
1930
+ 'yaotsu.gifu.jp' => 1,
1931
+ 'yoro.gifu.jp' => 1,
1932
+ 'annaka.gunma.jp' => 1,
1933
+ 'chiyoda.gunma.jp' => 1,
1934
+ 'fujioka.gunma.jp' => 1,
1935
+ 'higashiagatsuma.gunma.jp' => 1,
1936
+ 'isesaki.gunma.jp' => 1,
1937
+ 'itakura.gunma.jp' => 1,
1938
+ 'kanna.gunma.jp' => 1,
1939
+ 'kanra.gunma.jp' => 1,
1940
+ 'katashina.gunma.jp' => 1,
1941
+ 'kawaba.gunma.jp' => 1,
1942
+ 'kiryu.gunma.jp' => 1,
1943
+ 'kusatsu.gunma.jp' => 1,
1944
+ 'maebashi.gunma.jp' => 1,
1945
+ 'meiwa.gunma.jp' => 1,
1946
+ 'midori.gunma.jp' => 1,
1947
+ 'minakami.gunma.jp' => 1,
1948
+ 'naganohara.gunma.jp' => 1,
1949
+ 'nakanojo.gunma.jp' => 1,
1950
+ 'nanmoku.gunma.jp' => 1,
1951
+ 'numata.gunma.jp' => 1,
1952
+ 'oizumi.gunma.jp' => 1,
1953
+ 'ora.gunma.jp' => 1,
1954
+ 'ota.gunma.jp' => 1,
1955
+ 'shibukawa.gunma.jp' => 1,
1956
+ 'shimonita.gunma.jp' => 1,
1957
+ 'shinto.gunma.jp' => 1,
1958
+ 'showa.gunma.jp' => 1,
1959
+ 'takasaki.gunma.jp' => 1,
1960
+ 'takayama.gunma.jp' => 1,
1961
+ 'tamamura.gunma.jp' => 1,
1962
+ 'tatebayashi.gunma.jp' => 1,
1963
+ 'tomioka.gunma.jp' => 1,
1964
+ 'tsukiyono.gunma.jp' => 1,
1965
+ 'tsumagoi.gunma.jp' => 1,
1966
+ 'ueno.gunma.jp' => 1,
1967
+ 'yoshioka.gunma.jp' => 1,
1968
+ 'asaminami.hiroshima.jp' => 1,
1969
+ 'daiwa.hiroshima.jp' => 1,
1970
+ 'etajima.hiroshima.jp' => 1,
1971
+ 'fuchu.hiroshima.jp' => 1,
1972
+ 'fukuyama.hiroshima.jp' => 1,
1973
+ 'hatsukaichi.hiroshima.jp' => 1,
1974
+ 'higashihiroshima.hiroshima.jp' => 1,
1975
+ 'hongo.hiroshima.jp' => 1,
1976
+ 'jinsekikogen.hiroshima.jp' => 1,
1977
+ 'kaita.hiroshima.jp' => 1,
1978
+ 'kui.hiroshima.jp' => 1,
1979
+ 'kumano.hiroshima.jp' => 1,
1980
+ 'kure.hiroshima.jp' => 1,
1981
+ 'mihara.hiroshima.jp' => 1,
1982
+ 'miyoshi.hiroshima.jp' => 1,
1983
+ 'naka.hiroshima.jp' => 1,
1984
+ 'onomichi.hiroshima.jp' => 1,
1985
+ 'osakikamijima.hiroshima.jp' => 1,
1986
+ 'otake.hiroshima.jp' => 1,
1987
+ 'saka.hiroshima.jp' => 1,
1988
+ 'sera.hiroshima.jp' => 1,
1989
+ 'seranishi.hiroshima.jp' => 1,
1990
+ 'shinichi.hiroshima.jp' => 1,
1991
+ 'shobara.hiroshima.jp' => 1,
1992
+ 'takehara.hiroshima.jp' => 1,
1993
+ 'abashiri.hokkaido.jp' => 1,
1994
+ 'abira.hokkaido.jp' => 1,
1995
+ 'aibetsu.hokkaido.jp' => 1,
1996
+ 'akabira.hokkaido.jp' => 1,
1997
+ 'akkeshi.hokkaido.jp' => 1,
1998
+ 'asahikawa.hokkaido.jp' => 1,
1999
+ 'ashibetsu.hokkaido.jp' => 1,
2000
+ 'ashoro.hokkaido.jp' => 1,
2001
+ 'assabu.hokkaido.jp' => 1,
2002
+ 'atsuma.hokkaido.jp' => 1,
2003
+ 'bibai.hokkaido.jp' => 1,
2004
+ 'biei.hokkaido.jp' => 1,
2005
+ 'bifuka.hokkaido.jp' => 1,
2006
+ 'bihoro.hokkaido.jp' => 1,
2007
+ 'biratori.hokkaido.jp' => 1,
2008
+ 'chippubetsu.hokkaido.jp' => 1,
2009
+ 'chitose.hokkaido.jp' => 1,
2010
+ 'date.hokkaido.jp' => 1,
2011
+ 'ebetsu.hokkaido.jp' => 1,
2012
+ 'embetsu.hokkaido.jp' => 1,
2013
+ 'eniwa.hokkaido.jp' => 1,
2014
+ 'erimo.hokkaido.jp' => 1,
2015
+ 'esan.hokkaido.jp' => 1,
2016
+ 'esashi.hokkaido.jp' => 1,
2017
+ 'fukagawa.hokkaido.jp' => 1,
2018
+ 'fukushima.hokkaido.jp' => 1,
2019
+ 'furano.hokkaido.jp' => 1,
2020
+ 'furubira.hokkaido.jp' => 1,
2021
+ 'haboro.hokkaido.jp' => 1,
2022
+ 'hakodate.hokkaido.jp' => 1,
2023
+ 'hamatonbetsu.hokkaido.jp' => 1,
2024
+ 'hidaka.hokkaido.jp' => 1,
2025
+ 'higashikagura.hokkaido.jp' => 1,
2026
+ 'higashikawa.hokkaido.jp' => 1,
2027
+ 'hiroo.hokkaido.jp' => 1,
2028
+ 'hokuryu.hokkaido.jp' => 1,
2029
+ 'hokuto.hokkaido.jp' => 1,
2030
+ 'honbetsu.hokkaido.jp' => 1,
2031
+ 'horokanai.hokkaido.jp' => 1,
2032
+ 'horonobe.hokkaido.jp' => 1,
2033
+ 'ikeda.hokkaido.jp' => 1,
2034
+ 'imakane.hokkaido.jp' => 1,
2035
+ 'ishikari.hokkaido.jp' => 1,
2036
+ 'iwamizawa.hokkaido.jp' => 1,
2037
+ 'iwanai.hokkaido.jp' => 1,
2038
+ 'kamifurano.hokkaido.jp' => 1,
2039
+ 'kamikawa.hokkaido.jp' => 1,
2040
+ 'kamishihoro.hokkaido.jp' => 1,
2041
+ 'kamisunagawa.hokkaido.jp' => 1,
2042
+ 'kamoenai.hokkaido.jp' => 1,
2043
+ 'kayabe.hokkaido.jp' => 1,
2044
+ 'kembuchi.hokkaido.jp' => 1,
2045
+ 'kikonai.hokkaido.jp' => 1,
2046
+ 'kimobetsu.hokkaido.jp' => 1,
2047
+ 'kitahiroshima.hokkaido.jp' => 1,
2048
+ 'kitami.hokkaido.jp' => 1,
2049
+ 'kiyosato.hokkaido.jp' => 1,
2050
+ 'koshimizu.hokkaido.jp' => 1,
2051
+ 'kunneppu.hokkaido.jp' => 1,
2052
+ 'kuriyama.hokkaido.jp' => 1,
2053
+ 'kuromatsunai.hokkaido.jp' => 1,
2054
+ 'kushiro.hokkaido.jp' => 1,
2055
+ 'kutchan.hokkaido.jp' => 1,
2056
+ 'kyowa.hokkaido.jp' => 1,
2057
+ 'mashike.hokkaido.jp' => 1,
2058
+ 'matsumae.hokkaido.jp' => 1,
2059
+ 'mikasa.hokkaido.jp' => 1,
2060
+ 'minamifurano.hokkaido.jp' => 1,
2061
+ 'mombetsu.hokkaido.jp' => 1,
2062
+ 'moseushi.hokkaido.jp' => 1,
2063
+ 'mukawa.hokkaido.jp' => 1,
2064
+ 'muroran.hokkaido.jp' => 1,
2065
+ 'naie.hokkaido.jp' => 1,
2066
+ 'nakagawa.hokkaido.jp' => 1,
2067
+ 'nakasatsunai.hokkaido.jp' => 1,
2068
+ 'nakatombetsu.hokkaido.jp' => 1,
2069
+ 'nanae.hokkaido.jp' => 1,
2070
+ 'nanporo.hokkaido.jp' => 1,
2071
+ 'nayoro.hokkaido.jp' => 1,
2072
+ 'nemuro.hokkaido.jp' => 1,
2073
+ 'niikappu.hokkaido.jp' => 1,
2074
+ 'niki.hokkaido.jp' => 1,
2075
+ 'nishiokoppe.hokkaido.jp' => 1,
2076
+ 'noboribetsu.hokkaido.jp' => 1,
2077
+ 'numata.hokkaido.jp' => 1,
2078
+ 'obihiro.hokkaido.jp' => 1,
2079
+ 'obira.hokkaido.jp' => 1,
2080
+ 'oketo.hokkaido.jp' => 1,
2081
+ 'okoppe.hokkaido.jp' => 1,
2082
+ 'otaru.hokkaido.jp' => 1,
2083
+ 'otobe.hokkaido.jp' => 1,
2084
+ 'otofuke.hokkaido.jp' => 1,
2085
+ 'otoineppu.hokkaido.jp' => 1,
2086
+ 'oumu.hokkaido.jp' => 1,
2087
+ 'ozora.hokkaido.jp' => 1,
2088
+ 'pippu.hokkaido.jp' => 1,
2089
+ 'rankoshi.hokkaido.jp' => 1,
2090
+ 'rebun.hokkaido.jp' => 1,
2091
+ 'rikubetsu.hokkaido.jp' => 1,
2092
+ 'rishiri.hokkaido.jp' => 1,
2093
+ 'rishirifuji.hokkaido.jp' => 1,
2094
+ 'saroma.hokkaido.jp' => 1,
2095
+ 'sarufutsu.hokkaido.jp' => 1,
2096
+ 'shakotan.hokkaido.jp' => 1,
2097
+ 'shari.hokkaido.jp' => 1,
2098
+ 'shibecha.hokkaido.jp' => 1,
2099
+ 'shibetsu.hokkaido.jp' => 1,
2100
+ 'shikabe.hokkaido.jp' => 1,
2101
+ 'shikaoi.hokkaido.jp' => 1,
2102
+ 'shimamaki.hokkaido.jp' => 1,
2103
+ 'shimizu.hokkaido.jp' => 1,
2104
+ 'shimokawa.hokkaido.jp' => 1,
2105
+ 'shinshinotsu.hokkaido.jp' => 1,
2106
+ 'shintoku.hokkaido.jp' => 1,
2107
+ 'shiranuka.hokkaido.jp' => 1,
2108
+ 'shiraoi.hokkaido.jp' => 1,
2109
+ 'shiriuchi.hokkaido.jp' => 1,
2110
+ 'sobetsu.hokkaido.jp' => 1,
2111
+ 'sunagawa.hokkaido.jp' => 1,
2112
+ 'taiki.hokkaido.jp' => 1,
2113
+ 'takasu.hokkaido.jp' => 1,
2114
+ 'takikawa.hokkaido.jp' => 1,
2115
+ 'takinoue.hokkaido.jp' => 1,
2116
+ 'teshikaga.hokkaido.jp' => 1,
2117
+ 'tobetsu.hokkaido.jp' => 1,
2118
+ 'tohma.hokkaido.jp' => 1,
2119
+ 'tomakomai.hokkaido.jp' => 1,
2120
+ 'tomari.hokkaido.jp' => 1,
2121
+ 'toya.hokkaido.jp' => 1,
2122
+ 'toyako.hokkaido.jp' => 1,
2123
+ 'toyotomi.hokkaido.jp' => 1,
2124
+ 'toyoura.hokkaido.jp' => 1,
2125
+ 'tsubetsu.hokkaido.jp' => 1,
2126
+ 'tsukigata.hokkaido.jp' => 1,
2127
+ 'urakawa.hokkaido.jp' => 1,
2128
+ 'urausu.hokkaido.jp' => 1,
2129
+ 'uryu.hokkaido.jp' => 1,
2130
+ 'utashinai.hokkaido.jp' => 1,
2131
+ 'wakkanai.hokkaido.jp' => 1,
2132
+ 'wassamu.hokkaido.jp' => 1,
2133
+ 'yakumo.hokkaido.jp' => 1,
2134
+ 'yoichi.hokkaido.jp' => 1,
2135
+ 'aioi.hyogo.jp' => 1,
2136
+ 'akashi.hyogo.jp' => 1,
2137
+ 'ako.hyogo.jp' => 1,
2138
+ 'amagasaki.hyogo.jp' => 1,
2139
+ 'aogaki.hyogo.jp' => 1,
2140
+ 'asago.hyogo.jp' => 1,
2141
+ 'ashiya.hyogo.jp' => 1,
2142
+ 'awaji.hyogo.jp' => 1,
2143
+ 'fukusaki.hyogo.jp' => 1,
2144
+ 'goshiki.hyogo.jp' => 1,
2145
+ 'harima.hyogo.jp' => 1,
2146
+ 'himeji.hyogo.jp' => 1,
2147
+ 'ichikawa.hyogo.jp' => 1,
2148
+ 'inagawa.hyogo.jp' => 1,
2149
+ 'itami.hyogo.jp' => 1,
2150
+ 'kakogawa.hyogo.jp' => 1,
2151
+ 'kamigori.hyogo.jp' => 1,
2152
+ 'kamikawa.hyogo.jp' => 1,
2153
+ 'kasai.hyogo.jp' => 1,
2154
+ 'kasuga.hyogo.jp' => 1,
2155
+ 'kawanishi.hyogo.jp' => 1,
2156
+ 'miki.hyogo.jp' => 1,
2157
+ 'minamiawaji.hyogo.jp' => 1,
2158
+ 'nishinomiya.hyogo.jp' => 1,
2159
+ 'nishiwaki.hyogo.jp' => 1,
2160
+ 'ono.hyogo.jp' => 1,
2161
+ 'sanda.hyogo.jp' => 1,
2162
+ 'sannan.hyogo.jp' => 1,
2163
+ 'sasayama.hyogo.jp' => 1,
2164
+ 'sayo.hyogo.jp' => 1,
2165
+ 'shingu.hyogo.jp' => 1,
2166
+ 'shinonsen.hyogo.jp' => 1,
2167
+ 'shiso.hyogo.jp' => 1,
2168
+ 'sumoto.hyogo.jp' => 1,
2169
+ 'taishi.hyogo.jp' => 1,
2170
+ 'taka.hyogo.jp' => 1,
2171
+ 'takarazuka.hyogo.jp' => 1,
2172
+ 'takasago.hyogo.jp' => 1,
2173
+ 'takino.hyogo.jp' => 1,
2174
+ 'tamba.hyogo.jp' => 1,
2175
+ 'tatsuno.hyogo.jp' => 1,
2176
+ 'toyooka.hyogo.jp' => 1,
2177
+ 'yabu.hyogo.jp' => 1,
2178
+ 'yashiro.hyogo.jp' => 1,
2179
+ 'yoka.hyogo.jp' => 1,
2180
+ 'yokawa.hyogo.jp' => 1,
2181
+ 'ami.ibaraki.jp' => 1,
2182
+ 'asahi.ibaraki.jp' => 1,
2183
+ 'bando.ibaraki.jp' => 1,
2184
+ 'chikusei.ibaraki.jp' => 1,
2185
+ 'daigo.ibaraki.jp' => 1,
2186
+ 'fujishiro.ibaraki.jp' => 1,
2187
+ 'hitachi.ibaraki.jp' => 1,
2188
+ 'hitachinaka.ibaraki.jp' => 1,
2189
+ 'hitachiomiya.ibaraki.jp' => 1,
2190
+ 'hitachiota.ibaraki.jp' => 1,
2191
+ 'ibaraki.ibaraki.jp' => 1,
2192
+ 'ina.ibaraki.jp' => 1,
2193
+ 'inashiki.ibaraki.jp' => 1,
2194
+ 'itako.ibaraki.jp' => 1,
2195
+ 'iwama.ibaraki.jp' => 1,
2196
+ 'joso.ibaraki.jp' => 1,
2197
+ 'kamisu.ibaraki.jp' => 1,
2198
+ 'kasama.ibaraki.jp' => 1,
2199
+ 'kashima.ibaraki.jp' => 1,
2200
+ 'kasumigaura.ibaraki.jp' => 1,
2201
+ 'koga.ibaraki.jp' => 1,
2202
+ 'miho.ibaraki.jp' => 1,
2203
+ 'mito.ibaraki.jp' => 1,
2204
+ 'moriya.ibaraki.jp' => 1,
2205
+ 'naka.ibaraki.jp' => 1,
2206
+ 'namegata.ibaraki.jp' => 1,
2207
+ 'oarai.ibaraki.jp' => 1,
2208
+ 'ogawa.ibaraki.jp' => 1,
2209
+ 'omitama.ibaraki.jp' => 1,
2210
+ 'ryugasaki.ibaraki.jp' => 1,
2211
+ 'sakai.ibaraki.jp' => 1,
2212
+ 'sakuragawa.ibaraki.jp' => 1,
2213
+ 'shimodate.ibaraki.jp' => 1,
2214
+ 'shimotsuma.ibaraki.jp' => 1,
2215
+ 'shirosato.ibaraki.jp' => 1,
2216
+ 'sowa.ibaraki.jp' => 1,
2217
+ 'suifu.ibaraki.jp' => 1,
2218
+ 'takahagi.ibaraki.jp' => 1,
2219
+ 'tamatsukuri.ibaraki.jp' => 1,
2220
+ 'tokai.ibaraki.jp' => 1,
2221
+ 'tomobe.ibaraki.jp' => 1,
2222
+ 'tone.ibaraki.jp' => 1,
2223
+ 'toride.ibaraki.jp' => 1,
2224
+ 'tsuchiura.ibaraki.jp' => 1,
2225
+ 'tsukuba.ibaraki.jp' => 1,
2226
+ 'uchihara.ibaraki.jp' => 1,
2227
+ 'ushiku.ibaraki.jp' => 1,
2228
+ 'yachiyo.ibaraki.jp' => 1,
2229
+ 'yamagata.ibaraki.jp' => 1,
2230
+ 'yawara.ibaraki.jp' => 1,
2231
+ 'yuki.ibaraki.jp' => 1,
2232
+ 'anamizu.ishikawa.jp' => 1,
2233
+ 'hakui.ishikawa.jp' => 1,
2234
+ 'hakusan.ishikawa.jp' => 1,
2235
+ 'kaga.ishikawa.jp' => 1,
2236
+ 'kahoku.ishikawa.jp' => 1,
2237
+ 'kanazawa.ishikawa.jp' => 1,
2238
+ 'kawakita.ishikawa.jp' => 1,
2239
+ 'komatsu.ishikawa.jp' => 1,
2240
+ 'nakanoto.ishikawa.jp' => 1,
2241
+ 'nanao.ishikawa.jp' => 1,
2242
+ 'nomi.ishikawa.jp' => 1,
2243
+ 'nonoichi.ishikawa.jp' => 1,
2244
+ 'noto.ishikawa.jp' => 1,
2245
+ 'shika.ishikawa.jp' => 1,
2246
+ 'suzu.ishikawa.jp' => 1,
2247
+ 'tsubata.ishikawa.jp' => 1,
2248
+ 'tsurugi.ishikawa.jp' => 1,
2249
+ 'uchinada.ishikawa.jp' => 1,
2250
+ 'wajima.ishikawa.jp' => 1,
2251
+ 'fudai.iwate.jp' => 1,
2252
+ 'fujisawa.iwate.jp' => 1,
2253
+ 'hanamaki.iwate.jp' => 1,
2254
+ 'hiraizumi.iwate.jp' => 1,
2255
+ 'hirono.iwate.jp' => 1,
2256
+ 'ichinohe.iwate.jp' => 1,
2257
+ 'ichinoseki.iwate.jp' => 1,
2258
+ 'iwaizumi.iwate.jp' => 1,
2259
+ 'iwate.iwate.jp' => 1,
2260
+ 'joboji.iwate.jp' => 1,
2261
+ 'kamaishi.iwate.jp' => 1,
2262
+ 'kanegasaki.iwate.jp' => 1,
2263
+ 'karumai.iwate.jp' => 1,
2264
+ 'kawai.iwate.jp' => 1,
2265
+ 'kitakami.iwate.jp' => 1,
2266
+ 'kuji.iwate.jp' => 1,
2267
+ 'kunohe.iwate.jp' => 1,
2268
+ 'kuzumaki.iwate.jp' => 1,
2269
+ 'miyako.iwate.jp' => 1,
2270
+ 'mizusawa.iwate.jp' => 1,
2271
+ 'morioka.iwate.jp' => 1,
2272
+ 'ninohe.iwate.jp' => 1,
2273
+ 'noda.iwate.jp' => 1,
2274
+ 'ofunato.iwate.jp' => 1,
2275
+ 'oshu.iwate.jp' => 1,
2276
+ 'otsuchi.iwate.jp' => 1,
2277
+ 'rikuzentakata.iwate.jp' => 1,
2278
+ 'shiwa.iwate.jp' => 1,
2279
+ 'shizukuishi.iwate.jp' => 1,
2280
+ 'sumita.iwate.jp' => 1,
2281
+ 'tanohata.iwate.jp' => 1,
2282
+ 'tono.iwate.jp' => 1,
2283
+ 'yahaba.iwate.jp' => 1,
2284
+ 'yamada.iwate.jp' => 1,
2285
+ 'ayagawa.kagawa.jp' => 1,
2286
+ 'higashikagawa.kagawa.jp' => 1,
2287
+ 'kanonji.kagawa.jp' => 1,
2288
+ 'kotohira.kagawa.jp' => 1,
2289
+ 'manno.kagawa.jp' => 1,
2290
+ 'marugame.kagawa.jp' => 1,
2291
+ 'mitoyo.kagawa.jp' => 1,
2292
+ 'naoshima.kagawa.jp' => 1,
2293
+ 'sanuki.kagawa.jp' => 1,
2294
+ 'tadotsu.kagawa.jp' => 1,
2295
+ 'takamatsu.kagawa.jp' => 1,
2296
+ 'tonosho.kagawa.jp' => 1,
2297
+ 'uchinomi.kagawa.jp' => 1,
2298
+ 'utazu.kagawa.jp' => 1,
2299
+ 'zentsuji.kagawa.jp' => 1,
2300
+ 'akune.kagoshima.jp' => 1,
2301
+ 'amami.kagoshima.jp' => 1,
2302
+ 'hioki.kagoshima.jp' => 1,
2303
+ 'isa.kagoshima.jp' => 1,
2304
+ 'isen.kagoshima.jp' => 1,
2305
+ 'izumi.kagoshima.jp' => 1,
2306
+ 'kagoshima.kagoshima.jp' => 1,
2307
+ 'kanoya.kagoshima.jp' => 1,
2308
+ 'kawanabe.kagoshima.jp' => 1,
2309
+ 'kinko.kagoshima.jp' => 1,
2310
+ 'kouyama.kagoshima.jp' => 1,
2311
+ 'makurazaki.kagoshima.jp' => 1,
2312
+ 'matsumoto.kagoshima.jp' => 1,
2313
+ 'minamitane.kagoshima.jp' => 1,
2314
+ 'nakatane.kagoshima.jp' => 1,
2315
+ 'nishinoomote.kagoshima.jp' => 1,
2316
+ 'satsumasendai.kagoshima.jp' => 1,
2317
+ 'soo.kagoshima.jp' => 1,
2318
+ 'tarumizu.kagoshima.jp' => 1,
2319
+ 'yusui.kagoshima.jp' => 1,
2320
+ 'aikawa.kanagawa.jp' => 1,
2321
+ 'atsugi.kanagawa.jp' => 1,
2322
+ 'ayase.kanagawa.jp' => 1,
2323
+ 'chigasaki.kanagawa.jp' => 1,
2324
+ 'ebina.kanagawa.jp' => 1,
2325
+ 'fujisawa.kanagawa.jp' => 1,
2326
+ 'hadano.kanagawa.jp' => 1,
2327
+ 'hakone.kanagawa.jp' => 1,
2328
+ 'hiratsuka.kanagawa.jp' => 1,
2329
+ 'isehara.kanagawa.jp' => 1,
2330
+ 'kaisei.kanagawa.jp' => 1,
2331
+ 'kamakura.kanagawa.jp' => 1,
2332
+ 'kiyokawa.kanagawa.jp' => 1,
2333
+ 'matsuda.kanagawa.jp' => 1,
2334
+ 'minamiashigara.kanagawa.jp' => 1,
2335
+ 'miura.kanagawa.jp' => 1,
2336
+ 'nakai.kanagawa.jp' => 1,
2337
+ 'ninomiya.kanagawa.jp' => 1,
2338
+ 'odawara.kanagawa.jp' => 1,
2339
+ 'oi.kanagawa.jp' => 1,
2340
+ 'oiso.kanagawa.jp' => 1,
2341
+ 'sagamihara.kanagawa.jp' => 1,
2342
+ 'samukawa.kanagawa.jp' => 1,
2343
+ 'tsukui.kanagawa.jp' => 1,
2344
+ 'yamakita.kanagawa.jp' => 1,
2345
+ 'yamato.kanagawa.jp' => 1,
2346
+ 'yokosuka.kanagawa.jp' => 1,
2347
+ 'yugawara.kanagawa.jp' => 1,
2348
+ 'zama.kanagawa.jp' => 1,
2349
+ 'zushi.kanagawa.jp' => 1,
2350
+ 'aki.kochi.jp' => 1,
2351
+ 'geisei.kochi.jp' => 1,
2352
+ 'hidaka.kochi.jp' => 1,
2353
+ 'higashitsuno.kochi.jp' => 1,
2354
+ 'ino.kochi.jp' => 1,
2355
+ 'kagami.kochi.jp' => 1,
2356
+ 'kami.kochi.jp' => 1,
2357
+ 'kitagawa.kochi.jp' => 1,
2358
+ 'kochi.kochi.jp' => 1,
2359
+ 'mihara.kochi.jp' => 1,
2360
+ 'motoyama.kochi.jp' => 1,
2361
+ 'muroto.kochi.jp' => 1,
2362
+ 'nahari.kochi.jp' => 1,
2363
+ 'nakamura.kochi.jp' => 1,
2364
+ 'nankoku.kochi.jp' => 1,
2365
+ 'nishitosa.kochi.jp' => 1,
2366
+ 'niyodogawa.kochi.jp' => 1,
2367
+ 'ochi.kochi.jp' => 1,
2368
+ 'okawa.kochi.jp' => 1,
2369
+ 'otoyo.kochi.jp' => 1,
2370
+ 'otsuki.kochi.jp' => 1,
2371
+ 'sakawa.kochi.jp' => 1,
2372
+ 'sukumo.kochi.jp' => 1,
2373
+ 'susaki.kochi.jp' => 1,
2374
+ 'tosa.kochi.jp' => 1,
2375
+ 'tosashimizu.kochi.jp' => 1,
2376
+ 'toyo.kochi.jp' => 1,
2377
+ 'tsuno.kochi.jp' => 1,
2378
+ 'umaji.kochi.jp' => 1,
2379
+ 'yasuda.kochi.jp' => 1,
2380
+ 'yusuhara.kochi.jp' => 1,
2381
+ 'amakusa.kumamoto.jp' => 1,
2382
+ 'arao.kumamoto.jp' => 1,
2383
+ 'aso.kumamoto.jp' => 1,
2384
+ 'choyo.kumamoto.jp' => 1,
2385
+ 'gyokuto.kumamoto.jp' => 1,
2386
+ 'kamiamakusa.kumamoto.jp' => 1,
2387
+ 'kikuchi.kumamoto.jp' => 1,
2388
+ 'kumamoto.kumamoto.jp' => 1,
2389
+ 'mashiki.kumamoto.jp' => 1,
2390
+ 'mifune.kumamoto.jp' => 1,
2391
+ 'minamata.kumamoto.jp' => 1,
2392
+ 'minamioguni.kumamoto.jp' => 1,
2393
+ 'nagasu.kumamoto.jp' => 1,
2394
+ 'nishihara.kumamoto.jp' => 1,
2395
+ 'oguni.kumamoto.jp' => 1,
2396
+ 'ozu.kumamoto.jp' => 1,
2397
+ 'sumoto.kumamoto.jp' => 1,
2398
+ 'takamori.kumamoto.jp' => 1,
2399
+ 'uki.kumamoto.jp' => 1,
2400
+ 'uto.kumamoto.jp' => 1,
2401
+ 'yamaga.kumamoto.jp' => 1,
2402
+ 'yamato.kumamoto.jp' => 1,
2403
+ 'yatsushiro.kumamoto.jp' => 1,
2404
+ 'ayabe.kyoto.jp' => 1,
2405
+ 'fukuchiyama.kyoto.jp' => 1,
2406
+ 'higashiyama.kyoto.jp' => 1,
2407
+ 'ide.kyoto.jp' => 1,
2408
+ 'ine.kyoto.jp' => 1,
2409
+ 'joyo.kyoto.jp' => 1,
2410
+ 'kameoka.kyoto.jp' => 1,
2411
+ 'kamo.kyoto.jp' => 1,
2412
+ 'kita.kyoto.jp' => 1,
2413
+ 'kizu.kyoto.jp' => 1,
2414
+ 'kumiyama.kyoto.jp' => 1,
2415
+ 'kyotamba.kyoto.jp' => 1,
2416
+ 'kyotanabe.kyoto.jp' => 1,
2417
+ 'kyotango.kyoto.jp' => 1,
2418
+ 'maizuru.kyoto.jp' => 1,
2419
+ 'minami.kyoto.jp' => 1,
2420
+ 'minamiyamashiro.kyoto.jp' => 1,
2421
+ 'miyazu.kyoto.jp' => 1,
2422
+ 'muko.kyoto.jp' => 1,
2423
+ 'nagaokakyo.kyoto.jp' => 1,
2424
+ 'nakagyo.kyoto.jp' => 1,
2425
+ 'nantan.kyoto.jp' => 1,
2426
+ 'oyamazaki.kyoto.jp' => 1,
2427
+ 'sakyo.kyoto.jp' => 1,
2428
+ 'seika.kyoto.jp' => 1,
2429
+ 'tanabe.kyoto.jp' => 1,
2430
+ 'uji.kyoto.jp' => 1,
2431
+ 'ujitawara.kyoto.jp' => 1,
2432
+ 'wazuka.kyoto.jp' => 1,
2433
+ 'yamashina.kyoto.jp' => 1,
2434
+ 'yawata.kyoto.jp' => 1,
2435
+ 'asahi.mie.jp' => 1,
2436
+ 'inabe.mie.jp' => 1,
2437
+ 'ise.mie.jp' => 1,
2438
+ 'kameyama.mie.jp' => 1,
2439
+ 'kawagoe.mie.jp' => 1,
2440
+ 'kiho.mie.jp' => 1,
2441
+ 'kisosaki.mie.jp' => 1,
2442
+ 'kiwa.mie.jp' => 1,
2443
+ 'komono.mie.jp' => 1,
2444
+ 'kumano.mie.jp' => 1,
2445
+ 'kuwana.mie.jp' => 1,
2446
+ 'matsusaka.mie.jp' => 1,
2447
+ 'meiwa.mie.jp' => 1,
2448
+ 'mihama.mie.jp' => 1,
2449
+ 'minamiise.mie.jp' => 1,
2450
+ 'misugi.mie.jp' => 1,
2451
+ 'miyama.mie.jp' => 1,
2452
+ 'nabari.mie.jp' => 1,
2453
+ 'shima.mie.jp' => 1,
2454
+ 'suzuka.mie.jp' => 1,
2455
+ 'tado.mie.jp' => 1,
2456
+ 'taiki.mie.jp' => 1,
2457
+ 'taki.mie.jp' => 1,
2458
+ 'tamaki.mie.jp' => 1,
2459
+ 'toba.mie.jp' => 1,
2460
+ 'tsu.mie.jp' => 1,
2461
+ 'udono.mie.jp' => 1,
2462
+ 'ureshino.mie.jp' => 1,
2463
+ 'watarai.mie.jp' => 1,
2464
+ 'yokkaichi.mie.jp' => 1,
2465
+ 'furukawa.miyagi.jp' => 1,
2466
+ 'higashimatsushima.miyagi.jp' => 1,
2467
+ 'ishinomaki.miyagi.jp' => 1,
2468
+ 'iwanuma.miyagi.jp' => 1,
2469
+ 'kakuda.miyagi.jp' => 1,
2470
+ 'kami.miyagi.jp' => 1,
2471
+ 'kawasaki.miyagi.jp' => 1,
2472
+ 'marumori.miyagi.jp' => 1,
2473
+ 'matsushima.miyagi.jp' => 1,
2474
+ 'minamisanriku.miyagi.jp' => 1,
2475
+ 'misato.miyagi.jp' => 1,
2476
+ 'murata.miyagi.jp' => 1,
2477
+ 'natori.miyagi.jp' => 1,
2478
+ 'ogawara.miyagi.jp' => 1,
2479
+ 'ohira.miyagi.jp' => 1,
2480
+ 'onagawa.miyagi.jp' => 1,
2481
+ 'osaki.miyagi.jp' => 1,
2482
+ 'rifu.miyagi.jp' => 1,
2483
+ 'semine.miyagi.jp' => 1,
2484
+ 'shibata.miyagi.jp' => 1,
2485
+ 'shichikashuku.miyagi.jp' => 1,
2486
+ 'shikama.miyagi.jp' => 1,
2487
+ 'shiogama.miyagi.jp' => 1,
2488
+ 'shiroishi.miyagi.jp' => 1,
2489
+ 'tagajo.miyagi.jp' => 1,
2490
+ 'taiwa.miyagi.jp' => 1,
2491
+ 'tome.miyagi.jp' => 1,
2492
+ 'tomiya.miyagi.jp' => 1,
2493
+ 'wakuya.miyagi.jp' => 1,
2494
+ 'watari.miyagi.jp' => 1,
2495
+ 'yamamoto.miyagi.jp' => 1,
2496
+ 'zao.miyagi.jp' => 1,
2497
+ 'aya.miyazaki.jp' => 1,
2498
+ 'ebino.miyazaki.jp' => 1,
2499
+ 'gokase.miyazaki.jp' => 1,
2500
+ 'hyuga.miyazaki.jp' => 1,
2501
+ 'kadogawa.miyazaki.jp' => 1,
2502
+ 'kawaminami.miyazaki.jp' => 1,
2503
+ 'kijo.miyazaki.jp' => 1,
2504
+ 'kitagawa.miyazaki.jp' => 1,
2505
+ 'kitakata.miyazaki.jp' => 1,
2506
+ 'kitaura.miyazaki.jp' => 1,
2507
+ 'kobayashi.miyazaki.jp' => 1,
2508
+ 'kunitomi.miyazaki.jp' => 1,
2509
+ 'kushima.miyazaki.jp' => 1,
2510
+ 'mimata.miyazaki.jp' => 1,
2511
+ 'miyakonojo.miyazaki.jp' => 1,
2512
+ 'miyazaki.miyazaki.jp' => 1,
2513
+ 'morotsuka.miyazaki.jp' => 1,
2514
+ 'nichinan.miyazaki.jp' => 1,
2515
+ 'nishimera.miyazaki.jp' => 1,
2516
+ 'nobeoka.miyazaki.jp' => 1,
2517
+ 'saito.miyazaki.jp' => 1,
2518
+ 'shiiba.miyazaki.jp' => 1,
2519
+ 'shintomi.miyazaki.jp' => 1,
2520
+ 'takaharu.miyazaki.jp' => 1,
2521
+ 'takanabe.miyazaki.jp' => 1,
2522
+ 'takazaki.miyazaki.jp' => 1,
2523
+ 'tsuno.miyazaki.jp' => 1,
2524
+ 'achi.nagano.jp' => 1,
2525
+ 'agematsu.nagano.jp' => 1,
2526
+ 'anan.nagano.jp' => 1,
2527
+ 'aoki.nagano.jp' => 1,
2528
+ 'asahi.nagano.jp' => 1,
2529
+ 'azumino.nagano.jp' => 1,
2530
+ 'chikuhoku.nagano.jp' => 1,
2531
+ 'chikuma.nagano.jp' => 1,
2532
+ 'chino.nagano.jp' => 1,
2533
+ 'fujimi.nagano.jp' => 1,
2534
+ 'hakuba.nagano.jp' => 1,
2535
+ 'hara.nagano.jp' => 1,
2536
+ 'hiraya.nagano.jp' => 1,
2537
+ 'iida.nagano.jp' => 1,
2538
+ 'iijima.nagano.jp' => 1,
2539
+ 'iiyama.nagano.jp' => 1,
2540
+ 'iizuna.nagano.jp' => 1,
2541
+ 'ikeda.nagano.jp' => 1,
2542
+ 'ikusaka.nagano.jp' => 1,
2543
+ 'ina.nagano.jp' => 1,
2544
+ 'karuizawa.nagano.jp' => 1,
2545
+ 'kawakami.nagano.jp' => 1,
2546
+ 'kiso.nagano.jp' => 1,
2547
+ 'kisofukushima.nagano.jp' => 1,
2548
+ 'kitaaiki.nagano.jp' => 1,
2549
+ 'komagane.nagano.jp' => 1,
2550
+ 'komoro.nagano.jp' => 1,
2551
+ 'matsukawa.nagano.jp' => 1,
2552
+ 'matsumoto.nagano.jp' => 1,
2553
+ 'miasa.nagano.jp' => 1,
2554
+ 'minamiaiki.nagano.jp' => 1,
2555
+ 'minamimaki.nagano.jp' => 1,
2556
+ 'minamiminowa.nagano.jp' => 1,
2557
+ 'minowa.nagano.jp' => 1,
2558
+ 'miyada.nagano.jp' => 1,
2559
+ 'miyota.nagano.jp' => 1,
2560
+ 'mochizuki.nagano.jp' => 1,
2561
+ 'nagano.nagano.jp' => 1,
2562
+ 'nagawa.nagano.jp' => 1,
2563
+ 'nagiso.nagano.jp' => 1,
2564
+ 'nakagawa.nagano.jp' => 1,
2565
+ 'nakano.nagano.jp' => 1,
2566
+ 'nozawaonsen.nagano.jp' => 1,
2567
+ 'obuse.nagano.jp' => 1,
2568
+ 'ogawa.nagano.jp' => 1,
2569
+ 'okaya.nagano.jp' => 1,
2570
+ 'omachi.nagano.jp' => 1,
2571
+ 'omi.nagano.jp' => 1,
2572
+ 'ookuwa.nagano.jp' => 1,
2573
+ 'ooshika.nagano.jp' => 1,
2574
+ 'otaki.nagano.jp' => 1,
2575
+ 'otari.nagano.jp' => 1,
2576
+ 'sakae.nagano.jp' => 1,
2577
+ 'sakaki.nagano.jp' => 1,
2578
+ 'saku.nagano.jp' => 1,
2579
+ 'sakuho.nagano.jp' => 1,
2580
+ 'shimosuwa.nagano.jp' => 1,
2581
+ 'shinanomachi.nagano.jp' => 1,
2582
+ 'shiojiri.nagano.jp' => 1,
2583
+ 'suwa.nagano.jp' => 1,
2584
+ 'suzaka.nagano.jp' => 1,
2585
+ 'takagi.nagano.jp' => 1,
2586
+ 'takamori.nagano.jp' => 1,
2587
+ 'takayama.nagano.jp' => 1,
2588
+ 'tateshina.nagano.jp' => 1,
2589
+ 'tatsuno.nagano.jp' => 1,
2590
+ 'togakushi.nagano.jp' => 1,
2591
+ 'togura.nagano.jp' => 1,
2592
+ 'tomi.nagano.jp' => 1,
2593
+ 'ueda.nagano.jp' => 1,
2594
+ 'wada.nagano.jp' => 1,
2595
+ 'yamagata.nagano.jp' => 1,
2596
+ 'yamanouchi.nagano.jp' => 1,
2597
+ 'yasaka.nagano.jp' => 1,
2598
+ 'yasuoka.nagano.jp' => 1,
2599
+ 'chijiwa.nagasaki.jp' => 1,
2600
+ 'futsu.nagasaki.jp' => 1,
2601
+ 'goto.nagasaki.jp' => 1,
2602
+ 'hasami.nagasaki.jp' => 1,
2603
+ 'hirado.nagasaki.jp' => 1,
2604
+ 'iki.nagasaki.jp' => 1,
2605
+ 'isahaya.nagasaki.jp' => 1,
2606
+ 'kawatana.nagasaki.jp' => 1,
2607
+ 'kuchinotsu.nagasaki.jp' => 1,
2608
+ 'matsuura.nagasaki.jp' => 1,
2609
+ 'nagasaki.nagasaki.jp' => 1,
2610
+ 'obama.nagasaki.jp' => 1,
2611
+ 'omura.nagasaki.jp' => 1,
2612
+ 'oseto.nagasaki.jp' => 1,
2613
+ 'saikai.nagasaki.jp' => 1,
2614
+ 'sasebo.nagasaki.jp' => 1,
2615
+ 'seihi.nagasaki.jp' => 1,
2616
+ 'shimabara.nagasaki.jp' => 1,
2617
+ 'shinkamigoto.nagasaki.jp' => 1,
2618
+ 'togitsu.nagasaki.jp' => 1,
2619
+ 'tsushima.nagasaki.jp' => 1,
2620
+ 'unzen.nagasaki.jp' => 1,
2621
+ 'ando.nara.jp' => 1,
2622
+ 'gose.nara.jp' => 1,
2623
+ 'heguri.nara.jp' => 1,
2624
+ 'higashiyoshino.nara.jp' => 1,
2625
+ 'ikaruga.nara.jp' => 1,
2626
+ 'ikoma.nara.jp' => 1,
2627
+ 'kamikitayama.nara.jp' => 1,
2628
+ 'kanmaki.nara.jp' => 1,
2629
+ 'kashiba.nara.jp' => 1,
2630
+ 'kashihara.nara.jp' => 1,
2631
+ 'katsuragi.nara.jp' => 1,
2632
+ 'kawai.nara.jp' => 1,
2633
+ 'kawakami.nara.jp' => 1,
2634
+ 'kawanishi.nara.jp' => 1,
2635
+ 'koryo.nara.jp' => 1,
2636
+ 'kurotaki.nara.jp' => 1,
2637
+ 'mitsue.nara.jp' => 1,
2638
+ 'miyake.nara.jp' => 1,
2639
+ 'nara.nara.jp' => 1,
2640
+ 'nosegawa.nara.jp' => 1,
2641
+ 'oji.nara.jp' => 1,
2642
+ 'ouda.nara.jp' => 1,
2643
+ 'oyodo.nara.jp' => 1,
2644
+ 'sakurai.nara.jp' => 1,
2645
+ 'sango.nara.jp' => 1,
2646
+ 'shimoichi.nara.jp' => 1,
2647
+ 'shimokitayama.nara.jp' => 1,
2648
+ 'shinjo.nara.jp' => 1,
2649
+ 'soni.nara.jp' => 1,
2650
+ 'takatori.nara.jp' => 1,
2651
+ 'tawaramoto.nara.jp' => 1,
2652
+ 'tenkawa.nara.jp' => 1,
2653
+ 'tenri.nara.jp' => 1,
2654
+ 'uda.nara.jp' => 1,
2655
+ 'yamatokoriyama.nara.jp' => 1,
2656
+ 'yamatotakada.nara.jp' => 1,
2657
+ 'yamazoe.nara.jp' => 1,
2658
+ 'yoshino.nara.jp' => 1,
2659
+ 'aga.niigata.jp' => 1,
2660
+ 'agano.niigata.jp' => 1,
2661
+ 'gosen.niigata.jp' => 1,
2662
+ 'itoigawa.niigata.jp' => 1,
2663
+ 'izumozaki.niigata.jp' => 1,
2664
+ 'joetsu.niigata.jp' => 1,
2665
+ 'kamo.niigata.jp' => 1,
2666
+ 'kariwa.niigata.jp' => 1,
2667
+ 'kashiwazaki.niigata.jp' => 1,
2668
+ 'minamiuonuma.niigata.jp' => 1,
2669
+ 'mitsuke.niigata.jp' => 1,
2670
+ 'muika.niigata.jp' => 1,
2671
+ 'murakami.niigata.jp' => 1,
2672
+ 'myoko.niigata.jp' => 1,
2673
+ 'nagaoka.niigata.jp' => 1,
2674
+ 'niigata.niigata.jp' => 1,
2675
+ 'ojiya.niigata.jp' => 1,
2676
+ 'omi.niigata.jp' => 1,
2677
+ 'sado.niigata.jp' => 1,
2678
+ 'sanjo.niigata.jp' => 1,
2679
+ 'seiro.niigata.jp' => 1,
2680
+ 'seirou.niigata.jp' => 1,
2681
+ 'sekikawa.niigata.jp' => 1,
2682
+ 'shibata.niigata.jp' => 1,
2683
+ 'tagami.niigata.jp' => 1,
2684
+ 'tainai.niigata.jp' => 1,
2685
+ 'tochio.niigata.jp' => 1,
2686
+ 'tokamachi.niigata.jp' => 1,
2687
+ 'tsubame.niigata.jp' => 1,
2688
+ 'tsunan.niigata.jp' => 1,
2689
+ 'uonuma.niigata.jp' => 1,
2690
+ 'yahiko.niigata.jp' => 1,
2691
+ 'yoita.niigata.jp' => 1,
2692
+ 'yuzawa.niigata.jp' => 1,
2693
+ 'beppu.oita.jp' => 1,
2694
+ 'bungoono.oita.jp' => 1,
2695
+ 'bungotakada.oita.jp' => 1,
2696
+ 'hasama.oita.jp' => 1,
2697
+ 'hiji.oita.jp' => 1,
2698
+ 'himeshima.oita.jp' => 1,
2699
+ 'hita.oita.jp' => 1,
2700
+ 'kamitsue.oita.jp' => 1,
2701
+ 'kokonoe.oita.jp' => 1,
2702
+ 'kuju.oita.jp' => 1,
2703
+ 'kunisaki.oita.jp' => 1,
2704
+ 'kusu.oita.jp' => 1,
2705
+ 'oita.oita.jp' => 1,
2706
+ 'saiki.oita.jp' => 1,
2707
+ 'taketa.oita.jp' => 1,
2708
+ 'tsukumi.oita.jp' => 1,
2709
+ 'usa.oita.jp' => 1,
2710
+ 'usuki.oita.jp' => 1,
2711
+ 'yufu.oita.jp' => 1,
2712
+ 'akaiwa.okayama.jp' => 1,
2713
+ 'asakuchi.okayama.jp' => 1,
2714
+ 'bizen.okayama.jp' => 1,
2715
+ 'hayashima.okayama.jp' => 1,
2716
+ 'ibara.okayama.jp' => 1,
2717
+ 'kagamino.okayama.jp' => 1,
2718
+ 'kasaoka.okayama.jp' => 1,
2719
+ 'kibichuo.okayama.jp' => 1,
2720
+ 'kumenan.okayama.jp' => 1,
2721
+ 'kurashiki.okayama.jp' => 1,
2722
+ 'maniwa.okayama.jp' => 1,
2723
+ 'misaki.okayama.jp' => 1,
2724
+ 'nagi.okayama.jp' => 1,
2725
+ 'niimi.okayama.jp' => 1,
2726
+ 'nishiawakura.okayama.jp' => 1,
2727
+ 'okayama.okayama.jp' => 1,
2728
+ 'satosho.okayama.jp' => 1,
2729
+ 'setouchi.okayama.jp' => 1,
2730
+ 'shinjo.okayama.jp' => 1,
2731
+ 'shoo.okayama.jp' => 1,
2732
+ 'soja.okayama.jp' => 1,
2733
+ 'takahashi.okayama.jp' => 1,
2734
+ 'tamano.okayama.jp' => 1,
2735
+ 'tsuyama.okayama.jp' => 1,
2736
+ 'wake.okayama.jp' => 1,
2737
+ 'yakage.okayama.jp' => 1,
2738
+ 'aguni.okinawa.jp' => 1,
2739
+ 'ginowan.okinawa.jp' => 1,
2740
+ 'ginoza.okinawa.jp' => 1,
2741
+ 'gushikami.okinawa.jp' => 1,
2742
+ 'haebaru.okinawa.jp' => 1,
2743
+ 'higashi.okinawa.jp' => 1,
2744
+ 'hirara.okinawa.jp' => 1,
2745
+ 'iheya.okinawa.jp' => 1,
2746
+ 'ishigaki.okinawa.jp' => 1,
2747
+ 'ishikawa.okinawa.jp' => 1,
2748
+ 'itoman.okinawa.jp' => 1,
2749
+ 'izena.okinawa.jp' => 1,
2750
+ 'kadena.okinawa.jp' => 1,
2751
+ 'kin.okinawa.jp' => 1,
2752
+ 'kitadaito.okinawa.jp' => 1,
2753
+ 'kitanakagusuku.okinawa.jp' => 1,
2754
+ 'kumejima.okinawa.jp' => 1,
2755
+ 'kunigami.okinawa.jp' => 1,
2756
+ 'minamidaito.okinawa.jp' => 1,
2757
+ 'motobu.okinawa.jp' => 1,
2758
+ 'nago.okinawa.jp' => 1,
2759
+ 'naha.okinawa.jp' => 1,
2760
+ 'nakagusuku.okinawa.jp' => 1,
2761
+ 'nakijin.okinawa.jp' => 1,
2762
+ 'nanjo.okinawa.jp' => 1,
2763
+ 'nishihara.okinawa.jp' => 1,
2764
+ 'ogimi.okinawa.jp' => 1,
2765
+ 'okinawa.okinawa.jp' => 1,
2766
+ 'onna.okinawa.jp' => 1,
2767
+ 'shimoji.okinawa.jp' => 1,
2768
+ 'taketomi.okinawa.jp' => 1,
2769
+ 'tarama.okinawa.jp' => 1,
2770
+ 'tokashiki.okinawa.jp' => 1,
2771
+ 'tomigusuku.okinawa.jp' => 1,
2772
+ 'tonaki.okinawa.jp' => 1,
2773
+ 'urasoe.okinawa.jp' => 1,
2774
+ 'uruma.okinawa.jp' => 1,
2775
+ 'yaese.okinawa.jp' => 1,
2776
+ 'yomitan.okinawa.jp' => 1,
2777
+ 'yonabaru.okinawa.jp' => 1,
2778
+ 'yonaguni.okinawa.jp' => 1,
2779
+ 'zamami.okinawa.jp' => 1,
2780
+ 'abeno.osaka.jp' => 1,
2781
+ 'chihayaakasaka.osaka.jp' => 1,
2782
+ 'chuo.osaka.jp' => 1,
2783
+ 'daito.osaka.jp' => 1,
2784
+ 'fujiidera.osaka.jp' => 1,
2785
+ 'habikino.osaka.jp' => 1,
2786
+ 'hannan.osaka.jp' => 1,
2787
+ 'higashiosaka.osaka.jp' => 1,
2788
+ 'higashisumiyoshi.osaka.jp' => 1,
2789
+ 'higashiyodogawa.osaka.jp' => 1,
2790
+ 'hirakata.osaka.jp' => 1,
2791
+ 'ibaraki.osaka.jp' => 1,
2792
+ 'ikeda.osaka.jp' => 1,
2793
+ 'izumi.osaka.jp' => 1,
2794
+ 'izumiotsu.osaka.jp' => 1,
2795
+ 'izumisano.osaka.jp' => 1,
2796
+ 'kadoma.osaka.jp' => 1,
2797
+ 'kaizuka.osaka.jp' => 1,
2798
+ 'kanan.osaka.jp' => 1,
2799
+ 'kashiwara.osaka.jp' => 1,
2800
+ 'katano.osaka.jp' => 1,
2801
+ 'kawachinagano.osaka.jp' => 1,
2802
+ 'kishiwada.osaka.jp' => 1,
2803
+ 'kita.osaka.jp' => 1,
2804
+ 'kumatori.osaka.jp' => 1,
2805
+ 'matsubara.osaka.jp' => 1,
2806
+ 'minato.osaka.jp' => 1,
2807
+ 'minoh.osaka.jp' => 1,
2808
+ 'misaki.osaka.jp' => 1,
2809
+ 'moriguchi.osaka.jp' => 1,
2810
+ 'neyagawa.osaka.jp' => 1,
2811
+ 'nishi.osaka.jp' => 1,
2812
+ 'nose.osaka.jp' => 1,
2813
+ 'osakasayama.osaka.jp' => 1,
2814
+ 'sakai.osaka.jp' => 1,
2815
+ 'sayama.osaka.jp' => 1,
2816
+ 'sennan.osaka.jp' => 1,
2817
+ 'settsu.osaka.jp' => 1,
2818
+ 'shijonawate.osaka.jp' => 1,
2819
+ 'shimamoto.osaka.jp' => 1,
2820
+ 'suita.osaka.jp' => 1,
2821
+ 'tadaoka.osaka.jp' => 1,
2822
+ 'taishi.osaka.jp' => 1,
2823
+ 'tajiri.osaka.jp' => 1,
2824
+ 'takaishi.osaka.jp' => 1,
2825
+ 'takatsuki.osaka.jp' => 1,
2826
+ 'tondabayashi.osaka.jp' => 1,
2827
+ 'toyonaka.osaka.jp' => 1,
2828
+ 'toyono.osaka.jp' => 1,
2829
+ 'yao.osaka.jp' => 1,
2830
+ 'ariake.saga.jp' => 1,
2831
+ 'arita.saga.jp' => 1,
2832
+ 'fukudomi.saga.jp' => 1,
2833
+ 'genkai.saga.jp' => 1,
2834
+ 'hamatama.saga.jp' => 1,
2835
+ 'hizen.saga.jp' => 1,
2836
+ 'imari.saga.jp' => 1,
2837
+ 'kamimine.saga.jp' => 1,
2838
+ 'kanzaki.saga.jp' => 1,
2839
+ 'karatsu.saga.jp' => 1,
2840
+ 'kashima.saga.jp' => 1,
2841
+ 'kitagata.saga.jp' => 1,
2842
+ 'kitahata.saga.jp' => 1,
2843
+ 'kiyama.saga.jp' => 1,
2844
+ 'kouhoku.saga.jp' => 1,
2845
+ 'kyuragi.saga.jp' => 1,
2846
+ 'nishiarita.saga.jp' => 1,
2847
+ 'ogi.saga.jp' => 1,
2848
+ 'omachi.saga.jp' => 1,
2849
+ 'ouchi.saga.jp' => 1,
2850
+ 'saga.saga.jp' => 1,
2851
+ 'shiroishi.saga.jp' => 1,
2852
+ 'taku.saga.jp' => 1,
2853
+ 'tara.saga.jp' => 1,
2854
+ 'tosu.saga.jp' => 1,
2855
+ 'yoshinogari.saga.jp' => 1,
2856
+ 'arakawa.saitama.jp' => 1,
2857
+ 'asaka.saitama.jp' => 1,
2858
+ 'chichibu.saitama.jp' => 1,
2859
+ 'fujimi.saitama.jp' => 1,
2860
+ 'fujimino.saitama.jp' => 1,
2861
+ 'fukaya.saitama.jp' => 1,
2862
+ 'hanno.saitama.jp' => 1,
2863
+ 'hanyu.saitama.jp' => 1,
2864
+ 'hasuda.saitama.jp' => 1,
2865
+ 'hatogaya.saitama.jp' => 1,
2866
+ 'hatoyama.saitama.jp' => 1,
2867
+ 'hidaka.saitama.jp' => 1,
2868
+ 'higashichichibu.saitama.jp' => 1,
2869
+ 'higashimatsuyama.saitama.jp' => 1,
2870
+ 'honjo.saitama.jp' => 1,
2871
+ 'ina.saitama.jp' => 1,
2872
+ 'iruma.saitama.jp' => 1,
2873
+ 'iwatsuki.saitama.jp' => 1,
2874
+ 'kamiizumi.saitama.jp' => 1,
2875
+ 'kamikawa.saitama.jp' => 1,
2876
+ 'kamisato.saitama.jp' => 1,
2877
+ 'kasukabe.saitama.jp' => 1,
2878
+ 'kawagoe.saitama.jp' => 1,
2879
+ 'kawaguchi.saitama.jp' => 1,
2880
+ 'kawajima.saitama.jp' => 1,
2881
+ 'kazo.saitama.jp' => 1,
2882
+ 'kitamoto.saitama.jp' => 1,
2883
+ 'koshigaya.saitama.jp' => 1,
2884
+ 'kounosu.saitama.jp' => 1,
2885
+ 'kuki.saitama.jp' => 1,
2886
+ 'kumagaya.saitama.jp' => 1,
2887
+ 'matsubushi.saitama.jp' => 1,
2888
+ 'minano.saitama.jp' => 1,
2889
+ 'misato.saitama.jp' => 1,
2890
+ 'miyashiro.saitama.jp' => 1,
2891
+ 'miyoshi.saitama.jp' => 1,
2892
+ 'moroyama.saitama.jp' => 1,
2893
+ 'nagatoro.saitama.jp' => 1,
2894
+ 'namegawa.saitama.jp' => 1,
2895
+ 'niiza.saitama.jp' => 1,
2896
+ 'ogano.saitama.jp' => 1,
2897
+ 'ogawa.saitama.jp' => 1,
2898
+ 'ogose.saitama.jp' => 1,
2899
+ 'okegawa.saitama.jp' => 1,
2900
+ 'omiya.saitama.jp' => 1,
2901
+ 'otaki.saitama.jp' => 1,
2902
+ 'ranzan.saitama.jp' => 1,
2903
+ 'ryokami.saitama.jp' => 1,
2904
+ 'saitama.saitama.jp' => 1,
2905
+ 'sakado.saitama.jp' => 1,
2906
+ 'satte.saitama.jp' => 1,
2907
+ 'sayama.saitama.jp' => 1,
2908
+ 'shiki.saitama.jp' => 1,
2909
+ 'shiraoka.saitama.jp' => 1,
2910
+ 'soka.saitama.jp' => 1,
2911
+ 'sugito.saitama.jp' => 1,
2912
+ 'toda.saitama.jp' => 1,
2913
+ 'tokigawa.saitama.jp' => 1,
2914
+ 'tokorozawa.saitama.jp' => 1,
2915
+ 'tsurugashima.saitama.jp' => 1,
2916
+ 'urawa.saitama.jp' => 1,
2917
+ 'warabi.saitama.jp' => 1,
2918
+ 'yashio.saitama.jp' => 1,
2919
+ 'yokoze.saitama.jp' => 1,
2920
+ 'yono.saitama.jp' => 1,
2921
+ 'yorii.saitama.jp' => 1,
2922
+ 'yoshida.saitama.jp' => 1,
2923
+ 'yoshikawa.saitama.jp' => 1,
2924
+ 'yoshimi.saitama.jp' => 1,
2925
+ 'aisho.shiga.jp' => 1,
2926
+ 'gamo.shiga.jp' => 1,
2927
+ 'higashiomi.shiga.jp' => 1,
2928
+ 'hikone.shiga.jp' => 1,
2929
+ 'koka.shiga.jp' => 1,
2930
+ 'konan.shiga.jp' => 1,
2931
+ 'kosei.shiga.jp' => 1,
2932
+ 'koto.shiga.jp' => 1,
2933
+ 'kusatsu.shiga.jp' => 1,
2934
+ 'maibara.shiga.jp' => 1,
2935
+ 'moriyama.shiga.jp' => 1,
2936
+ 'nagahama.shiga.jp' => 1,
2937
+ 'nishiazai.shiga.jp' => 1,
2938
+ 'notogawa.shiga.jp' => 1,
2939
+ 'omihachiman.shiga.jp' => 1,
2940
+ 'otsu.shiga.jp' => 1,
2941
+ 'ritto.shiga.jp' => 1,
2942
+ 'ryuoh.shiga.jp' => 1,
2943
+ 'takashima.shiga.jp' => 1,
2944
+ 'takatsuki.shiga.jp' => 1,
2945
+ 'torahime.shiga.jp' => 1,
2946
+ 'toyosato.shiga.jp' => 1,
2947
+ 'yasu.shiga.jp' => 1,
2948
+ 'akagi.shimane.jp' => 1,
2949
+ 'ama.shimane.jp' => 1,
2950
+ 'gotsu.shimane.jp' => 1,
2951
+ 'hamada.shimane.jp' => 1,
2952
+ 'higashiizumo.shimane.jp' => 1,
2953
+ 'hikawa.shimane.jp' => 1,
2954
+ 'hikimi.shimane.jp' => 1,
2955
+ 'izumo.shimane.jp' => 1,
2956
+ 'kakinoki.shimane.jp' => 1,
2957
+ 'masuda.shimane.jp' => 1,
2958
+ 'matsue.shimane.jp' => 1,
2959
+ 'misato.shimane.jp' => 1,
2960
+ 'nishinoshima.shimane.jp' => 1,
2961
+ 'ohda.shimane.jp' => 1,
2962
+ 'okinoshima.shimane.jp' => 1,
2963
+ 'okuizumo.shimane.jp' => 1,
2964
+ 'shimane.shimane.jp' => 1,
2965
+ 'tamayu.shimane.jp' => 1,
2966
+ 'tsuwano.shimane.jp' => 1,
2967
+ 'unnan.shimane.jp' => 1,
2968
+ 'yakumo.shimane.jp' => 1,
2969
+ 'yasugi.shimane.jp' => 1,
2970
+ 'yatsuka.shimane.jp' => 1,
2971
+ 'arai.shizuoka.jp' => 1,
2972
+ 'atami.shizuoka.jp' => 1,
2973
+ 'fuji.shizuoka.jp' => 1,
2974
+ 'fujieda.shizuoka.jp' => 1,
2975
+ 'fujikawa.shizuoka.jp' => 1,
2976
+ 'fujinomiya.shizuoka.jp' => 1,
2977
+ 'fukuroi.shizuoka.jp' => 1,
2978
+ 'gotemba.shizuoka.jp' => 1,
2979
+ 'haibara.shizuoka.jp' => 1,
2980
+ 'hamamatsu.shizuoka.jp' => 1,
2981
+ 'higashiizu.shizuoka.jp' => 1,
2982
+ 'ito.shizuoka.jp' => 1,
2983
+ 'iwata.shizuoka.jp' => 1,
2984
+ 'izu.shizuoka.jp' => 1,
2985
+ 'izunokuni.shizuoka.jp' => 1,
2986
+ 'kakegawa.shizuoka.jp' => 1,
2987
+ 'kannami.shizuoka.jp' => 1,
2988
+ 'kawanehon.shizuoka.jp' => 1,
2989
+ 'kawazu.shizuoka.jp' => 1,
2990
+ 'kikugawa.shizuoka.jp' => 1,
2991
+ 'kosai.shizuoka.jp' => 1,
2992
+ 'makinohara.shizuoka.jp' => 1,
2993
+ 'matsuzaki.shizuoka.jp' => 1,
2994
+ 'minamiizu.shizuoka.jp' => 1,
2995
+ 'mishima.shizuoka.jp' => 1,
2996
+ 'morimachi.shizuoka.jp' => 1,
2997
+ 'nishiizu.shizuoka.jp' => 1,
2998
+ 'numazu.shizuoka.jp' => 1,
2999
+ 'omaezaki.shizuoka.jp' => 1,
3000
+ 'shimada.shizuoka.jp' => 1,
3001
+ 'shimizu.shizuoka.jp' => 1,
3002
+ 'shimoda.shizuoka.jp' => 1,
3003
+ 'shizuoka.shizuoka.jp' => 1,
3004
+ 'susono.shizuoka.jp' => 1,
3005
+ 'yaizu.shizuoka.jp' => 1,
3006
+ 'yoshida.shizuoka.jp' => 1,
3007
+ 'ashikaga.tochigi.jp' => 1,
3008
+ 'bato.tochigi.jp' => 1,
3009
+ 'haga.tochigi.jp' => 1,
3010
+ 'ichikai.tochigi.jp' => 1,
3011
+ 'iwafune.tochigi.jp' => 1,
3012
+ 'kaminokawa.tochigi.jp' => 1,
3013
+ 'kanuma.tochigi.jp' => 1,
3014
+ 'karasuyama.tochigi.jp' => 1,
3015
+ 'kuroiso.tochigi.jp' => 1,
3016
+ 'mashiko.tochigi.jp' => 1,
3017
+ 'mibu.tochigi.jp' => 1,
3018
+ 'moka.tochigi.jp' => 1,
3019
+ 'motegi.tochigi.jp' => 1,
3020
+ 'nasu.tochigi.jp' => 1,
3021
+ 'nasushiobara.tochigi.jp' => 1,
3022
+ 'nikko.tochigi.jp' => 1,
3023
+ 'nishikata.tochigi.jp' => 1,
3024
+ 'nogi.tochigi.jp' => 1,
3025
+ 'ohira.tochigi.jp' => 1,
3026
+ 'ohtawara.tochigi.jp' => 1,
3027
+ 'oyama.tochigi.jp' => 1,
3028
+ 'sakura.tochigi.jp' => 1,
3029
+ 'sano.tochigi.jp' => 1,
3030
+ 'shimotsuke.tochigi.jp' => 1,
3031
+ 'shioya.tochigi.jp' => 1,
3032
+ 'takanezawa.tochigi.jp' => 1,
3033
+ 'tochigi.tochigi.jp' => 1,
3034
+ 'tsuga.tochigi.jp' => 1,
3035
+ 'ujiie.tochigi.jp' => 1,
3036
+ 'utsunomiya.tochigi.jp' => 1,
3037
+ 'yaita.tochigi.jp' => 1,
3038
+ 'aizumi.tokushima.jp' => 1,
3039
+ 'anan.tokushima.jp' => 1,
3040
+ 'ichiba.tokushima.jp' => 1,
3041
+ 'itano.tokushima.jp' => 1,
3042
+ 'kainan.tokushima.jp' => 1,
3043
+ 'komatsushima.tokushima.jp' => 1,
3044
+ 'matsushige.tokushima.jp' => 1,
3045
+ 'mima.tokushima.jp' => 1,
3046
+ 'minami.tokushima.jp' => 1,
3047
+ 'miyoshi.tokushima.jp' => 1,
3048
+ 'mugi.tokushima.jp' => 1,
3049
+ 'nakagawa.tokushima.jp' => 1,
3050
+ 'naruto.tokushima.jp' => 1,
3051
+ 'sanagochi.tokushima.jp' => 1,
3052
+ 'shishikui.tokushima.jp' => 1,
3053
+ 'tokushima.tokushima.jp' => 1,
3054
+ 'wajiki.tokushima.jp' => 1,
3055
+ 'adachi.tokyo.jp' => 1,
3056
+ 'akiruno.tokyo.jp' => 1,
3057
+ 'akishima.tokyo.jp' => 1,
3058
+ 'aogashima.tokyo.jp' => 1,
3059
+ 'arakawa.tokyo.jp' => 1,
3060
+ 'bunkyo.tokyo.jp' => 1,
3061
+ 'chiyoda.tokyo.jp' => 1,
3062
+ 'chofu.tokyo.jp' => 1,
3063
+ 'chuo.tokyo.jp' => 1,
3064
+ 'edogawa.tokyo.jp' => 1,
3065
+ 'fuchu.tokyo.jp' => 1,
3066
+ 'fussa.tokyo.jp' => 1,
3067
+ 'hachijo.tokyo.jp' => 1,
3068
+ 'hachioji.tokyo.jp' => 1,
3069
+ 'hamura.tokyo.jp' => 1,
3070
+ 'higashikurume.tokyo.jp' => 1,
3071
+ 'higashimurayama.tokyo.jp' => 1,
3072
+ 'higashiyamato.tokyo.jp' => 1,
3073
+ 'hino.tokyo.jp' => 1,
3074
+ 'hinode.tokyo.jp' => 1,
3075
+ 'hinohara.tokyo.jp' => 1,
3076
+ 'inagi.tokyo.jp' => 1,
3077
+ 'itabashi.tokyo.jp' => 1,
3078
+ 'katsushika.tokyo.jp' => 1,
3079
+ 'kita.tokyo.jp' => 1,
3080
+ 'kiyose.tokyo.jp' => 1,
3081
+ 'kodaira.tokyo.jp' => 1,
3082
+ 'koganei.tokyo.jp' => 1,
3083
+ 'kokubunji.tokyo.jp' => 1,
3084
+ 'komae.tokyo.jp' => 1,
3085
+ 'koto.tokyo.jp' => 1,
3086
+ 'kouzushima.tokyo.jp' => 1,
3087
+ 'kunitachi.tokyo.jp' => 1,
3088
+ 'machida.tokyo.jp' => 1,
3089
+ 'meguro.tokyo.jp' => 1,
3090
+ 'minato.tokyo.jp' => 1,
3091
+ 'mitaka.tokyo.jp' => 1,
3092
+ 'mizuho.tokyo.jp' => 1,
3093
+ 'musashimurayama.tokyo.jp' => 1,
3094
+ 'musashino.tokyo.jp' => 1,
3095
+ 'nakano.tokyo.jp' => 1,
3096
+ 'nerima.tokyo.jp' => 1,
3097
+ 'ogasawara.tokyo.jp' => 1,
3098
+ 'okutama.tokyo.jp' => 1,
3099
+ 'ome.tokyo.jp' => 1,
3100
+ 'oshima.tokyo.jp' => 1,
3101
+ 'ota.tokyo.jp' => 1,
3102
+ 'setagaya.tokyo.jp' => 1,
3103
+ 'shibuya.tokyo.jp' => 1,
3104
+ 'shinagawa.tokyo.jp' => 1,
3105
+ 'shinjuku.tokyo.jp' => 1,
3106
+ 'suginami.tokyo.jp' => 1,
3107
+ 'sumida.tokyo.jp' => 1,
3108
+ 'tachikawa.tokyo.jp' => 1,
3109
+ 'taito.tokyo.jp' => 1,
3110
+ 'tama.tokyo.jp' => 1,
3111
+ 'toshima.tokyo.jp' => 1,
3112
+ 'chizu.tottori.jp' => 1,
3113
+ 'hino.tottori.jp' => 1,
3114
+ 'kawahara.tottori.jp' => 1,
3115
+ 'koge.tottori.jp' => 1,
3116
+ 'kotoura.tottori.jp' => 1,
3117
+ 'misasa.tottori.jp' => 1,
3118
+ 'nanbu.tottori.jp' => 1,
3119
+ 'nichinan.tottori.jp' => 1,
3120
+ 'sakaiminato.tottori.jp' => 1,
3121
+ 'tottori.tottori.jp' => 1,
3122
+ 'wakasa.tottori.jp' => 1,
3123
+ 'yazu.tottori.jp' => 1,
3124
+ 'yonago.tottori.jp' => 1,
3125
+ 'asahi.toyama.jp' => 1,
3126
+ 'fuchu.toyama.jp' => 1,
3127
+ 'fukumitsu.toyama.jp' => 1,
3128
+ 'funahashi.toyama.jp' => 1,
3129
+ 'himi.toyama.jp' => 1,
3130
+ 'imizu.toyama.jp' => 1,
3131
+ 'inami.toyama.jp' => 1,
3132
+ 'johana.toyama.jp' => 1,
3133
+ 'kamiichi.toyama.jp' => 1,
3134
+ 'kurobe.toyama.jp' => 1,
3135
+ 'nakaniikawa.toyama.jp' => 1,
3136
+ 'namerikawa.toyama.jp' => 1,
3137
+ 'nanto.toyama.jp' => 1,
3138
+ 'nyuzen.toyama.jp' => 1,
3139
+ 'oyabe.toyama.jp' => 1,
3140
+ 'taira.toyama.jp' => 1,
3141
+ 'takaoka.toyama.jp' => 1,
3142
+ 'tateyama.toyama.jp' => 1,
3143
+ 'toga.toyama.jp' => 1,
3144
+ 'tonami.toyama.jp' => 1,
3145
+ 'toyama.toyama.jp' => 1,
3146
+ 'unazuki.toyama.jp' => 1,
3147
+ 'uozu.toyama.jp' => 1,
3148
+ 'yamada.toyama.jp' => 1,
3149
+ 'arida.wakayama.jp' => 1,
3150
+ 'aridagawa.wakayama.jp' => 1,
3151
+ 'gobo.wakayama.jp' => 1,
3152
+ 'hashimoto.wakayama.jp' => 1,
3153
+ 'hidaka.wakayama.jp' => 1,
3154
+ 'hirogawa.wakayama.jp' => 1,
3155
+ 'inami.wakayama.jp' => 1,
3156
+ 'iwade.wakayama.jp' => 1,
3157
+ 'kainan.wakayama.jp' => 1,
3158
+ 'kamitonda.wakayama.jp' => 1,
3159
+ 'katsuragi.wakayama.jp' => 1,
3160
+ 'kimino.wakayama.jp' => 1,
3161
+ 'kinokawa.wakayama.jp' => 1,
3162
+ 'kitayama.wakayama.jp' => 1,
3163
+ 'koya.wakayama.jp' => 1,
3164
+ 'koza.wakayama.jp' => 1,
3165
+ 'kozagawa.wakayama.jp' => 1,
3166
+ 'kudoyama.wakayama.jp' => 1,
3167
+ 'kushimoto.wakayama.jp' => 1,
3168
+ 'mihama.wakayama.jp' => 1,
3169
+ 'misato.wakayama.jp' => 1,
3170
+ 'nachikatsuura.wakayama.jp' => 1,
3171
+ 'shingu.wakayama.jp' => 1,
3172
+ 'shirahama.wakayama.jp' => 1,
3173
+ 'taiji.wakayama.jp' => 1,
3174
+ 'tanabe.wakayama.jp' => 1,
3175
+ 'wakayama.wakayama.jp' => 1,
3176
+ 'yuasa.wakayama.jp' => 1,
3177
+ 'yura.wakayama.jp' => 1,
3178
+ 'asahi.yamagata.jp' => 1,
3179
+ 'funagata.yamagata.jp' => 1,
3180
+ 'higashine.yamagata.jp' => 1,
3181
+ 'iide.yamagata.jp' => 1,
3182
+ 'kahoku.yamagata.jp' => 1,
3183
+ 'kaminoyama.yamagata.jp' => 1,
3184
+ 'kaneyama.yamagata.jp' => 1,
3185
+ 'kawanishi.yamagata.jp' => 1,
3186
+ 'mamurogawa.yamagata.jp' => 1,
3187
+ 'mikawa.yamagata.jp' => 1,
3188
+ 'murayama.yamagata.jp' => 1,
3189
+ 'nagai.yamagata.jp' => 1,
3190
+ 'nakayama.yamagata.jp' => 1,
3191
+ 'nanyo.yamagata.jp' => 1,
3192
+ 'nishikawa.yamagata.jp' => 1,
3193
+ 'obanazawa.yamagata.jp' => 1,
3194
+ 'oe.yamagata.jp' => 1,
3195
+ 'oguni.yamagata.jp' => 1,
3196
+ 'ohkura.yamagata.jp' => 1,
3197
+ 'oishida.yamagata.jp' => 1,
3198
+ 'sagae.yamagata.jp' => 1,
3199
+ 'sakata.yamagata.jp' => 1,
3200
+ 'sakegawa.yamagata.jp' => 1,
3201
+ 'shinjo.yamagata.jp' => 1,
3202
+ 'shirataka.yamagata.jp' => 1,
3203
+ 'shonai.yamagata.jp' => 1,
3204
+ 'takahata.yamagata.jp' => 1,
3205
+ 'tendo.yamagata.jp' => 1,
3206
+ 'tozawa.yamagata.jp' => 1,
3207
+ 'tsuruoka.yamagata.jp' => 1,
3208
+ 'yamagata.yamagata.jp' => 1,
3209
+ 'yamanobe.yamagata.jp' => 1,
3210
+ 'yonezawa.yamagata.jp' => 1,
3211
+ 'yuza.yamagata.jp' => 1,
3212
+ 'abu.yamaguchi.jp' => 1,
3213
+ 'hagi.yamaguchi.jp' => 1,
3214
+ 'hikari.yamaguchi.jp' => 1,
3215
+ 'hofu.yamaguchi.jp' => 1,
3216
+ 'iwakuni.yamaguchi.jp' => 1,
3217
+ 'kudamatsu.yamaguchi.jp' => 1,
3218
+ 'mitou.yamaguchi.jp' => 1,
3219
+ 'nagato.yamaguchi.jp' => 1,
3220
+ 'oshima.yamaguchi.jp' => 1,
3221
+ 'shimonoseki.yamaguchi.jp' => 1,
3222
+ 'shunan.yamaguchi.jp' => 1,
3223
+ 'tabuse.yamaguchi.jp' => 1,
3224
+ 'tokuyama.yamaguchi.jp' => 1,
3225
+ 'toyota.yamaguchi.jp' => 1,
3226
+ 'ube.yamaguchi.jp' => 1,
3227
+ 'yuu.yamaguchi.jp' => 1,
3228
+ 'chuo.yamanashi.jp' => 1,
3229
+ 'doshi.yamanashi.jp' => 1,
3230
+ 'fuefuki.yamanashi.jp' => 1,
3231
+ 'fujikawa.yamanashi.jp' => 1,
3232
+ 'fujikawaguchiko.yamanashi.jp' => 1,
3233
+ 'fujiyoshida.yamanashi.jp' => 1,
3234
+ 'hayakawa.yamanashi.jp' => 1,
3235
+ 'hokuto.yamanashi.jp' => 1,
3236
+ 'ichikawamisato.yamanashi.jp' => 1,
3237
+ 'kai.yamanashi.jp' => 1,
3238
+ 'kofu.yamanashi.jp' => 1,
3239
+ 'koshu.yamanashi.jp' => 1,
3240
+ 'kosuge.yamanashi.jp' => 1,
3241
+ 'minami-alps.yamanashi.jp' => 1,
3242
+ 'minobu.yamanashi.jp' => 1,
3243
+ 'nakamichi.yamanashi.jp' => 1,
3244
+ 'nanbu.yamanashi.jp' => 1,
3245
+ 'narusawa.yamanashi.jp' => 1,
3246
+ 'nirasaki.yamanashi.jp' => 1,
3247
+ 'nishikatsura.yamanashi.jp' => 1,
3248
+ 'oshino.yamanashi.jp' => 1,
3249
+ 'otsuki.yamanashi.jp' => 1,
3250
+ 'showa.yamanashi.jp' => 1,
3251
+ 'tabayama.yamanashi.jp' => 1,
3252
+ 'tsuru.yamanashi.jp' => 1,
3253
+ 'uenohara.yamanashi.jp' => 1,
3254
+ 'yamanakako.yamanashi.jp' => 1,
3255
+ 'yamanashi.yamanashi.jp' => 1,
3256
+ 'ke' => 1,
3257
+ 'ac.ke' => 1,
3258
+ 'co.ke' => 1,
3259
+ 'go.ke' => 1,
3260
+ 'info.ke' => 1,
3261
+ 'me.ke' => 1,
3262
+ 'mobi.ke' => 1,
3263
+ 'ne.ke' => 1,
3264
+ 'or.ke' => 1,
3265
+ 'sc.ke' => 1,
3266
+ 'kg' => 1,
3267
+ 'org.kg' => 1,
3268
+ 'net.kg' => 1,
3269
+ 'com.kg' => 1,
3270
+ 'edu.kg' => 1,
3271
+ 'gov.kg' => 1,
3272
+ 'mil.kg' => 1,
3273
+ '*.kh' => 1,
3274
+ 'ki' => 1,
3275
+ 'edu.ki' => 1,
3276
+ 'biz.ki' => 1,
3277
+ 'net.ki' => 1,
3278
+ 'org.ki' => 1,
3279
+ 'gov.ki' => 1,
3280
+ 'info.ki' => 1,
3281
+ 'com.ki' => 1,
3282
+ 'km' => 1,
3283
+ 'org.km' => 1,
3284
+ 'nom.km' => 1,
3285
+ 'gov.km' => 1,
3286
+ 'prd.km' => 1,
3287
+ 'tm.km' => 1,
3288
+ 'edu.km' => 1,
3289
+ 'mil.km' => 1,
3290
+ 'ass.km' => 1,
3291
+ 'com.km' => 1,
3292
+ 'coop.km' => 1,
3293
+ 'asso.km' => 1,
3294
+ 'presse.km' => 1,
3295
+ 'medecin.km' => 1,
3296
+ 'notaires.km' => 1,
3297
+ 'pharmaciens.km' => 1,
3298
+ 'veterinaire.km' => 1,
3299
+ 'gouv.km' => 1,
3300
+ 'kn' => 1,
3301
+ 'net.kn' => 1,
3302
+ 'org.kn' => 1,
3303
+ 'edu.kn' => 1,
3304
+ 'gov.kn' => 1,
3305
+ 'kp' => 1,
3306
+ 'com.kp' => 1,
3307
+ 'edu.kp' => 1,
3308
+ 'gov.kp' => 1,
3309
+ 'org.kp' => 1,
3310
+ 'rep.kp' => 1,
3311
+ 'tra.kp' => 1,
3312
+ 'kr' => 1,
3313
+ 'ac.kr' => 1,
3314
+ 'co.kr' => 1,
3315
+ 'es.kr' => 1,
3316
+ 'go.kr' => 1,
3317
+ 'hs.kr' => 1,
3318
+ 'kg.kr' => 1,
3319
+ 'mil.kr' => 1,
3320
+ 'ms.kr' => 1,
3321
+ 'ne.kr' => 1,
3322
+ 'or.kr' => 1,
3323
+ 'pe.kr' => 1,
3324
+ 're.kr' => 1,
3325
+ 'sc.kr' => 1,
3326
+ 'busan.kr' => 1,
3327
+ 'chungbuk.kr' => 1,
3328
+ 'chungnam.kr' => 1,
3329
+ 'daegu.kr' => 1,
3330
+ 'daejeon.kr' => 1,
3331
+ 'gangwon.kr' => 1,
3332
+ 'gwangju.kr' => 1,
3333
+ 'gyeongbuk.kr' => 1,
3334
+ 'gyeonggi.kr' => 1,
3335
+ 'gyeongnam.kr' => 1,
3336
+ 'incheon.kr' => 1,
3337
+ 'jeju.kr' => 1,
3338
+ 'jeonbuk.kr' => 1,
3339
+ 'jeonnam.kr' => 1,
3340
+ 'seoul.kr' => 1,
3341
+ 'ulsan.kr' => 1,
3342
+ 'kw' => 1,
3343
+ 'com.kw' => 1,
3344
+ 'edu.kw' => 1,
3345
+ 'emb.kw' => 1,
3346
+ 'gov.kw' => 1,
3347
+ 'ind.kw' => 1,
3348
+ 'net.kw' => 1,
3349
+ 'org.kw' => 1,
3350
+ 'ky' => 1,
3351
+ 'edu.ky' => 1,
3352
+ 'gov.ky' => 1,
3353
+ 'com.ky' => 1,
3354
+ 'org.ky' => 1,
3355
+ 'net.ky' => 1,
3356
+ 'kz' => 1,
3357
+ 'org.kz' => 1,
3358
+ 'edu.kz' => 1,
3359
+ 'net.kz' => 1,
3360
+ 'gov.kz' => 1,
3361
+ 'mil.kz' => 1,
3362
+ 'com.kz' => 1,
3363
+ 'la' => 1,
3364
+ 'int.la' => 1,
3365
+ 'net.la' => 1,
3366
+ 'info.la' => 1,
3367
+ 'edu.la' => 1,
3368
+ 'gov.la' => 1,
3369
+ 'per.la' => 1,
3370
+ 'com.la' => 1,
3371
+ 'org.la' => 1,
3372
+ 'lb' => 1,
3373
+ 'com.lb' => 1,
3374
+ 'edu.lb' => 1,
3375
+ 'gov.lb' => 1,
3376
+ 'net.lb' => 1,
3377
+ 'org.lb' => 1,
3378
+ 'lc' => 1,
3379
+ 'com.lc' => 1,
3380
+ 'net.lc' => 1,
3381
+ 'co.lc' => 1,
3382
+ 'org.lc' => 1,
3383
+ 'edu.lc' => 1,
3384
+ 'gov.lc' => 1,
3385
+ 'li' => 1,
3386
+ 'lk' => 1,
3387
+ 'gov.lk' => 1,
3388
+ 'sch.lk' => 1,
3389
+ 'net.lk' => 1,
3390
+ 'int.lk' => 1,
3391
+ 'com.lk' => 1,
3392
+ 'org.lk' => 1,
3393
+ 'edu.lk' => 1,
3394
+ 'ngo.lk' => 1,
3395
+ 'soc.lk' => 1,
3396
+ 'web.lk' => 1,
3397
+ 'ltd.lk' => 1,
3398
+ 'assn.lk' => 1,
3399
+ 'grp.lk' => 1,
3400
+ 'hotel.lk' => 1,
3401
+ 'ac.lk' => 1,
3402
+ 'lr' => 1,
3403
+ 'com.lr' => 1,
3404
+ 'edu.lr' => 1,
3405
+ 'gov.lr' => 1,
3406
+ 'org.lr' => 1,
3407
+ 'net.lr' => 1,
3408
+ 'ls' => 1,
3409
+ 'ac.ls' => 1,
3410
+ 'biz.ls' => 1,
3411
+ 'co.ls' => 1,
3412
+ 'edu.ls' => 1,
3413
+ 'gov.ls' => 1,
3414
+ 'info.ls' => 1,
3415
+ 'net.ls' => 1,
3416
+ 'org.ls' => 1,
3417
+ 'sc.ls' => 1,
3418
+ 'lt' => 1,
3419
+ 'gov.lt' => 1,
3420
+ 'lu' => 1,
3421
+ 'lv' => 1,
3422
+ 'com.lv' => 1,
3423
+ 'edu.lv' => 1,
3424
+ 'gov.lv' => 1,
3425
+ 'org.lv' => 1,
3426
+ 'mil.lv' => 1,
3427
+ 'id.lv' => 1,
3428
+ 'net.lv' => 1,
3429
+ 'asn.lv' => 1,
3430
+ 'conf.lv' => 1,
3431
+ 'ly' => 1,
3432
+ 'com.ly' => 1,
3433
+ 'net.ly' => 1,
3434
+ 'gov.ly' => 1,
3435
+ 'plc.ly' => 1,
3436
+ 'edu.ly' => 1,
3437
+ 'sch.ly' => 1,
3438
+ 'med.ly' => 1,
3439
+ 'org.ly' => 1,
3440
+ 'id.ly' => 1,
3441
+ 'ma' => 1,
3442
+ 'co.ma' => 1,
3443
+ 'net.ma' => 1,
3444
+ 'gov.ma' => 1,
3445
+ 'org.ma' => 1,
3446
+ 'ac.ma' => 1,
3447
+ 'press.ma' => 1,
3448
+ 'mc' => 1,
3449
+ 'tm.mc' => 1,
3450
+ 'asso.mc' => 1,
3451
+ 'md' => 1,
3452
+ 'me' => 1,
3453
+ 'co.me' => 1,
3454
+ 'net.me' => 1,
3455
+ 'org.me' => 1,
3456
+ 'edu.me' => 1,
3457
+ 'ac.me' => 1,
3458
+ 'gov.me' => 1,
3459
+ 'its.me' => 1,
3460
+ 'priv.me' => 1,
3461
+ 'mg' => 1,
3462
+ 'org.mg' => 1,
3463
+ 'nom.mg' => 1,
3464
+ 'gov.mg' => 1,
3465
+ 'prd.mg' => 1,
3466
+ 'tm.mg' => 1,
3467
+ 'edu.mg' => 1,
3468
+ 'mil.mg' => 1,
3469
+ 'com.mg' => 1,
3470
+ 'co.mg' => 1,
3471
+ 'mh' => 1,
3472
+ 'mil' => 1,
3473
+ 'mk' => 1,
3474
+ 'com.mk' => 1,
3475
+ 'org.mk' => 1,
3476
+ 'net.mk' => 1,
3477
+ 'edu.mk' => 1,
3478
+ 'gov.mk' => 1,
3479
+ 'inf.mk' => 1,
3480
+ 'name.mk' => 1,
3481
+ 'ml' => 1,
3482
+ 'com.ml' => 1,
3483
+ 'edu.ml' => 1,
3484
+ 'gouv.ml' => 1,
3485
+ 'gov.ml' => 1,
3486
+ 'net.ml' => 1,
3487
+ 'org.ml' => 1,
3488
+ 'presse.ml' => 1,
3489
+ '*.mm' => 1,
3490
+ 'mn' => 1,
3491
+ 'gov.mn' => 1,
3492
+ 'edu.mn' => 1,
3493
+ 'org.mn' => 1,
3494
+ 'mo' => 1,
3495
+ 'com.mo' => 1,
3496
+ 'net.mo' => 1,
3497
+ 'org.mo' => 1,
3498
+ 'edu.mo' => 1,
3499
+ 'gov.mo' => 1,
3500
+ 'mobi' => 1,
3501
+ 'mp' => 1,
3502
+ 'mq' => 1,
3503
+ 'mr' => 1,
3504
+ 'gov.mr' => 1,
3505
+ 'ms' => 1,
3506
+ 'com.ms' => 1,
3507
+ 'edu.ms' => 1,
3508
+ 'gov.ms' => 1,
3509
+ 'net.ms' => 1,
3510
+ 'org.ms' => 1,
3511
+ 'mt' => 1,
3512
+ 'com.mt' => 1,
3513
+ 'edu.mt' => 1,
3514
+ 'net.mt' => 1,
3515
+ 'org.mt' => 1,
3516
+ 'mu' => 1,
3517
+ 'com.mu' => 1,
3518
+ 'net.mu' => 1,
3519
+ 'org.mu' => 1,
3520
+ 'gov.mu' => 1,
3521
+ 'ac.mu' => 1,
3522
+ 'co.mu' => 1,
3523
+ 'or.mu' => 1,
3524
+ 'museum' => 1,
3525
+ 'academy.museum' => 1,
3526
+ 'agriculture.museum' => 1,
3527
+ 'air.museum' => 1,
3528
+ 'airguard.museum' => 1,
3529
+ 'alabama.museum' => 1,
3530
+ 'alaska.museum' => 1,
3531
+ 'amber.museum' => 1,
3532
+ 'ambulance.museum' => 1,
3533
+ 'american.museum' => 1,
3534
+ 'americana.museum' => 1,
3535
+ 'americanantiques.museum' => 1,
3536
+ 'americanart.museum' => 1,
3537
+ 'amsterdam.museum' => 1,
3538
+ 'and.museum' => 1,
3539
+ 'annefrank.museum' => 1,
3540
+ 'anthro.museum' => 1,
3541
+ 'anthropology.museum' => 1,
3542
+ 'antiques.museum' => 1,
3543
+ 'aquarium.museum' => 1,
3544
+ 'arboretum.museum' => 1,
3545
+ 'archaeological.museum' => 1,
3546
+ 'archaeology.museum' => 1,
3547
+ 'architecture.museum' => 1,
3548
+ 'art.museum' => 1,
3549
+ 'artanddesign.museum' => 1,
3550
+ 'artcenter.museum' => 1,
3551
+ 'artdeco.museum' => 1,
3552
+ 'arteducation.museum' => 1,
3553
+ 'artgallery.museum' => 1,
3554
+ 'arts.museum' => 1,
3555
+ 'artsandcrafts.museum' => 1,
3556
+ 'asmatart.museum' => 1,
3557
+ 'assassination.museum' => 1,
3558
+ 'assisi.museum' => 1,
3559
+ 'association.museum' => 1,
3560
+ 'astronomy.museum' => 1,
3561
+ 'atlanta.museum' => 1,
3562
+ 'austin.museum' => 1,
3563
+ 'australia.museum' => 1,
3564
+ 'automotive.museum' => 1,
3565
+ 'aviation.museum' => 1,
3566
+ 'axis.museum' => 1,
3567
+ 'badajoz.museum' => 1,
3568
+ 'baghdad.museum' => 1,
3569
+ 'bahn.museum' => 1,
3570
+ 'bale.museum' => 1,
3571
+ 'baltimore.museum' => 1,
3572
+ 'barcelona.museum' => 1,
3573
+ 'baseball.museum' => 1,
3574
+ 'basel.museum' => 1,
3575
+ 'baths.museum' => 1,
3576
+ 'bauern.museum' => 1,
3577
+ 'beauxarts.museum' => 1,
3578
+ 'beeldengeluid.museum' => 1,
3579
+ 'bellevue.museum' => 1,
3580
+ 'bergbau.museum' => 1,
3581
+ 'berkeley.museum' => 1,
3582
+ 'berlin.museum' => 1,
3583
+ 'bern.museum' => 1,
3584
+ 'bible.museum' => 1,
3585
+ 'bilbao.museum' => 1,
3586
+ 'bill.museum' => 1,
3587
+ 'birdart.museum' => 1,
3588
+ 'birthplace.museum' => 1,
3589
+ 'bonn.museum' => 1,
3590
+ 'boston.museum' => 1,
3591
+ 'botanical.museum' => 1,
3592
+ 'botanicalgarden.museum' => 1,
3593
+ 'botanicgarden.museum' => 1,
3594
+ 'botany.museum' => 1,
3595
+ 'brandywinevalley.museum' => 1,
3596
+ 'brasil.museum' => 1,
3597
+ 'bristol.museum' => 1,
3598
+ 'british.museum' => 1,
3599
+ 'britishcolumbia.museum' => 1,
3600
+ 'broadcast.museum' => 1,
3601
+ 'brunel.museum' => 1,
3602
+ 'brussel.museum' => 1,
3603
+ 'brussels.museum' => 1,
3604
+ 'bruxelles.museum' => 1,
3605
+ 'building.museum' => 1,
3606
+ 'burghof.museum' => 1,
3607
+ 'bus.museum' => 1,
3608
+ 'bushey.museum' => 1,
3609
+ 'cadaques.museum' => 1,
3610
+ 'california.museum' => 1,
3611
+ 'cambridge.museum' => 1,
3612
+ 'can.museum' => 1,
3613
+ 'canada.museum' => 1,
3614
+ 'capebreton.museum' => 1,
3615
+ 'carrier.museum' => 1,
3616
+ 'cartoonart.museum' => 1,
3617
+ 'casadelamoneda.museum' => 1,
3618
+ 'castle.museum' => 1,
3619
+ 'castres.museum' => 1,
3620
+ 'celtic.museum' => 1,
3621
+ 'center.museum' => 1,
3622
+ 'chattanooga.museum' => 1,
3623
+ 'cheltenham.museum' => 1,
3624
+ 'chesapeakebay.museum' => 1,
3625
+ 'chicago.museum' => 1,
3626
+ 'children.museum' => 1,
3627
+ 'childrens.museum' => 1,
3628
+ 'childrensgarden.museum' => 1,
3629
+ 'chiropractic.museum' => 1,
3630
+ 'chocolate.museum' => 1,
3631
+ 'christiansburg.museum' => 1,
3632
+ 'cincinnati.museum' => 1,
3633
+ 'cinema.museum' => 1,
3634
+ 'circus.museum' => 1,
3635
+ 'civilisation.museum' => 1,
3636
+ 'civilization.museum' => 1,
3637
+ 'civilwar.museum' => 1,
3638
+ 'clinton.museum' => 1,
3639
+ 'clock.museum' => 1,
3640
+ 'coal.museum' => 1,
3641
+ 'coastaldefence.museum' => 1,
3642
+ 'cody.museum' => 1,
3643
+ 'coldwar.museum' => 1,
3644
+ 'collection.museum' => 1,
3645
+ 'colonialwilliamsburg.museum' => 1,
3646
+ 'coloradoplateau.museum' => 1,
3647
+ 'columbia.museum' => 1,
3648
+ 'columbus.museum' => 1,
3649
+ 'communication.museum' => 1,
3650
+ 'communications.museum' => 1,
3651
+ 'community.museum' => 1,
3652
+ 'computer.museum' => 1,
3653
+ 'computerhistory.museum' => 1,
3654
+ 'comunicações.museum' => 1,
3655
+ 'contemporary.museum' => 1,
3656
+ 'contemporaryart.museum' => 1,
3657
+ 'convent.museum' => 1,
3658
+ 'copenhagen.museum' => 1,
3659
+ 'corporation.museum' => 1,
3660
+ 'correios-e-telecomunicações.museum' => 1,
3661
+ 'corvette.museum' => 1,
3662
+ 'costume.museum' => 1,
3663
+ 'countryestate.museum' => 1,
3664
+ 'county.museum' => 1,
3665
+ 'crafts.museum' => 1,
3666
+ 'cranbrook.museum' => 1,
3667
+ 'creation.museum' => 1,
3668
+ 'cultural.museum' => 1,
3669
+ 'culturalcenter.museum' => 1,
3670
+ 'culture.museum' => 1,
3671
+ 'cyber.museum' => 1,
3672
+ 'cymru.museum' => 1,
3673
+ 'dali.museum' => 1,
3674
+ 'dallas.museum' => 1,
3675
+ 'database.museum' => 1,
3676
+ 'ddr.museum' => 1,
3677
+ 'decorativearts.museum' => 1,
3678
+ 'delaware.museum' => 1,
3679
+ 'delmenhorst.museum' => 1,
3680
+ 'denmark.museum' => 1,
3681
+ 'depot.museum' => 1,
3682
+ 'design.museum' => 1,
3683
+ 'detroit.museum' => 1,
3684
+ 'dinosaur.museum' => 1,
3685
+ 'discovery.museum' => 1,
3686
+ 'dolls.museum' => 1,
3687
+ 'donostia.museum' => 1,
3688
+ 'durham.museum' => 1,
3689
+ 'eastafrica.museum' => 1,
3690
+ 'eastcoast.museum' => 1,
3691
+ 'education.museum' => 1,
3692
+ 'educational.museum' => 1,
3693
+ 'egyptian.museum' => 1,
3694
+ 'eisenbahn.museum' => 1,
3695
+ 'elburg.museum' => 1,
3696
+ 'elvendrell.museum' => 1,
3697
+ 'embroidery.museum' => 1,
3698
+ 'encyclopedic.museum' => 1,
3699
+ 'england.museum' => 1,
3700
+ 'entomology.museum' => 1,
3701
+ 'environment.museum' => 1,
3702
+ 'environmentalconservation.museum' => 1,
3703
+ 'epilepsy.museum' => 1,
3704
+ 'essex.museum' => 1,
3705
+ 'estate.museum' => 1,
3706
+ 'ethnology.museum' => 1,
3707
+ 'exeter.museum' => 1,
3708
+ 'exhibition.museum' => 1,
3709
+ 'family.museum' => 1,
3710
+ 'farm.museum' => 1,
3711
+ 'farmequipment.museum' => 1,
3712
+ 'farmers.museum' => 1,
3713
+ 'farmstead.museum' => 1,
3714
+ 'field.museum' => 1,
3715
+ 'figueres.museum' => 1,
3716
+ 'filatelia.museum' => 1,
3717
+ 'film.museum' => 1,
3718
+ 'fineart.museum' => 1,
3719
+ 'finearts.museum' => 1,
3720
+ 'finland.museum' => 1,
3721
+ 'flanders.museum' => 1,
3722
+ 'florida.museum' => 1,
3723
+ 'force.museum' => 1,
3724
+ 'fortmissoula.museum' => 1,
3725
+ 'fortworth.museum' => 1,
3726
+ 'foundation.museum' => 1,
3727
+ 'francaise.museum' => 1,
3728
+ 'frankfurt.museum' => 1,
3729
+ 'franziskaner.museum' => 1,
3730
+ 'freemasonry.museum' => 1,
3731
+ 'freiburg.museum' => 1,
3732
+ 'fribourg.museum' => 1,
3733
+ 'frog.museum' => 1,
3734
+ 'fundacio.museum' => 1,
3735
+ 'furniture.museum' => 1,
3736
+ 'gallery.museum' => 1,
3737
+ 'garden.museum' => 1,
3738
+ 'gateway.museum' => 1,
3739
+ 'geelvinck.museum' => 1,
3740
+ 'gemological.museum' => 1,
3741
+ 'geology.museum' => 1,
3742
+ 'georgia.museum' => 1,
3743
+ 'giessen.museum' => 1,
3744
+ 'glas.museum' => 1,
3745
+ 'glass.museum' => 1,
3746
+ 'gorge.museum' => 1,
3747
+ 'grandrapids.museum' => 1,
3748
+ 'graz.museum' => 1,
3749
+ 'guernsey.museum' => 1,
3750
+ 'halloffame.museum' => 1,
3751
+ 'hamburg.museum' => 1,
3752
+ 'handson.museum' => 1,
3753
+ 'harvestcelebration.museum' => 1,
3754
+ 'hawaii.museum' => 1,
3755
+ 'health.museum' => 1,
3756
+ 'heimatunduhren.museum' => 1,
3757
+ 'hellas.museum' => 1,
3758
+ 'helsinki.museum' => 1,
3759
+ 'hembygdsforbund.museum' => 1,
3760
+ 'heritage.museum' => 1,
3761
+ 'histoire.museum' => 1,
3762
+ 'historical.museum' => 1,
3763
+ 'historicalsociety.museum' => 1,
3764
+ 'historichouses.museum' => 1,
3765
+ 'historisch.museum' => 1,
3766
+ 'historisches.museum' => 1,
3767
+ 'history.museum' => 1,
3768
+ 'historyofscience.museum' => 1,
3769
+ 'horology.museum' => 1,
3770
+ 'house.museum' => 1,
3771
+ 'humanities.museum' => 1,
3772
+ 'illustration.museum' => 1,
3773
+ 'imageandsound.museum' => 1,
3774
+ 'indian.museum' => 1,
3775
+ 'indiana.museum' => 1,
3776
+ 'indianapolis.museum' => 1,
3777
+ 'indianmarket.museum' => 1,
3778
+ 'intelligence.museum' => 1,
3779
+ 'interactive.museum' => 1,
3780
+ 'iraq.museum' => 1,
3781
+ 'iron.museum' => 1,
3782
+ 'isleofman.museum' => 1,
3783
+ 'jamison.museum' => 1,
3784
+ 'jefferson.museum' => 1,
3785
+ 'jerusalem.museum' => 1,
3786
+ 'jewelry.museum' => 1,
3787
+ 'jewish.museum' => 1,
3788
+ 'jewishart.museum' => 1,
3789
+ 'jfk.museum' => 1,
3790
+ 'journalism.museum' => 1,
3791
+ 'judaica.museum' => 1,
3792
+ 'judygarland.museum' => 1,
3793
+ 'juedisches.museum' => 1,
3794
+ 'juif.museum' => 1,
3795
+ 'karate.museum' => 1,
3796
+ 'karikatur.museum' => 1,
3797
+ 'kids.museum' => 1,
3798
+ 'koebenhavn.museum' => 1,
3799
+ 'koeln.museum' => 1,
3800
+ 'kunst.museum' => 1,
3801
+ 'kunstsammlung.museum' => 1,
3802
+ 'kunstunddesign.museum' => 1,
3803
+ 'labor.museum' => 1,
3804
+ 'labour.museum' => 1,
3805
+ 'lajolla.museum' => 1,
3806
+ 'lancashire.museum' => 1,
3807
+ 'landes.museum' => 1,
3808
+ 'lans.museum' => 1,
3809
+ 'läns.museum' => 1,
3810
+ 'larsson.museum' => 1,
3811
+ 'lewismiller.museum' => 1,
3812
+ 'lincoln.museum' => 1,
3813
+ 'linz.museum' => 1,
3814
+ 'living.museum' => 1,
3815
+ 'livinghistory.museum' => 1,
3816
+ 'localhistory.museum' => 1,
3817
+ 'london.museum' => 1,
3818
+ 'losangeles.museum' => 1,
3819
+ 'louvre.museum' => 1,
3820
+ 'loyalist.museum' => 1,
3821
+ 'lucerne.museum' => 1,
3822
+ 'luxembourg.museum' => 1,
3823
+ 'luzern.museum' => 1,
3824
+ 'mad.museum' => 1,
3825
+ 'madrid.museum' => 1,
3826
+ 'mallorca.museum' => 1,
3827
+ 'manchester.museum' => 1,
3828
+ 'mansion.museum' => 1,
3829
+ 'mansions.museum' => 1,
3830
+ 'manx.museum' => 1,
3831
+ 'marburg.museum' => 1,
3832
+ 'maritime.museum' => 1,
3833
+ 'maritimo.museum' => 1,
3834
+ 'maryland.museum' => 1,
3835
+ 'marylhurst.museum' => 1,
3836
+ 'media.museum' => 1,
3837
+ 'medical.museum' => 1,
3838
+ 'medizinhistorisches.museum' => 1,
3839
+ 'meeres.museum' => 1,
3840
+ 'memorial.museum' => 1,
3841
+ 'mesaverde.museum' => 1,
3842
+ 'michigan.museum' => 1,
3843
+ 'midatlantic.museum' => 1,
3844
+ 'military.museum' => 1,
3845
+ 'mill.museum' => 1,
3846
+ 'miners.museum' => 1,
3847
+ 'mining.museum' => 1,
3848
+ 'minnesota.museum' => 1,
3849
+ 'missile.museum' => 1,
3850
+ 'missoula.museum' => 1,
3851
+ 'modern.museum' => 1,
3852
+ 'moma.museum' => 1,
3853
+ 'money.museum' => 1,
3854
+ 'monmouth.museum' => 1,
3855
+ 'monticello.museum' => 1,
3856
+ 'montreal.museum' => 1,
3857
+ 'moscow.museum' => 1,
3858
+ 'motorcycle.museum' => 1,
3859
+ 'muenchen.museum' => 1,
3860
+ 'muenster.museum' => 1,
3861
+ 'mulhouse.museum' => 1,
3862
+ 'muncie.museum' => 1,
3863
+ 'museet.museum' => 1,
3864
+ 'museumcenter.museum' => 1,
3865
+ 'museumvereniging.museum' => 1,
3866
+ 'music.museum' => 1,
3867
+ 'national.museum' => 1,
3868
+ 'nationalfirearms.museum' => 1,
3869
+ 'nationalheritage.museum' => 1,
3870
+ 'nativeamerican.museum' => 1,
3871
+ 'naturalhistory.museum' => 1,
3872
+ 'naturalhistorymuseum.museum' => 1,
3873
+ 'naturalsciences.museum' => 1,
3874
+ 'nature.museum' => 1,
3875
+ 'naturhistorisches.museum' => 1,
3876
+ 'natuurwetenschappen.museum' => 1,
3877
+ 'naumburg.museum' => 1,
3878
+ 'naval.museum' => 1,
3879
+ 'nebraska.museum' => 1,
3880
+ 'neues.museum' => 1,
3881
+ 'newhampshire.museum' => 1,
3882
+ 'newjersey.museum' => 1,
3883
+ 'newmexico.museum' => 1,
3884
+ 'newport.museum' => 1,
3885
+ 'newspaper.museum' => 1,
3886
+ 'newyork.museum' => 1,
3887
+ 'niepce.museum' => 1,
3888
+ 'norfolk.museum' => 1,
3889
+ 'north.museum' => 1,
3890
+ 'nrw.museum' => 1,
3891
+ 'nuernberg.museum' => 1,
3892
+ 'nuremberg.museum' => 1,
3893
+ 'nyc.museum' => 1,
3894
+ 'nyny.museum' => 1,
3895
+ 'oceanographic.museum' => 1,
3896
+ 'oceanographique.museum' => 1,
3897
+ 'omaha.museum' => 1,
3898
+ 'online.museum' => 1,
3899
+ 'ontario.museum' => 1,
3900
+ 'openair.museum' => 1,
3901
+ 'oregon.museum' => 1,
3902
+ 'oregontrail.museum' => 1,
3903
+ 'otago.museum' => 1,
3904
+ 'oxford.museum' => 1,
3905
+ 'pacific.museum' => 1,
3906
+ 'paderborn.museum' => 1,
3907
+ 'palace.museum' => 1,
3908
+ 'paleo.museum' => 1,
3909
+ 'palmsprings.museum' => 1,
3910
+ 'panama.museum' => 1,
3911
+ 'paris.museum' => 1,
3912
+ 'pasadena.museum' => 1,
3913
+ 'pharmacy.museum' => 1,
3914
+ 'philadelphia.museum' => 1,
3915
+ 'philadelphiaarea.museum' => 1,
3916
+ 'philately.museum' => 1,
3917
+ 'phoenix.museum' => 1,
3918
+ 'photography.museum' => 1,
3919
+ 'pilots.museum' => 1,
3920
+ 'pittsburgh.museum' => 1,
3921
+ 'planetarium.museum' => 1,
3922
+ 'plantation.museum' => 1,
3923
+ 'plants.museum' => 1,
3924
+ 'plaza.museum' => 1,
3925
+ 'portal.museum' => 1,
3926
+ 'portland.museum' => 1,
3927
+ 'portlligat.museum' => 1,
3928
+ 'posts-and-telecommunications.museum' => 1,
3929
+ 'preservation.museum' => 1,
3930
+ 'presidio.museum' => 1,
3931
+ 'press.museum' => 1,
3932
+ 'project.museum' => 1,
3933
+ 'public.museum' => 1,
3934
+ 'pubol.museum' => 1,
3935
+ 'quebec.museum' => 1,
3936
+ 'railroad.museum' => 1,
3937
+ 'railway.museum' => 1,
3938
+ 'research.museum' => 1,
3939
+ 'resistance.museum' => 1,
3940
+ 'riodejaneiro.museum' => 1,
3941
+ 'rochester.museum' => 1,
3942
+ 'rockart.museum' => 1,
3943
+ 'roma.museum' => 1,
3944
+ 'russia.museum' => 1,
3945
+ 'saintlouis.museum' => 1,
3946
+ 'salem.museum' => 1,
3947
+ 'salvadordali.museum' => 1,
3948
+ 'salzburg.museum' => 1,
3949
+ 'sandiego.museum' => 1,
3950
+ 'sanfrancisco.museum' => 1,
3951
+ 'santabarbara.museum' => 1,
3952
+ 'santacruz.museum' => 1,
3953
+ 'santafe.museum' => 1,
3954
+ 'saskatchewan.museum' => 1,
3955
+ 'satx.museum' => 1,
3956
+ 'savannahga.museum' => 1,
3957
+ 'schlesisches.museum' => 1,
3958
+ 'schoenbrunn.museum' => 1,
3959
+ 'schokoladen.museum' => 1,
3960
+ 'school.museum' => 1,
3961
+ 'schweiz.museum' => 1,
3962
+ 'science.museum' => 1,
3963
+ 'scienceandhistory.museum' => 1,
3964
+ 'scienceandindustry.museum' => 1,
3965
+ 'sciencecenter.museum' => 1,
3966
+ 'sciencecenters.museum' => 1,
3967
+ 'science-fiction.museum' => 1,
3968
+ 'sciencehistory.museum' => 1,
3969
+ 'sciences.museum' => 1,
3970
+ 'sciencesnaturelles.museum' => 1,
3971
+ 'scotland.museum' => 1,
3972
+ 'seaport.museum' => 1,
3973
+ 'settlement.museum' => 1,
3974
+ 'settlers.museum' => 1,
3975
+ 'shell.museum' => 1,
3976
+ 'sherbrooke.museum' => 1,
3977
+ 'sibenik.museum' => 1,
3978
+ 'silk.museum' => 1,
3979
+ 'ski.museum' => 1,
3980
+ 'skole.museum' => 1,
3981
+ 'society.museum' => 1,
3982
+ 'sologne.museum' => 1,
3983
+ 'soundandvision.museum' => 1,
3984
+ 'southcarolina.museum' => 1,
3985
+ 'southwest.museum' => 1,
3986
+ 'space.museum' => 1,
3987
+ 'spy.museum' => 1,
3988
+ 'square.museum' => 1,
3989
+ 'stadt.museum' => 1,
3990
+ 'stalbans.museum' => 1,
3991
+ 'starnberg.museum' => 1,
3992
+ 'state.museum' => 1,
3993
+ 'stateofdelaware.museum' => 1,
3994
+ 'station.museum' => 1,
3995
+ 'steam.museum' => 1,
3996
+ 'steiermark.museum' => 1,
3997
+ 'stjohn.museum' => 1,
3998
+ 'stockholm.museum' => 1,
3999
+ 'stpetersburg.museum' => 1,
4000
+ 'stuttgart.museum' => 1,
4001
+ 'suisse.museum' => 1,
4002
+ 'surgeonshall.museum' => 1,
4003
+ 'surrey.museum' => 1,
4004
+ 'svizzera.museum' => 1,
4005
+ 'sweden.museum' => 1,
4006
+ 'sydney.museum' => 1,
4007
+ 'tank.museum' => 1,
4008
+ 'tcm.museum' => 1,
4009
+ 'technology.museum' => 1,
4010
+ 'telekommunikation.museum' => 1,
4011
+ 'television.museum' => 1,
4012
+ 'texas.museum' => 1,
4013
+ 'textile.museum' => 1,
4014
+ 'theater.museum' => 1,
4015
+ 'time.museum' => 1,
4016
+ 'timekeeping.museum' => 1,
4017
+ 'topology.museum' => 1,
4018
+ 'torino.museum' => 1,
4019
+ 'touch.museum' => 1,
4020
+ 'town.museum' => 1,
4021
+ 'transport.museum' => 1,
4022
+ 'tree.museum' => 1,
4023
+ 'trolley.museum' => 1,
4024
+ 'trust.museum' => 1,
4025
+ 'trustee.museum' => 1,
4026
+ 'uhren.museum' => 1,
4027
+ 'ulm.museum' => 1,
4028
+ 'undersea.museum' => 1,
4029
+ 'university.museum' => 1,
4030
+ 'usa.museum' => 1,
4031
+ 'usantiques.museum' => 1,
4032
+ 'usarts.museum' => 1,
4033
+ 'uscountryestate.museum' => 1,
4034
+ 'usculture.museum' => 1,
4035
+ 'usdecorativearts.museum' => 1,
4036
+ 'usgarden.museum' => 1,
4037
+ 'ushistory.museum' => 1,
4038
+ 'ushuaia.museum' => 1,
4039
+ 'uslivinghistory.museum' => 1,
4040
+ 'utah.museum' => 1,
4041
+ 'uvic.museum' => 1,
4042
+ 'valley.museum' => 1,
4043
+ 'vantaa.museum' => 1,
4044
+ 'versailles.museum' => 1,
4045
+ 'viking.museum' => 1,
4046
+ 'village.museum' => 1,
4047
+ 'virginia.museum' => 1,
4048
+ 'virtual.museum' => 1,
4049
+ 'virtuel.museum' => 1,
4050
+ 'vlaanderen.museum' => 1,
4051
+ 'volkenkunde.museum' => 1,
4052
+ 'wales.museum' => 1,
4053
+ 'wallonie.museum' => 1,
4054
+ 'war.museum' => 1,
4055
+ 'washingtondc.museum' => 1,
4056
+ 'watchandclock.museum' => 1,
4057
+ 'watch-and-clock.museum' => 1,
4058
+ 'western.museum' => 1,
4059
+ 'westfalen.museum' => 1,
4060
+ 'whaling.museum' => 1,
4061
+ 'wildlife.museum' => 1,
4062
+ 'williamsburg.museum' => 1,
4063
+ 'windmill.museum' => 1,
4064
+ 'workshop.museum' => 1,
4065
+ 'york.museum' => 1,
4066
+ 'yorkshire.museum' => 1,
4067
+ 'yosemite.museum' => 1,
4068
+ 'youth.museum' => 1,
4069
+ 'zoological.museum' => 1,
4070
+ 'zoology.museum' => 1,
4071
+ 'ירושלים.museum' => 1,
4072
+ 'иком.museum' => 1,
4073
+ 'mv' => 1,
4074
+ 'aero.mv' => 1,
4075
+ 'biz.mv' => 1,
4076
+ 'com.mv' => 1,
4077
+ 'coop.mv' => 1,
4078
+ 'edu.mv' => 1,
4079
+ 'gov.mv' => 1,
4080
+ 'info.mv' => 1,
4081
+ 'int.mv' => 1,
4082
+ 'mil.mv' => 1,
4083
+ 'museum.mv' => 1,
4084
+ 'name.mv' => 1,
4085
+ 'net.mv' => 1,
4086
+ 'org.mv' => 1,
4087
+ 'pro.mv' => 1,
4088
+ 'mw' => 1,
4089
+ 'ac.mw' => 1,
4090
+ 'biz.mw' => 1,
4091
+ 'co.mw' => 1,
4092
+ 'com.mw' => 1,
4093
+ 'coop.mw' => 1,
4094
+ 'edu.mw' => 1,
4095
+ 'gov.mw' => 1,
4096
+ 'int.mw' => 1,
4097
+ 'museum.mw' => 1,
4098
+ 'net.mw' => 1,
4099
+ 'org.mw' => 1,
4100
+ 'mx' => 1,
4101
+ 'com.mx' => 1,
4102
+ 'org.mx' => 1,
4103
+ 'gob.mx' => 1,
4104
+ 'edu.mx' => 1,
4105
+ 'net.mx' => 1,
4106
+ 'my' => 1,
4107
+ 'com.my' => 1,
4108
+ 'net.my' => 1,
4109
+ 'org.my' => 1,
4110
+ 'gov.my' => 1,
4111
+ 'edu.my' => 1,
4112
+ 'mil.my' => 1,
4113
+ 'name.my' => 1,
4114
+ 'mz' => 1,
4115
+ 'ac.mz' => 1,
4116
+ 'adv.mz' => 1,
4117
+ 'co.mz' => 1,
4118
+ 'edu.mz' => 1,
4119
+ 'gov.mz' => 1,
4120
+ 'mil.mz' => 1,
4121
+ 'net.mz' => 1,
4122
+ 'org.mz' => 1,
4123
+ 'na' => 1,
4124
+ 'info.na' => 1,
4125
+ 'pro.na' => 1,
4126
+ 'name.na' => 1,
4127
+ 'school.na' => 1,
4128
+ 'or.na' => 1,
4129
+ 'dr.na' => 1,
4130
+ 'us.na' => 1,
4131
+ 'mx.na' => 1,
4132
+ 'ca.na' => 1,
4133
+ 'in.na' => 1,
4134
+ 'cc.na' => 1,
4135
+ 'tv.na' => 1,
4136
+ 'ws.na' => 1,
4137
+ 'mobi.na' => 1,
4138
+ 'co.na' => 1,
4139
+ 'com.na' => 1,
4140
+ 'org.na' => 1,
4141
+ 'name' => 1,
4142
+ 'nc' => 1,
4143
+ 'asso.nc' => 1,
4144
+ 'nom.nc' => 1,
4145
+ 'ne' => 1,
4146
+ 'net' => 1,
4147
+ 'nf' => 1,
4148
+ 'com.nf' => 1,
4149
+ 'net.nf' => 1,
4150
+ 'per.nf' => 1,
4151
+ 'rec.nf' => 1,
4152
+ 'web.nf' => 1,
4153
+ 'arts.nf' => 1,
4154
+ 'firm.nf' => 1,
4155
+ 'info.nf' => 1,
4156
+ 'other.nf' => 1,
4157
+ 'store.nf' => 1,
4158
+ 'ng' => 1,
4159
+ 'com.ng' => 1,
4160
+ 'edu.ng' => 1,
4161
+ 'gov.ng' => 1,
4162
+ 'i.ng' => 1,
4163
+ 'mil.ng' => 1,
4164
+ 'mobi.ng' => 1,
4165
+ 'name.ng' => 1,
4166
+ 'net.ng' => 1,
4167
+ 'org.ng' => 1,
4168
+ 'sch.ng' => 1,
4169
+ 'ni' => 1,
4170
+ 'ac.ni' => 1,
4171
+ 'biz.ni' => 1,
4172
+ 'co.ni' => 1,
4173
+ 'com.ni' => 1,
4174
+ 'edu.ni' => 1,
4175
+ 'gob.ni' => 1,
4176
+ 'in.ni' => 1,
4177
+ 'info.ni' => 1,
4178
+ 'int.ni' => 1,
4179
+ 'mil.ni' => 1,
4180
+ 'net.ni' => 1,
4181
+ 'nom.ni' => 1,
4182
+ 'org.ni' => 1,
4183
+ 'web.ni' => 1,
4184
+ 'nl' => 1,
4185
+ 'no' => 1,
4186
+ 'fhs.no' => 1,
4187
+ 'vgs.no' => 1,
4188
+ 'fylkesbibl.no' => 1,
4189
+ 'folkebibl.no' => 1,
4190
+ 'museum.no' => 1,
4191
+ 'idrett.no' => 1,
4192
+ 'priv.no' => 1,
4193
+ 'mil.no' => 1,
4194
+ 'stat.no' => 1,
4195
+ 'dep.no' => 1,
4196
+ 'kommune.no' => 1,
4197
+ 'herad.no' => 1,
4198
+ 'aa.no' => 1,
4199
+ 'ah.no' => 1,
4200
+ 'bu.no' => 1,
4201
+ 'fm.no' => 1,
4202
+ 'hl.no' => 1,
4203
+ 'hm.no' => 1,
4204
+ 'jan-mayen.no' => 1,
4205
+ 'mr.no' => 1,
4206
+ 'nl.no' => 1,
4207
+ 'nt.no' => 1,
4208
+ 'of.no' => 1,
4209
+ 'ol.no' => 1,
4210
+ 'oslo.no' => 1,
4211
+ 'rl.no' => 1,
4212
+ 'sf.no' => 1,
4213
+ 'st.no' => 1,
4214
+ 'svalbard.no' => 1,
4215
+ 'tm.no' => 1,
4216
+ 'tr.no' => 1,
4217
+ 'va.no' => 1,
4218
+ 'vf.no' => 1,
4219
+ 'gs.aa.no' => 1,
4220
+ 'gs.ah.no' => 1,
4221
+ 'gs.bu.no' => 1,
4222
+ 'gs.fm.no' => 1,
4223
+ 'gs.hl.no' => 1,
4224
+ 'gs.hm.no' => 1,
4225
+ 'gs.jan-mayen.no' => 1,
4226
+ 'gs.mr.no' => 1,
4227
+ 'gs.nl.no' => 1,
4228
+ 'gs.nt.no' => 1,
4229
+ 'gs.of.no' => 1,
4230
+ 'gs.ol.no' => 1,
4231
+ 'gs.oslo.no' => 1,
4232
+ 'gs.rl.no' => 1,
4233
+ 'gs.sf.no' => 1,
4234
+ 'gs.st.no' => 1,
4235
+ 'gs.svalbard.no' => 1,
4236
+ 'gs.tm.no' => 1,
4237
+ 'gs.tr.no' => 1,
4238
+ 'gs.va.no' => 1,
4239
+ 'gs.vf.no' => 1,
4240
+ 'akrehamn.no' => 1,
4241
+ 'åkrehamn.no' => 1,
4242
+ 'algard.no' => 1,
4243
+ 'ålgård.no' => 1,
4244
+ 'arna.no' => 1,
4245
+ 'brumunddal.no' => 1,
4246
+ 'bryne.no' => 1,
4247
+ 'bronnoysund.no' => 1,
4248
+ 'brønnøysund.no' => 1,
4249
+ 'drobak.no' => 1,
4250
+ 'drøbak.no' => 1,
4251
+ 'egersund.no' => 1,
4252
+ 'fetsund.no' => 1,
4253
+ 'floro.no' => 1,
4254
+ 'florø.no' => 1,
4255
+ 'fredrikstad.no' => 1,
4256
+ 'hokksund.no' => 1,
4257
+ 'honefoss.no' => 1,
4258
+ 'hønefoss.no' => 1,
4259
+ 'jessheim.no' => 1,
4260
+ 'jorpeland.no' => 1,
4261
+ 'jørpeland.no' => 1,
4262
+ 'kirkenes.no' => 1,
4263
+ 'kopervik.no' => 1,
4264
+ 'krokstadelva.no' => 1,
4265
+ 'langevag.no' => 1,
4266
+ 'langevåg.no' => 1,
4267
+ 'leirvik.no' => 1,
4268
+ 'mjondalen.no' => 1,
4269
+ 'mjøndalen.no' => 1,
4270
+ 'mo-i-rana.no' => 1,
4271
+ 'mosjoen.no' => 1,
4272
+ 'mosjøen.no' => 1,
4273
+ 'nesoddtangen.no' => 1,
4274
+ 'orkanger.no' => 1,
4275
+ 'osoyro.no' => 1,
4276
+ 'osøyro.no' => 1,
4277
+ 'raholt.no' => 1,
4278
+ 'råholt.no' => 1,
4279
+ 'sandnessjoen.no' => 1,
4280
+ 'sandnessjøen.no' => 1,
4281
+ 'skedsmokorset.no' => 1,
4282
+ 'slattum.no' => 1,
4283
+ 'spjelkavik.no' => 1,
4284
+ 'stathelle.no' => 1,
4285
+ 'stavern.no' => 1,
4286
+ 'stjordalshalsen.no' => 1,
4287
+ 'stjørdalshalsen.no' => 1,
4288
+ 'tananger.no' => 1,
4289
+ 'tranby.no' => 1,
4290
+ 'vossevangen.no' => 1,
4291
+ 'afjord.no' => 1,
4292
+ 'åfjord.no' => 1,
4293
+ 'agdenes.no' => 1,
4294
+ 'al.no' => 1,
4295
+ 'ål.no' => 1,
4296
+ 'alesund.no' => 1,
4297
+ 'ålesund.no' => 1,
4298
+ 'alstahaug.no' => 1,
4299
+ 'alta.no' => 1,
4300
+ 'áltá.no' => 1,
4301
+ 'alaheadju.no' => 1,
4302
+ 'álaheadju.no' => 1,
4303
+ 'alvdal.no' => 1,
4304
+ 'amli.no' => 1,
4305
+ 'åmli.no' => 1,
4306
+ 'amot.no' => 1,
4307
+ 'åmot.no' => 1,
4308
+ 'andebu.no' => 1,
4309
+ 'andoy.no' => 1,
4310
+ 'andøy.no' => 1,
4311
+ 'andasuolo.no' => 1,
4312
+ 'ardal.no' => 1,
4313
+ 'årdal.no' => 1,
4314
+ 'aremark.no' => 1,
4315
+ 'arendal.no' => 1,
4316
+ 'ås.no' => 1,
4317
+ 'aseral.no' => 1,
4318
+ 'åseral.no' => 1,
4319
+ 'asker.no' => 1,
4320
+ 'askim.no' => 1,
4321
+ 'askvoll.no' => 1,
4322
+ 'askoy.no' => 1,
4323
+ 'askøy.no' => 1,
4324
+ 'asnes.no' => 1,
4325
+ 'åsnes.no' => 1,
4326
+ 'audnedaln.no' => 1,
4327
+ 'aukra.no' => 1,
4328
+ 'aure.no' => 1,
4329
+ 'aurland.no' => 1,
4330
+ 'aurskog-holand.no' => 1,
4331
+ 'aurskog-høland.no' => 1,
4332
+ 'austevoll.no' => 1,
4333
+ 'austrheim.no' => 1,
4334
+ 'averoy.no' => 1,
4335
+ 'averøy.no' => 1,
4336
+ 'balestrand.no' => 1,
4337
+ 'ballangen.no' => 1,
4338
+ 'balat.no' => 1,
4339
+ 'bálát.no' => 1,
4340
+ 'balsfjord.no' => 1,
4341
+ 'bahccavuotna.no' => 1,
4342
+ 'báhccavuotna.no' => 1,
4343
+ 'bamble.no' => 1,
4344
+ 'bardu.no' => 1,
4345
+ 'beardu.no' => 1,
4346
+ 'beiarn.no' => 1,
4347
+ 'bajddar.no' => 1,
4348
+ 'bájddar.no' => 1,
4349
+ 'baidar.no' => 1,
4350
+ 'báidár.no' => 1,
4351
+ 'berg.no' => 1,
4352
+ 'bergen.no' => 1,
4353
+ 'berlevag.no' => 1,
4354
+ 'berlevåg.no' => 1,
4355
+ 'bearalvahki.no' => 1,
4356
+ 'bearalváhki.no' => 1,
4357
+ 'bindal.no' => 1,
4358
+ 'birkenes.no' => 1,
4359
+ 'bjarkoy.no' => 1,
4360
+ 'bjarkøy.no' => 1,
4361
+ 'bjerkreim.no' => 1,
4362
+ 'bjugn.no' => 1,
4363
+ 'bodo.no' => 1,
4364
+ 'bodø.no' => 1,
4365
+ 'badaddja.no' => 1,
4366
+ 'bådåddjå.no' => 1,
4367
+ 'budejju.no' => 1,
4368
+ 'bokn.no' => 1,
4369
+ 'bremanger.no' => 1,
4370
+ 'bronnoy.no' => 1,
4371
+ 'brønnøy.no' => 1,
4372
+ 'bygland.no' => 1,
4373
+ 'bykle.no' => 1,
4374
+ 'barum.no' => 1,
4375
+ 'bærum.no' => 1,
4376
+ 'bo.telemark.no' => 1,
4377
+ 'bø.telemark.no' => 1,
4378
+ 'bo.nordland.no' => 1,
4379
+ 'bø.nordland.no' => 1,
4380
+ 'bievat.no' => 1,
4381
+ 'bievát.no' => 1,
4382
+ 'bomlo.no' => 1,
4383
+ 'bømlo.no' => 1,
4384
+ 'batsfjord.no' => 1,
4385
+ 'båtsfjord.no' => 1,
4386
+ 'bahcavuotna.no' => 1,
4387
+ 'báhcavuotna.no' => 1,
4388
+ 'dovre.no' => 1,
4389
+ 'drammen.no' => 1,
4390
+ 'drangedal.no' => 1,
4391
+ 'dyroy.no' => 1,
4392
+ 'dyrøy.no' => 1,
4393
+ 'donna.no' => 1,
4394
+ 'dønna.no' => 1,
4395
+ 'eid.no' => 1,
4396
+ 'eidfjord.no' => 1,
4397
+ 'eidsberg.no' => 1,
4398
+ 'eidskog.no' => 1,
4399
+ 'eidsvoll.no' => 1,
4400
+ 'eigersund.no' => 1,
4401
+ 'elverum.no' => 1,
4402
+ 'enebakk.no' => 1,
4403
+ 'engerdal.no' => 1,
4404
+ 'etne.no' => 1,
4405
+ 'etnedal.no' => 1,
4406
+ 'evenes.no' => 1,
4407
+ 'evenassi.no' => 1,
4408
+ 'evenášši.no' => 1,
4409
+ 'evje-og-hornnes.no' => 1,
4410
+ 'farsund.no' => 1,
4411
+ 'fauske.no' => 1,
4412
+ 'fuossko.no' => 1,
4413
+ 'fuoisku.no' => 1,
4414
+ 'fedje.no' => 1,
4415
+ 'fet.no' => 1,
4416
+ 'finnoy.no' => 1,
4417
+ 'finnøy.no' => 1,
4418
+ 'fitjar.no' => 1,
4419
+ 'fjaler.no' => 1,
4420
+ 'fjell.no' => 1,
4421
+ 'flakstad.no' => 1,
4422
+ 'flatanger.no' => 1,
4423
+ 'flekkefjord.no' => 1,
4424
+ 'flesberg.no' => 1,
4425
+ 'flora.no' => 1,
4426
+ 'fla.no' => 1,
4427
+ 'flå.no' => 1,
4428
+ 'folldal.no' => 1,
4429
+ 'forsand.no' => 1,
4430
+ 'fosnes.no' => 1,
4431
+ 'frei.no' => 1,
4432
+ 'frogn.no' => 1,
4433
+ 'froland.no' => 1,
4434
+ 'frosta.no' => 1,
4435
+ 'frana.no' => 1,
4436
+ 'fræna.no' => 1,
4437
+ 'froya.no' => 1,
4438
+ 'frøya.no' => 1,
4439
+ 'fusa.no' => 1,
4440
+ 'fyresdal.no' => 1,
4441
+ 'forde.no' => 1,
4442
+ 'førde.no' => 1,
4443
+ 'gamvik.no' => 1,
4444
+ 'gangaviika.no' => 1,
4445
+ 'gáŋgaviika.no' => 1,
4446
+ 'gaular.no' => 1,
4447
+ 'gausdal.no' => 1,
4448
+ 'gildeskal.no' => 1,
4449
+ 'gildeskål.no' => 1,
4450
+ 'giske.no' => 1,
4451
+ 'gjemnes.no' => 1,
4452
+ 'gjerdrum.no' => 1,
4453
+ 'gjerstad.no' => 1,
4454
+ 'gjesdal.no' => 1,
4455
+ 'gjovik.no' => 1,
4456
+ 'gjøvik.no' => 1,
4457
+ 'gloppen.no' => 1,
4458
+ 'gol.no' => 1,
4459
+ 'gran.no' => 1,
4460
+ 'grane.no' => 1,
4461
+ 'granvin.no' => 1,
4462
+ 'gratangen.no' => 1,
4463
+ 'grimstad.no' => 1,
4464
+ 'grong.no' => 1,
4465
+ 'kraanghke.no' => 1,
4466
+ 'kråanghke.no' => 1,
4467
+ 'grue.no' => 1,
4468
+ 'gulen.no' => 1,
4469
+ 'hadsel.no' => 1,
4470
+ 'halden.no' => 1,
4471
+ 'halsa.no' => 1,
4472
+ 'hamar.no' => 1,
4473
+ 'hamaroy.no' => 1,
4474
+ 'habmer.no' => 1,
4475
+ 'hábmer.no' => 1,
4476
+ 'hapmir.no' => 1,
4477
+ 'hápmir.no' => 1,
4478
+ 'hammerfest.no' => 1,
4479
+ 'hammarfeasta.no' => 1,
4480
+ 'hámmárfeasta.no' => 1,
4481
+ 'haram.no' => 1,
4482
+ 'hareid.no' => 1,
4483
+ 'harstad.no' => 1,
4484
+ 'hasvik.no' => 1,
4485
+ 'aknoluokta.no' => 1,
4486
+ 'ákŋoluokta.no' => 1,
4487
+ 'hattfjelldal.no' => 1,
4488
+ 'aarborte.no' => 1,
4489
+ 'haugesund.no' => 1,
4490
+ 'hemne.no' => 1,
4491
+ 'hemnes.no' => 1,
4492
+ 'hemsedal.no' => 1,
4493
+ 'heroy.more-og-romsdal.no' => 1,
4494
+ 'herøy.møre-og-romsdal.no' => 1,
4495
+ 'heroy.nordland.no' => 1,
4496
+ 'herøy.nordland.no' => 1,
4497
+ 'hitra.no' => 1,
4498
+ 'hjartdal.no' => 1,
4499
+ 'hjelmeland.no' => 1,
4500
+ 'hobol.no' => 1,
4501
+ 'hobøl.no' => 1,
4502
+ 'hof.no' => 1,
4503
+ 'hol.no' => 1,
4504
+ 'hole.no' => 1,
4505
+ 'holmestrand.no' => 1,
4506
+ 'holtalen.no' => 1,
4507
+ 'holtålen.no' => 1,
4508
+ 'hornindal.no' => 1,
4509
+ 'horten.no' => 1,
4510
+ 'hurdal.no' => 1,
4511
+ 'hurum.no' => 1,
4512
+ 'hvaler.no' => 1,
4513
+ 'hyllestad.no' => 1,
4514
+ 'hagebostad.no' => 1,
4515
+ 'hægebostad.no' => 1,
4516
+ 'hoyanger.no' => 1,
4517
+ 'høyanger.no' => 1,
4518
+ 'hoylandet.no' => 1,
4519
+ 'høylandet.no' => 1,
4520
+ 'ha.no' => 1,
4521
+ 'hå.no' => 1,
4522
+ 'ibestad.no' => 1,
4523
+ 'inderoy.no' => 1,
4524
+ 'inderøy.no' => 1,
4525
+ 'iveland.no' => 1,
4526
+ 'jevnaker.no' => 1,
4527
+ 'jondal.no' => 1,
4528
+ 'jolster.no' => 1,
4529
+ 'jølster.no' => 1,
4530
+ 'karasjok.no' => 1,
4531
+ 'karasjohka.no' => 1,
4532
+ 'kárášjohka.no' => 1,
4533
+ 'karlsoy.no' => 1,
4534
+ 'galsa.no' => 1,
4535
+ 'gálsá.no' => 1,
4536
+ 'karmoy.no' => 1,
4537
+ 'karmøy.no' => 1,
4538
+ 'kautokeino.no' => 1,
4539
+ 'guovdageaidnu.no' => 1,
4540
+ 'klepp.no' => 1,
4541
+ 'klabu.no' => 1,
4542
+ 'klæbu.no' => 1,
4543
+ 'kongsberg.no' => 1,
4544
+ 'kongsvinger.no' => 1,
4545
+ 'kragero.no' => 1,
4546
+ 'kragerø.no' => 1,
4547
+ 'kristiansand.no' => 1,
4548
+ 'kristiansund.no' => 1,
4549
+ 'krodsherad.no' => 1,
4550
+ 'krødsherad.no' => 1,
4551
+ 'kvalsund.no' => 1,
4552
+ 'rahkkeravju.no' => 1,
4553
+ 'ráhkkerávju.no' => 1,
4554
+ 'kvam.no' => 1,
4555
+ 'kvinesdal.no' => 1,
4556
+ 'kvinnherad.no' => 1,
4557
+ 'kviteseid.no' => 1,
4558
+ 'kvitsoy.no' => 1,
4559
+ 'kvitsøy.no' => 1,
4560
+ 'kvafjord.no' => 1,
4561
+ 'kvæfjord.no' => 1,
4562
+ 'giehtavuoatna.no' => 1,
4563
+ 'kvanangen.no' => 1,
4564
+ 'kvænangen.no' => 1,
4565
+ 'navuotna.no' => 1,
4566
+ 'návuotna.no' => 1,
4567
+ 'kafjord.no' => 1,
4568
+ 'kåfjord.no' => 1,
4569
+ 'gaivuotna.no' => 1,
4570
+ 'gáivuotna.no' => 1,
4571
+ 'larvik.no' => 1,
4572
+ 'lavangen.no' => 1,
4573
+ 'lavagis.no' => 1,
4574
+ 'loabat.no' => 1,
4575
+ 'loabát.no' => 1,
4576
+ 'lebesby.no' => 1,
4577
+ 'davvesiida.no' => 1,
4578
+ 'leikanger.no' => 1,
4579
+ 'leirfjord.no' => 1,
4580
+ 'leka.no' => 1,
4581
+ 'leksvik.no' => 1,
4582
+ 'lenvik.no' => 1,
4583
+ 'leangaviika.no' => 1,
4584
+ 'leaŋgaviika.no' => 1,
4585
+ 'lesja.no' => 1,
4586
+ 'levanger.no' => 1,
4587
+ 'lier.no' => 1,
4588
+ 'lierne.no' => 1,
4589
+ 'lillehammer.no' => 1,
4590
+ 'lillesand.no' => 1,
4591
+ 'lindesnes.no' => 1,
4592
+ 'lindas.no' => 1,
4593
+ 'lindås.no' => 1,
4594
+ 'lom.no' => 1,
4595
+ 'loppa.no' => 1,
4596
+ 'lahppi.no' => 1,
4597
+ 'láhppi.no' => 1,
4598
+ 'lund.no' => 1,
4599
+ 'lunner.no' => 1,
4600
+ 'luroy.no' => 1,
4601
+ 'lurøy.no' => 1,
4602
+ 'luster.no' => 1,
4603
+ 'lyngdal.no' => 1,
4604
+ 'lyngen.no' => 1,
4605
+ 'ivgu.no' => 1,
4606
+ 'lardal.no' => 1,
4607
+ 'lerdal.no' => 1,
4608
+ 'lærdal.no' => 1,
4609
+ 'lodingen.no' => 1,
4610
+ 'lødingen.no' => 1,
4611
+ 'lorenskog.no' => 1,
4612
+ 'lørenskog.no' => 1,
4613
+ 'loten.no' => 1,
4614
+ 'løten.no' => 1,
4615
+ 'malvik.no' => 1,
4616
+ 'masoy.no' => 1,
4617
+ 'måsøy.no' => 1,
4618
+ 'muosat.no' => 1,
4619
+ 'muosát.no' => 1,
4620
+ 'mandal.no' => 1,
4621
+ 'marker.no' => 1,
4622
+ 'marnardal.no' => 1,
4623
+ 'masfjorden.no' => 1,
4624
+ 'meland.no' => 1,
4625
+ 'meldal.no' => 1,
4626
+ 'melhus.no' => 1,
4627
+ 'meloy.no' => 1,
4628
+ 'meløy.no' => 1,
4629
+ 'meraker.no' => 1,
4630
+ 'meråker.no' => 1,
4631
+ 'moareke.no' => 1,
4632
+ 'moåreke.no' => 1,
4633
+ 'midsund.no' => 1,
4634
+ 'midtre-gauldal.no' => 1,
4635
+ 'modalen.no' => 1,
4636
+ 'modum.no' => 1,
4637
+ 'molde.no' => 1,
4638
+ 'moskenes.no' => 1,
4639
+ 'moss.no' => 1,
4640
+ 'mosvik.no' => 1,
4641
+ 'malselv.no' => 1,
4642
+ 'målselv.no' => 1,
4643
+ 'malatvuopmi.no' => 1,
4644
+ 'málatvuopmi.no' => 1,
4645
+ 'namdalseid.no' => 1,
4646
+ 'aejrie.no' => 1,
4647
+ 'namsos.no' => 1,
4648
+ 'namsskogan.no' => 1,
4649
+ 'naamesjevuemie.no' => 1,
4650
+ 'nååmesjevuemie.no' => 1,
4651
+ 'laakesvuemie.no' => 1,
4652
+ 'nannestad.no' => 1,
4653
+ 'narvik.no' => 1,
4654
+ 'narviika.no' => 1,
4655
+ 'naustdal.no' => 1,
4656
+ 'nedre-eiker.no' => 1,
4657
+ 'nes.akershus.no' => 1,
4658
+ 'nes.buskerud.no' => 1,
4659
+ 'nesna.no' => 1,
4660
+ 'nesodden.no' => 1,
4661
+ 'nesseby.no' => 1,
4662
+ 'unjarga.no' => 1,
4663
+ 'unjárga.no' => 1,
4664
+ 'nesset.no' => 1,
4665
+ 'nissedal.no' => 1,
4666
+ 'nittedal.no' => 1,
4667
+ 'nord-aurdal.no' => 1,
4668
+ 'nord-fron.no' => 1,
4669
+ 'nord-odal.no' => 1,
4670
+ 'norddal.no' => 1,
4671
+ 'nordkapp.no' => 1,
4672
+ 'davvenjarga.no' => 1,
4673
+ 'davvenjárga.no' => 1,
4674
+ 'nordre-land.no' => 1,
4675
+ 'nordreisa.no' => 1,
4676
+ 'raisa.no' => 1,
4677
+ 'ráisa.no' => 1,
4678
+ 'nore-og-uvdal.no' => 1,
4679
+ 'notodden.no' => 1,
4680
+ 'naroy.no' => 1,
4681
+ 'nærøy.no' => 1,
4682
+ 'notteroy.no' => 1,
4683
+ 'nøtterøy.no' => 1,
4684
+ 'odda.no' => 1,
4685
+ 'oksnes.no' => 1,
4686
+ 'øksnes.no' => 1,
4687
+ 'oppdal.no' => 1,
4688
+ 'oppegard.no' => 1,
4689
+ 'oppegård.no' => 1,
4690
+ 'orkdal.no' => 1,
4691
+ 'orland.no' => 1,
4692
+ 'ørland.no' => 1,
4693
+ 'orskog.no' => 1,
4694
+ 'ørskog.no' => 1,
4695
+ 'orsta.no' => 1,
4696
+ 'ørsta.no' => 1,
4697
+ 'os.hedmark.no' => 1,
4698
+ 'os.hordaland.no' => 1,
4699
+ 'osen.no' => 1,
4700
+ 'osteroy.no' => 1,
4701
+ 'osterøy.no' => 1,
4702
+ 'ostre-toten.no' => 1,
4703
+ 'østre-toten.no' => 1,
4704
+ 'overhalla.no' => 1,
4705
+ 'ovre-eiker.no' => 1,
4706
+ 'øvre-eiker.no' => 1,
4707
+ 'oyer.no' => 1,
4708
+ 'øyer.no' => 1,
4709
+ 'oygarden.no' => 1,
4710
+ 'øygarden.no' => 1,
4711
+ 'oystre-slidre.no' => 1,
4712
+ 'øystre-slidre.no' => 1,
4713
+ 'porsanger.no' => 1,
4714
+ 'porsangu.no' => 1,
4715
+ 'porsáŋgu.no' => 1,
4716
+ 'porsgrunn.no' => 1,
4717
+ 'radoy.no' => 1,
4718
+ 'radøy.no' => 1,
4719
+ 'rakkestad.no' => 1,
4720
+ 'rana.no' => 1,
4721
+ 'ruovat.no' => 1,
4722
+ 'randaberg.no' => 1,
4723
+ 'rauma.no' => 1,
4724
+ 'rendalen.no' => 1,
4725
+ 'rennebu.no' => 1,
4726
+ 'rennesoy.no' => 1,
4727
+ 'rennesøy.no' => 1,
4728
+ 'rindal.no' => 1,
4729
+ 'ringebu.no' => 1,
4730
+ 'ringerike.no' => 1,
4731
+ 'ringsaker.no' => 1,
4732
+ 'rissa.no' => 1,
4733
+ 'risor.no' => 1,
4734
+ 'risør.no' => 1,
4735
+ 'roan.no' => 1,
4736
+ 'rollag.no' => 1,
4737
+ 'rygge.no' => 1,
4738
+ 'ralingen.no' => 1,
4739
+ 'rælingen.no' => 1,
4740
+ 'rodoy.no' => 1,
4741
+ 'rødøy.no' => 1,
4742
+ 'romskog.no' => 1,
4743
+ 'rømskog.no' => 1,
4744
+ 'roros.no' => 1,
4745
+ 'røros.no' => 1,
4746
+ 'rost.no' => 1,
4747
+ 'røst.no' => 1,
4748
+ 'royken.no' => 1,
4749
+ 'røyken.no' => 1,
4750
+ 'royrvik.no' => 1,
4751
+ 'røyrvik.no' => 1,
4752
+ 'rade.no' => 1,
4753
+ 'råde.no' => 1,
4754
+ 'salangen.no' => 1,
4755
+ 'siellak.no' => 1,
4756
+ 'saltdal.no' => 1,
4757
+ 'salat.no' => 1,
4758
+ 'sálát.no' => 1,
4759
+ 'sálat.no' => 1,
4760
+ 'samnanger.no' => 1,
4761
+ 'sande.more-og-romsdal.no' => 1,
4762
+ 'sande.møre-og-romsdal.no' => 1,
4763
+ 'sande.vestfold.no' => 1,
4764
+ 'sandefjord.no' => 1,
4765
+ 'sandnes.no' => 1,
4766
+ 'sandoy.no' => 1,
4767
+ 'sandøy.no' => 1,
4768
+ 'sarpsborg.no' => 1,
4769
+ 'sauda.no' => 1,
4770
+ 'sauherad.no' => 1,
4771
+ 'sel.no' => 1,
4772
+ 'selbu.no' => 1,
4773
+ 'selje.no' => 1,
4774
+ 'seljord.no' => 1,
4775
+ 'sigdal.no' => 1,
4776
+ 'siljan.no' => 1,
4777
+ 'sirdal.no' => 1,
4778
+ 'skaun.no' => 1,
4779
+ 'skedsmo.no' => 1,
4780
+ 'ski.no' => 1,
4781
+ 'skien.no' => 1,
4782
+ 'skiptvet.no' => 1,
4783
+ 'skjervoy.no' => 1,
4784
+ 'skjervøy.no' => 1,
4785
+ 'skierva.no' => 1,
4786
+ 'skiervá.no' => 1,
4787
+ 'skjak.no' => 1,
4788
+ 'skjåk.no' => 1,
4789
+ 'skodje.no' => 1,
4790
+ 'skanland.no' => 1,
4791
+ 'skånland.no' => 1,
4792
+ 'skanit.no' => 1,
4793
+ 'skánit.no' => 1,
4794
+ 'smola.no' => 1,
4795
+ 'smøla.no' => 1,
4796
+ 'snillfjord.no' => 1,
4797
+ 'snasa.no' => 1,
4798
+ 'snåsa.no' => 1,
4799
+ 'snoasa.no' => 1,
4800
+ 'snaase.no' => 1,
4801
+ 'snåase.no' => 1,
4802
+ 'sogndal.no' => 1,
4803
+ 'sokndal.no' => 1,
4804
+ 'sola.no' => 1,
4805
+ 'solund.no' => 1,
4806
+ 'songdalen.no' => 1,
4807
+ 'sortland.no' => 1,
4808
+ 'spydeberg.no' => 1,
4809
+ 'stange.no' => 1,
4810
+ 'stavanger.no' => 1,
4811
+ 'steigen.no' => 1,
4812
+ 'steinkjer.no' => 1,
4813
+ 'stjordal.no' => 1,
4814
+ 'stjørdal.no' => 1,
4815
+ 'stokke.no' => 1,
4816
+ 'stor-elvdal.no' => 1,
4817
+ 'stord.no' => 1,
4818
+ 'stordal.no' => 1,
4819
+ 'storfjord.no' => 1,
4820
+ 'omasvuotna.no' => 1,
4821
+ 'strand.no' => 1,
4822
+ 'stranda.no' => 1,
4823
+ 'stryn.no' => 1,
4824
+ 'sula.no' => 1,
4825
+ 'suldal.no' => 1,
4826
+ 'sund.no' => 1,
4827
+ 'sunndal.no' => 1,
4828
+ 'surnadal.no' => 1,
4829
+ 'sveio.no' => 1,
4830
+ 'svelvik.no' => 1,
4831
+ 'sykkylven.no' => 1,
4832
+ 'sogne.no' => 1,
4833
+ 'søgne.no' => 1,
4834
+ 'somna.no' => 1,
4835
+ 'sømna.no' => 1,
4836
+ 'sondre-land.no' => 1,
4837
+ 'søndre-land.no' => 1,
4838
+ 'sor-aurdal.no' => 1,
4839
+ 'sør-aurdal.no' => 1,
4840
+ 'sor-fron.no' => 1,
4841
+ 'sør-fron.no' => 1,
4842
+ 'sor-odal.no' => 1,
4843
+ 'sør-odal.no' => 1,
4844
+ 'sor-varanger.no' => 1,
4845
+ 'sør-varanger.no' => 1,
4846
+ 'matta-varjjat.no' => 1,
4847
+ 'mátta-várjjat.no' => 1,
4848
+ 'sorfold.no' => 1,
4849
+ 'sørfold.no' => 1,
4850
+ 'sorreisa.no' => 1,
4851
+ 'sørreisa.no' => 1,
4852
+ 'sorum.no' => 1,
4853
+ 'sørum.no' => 1,
4854
+ 'tana.no' => 1,
4855
+ 'deatnu.no' => 1,
4856
+ 'time.no' => 1,
4857
+ 'tingvoll.no' => 1,
4858
+ 'tinn.no' => 1,
4859
+ 'tjeldsund.no' => 1,
4860
+ 'dielddanuorri.no' => 1,
4861
+ 'tjome.no' => 1,
4862
+ 'tjøme.no' => 1,
4863
+ 'tokke.no' => 1,
4864
+ 'tolga.no' => 1,
4865
+ 'torsken.no' => 1,
4866
+ 'tranoy.no' => 1,
4867
+ 'tranøy.no' => 1,
4868
+ 'tromso.no' => 1,
4869
+ 'tromsø.no' => 1,
4870
+ 'tromsa.no' => 1,
4871
+ 'romsa.no' => 1,
4872
+ 'trondheim.no' => 1,
4873
+ 'troandin.no' => 1,
4874
+ 'trysil.no' => 1,
4875
+ 'trana.no' => 1,
4876
+ 'træna.no' => 1,
4877
+ 'trogstad.no' => 1,
4878
+ 'trøgstad.no' => 1,
4879
+ 'tvedestrand.no' => 1,
4880
+ 'tydal.no' => 1,
4881
+ 'tynset.no' => 1,
4882
+ 'tysfjord.no' => 1,
4883
+ 'divtasvuodna.no' => 1,
4884
+ 'divttasvuotna.no' => 1,
4885
+ 'tysnes.no' => 1,
4886
+ 'tysvar.no' => 1,
4887
+ 'tysvær.no' => 1,
4888
+ 'tonsberg.no' => 1,
4889
+ 'tønsberg.no' => 1,
4890
+ 'ullensaker.no' => 1,
4891
+ 'ullensvang.no' => 1,
4892
+ 'ulvik.no' => 1,
4893
+ 'utsira.no' => 1,
4894
+ 'vadso.no' => 1,
4895
+ 'vadsø.no' => 1,
4896
+ 'cahcesuolo.no' => 1,
4897
+ 'čáhcesuolo.no' => 1,
4898
+ 'vaksdal.no' => 1,
4899
+ 'valle.no' => 1,
4900
+ 'vang.no' => 1,
4901
+ 'vanylven.no' => 1,
4902
+ 'vardo.no' => 1,
4903
+ 'vardø.no' => 1,
4904
+ 'varggat.no' => 1,
4905
+ 'várggát.no' => 1,
4906
+ 'vefsn.no' => 1,
4907
+ 'vaapste.no' => 1,
4908
+ 'vega.no' => 1,
4909
+ 'vegarshei.no' => 1,
4910
+ 'vegårshei.no' => 1,
4911
+ 'vennesla.no' => 1,
4912
+ 'verdal.no' => 1,
4913
+ 'verran.no' => 1,
4914
+ 'vestby.no' => 1,
4915
+ 'vestnes.no' => 1,
4916
+ 'vestre-slidre.no' => 1,
4917
+ 'vestre-toten.no' => 1,
4918
+ 'vestvagoy.no' => 1,
4919
+ 'vestvågøy.no' => 1,
4920
+ 'vevelstad.no' => 1,
4921
+ 'vik.no' => 1,
4922
+ 'vikna.no' => 1,
4923
+ 'vindafjord.no' => 1,
4924
+ 'volda.no' => 1,
4925
+ 'voss.no' => 1,
4926
+ 'varoy.no' => 1,
4927
+ 'værøy.no' => 1,
4928
+ 'vagan.no' => 1,
4929
+ 'vågan.no' => 1,
4930
+ 'voagat.no' => 1,
4931
+ 'vagsoy.no' => 1,
4932
+ 'vågsøy.no' => 1,
4933
+ 'vaga.no' => 1,
4934
+ 'vågå.no' => 1,
4935
+ 'valer.ostfold.no' => 1,
4936
+ 'våler.østfold.no' => 1,
4937
+ 'valer.hedmark.no' => 1,
4938
+ 'våler.hedmark.no' => 1,
4939
+ '*.np' => 1,
4940
+ 'nr' => 1,
4941
+ 'biz.nr' => 1,
4942
+ 'info.nr' => 1,
4943
+ 'gov.nr' => 1,
4944
+ 'edu.nr' => 1,
4945
+ 'org.nr' => 1,
4946
+ 'net.nr' => 1,
4947
+ 'com.nr' => 1,
4948
+ 'nu' => 1,
4949
+ 'nz' => 1,
4950
+ 'ac.nz' => 1,
4951
+ 'co.nz' => 1,
4952
+ 'cri.nz' => 1,
4953
+ 'geek.nz' => 1,
4954
+ 'gen.nz' => 1,
4955
+ 'govt.nz' => 1,
4956
+ 'health.nz' => 1,
4957
+ 'iwi.nz' => 1,
4958
+ 'kiwi.nz' => 1,
4959
+ 'maori.nz' => 1,
4960
+ 'mil.nz' => 1,
4961
+ 'māori.nz' => 1,
4962
+ 'net.nz' => 1,
4963
+ 'org.nz' => 1,
4964
+ 'parliament.nz' => 1,
4965
+ 'school.nz' => 1,
4966
+ 'om' => 1,
4967
+ 'co.om' => 1,
4968
+ 'com.om' => 1,
4969
+ 'edu.om' => 1,
4970
+ 'gov.om' => 1,
4971
+ 'med.om' => 1,
4972
+ 'museum.om' => 1,
4973
+ 'net.om' => 1,
4974
+ 'org.om' => 1,
4975
+ 'pro.om' => 1,
4976
+ 'onion' => 1,
4977
+ 'org' => 1,
4978
+ 'pa' => 1,
4979
+ 'ac.pa' => 1,
4980
+ 'gob.pa' => 1,
4981
+ 'com.pa' => 1,
4982
+ 'org.pa' => 1,
4983
+ 'sld.pa' => 1,
4984
+ 'edu.pa' => 1,
4985
+ 'net.pa' => 1,
4986
+ 'ing.pa' => 1,
4987
+ 'abo.pa' => 1,
4988
+ 'med.pa' => 1,
4989
+ 'nom.pa' => 1,
4990
+ 'pe' => 1,
4991
+ 'edu.pe' => 1,
4992
+ 'gob.pe' => 1,
4993
+ 'nom.pe' => 1,
4994
+ 'mil.pe' => 1,
4995
+ 'org.pe' => 1,
4996
+ 'com.pe' => 1,
4997
+ 'net.pe' => 1,
4998
+ 'pf' => 1,
4999
+ 'com.pf' => 1,
5000
+ 'org.pf' => 1,
5001
+ 'edu.pf' => 1,
5002
+ '*.pg' => 1,
5003
+ 'ph' => 1,
5004
+ 'com.ph' => 1,
5005
+ 'net.ph' => 1,
5006
+ 'org.ph' => 1,
5007
+ 'gov.ph' => 1,
5008
+ 'edu.ph' => 1,
5009
+ 'ngo.ph' => 1,
5010
+ 'mil.ph' => 1,
5011
+ 'i.ph' => 1,
5012
+ 'pk' => 1,
5013
+ 'com.pk' => 1,
5014
+ 'net.pk' => 1,
5015
+ 'edu.pk' => 1,
5016
+ 'org.pk' => 1,
5017
+ 'fam.pk' => 1,
5018
+ 'biz.pk' => 1,
5019
+ 'web.pk' => 1,
5020
+ 'gov.pk' => 1,
5021
+ 'gob.pk' => 1,
5022
+ 'gok.pk' => 1,
5023
+ 'gon.pk' => 1,
5024
+ 'gop.pk' => 1,
5025
+ 'gos.pk' => 1,
5026
+ 'info.pk' => 1,
5027
+ 'pl' => 1,
5028
+ 'com.pl' => 1,
5029
+ 'net.pl' => 1,
5030
+ 'org.pl' => 1,
5031
+ 'aid.pl' => 1,
5032
+ 'agro.pl' => 1,
5033
+ 'atm.pl' => 1,
5034
+ 'auto.pl' => 1,
5035
+ 'biz.pl' => 1,
5036
+ 'edu.pl' => 1,
5037
+ 'gmina.pl' => 1,
5038
+ 'gsm.pl' => 1,
5039
+ 'info.pl' => 1,
5040
+ 'mail.pl' => 1,
5041
+ 'miasta.pl' => 1,
5042
+ 'media.pl' => 1,
5043
+ 'mil.pl' => 1,
5044
+ 'nieruchomosci.pl' => 1,
5045
+ 'nom.pl' => 1,
5046
+ 'pc.pl' => 1,
5047
+ 'powiat.pl' => 1,
5048
+ 'priv.pl' => 1,
5049
+ 'realestate.pl' => 1,
5050
+ 'rel.pl' => 1,
5051
+ 'sex.pl' => 1,
5052
+ 'shop.pl' => 1,
5053
+ 'sklep.pl' => 1,
5054
+ 'sos.pl' => 1,
5055
+ 'szkola.pl' => 1,
5056
+ 'targi.pl' => 1,
5057
+ 'tm.pl' => 1,
5058
+ 'tourism.pl' => 1,
5059
+ 'travel.pl' => 1,
5060
+ 'turystyka.pl' => 1,
5061
+ 'gov.pl' => 1,
5062
+ 'ap.gov.pl' => 1,
5063
+ 'ic.gov.pl' => 1,
5064
+ 'is.gov.pl' => 1,
5065
+ 'us.gov.pl' => 1,
5066
+ 'kmpsp.gov.pl' => 1,
5067
+ 'kppsp.gov.pl' => 1,
5068
+ 'kwpsp.gov.pl' => 1,
5069
+ 'psp.gov.pl' => 1,
5070
+ 'wskr.gov.pl' => 1,
5071
+ 'kwp.gov.pl' => 1,
5072
+ 'mw.gov.pl' => 1,
5073
+ 'ug.gov.pl' => 1,
5074
+ 'um.gov.pl' => 1,
5075
+ 'umig.gov.pl' => 1,
5076
+ 'ugim.gov.pl' => 1,
5077
+ 'upow.gov.pl' => 1,
5078
+ 'uw.gov.pl' => 1,
5079
+ 'starostwo.gov.pl' => 1,
5080
+ 'pa.gov.pl' => 1,
5081
+ 'po.gov.pl' => 1,
5082
+ 'psse.gov.pl' => 1,
5083
+ 'pup.gov.pl' => 1,
5084
+ 'rzgw.gov.pl' => 1,
5085
+ 'sa.gov.pl' => 1,
5086
+ 'so.gov.pl' => 1,
5087
+ 'sr.gov.pl' => 1,
5088
+ 'wsa.gov.pl' => 1,
5089
+ 'sko.gov.pl' => 1,
5090
+ 'uzs.gov.pl' => 1,
5091
+ 'wiih.gov.pl' => 1,
5092
+ 'winb.gov.pl' => 1,
5093
+ 'pinb.gov.pl' => 1,
5094
+ 'wios.gov.pl' => 1,
5095
+ 'witd.gov.pl' => 1,
5096
+ 'wzmiuw.gov.pl' => 1,
5097
+ 'piw.gov.pl' => 1,
5098
+ 'wiw.gov.pl' => 1,
5099
+ 'griw.gov.pl' => 1,
5100
+ 'wif.gov.pl' => 1,
5101
+ 'oum.gov.pl' => 1,
5102
+ 'sdn.gov.pl' => 1,
5103
+ 'zp.gov.pl' => 1,
5104
+ 'uppo.gov.pl' => 1,
5105
+ 'mup.gov.pl' => 1,
5106
+ 'wuoz.gov.pl' => 1,
5107
+ 'konsulat.gov.pl' => 1,
5108
+ 'oirm.gov.pl' => 1,
5109
+ 'augustow.pl' => 1,
5110
+ 'babia-gora.pl' => 1,
5111
+ 'bedzin.pl' => 1,
5112
+ 'beskidy.pl' => 1,
5113
+ 'bialowieza.pl' => 1,
5114
+ 'bialystok.pl' => 1,
5115
+ 'bielawa.pl' => 1,
5116
+ 'bieszczady.pl' => 1,
5117
+ 'boleslawiec.pl' => 1,
5118
+ 'bydgoszcz.pl' => 1,
5119
+ 'bytom.pl' => 1,
5120
+ 'cieszyn.pl' => 1,
5121
+ 'czeladz.pl' => 1,
5122
+ 'czest.pl' => 1,
5123
+ 'dlugoleka.pl' => 1,
5124
+ 'elblag.pl' => 1,
5125
+ 'elk.pl' => 1,
5126
+ 'glogow.pl' => 1,
5127
+ 'gniezno.pl' => 1,
5128
+ 'gorlice.pl' => 1,
5129
+ 'grajewo.pl' => 1,
5130
+ 'ilawa.pl' => 1,
5131
+ 'jaworzno.pl' => 1,
5132
+ 'jelenia-gora.pl' => 1,
5133
+ 'jgora.pl' => 1,
5134
+ 'kalisz.pl' => 1,
5135
+ 'kazimierz-dolny.pl' => 1,
5136
+ 'karpacz.pl' => 1,
5137
+ 'kartuzy.pl' => 1,
5138
+ 'kaszuby.pl' => 1,
5139
+ 'katowice.pl' => 1,
5140
+ 'kepno.pl' => 1,
5141
+ 'ketrzyn.pl' => 1,
5142
+ 'klodzko.pl' => 1,
5143
+ 'kobierzyce.pl' => 1,
5144
+ 'kolobrzeg.pl' => 1,
5145
+ 'konin.pl' => 1,
5146
+ 'konskowola.pl' => 1,
5147
+ 'kutno.pl' => 1,
5148
+ 'lapy.pl' => 1,
5149
+ 'lebork.pl' => 1,
5150
+ 'legnica.pl' => 1,
5151
+ 'lezajsk.pl' => 1,
5152
+ 'limanowa.pl' => 1,
5153
+ 'lomza.pl' => 1,
5154
+ 'lowicz.pl' => 1,
5155
+ 'lubin.pl' => 1,
5156
+ 'lukow.pl' => 1,
5157
+ 'malbork.pl' => 1,
5158
+ 'malopolska.pl' => 1,
5159
+ 'mazowsze.pl' => 1,
5160
+ 'mazury.pl' => 1,
5161
+ 'mielec.pl' => 1,
5162
+ 'mielno.pl' => 1,
5163
+ 'mragowo.pl' => 1,
5164
+ 'naklo.pl' => 1,
5165
+ 'nowaruda.pl' => 1,
5166
+ 'nysa.pl' => 1,
5167
+ 'olawa.pl' => 1,
5168
+ 'olecko.pl' => 1,
5169
+ 'olkusz.pl' => 1,
5170
+ 'olsztyn.pl' => 1,
5171
+ 'opoczno.pl' => 1,
5172
+ 'opole.pl' => 1,
5173
+ 'ostroda.pl' => 1,
5174
+ 'ostroleka.pl' => 1,
5175
+ 'ostrowiec.pl' => 1,
5176
+ 'ostrowwlkp.pl' => 1,
5177
+ 'pila.pl' => 1,
5178
+ 'pisz.pl' => 1,
5179
+ 'podhale.pl' => 1,
5180
+ 'podlasie.pl' => 1,
5181
+ 'polkowice.pl' => 1,
5182
+ 'pomorze.pl' => 1,
5183
+ 'pomorskie.pl' => 1,
5184
+ 'prochowice.pl' => 1,
5185
+ 'pruszkow.pl' => 1,
5186
+ 'przeworsk.pl' => 1,
5187
+ 'pulawy.pl' => 1,
5188
+ 'radom.pl' => 1,
5189
+ 'rawa-maz.pl' => 1,
5190
+ 'rybnik.pl' => 1,
5191
+ 'rzeszow.pl' => 1,
5192
+ 'sanok.pl' => 1,
5193
+ 'sejny.pl' => 1,
5194
+ 'slask.pl' => 1,
5195
+ 'slupsk.pl' => 1,
5196
+ 'sosnowiec.pl' => 1,
5197
+ 'stalowa-wola.pl' => 1,
5198
+ 'skoczow.pl' => 1,
5199
+ 'starachowice.pl' => 1,
5200
+ 'stargard.pl' => 1,
5201
+ 'suwalki.pl' => 1,
5202
+ 'swidnica.pl' => 1,
5203
+ 'swiebodzin.pl' => 1,
5204
+ 'swinoujscie.pl' => 1,
5205
+ 'szczecin.pl' => 1,
5206
+ 'szczytno.pl' => 1,
5207
+ 'tarnobrzeg.pl' => 1,
5208
+ 'tgory.pl' => 1,
5209
+ 'turek.pl' => 1,
5210
+ 'tychy.pl' => 1,
5211
+ 'ustka.pl' => 1,
5212
+ 'walbrzych.pl' => 1,
5213
+ 'warmia.pl' => 1,
5214
+ 'warszawa.pl' => 1,
5215
+ 'waw.pl' => 1,
5216
+ 'wegrow.pl' => 1,
5217
+ 'wielun.pl' => 1,
5218
+ 'wlocl.pl' => 1,
5219
+ 'wloclawek.pl' => 1,
5220
+ 'wodzislaw.pl' => 1,
5221
+ 'wolomin.pl' => 1,
5222
+ 'wroclaw.pl' => 1,
5223
+ 'zachpomor.pl' => 1,
5224
+ 'zagan.pl' => 1,
5225
+ 'zarow.pl' => 1,
5226
+ 'zgora.pl' => 1,
5227
+ 'zgorzelec.pl' => 1,
5228
+ 'pm' => 1,
5229
+ 'pn' => 1,
5230
+ 'gov.pn' => 1,
5231
+ 'co.pn' => 1,
5232
+ 'org.pn' => 1,
5233
+ 'edu.pn' => 1,
5234
+ 'net.pn' => 1,
5235
+ 'post' => 1,
5236
+ 'pr' => 1,
5237
+ 'com.pr' => 1,
5238
+ 'net.pr' => 1,
5239
+ 'org.pr' => 1,
5240
+ 'gov.pr' => 1,
5241
+ 'edu.pr' => 1,
5242
+ 'isla.pr' => 1,
5243
+ 'pro.pr' => 1,
5244
+ 'biz.pr' => 1,
5245
+ 'info.pr' => 1,
5246
+ 'name.pr' => 1,
5247
+ 'est.pr' => 1,
5248
+ 'prof.pr' => 1,
5249
+ 'ac.pr' => 1,
5250
+ 'pro' => 1,
5251
+ 'aaa.pro' => 1,
5252
+ 'aca.pro' => 1,
5253
+ 'acct.pro' => 1,
5254
+ 'avocat.pro' => 1,
5255
+ 'bar.pro' => 1,
5256
+ 'cpa.pro' => 1,
5257
+ 'eng.pro' => 1,
5258
+ 'jur.pro' => 1,
5259
+ 'law.pro' => 1,
5260
+ 'med.pro' => 1,
5261
+ 'recht.pro' => 1,
5262
+ 'ps' => 1,
5263
+ 'edu.ps' => 1,
5264
+ 'gov.ps' => 1,
5265
+ 'sec.ps' => 1,
5266
+ 'plo.ps' => 1,
5267
+ 'com.ps' => 1,
5268
+ 'org.ps' => 1,
5269
+ 'net.ps' => 1,
5270
+ 'pt' => 1,
5271
+ 'net.pt' => 1,
5272
+ 'gov.pt' => 1,
5273
+ 'org.pt' => 1,
5274
+ 'edu.pt' => 1,
5275
+ 'int.pt' => 1,
5276
+ 'publ.pt' => 1,
5277
+ 'com.pt' => 1,
5278
+ 'nome.pt' => 1,
5279
+ 'pw' => 1,
5280
+ 'co.pw' => 1,
5281
+ 'ne.pw' => 1,
5282
+ 'or.pw' => 1,
5283
+ 'ed.pw' => 1,
5284
+ 'go.pw' => 1,
5285
+ 'belau.pw' => 1,
5286
+ 'py' => 1,
5287
+ 'com.py' => 1,
5288
+ 'coop.py' => 1,
5289
+ 'edu.py' => 1,
5290
+ 'gov.py' => 1,
5291
+ 'mil.py' => 1,
5292
+ 'net.py' => 1,
5293
+ 'org.py' => 1,
5294
+ 'qa' => 1,
5295
+ 'com.qa' => 1,
5296
+ 'edu.qa' => 1,
5297
+ 'gov.qa' => 1,
5298
+ 'mil.qa' => 1,
5299
+ 'name.qa' => 1,
5300
+ 'net.qa' => 1,
5301
+ 'org.qa' => 1,
5302
+ 'sch.qa' => 1,
5303
+ 're' => 1,
5304
+ 'asso.re' => 1,
5305
+ 'com.re' => 1,
5306
+ 'nom.re' => 1,
5307
+ 'ro' => 1,
5308
+ 'arts.ro' => 1,
5309
+ 'com.ro' => 1,
5310
+ 'firm.ro' => 1,
5311
+ 'info.ro' => 1,
5312
+ 'nom.ro' => 1,
5313
+ 'nt.ro' => 1,
5314
+ 'org.ro' => 1,
5315
+ 'rec.ro' => 1,
5316
+ 'store.ro' => 1,
5317
+ 'tm.ro' => 1,
5318
+ 'www.ro' => 1,
5319
+ 'rs' => 1,
5320
+ 'ac.rs' => 1,
5321
+ 'co.rs' => 1,
5322
+ 'edu.rs' => 1,
5323
+ 'gov.rs' => 1,
5324
+ 'in.rs' => 1,
5325
+ 'org.rs' => 1,
5326
+ 'ru' => 1,
5327
+ 'ac.ru' => 1,
5328
+ 'edu.ru' => 1,
5329
+ 'gov.ru' => 1,
5330
+ 'int.ru' => 1,
5331
+ 'mil.ru' => 1,
5332
+ 'test.ru' => 1,
5333
+ 'rw' => 1,
5334
+ 'ac.rw' => 1,
5335
+ 'co.rw' => 1,
5336
+ 'coop.rw' => 1,
5337
+ 'gov.rw' => 1,
5338
+ 'mil.rw' => 1,
5339
+ 'net.rw' => 1,
5340
+ 'org.rw' => 1,
5341
+ 'sa' => 1,
5342
+ 'com.sa' => 1,
5343
+ 'net.sa' => 1,
5344
+ 'org.sa' => 1,
5345
+ 'gov.sa' => 1,
5346
+ 'med.sa' => 1,
5347
+ 'pub.sa' => 1,
5348
+ 'edu.sa' => 1,
5349
+ 'sch.sa' => 1,
5350
+ 'sb' => 1,
5351
+ 'com.sb' => 1,
5352
+ 'edu.sb' => 1,
5353
+ 'gov.sb' => 1,
5354
+ 'net.sb' => 1,
5355
+ 'org.sb' => 1,
5356
+ 'sc' => 1,
5357
+ 'com.sc' => 1,
5358
+ 'gov.sc' => 1,
5359
+ 'net.sc' => 1,
5360
+ 'org.sc' => 1,
5361
+ 'edu.sc' => 1,
5362
+ 'sd' => 1,
5363
+ 'com.sd' => 1,
5364
+ 'net.sd' => 1,
5365
+ 'org.sd' => 1,
5366
+ 'edu.sd' => 1,
5367
+ 'med.sd' => 1,
5368
+ 'tv.sd' => 1,
5369
+ 'gov.sd' => 1,
5370
+ 'info.sd' => 1,
5371
+ 'se' => 1,
5372
+ 'a.se' => 1,
5373
+ 'ac.se' => 1,
5374
+ 'b.se' => 1,
5375
+ 'bd.se' => 1,
5376
+ 'brand.se' => 1,
5377
+ 'c.se' => 1,
5378
+ 'd.se' => 1,
5379
+ 'e.se' => 1,
5380
+ 'f.se' => 1,
5381
+ 'fh.se' => 1,
5382
+ 'fhsk.se' => 1,
5383
+ 'fhv.se' => 1,
5384
+ 'g.se' => 1,
5385
+ 'h.se' => 1,
5386
+ 'i.se' => 1,
5387
+ 'k.se' => 1,
5388
+ 'komforb.se' => 1,
5389
+ 'kommunalforbund.se' => 1,
5390
+ 'komvux.se' => 1,
5391
+ 'l.se' => 1,
5392
+ 'lanbib.se' => 1,
5393
+ 'm.se' => 1,
5394
+ 'n.se' => 1,
5395
+ 'naturbruksgymn.se' => 1,
5396
+ 'o.se' => 1,
5397
+ 'org.se' => 1,
5398
+ 'p.se' => 1,
5399
+ 'parti.se' => 1,
5400
+ 'pp.se' => 1,
5401
+ 'press.se' => 1,
5402
+ 'r.se' => 1,
5403
+ 's.se' => 1,
5404
+ 't.se' => 1,
5405
+ 'tm.se' => 1,
5406
+ 'u.se' => 1,
5407
+ 'w.se' => 1,
5408
+ 'x.se' => 1,
5409
+ 'y.se' => 1,
5410
+ 'z.se' => 1,
5411
+ 'sg' => 1,
5412
+ 'com.sg' => 1,
5413
+ 'net.sg' => 1,
5414
+ 'org.sg' => 1,
5415
+ 'gov.sg' => 1,
5416
+ 'edu.sg' => 1,
5417
+ 'per.sg' => 1,
5418
+ 'sh' => 1,
5419
+ 'com.sh' => 1,
5420
+ 'net.sh' => 1,
5421
+ 'gov.sh' => 1,
5422
+ 'org.sh' => 1,
5423
+ 'mil.sh' => 1,
5424
+ 'si' => 1,
5425
+ 'sj' => 1,
5426
+ 'sk' => 1,
5427
+ 'sl' => 1,
5428
+ 'com.sl' => 1,
5429
+ 'net.sl' => 1,
5430
+ 'edu.sl' => 1,
5431
+ 'gov.sl' => 1,
5432
+ 'org.sl' => 1,
5433
+ 'sm' => 1,
5434
+ 'sn' => 1,
5435
+ 'art.sn' => 1,
5436
+ 'com.sn' => 1,
5437
+ 'edu.sn' => 1,
5438
+ 'gouv.sn' => 1,
5439
+ 'org.sn' => 1,
5440
+ 'perso.sn' => 1,
5441
+ 'univ.sn' => 1,
5442
+ 'so' => 1,
5443
+ 'com.so' => 1,
5444
+ 'net.so' => 1,
5445
+ 'org.so' => 1,
5446
+ 'sr' => 1,
5447
+ 'st' => 1,
5448
+ 'co.st' => 1,
5449
+ 'com.st' => 1,
5450
+ 'consulado.st' => 1,
5451
+ 'edu.st' => 1,
5452
+ 'embaixada.st' => 1,
5453
+ 'gov.st' => 1,
5454
+ 'mil.st' => 1,
5455
+ 'net.st' => 1,
5456
+ 'org.st' => 1,
5457
+ 'principe.st' => 1,
5458
+ 'saotome.st' => 1,
5459
+ 'store.st' => 1,
5460
+ 'su' => 1,
5461
+ 'sv' => 1,
5462
+ 'com.sv' => 1,
5463
+ 'edu.sv' => 1,
5464
+ 'gob.sv' => 1,
5465
+ 'org.sv' => 1,
5466
+ 'red.sv' => 1,
5467
+ 'sx' => 1,
5468
+ 'gov.sx' => 1,
5469
+ 'sy' => 1,
5470
+ 'edu.sy' => 1,
5471
+ 'gov.sy' => 1,
5472
+ 'net.sy' => 1,
5473
+ 'mil.sy' => 1,
5474
+ 'com.sy' => 1,
5475
+ 'org.sy' => 1,
5476
+ 'sz' => 1,
5477
+ 'co.sz' => 1,
5478
+ 'ac.sz' => 1,
5479
+ 'org.sz' => 1,
5480
+ 'tc' => 1,
5481
+ 'td' => 1,
5482
+ 'tel' => 1,
5483
+ 'tf' => 1,
5484
+ 'tg' => 1,
5485
+ 'th' => 1,
5486
+ 'ac.th' => 1,
5487
+ 'co.th' => 1,
5488
+ 'go.th' => 1,
5489
+ 'in.th' => 1,
5490
+ 'mi.th' => 1,
5491
+ 'net.th' => 1,
5492
+ 'or.th' => 1,
5493
+ 'tj' => 1,
5494
+ 'ac.tj' => 1,
5495
+ 'biz.tj' => 1,
5496
+ 'co.tj' => 1,
5497
+ 'com.tj' => 1,
5498
+ 'edu.tj' => 1,
5499
+ 'go.tj' => 1,
5500
+ 'gov.tj' => 1,
5501
+ 'int.tj' => 1,
5502
+ 'mil.tj' => 1,
5503
+ 'name.tj' => 1,
5504
+ 'net.tj' => 1,
5505
+ 'nic.tj' => 1,
5506
+ 'org.tj' => 1,
5507
+ 'test.tj' => 1,
5508
+ 'web.tj' => 1,
5509
+ 'tk' => 1,
5510
+ 'tl' => 1,
5511
+ 'gov.tl' => 1,
5512
+ 'tm' => 1,
5513
+ 'com.tm' => 1,
5514
+ 'co.tm' => 1,
5515
+ 'org.tm' => 1,
5516
+ 'net.tm' => 1,
5517
+ 'nom.tm' => 1,
5518
+ 'gov.tm' => 1,
5519
+ 'mil.tm' => 1,
5520
+ 'edu.tm' => 1,
5521
+ 'tn' => 1,
5522
+ 'com.tn' => 1,
5523
+ 'ens.tn' => 1,
5524
+ 'fin.tn' => 1,
5525
+ 'gov.tn' => 1,
5526
+ 'ind.tn' => 1,
5527
+ 'intl.tn' => 1,
5528
+ 'nat.tn' => 1,
5529
+ 'net.tn' => 1,
5530
+ 'org.tn' => 1,
5531
+ 'info.tn' => 1,
5532
+ 'perso.tn' => 1,
5533
+ 'tourism.tn' => 1,
5534
+ 'edunet.tn' => 1,
5535
+ 'rnrt.tn' => 1,
5536
+ 'rns.tn' => 1,
5537
+ 'rnu.tn' => 1,
5538
+ 'mincom.tn' => 1,
5539
+ 'agrinet.tn' => 1,
5540
+ 'defense.tn' => 1,
5541
+ 'turen.tn' => 1,
5542
+ 'to' => 1,
5543
+ 'com.to' => 1,
5544
+ 'gov.to' => 1,
5545
+ 'net.to' => 1,
5546
+ 'org.to' => 1,
5547
+ 'edu.to' => 1,
5548
+ 'mil.to' => 1,
5549
+ 'tr' => 1,
5550
+ 'av.tr' => 1,
5551
+ 'bbs.tr' => 1,
5552
+ 'bel.tr' => 1,
5553
+ 'biz.tr' => 1,
5554
+ 'com.tr' => 1,
5555
+ 'dr.tr' => 1,
5556
+ 'edu.tr' => 1,
5557
+ 'gen.tr' => 1,
5558
+ 'gov.tr' => 1,
5559
+ 'info.tr' => 1,
5560
+ 'mil.tr' => 1,
5561
+ 'k12.tr' => 1,
5562
+ 'kep.tr' => 1,
5563
+ 'name.tr' => 1,
5564
+ 'net.tr' => 1,
5565
+ 'org.tr' => 1,
5566
+ 'pol.tr' => 1,
5567
+ 'tel.tr' => 1,
5568
+ 'tsk.tr' => 1,
5569
+ 'tv.tr' => 1,
5570
+ 'web.tr' => 1,
5571
+ 'nc.tr' => 1,
5572
+ 'gov.nc.tr' => 1,
5573
+ 'tt' => 1,
5574
+ 'co.tt' => 1,
5575
+ 'com.tt' => 1,
5576
+ 'org.tt' => 1,
5577
+ 'net.tt' => 1,
5578
+ 'biz.tt' => 1,
5579
+ 'info.tt' => 1,
5580
+ 'pro.tt' => 1,
5581
+ 'int.tt' => 1,
5582
+ 'coop.tt' => 1,
5583
+ 'jobs.tt' => 1,
5584
+ 'mobi.tt' => 1,
5585
+ 'travel.tt' => 1,
5586
+ 'museum.tt' => 1,
5587
+ 'aero.tt' => 1,
5588
+ 'name.tt' => 1,
5589
+ 'gov.tt' => 1,
5590
+ 'edu.tt' => 1,
5591
+ 'tv' => 1,
5592
+ 'tw' => 1,
5593
+ 'edu.tw' => 1,
5594
+ 'gov.tw' => 1,
5595
+ 'mil.tw' => 1,
5596
+ 'com.tw' => 1,
5597
+ 'net.tw' => 1,
5598
+ 'org.tw' => 1,
5599
+ 'idv.tw' => 1,
5600
+ 'game.tw' => 1,
5601
+ 'ebiz.tw' => 1,
5602
+ 'club.tw' => 1,
5603
+ '網路.tw' => 1,
5604
+ '組織.tw' => 1,
5605
+ '商業.tw' => 1,
5606
+ 'tz' => 1,
5607
+ 'ac.tz' => 1,
5608
+ 'co.tz' => 1,
5609
+ 'go.tz' => 1,
5610
+ 'hotel.tz' => 1,
5611
+ 'info.tz' => 1,
5612
+ 'me.tz' => 1,
5613
+ 'mil.tz' => 1,
5614
+ 'mobi.tz' => 1,
5615
+ 'ne.tz' => 1,
5616
+ 'or.tz' => 1,
5617
+ 'sc.tz' => 1,
5618
+ 'tv.tz' => 1,
5619
+ 'ua' => 1,
5620
+ 'com.ua' => 1,
5621
+ 'edu.ua' => 1,
5622
+ 'gov.ua' => 1,
5623
+ 'in.ua' => 1,
5624
+ 'net.ua' => 1,
5625
+ 'org.ua' => 1,
5626
+ 'cherkassy.ua' => 1,
5627
+ 'cherkasy.ua' => 1,
5628
+ 'chernigov.ua' => 1,
5629
+ 'chernihiv.ua' => 1,
5630
+ 'chernivtsi.ua' => 1,
5631
+ 'chernovtsy.ua' => 1,
5632
+ 'ck.ua' => 1,
5633
+ 'cn.ua' => 1,
5634
+ 'cr.ua' => 1,
5635
+ 'crimea.ua' => 1,
5636
+ 'cv.ua' => 1,
5637
+ 'dn.ua' => 1,
5638
+ 'dnepropetrovsk.ua' => 1,
5639
+ 'dnipropetrovsk.ua' => 1,
5640
+ 'dominic.ua' => 1,
5641
+ 'donetsk.ua' => 1,
5642
+ 'dp.ua' => 1,
5643
+ 'if.ua' => 1,
5644
+ 'ivano-frankivsk.ua' => 1,
5645
+ 'kh.ua' => 1,
5646
+ 'kharkiv.ua' => 1,
5647
+ 'kharkov.ua' => 1,
5648
+ 'kherson.ua' => 1,
5649
+ 'khmelnitskiy.ua' => 1,
5650
+ 'khmelnytskyi.ua' => 1,
5651
+ 'kiev.ua' => 1,
5652
+ 'kirovograd.ua' => 1,
5653
+ 'km.ua' => 1,
5654
+ 'kr.ua' => 1,
5655
+ 'krym.ua' => 1,
5656
+ 'ks.ua' => 1,
5657
+ 'kv.ua' => 1,
5658
+ 'kyiv.ua' => 1,
5659
+ 'lg.ua' => 1,
5660
+ 'lt.ua' => 1,
5661
+ 'lugansk.ua' => 1,
5662
+ 'lutsk.ua' => 1,
5663
+ 'lv.ua' => 1,
5664
+ 'lviv.ua' => 1,
5665
+ 'mk.ua' => 1,
5666
+ 'mykolaiv.ua' => 1,
5667
+ 'nikolaev.ua' => 1,
5668
+ 'od.ua' => 1,
5669
+ 'odesa.ua' => 1,
5670
+ 'odessa.ua' => 1,
5671
+ 'pl.ua' => 1,
5672
+ 'poltava.ua' => 1,
5673
+ 'rivne.ua' => 1,
5674
+ 'rovno.ua' => 1,
5675
+ 'rv.ua' => 1,
5676
+ 'sb.ua' => 1,
5677
+ 'sebastopol.ua' => 1,
5678
+ 'sevastopol.ua' => 1,
5679
+ 'sm.ua' => 1,
5680
+ 'sumy.ua' => 1,
5681
+ 'te.ua' => 1,
5682
+ 'ternopil.ua' => 1,
5683
+ 'uz.ua' => 1,
5684
+ 'uzhgorod.ua' => 1,
5685
+ 'vinnica.ua' => 1,
5686
+ 'vinnytsia.ua' => 1,
5687
+ 'vn.ua' => 1,
5688
+ 'volyn.ua' => 1,
5689
+ 'yalta.ua' => 1,
5690
+ 'zaporizhzhe.ua' => 1,
5691
+ 'zaporizhzhia.ua' => 1,
5692
+ 'zhitomir.ua' => 1,
5693
+ 'zhytomyr.ua' => 1,
5694
+ 'zp.ua' => 1,
5695
+ 'zt.ua' => 1,
5696
+ 'ug' => 1,
5697
+ 'co.ug' => 1,
5698
+ 'or.ug' => 1,
5699
+ 'ac.ug' => 1,
5700
+ 'sc.ug' => 1,
5701
+ 'go.ug' => 1,
5702
+ 'ne.ug' => 1,
5703
+ 'com.ug' => 1,
5704
+ 'org.ug' => 1,
5705
+ 'uk' => 1,
5706
+ 'ac.uk' => 1,
5707
+ 'co.uk' => 1,
5708
+ 'gov.uk' => 1,
5709
+ 'ltd.uk' => 1,
5710
+ 'me.uk' => 1,
5711
+ 'net.uk' => 1,
5712
+ 'nhs.uk' => 1,
5713
+ 'org.uk' => 1,
5714
+ 'plc.uk' => 1,
5715
+ 'police.uk' => 1,
5716
+ '*.sch.uk' => 1,
5717
+ 'us' => 1,
5718
+ 'dni.us' => 1,
5719
+ 'fed.us' => 1,
5720
+ 'isa.us' => 1,
5721
+ 'kids.us' => 1,
5722
+ 'nsn.us' => 1,
5723
+ 'ak.us' => 1,
5724
+ 'al.us' => 1,
5725
+ 'ar.us' => 1,
5726
+ 'as.us' => 1,
5727
+ 'az.us' => 1,
5728
+ 'ca.us' => 1,
5729
+ 'co.us' => 1,
5730
+ 'ct.us' => 1,
5731
+ 'dc.us' => 1,
5732
+ 'de.us' => 1,
5733
+ 'fl.us' => 1,
5734
+ 'ga.us' => 1,
5735
+ 'gu.us' => 1,
5736
+ 'hi.us' => 1,
5737
+ 'ia.us' => 1,
5738
+ 'id.us' => 1,
5739
+ 'il.us' => 1,
5740
+ 'in.us' => 1,
5741
+ 'ks.us' => 1,
5742
+ 'ky.us' => 1,
5743
+ 'la.us' => 1,
5744
+ 'ma.us' => 1,
5745
+ 'md.us' => 1,
5746
+ 'me.us' => 1,
5747
+ 'mi.us' => 1,
5748
+ 'mn.us' => 1,
5749
+ 'mo.us' => 1,
5750
+ 'ms.us' => 1,
5751
+ 'mt.us' => 1,
5752
+ 'nc.us' => 1,
5753
+ 'nd.us' => 1,
5754
+ 'ne.us' => 1,
5755
+ 'nh.us' => 1,
5756
+ 'nj.us' => 1,
5757
+ 'nm.us' => 1,
5758
+ 'nv.us' => 1,
5759
+ 'ny.us' => 1,
5760
+ 'oh.us' => 1,
5761
+ 'ok.us' => 1,
5762
+ 'or.us' => 1,
5763
+ 'pa.us' => 1,
5764
+ 'pr.us' => 1,
5765
+ 'ri.us' => 1,
5766
+ 'sc.us' => 1,
5767
+ 'sd.us' => 1,
5768
+ 'tn.us' => 1,
5769
+ 'tx.us' => 1,
5770
+ 'ut.us' => 1,
5771
+ 'vi.us' => 1,
5772
+ 'vt.us' => 1,
5773
+ 'va.us' => 1,
5774
+ 'wa.us' => 1,
5775
+ 'wi.us' => 1,
5776
+ 'wv.us' => 1,
5777
+ 'wy.us' => 1,
5778
+ 'k12.ak.us' => 1,
5779
+ 'k12.al.us' => 1,
5780
+ 'k12.ar.us' => 1,
5781
+ 'k12.as.us' => 1,
5782
+ 'k12.az.us' => 1,
5783
+ 'k12.ca.us' => 1,
5784
+ 'k12.co.us' => 1,
5785
+ 'k12.ct.us' => 1,
5786
+ 'k12.dc.us' => 1,
5787
+ 'k12.de.us' => 1,
5788
+ 'k12.fl.us' => 1,
5789
+ 'k12.ga.us' => 1,
5790
+ 'k12.gu.us' => 1,
5791
+ 'k12.ia.us' => 1,
5792
+ 'k12.id.us' => 1,
5793
+ 'k12.il.us' => 1,
5794
+ 'k12.in.us' => 1,
5795
+ 'k12.ks.us' => 1,
5796
+ 'k12.ky.us' => 1,
5797
+ 'k12.la.us' => 1,
5798
+ 'k12.ma.us' => 1,
5799
+ 'k12.md.us' => 1,
5800
+ 'k12.me.us' => 1,
5801
+ 'k12.mi.us' => 1,
5802
+ 'k12.mn.us' => 1,
5803
+ 'k12.mo.us' => 1,
5804
+ 'k12.ms.us' => 1,
5805
+ 'k12.mt.us' => 1,
5806
+ 'k12.nc.us' => 1,
5807
+ 'k12.ne.us' => 1,
5808
+ 'k12.nh.us' => 1,
5809
+ 'k12.nj.us' => 1,
5810
+ 'k12.nm.us' => 1,
5811
+ 'k12.nv.us' => 1,
5812
+ 'k12.ny.us' => 1,
5813
+ 'k12.oh.us' => 1,
5814
+ 'k12.ok.us' => 1,
5815
+ 'k12.or.us' => 1,
5816
+ 'k12.pa.us' => 1,
5817
+ 'k12.pr.us' => 1,
5818
+ 'k12.ri.us' => 1,
5819
+ 'k12.sc.us' => 1,
5820
+ 'k12.tn.us' => 1,
5821
+ 'k12.tx.us' => 1,
5822
+ 'k12.ut.us' => 1,
5823
+ 'k12.vi.us' => 1,
5824
+ 'k12.vt.us' => 1,
5825
+ 'k12.va.us' => 1,
5826
+ 'k12.wa.us' => 1,
5827
+ 'k12.wi.us' => 1,
5828
+ 'k12.wy.us' => 1,
5829
+ 'cc.ak.us' => 1,
5830
+ 'cc.al.us' => 1,
5831
+ 'cc.ar.us' => 1,
5832
+ 'cc.as.us' => 1,
5833
+ 'cc.az.us' => 1,
5834
+ 'cc.ca.us' => 1,
5835
+ 'cc.co.us' => 1,
5836
+ 'cc.ct.us' => 1,
5837
+ 'cc.dc.us' => 1,
5838
+ 'cc.de.us' => 1,
5839
+ 'cc.fl.us' => 1,
5840
+ 'cc.ga.us' => 1,
5841
+ 'cc.gu.us' => 1,
5842
+ 'cc.hi.us' => 1,
5843
+ 'cc.ia.us' => 1,
5844
+ 'cc.id.us' => 1,
5845
+ 'cc.il.us' => 1,
5846
+ 'cc.in.us' => 1,
5847
+ 'cc.ks.us' => 1,
5848
+ 'cc.ky.us' => 1,
5849
+ 'cc.la.us' => 1,
5850
+ 'cc.ma.us' => 1,
5851
+ 'cc.md.us' => 1,
5852
+ 'cc.me.us' => 1,
5853
+ 'cc.mi.us' => 1,
5854
+ 'cc.mn.us' => 1,
5855
+ 'cc.mo.us' => 1,
5856
+ 'cc.ms.us' => 1,
5857
+ 'cc.mt.us' => 1,
5858
+ 'cc.nc.us' => 1,
5859
+ 'cc.nd.us' => 1,
5860
+ 'cc.ne.us' => 1,
5861
+ 'cc.nh.us' => 1,
5862
+ 'cc.nj.us' => 1,
5863
+ 'cc.nm.us' => 1,
5864
+ 'cc.nv.us' => 1,
5865
+ 'cc.ny.us' => 1,
5866
+ 'cc.oh.us' => 1,
5867
+ 'cc.ok.us' => 1,
5868
+ 'cc.or.us' => 1,
5869
+ 'cc.pa.us' => 1,
5870
+ 'cc.pr.us' => 1,
5871
+ 'cc.ri.us' => 1,
5872
+ 'cc.sc.us' => 1,
5873
+ 'cc.sd.us' => 1,
5874
+ 'cc.tn.us' => 1,
5875
+ 'cc.tx.us' => 1,
5876
+ 'cc.ut.us' => 1,
5877
+ 'cc.vi.us' => 1,
5878
+ 'cc.vt.us' => 1,
5879
+ 'cc.va.us' => 1,
5880
+ 'cc.wa.us' => 1,
5881
+ 'cc.wi.us' => 1,
5882
+ 'cc.wv.us' => 1,
5883
+ 'cc.wy.us' => 1,
5884
+ 'lib.ak.us' => 1,
5885
+ 'lib.al.us' => 1,
5886
+ 'lib.ar.us' => 1,
5887
+ 'lib.as.us' => 1,
5888
+ 'lib.az.us' => 1,
5889
+ 'lib.ca.us' => 1,
5890
+ 'lib.co.us' => 1,
5891
+ 'lib.ct.us' => 1,
5892
+ 'lib.dc.us' => 1,
5893
+ 'lib.fl.us' => 1,
5894
+ 'lib.ga.us' => 1,
5895
+ 'lib.gu.us' => 1,
5896
+ 'lib.hi.us' => 1,
5897
+ 'lib.ia.us' => 1,
5898
+ 'lib.id.us' => 1,
5899
+ 'lib.il.us' => 1,
5900
+ 'lib.in.us' => 1,
5901
+ 'lib.ks.us' => 1,
5902
+ 'lib.ky.us' => 1,
5903
+ 'lib.la.us' => 1,
5904
+ 'lib.ma.us' => 1,
5905
+ 'lib.md.us' => 1,
5906
+ 'lib.me.us' => 1,
5907
+ 'lib.mi.us' => 1,
5908
+ 'lib.mn.us' => 1,
5909
+ 'lib.mo.us' => 1,
5910
+ 'lib.ms.us' => 1,
5911
+ 'lib.mt.us' => 1,
5912
+ 'lib.nc.us' => 1,
5913
+ 'lib.nd.us' => 1,
5914
+ 'lib.ne.us' => 1,
5915
+ 'lib.nh.us' => 1,
5916
+ 'lib.nj.us' => 1,
5917
+ 'lib.nm.us' => 1,
5918
+ 'lib.nv.us' => 1,
5919
+ 'lib.ny.us' => 1,
5920
+ 'lib.oh.us' => 1,
5921
+ 'lib.ok.us' => 1,
5922
+ 'lib.or.us' => 1,
5923
+ 'lib.pa.us' => 1,
5924
+ 'lib.pr.us' => 1,
5925
+ 'lib.ri.us' => 1,
5926
+ 'lib.sc.us' => 1,
5927
+ 'lib.sd.us' => 1,
5928
+ 'lib.tn.us' => 1,
5929
+ 'lib.tx.us' => 1,
5930
+ 'lib.ut.us' => 1,
5931
+ 'lib.vi.us' => 1,
5932
+ 'lib.vt.us' => 1,
5933
+ 'lib.va.us' => 1,
5934
+ 'lib.wa.us' => 1,
5935
+ 'lib.wi.us' => 1,
5936
+ 'lib.wy.us' => 1,
5937
+ 'pvt.k12.ma.us' => 1,
5938
+ 'chtr.k12.ma.us' => 1,
5939
+ 'paroch.k12.ma.us' => 1,
5940
+ 'ann-arbor.mi.us' => 1,
5941
+ 'cog.mi.us' => 1,
5942
+ 'dst.mi.us' => 1,
5943
+ 'eaton.mi.us' => 1,
5944
+ 'gen.mi.us' => 1,
5945
+ 'mus.mi.us' => 1,
5946
+ 'tec.mi.us' => 1,
5947
+ 'washtenaw.mi.us' => 1,
5948
+ 'uy' => 1,
5949
+ 'com.uy' => 1,
5950
+ 'edu.uy' => 1,
5951
+ 'gub.uy' => 1,
5952
+ 'mil.uy' => 1,
5953
+ 'net.uy' => 1,
5954
+ 'org.uy' => 1,
5955
+ 'uz' => 1,
5956
+ 'co.uz' => 1,
5957
+ 'com.uz' => 1,
5958
+ 'net.uz' => 1,
5959
+ 'org.uz' => 1,
5960
+ 'va' => 1,
5961
+ 'vc' => 1,
5962
+ 'com.vc' => 1,
5963
+ 'net.vc' => 1,
5964
+ 'org.vc' => 1,
5965
+ 'gov.vc' => 1,
5966
+ 'mil.vc' => 1,
5967
+ 'edu.vc' => 1,
5968
+ 've' => 1,
5969
+ 'arts.ve' => 1,
5970
+ 'co.ve' => 1,
5971
+ 'com.ve' => 1,
5972
+ 'e12.ve' => 1,
5973
+ 'edu.ve' => 1,
5974
+ 'firm.ve' => 1,
5975
+ 'gob.ve' => 1,
5976
+ 'gov.ve' => 1,
5977
+ 'info.ve' => 1,
5978
+ 'int.ve' => 1,
5979
+ 'mil.ve' => 1,
5980
+ 'net.ve' => 1,
5981
+ 'org.ve' => 1,
5982
+ 'rec.ve' => 1,
5983
+ 'store.ve' => 1,
5984
+ 'tec.ve' => 1,
5985
+ 'web.ve' => 1,
5986
+ 'vg' => 1,
5987
+ 'vi' => 1,
5988
+ 'co.vi' => 1,
5989
+ 'com.vi' => 1,
5990
+ 'k12.vi' => 1,
5991
+ 'net.vi' => 1,
5992
+ 'org.vi' => 1,
5993
+ 'vn' => 1,
5994
+ 'com.vn' => 1,
5995
+ 'net.vn' => 1,
5996
+ 'org.vn' => 1,
5997
+ 'edu.vn' => 1,
5998
+ 'gov.vn' => 1,
5999
+ 'int.vn' => 1,
6000
+ 'ac.vn' => 1,
6001
+ 'biz.vn' => 1,
6002
+ 'info.vn' => 1,
6003
+ 'name.vn' => 1,
6004
+ 'pro.vn' => 1,
6005
+ 'health.vn' => 1,
6006
+ 'vu' => 1,
6007
+ 'com.vu' => 1,
6008
+ 'edu.vu' => 1,
6009
+ 'net.vu' => 1,
6010
+ 'org.vu' => 1,
6011
+ 'wf' => 1,
6012
+ 'ws' => 1,
6013
+ 'com.ws' => 1,
6014
+ 'net.ws' => 1,
6015
+ 'org.ws' => 1,
6016
+ 'gov.ws' => 1,
6017
+ 'edu.ws' => 1,
6018
+ 'yt' => 1,
6019
+ 'امارات' => 1,
6020
+ 'հայ' => 1,
6021
+ 'বাংলা' => 1,
6022
+ 'бг' => 1,
6023
+ 'бел' => 1,
6024
+ '中国' => 1,
6025
+ '中國' => 1,
6026
+ 'الجزائر' => 1,
6027
+ 'مصر' => 1,
6028
+ 'ею' => 1,
6029
+ 'გე' => 1,
6030
+ 'ελ' => 1,
6031
+ '香港' => 1,
6032
+ '公司.香港' => 1,
6033
+ '教育.香港' => 1,
6034
+ '政府.香港' => 1,
6035
+ '個人.香港' => 1,
6036
+ '網絡.香港' => 1,
6037
+ '組織.香港' => 1,
6038
+ 'ಭಾರತ' => 1,
6039
+ 'ଭାରତ' => 1,
6040
+ 'ভাৰত' => 1,
6041
+ 'भारतम्' => 1,
6042
+ 'भारोत' => 1,
6043
+ 'ڀارت' => 1,
6044
+ 'ഭാരതം' => 1,
6045
+ 'भारत' => 1,
6046
+ 'بارت' => 1,
6047
+ 'بھارت' => 1,
6048
+ 'భారత్' => 1,
6049
+ 'ભારત' => 1,
6050
+ 'ਭਾਰਤ' => 1,
6051
+ 'ভারত' => 1,
6052
+ 'இந்தியா' => 1,
6053
+ 'ایران' => 1,
6054
+ 'ايران' => 1,
6055
+ 'عراق' => 1,
6056
+ 'الاردن' => 1,
6057
+ '한국' => 1,
6058
+ 'қаз' => 1,
6059
+ 'ලංකා' => 1,
6060
+ 'இலங்கை' => 1,
6061
+ 'المغرب' => 1,
6062
+ 'мкд' => 1,
6063
+ 'мон' => 1,
6064
+ '澳門' => 1,
6065
+ '澳门' => 1,
6066
+ 'مليسيا' => 1,
6067
+ 'عمان' => 1,
6068
+ 'پاکستان' => 1,
6069
+ 'پاكستان' => 1,
6070
+ 'فلسطين' => 1,
6071
+ 'срб' => 1,
6072
+ 'пр.срб' => 1,
6073
+ 'орг.срб' => 1,
6074
+ 'обр.срб' => 1,
6075
+ 'од.срб' => 1,
6076
+ 'упр.срб' => 1,
6077
+ 'ак.срб' => 1,
6078
+ 'рф' => 1,
6079
+ 'قطر' => 1,
6080
+ 'السعودية' => 1,
6081
+ 'السعودیة' => 1,
6082
+ 'السعودیۃ' => 1,
6083
+ 'السعوديه' => 1,
6084
+ 'سودان' => 1,
6085
+ '新加坡' => 1,
6086
+ 'சிங்கப்பூர்' => 1,
6087
+ 'سورية' => 1,
6088
+ 'سوريا' => 1,
6089
+ 'ไทย' => 1,
6090
+ 'ศึกษา.ไทย' => 1,
6091
+ 'ธุรกิจ.ไทย' => 1,
6092
+ 'รัฐบาล.ไทย' => 1,
6093
+ 'ทหาร.ไทย' => 1,
6094
+ 'เน็ต.ไทย' => 1,
6095
+ 'องค์กร.ไทย' => 1,
6096
+ 'تونس' => 1,
6097
+ '台灣' => 1,
6098
+ '台湾' => 1,
6099
+ '臺灣' => 1,
6100
+ 'укр' => 1,
6101
+ 'اليمن' => 1,
6102
+ 'xxx' => 1,
6103
+ '*.ye' => 1,
6104
+ 'ac.za' => 1,
6105
+ 'agric.za' => 1,
6106
+ 'alt.za' => 1,
6107
+ 'co.za' => 1,
6108
+ 'edu.za' => 1,
6109
+ 'gov.za' => 1,
6110
+ 'grondar.za' => 1,
6111
+ 'law.za' => 1,
6112
+ 'mil.za' => 1,
6113
+ 'net.za' => 1,
6114
+ 'ngo.za' => 1,
6115
+ 'nic.za' => 1,
6116
+ 'nis.za' => 1,
6117
+ 'nom.za' => 1,
6118
+ 'org.za' => 1,
6119
+ 'school.za' => 1,
6120
+ 'tm.za' => 1,
6121
+ 'web.za' => 1,
6122
+ 'zm' => 1,
6123
+ 'ac.zm' => 1,
6124
+ 'biz.zm' => 1,
6125
+ 'co.zm' => 1,
6126
+ 'com.zm' => 1,
6127
+ 'edu.zm' => 1,
6128
+ 'gov.zm' => 1,
6129
+ 'info.zm' => 1,
6130
+ 'mil.zm' => 1,
6131
+ 'net.zm' => 1,
6132
+ 'org.zm' => 1,
6133
+ 'sch.zm' => 1,
6134
+ 'zw' => 1,
6135
+ 'ac.zw' => 1,
6136
+ 'co.zw' => 1,
6137
+ 'gov.zw' => 1,
6138
+ 'mil.zw' => 1,
6139
+ 'org.zw' => 1,
6140
+ 'aaa' => 1,
6141
+ 'aarp' => 1,
6142
+ 'abarth' => 1,
6143
+ 'abb' => 1,
6144
+ 'abbott' => 1,
6145
+ 'abbvie' => 1,
6146
+ 'abc' => 1,
6147
+ 'able' => 1,
6148
+ 'abogado' => 1,
6149
+ 'abudhabi' => 1,
6150
+ 'academy' => 1,
6151
+ 'accenture' => 1,
6152
+ 'accountant' => 1,
6153
+ 'accountants' => 1,
6154
+ 'aco' => 1,
6155
+ 'actor' => 1,
6156
+ 'adac' => 1,
6157
+ 'ads' => 1,
6158
+ 'adult' => 1,
6159
+ 'aeg' => 1,
6160
+ 'aetna' => 1,
6161
+ 'afamilycompany' => 1,
6162
+ 'afl' => 1,
6163
+ 'africa' => 1,
6164
+ 'agakhan' => 1,
6165
+ 'agency' => 1,
6166
+ 'aig' => 1,
6167
+ 'aigo' => 1,
6168
+ 'airbus' => 1,
6169
+ 'airforce' => 1,
6170
+ 'airtel' => 1,
6171
+ 'akdn' => 1,
6172
+ 'alfaromeo' => 1,
6173
+ 'alibaba' => 1,
6174
+ 'alipay' => 1,
6175
+ 'allfinanz' => 1,
6176
+ 'allstate' => 1,
6177
+ 'ally' => 1,
6178
+ 'alsace' => 1,
6179
+ 'alstom' => 1,
6180
+ 'americanexpress' => 1,
6181
+ 'americanfamily' => 1,
6182
+ 'amex' => 1,
6183
+ 'amfam' => 1,
6184
+ 'amica' => 1,
6185
+ 'amsterdam' => 1,
6186
+ 'analytics' => 1,
6187
+ 'android' => 1,
6188
+ 'anquan' => 1,
6189
+ 'anz' => 1,
6190
+ 'aol' => 1,
6191
+ 'apartments' => 1,
6192
+ 'app' => 1,
6193
+ 'apple' => 1,
6194
+ 'aquarelle' => 1,
6195
+ 'arab' => 1,
6196
+ 'aramco' => 1,
6197
+ 'archi' => 1,
6198
+ 'army' => 1,
6199
+ 'art' => 1,
6200
+ 'arte' => 1,
6201
+ 'asda' => 1,
6202
+ 'associates' => 1,
6203
+ 'athleta' => 1,
6204
+ 'attorney' => 1,
6205
+ 'auction' => 1,
6206
+ 'audi' => 1,
6207
+ 'audible' => 1,
6208
+ 'audio' => 1,
6209
+ 'auspost' => 1,
6210
+ 'author' => 1,
6211
+ 'auto' => 1,
6212
+ 'autos' => 1,
6213
+ 'avianca' => 1,
6214
+ 'aws' => 1,
6215
+ 'axa' => 1,
6216
+ 'azure' => 1,
6217
+ 'baby' => 1,
6218
+ 'baidu' => 1,
6219
+ 'banamex' => 1,
6220
+ 'bananarepublic' => 1,
6221
+ 'band' => 1,
6222
+ 'bank' => 1,
6223
+ 'bar' => 1,
6224
+ 'barcelona' => 1,
6225
+ 'barclaycard' => 1,
6226
+ 'barclays' => 1,
6227
+ 'barefoot' => 1,
6228
+ 'bargains' => 1,
6229
+ 'baseball' => 1,
6230
+ 'basketball' => 1,
6231
+ 'bauhaus' => 1,
6232
+ 'bayern' => 1,
6233
+ 'bbc' => 1,
6234
+ 'bbt' => 1,
6235
+ 'bbva' => 1,
6236
+ 'bcg' => 1,
6237
+ 'bcn' => 1,
6238
+ 'beats' => 1,
6239
+ 'beauty' => 1,
6240
+ 'beer' => 1,
6241
+ 'bentley' => 1,
6242
+ 'berlin' => 1,
6243
+ 'best' => 1,
6244
+ 'bestbuy' => 1,
6245
+ 'bet' => 1,
6246
+ 'bharti' => 1,
6247
+ 'bible' => 1,
6248
+ 'bid' => 1,
6249
+ 'bike' => 1,
6250
+ 'bing' => 1,
6251
+ 'bingo' => 1,
6252
+ 'bio' => 1,
6253
+ 'black' => 1,
6254
+ 'blackfriday' => 1,
6255
+ 'blockbuster' => 1,
6256
+ 'blog' => 1,
6257
+ 'bloomberg' => 1,
6258
+ 'blue' => 1,
6259
+ 'bms' => 1,
6260
+ 'bmw' => 1,
6261
+ 'bnl' => 1,
6262
+ 'bnpparibas' => 1,
6263
+ 'boats' => 1,
6264
+ 'boehringer' => 1,
6265
+ 'bofa' => 1,
6266
+ 'bom' => 1,
6267
+ 'bond' => 1,
6268
+ 'boo' => 1,
6269
+ 'book' => 1,
6270
+ 'booking' => 1,
6271
+ 'bosch' => 1,
6272
+ 'bostik' => 1,
6273
+ 'boston' => 1,
6274
+ 'bot' => 1,
6275
+ 'boutique' => 1,
6276
+ 'box' => 1,
6277
+ 'bradesco' => 1,
6278
+ 'bridgestone' => 1,
6279
+ 'broadway' => 1,
6280
+ 'broker' => 1,
6281
+ 'brother' => 1,
6282
+ 'brussels' => 1,
6283
+ 'budapest' => 1,
6284
+ 'bugatti' => 1,
6285
+ 'build' => 1,
6286
+ 'builders' => 1,
6287
+ 'business' => 1,
6288
+ 'buy' => 1,
6289
+ 'buzz' => 1,
6290
+ 'bzh' => 1,
6291
+ 'cab' => 1,
6292
+ 'cafe' => 1,
6293
+ 'cal' => 1,
6294
+ 'call' => 1,
6295
+ 'calvinklein' => 1,
6296
+ 'cam' => 1,
6297
+ 'camera' => 1,
6298
+ 'camp' => 1,
6299
+ 'cancerresearch' => 1,
6300
+ 'canon' => 1,
6301
+ 'capetown' => 1,
6302
+ 'capital' => 1,
6303
+ 'capitalone' => 1,
6304
+ 'car' => 1,
6305
+ 'caravan' => 1,
6306
+ 'cards' => 1,
6307
+ 'care' => 1,
6308
+ 'career' => 1,
6309
+ 'careers' => 1,
6310
+ 'cars' => 1,
6311
+ 'cartier' => 1,
6312
+ 'casa' => 1,
6313
+ 'case' => 1,
6314
+ 'caseih' => 1,
6315
+ 'cash' => 1,
6316
+ 'casino' => 1,
6317
+ 'catering' => 1,
6318
+ 'catholic' => 1,
6319
+ 'cba' => 1,
6320
+ 'cbn' => 1,
6321
+ 'cbre' => 1,
6322
+ 'cbs' => 1,
6323
+ 'ceb' => 1,
6324
+ 'center' => 1,
6325
+ 'ceo' => 1,
6326
+ 'cern' => 1,
6327
+ 'cfa' => 1,
6328
+ 'cfd' => 1,
6329
+ 'chanel' => 1,
6330
+ 'channel' => 1,
6331
+ 'charity' => 1,
6332
+ 'chase' => 1,
6333
+ 'chat' => 1,
6334
+ 'cheap' => 1,
6335
+ 'chintai' => 1,
6336
+ 'christmas' => 1,
6337
+ 'chrome' => 1,
6338
+ 'chrysler' => 1,
6339
+ 'church' => 1,
6340
+ 'cipriani' => 1,
6341
+ 'circle' => 1,
6342
+ 'cisco' => 1,
6343
+ 'citadel' => 1,
6344
+ 'citi' => 1,
6345
+ 'citic' => 1,
6346
+ 'city' => 1,
6347
+ 'cityeats' => 1,
6348
+ 'claims' => 1,
6349
+ 'cleaning' => 1,
6350
+ 'click' => 1,
6351
+ 'clinic' => 1,
6352
+ 'clinique' => 1,
6353
+ 'clothing' => 1,
6354
+ 'cloud' => 1,
6355
+ 'club' => 1,
6356
+ 'clubmed' => 1,
6357
+ 'coach' => 1,
6358
+ 'codes' => 1,
6359
+ 'coffee' => 1,
6360
+ 'college' => 1,
6361
+ 'cologne' => 1,
6362
+ 'comcast' => 1,
6363
+ 'commbank' => 1,
6364
+ 'community' => 1,
6365
+ 'company' => 1,
6366
+ 'compare' => 1,
6367
+ 'computer' => 1,
6368
+ 'comsec' => 1,
6369
+ 'condos' => 1,
6370
+ 'construction' => 1,
6371
+ 'consulting' => 1,
6372
+ 'contact' => 1,
6373
+ 'contractors' => 1,
6374
+ 'cooking' => 1,
6375
+ 'cookingchannel' => 1,
6376
+ 'cool' => 1,
6377
+ 'corsica' => 1,
6378
+ 'country' => 1,
6379
+ 'coupon' => 1,
6380
+ 'coupons' => 1,
6381
+ 'courses' => 1,
6382
+ 'cpa' => 1,
6383
+ 'credit' => 1,
6384
+ 'creditcard' => 1,
6385
+ 'creditunion' => 1,
6386
+ 'cricket' => 1,
6387
+ 'crown' => 1,
6388
+ 'crs' => 1,
6389
+ 'cruise' => 1,
6390
+ 'cruises' => 1,
6391
+ 'csc' => 1,
6392
+ 'cuisinella' => 1,
6393
+ 'cymru' => 1,
6394
+ 'cyou' => 1,
6395
+ 'dabur' => 1,
6396
+ 'dad' => 1,
6397
+ 'dance' => 1,
6398
+ 'data' => 1,
6399
+ 'date' => 1,
6400
+ 'dating' => 1,
6401
+ 'datsun' => 1,
6402
+ 'day' => 1,
6403
+ 'dclk' => 1,
6404
+ 'dds' => 1,
6405
+ 'deal' => 1,
6406
+ 'dealer' => 1,
6407
+ 'deals' => 1,
6408
+ 'degree' => 1,
6409
+ 'delivery' => 1,
6410
+ 'dell' => 1,
6411
+ 'deloitte' => 1,
6412
+ 'delta' => 1,
6413
+ 'democrat' => 1,
6414
+ 'dental' => 1,
6415
+ 'dentist' => 1,
6416
+ 'desi' => 1,
6417
+ 'design' => 1,
6418
+ 'dev' => 1,
6419
+ 'dhl' => 1,
6420
+ 'diamonds' => 1,
6421
+ 'diet' => 1,
6422
+ 'digital' => 1,
6423
+ 'direct' => 1,
6424
+ 'directory' => 1,
6425
+ 'discount' => 1,
6426
+ 'discover' => 1,
6427
+ 'dish' => 1,
6428
+ 'diy' => 1,
6429
+ 'dnp' => 1,
6430
+ 'docs' => 1,
6431
+ 'doctor' => 1,
6432
+ 'dodge' => 1,
6433
+ 'dog' => 1,
6434
+ 'domains' => 1,
6435
+ 'dot' => 1,
6436
+ 'download' => 1,
6437
+ 'drive' => 1,
6438
+ 'dtv' => 1,
6439
+ 'dubai' => 1,
6440
+ 'duck' => 1,
6441
+ 'dunlop' => 1,
6442
+ 'duns' => 1,
6443
+ 'dupont' => 1,
6444
+ 'durban' => 1,
6445
+ 'dvag' => 1,
6446
+ 'dvr' => 1,
6447
+ 'earth' => 1,
6448
+ 'eat' => 1,
6449
+ 'eco' => 1,
6450
+ 'edeka' => 1,
6451
+ 'education' => 1,
6452
+ 'email' => 1,
6453
+ 'emerck' => 1,
6454
+ 'energy' => 1,
6455
+ 'engineer' => 1,
6456
+ 'engineering' => 1,
6457
+ 'enterprises' => 1,
6458
+ 'epson' => 1,
6459
+ 'equipment' => 1,
6460
+ 'ericsson' => 1,
6461
+ 'erni' => 1,
6462
+ 'esq' => 1,
6463
+ 'estate' => 1,
6464
+ 'esurance' => 1,
6465
+ 'etisalat' => 1,
6466
+ 'eurovision' => 1,
6467
+ 'eus' => 1,
6468
+ 'events' => 1,
6469
+ 'everbank' => 1,
6470
+ 'exchange' => 1,
6471
+ 'expert' => 1,
6472
+ 'exposed' => 1,
6473
+ 'express' => 1,
6474
+ 'extraspace' => 1,
6475
+ 'fage' => 1,
6476
+ 'fail' => 1,
6477
+ 'fairwinds' => 1,
6478
+ 'faith' => 1,
6479
+ 'family' => 1,
6480
+ 'fan' => 1,
6481
+ 'fans' => 1,
6482
+ 'farm' => 1,
6483
+ 'farmers' => 1,
6484
+ 'fashion' => 1,
6485
+ 'fast' => 1,
6486
+ 'fedex' => 1,
6487
+ 'feedback' => 1,
6488
+ 'ferrari' => 1,
6489
+ 'ferrero' => 1,
6490
+ 'fiat' => 1,
6491
+ 'fidelity' => 1,
6492
+ 'fido' => 1,
6493
+ 'film' => 1,
6494
+ 'final' => 1,
6495
+ 'finance' => 1,
6496
+ 'financial' => 1,
6497
+ 'fire' => 1,
6498
+ 'firestone' => 1,
6499
+ 'firmdale' => 1,
6500
+ 'fish' => 1,
6501
+ 'fishing' => 1,
6502
+ 'fit' => 1,
6503
+ 'fitness' => 1,
6504
+ 'flickr' => 1,
6505
+ 'flights' => 1,
6506
+ 'flir' => 1,
6507
+ 'florist' => 1,
6508
+ 'flowers' => 1,
6509
+ 'fly' => 1,
6510
+ 'foo' => 1,
6511
+ 'food' => 1,
6512
+ 'foodnetwork' => 1,
6513
+ 'football' => 1,
6514
+ 'ford' => 1,
6515
+ 'forex' => 1,
6516
+ 'forsale' => 1,
6517
+ 'forum' => 1,
6518
+ 'foundation' => 1,
6519
+ 'fox' => 1,
6520
+ 'free' => 1,
6521
+ 'fresenius' => 1,
6522
+ 'frl' => 1,
6523
+ 'frogans' => 1,
6524
+ 'frontdoor' => 1,
6525
+ 'frontier' => 1,
6526
+ 'ftr' => 1,
6527
+ 'fujitsu' => 1,
6528
+ 'fujixerox' => 1,
6529
+ 'fun' => 1,
6530
+ 'fund' => 1,
6531
+ 'furniture' => 1,
6532
+ 'futbol' => 1,
6533
+ 'fyi' => 1,
6534
+ 'gal' => 1,
6535
+ 'gallery' => 1,
6536
+ 'gallo' => 1,
6537
+ 'gallup' => 1,
6538
+ 'game' => 1,
6539
+ 'games' => 1,
6540
+ 'gap' => 1,
6541
+ 'garden' => 1,
6542
+ 'gay' => 1,
6543
+ 'gbiz' => 1,
6544
+ 'gdn' => 1,
6545
+ 'gea' => 1,
6546
+ 'gent' => 1,
6547
+ 'genting' => 1,
6548
+ 'george' => 1,
6549
+ 'ggee' => 1,
6550
+ 'gift' => 1,
6551
+ 'gifts' => 1,
6552
+ 'gives' => 1,
6553
+ 'giving' => 1,
6554
+ 'glade' => 1,
6555
+ 'glass' => 1,
6556
+ 'gle' => 1,
6557
+ 'global' => 1,
6558
+ 'globo' => 1,
6559
+ 'gmail' => 1,
6560
+ 'gmbh' => 1,
6561
+ 'gmo' => 1,
6562
+ 'gmx' => 1,
6563
+ 'godaddy' => 1,
6564
+ 'gold' => 1,
6565
+ 'goldpoint' => 1,
6566
+ 'golf' => 1,
6567
+ 'goo' => 1,
6568
+ 'goodyear' => 1,
6569
+ 'goog' => 1,
6570
+ 'google' => 1,
6571
+ 'gop' => 1,
6572
+ 'got' => 1,
6573
+ 'grainger' => 1,
6574
+ 'graphics' => 1,
6575
+ 'gratis' => 1,
6576
+ 'green' => 1,
6577
+ 'gripe' => 1,
6578
+ 'grocery' => 1,
6579
+ 'group' => 1,
6580
+ 'guardian' => 1,
6581
+ 'gucci' => 1,
6582
+ 'guge' => 1,
6583
+ 'guide' => 1,
6584
+ 'guitars' => 1,
6585
+ 'guru' => 1,
6586
+ 'hair' => 1,
6587
+ 'hamburg' => 1,
6588
+ 'hangout' => 1,
6589
+ 'haus' => 1,
6590
+ 'hbo' => 1,
6591
+ 'hdfc' => 1,
6592
+ 'hdfcbank' => 1,
6593
+ 'health' => 1,
6594
+ 'healthcare' => 1,
6595
+ 'help' => 1,
6596
+ 'helsinki' => 1,
6597
+ 'here' => 1,
6598
+ 'hermes' => 1,
6599
+ 'hgtv' => 1,
6600
+ 'hiphop' => 1,
6601
+ 'hisamitsu' => 1,
6602
+ 'hitachi' => 1,
6603
+ 'hiv' => 1,
6604
+ 'hkt' => 1,
6605
+ 'hockey' => 1,
6606
+ 'holdings' => 1,
6607
+ 'holiday' => 1,
6608
+ 'homedepot' => 1,
6609
+ 'homegoods' => 1,
6610
+ 'homes' => 1,
6611
+ 'homesense' => 1,
6612
+ 'honda' => 1,
6613
+ 'honeywell' => 1,
6614
+ 'horse' => 1,
6615
+ 'hospital' => 1,
6616
+ 'host' => 1,
6617
+ 'hosting' => 1,
6618
+ 'hot' => 1,
6619
+ 'hoteles' => 1,
6620
+ 'hotels' => 1,
6621
+ 'hotmail' => 1,
6622
+ 'house' => 1,
6623
+ 'how' => 1,
6624
+ 'hsbc' => 1,
6625
+ 'hughes' => 1,
6626
+ 'hyatt' => 1,
6627
+ 'hyundai' => 1,
6628
+ 'ibm' => 1,
6629
+ 'icbc' => 1,
6630
+ 'ice' => 1,
6631
+ 'icu' => 1,
6632
+ 'ieee' => 1,
6633
+ 'ifm' => 1,
6634
+ 'ikano' => 1,
6635
+ 'imamat' => 1,
6636
+ 'imdb' => 1,
6637
+ 'immo' => 1,
6638
+ 'immobilien' => 1,
6639
+ 'inc' => 1,
6640
+ 'industries' => 1,
6641
+ 'infiniti' => 1,
6642
+ 'ing' => 1,
6643
+ 'ink' => 1,
6644
+ 'institute' => 1,
6645
+ 'insurance' => 1,
6646
+ 'insure' => 1,
6647
+ 'intel' => 1,
6648
+ 'international' => 1,
6649
+ 'intuit' => 1,
6650
+ 'investments' => 1,
6651
+ 'ipiranga' => 1,
6652
+ 'irish' => 1,
6653
+ 'iselect' => 1,
6654
+ 'ismaili' => 1,
6655
+ 'ist' => 1,
6656
+ 'istanbul' => 1,
6657
+ 'itau' => 1,
6658
+ 'itv' => 1,
6659
+ 'iveco' => 1,
6660
+ 'jaguar' => 1,
6661
+ 'java' => 1,
6662
+ 'jcb' => 1,
6663
+ 'jcp' => 1,
6664
+ 'jeep' => 1,
6665
+ 'jetzt' => 1,
6666
+ 'jewelry' => 1,
6667
+ 'jio' => 1,
6668
+ 'jll' => 1,
6669
+ 'jmp' => 1,
6670
+ 'jnj' => 1,
6671
+ 'joburg' => 1,
6672
+ 'jot' => 1,
6673
+ 'joy' => 1,
6674
+ 'jpmorgan' => 1,
6675
+ 'jprs' => 1,
6676
+ 'juegos' => 1,
6677
+ 'juniper' => 1,
6678
+ 'kaufen' => 1,
6679
+ 'kddi' => 1,
6680
+ 'kerryhotels' => 1,
6681
+ 'kerrylogistics' => 1,
6682
+ 'kerryproperties' => 1,
6683
+ 'kfh' => 1,
6684
+ 'kia' => 1,
6685
+ 'kim' => 1,
6686
+ 'kinder' => 1,
6687
+ 'kindle' => 1,
6688
+ 'kitchen' => 1,
6689
+ 'kiwi' => 1,
6690
+ 'koeln' => 1,
6691
+ 'komatsu' => 1,
6692
+ 'kosher' => 1,
6693
+ 'kpmg' => 1,
6694
+ 'kpn' => 1,
6695
+ 'krd' => 1,
6696
+ 'kred' => 1,
6697
+ 'kuokgroup' => 1,
6698
+ 'kyoto' => 1,
6699
+ 'lacaixa' => 1,
6700
+ 'ladbrokes' => 1,
6701
+ 'lamborghini' => 1,
6702
+ 'lamer' => 1,
6703
+ 'lancaster' => 1,
6704
+ 'lancia' => 1,
6705
+ 'lancome' => 1,
6706
+ 'land' => 1,
6707
+ 'landrover' => 1,
6708
+ 'lanxess' => 1,
6709
+ 'lasalle' => 1,
6710
+ 'lat' => 1,
6711
+ 'latino' => 1,
6712
+ 'latrobe' => 1,
6713
+ 'law' => 1,
6714
+ 'lawyer' => 1,
6715
+ 'lds' => 1,
6716
+ 'lease' => 1,
6717
+ 'leclerc' => 1,
6718
+ 'lefrak' => 1,
6719
+ 'legal' => 1,
6720
+ 'lego' => 1,
6721
+ 'lexus' => 1,
6722
+ 'lgbt' => 1,
6723
+ 'liaison' => 1,
6724
+ 'lidl' => 1,
6725
+ 'life' => 1,
6726
+ 'lifeinsurance' => 1,
6727
+ 'lifestyle' => 1,
6728
+ 'lighting' => 1,
6729
+ 'like' => 1,
6730
+ 'lilly' => 1,
6731
+ 'limited' => 1,
6732
+ 'limo' => 1,
6733
+ 'lincoln' => 1,
6734
+ 'linde' => 1,
6735
+ 'link' => 1,
6736
+ 'lipsy' => 1,
6737
+ 'live' => 1,
6738
+ 'living' => 1,
6739
+ 'lixil' => 1,
6740
+ 'llc' => 1,
6741
+ 'loan' => 1,
6742
+ 'loans' => 1,
6743
+ 'locker' => 1,
6744
+ 'locus' => 1,
6745
+ 'loft' => 1,
6746
+ 'lol' => 1,
6747
+ 'london' => 1,
6748
+ 'lotte' => 1,
6749
+ 'lotto' => 1,
6750
+ 'love' => 1,
6751
+ 'lpl' => 1,
6752
+ 'lplfinancial' => 1,
6753
+ 'ltd' => 1,
6754
+ 'ltda' => 1,
6755
+ 'lundbeck' => 1,
6756
+ 'lupin' => 1,
6757
+ 'luxe' => 1,
6758
+ 'luxury' => 1,
6759
+ 'macys' => 1,
6760
+ 'madrid' => 1,
6761
+ 'maif' => 1,
6762
+ 'maison' => 1,
6763
+ 'makeup' => 1,
6764
+ 'man' => 1,
6765
+ 'management' => 1,
6766
+ 'mango' => 1,
6767
+ 'map' => 1,
6768
+ 'market' => 1,
6769
+ 'marketing' => 1,
6770
+ 'markets' => 1,
6771
+ 'marriott' => 1,
6772
+ 'marshalls' => 1,
6773
+ 'maserati' => 1,
6774
+ 'mattel' => 1,
6775
+ 'mba' => 1,
6776
+ 'mckinsey' => 1,
6777
+ 'med' => 1,
6778
+ 'media' => 1,
6779
+ 'meet' => 1,
6780
+ 'melbourne' => 1,
6781
+ 'meme' => 1,
6782
+ 'memorial' => 1,
6783
+ 'men' => 1,
6784
+ 'menu' => 1,
6785
+ 'merckmsd' => 1,
6786
+ 'metlife' => 1,
6787
+ 'miami' => 1,
6788
+ 'microsoft' => 1,
6789
+ 'mini' => 1,
6790
+ 'mint' => 1,
6791
+ 'mit' => 1,
6792
+ 'mitsubishi' => 1,
6793
+ 'mlb' => 1,
6794
+ 'mls' => 1,
6795
+ 'mma' => 1,
6796
+ 'mobile' => 1,
6797
+ 'mobily' => 1,
6798
+ 'moda' => 1,
6799
+ 'moe' => 1,
6800
+ 'moi' => 1,
6801
+ 'mom' => 1,
6802
+ 'monash' => 1,
6803
+ 'money' => 1,
6804
+ 'monster' => 1,
6805
+ 'mopar' => 1,
6806
+ 'mormon' => 1,
6807
+ 'mortgage' => 1,
6808
+ 'moscow' => 1,
6809
+ 'moto' => 1,
6810
+ 'motorcycles' => 1,
6811
+ 'mov' => 1,
6812
+ 'movie' => 1,
6813
+ 'movistar' => 1,
6814
+ 'msd' => 1,
6815
+ 'mtn' => 1,
6816
+ 'mtr' => 1,
6817
+ 'mutual' => 1,
6818
+ 'nab' => 1,
6819
+ 'nadex' => 1,
6820
+ 'nagoya' => 1,
6821
+ 'nationwide' => 1,
6822
+ 'natura' => 1,
6823
+ 'navy' => 1,
6824
+ 'nba' => 1,
6825
+ 'nec' => 1,
6826
+ 'netbank' => 1,
6827
+ 'netflix' => 1,
6828
+ 'network' => 1,
6829
+ 'neustar' => 1,
6830
+ 'new' => 1,
6831
+ 'newholland' => 1,
6832
+ 'news' => 1,
6833
+ 'next' => 1,
6834
+ 'nextdirect' => 1,
6835
+ 'nexus' => 1,
6836
+ 'nfl' => 1,
6837
+ 'ngo' => 1,
6838
+ 'nhk' => 1,
6839
+ 'nico' => 1,
6840
+ 'nike' => 1,
6841
+ 'nikon' => 1,
6842
+ 'ninja' => 1,
6843
+ 'nissan' => 1,
6844
+ 'nissay' => 1,
6845
+ 'nokia' => 1,
6846
+ 'northwesternmutual' => 1,
6847
+ 'norton' => 1,
6848
+ 'now' => 1,
6849
+ 'nowruz' => 1,
6850
+ 'nowtv' => 1,
6851
+ 'nra' => 1,
6852
+ 'nrw' => 1,
6853
+ 'ntt' => 1,
6854
+ 'nyc' => 1,
6855
+ 'obi' => 1,
6856
+ 'observer' => 1,
6857
+ 'off' => 1,
6858
+ 'office' => 1,
6859
+ 'okinawa' => 1,
6860
+ 'olayan' => 1,
6861
+ 'olayangroup' => 1,
6862
+ 'oldnavy' => 1,
6863
+ 'ollo' => 1,
6864
+ 'omega' => 1,
6865
+ 'one' => 1,
6866
+ 'ong' => 1,
6867
+ 'onl' => 1,
6868
+ 'online' => 1,
6869
+ 'onyourside' => 1,
6870
+ 'ooo' => 1,
6871
+ 'open' => 1,
6872
+ 'oracle' => 1,
6873
+ 'orange' => 1,
6874
+ 'organic' => 1,
6875
+ 'origins' => 1,
6876
+ 'osaka' => 1,
6877
+ 'otsuka' => 1,
6878
+ 'ott' => 1,
6879
+ 'ovh' => 1,
6880
+ 'page' => 1,
6881
+ 'panasonic' => 1,
6882
+ 'paris' => 1,
6883
+ 'pars' => 1,
6884
+ 'partners' => 1,
6885
+ 'parts' => 1,
6886
+ 'party' => 1,
6887
+ 'passagens' => 1,
6888
+ 'pay' => 1,
6889
+ 'pccw' => 1,
6890
+ 'pet' => 1,
6891
+ 'pfizer' => 1,
6892
+ 'pharmacy' => 1,
6893
+ 'phd' => 1,
6894
+ 'philips' => 1,
6895
+ 'phone' => 1,
6896
+ 'photo' => 1,
6897
+ 'photography' => 1,
6898
+ 'photos' => 1,
6899
+ 'physio' => 1,
6900
+ 'piaget' => 1,
6901
+ 'pics' => 1,
6902
+ 'pictet' => 1,
6903
+ 'pictures' => 1,
6904
+ 'pid' => 1,
6905
+ 'pin' => 1,
6906
+ 'ping' => 1,
6907
+ 'pink' => 1,
6908
+ 'pioneer' => 1,
6909
+ 'pizza' => 1,
6910
+ 'place' => 1,
6911
+ 'play' => 1,
6912
+ 'playstation' => 1,
6913
+ 'plumbing' => 1,
6914
+ 'plus' => 1,
6915
+ 'pnc' => 1,
6916
+ 'pohl' => 1,
6917
+ 'poker' => 1,
6918
+ 'politie' => 1,
6919
+ 'porn' => 1,
6920
+ 'pramerica' => 1,
6921
+ 'praxi' => 1,
6922
+ 'press' => 1,
6923
+ 'prime' => 1,
6924
+ 'prod' => 1,
6925
+ 'productions' => 1,
6926
+ 'prof' => 1,
6927
+ 'progressive' => 1,
6928
+ 'promo' => 1,
6929
+ 'properties' => 1,
6930
+ 'property' => 1,
6931
+ 'protection' => 1,
6932
+ 'pru' => 1,
6933
+ 'prudential' => 1,
6934
+ 'pub' => 1,
6935
+ 'pwc' => 1,
6936
+ 'qpon' => 1,
6937
+ 'quebec' => 1,
6938
+ 'quest' => 1,
6939
+ 'qvc' => 1,
6940
+ 'racing' => 1,
6941
+ 'radio' => 1,
6942
+ 'raid' => 1,
6943
+ 'read' => 1,
6944
+ 'realestate' => 1,
6945
+ 'realtor' => 1,
6946
+ 'realty' => 1,
6947
+ 'recipes' => 1,
6948
+ 'red' => 1,
6949
+ 'redstone' => 1,
6950
+ 'redumbrella' => 1,
6951
+ 'rehab' => 1,
6952
+ 'reise' => 1,
6953
+ 'reisen' => 1,
6954
+ 'reit' => 1,
6955
+ 'reliance' => 1,
6956
+ 'ren' => 1,
6957
+ 'rent' => 1,
6958
+ 'rentals' => 1,
6959
+ 'repair' => 1,
6960
+ 'report' => 1,
6961
+ 'republican' => 1,
6962
+ 'rest' => 1,
6963
+ 'restaurant' => 1,
6964
+ 'review' => 1,
6965
+ 'reviews' => 1,
6966
+ 'rexroth' => 1,
6967
+ 'rich' => 1,
6968
+ 'richardli' => 1,
6969
+ 'ricoh' => 1,
6970
+ 'rightathome' => 1,
6971
+ 'ril' => 1,
6972
+ 'rio' => 1,
6973
+ 'rip' => 1,
6974
+ 'rmit' => 1,
6975
+ 'rocher' => 1,
6976
+ 'rocks' => 1,
6977
+ 'rodeo' => 1,
6978
+ 'rogers' => 1,
6979
+ 'room' => 1,
6980
+ 'rsvp' => 1,
6981
+ 'rugby' => 1,
6982
+ 'ruhr' => 1,
6983
+ 'run' => 1,
6984
+ 'rwe' => 1,
6985
+ 'ryukyu' => 1,
6986
+ 'saarland' => 1,
6987
+ 'safe' => 1,
6988
+ 'safety' => 1,
6989
+ 'sakura' => 1,
6990
+ 'sale' => 1,
6991
+ 'salon' => 1,
6992
+ 'samsclub' => 1,
6993
+ 'samsung' => 1,
6994
+ 'sandvik' => 1,
6995
+ 'sandvikcoromant' => 1,
6996
+ 'sanofi' => 1,
6997
+ 'sap' => 1,
6998
+ 'sarl' => 1,
6999
+ 'sas' => 1,
7000
+ 'save' => 1,
7001
+ 'saxo' => 1,
7002
+ 'sbi' => 1,
7003
+ 'sbs' => 1,
7004
+ 'sca' => 1,
7005
+ 'scb' => 1,
7006
+ 'schaeffler' => 1,
7007
+ 'schmidt' => 1,
7008
+ 'scholarships' => 1,
7009
+ 'school' => 1,
7010
+ 'schule' => 1,
7011
+ 'schwarz' => 1,
7012
+ 'science' => 1,
7013
+ 'scjohnson' => 1,
7014
+ 'scor' => 1,
7015
+ 'scot' => 1,
7016
+ 'search' => 1,
7017
+ 'seat' => 1,
7018
+ 'secure' => 1,
7019
+ 'security' => 1,
7020
+ 'seek' => 1,
7021
+ 'select' => 1,
7022
+ 'sener' => 1,
7023
+ 'services' => 1,
7024
+ 'ses' => 1,
7025
+ 'seven' => 1,
7026
+ 'sew' => 1,
7027
+ 'sex' => 1,
7028
+ 'sexy' => 1,
7029
+ 'sfr' => 1,
7030
+ 'shangrila' => 1,
7031
+ 'sharp' => 1,
7032
+ 'shaw' => 1,
7033
+ 'shell' => 1,
7034
+ 'shia' => 1,
7035
+ 'shiksha' => 1,
7036
+ 'shoes' => 1,
7037
+ 'shop' => 1,
7038
+ 'shopping' => 1,
7039
+ 'shouji' => 1,
7040
+ 'show' => 1,
7041
+ 'showtime' => 1,
7042
+ 'shriram' => 1,
7043
+ 'silk' => 1,
7044
+ 'sina' => 1,
7045
+ 'singles' => 1,
7046
+ 'site' => 1,
7047
+ 'ski' => 1,
7048
+ 'skin' => 1,
7049
+ 'sky' => 1,
7050
+ 'skype' => 1,
7051
+ 'sling' => 1,
7052
+ 'smart' => 1,
7053
+ 'smile' => 1,
7054
+ 'sncf' => 1,
7055
+ 'soccer' => 1,
7056
+ 'social' => 1,
7057
+ 'softbank' => 1,
7058
+ 'software' => 1,
7059
+ 'sohu' => 1,
7060
+ 'solar' => 1,
7061
+ 'solutions' => 1,
7062
+ 'song' => 1,
7063
+ 'sony' => 1,
7064
+ 'soy' => 1,
7065
+ 'space' => 1,
7066
+ 'sport' => 1,
7067
+ 'spot' => 1,
7068
+ 'spreadbetting' => 1,
7069
+ 'srl' => 1,
7070
+ 'srt' => 1,
7071
+ 'stada' => 1,
7072
+ 'staples' => 1,
7073
+ 'star' => 1,
7074
+ 'starhub' => 1,
7075
+ 'statebank' => 1,
7076
+ 'statefarm' => 1,
7077
+ 'stc' => 1,
7078
+ 'stcgroup' => 1,
7079
+ 'stockholm' => 1,
7080
+ 'storage' => 1,
7081
+ 'store' => 1,
7082
+ 'stream' => 1,
7083
+ 'studio' => 1,
7084
+ 'study' => 1,
7085
+ 'style' => 1,
7086
+ 'sucks' => 1,
7087
+ 'supplies' => 1,
7088
+ 'supply' => 1,
7089
+ 'support' => 1,
7090
+ 'surf' => 1,
7091
+ 'surgery' => 1,
7092
+ 'suzuki' => 1,
7093
+ 'swatch' => 1,
7094
+ 'swiftcover' => 1,
7095
+ 'swiss' => 1,
7096
+ 'sydney' => 1,
7097
+ 'symantec' => 1,
7098
+ 'systems' => 1,
7099
+ 'tab' => 1,
7100
+ 'taipei' => 1,
7101
+ 'talk' => 1,
7102
+ 'taobao' => 1,
7103
+ 'target' => 1,
7104
+ 'tatamotors' => 1,
7105
+ 'tatar' => 1,
7106
+ 'tattoo' => 1,
7107
+ 'tax' => 1,
7108
+ 'taxi' => 1,
7109
+ 'tci' => 1,
7110
+ 'tdk' => 1,
7111
+ 'team' => 1,
7112
+ 'tech' => 1,
7113
+ 'technology' => 1,
7114
+ 'telefonica' => 1,
7115
+ 'temasek' => 1,
7116
+ 'tennis' => 1,
7117
+ 'teva' => 1,
7118
+ 'thd' => 1,
7119
+ 'theater' => 1,
7120
+ 'theatre' => 1,
7121
+ 'tiaa' => 1,
7122
+ 'tickets' => 1,
7123
+ 'tienda' => 1,
7124
+ 'tiffany' => 1,
7125
+ 'tips' => 1,
7126
+ 'tires' => 1,
7127
+ 'tirol' => 1,
7128
+ 'tjmaxx' => 1,
7129
+ 'tjx' => 1,
7130
+ 'tkmaxx' => 1,
7131
+ 'tmall' => 1,
7132
+ 'today' => 1,
7133
+ 'tokyo' => 1,
7134
+ 'tools' => 1,
7135
+ 'top' => 1,
7136
+ 'toray' => 1,
7137
+ 'toshiba' => 1,
7138
+ 'total' => 1,
7139
+ 'tours' => 1,
7140
+ 'town' => 1,
7141
+ 'toyota' => 1,
7142
+ 'toys' => 1,
7143
+ 'trade' => 1,
7144
+ 'trading' => 1,
7145
+ 'training' => 1,
7146
+ 'travel' => 1,
7147
+ 'travelchannel' => 1,
7148
+ 'travelers' => 1,
7149
+ 'travelersinsurance' => 1,
7150
+ 'trust' => 1,
7151
+ 'trv' => 1,
7152
+ 'tube' => 1,
7153
+ 'tui' => 1,
7154
+ 'tunes' => 1,
7155
+ 'tushu' => 1,
7156
+ 'tvs' => 1,
7157
+ 'ubank' => 1,
7158
+ 'ubs' => 1,
7159
+ 'uconnect' => 1,
7160
+ 'unicom' => 1,
7161
+ 'university' => 1,
7162
+ 'uno' => 1,
7163
+ 'uol' => 1,
7164
+ 'ups' => 1,
7165
+ 'vacations' => 1,
7166
+ 'vana' => 1,
7167
+ 'vanguard' => 1,
7168
+ 'vegas' => 1,
7169
+ 'ventures' => 1,
7170
+ 'verisign' => 1,
7171
+ 'versicherung' => 1,
7172
+ 'vet' => 1,
7173
+ 'viajes' => 1,
7174
+ 'video' => 1,
7175
+ 'vig' => 1,
7176
+ 'viking' => 1,
7177
+ 'villas' => 1,
7178
+ 'vin' => 1,
7179
+ 'vip' => 1,
7180
+ 'virgin' => 1,
7181
+ 'visa' => 1,
7182
+ 'vision' => 1,
7183
+ 'vistaprint' => 1,
7184
+ 'viva' => 1,
7185
+ 'vivo' => 1,
7186
+ 'vlaanderen' => 1,
7187
+ 'vodka' => 1,
7188
+ 'volkswagen' => 1,
7189
+ 'volvo' => 1,
7190
+ 'vote' => 1,
7191
+ 'voting' => 1,
7192
+ 'voto' => 1,
7193
+ 'voyage' => 1,
7194
+ 'vuelos' => 1,
7195
+ 'wales' => 1,
7196
+ 'walmart' => 1,
7197
+ 'walter' => 1,
7198
+ 'wang' => 1,
7199
+ 'wanggou' => 1,
7200
+ 'warman' => 1,
7201
+ 'watch' => 1,
7202
+ 'watches' => 1,
7203
+ 'weather' => 1,
7204
+ 'weatherchannel' => 1,
7205
+ 'webcam' => 1,
7206
+ 'weber' => 1,
7207
+ 'website' => 1,
7208
+ 'wed' => 1,
7209
+ 'wedding' => 1,
7210
+ 'weibo' => 1,
7211
+ 'weir' => 1,
7212
+ 'whoswho' => 1,
7213
+ 'wien' => 1,
7214
+ 'wiki' => 1,
7215
+ 'williamhill' => 1,
7216
+ 'win' => 1,
7217
+ 'windows' => 1,
7218
+ 'wine' => 1,
7219
+ 'winners' => 1,
7220
+ 'wme' => 1,
7221
+ 'wolterskluwer' => 1,
7222
+ 'woodside' => 1,
7223
+ 'work' => 1,
7224
+ 'works' => 1,
7225
+ 'world' => 1,
7226
+ 'wow' => 1,
7227
+ 'wtc' => 1,
7228
+ 'wtf' => 1,
7229
+ 'xbox' => 1,
7230
+ 'xerox' => 1,
7231
+ 'xfinity' => 1,
7232
+ 'xihuan' => 1,
7233
+ 'xin' => 1,
7234
+ 'कॉम' => 1,
7235
+ 'セール' => 1,
7236
+ '佛山' => 1,
7237
+ '慈善' => 1,
7238
+ '集团' => 1,
7239
+ '在线' => 1,
7240
+ '大众汽车' => 1,
7241
+ '点看' => 1,
7242
+ 'คอม' => 1,
7243
+ '八卦' => 1,
7244
+ 'موقع' => 1,
7245
+ '公益' => 1,
7246
+ '公司' => 1,
7247
+ '香格里拉' => 1,
7248
+ '网站' => 1,
7249
+ '移动' => 1,
7250
+ '我爱你' => 1,
7251
+ 'москва' => 1,
7252
+ 'католик' => 1,
7253
+ 'онлайн' => 1,
7254
+ 'сайт' => 1,
7255
+ '联通' => 1,
7256
+ 'קום' => 1,
7257
+ '时尚' => 1,
7258
+ '微博' => 1,
7259
+ '淡马锡' => 1,
7260
+ 'ファッション' => 1,
7261
+ 'орг' => 1,
7262
+ 'नेट' => 1,
7263
+ 'ストア' => 1,
7264
+ '삼성' => 1,
7265
+ '商标' => 1,
7266
+ '商店' => 1,
7267
+ '商城' => 1,
7268
+ 'дети' => 1,
7269
+ 'ポイント' => 1,
7270
+ '新闻' => 1,
7271
+ '工行' => 1,
7272
+ '家電' => 1,
7273
+ 'كوم' => 1,
7274
+ '中文网' => 1,
7275
+ '中信' => 1,
7276
+ '娱乐' => 1,
7277
+ '谷歌' => 1,
7278
+ '電訊盈科' => 1,
7279
+ '购物' => 1,
7280
+ 'クラウド' => 1,
7281
+ '通販' => 1,
7282
+ '网店' => 1,
7283
+ 'संगठन' => 1,
7284
+ '餐厅' => 1,
7285
+ '网络' => 1,
7286
+ 'ком' => 1,
7287
+ '诺基亚' => 1,
7288
+ '食品' => 1,
7289
+ '飞利浦' => 1,
7290
+ '手表' => 1,
7291
+ '手机' => 1,
7292
+ 'ارامكو' => 1,
7293
+ 'العليان' => 1,
7294
+ 'اتصالات' => 1,
7295
+ 'بازار' => 1,
7296
+ 'موبايلي' => 1,
7297
+ 'ابوظبي' => 1,
7298
+ 'كاثوليك' => 1,
7299
+ 'همراه' => 1,
7300
+ '닷컴' => 1,
7301
+ '政府' => 1,
7302
+ 'شبكة' => 1,
7303
+ 'بيتك' => 1,
7304
+ 'عرب' => 1,
7305
+ '机构' => 1,
7306
+ '组织机构' => 1,
7307
+ '健康' => 1,
7308
+ '招聘' => 1,
7309
+ 'рус' => 1,
7310
+ '珠宝' => 1,
7311
+ '大拿' => 1,
7312
+ 'みんな' => 1,
7313
+ 'グーグル' => 1,
7314
+ '世界' => 1,
7315
+ '書籍' => 1,
7316
+ '网址' => 1,
7317
+ '닷넷' => 1,
7318
+ 'コム' => 1,
7319
+ '天主教' => 1,
7320
+ '游戏' => 1,
7321
+ 'vermögensberater' => 1,
7322
+ 'vermögensberatung' => 1,
7323
+ '企业' => 1,
7324
+ '信息' => 1,
7325
+ '嘉里大酒店' => 1,
7326
+ '嘉里' => 1,
7327
+ '广东' => 1,
7328
+ '政务' => 1,
7329
+ 'xyz' => 1,
7330
+ 'yachts' => 1,
7331
+ 'yahoo' => 1,
7332
+ 'yamaxun' => 1,
7333
+ 'yandex' => 1,
7334
+ 'yodobashi' => 1,
7335
+ 'yoga' => 1,
7336
+ 'yokohama' => 1,
7337
+ 'you' => 1,
7338
+ 'youtube' => 1,
7339
+ 'yun' => 1,
7340
+ 'zappos' => 1,
7341
+ 'zara' => 1,
7342
+ 'zero' => 1,
7343
+ 'zip' => 1,
7344
+ 'zone' => 1,
7345
+ 'zuerich' => 1,
7346
+ 'cc.ua' => 2,
7347
+ 'inf.ua' => 2,
7348
+ 'ltd.ua' => 2,
7349
+ 'beep.pl' => 2,
7350
+ 'barsy.ca' => 2,
7351
+ '*.compute.estate' => 2,
7352
+ '*.alces.network' => 2,
7353
+ 'alwaysdata.net' => 2,
7354
+ 'cloudfront.net' => 2,
7355
+ '*.compute.amazonaws.com' => 2,
7356
+ '*.compute-1.amazonaws.com' => 2,
7357
+ '*.compute.amazonaws.com.cn' => 2,
7358
+ 'us-east-1.amazonaws.com' => 2,
7359
+ 'cn-north-1.eb.amazonaws.com.cn' => 2,
7360
+ 'cn-northwest-1.eb.amazonaws.com.cn' => 2,
7361
+ 'elasticbeanstalk.com' => 2,
7362
+ 'ap-northeast-1.elasticbeanstalk.com' => 2,
7363
+ 'ap-northeast-2.elasticbeanstalk.com' => 2,
7364
+ 'ap-northeast-3.elasticbeanstalk.com' => 2,
7365
+ 'ap-south-1.elasticbeanstalk.com' => 2,
7366
+ 'ap-southeast-1.elasticbeanstalk.com' => 2,
7367
+ 'ap-southeast-2.elasticbeanstalk.com' => 2,
7368
+ 'ca-central-1.elasticbeanstalk.com' => 2,
7369
+ 'eu-central-1.elasticbeanstalk.com' => 2,
7370
+ 'eu-west-1.elasticbeanstalk.com' => 2,
7371
+ 'eu-west-2.elasticbeanstalk.com' => 2,
7372
+ 'eu-west-3.elasticbeanstalk.com' => 2,
7373
+ 'sa-east-1.elasticbeanstalk.com' => 2,
7374
+ 'us-east-1.elasticbeanstalk.com' => 2,
7375
+ 'us-east-2.elasticbeanstalk.com' => 2,
7376
+ 'us-gov-west-1.elasticbeanstalk.com' => 2,
7377
+ 'us-west-1.elasticbeanstalk.com' => 2,
7378
+ 'us-west-2.elasticbeanstalk.com' => 2,
7379
+ '*.elb.amazonaws.com' => 2,
7380
+ '*.elb.amazonaws.com.cn' => 2,
7381
+ 's3.amazonaws.com' => 2,
7382
+ 's3-ap-northeast-1.amazonaws.com' => 2,
7383
+ 's3-ap-northeast-2.amazonaws.com' => 2,
7384
+ 's3-ap-south-1.amazonaws.com' => 2,
7385
+ 's3-ap-southeast-1.amazonaws.com' => 2,
7386
+ 's3-ap-southeast-2.amazonaws.com' => 2,
7387
+ 's3-ca-central-1.amazonaws.com' => 2,
7388
+ 's3-eu-central-1.amazonaws.com' => 2,
7389
+ 's3-eu-west-1.amazonaws.com' => 2,
7390
+ 's3-eu-west-2.amazonaws.com' => 2,
7391
+ 's3-eu-west-3.amazonaws.com' => 2,
7392
+ 's3-external-1.amazonaws.com' => 2,
7393
+ 's3-fips-us-gov-west-1.amazonaws.com' => 2,
7394
+ 's3-sa-east-1.amazonaws.com' => 2,
7395
+ 's3-us-gov-west-1.amazonaws.com' => 2,
7396
+ 's3-us-east-2.amazonaws.com' => 2,
7397
+ 's3-us-west-1.amazonaws.com' => 2,
7398
+ 's3-us-west-2.amazonaws.com' => 2,
7399
+ 's3.ap-northeast-2.amazonaws.com' => 2,
7400
+ 's3.ap-south-1.amazonaws.com' => 2,
7401
+ 's3.cn-north-1.amazonaws.com.cn' => 2,
7402
+ 's3.ca-central-1.amazonaws.com' => 2,
7403
+ 's3.eu-central-1.amazonaws.com' => 2,
7404
+ 's3.eu-west-2.amazonaws.com' => 2,
7405
+ 's3.eu-west-3.amazonaws.com' => 2,
7406
+ 's3.us-east-2.amazonaws.com' => 2,
7407
+ 's3.dualstack.ap-northeast-1.amazonaws.com' => 2,
7408
+ 's3.dualstack.ap-northeast-2.amazonaws.com' => 2,
7409
+ 's3.dualstack.ap-south-1.amazonaws.com' => 2,
7410
+ 's3.dualstack.ap-southeast-1.amazonaws.com' => 2,
7411
+ 's3.dualstack.ap-southeast-2.amazonaws.com' => 2,
7412
+ 's3.dualstack.ca-central-1.amazonaws.com' => 2,
7413
+ 's3.dualstack.eu-central-1.amazonaws.com' => 2,
7414
+ 's3.dualstack.eu-west-1.amazonaws.com' => 2,
7415
+ 's3.dualstack.eu-west-2.amazonaws.com' => 2,
7416
+ 's3.dualstack.eu-west-3.amazonaws.com' => 2,
7417
+ 's3.dualstack.sa-east-1.amazonaws.com' => 2,
7418
+ 's3.dualstack.us-east-1.amazonaws.com' => 2,
7419
+ 's3.dualstack.us-east-2.amazonaws.com' => 2,
7420
+ 's3-website-us-east-1.amazonaws.com' => 2,
7421
+ 's3-website-us-west-1.amazonaws.com' => 2,
7422
+ 's3-website-us-west-2.amazonaws.com' => 2,
7423
+ 's3-website-ap-northeast-1.amazonaws.com' => 2,
7424
+ 's3-website-ap-southeast-1.amazonaws.com' => 2,
7425
+ 's3-website-ap-southeast-2.amazonaws.com' => 2,
7426
+ 's3-website-eu-west-1.amazonaws.com' => 2,
7427
+ 's3-website-sa-east-1.amazonaws.com' => 2,
7428
+ 's3-website.ap-northeast-2.amazonaws.com' => 2,
7429
+ 's3-website.ap-south-1.amazonaws.com' => 2,
7430
+ 's3-website.ca-central-1.amazonaws.com' => 2,
7431
+ 's3-website.eu-central-1.amazonaws.com' => 2,
7432
+ 's3-website.eu-west-2.amazonaws.com' => 2,
7433
+ 's3-website.eu-west-3.amazonaws.com' => 2,
7434
+ 's3-website.us-east-2.amazonaws.com' => 2,
7435
+ 't3l3p0rt.net' => 2,
7436
+ 'tele.amune.org' => 2,
7437
+ 'apigee.io' => 2,
7438
+ 'on-aptible.com' => 2,
7439
+ 'user.aseinet.ne.jp' => 2,
7440
+ 'gv.vc' => 2,
7441
+ 'd.gv.vc' => 2,
7442
+ 'user.party.eus' => 2,
7443
+ 'pimienta.org' => 2,
7444
+ 'poivron.org' => 2,
7445
+ 'potager.org' => 2,
7446
+ 'sweetpepper.org' => 2,
7447
+ 'myasustor.com' => 2,
7448
+ 'go-vip.co' => 2,
7449
+ 'go-vip.net' => 2,
7450
+ 'wpcomstaging.com' => 2,
7451
+ 'myfritz.net' => 2,
7452
+ '*.awdev.ca' => 2,
7453
+ '*.advisor.ws' => 2,
7454
+ 'b-data.io' => 2,
7455
+ 'backplaneapp.io' => 2,
7456
+ 'balena-devices.com' => 2,
7457
+ 'app.banzaicloud.io' => 2,
7458
+ 'betainabox.com' => 2,
7459
+ 'bnr.la' => 2,
7460
+ 'blackbaudcdn.net' => 2,
7461
+ 'boomla.net' => 2,
7462
+ 'boxfuse.io' => 2,
7463
+ 'square7.ch' => 2,
7464
+ 'bplaced.com' => 2,
7465
+ 'bplaced.de' => 2,
7466
+ 'square7.de' => 2,
7467
+ 'bplaced.net' => 2,
7468
+ 'square7.net' => 2,
7469
+ 'browsersafetymark.io' => 2,
7470
+ 'uk0.bigv.io' => 2,
7471
+ 'dh.bytemark.co.uk' => 2,
7472
+ 'vm.bytemark.co.uk' => 2,
7473
+ 'mycd.eu' => 2,
7474
+ 'carrd.co' => 2,
7475
+ 'crd.co' => 2,
7476
+ 'uwu.ai' => 2,
7477
+ 'ae.org' => 2,
7478
+ 'ar.com' => 2,
7479
+ 'br.com' => 2,
7480
+ 'cn.com' => 2,
7481
+ 'com.de' => 2,
7482
+ 'com.se' => 2,
7483
+ 'de.com' => 2,
7484
+ 'eu.com' => 2,
7485
+ 'gb.com' => 2,
7486
+ 'gb.net' => 2,
7487
+ 'hu.com' => 2,
7488
+ 'hu.net' => 2,
7489
+ 'jp.net' => 2,
7490
+ 'jpn.com' => 2,
7491
+ 'kr.com' => 2,
7492
+ 'mex.com' => 2,
7493
+ 'no.com' => 2,
7494
+ 'qc.com' => 2,
7495
+ 'ru.com' => 2,
7496
+ 'sa.com' => 2,
7497
+ 'se.net' => 2,
7498
+ 'uk.com' => 2,
7499
+ 'uk.net' => 2,
7500
+ 'us.com' => 2,
7501
+ 'uy.com' => 2,
7502
+ 'za.bz' => 2,
7503
+ 'za.com' => 2,
7504
+ 'africa.com' => 2,
7505
+ 'gr.com' => 2,
7506
+ 'in.net' => 2,
7507
+ 'us.org' => 2,
7508
+ 'co.com' => 2,
7509
+ 'c.la' => 2,
7510
+ 'certmgr.org' => 2,
7511
+ 'xenapponazure.com' => 2,
7512
+ 'discourse.group' => 2,
7513
+ 'virtueeldomein.nl' => 2,
7514
+ 'cleverapps.io' => 2,
7515
+ '*.lcl.dev' => 2,
7516
+ '*.stg.dev' => 2,
7517
+ 'c66.me' => 2,
7518
+ 'cloud66.ws' => 2,
7519
+ 'cloud66.zone' => 2,
7520
+ 'jdevcloud.com' => 2,
7521
+ 'wpdevcloud.com' => 2,
7522
+ 'cloudaccess.host' => 2,
7523
+ 'freesite.host' => 2,
7524
+ 'cloudaccess.net' => 2,
7525
+ 'cloudcontrolled.com' => 2,
7526
+ 'cloudcontrolapp.com' => 2,
7527
+ 'cloudera.site' => 2,
7528
+ 'trycloudflare.com' => 2,
7529
+ 'workers.dev' => 2,
7530
+ 'wnext.app' => 2,
7531
+ 'co.ca' => 2,
7532
+ '*.otap.co' => 2,
7533
+ 'co.cz' => 2,
7534
+ 'c.cdn77.org' => 2,
7535
+ 'cdn77-ssl.net' => 2,
7536
+ 'r.cdn77.net' => 2,
7537
+ 'rsc.cdn77.org' => 2,
7538
+ 'ssl.origin.cdn77-secure.org' => 2,
7539
+ 'cloudns.asia' => 2,
7540
+ 'cloudns.biz' => 2,
7541
+ 'cloudns.club' => 2,
7542
+ 'cloudns.cc' => 2,
7543
+ 'cloudns.eu' => 2,
7544
+ 'cloudns.in' => 2,
7545
+ 'cloudns.info' => 2,
7546
+ 'cloudns.org' => 2,
7547
+ 'cloudns.pro' => 2,
7548
+ 'cloudns.pw' => 2,
7549
+ 'cloudns.us' => 2,
7550
+ 'cloudeity.net' => 2,
7551
+ 'cnpy.gdn' => 2,
7552
+ 'co.nl' => 2,
7553
+ 'co.no' => 2,
7554
+ 'webhosting.be' => 2,
7555
+ 'hosting-cluster.nl' => 2,
7556
+ 'dyn.cosidns.de' => 2,
7557
+ 'dynamisches-dns.de' => 2,
7558
+ 'dnsupdater.de' => 2,
7559
+ 'internet-dns.de' => 2,
7560
+ 'l-o-g-i-n.de' => 2,
7561
+ 'dynamic-dns.info' => 2,
7562
+ 'feste-ip.net' => 2,
7563
+ 'knx-server.net' => 2,
7564
+ 'static-access.net' => 2,
7565
+ 'realm.cz' => 2,
7566
+ '*.cryptonomic.net' => 2,
7567
+ 'cupcake.is' => 2,
7568
+ 'cyon.link' => 2,
7569
+ 'cyon.site' => 2,
7570
+ 'daplie.me' => 2,
7571
+ 'localhost.daplie.me' => 2,
7572
+ 'dattolocal.com' => 2,
7573
+ 'dattorelay.com' => 2,
7574
+ 'dattoweb.com' => 2,
7575
+ 'mydatto.com' => 2,
7576
+ 'dattolocal.net' => 2,
7577
+ 'mydatto.net' => 2,
7578
+ 'biz.dk' => 2,
7579
+ 'co.dk' => 2,
7580
+ 'firm.dk' => 2,
7581
+ 'reg.dk' => 2,
7582
+ 'store.dk' => 2,
7583
+ '*.dapps.earth' => 2,
7584
+ '*.bzz.dapps.earth' => 2,
7585
+ 'debian.net' => 2,
7586
+ 'dedyn.io' => 2,
7587
+ 'dnshome.de' => 2,
7588
+ 'online.th' => 2,
7589
+ 'shop.th' => 2,
7590
+ 'drayddns.com' => 2,
7591
+ 'dreamhosters.com' => 2,
7592
+ 'mydrobo.com' => 2,
7593
+ 'drud.io' => 2,
7594
+ 'drud.us' => 2,
7595
+ 'duckdns.org' => 2,
7596
+ 'dy.fi' => 2,
7597
+ 'tunk.org' => 2,
7598
+ 'dyndns-at-home.com' => 2,
7599
+ 'dyndns-at-work.com' => 2,
7600
+ 'dyndns-blog.com' => 2,
7601
+ 'dyndns-free.com' => 2,
7602
+ 'dyndns-home.com' => 2,
7603
+ 'dyndns-ip.com' => 2,
7604
+ 'dyndns-mail.com' => 2,
7605
+ 'dyndns-office.com' => 2,
7606
+ 'dyndns-pics.com' => 2,
7607
+ 'dyndns-remote.com' => 2,
7608
+ 'dyndns-server.com' => 2,
7609
+ 'dyndns-web.com' => 2,
7610
+ 'dyndns-wiki.com' => 2,
7611
+ 'dyndns-work.com' => 2,
7612
+ 'dyndns.biz' => 2,
7613
+ 'dyndns.info' => 2,
7614
+ 'dyndns.org' => 2,
7615
+ 'dyndns.tv' => 2,
7616
+ 'at-band-camp.net' => 2,
7617
+ 'ath.cx' => 2,
7618
+ 'barrel-of-knowledge.info' => 2,
7619
+ 'barrell-of-knowledge.info' => 2,
7620
+ 'better-than.tv' => 2,
7621
+ 'blogdns.com' => 2,
7622
+ 'blogdns.net' => 2,
7623
+ 'blogdns.org' => 2,
7624
+ 'blogsite.org' => 2,
7625
+ 'boldlygoingnowhere.org' => 2,
7626
+ 'broke-it.net' => 2,
7627
+ 'buyshouses.net' => 2,
7628
+ 'cechire.com' => 2,
7629
+ 'dnsalias.com' => 2,
7630
+ 'dnsalias.net' => 2,
7631
+ 'dnsalias.org' => 2,
7632
+ 'dnsdojo.com' => 2,
7633
+ 'dnsdojo.net' => 2,
7634
+ 'dnsdojo.org' => 2,
7635
+ 'does-it.net' => 2,
7636
+ 'doesntexist.com' => 2,
7637
+ 'doesntexist.org' => 2,
7638
+ 'dontexist.com' => 2,
7639
+ 'dontexist.net' => 2,
7640
+ 'dontexist.org' => 2,
7641
+ 'doomdns.com' => 2,
7642
+ 'doomdns.org' => 2,
7643
+ 'dvrdns.org' => 2,
7644
+ 'dyn-o-saur.com' => 2,
7645
+ 'dynalias.com' => 2,
7646
+ 'dynalias.net' => 2,
7647
+ 'dynalias.org' => 2,
7648
+ 'dynathome.net' => 2,
7649
+ 'dyndns.ws' => 2,
7650
+ 'endofinternet.net' => 2,
7651
+ 'endofinternet.org' => 2,
7652
+ 'endoftheinternet.org' => 2,
7653
+ 'est-a-la-maison.com' => 2,
7654
+ 'est-a-la-masion.com' => 2,
7655
+ 'est-le-patron.com' => 2,
7656
+ 'est-mon-blogueur.com' => 2,
7657
+ 'for-better.biz' => 2,
7658
+ 'for-more.biz' => 2,
7659
+ 'for-our.info' => 2,
7660
+ 'for-some.biz' => 2,
7661
+ 'for-the.biz' => 2,
7662
+ 'forgot.her.name' => 2,
7663
+ 'forgot.his.name' => 2,
7664
+ 'from-ak.com' => 2,
7665
+ 'from-al.com' => 2,
7666
+ 'from-ar.com' => 2,
7667
+ 'from-az.net' => 2,
7668
+ 'from-ca.com' => 2,
7669
+ 'from-co.net' => 2,
7670
+ 'from-ct.com' => 2,
7671
+ 'from-dc.com' => 2,
7672
+ 'from-de.com' => 2,
7673
+ 'from-fl.com' => 2,
7674
+ 'from-ga.com' => 2,
7675
+ 'from-hi.com' => 2,
7676
+ 'from-ia.com' => 2,
7677
+ 'from-id.com' => 2,
7678
+ 'from-il.com' => 2,
7679
+ 'from-in.com' => 2,
7680
+ 'from-ks.com' => 2,
7681
+ 'from-ky.com' => 2,
7682
+ 'from-la.net' => 2,
7683
+ 'from-ma.com' => 2,
7684
+ 'from-md.com' => 2,
7685
+ 'from-me.org' => 2,
7686
+ 'from-mi.com' => 2,
7687
+ 'from-mn.com' => 2,
7688
+ 'from-mo.com' => 2,
7689
+ 'from-ms.com' => 2,
7690
+ 'from-mt.com' => 2,
7691
+ 'from-nc.com' => 2,
7692
+ 'from-nd.com' => 2,
7693
+ 'from-ne.com' => 2,
7694
+ 'from-nh.com' => 2,
7695
+ 'from-nj.com' => 2,
7696
+ 'from-nm.com' => 2,
7697
+ 'from-nv.com' => 2,
7698
+ 'from-ny.net' => 2,
7699
+ 'from-oh.com' => 2,
7700
+ 'from-ok.com' => 2,
7701
+ 'from-or.com' => 2,
7702
+ 'from-pa.com' => 2,
7703
+ 'from-pr.com' => 2,
7704
+ 'from-ri.com' => 2,
7705
+ 'from-sc.com' => 2,
7706
+ 'from-sd.com' => 2,
7707
+ 'from-tn.com' => 2,
7708
+ 'from-tx.com' => 2,
7709
+ 'from-ut.com' => 2,
7710
+ 'from-va.com' => 2,
7711
+ 'from-vt.com' => 2,
7712
+ 'from-wa.com' => 2,
7713
+ 'from-wi.com' => 2,
7714
+ 'from-wv.com' => 2,
7715
+ 'from-wy.com' => 2,
7716
+ 'ftpaccess.cc' => 2,
7717
+ 'fuettertdasnetz.de' => 2,
7718
+ 'game-host.org' => 2,
7719
+ 'game-server.cc' => 2,
7720
+ 'getmyip.com' => 2,
7721
+ 'gets-it.net' => 2,
7722
+ 'go.dyndns.org' => 2,
7723
+ 'gotdns.com' => 2,
7724
+ 'gotdns.org' => 2,
7725
+ 'groks-the.info' => 2,
7726
+ 'groks-this.info' => 2,
7727
+ 'ham-radio-op.net' => 2,
7728
+ 'here-for-more.info' => 2,
7729
+ 'hobby-site.com' => 2,
7730
+ 'hobby-site.org' => 2,
7731
+ 'home.dyndns.org' => 2,
7732
+ 'homedns.org' => 2,
7733
+ 'homeftp.net' => 2,
7734
+ 'homeftp.org' => 2,
7735
+ 'homeip.net' => 2,
7736
+ 'homelinux.com' => 2,
7737
+ 'homelinux.net' => 2,
7738
+ 'homelinux.org' => 2,
7739
+ 'homeunix.com' => 2,
7740
+ 'homeunix.net' => 2,
7741
+ 'homeunix.org' => 2,
7742
+ 'iamallama.com' => 2,
7743
+ 'in-the-band.net' => 2,
7744
+ 'is-a-anarchist.com' => 2,
7745
+ 'is-a-blogger.com' => 2,
7746
+ 'is-a-bookkeeper.com' => 2,
7747
+ 'is-a-bruinsfan.org' => 2,
7748
+ 'is-a-bulls-fan.com' => 2,
7749
+ 'is-a-candidate.org' => 2,
7750
+ 'is-a-caterer.com' => 2,
7751
+ 'is-a-celticsfan.org' => 2,
7752
+ 'is-a-chef.com' => 2,
7753
+ 'is-a-chef.net' => 2,
7754
+ 'is-a-chef.org' => 2,
7755
+ 'is-a-conservative.com' => 2,
7756
+ 'is-a-cpa.com' => 2,
7757
+ 'is-a-cubicle-slave.com' => 2,
7758
+ 'is-a-democrat.com' => 2,
7759
+ 'is-a-designer.com' => 2,
7760
+ 'is-a-doctor.com' => 2,
7761
+ 'is-a-financialadvisor.com' => 2,
7762
+ 'is-a-geek.com' => 2,
7763
+ 'is-a-geek.net' => 2,
7764
+ 'is-a-geek.org' => 2,
7765
+ 'is-a-green.com' => 2,
7766
+ 'is-a-guru.com' => 2,
7767
+ 'is-a-hard-worker.com' => 2,
7768
+ 'is-a-hunter.com' => 2,
7769
+ 'is-a-knight.org' => 2,
7770
+ 'is-a-landscaper.com' => 2,
7771
+ 'is-a-lawyer.com' => 2,
7772
+ 'is-a-liberal.com' => 2,
7773
+ 'is-a-libertarian.com' => 2,
7774
+ 'is-a-linux-user.org' => 2,
7775
+ 'is-a-llama.com' => 2,
7776
+ 'is-a-musician.com' => 2,
7777
+ 'is-a-nascarfan.com' => 2,
7778
+ 'is-a-nurse.com' => 2,
7779
+ 'is-a-painter.com' => 2,
7780
+ 'is-a-patsfan.org' => 2,
7781
+ 'is-a-personaltrainer.com' => 2,
7782
+ 'is-a-photographer.com' => 2,
7783
+ 'is-a-player.com' => 2,
7784
+ 'is-a-republican.com' => 2,
7785
+ 'is-a-rockstar.com' => 2,
7786
+ 'is-a-socialist.com' => 2,
7787
+ 'is-a-soxfan.org' => 2,
7788
+ 'is-a-student.com' => 2,
7789
+ 'is-a-teacher.com' => 2,
7790
+ 'is-a-techie.com' => 2,
7791
+ 'is-a-therapist.com' => 2,
7792
+ 'is-an-accountant.com' => 2,
7793
+ 'is-an-actor.com' => 2,
7794
+ 'is-an-actress.com' => 2,
7795
+ 'is-an-anarchist.com' => 2,
7796
+ 'is-an-artist.com' => 2,
7797
+ 'is-an-engineer.com' => 2,
7798
+ 'is-an-entertainer.com' => 2,
7799
+ 'is-by.us' => 2,
7800
+ 'is-certified.com' => 2,
7801
+ 'is-found.org' => 2,
7802
+ 'is-gone.com' => 2,
7803
+ 'is-into-anime.com' => 2,
7804
+ 'is-into-cars.com' => 2,
7805
+ 'is-into-cartoons.com' => 2,
7806
+ 'is-into-games.com' => 2,
7807
+ 'is-leet.com' => 2,
7808
+ 'is-lost.org' => 2,
7809
+ 'is-not-certified.com' => 2,
7810
+ 'is-saved.org' => 2,
7811
+ 'is-slick.com' => 2,
7812
+ 'is-uberleet.com' => 2,
7813
+ 'is-very-bad.org' => 2,
7814
+ 'is-very-evil.org' => 2,
7815
+ 'is-very-good.org' => 2,
7816
+ 'is-very-nice.org' => 2,
7817
+ 'is-very-sweet.org' => 2,
7818
+ 'is-with-theband.com' => 2,
7819
+ 'isa-geek.com' => 2,
7820
+ 'isa-geek.net' => 2,
7821
+ 'isa-geek.org' => 2,
7822
+ 'isa-hockeynut.com' => 2,
7823
+ 'issmarterthanyou.com' => 2,
7824
+ 'isteingeek.de' => 2,
7825
+ 'istmein.de' => 2,
7826
+ 'kicks-ass.net' => 2,
7827
+ 'kicks-ass.org' => 2,
7828
+ 'knowsitall.info' => 2,
7829
+ 'land-4-sale.us' => 2,
7830
+ 'lebtimnetz.de' => 2,
7831
+ 'leitungsen.de' => 2,
7832
+ 'likes-pie.com' => 2,
7833
+ 'likescandy.com' => 2,
7834
+ 'merseine.nu' => 2,
7835
+ 'mine.nu' => 2,
7836
+ 'misconfused.org' => 2,
7837
+ 'mypets.ws' => 2,
7838
+ 'myphotos.cc' => 2,
7839
+ 'neat-url.com' => 2,
7840
+ 'office-on-the.net' => 2,
7841
+ 'on-the-web.tv' => 2,
7842
+ 'podzone.net' => 2,
7843
+ 'podzone.org' => 2,
7844
+ 'readmyblog.org' => 2,
7845
+ 'saves-the-whales.com' => 2,
7846
+ 'scrapper-site.net' => 2,
7847
+ 'scrapping.cc' => 2,
7848
+ 'selfip.biz' => 2,
7849
+ 'selfip.com' => 2,
7850
+ 'selfip.info' => 2,
7851
+ 'selfip.net' => 2,
7852
+ 'selfip.org' => 2,
7853
+ 'sells-for-less.com' => 2,
7854
+ 'sells-for-u.com' => 2,
7855
+ 'sells-it.net' => 2,
7856
+ 'sellsyourhome.org' => 2,
7857
+ 'servebbs.com' => 2,
7858
+ 'servebbs.net' => 2,
7859
+ 'servebbs.org' => 2,
7860
+ 'serveftp.net' => 2,
7861
+ 'serveftp.org' => 2,
7862
+ 'servegame.org' => 2,
7863
+ 'shacknet.nu' => 2,
7864
+ 'simple-url.com' => 2,
7865
+ 'space-to-rent.com' => 2,
7866
+ 'stuff-4-sale.org' => 2,
7867
+ 'stuff-4-sale.us' => 2,
7868
+ 'teaches-yoga.com' => 2,
7869
+ 'thruhere.net' => 2,
7870
+ 'traeumtgerade.de' => 2,
7871
+ 'webhop.biz' => 2,
7872
+ 'webhop.info' => 2,
7873
+ 'webhop.net' => 2,
7874
+ 'webhop.org' => 2,
7875
+ 'worse-than.tv' => 2,
7876
+ 'writesthisblog.com' => 2,
7877
+ 'ddnss.de' => 2,
7878
+ 'dyn.ddnss.de' => 2,
7879
+ 'dyndns.ddnss.de' => 2,
7880
+ 'dyndns1.de' => 2,
7881
+ 'dyn-ip24.de' => 2,
7882
+ 'home-webserver.de' => 2,
7883
+ 'dyn.home-webserver.de' => 2,
7884
+ 'myhome-server.de' => 2,
7885
+ 'ddnss.org' => 2,
7886
+ 'definima.net' => 2,
7887
+ 'definima.io' => 2,
7888
+ 'bci.dnstrace.pro' => 2,
7889
+ 'ddnsfree.com' => 2,
7890
+ 'ddnsgeek.com' => 2,
7891
+ 'giize.com' => 2,
7892
+ 'gleeze.com' => 2,
7893
+ 'kozow.com' => 2,
7894
+ 'loseyourip.com' => 2,
7895
+ 'ooguy.com' => 2,
7896
+ 'theworkpc.com' => 2,
7897
+ 'casacam.net' => 2,
7898
+ 'dynu.net' => 2,
7899
+ 'accesscam.org' => 2,
7900
+ 'camdvr.org' => 2,
7901
+ 'freeddns.org' => 2,
7902
+ 'mywire.org' => 2,
7903
+ 'webredirect.org' => 2,
7904
+ 'myddns.rocks' => 2,
7905
+ 'blogsite.xyz' => 2,
7906
+ 'dynv6.net' => 2,
7907
+ 'e4.cz' => 2,
7908
+ 'mytuleap.com' => 2,
7909
+ 'onred.one' => 2,
7910
+ 'staging.onred.one' => 2,
7911
+ 'enonic.io' => 2,
7912
+ 'customer.enonic.io' => 2,
7913
+ 'eu.org' => 2,
7914
+ 'al.eu.org' => 2,
7915
+ 'asso.eu.org' => 2,
7916
+ 'at.eu.org' => 2,
7917
+ 'au.eu.org' => 2,
7918
+ 'be.eu.org' => 2,
7919
+ 'bg.eu.org' => 2,
7920
+ 'ca.eu.org' => 2,
7921
+ 'cd.eu.org' => 2,
7922
+ 'ch.eu.org' => 2,
7923
+ 'cn.eu.org' => 2,
7924
+ 'cy.eu.org' => 2,
7925
+ 'cz.eu.org' => 2,
7926
+ 'de.eu.org' => 2,
7927
+ 'dk.eu.org' => 2,
7928
+ 'edu.eu.org' => 2,
7929
+ 'ee.eu.org' => 2,
7930
+ 'es.eu.org' => 2,
7931
+ 'fi.eu.org' => 2,
7932
+ 'fr.eu.org' => 2,
7933
+ 'gr.eu.org' => 2,
7934
+ 'hr.eu.org' => 2,
7935
+ 'hu.eu.org' => 2,
7936
+ 'ie.eu.org' => 2,
7937
+ 'il.eu.org' => 2,
7938
+ 'in.eu.org' => 2,
7939
+ 'int.eu.org' => 2,
7940
+ 'is.eu.org' => 2,
7941
+ 'it.eu.org' => 2,
7942
+ 'jp.eu.org' => 2,
7943
+ 'kr.eu.org' => 2,
7944
+ 'lt.eu.org' => 2,
7945
+ 'lu.eu.org' => 2,
7946
+ 'lv.eu.org' => 2,
7947
+ 'mc.eu.org' => 2,
7948
+ 'me.eu.org' => 2,
7949
+ 'mk.eu.org' => 2,
7950
+ 'mt.eu.org' => 2,
7951
+ 'my.eu.org' => 2,
7952
+ 'net.eu.org' => 2,
7953
+ 'ng.eu.org' => 2,
7954
+ 'nl.eu.org' => 2,
7955
+ 'no.eu.org' => 2,
7956
+ 'nz.eu.org' => 2,
7957
+ 'paris.eu.org' => 2,
7958
+ 'pl.eu.org' => 2,
7959
+ 'pt.eu.org' => 2,
7960
+ 'q-a.eu.org' => 2,
7961
+ 'ro.eu.org' => 2,
7962
+ 'ru.eu.org' => 2,
7963
+ 'se.eu.org' => 2,
7964
+ 'si.eu.org' => 2,
7965
+ 'sk.eu.org' => 2,
7966
+ 'tr.eu.org' => 2,
7967
+ 'uk.eu.org' => 2,
7968
+ 'us.eu.org' => 2,
7969
+ 'eu-1.evennode.com' => 2,
7970
+ 'eu-2.evennode.com' => 2,
7971
+ 'eu-3.evennode.com' => 2,
7972
+ 'eu-4.evennode.com' => 2,
7973
+ 'us-1.evennode.com' => 2,
7974
+ 'us-2.evennode.com' => 2,
7975
+ 'us-3.evennode.com' => 2,
7976
+ 'us-4.evennode.com' => 2,
7977
+ 'twmail.cc' => 2,
7978
+ 'twmail.net' => 2,
7979
+ 'twmail.org' => 2,
7980
+ 'mymailer.com.tw' => 2,
7981
+ 'url.tw' => 2,
7982
+ 'apps.fbsbx.com' => 2,
7983
+ 'ru.net' => 2,
7984
+ 'adygeya.ru' => 2,
7985
+ 'bashkiria.ru' => 2,
7986
+ 'bir.ru' => 2,
7987
+ 'cbg.ru' => 2,
7988
+ 'com.ru' => 2,
7989
+ 'dagestan.ru' => 2,
7990
+ 'grozny.ru' => 2,
7991
+ 'kalmykia.ru' => 2,
7992
+ 'kustanai.ru' => 2,
7993
+ 'marine.ru' => 2,
7994
+ 'mordovia.ru' => 2,
7995
+ 'msk.ru' => 2,
7996
+ 'mytis.ru' => 2,
7997
+ 'nalchik.ru' => 2,
7998
+ 'nov.ru' => 2,
7999
+ 'pyatigorsk.ru' => 2,
8000
+ 'spb.ru' => 2,
8001
+ 'vladikavkaz.ru' => 2,
8002
+ 'vladimir.ru' => 2,
8003
+ 'abkhazia.su' => 2,
8004
+ 'adygeya.su' => 2,
8005
+ 'aktyubinsk.su' => 2,
8006
+ 'arkhangelsk.su' => 2,
8007
+ 'armenia.su' => 2,
8008
+ 'ashgabad.su' => 2,
8009
+ 'azerbaijan.su' => 2,
8010
+ 'balashov.su' => 2,
8011
+ 'bashkiria.su' => 2,
8012
+ 'bryansk.su' => 2,
8013
+ 'bukhara.su' => 2,
8014
+ 'chimkent.su' => 2,
8015
+ 'dagestan.su' => 2,
8016
+ 'east-kazakhstan.su' => 2,
8017
+ 'exnet.su' => 2,
8018
+ 'georgia.su' => 2,
8019
+ 'grozny.su' => 2,
8020
+ 'ivanovo.su' => 2,
8021
+ 'jambyl.su' => 2,
8022
+ 'kalmykia.su' => 2,
8023
+ 'kaluga.su' => 2,
8024
+ 'karacol.su' => 2,
8025
+ 'karaganda.su' => 2,
8026
+ 'karelia.su' => 2,
8027
+ 'khakassia.su' => 2,
8028
+ 'krasnodar.su' => 2,
8029
+ 'kurgan.su' => 2,
8030
+ 'kustanai.su' => 2,
8031
+ 'lenug.su' => 2,
8032
+ 'mangyshlak.su' => 2,
8033
+ 'mordovia.su' => 2,
8034
+ 'msk.su' => 2,
8035
+ 'murmansk.su' => 2,
8036
+ 'nalchik.su' => 2,
8037
+ 'navoi.su' => 2,
8038
+ 'north-kazakhstan.su' => 2,
8039
+ 'nov.su' => 2,
8040
+ 'obninsk.su' => 2,
8041
+ 'penza.su' => 2,
8042
+ 'pokrovsk.su' => 2,
8043
+ 'sochi.su' => 2,
8044
+ 'spb.su' => 2,
8045
+ 'tashkent.su' => 2,
8046
+ 'termez.su' => 2,
8047
+ 'togliatti.su' => 2,
8048
+ 'troitsk.su' => 2,
8049
+ 'tselinograd.su' => 2,
8050
+ 'tula.su' => 2,
8051
+ 'tuva.su' => 2,
8052
+ 'vladikavkaz.su' => 2,
8053
+ 'vladimir.su' => 2,
8054
+ 'vologda.su' => 2,
8055
+ 'channelsdvr.net' => 2,
8056
+ 'fastly-terrarium.com' => 2,
8057
+ 'fastlylb.net' => 2,
8058
+ 'map.fastlylb.net' => 2,
8059
+ 'freetls.fastly.net' => 2,
8060
+ 'map.fastly.net' => 2,
8061
+ 'a.prod.fastly.net' => 2,
8062
+ 'global.prod.fastly.net' => 2,
8063
+ 'a.ssl.fastly.net' => 2,
8064
+ 'b.ssl.fastly.net' => 2,
8065
+ 'global.ssl.fastly.net' => 2,
8066
+ 'fastpanel.direct' => 2,
8067
+ 'fastvps-server.com' => 2,
8068
+ 'fhapp.xyz' => 2,
8069
+ 'fedorainfracloud.org' => 2,
8070
+ 'fedorapeople.org' => 2,
8071
+ 'cloud.fedoraproject.org' => 2,
8072
+ 'app.os.fedoraproject.org' => 2,
8073
+ 'app.os.stg.fedoraproject.org' => 2,
8074
+ 'mydobiss.com' => 2,
8075
+ 'filegear.me' => 2,
8076
+ 'filegear-au.me' => 2,
8077
+ 'filegear-de.me' => 2,
8078
+ 'filegear-gb.me' => 2,
8079
+ 'filegear-ie.me' => 2,
8080
+ 'filegear-jp.me' => 2,
8081
+ 'filegear-sg.me' => 2,
8082
+ 'firebaseapp.com' => 2,
8083
+ 'flynnhub.com' => 2,
8084
+ 'flynnhosting.net' => 2,
8085
+ 'freebox-os.com' => 2,
8086
+ 'freeboxos.com' => 2,
8087
+ 'fbx-os.fr' => 2,
8088
+ 'fbxos.fr' => 2,
8089
+ 'freebox-os.fr' => 2,
8090
+ 'freeboxos.fr' => 2,
8091
+ 'freedesktop.org' => 2,
8092
+ '*.futurecms.at' => 2,
8093
+ '*.ex.futurecms.at' => 2,
8094
+ '*.in.futurecms.at' => 2,
8095
+ 'futurehosting.at' => 2,
8096
+ 'futuremailing.at' => 2,
8097
+ '*.ex.ortsinfo.at' => 2,
8098
+ '*.kunden.ortsinfo.at' => 2,
8099
+ '*.statics.cloud' => 2,
8100
+ 'service.gov.uk' => 2,
8101
+ 'gehirn.ne.jp' => 2,
8102
+ 'usercontent.jp' => 2,
8103
+ 'lab.ms' => 2,
8104
+ 'github.io' => 2,
8105
+ 'githubusercontent.com' => 2,
8106
+ 'gitlab.io' => 2,
8107
+ 'glitch.me' => 2,
8108
+ 'cloudapps.digital' => 2,
8109
+ 'london.cloudapps.digital' => 2,
8110
+ 'homeoffice.gov.uk' => 2,
8111
+ 'ro.im' => 2,
8112
+ 'shop.ro' => 2,
8113
+ 'goip.de' => 2,
8114
+ 'run.app' => 2,
8115
+ 'a.run.app' => 2,
8116
+ 'web.app' => 2,
8117
+ '*.0emm.com' => 2,
8118
+ 'appspot.com' => 2,
8119
+ 'blogspot.ae' => 2,
8120
+ 'blogspot.al' => 2,
8121
+ 'blogspot.am' => 2,
8122
+ 'blogspot.ba' => 2,
8123
+ 'blogspot.be' => 2,
8124
+ 'blogspot.bg' => 2,
8125
+ 'blogspot.bj' => 2,
8126
+ 'blogspot.ca' => 2,
8127
+ 'blogspot.cf' => 2,
8128
+ 'blogspot.ch' => 2,
8129
+ 'blogspot.cl' => 2,
8130
+ 'blogspot.co.at' => 2,
8131
+ 'blogspot.co.id' => 2,
8132
+ 'blogspot.co.il' => 2,
8133
+ 'blogspot.co.ke' => 2,
8134
+ 'blogspot.co.nz' => 2,
8135
+ 'blogspot.co.uk' => 2,
8136
+ 'blogspot.co.za' => 2,
8137
+ 'blogspot.com' => 2,
8138
+ 'blogspot.com.ar' => 2,
8139
+ 'blogspot.com.au' => 2,
8140
+ 'blogspot.com.br' => 2,
8141
+ 'blogspot.com.by' => 2,
8142
+ 'blogspot.com.co' => 2,
8143
+ 'blogspot.com.cy' => 2,
8144
+ 'blogspot.com.ee' => 2,
8145
+ 'blogspot.com.eg' => 2,
8146
+ 'blogspot.com.es' => 2,
8147
+ 'blogspot.com.mt' => 2,
8148
+ 'blogspot.com.ng' => 2,
8149
+ 'blogspot.com.tr' => 2,
8150
+ 'blogspot.com.uy' => 2,
8151
+ 'blogspot.cv' => 2,
8152
+ 'blogspot.cz' => 2,
8153
+ 'blogspot.de' => 2,
8154
+ 'blogspot.dk' => 2,
8155
+ 'blogspot.fi' => 2,
8156
+ 'blogspot.fr' => 2,
8157
+ 'blogspot.gr' => 2,
8158
+ 'blogspot.hk' => 2,
8159
+ 'blogspot.hr' => 2,
8160
+ 'blogspot.hu' => 2,
8161
+ 'blogspot.ie' => 2,
8162
+ 'blogspot.in' => 2,
8163
+ 'blogspot.is' => 2,
8164
+ 'blogspot.it' => 2,
8165
+ 'blogspot.jp' => 2,
8166
+ 'blogspot.kr' => 2,
8167
+ 'blogspot.li' => 2,
8168
+ 'blogspot.lt' => 2,
8169
+ 'blogspot.lu' => 2,
8170
+ 'blogspot.md' => 2,
8171
+ 'blogspot.mk' => 2,
8172
+ 'blogspot.mr' => 2,
8173
+ 'blogspot.mx' => 2,
8174
+ 'blogspot.my' => 2,
8175
+ 'blogspot.nl' => 2,
8176
+ 'blogspot.no' => 2,
8177
+ 'blogspot.pe' => 2,
8178
+ 'blogspot.pt' => 2,
8179
+ 'blogspot.qa' => 2,
8180
+ 'blogspot.re' => 2,
8181
+ 'blogspot.ro' => 2,
8182
+ 'blogspot.rs' => 2,
8183
+ 'blogspot.ru' => 2,
8184
+ 'blogspot.se' => 2,
8185
+ 'blogspot.sg' => 2,
8186
+ 'blogspot.si' => 2,
8187
+ 'blogspot.sk' => 2,
8188
+ 'blogspot.sn' => 2,
8189
+ 'blogspot.td' => 2,
8190
+ 'blogspot.tw' => 2,
8191
+ 'blogspot.ug' => 2,
8192
+ 'blogspot.vn' => 2,
8193
+ 'cloudfunctions.net' => 2,
8194
+ 'cloud.goog' => 2,
8195
+ 'codespot.com' => 2,
8196
+ 'googleapis.com' => 2,
8197
+ 'googlecode.com' => 2,
8198
+ 'pagespeedmobilizer.com' => 2,
8199
+ 'publishproxy.com' => 2,
8200
+ 'withgoogle.com' => 2,
8201
+ 'withyoutube.com' => 2,
8202
+ 'fin.ci' => 2,
8203
+ 'free.hr' => 2,
8204
+ 'caa.li' => 2,
8205
+ 'ua.rs' => 2,
8206
+ 'conf.se' => 2,
8207
+ 'hs.zone' => 2,
8208
+ 'hs.run' => 2,
8209
+ 'hashbang.sh' => 2,
8210
+ 'hasura.app' => 2,
8211
+ 'hasura-app.io' => 2,
8212
+ 'hepforge.org' => 2,
8213
+ 'herokuapp.com' => 2,
8214
+ 'herokussl.com' => 2,
8215
+ 'myravendb.com' => 2,
8216
+ 'ravendb.community' => 2,
8217
+ 'ravendb.me' => 2,
8218
+ 'development.run' => 2,
8219
+ 'ravendb.run' => 2,
8220
+ 'bpl.biz' => 2,
8221
+ 'orx.biz' => 2,
8222
+ 'ng.city' => 2,
8223
+ 'biz.gl' => 2,
8224
+ 'ng.ink' => 2,
8225
+ 'col.ng' => 2,
8226
+ 'firm.ng' => 2,
8227
+ 'gen.ng' => 2,
8228
+ 'ltd.ng' => 2,
8229
+ 'ng.school' => 2,
8230
+ 'sch.so' => 2,
8231
+ 'häkkinen.fi' => 2,
8232
+ '*.moonscale.io' => 2,
8233
+ 'moonscale.net' => 2,
8234
+ 'iki.fi' => 2,
8235
+ 'dyn-berlin.de' => 2,
8236
+ 'in-berlin.de' => 2,
8237
+ 'in-brb.de' => 2,
8238
+ 'in-butter.de' => 2,
8239
+ 'in-dsl.de' => 2,
8240
+ 'in-dsl.net' => 2,
8241
+ 'in-dsl.org' => 2,
8242
+ 'in-vpn.de' => 2,
8243
+ 'in-vpn.net' => 2,
8244
+ 'in-vpn.org' => 2,
8245
+ 'biz.at' => 2,
8246
+ 'info.at' => 2,
8247
+ 'info.cx' => 2,
8248
+ 'ac.leg.br' => 2,
8249
+ 'al.leg.br' => 2,
8250
+ 'am.leg.br' => 2,
8251
+ 'ap.leg.br' => 2,
8252
+ 'ba.leg.br' => 2,
8253
+ 'ce.leg.br' => 2,
8254
+ 'df.leg.br' => 2,
8255
+ 'es.leg.br' => 2,
8256
+ 'go.leg.br' => 2,
8257
+ 'ma.leg.br' => 2,
8258
+ 'mg.leg.br' => 2,
8259
+ 'ms.leg.br' => 2,
8260
+ 'mt.leg.br' => 2,
8261
+ 'pa.leg.br' => 2,
8262
+ 'pb.leg.br' => 2,
8263
+ 'pe.leg.br' => 2,
8264
+ 'pi.leg.br' => 2,
8265
+ 'pr.leg.br' => 2,
8266
+ 'rj.leg.br' => 2,
8267
+ 'rn.leg.br' => 2,
8268
+ 'ro.leg.br' => 2,
8269
+ 'rr.leg.br' => 2,
8270
+ 'rs.leg.br' => 2,
8271
+ 'sc.leg.br' => 2,
8272
+ 'se.leg.br' => 2,
8273
+ 'sp.leg.br' => 2,
8274
+ 'to.leg.br' => 2,
8275
+ 'pixolino.com' => 2,
8276
+ 'ipifony.net' => 2,
8277
+ 'mein-iserv.de' => 2,
8278
+ 'test-iserv.de' => 2,
8279
+ 'iserv.dev' => 2,
8280
+ 'iobb.net' => 2,
8281
+ 'myjino.ru' => 2,
8282
+ '*.hosting.myjino.ru' => 2,
8283
+ '*.landing.myjino.ru' => 2,
8284
+ '*.spectrum.myjino.ru' => 2,
8285
+ '*.vps.myjino.ru' => 2,
8286
+ '*.triton.zone' => 2,
8287
+ '*.cns.joyent.com' => 2,
8288
+ 'js.org' => 2,
8289
+ 'kaas.gg' => 2,
8290
+ 'khplay.nl' => 2,
8291
+ 'keymachine.de' => 2,
8292
+ 'kinghost.net' => 2,
8293
+ 'uni5.net' => 2,
8294
+ 'knightpoint.systems' => 2,
8295
+ 'co.krd' => 2,
8296
+ 'edu.krd' => 2,
8297
+ 'git-repos.de' => 2,
8298
+ 'lcube-server.de' => 2,
8299
+ 'svn-repos.de' => 2,
8300
+ 'leadpages.co' => 2,
8301
+ 'lpages.co' => 2,
8302
+ 'lpusercontent.com' => 2,
8303
+ 'lelux.site' => 2,
8304
+ 'co.business' => 2,
8305
+ 'co.education' => 2,
8306
+ 'co.events' => 2,
8307
+ 'co.financial' => 2,
8308
+ 'co.network' => 2,
8309
+ 'co.place' => 2,
8310
+ 'co.technology' => 2,
8311
+ 'app.lmpm.com' => 2,
8312
+ 'linkitools.space' => 2,
8313
+ 'linkyard.cloud' => 2,
8314
+ 'linkyard-cloud.ch' => 2,
8315
+ 'members.linode.com' => 2,
8316
+ 'nodebalancer.linode.com' => 2,
8317
+ 'we.bs' => 2,
8318
+ 'loginline.app' => 2,
8319
+ 'loginline.dev' => 2,
8320
+ 'loginline.io' => 2,
8321
+ 'loginline.services' => 2,
8322
+ 'loginline.site' => 2,
8323
+ 'krasnik.pl' => 2,
8324
+ 'leczna.pl' => 2,
8325
+ 'lubartow.pl' => 2,
8326
+ 'lublin.pl' => 2,
8327
+ 'poniatowa.pl' => 2,
8328
+ 'swidnik.pl' => 2,
8329
+ 'uklugs.org' => 2,
8330
+ 'glug.org.uk' => 2,
8331
+ 'lug.org.uk' => 2,
8332
+ 'lugs.org.uk' => 2,
8333
+ 'barsy.bg' => 2,
8334
+ 'barsy.co.uk' => 2,
8335
+ 'barsyonline.co.uk' => 2,
8336
+ 'barsycenter.com' => 2,
8337
+ 'barsyonline.com' => 2,
8338
+ 'barsy.club' => 2,
8339
+ 'barsy.de' => 2,
8340
+ 'barsy.eu' => 2,
8341
+ 'barsy.in' => 2,
8342
+ 'barsy.info' => 2,
8343
+ 'barsy.io' => 2,
8344
+ 'barsy.me' => 2,
8345
+ 'barsy.menu' => 2,
8346
+ 'barsy.mobi' => 2,
8347
+ 'barsy.net' => 2,
8348
+ 'barsy.online' => 2,
8349
+ 'barsy.org' => 2,
8350
+ 'barsy.pro' => 2,
8351
+ 'barsy.pub' => 2,
8352
+ 'barsy.shop' => 2,
8353
+ 'barsy.site' => 2,
8354
+ 'barsy.support' => 2,
8355
+ 'barsy.uk' => 2,
8356
+ '*.magentosite.cloud' => 2,
8357
+ 'mayfirst.info' => 2,
8358
+ 'mayfirst.org' => 2,
8359
+ 'hb.cldmail.ru' => 2,
8360
+ 'miniserver.com' => 2,
8361
+ 'memset.net' => 2,
8362
+ 'cloud.metacentrum.cz' => 2,
8363
+ 'custom.metacentrum.cz' => 2,
8364
+ 'flt.cloud.muni.cz' => 2,
8365
+ 'usr.cloud.muni.cz' => 2,
8366
+ 'meteorapp.com' => 2,
8367
+ 'eu.meteorapp.com' => 2,
8368
+ 'co.pl' => 2,
8369
+ 'azurecontainer.io' => 2,
8370
+ 'azurewebsites.net' => 2,
8371
+ 'azure-mobile.net' => 2,
8372
+ 'cloudapp.net' => 2,
8373
+ 'mozilla-iot.org' => 2,
8374
+ 'bmoattachments.org' => 2,
8375
+ 'net.ru' => 2,
8376
+ 'org.ru' => 2,
8377
+ 'pp.ru' => 2,
8378
+ 'ui.nabu.casa' => 2,
8379
+ 'pony.club' => 2,
8380
+ 'of.fashion' => 2,
8381
+ 'on.fashion' => 2,
8382
+ 'of.football' => 2,
8383
+ 'in.london' => 2,
8384
+ 'of.london' => 2,
8385
+ 'for.men' => 2,
8386
+ 'and.mom' => 2,
8387
+ 'for.mom' => 2,
8388
+ 'for.one' => 2,
8389
+ 'for.sale' => 2,
8390
+ 'of.work' => 2,
8391
+ 'to.work' => 2,
8392
+ 'nctu.me' => 2,
8393
+ 'bitballoon.com' => 2,
8394
+ 'netlify.com' => 2,
8395
+ '4u.com' => 2,
8396
+ 'ngrok.io' => 2,
8397
+ 'nh-serv.co.uk' => 2,
8398
+ 'nfshost.com' => 2,
8399
+ 'dnsking.ch' => 2,
8400
+ 'mypi.co' => 2,
8401
+ 'n4t.co' => 2,
8402
+ '001www.com' => 2,
8403
+ 'ddnslive.com' => 2,
8404
+ 'myiphost.com' => 2,
8405
+ 'forumz.info' => 2,
8406
+ '16-b.it' => 2,
8407
+ '32-b.it' => 2,
8408
+ '64-b.it' => 2,
8409
+ 'soundcast.me' => 2,
8410
+ 'tcp4.me' => 2,
8411
+ 'dnsup.net' => 2,
8412
+ 'hicam.net' => 2,
8413
+ 'now-dns.net' => 2,
8414
+ 'ownip.net' => 2,
8415
+ 'vpndns.net' => 2,
8416
+ 'dynserv.org' => 2,
8417
+ 'now-dns.org' => 2,
8418
+ 'x443.pw' => 2,
8419
+ 'now-dns.top' => 2,
8420
+ 'ntdll.top' => 2,
8421
+ 'freeddns.us' => 2,
8422
+ 'crafting.xyz' => 2,
8423
+ 'zapto.xyz' => 2,
8424
+ 'nsupdate.info' => 2,
8425
+ 'nerdpol.ovh' => 2,
8426
+ 'blogsyte.com' => 2,
8427
+ 'brasilia.me' => 2,
8428
+ 'cable-modem.org' => 2,
8429
+ 'ciscofreak.com' => 2,
8430
+ 'collegefan.org' => 2,
8431
+ 'couchpotatofries.org' => 2,
8432
+ 'damnserver.com' => 2,
8433
+ 'ddns.me' => 2,
8434
+ 'ditchyourip.com' => 2,
8435
+ 'dnsfor.me' => 2,
8436
+ 'dnsiskinky.com' => 2,
8437
+ 'dvrcam.info' => 2,
8438
+ 'dynns.com' => 2,
8439
+ 'eating-organic.net' => 2,
8440
+ 'fantasyleague.cc' => 2,
8441
+ 'geekgalaxy.com' => 2,
8442
+ 'golffan.us' => 2,
8443
+ 'health-carereform.com' => 2,
8444
+ 'homesecuritymac.com' => 2,
8445
+ 'homesecuritypc.com' => 2,
8446
+ 'hopto.me' => 2,
8447
+ 'ilovecollege.info' => 2,
8448
+ 'loginto.me' => 2,
8449
+ 'mlbfan.org' => 2,
8450
+ 'mmafan.biz' => 2,
8451
+ 'myactivedirectory.com' => 2,
8452
+ 'mydissent.net' => 2,
8453
+ 'myeffect.net' => 2,
8454
+ 'mymediapc.net' => 2,
8455
+ 'mypsx.net' => 2,
8456
+ 'mysecuritycamera.com' => 2,
8457
+ 'mysecuritycamera.net' => 2,
8458
+ 'mysecuritycamera.org' => 2,
8459
+ 'net-freaks.com' => 2,
8460
+ 'nflfan.org' => 2,
8461
+ 'nhlfan.net' => 2,
8462
+ 'no-ip.ca' => 2,
8463
+ 'no-ip.co.uk' => 2,
8464
+ 'no-ip.net' => 2,
8465
+ 'noip.us' => 2,
8466
+ 'onthewifi.com' => 2,
8467
+ 'pgafan.net' => 2,
8468
+ 'point2this.com' => 2,
8469
+ 'pointto.us' => 2,
8470
+ 'privatizehealthinsurance.net' => 2,
8471
+ 'quicksytes.com' => 2,
8472
+ 'read-books.org' => 2,
8473
+ 'securitytactics.com' => 2,
8474
+ 'serveexchange.com' => 2,
8475
+ 'servehumour.com' => 2,
8476
+ 'servep2p.com' => 2,
8477
+ 'servesarcasm.com' => 2,
8478
+ 'stufftoread.com' => 2,
8479
+ 'ufcfan.org' => 2,
8480
+ 'unusualperson.com' => 2,
8481
+ 'workisboring.com' => 2,
8482
+ '3utilities.com' => 2,
8483
+ 'bounceme.net' => 2,
8484
+ 'ddns.net' => 2,
8485
+ 'ddnsking.com' => 2,
8486
+ 'gotdns.ch' => 2,
8487
+ 'hopto.org' => 2,
8488
+ 'myftp.biz' => 2,
8489
+ 'myftp.org' => 2,
8490
+ 'myvnc.com' => 2,
8491
+ 'no-ip.biz' => 2,
8492
+ 'no-ip.info' => 2,
8493
+ 'no-ip.org' => 2,
8494
+ 'noip.me' => 2,
8495
+ 'redirectme.net' => 2,
8496
+ 'servebeer.com' => 2,
8497
+ 'serveblog.net' => 2,
8498
+ 'servecounterstrike.com' => 2,
8499
+ 'serveftp.com' => 2,
8500
+ 'servegame.com' => 2,
8501
+ 'servehalflife.com' => 2,
8502
+ 'servehttp.com' => 2,
8503
+ 'serveirc.com' => 2,
8504
+ 'serveminecraft.net' => 2,
8505
+ 'servemp3.com' => 2,
8506
+ 'servepics.com' => 2,
8507
+ 'servequake.com' => 2,
8508
+ 'sytes.net' => 2,
8509
+ 'webhop.me' => 2,
8510
+ 'zapto.org' => 2,
8511
+ 'stage.nodeart.io' => 2,
8512
+ 'nodum.co' => 2,
8513
+ 'nodum.io' => 2,
8514
+ 'pcloud.host' => 2,
8515
+ 'nyc.mn' => 2,
8516
+ 'nom.ae' => 2,
8517
+ 'nom.af' => 2,
8518
+ 'nom.ai' => 2,
8519
+ 'nom.al' => 2,
8520
+ 'nym.by' => 2,
8521
+ 'nym.bz' => 2,
8522
+ 'nom.cl' => 2,
8523
+ 'nym.ec' => 2,
8524
+ 'nom.gd' => 2,
8525
+ 'nom.ge' => 2,
8526
+ 'nom.gl' => 2,
8527
+ 'nym.gr' => 2,
8528
+ 'nom.gt' => 2,
8529
+ 'nym.gy' => 2,
8530
+ 'nym.hk' => 2,
8531
+ 'nom.hn' => 2,
8532
+ 'nym.ie' => 2,
8533
+ 'nom.im' => 2,
8534
+ 'nom.ke' => 2,
8535
+ 'nym.kz' => 2,
8536
+ 'nym.la' => 2,
8537
+ 'nym.lc' => 2,
8538
+ 'nom.li' => 2,
8539
+ 'nym.li' => 2,
8540
+ 'nym.lt' => 2,
8541
+ 'nym.lu' => 2,
8542
+ 'nym.me' => 2,
8543
+ 'nom.mk' => 2,
8544
+ 'nym.mn' => 2,
8545
+ 'nym.mx' => 2,
8546
+ 'nom.nu' => 2,
8547
+ 'nym.nz' => 2,
8548
+ 'nym.pe' => 2,
8549
+ 'nym.pt' => 2,
8550
+ 'nom.pw' => 2,
8551
+ 'nom.qa' => 2,
8552
+ 'nym.ro' => 2,
8553
+ 'nom.rs' => 2,
8554
+ 'nom.si' => 2,
8555
+ 'nym.sk' => 2,
8556
+ 'nom.st' => 2,
8557
+ 'nym.su' => 2,
8558
+ 'nym.sx' => 2,
8559
+ 'nom.tj' => 2,
8560
+ 'nym.tw' => 2,
8561
+ 'nom.ug' => 2,
8562
+ 'nom.uy' => 2,
8563
+ 'nom.vc' => 2,
8564
+ 'nom.vg' => 2,
8565
+ 'cya.gg' => 2,
8566
+ 'cloudycluster.net' => 2,
8567
+ 'nid.io' => 2,
8568
+ 'opencraft.hosting' => 2,
8569
+ 'operaunite.com' => 2,
8570
+ 'outsystemscloud.com' => 2,
8571
+ 'ownprovider.com' => 2,
8572
+ 'own.pm' => 2,
8573
+ 'ox.rs' => 2,
8574
+ 'oy.lc' => 2,
8575
+ 'pgfog.com' => 2,
8576
+ 'pagefrontapp.com' => 2,
8577
+ 'art.pl' => 2,
8578
+ 'gliwice.pl' => 2,
8579
+ 'krakow.pl' => 2,
8580
+ 'poznan.pl' => 2,
8581
+ 'wroc.pl' => 2,
8582
+ 'zakopane.pl' => 2,
8583
+ 'pantheonsite.io' => 2,
8584
+ 'gotpantheon.com' => 2,
8585
+ 'mypep.link' => 2,
8586
+ 'on-web.fr' => 2,
8587
+ '*.platform.sh' => 2,
8588
+ '*.platformsh.site' => 2,
8589
+ 'dyn53.io' => 2,
8590
+ 'co.bn' => 2,
8591
+ 'xen.prgmr.com' => 2,
8592
+ 'priv.at' => 2,
8593
+ 'prvcy.page' => 2,
8594
+ '*.dweb.link' => 2,
8595
+ 'protonet.io' => 2,
8596
+ 'chirurgiens-dentistes-en-france.fr' => 2,
8597
+ 'byen.site' => 2,
8598
+ 'pubtls.org' => 2,
8599
+ 'qualifioapp.com' => 2,
8600
+ 'instantcloud.cn' => 2,
8601
+ 'ras.ru' => 2,
8602
+ 'qa2.com' => 2,
8603
+ 'dev-myqnapcloud.com' => 2,
8604
+ 'alpha-myqnapcloud.com' => 2,
8605
+ 'myqnapcloud.com' => 2,
8606
+ '*.quipelements.com' => 2,
8607
+ 'vapor.cloud' => 2,
8608
+ 'vaporcloud.io' => 2,
8609
+ 'rackmaze.com' => 2,
8610
+ 'rackmaze.net' => 2,
8611
+ '*.on-rancher.cloud' => 2,
8612
+ '*.on-rio.io' => 2,
8613
+ 'readthedocs.io' => 2,
8614
+ 'rhcloud.com' => 2,
8615
+ 'app.render.com' => 2,
8616
+ 'onrender.com' => 2,
8617
+ 'repl.co' => 2,
8618
+ 'repl.run' => 2,
8619
+ 'resindevice.io' => 2,
8620
+ 'devices.resinstaging.io' => 2,
8621
+ 'hzc.io' => 2,
8622
+ 'wellbeingzone.eu' => 2,
8623
+ 'ptplus.fit' => 2,
8624
+ 'wellbeingzone.co.uk' => 2,
8625
+ 'git-pages.rit.edu' => 2,
8626
+ 'sandcats.io' => 2,
8627
+ 'logoip.de' => 2,
8628
+ 'logoip.com' => 2,
8629
+ 'schokokeks.net' => 2,
8630
+ 'scrysec.com' => 2,
8631
+ 'firewall-gateway.com' => 2,
8632
+ 'firewall-gateway.de' => 2,
8633
+ 'my-gateway.de' => 2,
8634
+ 'my-router.de' => 2,
8635
+ 'spdns.de' => 2,
8636
+ 'spdns.eu' => 2,
8637
+ 'firewall-gateway.net' => 2,
8638
+ 'my-firewall.org' => 2,
8639
+ 'myfirewall.org' => 2,
8640
+ 'spdns.org' => 2,
8641
+ 'biz.ua' => 2,
8642
+ 'co.ua' => 2,
8643
+ 'pp.ua' => 2,
8644
+ 'shiftedit.io' => 2,
8645
+ 'myshopblocks.com' => 2,
8646
+ 'shopitsite.com' => 2,
8647
+ 'mo-siemens.io' => 2,
8648
+ '1kapp.com' => 2,
8649
+ 'appchizi.com' => 2,
8650
+ 'applinzi.com' => 2,
8651
+ 'sinaapp.com' => 2,
8652
+ 'vipsinaapp.com' => 2,
8653
+ 'siteleaf.net' => 2,
8654
+ 'bounty-full.com' => 2,
8655
+ 'alpha.bounty-full.com' => 2,
8656
+ 'beta.bounty-full.com' => 2,
8657
+ 'stackhero-network.com' => 2,
8658
+ 'static.land' => 2,
8659
+ 'dev.static.land' => 2,
8660
+ 'sites.static.land' => 2,
8661
+ 'apps.lair.io' => 2,
8662
+ '*.stolos.io' => 2,
8663
+ 'spacekit.io' => 2,
8664
+ 'customer.speedpartner.de' => 2,
8665
+ 'api.stdlib.com' => 2,
8666
+ 'storj.farm' => 2,
8667
+ 'utwente.io' => 2,
8668
+ 'soc.srcf.net' => 2,
8669
+ 'user.srcf.net' => 2,
8670
+ 'temp-dns.com' => 2,
8671
+ 'applicationcloud.io' => 2,
8672
+ 'scapp.io' => 2,
8673
+ '*.s5y.io' => 2,
8674
+ '*.sensiosite.cloud' => 2,
8675
+ 'syncloud.it' => 2,
8676
+ 'diskstation.me' => 2,
8677
+ 'dscloud.biz' => 2,
8678
+ 'dscloud.me' => 2,
8679
+ 'dscloud.mobi' => 2,
8680
+ 'dsmynas.com' => 2,
8681
+ 'dsmynas.net' => 2,
8682
+ 'dsmynas.org' => 2,
8683
+ 'familyds.com' => 2,
8684
+ 'familyds.net' => 2,
8685
+ 'familyds.org' => 2,
8686
+ 'i234.me' => 2,
8687
+ 'myds.me' => 2,
8688
+ 'synology.me' => 2,
8689
+ 'vpnplus.to' => 2,
8690
+ 'taifun-dns.de' => 2,
8691
+ 'gda.pl' => 2,
8692
+ 'gdansk.pl' => 2,
8693
+ 'gdynia.pl' => 2,
8694
+ 'med.pl' => 2,
8695
+ 'sopot.pl' => 2,
8696
+ 'edugit.org' => 2,
8697
+ 'telebit.app' => 2,
8698
+ 'telebit.io' => 2,
8699
+ '*.telebit.xyz' => 2,
8700
+ 'gwiddle.co.uk' => 2,
8701
+ 'thingdustdata.com' => 2,
8702
+ 'cust.dev.thingdust.io' => 2,
8703
+ 'cust.disrec.thingdust.io' => 2,
8704
+ 'cust.prod.thingdust.io' => 2,
8705
+ 'cust.testing.thingdust.io' => 2,
8706
+ 'arvo.network' => 2,
8707
+ 'azimuth.network' => 2,
8708
+ 'bloxcms.com' => 2,
8709
+ 'townnews-staging.com' => 2,
8710
+ '12hp.at' => 2,
8711
+ '2ix.at' => 2,
8712
+ '4lima.at' => 2,
8713
+ 'lima-city.at' => 2,
8714
+ '12hp.ch' => 2,
8715
+ '2ix.ch' => 2,
8716
+ '4lima.ch' => 2,
8717
+ 'lima-city.ch' => 2,
8718
+ 'trafficplex.cloud' => 2,
8719
+ 'de.cool' => 2,
8720
+ '12hp.de' => 2,
8721
+ '2ix.de' => 2,
8722
+ '4lima.de' => 2,
8723
+ 'lima-city.de' => 2,
8724
+ '1337.pictures' => 2,
8725
+ 'clan.rip' => 2,
8726
+ 'lima-city.rocks' => 2,
8727
+ 'webspace.rocks' => 2,
8728
+ 'lima.zone' => 2,
8729
+ '*.transurl.be' => 2,
8730
+ '*.transurl.eu' => 2,
8731
+ '*.transurl.nl' => 2,
8732
+ 'tuxfamily.org' => 2,
8733
+ 'dd-dns.de' => 2,
8734
+ 'diskstation.eu' => 2,
8735
+ 'diskstation.org' => 2,
8736
+ 'dray-dns.de' => 2,
8737
+ 'draydns.de' => 2,
8738
+ 'dyn-vpn.de' => 2,
8739
+ 'dynvpn.de' => 2,
8740
+ 'mein-vigor.de' => 2,
8741
+ 'my-vigor.de' => 2,
8742
+ 'my-wan.de' => 2,
8743
+ 'syno-ds.de' => 2,
8744
+ 'synology-diskstation.de' => 2,
8745
+ 'synology-ds.de' => 2,
8746
+ 'uber.space' => 2,
8747
+ '*.uberspace.de' => 2,
8748
+ 'hk.com' => 2,
8749
+ 'hk.org' => 2,
8750
+ 'ltd.hk' => 2,
8751
+ 'inc.hk' => 2,
8752
+ 'virtualuser.de' => 2,
8753
+ 'virtual-user.de' => 2,
8754
+ 'lib.de.us' => 2,
8755
+ '2038.io' => 2,
8756
+ 'router.management' => 2,
8757
+ 'v-info.info' => 2,
8758
+ 'voorloper.cloud' => 2,
8759
+ 'wafflecell.com' => 2,
8760
+ '*.webhare.dev' => 2,
8761
+ 'wedeploy.io' => 2,
8762
+ 'wedeploy.me' => 2,
8763
+ 'wedeploy.sh' => 2,
8764
+ 'remotewd.com' => 2,
8765
+ 'wmflabs.org' => 2,
8766
+ 'half.host' => 2,
8767
+ 'xnbay.com' => 2,
8768
+ 'u2.xnbay.com' => 2,
8769
+ 'u2-local.xnbay.com' => 2,
8770
+ 'cistron.nl' => 2,
8771
+ 'demon.nl' => 2,
8772
+ 'xs4all.space' => 2,
8773
+ 'yandexcloud.net' => 2,
8774
+ 'storage.yandexcloud.net' => 2,
8775
+ 'website.yandexcloud.net' => 2,
8776
+ 'official.academy' => 2,
8777
+ 'yolasite.com' => 2,
8778
+ 'ybo.faith' => 2,
8779
+ 'yombo.me' => 2,
8780
+ 'homelink.one' => 2,
8781
+ 'ybo.party' => 2,
8782
+ 'ybo.review' => 2,
8783
+ 'ybo.science' => 2,
8784
+ 'ybo.trade' => 2,
8785
+ 'nohost.me' => 2,
8786
+ 'noho.st' => 2,
8787
+ 'za.net' => 2,
8788
+ 'za.org' => 2,
8789
+ 'now.sh' => 2,
8790
+ 'bss.design' => 2,
8791
+ 'basicserver.io' => 2,
8792
+ 'virtualserver.io' => 2,
8793
+ 'site.builder.nu' => 2,
8794
+ 'enterprisecloud.nu' => 2,
8795
+ 'zone.id' => 2,
8796
+ );
app/controller.php CHANGED
@@ -16,7 +16,7 @@ class Controller extends Component {
16
  * @return bool
17
  */
18
  protected function isInPage() {
19
- return HTTP_Helper::retrieve_get( 'page' ) == $this->slug;
20
  }
21
 
22
  /**
@@ -25,14 +25,14 @@ class Controller extends Component {
25
  * @return bool
26
  */
27
  public function isView( $view ) {
28
- return HTTP_Helper::retrieve_get( 'view' ) == $view;
29
  }
30
 
31
  /**
32
  * @return bool
33
  */
34
  public function isDashboard() {
35
- return HTTP_Helper::retrieve_get( 'page' ) == 'wp-defender';
36
  }
37
 
38
  /**
16
  * @return bool
17
  */
18
  protected function isInPage() {
19
+ return HTTP_Helper::retrieveGet( 'page' ) == $this->slug;
20
  }
21
 
22
  /**
25
  * @return bool
26
  */
27
  public function isView( $view ) {
28
+ return HTTP_Helper::retrieveGet( 'view' ) == $view;
29
  }
30
 
31
  /**
32
  * @return bool
33
  */
34
  public function isDashboard() {
35
+ return HTTP_Helper::retrieveGet( 'page' ) == 'wp-defender';
36
  }
37
 
38
  /**
app/controller/dashboard.php CHANGED
@@ -8,51 +8,56 @@ namespace WP_Defender\Controller;
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\WP_Helper;
10
  use WP_Defender\Behavior\Utils;
 
11
  use WP_Defender\Controller;
12
  use WP_Defender\Module\Audit\Component\Audit_API;
 
13
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
14
  use WP_Defender\Module\Scan\Component\Scan_Api;
15
  use WP_Defender\Module\Scan\Model\Result_Item;
16
  use WP_Defender\Module\Scan\Model\Settings;
 
17
 
18
  class Dashboard extends Controller {
19
  protected $slug = 'wp-defender';
20
-
21
  public function __construct() {
22
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
23
- $this->add_action( 'network_admin_menu', 'admin_menu' );
24
  } else {
25
- $this->add_action( 'admin_menu', 'admin_menu' );
26
  }
27
-
28
  if ( $this->isInPage() ) {
29
- $this->add_action( 'defender_enqueue_assets', 'scripts', 11 );
30
  }
31
-
32
- $this->add_ajax_action( 'blacklistWidgetStatus', 'blacklistWidgetStatus' );
33
- $this->add_ajax_action( 'toggleBlacklistWidget', 'toggleBlacklistWidget' );
34
- $this->add_ajax_action( 'activateModule', 'activateModule' );
35
- $this->add_ajax_action( 'skipActivator', 'skipActivator' );
36
- $this->add_action( 'defenderSubmitStats', 'defenderSubmitStats' );
37
- $this->add_filter( 'wdp_register_hub_action', 'addMyEndpoint' );
38
  add_filter( 'custom_menu_order', '__return_true' );
39
- $this->add_filter( 'menu_order', 'menuOrder' );
40
- add_action( 'admin_head', array( &$this, 'replaceHeroImages' ) );
41
  }
42
-
43
- public function skipActivator() {
 
 
 
44
  if ( ! $this->checkPermission() ) {
45
  return;
46
  }
47
-
48
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'skipActivator' ) ) {
49
  return;
50
  }
51
- $cache = WP_Helper::getCache();
52
- $cache->set( wp_defender()->isFree ? 'wdf_isActivated' : 'isActivated', 1, 0 );
53
  wp_send_json_success();
54
  }
55
-
56
  public function menuOrder( $menu_order ) {
57
  global $submenu;
58
  if ( isset( $submenu['wp-defender'] ) ) {
@@ -62,7 +67,7 @@ class Dashboard extends Controller {
62
  $defender_menu = array_values( $defender_menu );
63
  $submenu['wp-defender'] = $defender_menu;
64
  }
65
-
66
  global $menu;
67
  $count = $this->countTotalIssues();
68
  $indicator = $count > 0 ? ' <span class="update-plugins wd-issue-indicator-sidebar"><span class="plugin-count">' . $count . '</span></span>' : null;
@@ -71,54 +76,82 @@ class Dashboard extends Controller {
71
  $menu[ $k ][0] .= $indicator;
72
  }
73
  }
74
-
75
  return $menu_order;
76
  }
77
-
78
- public function replaceHeroImages() {
79
- if ( strlen( wp_defender()->heroImage ) > 0 ) {
80
- ?>
81
- <style type="text/css">
82
- .wp-defender .def-dashboard .sui-summary:not(.sui-unbranded),
83
- .wp-defender .wdf-scanning .sui-summary:not(.sui-unbranded),
84
- .wp-defender .auditing .sui-summary:not(.sui-unbranded),
85
- .wp-defender .iplockout .sui-summary:not(.sui-unbranded) {
86
- background-image: url("<?php echo wp_defender()->heroImage ?>");
87
- background-position: 3% 50%;
88
- }
89
- .wp-defender .hardener .sui-summary:not(.sui-unbranded){
90
- background-image: none;
91
- }
92
- </style>
93
- <?php
94
- }
95
- }
96
-
97
  public function defenderSubmitStats() {
98
  if ( $this->hasMethod( '_submitStatsToDev' ) ) {
99
  $this->_submitStatsToDev();
100
  }
101
  }
102
-
103
  /**
104
  * @param $actions
105
  *
106
  * @return mixed
107
  */
108
  public function addMyEndpoint( $actions ) {
109
- $actions['defender_new_scan'] = array( &$this, 'new_scan' );
110
- $actions['defender_schedule_scan'] = array( &$this, 'schedule_scan' );
111
- $actions['defender_manage_audit_log'] = array( &$this, 'manage_audit_log' );
112
- $actions['defender_manage_lockout'] = array( &$this, 'manage_lockout' );
113
- $actions['defender_whitelist_ip'] = array( &$this, 'whitelist_ip' );
114
- $actions['defender_blacklist_ip'] = array( &$this, 'blacklist_ip' );
115
- $actions['defender_get_stats'] = array( &$this, 'get_stats' );
116
- $actions['defender_get_scan_progress'] = array( &$this, 'get_scan_progress' );
117
-
 
 
 
 
118
  return $actions;
119
  }
120
-
121
- public function get_scan_progress() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  $ret = Scan_Api::processActiveScan();
123
  if ( is_wp_error( $ret ) ) {
124
  wp_send_json_error( array(
@@ -134,27 +167,27 @@ class Dashboard extends Controller {
134
  ) );
135
  }
136
  }
137
-
138
  /**
139
  * @param $params
140
  * @param $action
141
  */
142
- public function new_scan( $params, $action ) {
143
  $ret = Scan_Api::createScan();
144
  if ( is_wp_error( $ret ) ) {
145
  wp_send_json_error( array(
146
  'message' => $ret->get_error_message()
147
  ) );
148
  }
149
-
150
  wp_send_json_success();
151
  }
152
-
153
  /**
154
  * @param $params
155
  * @param $action
156
  */
157
- public function schedule_scan( $params, $action ) {
158
  $frequency = $params['frequency'];
159
  $day = $params['day'];
160
  $time = $params['time'];
@@ -166,17 +199,17 @@ class Dashboard extends Controller {
166
  $settings->frequency = $frequency;
167
  $settings->day = $day;
168
  $settings->time = $time;
169
-
170
  wp_send_json_success();
171
  }
172
-
173
  /**
174
  * Hub Audit log endpoint
175
  *
176
  * @param $params
177
  * @param $action
178
  */
179
- public function manage_audit_log( $params, $action ) {
180
  $response = null;
181
  if ( class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
182
  $response = array();
@@ -192,14 +225,14 @@ class Dashboard extends Controller {
192
  }
193
  wp_send_json_success( $response );
194
  }
195
-
196
  /**
197
  * Hub Lockouts endpoint
198
  *
199
  * @param $params
200
  * @param $action
201
  */
202
- public function manage_lockout( $params, $action ) {
203
  $type = $params['type'];
204
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
205
  $response = array();
@@ -226,14 +259,14 @@ class Dashboard extends Controller {
226
  }
227
  wp_send_json_success();
228
  }
229
-
230
  /**
231
  * Hub Whitelist IP endpoint
232
  *
233
  * @param $params
234
  * @param $action
235
  */
236
- public function whitelist_ip( $params, $action ) {
237
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
238
  $ip = $params['ip'];
239
  if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
@@ -244,14 +277,14 @@ class Dashboard extends Controller {
244
  }
245
  wp_send_json_success();
246
  }
247
-
248
  /**
249
  * Hub Blacklist IP endpoint
250
  *
251
  * @param $params
252
  * @param $action
253
  */
254
- public function blacklist_ip( $params, $action ) {
255
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
256
  $ip = $params['ip'];
257
  if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
@@ -262,14 +295,14 @@ class Dashboard extends Controller {
262
  }
263
  wp_send_json_success();
264
  }
265
-
266
  /**
267
  * Hub Stats endpoint
268
  *
269
  * @param $params
270
  * @param $action
271
  */
272
- public function get_stats( $params, $action ) {
273
  $stats = Utils::instance()->generateStats();
274
  wp_send_json_success(
275
  array(
@@ -277,43 +310,43 @@ class Dashboard extends Controller {
277
  )
278
  );
279
  }
280
-
281
  public function actionIndex() {
282
- $this->render( 'dashboard' );
283
  }
284
-
285
  public function blacklistWidgetStatus() {
286
  if ( ! $this->checkPermission() ) {
287
  return;
288
  }
289
-
290
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'blacklistWidgetStatus' ) ) {
291
  return;
292
  }
293
-
294
  if ( $this->hasMethod( 'pullBlacklistStatus' ) ) {
295
  $this->pullBlacklistStatus();
296
  }
297
-
298
  exit;
299
  }
300
-
301
  public function toggleBlacklistWidget() {
302
  if ( ! $this->checkPermission() ) {
303
  return;
304
  }
305
-
306
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'toggleBlacklistWidget' ) ) {
307
  return;
308
  }
309
-
310
  if ( $this->hasMethod( 'toggleStatus' ) ) {
311
  $this->toggleStatus();
312
  }
313
-
314
  exit;
315
  }
316
-
317
  /**
318
  * @param bool $detail
319
  *
@@ -326,29 +359,28 @@ class Dashboard extends Controller {
326
  $scanCount = 0;
327
  if ( is_object( $scan ) ) {
328
  $scanCount = $scan->countAll( Result_Item::STATUS_ISSUE );
329
-
330
  $total += $scanCount;
331
  }
332
  if ( $detail == false ) {
333
  return $total;
334
  }
335
-
336
  return array( $hardenerCount, $scanCount );
337
  }
338
-
339
  /**
340
  *
341
  */
342
  public function admin_menu() {
343
  $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
344
  $menu_title = wp_defender()->isFree ? esc_html__( "Defender", "defender-security" ) : esc_html__( "Defender Pro", "defender-security" );
345
- //$menu_title = sprintf( $menu_title, $indicator );
346
  add_menu_page( $menu_title, $menu_title, $cap, 'wp-defender', array(
347
  &$this,
348
  'actionIndex'
349
  ), $this->get_menu_icon() );
350
  }
351
-
352
  /**
353
  * Return svg image
354
  * @return string
@@ -356,36 +388,47 @@ class Dashboard extends Controller {
356
  private function get_menu_icon() {
357
  ob_start();
358
  ?>
359
- <svg width="17px" height="18px" viewBox="10 397 17 18" version="1.1" xmlns="http://www.w3.org/2000/svg"
360
- xmlns:xlink="http://www.w3.org/1999/xlink">
361
- <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
362
- <desc>Created with Sketch.</desc>
363
- <defs></defs>
364
- <path
365
- d="M24.8009393,403.7962 L23.7971393,410.1724 C23.7395393,410.5372 23.5313393,410.8528 23.2229393,411.0532 L18.4001393,413.6428 L13.5767393,411.0532 C13.2683393,410.8528 13.0601393,410.5372 13.0019393,410.1724 L11.9993393,403.7962 L11.6153393,401.3566 C12.5321393,402.9514 14.4893393,405.5518 18.4001393,408.082 C22.3115393,405.5518 24.2675393,402.9514 25.1855393,401.3566 L24.8009393,403.7962 Z M26.5985393,398.0644 C25.7435393,397.87 22.6919393,397.2106 19.9571393,397 L19.9571393,403.4374 L18.4037393,404.5558 L16.8431393,403.4374 L16.8431393,397 C14.1077393,397.2106 11.0561393,397.87 10.2011393,398.0644 C10.0685393,398.0938 9.98213933,398.221 10.0031393,398.3536 L10.8875393,403.969 L11.8913393,410.3446 C12.0071393,411.0796 12.4559393,411.7192 13.1105393,412.0798 L16.8431393,414.1402 L18.4001393,415 L19.9571393,414.1402 L23.6891393,412.0798 C24.3431393,411.7192 24.7925393,411.0796 24.9083393,410.3446 L25.9121393,403.969 L26.7965393,398.3536 C26.8175393,398.221 26.7311393,398.0938 26.5985393,398.0644 L26.5985393,398.0644 Z"
366
- id="Defender-Icon" stroke="none" fill="#FFFFFF" fill-rule="evenodd"></path>
367
- </svg>
368
  <?php
369
  $svg = ob_get_clean();
370
-
371
  return 'data:image/svg+xml;base64,' . base64_encode( $svg );
372
  }
373
-
374
  public function scripts() {
375
- wp_enqueue_script( 'wpmudev-sui' );
376
- wp_enqueue_style( 'wpmudev-sui' );
377
- wp_enqueue_script( 'defender' );
378
- $data = array(
379
- 'activator_title' => __( "QUICK SETUP", "defender-security" ) . '<form method="post" class="skip-activator float-r"><input type="hidden" name="action" value="skipActivator"/>' . wp_nonce_field( 'skipActivator', '_wpnonce', true, false ) . '<button type="submit" class="button button-small button-secondary">' . __( "Skip", "defender-security" ) . '</button></form>',
380
- 'activate_scan' => __( "Activating File Scanning...", "defender-security" ),
381
- 'activate_audit' => __( "Activating Audit Module...", "defender-security" ),
382
- 'activate_lockout' => __( "Activating IP Lockouts Module...", "defender-security" ),
383
- 'activate_blacklist' => __( "Activating Blacklist Monitoring...", "defender-security" )
384
- );
385
  wp_enqueue_style( 'defender' );
386
- wp_localize_script( 'defender', 'dashboard', $data );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  }
388
-
389
  /**
390
  * @return array
391
  */
@@ -393,13 +436,7 @@ class Dashboard extends Controller {
393
  return array(
394
  'utils' => '\WP_Defender\Behavior\Utils',
395
  'activator' => wp_defender()->isFree ? '\WP_Defender\Behavior\Activator_Free' : '\WP_Defender\Behavior\Activator',
396
- 'hardener' => '\WP_Defender\Module\Hardener\Behavior\Widget',
397
- 'scan' => '\WP_Defender\Module\Scan\Behavior\Scan_Widget',
398
- 'lockout' => '\WP_Defender\Module\IP_Lockout\Behavior\Widget',
399
- 'audit' => wp_defender()->isFree ? '\WP_Defender\Module\Audit\Behavior\Audit_Free' : '\WP_Defender\Module\Audit\Behavior\Audit',
400
  'blacklist' => wp_defender()->isFree ? '\WP_Defender\Behavior\Blacklist_Free' : '\WP_Defender\Behavior\Blacklist',
401
- 'report' => wp_defender()->isFree ? '\WP_Defender\Behavior\Report_Free' : '\WP_Defender\Behavior\Report',
402
- 'at' => '\WP_Defender\Module\Advanced_Tools\Behavior\AT_Widget'
403
  );
404
  }
405
  }
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\WP_Helper;
10
  use WP_Defender\Behavior\Utils;
11
+ use WP_Defender\Component\Data_Factory;
12
  use WP_Defender\Controller;
13
  use WP_Defender\Module\Audit\Component\Audit_API;
14
+ use WP_Defender\Module\IP_Lockout;
15
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
16
  use WP_Defender\Module\Scan\Component\Scan_Api;
17
  use WP_Defender\Module\Scan\Model\Result_Item;
18
  use WP_Defender\Module\Scan\Model\Settings;
19
+ use WP_Defender\Module\Setting\Component\Backup_Settings;
20
 
21
  class Dashboard extends Controller {
22
  protected $slug = 'wp-defender';
23
+
24
  public function __construct() {
25
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
26
+ $this->addAction( 'network_admin_menu', 'admin_menu' );
27
  } else {
28
+ $this->addAction( 'admin_menu', 'admin_menu' );
29
  }
30
+
31
  if ( $this->isInPage() ) {
32
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
33
  }
34
+
35
+ $this->addAjaxAction( 'wp-defender/v1/blacklistWidgetStatus', 'blacklistWidgetStatus' );
36
+ $this->addAjaxAction( 'wp-defender/v1/toggleBlacklistWidget', 'toggleBlacklistWidget' );
37
+ $this->addAjaxAction( 'wp-defender/v1/activateModule', 'activateModule' );
38
+ $this->addAjaxAction( 'wp-defender/v1/skipActivator', 'skipQuickSetup' );
39
+ $this->addAction( 'defenderSubmitStats', 'defenderSubmitStats' );
40
+ $this->addFilter( 'wdp_register_hub_action', 'addMyEndpoint' );
41
  add_filter( 'custom_menu_order', '__return_true' );
42
+ $this->addFilter( 'menu_order', 'menuOrder' );
 
43
  }
44
+
45
+ /**
46
+ * Skip quick setup
47
+ */
48
+ public function skipQuickSetup() {
49
  if ( ! $this->checkPermission() ) {
50
  return;
51
  }
52
+
53
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'skipActivator' ) ) {
54
  return;
55
  }
56
+ $is_free = wp_defender()->isFree ? '_free' : null;
57
+ update_site_option( 'wp_defender' . $is_free . '_is_activated', 1 );
58
  wp_send_json_success();
59
  }
60
+
61
  public function menuOrder( $menu_order ) {
62
  global $submenu;
63
  if ( isset( $submenu['wp-defender'] ) ) {
67
  $defender_menu = array_values( $defender_menu );
68
  $submenu['wp-defender'] = $defender_menu;
69
  }
70
+
71
  global $menu;
72
  $count = $this->countTotalIssues();
73
  $indicator = $count > 0 ? ' <span class="update-plugins wd-issue-indicator-sidebar"><span class="plugin-count">' . $count . '</span></span>' : null;
76
  $menu[ $k ][0] .= $indicator;
77
  }
78
  }
79
+
80
  return $menu_order;
81
  }
82
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  public function defenderSubmitStats() {
84
  if ( $this->hasMethod( '_submitStatsToDev' ) ) {
85
  $this->_submitStatsToDev();
86
  }
87
  }
88
+
89
  /**
90
  * @param $actions
91
  *
92
  * @return mixed
93
  */
94
  public function addMyEndpoint( $actions ) {
95
+ $actions['defender_new_scan'] = array( &$this, 'newScan' );
96
+ $actions['defender_schedule_scan'] = array( &$this, 'scheduleScan' );
97
+ $actions['defender_manage_audit_log'] = array( &$this, 'manageAuditLog' );
98
+ $actions['defender_manage_lockout'] = array( &$this, 'manageLockout' );
99
+ $actions['defender_whitelist_ip'] = array( &$this, 'whitelistIP' );
100
+ $actions['defender_blacklist_ip'] = array( &$this, 'blacklistIP' );
101
+ $actions['defender_get_stats'] = array( &$this, 'getStats' );
102
+ $actions['defender_get_scan_progress'] = array( &$this, 'getScanProgress' );
103
+
104
+ //backup/restore settings
105
+ $actions['defender_export_settings'] = array( &$this, 'exportSettings' );
106
+ $actions['defender_import_settings'] = array( &$this, 'importSettings' );
107
+
108
  return $actions;
109
  }
110
+
111
+ public function importSettings( $params ) {
112
+ //dirty but quick
113
+ $configs = json_decode( json_encode( $params->configs ), true );
114
+ foreach ( $configs as $module => $mdata ) {
115
+ foreach ( $mdata as $key => $value ) {
116
+ if ( $key == 'geoIP_db' ) {
117
+ if ( ! empty( $value ) ) {
118
+ //download it
119
+ Login_Protection_Api::downloadGeoIP();
120
+ } else {
121
+ //reset it
122
+ $mdata[ $key ] = '';
123
+ }
124
+ } elseif ( is_string( $value ) ) {
125
+ $value = str_replace( '{nl}', PHP_EOL, $value );
126
+ $mdata[ $key ] = $value;
127
+ }
128
+
129
+ }
130
+ $configs[ $module ] = $mdata;
131
+ }
132
+ Backup_Settings::restoreData( $configs );
133
+
134
+ wp_send_json_success();
135
+ }
136
+
137
+ public function exportSettings() {
138
+ $data = Backup_Settings::parseDataForHub();
139
+ //we have to replace all the new line in configs
140
+ $configs = $data['configs'];
141
+ foreach ( $configs as $module => $mdata ) {
142
+ foreach ( $mdata as $key => $value ) {
143
+ if ( is_string( $value ) ) {
144
+ $value = str_replace( array( "\r", "\n" ), '{nl}', $value );
145
+ $mdata[ $key ] = $value;
146
+ }
147
+ }
148
+ $configs[ $module ] = $mdata;
149
+ }
150
+ $data['configs'] = $configs;
151
+ wp_send_json_success( $data );
152
+ }
153
+
154
+ public function getScanProgress() {
155
  $ret = Scan_Api::processActiveScan();
156
  if ( is_wp_error( $ret ) ) {
157
  wp_send_json_error( array(
167
  ) );
168
  }
169
  }
170
+
171
  /**
172
  * @param $params
173
  * @param $action
174
  */
175
+ public function newScan( $params, $action ) {
176
  $ret = Scan_Api::createScan();
177
  if ( is_wp_error( $ret ) ) {
178
  wp_send_json_error( array(
179
  'message' => $ret->get_error_message()
180
  ) );
181
  }
182
+
183
  wp_send_json_success();
184
  }
185
+
186
  /**
187
  * @param $params
188
  * @param $action
189
  */
190
+ public function scheduleScan( $params, $action ) {
191
  $frequency = $params['frequency'];
192
  $day = $params['day'];
193
  $time = $params['time'];
199
  $settings->frequency = $frequency;
200
  $settings->day = $day;
201
  $settings->time = $time;
202
+
203
  wp_send_json_success();
204
  }
205
+
206
  /**
207
  * Hub Audit log endpoint
208
  *
209
  * @param $params
210
  * @param $action
211
  */
212
+ public function manageAuditLog( $params, $action ) {
213
  $response = null;
214
  if ( class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
215
  $response = array();
225
  }
226
  wp_send_json_success( $response );
227
  }
228
+
229
  /**
230
  * Hub Lockouts endpoint
231
  *
232
  * @param $params
233
  * @param $action
234
  */
235
+ public function manageLockout( $params, $action ) {
236
  $type = $params['type'];
237
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
238
  $response = array();
259
  }
260
  wp_send_json_success();
261
  }
262
+
263
  /**
264
  * Hub Whitelist IP endpoint
265
  *
266
  * @param $params
267
  * @param $action
268
  */
269
+ public function whitelistIP( $params, $action ) {
270
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
271
  $ip = $params['ip'];
272
  if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
277
  }
278
  wp_send_json_success();
279
  }
280
+
281
  /**
282
  * Hub Blacklist IP endpoint
283
  *
284
  * @param $params
285
  * @param $action
286
  */
287
+ public function blacklistIP( $params, $action ) {
288
  $settings = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
289
  $ip = $params['ip'];
290
  if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
295
  }
296
  wp_send_json_success();
297
  }
298
+
299
  /**
300
  * Hub Stats endpoint
301
  *
302
  * @param $params
303
  * @param $action
304
  */
305
+ public function getStats( $params, $action ) {
306
  $stats = Utils::instance()->generateStats();
307
  wp_send_json_success(
308
  array(
310
  )
311
  );
312
  }
313
+
314
  public function actionIndex() {
315
+ $this->render( 'main' );
316
  }
317
+
318
  public function blacklistWidgetStatus() {
319
  if ( ! $this->checkPermission() ) {
320
  return;
321
  }
322
+
323
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'blacklistWidgetStatus' ) ) {
324
  return;
325
  }
326
+
327
  if ( $this->hasMethod( 'pullBlacklistStatus' ) ) {
328
  $this->pullBlacklistStatus();
329
  }
330
+
331
  exit;
332
  }
333
+
334
  public function toggleBlacklistWidget() {
335
  if ( ! $this->checkPermission() ) {
336
  return;
337
  }
338
+
339
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'toggleBlacklistWidget' ) ) {
340
  return;
341
  }
342
+
343
  if ( $this->hasMethod( 'toggleStatus' ) ) {
344
  $this->toggleStatus();
345
  }
346
+
347
  exit;
348
  }
349
+
350
  /**
351
  * @param bool $detail
352
  *
359
  $scanCount = 0;
360
  if ( is_object( $scan ) ) {
361
  $scanCount = $scan->countAll( Result_Item::STATUS_ISSUE );
362
+
363
  $total += $scanCount;
364
  }
365
  if ( $detail == false ) {
366
  return $total;
367
  }
368
+
369
  return array( $hardenerCount, $scanCount );
370
  }
371
+
372
  /**
373
  *
374
  */
375
  public function admin_menu() {
376
  $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
377
  $menu_title = wp_defender()->isFree ? esc_html__( "Defender", "defender-security" ) : esc_html__( "Defender Pro", "defender-security" );
 
378
  add_menu_page( $menu_title, $menu_title, $cap, 'wp-defender', array(
379
  &$this,
380
  'actionIndex'
381
  ), $this->get_menu_icon() );
382
  }
383
+
384
  /**
385
  * Return svg image
386
  * @return string
388
  private function get_menu_icon() {
389
  ob_start();
390
  ?>
391
+ <svg width="17px" height="18px" viewBox="10 397 17 18" version="1.1" xmlns="http://www.w3.org/2000/svg"
392
+ xmlns:xlink="http://www.w3.org/1999/xlink">
393
+ <!-- Generator: Sketch 3.8.3 (29802) - http://www.bohemiancoding.com/sketch -->
394
+ <desc>Created with Sketch.</desc>
395
+ <defs></defs>
396
+ <path
397
+ d="M24.8009393,403.7962 L23.7971393,410.1724 C23.7395393,410.5372 23.5313393,410.8528 23.2229393,411.0532 L18.4001393,413.6428 L13.5767393,411.0532 C13.2683393,410.8528 13.0601393,410.5372 13.0019393,410.1724 L11.9993393,403.7962 L11.6153393,401.3566 C12.5321393,402.9514 14.4893393,405.5518 18.4001393,408.082 C22.3115393,405.5518 24.2675393,402.9514 25.1855393,401.3566 L24.8009393,403.7962 Z M26.5985393,398.0644 C25.7435393,397.87 22.6919393,397.2106 19.9571393,397 L19.9571393,403.4374 L18.4037393,404.5558 L16.8431393,403.4374 L16.8431393,397 C14.1077393,397.2106 11.0561393,397.87 10.2011393,398.0644 C10.0685393,398.0938 9.98213933,398.221 10.0031393,398.3536 L10.8875393,403.969 L11.8913393,410.3446 C12.0071393,411.0796 12.4559393,411.7192 13.1105393,412.0798 L16.8431393,414.1402 L18.4001393,415 L19.9571393,414.1402 L23.6891393,412.0798 C24.3431393,411.7192 24.7925393,411.0796 24.9083393,410.3446 L25.9121393,403.969 L26.7965393,398.3536 C26.8175393,398.221 26.7311393,398.0938 26.5985393,398.0644 L26.5985393,398.0644 Z"
398
+ id="Defender-Icon" stroke="none" fill="#FFFFFF" fill-rule="evenodd"></path>
399
+ </svg>
400
  <?php
401
  $svg = ob_get_clean();
402
+
403
  return 'data:image/svg+xml;base64,' . base64_encode( $svg );
404
  }
405
+
406
  public function scripts() {
 
 
 
 
 
 
 
 
 
 
407
  wp_enqueue_style( 'defender' );
408
+ wp_register_script( 'defender-dashboard', wp_defender()->getPluginUrl() . 'assets/app/dashboard.js', array(
409
+ 'vue',
410
+ 'defender',
411
+ 'wp-i18n'
412
+ ), wp_defender()->version, true );
413
+ \WP_Defender\Behavior\Utils::instance()->createTranslationJson( 'defender-dashboard' );
414
+ wp_set_script_translations( 'defender-dashboard', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
415
+ wp_localize_script( 'defender-dashboard', 'dashboard', array_merge( Data_Factory::buildData(), [
416
+ 'quick_setup' => [
417
+ 'show' => $this->isShowActivator(),
418
+ 'nonces' => [
419
+ 'skip' => wp_create_nonce( 'skipActivator' ),
420
+ 'activate' => wp_create_nonce( 'activateModule' )
421
+ ],
422
+ 'endpoints' => [
423
+ 'skip' => 'wp-defender/v1/skipActivator',
424
+ 'activate' => 'wp-defender/v1/activateModule'
425
+ ]
426
+ ]
427
+ ] ) );
428
+ wp_enqueue_script( 'defender-dashboard' );
429
+ wp_enqueue_script( 'wpmudev-sui' );
430
  }
431
+
432
  /**
433
  * @return array
434
  */
436
  return array(
437
  'utils' => '\WP_Defender\Behavior\Utils',
438
  'activator' => wp_defender()->isFree ? '\WP_Defender\Behavior\Activator_Free' : '\WP_Defender\Behavior\Activator',
 
 
 
 
439
  'blacklist' => wp_defender()->isFree ? '\WP_Defender\Behavior\Blacklist_Free' : '\WP_Defender\Behavior\Blacklist',
 
 
440
  );
441
  }
442
  }
app/controller/debug.php CHANGED
@@ -5,24 +5,48 @@
5
 
6
  namespace WP_Defender\Controller;
7
 
 
8
  use Hammer\Helper\HTTP_Helper;
9
  use WP_Defender\Controller;
 
 
 
10
 
11
  class Debug extends Controller {
12
  protected $slug = 'wdf-debug';
13
-
14
  public function __construct() {
15
- if ( HTTP_Helper::retrieve_get( 'page' ) != 'wdf-debug' ) {
16
  return;
17
  }
18
-
19
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
20
- $this->add_action( 'network_admin_menu', 'adminMenu' );
 
 
21
  } else {
22
- $this->add_action( 'admin_menu', 'adminMenu' );
23
  }
 
 
24
  }
25
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
  * Add submit admin page
28
  */
@@ -33,11 +57,16 @@ class Debug extends Controller {
33
  'actionIndex'
34
  ) );
35
  }
36
-
37
  public function actionIndex() {
38
- $this->render( 'debug' );
 
 
 
 
 
39
  }
40
-
41
  /**
42
  * @return array
43
  */
5
 
6
  namespace WP_Defender\Controller;
7
 
8
+ use Hammer\Base\Container;
9
  use Hammer\Helper\HTTP_Helper;
10
  use WP_Defender\Controller;
11
+ use WP_Defender\Module\Hardener\Model\Settings;
12
+ use WP_Defender\Module\Scan\Component\Scan_Api;
13
+ use WP_Defender\Module\Scan\Component\Scanning;
14
 
15
  class Debug extends Controller {
16
  protected $slug = 'wdf-debug';
17
+
18
  public function __construct() {
19
+ if ( HTTP_Helper::retrieveGet( 'page' ) != 'wdf-debug' ) {
20
  return;
21
  }
22
+ if ( $this->isInPage() ) {
23
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
24
+ }
25
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
26
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
27
  } else {
28
+ $this->addAction( 'admin_menu', 'adminMenu' );
29
  }
30
+
31
+ $this->addAction( 'wp_loaded', 'clearTweaksCache' );
32
  }
33
+
34
+ public function clearTweaksCache() {
35
+ if ( $_SERVER['REQUEST_METHOD'] != 'POST' ) {
36
+ return;
37
+ }
38
+
39
+ if ( isset( $_POST['_defnonce'] ) && wp_verify_nonce( $_POST['_defnonce'], 'flush_tweaks_cache' ) ) {
40
+ $model = Settings::instance();
41
+ $model->setDValues( 'head_requests', null );
42
+ }
43
+ }
44
+
45
+ public function scripts() {
46
+ wp_enqueue_style( 'defender' );
47
+ wp_enqueue_script( 'wpmudev-sui' );
48
+ }
49
+
50
  /**
51
  * Add submit admin page
52
  */
57
  'actionIndex'
58
  ) );
59
  }
60
+
61
  public function actionIndex() {
62
+ $cache = Container::instance()->get( 'cache' );
63
+ $this->render( 'debug', [
64
+ 'core' => $cache->get( Scan_Api::CACHE_CORE, [] ),
65
+ 'content' => $cache->get( Scan_Api::CACHE_CONTENT, [] ),
66
+ 'progress' => Scan_Api::getScanProgress( true )
67
+ ] );
68
  }
69
+
70
  /**
71
  * @return array
72
  */
app/controller/gdpr.php CHANGED
@@ -9,7 +9,7 @@ use WP_Defender\Controller;
9
 
10
  class GDPR extends Controller {
11
  public function __construct() {
12
- $this->add_filter( 'wp_get_default_privacy_policy_content', 'addPolicy' );
13
  }
14
 
15
  public function addPolicy( $content ) {
9
 
10
  class GDPR extends Controller {
11
  public function __construct() {
12
+ $this->addFilter( 'wp_get_default_privacy_policy_content', 'addPolicy' );
13
  }
14
 
15
  public function addPolicy( $content ) {
app/module/advanced-tools.php CHANGED
@@ -8,10 +8,12 @@ namespace WP_Defender\Module;
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Advanced_Tools\Controller\Main;
10
  use WP_Defender\Module\Advanced_Tools\Controller\Mask_Login;
 
 
11
 
12
  class Advanced_Tools extends Module {
13
  public function __construct() {
14
- $main = new Main();
15
- $maskLogin = new Mask_Login();
16
  }
17
  }
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Advanced_Tools\Controller\Main;
10
  use WP_Defender\Module\Advanced_Tools\Controller\Mask_Login;
11
+ use WP_Defender\Module\Advanced_Tools\Controller\Rest;
12
+ use WP_Defender\Module\Advanced_Tools\Controller\Rest_Auth;
13
 
14
  class Advanced_Tools extends Module {
15
  public function __construct() {
16
+ $main = new Main();
17
+ $rest = new Rest();
18
  }
19
  }
app/module/advanced-tools/component/auth-api.php CHANGED
@@ -5,13 +5,7 @@
5
 
6
  namespace WP_Defender\Module\Advanced_Tools\Component;
7
 
8
- use BaconQrCode\Renderer\Image\ImagickImageBackEnd;
9
- use BaconQrCode\Renderer\Image\SvgImageBackEnd;
10
- use BaconQrCode\Renderer\ImageRenderer;
11
- use BaconQrCode\Renderer\RendererStyle\RendererStyle;
12
- use BaconQrCode\Writer;
13
  use Hammer\Base\Component;
14
- use PragmaRX\Google2FA\Google2FA;
15
  use WP_Defender\Behavior\Utils;
16
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
17
 
@@ -27,10 +21,10 @@ class Auth_API extends Component {
27
  for ( $i = 0; $i < $length; $i ++ ) {
28
  $secret[] = $strings[ rand( 0, strlen( $strings ) - 1 ) ];
29
  }
30
-
31
  return implode( "", $secret );
32
  }
33
-
34
  /**
35
  * @param $name
36
  * @param $secret
@@ -46,12 +40,12 @@ class Auth_API extends Component {
46
  $chl .= ( '&issuer=' . rawurlencode( $title ) );
47
  }
48
  //manually include the autoload
49
- require_once wp_defender()->getPluginPath() . 'vendor/phpqrcode/qrlib.php';
50
-
51
  $code = \QRcode::svg( $chl, false, QR_ECLEVEL_L, 4 );
52
-
53
  }
54
-
55
  /**
56
  * Calculate the TOTP code
57
  *
@@ -89,10 +83,10 @@ class Auth_API extends Component {
89
  $code = $value % pow( 10, 6 );
90
  //in some case we have the 0 before, so it become lesser than 6, make sure it always right
91
  $code = str_pad( $code, 6, '0', STR_PAD_LEFT );
92
-
93
  return $code;
94
  }
95
-
96
  /**
97
  * @param $secret
98
  * @param $userCode
@@ -104,7 +98,7 @@ class Auth_API extends Component {
104
  if ( strlen( $userCode ) != 6 ) {
105
  return false;
106
  }
107
-
108
  /**
109
  * window is 30 seconds, before and after
110
  */
@@ -115,11 +109,11 @@ class Auth_API extends Component {
115
  return true;
116
  }
117
  }
118
-
119
-
120
  return false;
121
  }
122
-
123
  /**
124
  * Timing attack safe string comparison, replacement of has_equals which only on 5.6+
125
  *
@@ -133,23 +127,23 @@ class Auth_API extends Component {
133
  if ( function_exists( 'hash_equals' ) ) {
134
  return hash_equals( $known_string, $user_string );
135
  }
136
-
137
  $ret = 0;
138
-
139
  if ( strlen( $known_string ) !== strlen( $user_string ) ) {
140
  $user_string = $known_string;
141
  $ret = 1;
142
  }
143
-
144
  $res = $known_string ^ $user_string;
145
-
146
  for ( $i = strlen( $res ) - 1; $i >= 0; -- $i ) {
147
  $ret |= ord( $res[ $i ] );
148
  }
149
-
150
  return ! $ret;
151
  }
152
-
153
  /**
154
  * @return bool
155
  */
@@ -164,10 +158,13 @@ class Auth_API extends Component {
164
  if ( 0 === count( $user->roles ) ) {
165
  return true;
166
  }
167
-
168
  if ( Utils::instance()->isActivatedSingle() ) {
169
- $allowedForThisRole = array_intersect( $settings->userRoles, $user->roles );
170
-
 
 
 
171
  return count( $allowedForThisRole ) > 0;
172
  } else {
173
  $blogs = get_blogs_of_user( $user->ID );
@@ -177,12 +174,12 @@ class Auth_API extends Component {
177
  $u = new \WP_User( $user->ID, '', $blog->userblog_id );
178
  $userRoles = array_merge( $u->roles, $userRoles );
179
  }
180
- $allowedForThisRole = array_intersect( $settings->userRoles, $userRoles );
181
-
182
  return count( $allowedForThisRole ) > 0;
183
  }
184
  }
185
-
186
  /**
187
  * @param null $user
188
  *
@@ -200,10 +197,10 @@ class Auth_API extends Component {
200
  //this mean user just added but have no roles, we dnt force them
201
  return false;
202
  }
203
-
204
  if ( Utils::instance()->isActivatedSingle() ) {
205
- $isForced = array_intersect( $settings->forceAuthRoles, $user->roles );
206
-
207
  return count( $isForced ) > 0;
208
  } else {
209
  $blogs = get_blogs_of_user( $user->ID );
@@ -213,12 +210,12 @@ class Auth_API extends Component {
213
  $u = new \WP_User( $user->ID, '', $blog->userblog_id );
214
  $userRoles = array_merge( $u->roles, $userRoles );
215
  }
216
- $isForced = array_intersect( $settings->forceAuthRoles, $userRoles );
217
-
218
  return count( $isForced ) > 0;
219
  }
220
  }
221
-
222
  /**
223
  * @return bool|mixed|string
224
  */
@@ -226,16 +223,16 @@ class Auth_API extends Component {
226
  if ( ! is_user_logged_in() ) {
227
  return false;
228
  }
229
-
230
  $secret = get_user_meta( get_current_user_id(), 'defenderAuthSecret', true );
231
  if ( ! $secret ) {
232
  $secret = self::generateSecret();
233
  update_user_meta( get_current_user_id(), 'defenderAuthSecret', $secret );
234
  }
235
-
236
  return $secret;
237
  }
238
-
239
  /**
240
  * @param null $userID
241
  *
@@ -249,10 +246,10 @@ class Auth_API extends Component {
249
  if ( ! $secret ) {
250
  return false;
251
  }
252
-
253
  return $secret;
254
  }
255
-
256
  /**
257
  * @param $userID
258
  *
@@ -268,12 +265,12 @@ class Auth_API extends Component {
268
  if ( ! self::isEnableForCurrentRole( $user ) ) {
269
  return false;
270
  }
271
-
272
  $isOn = get_user_meta( $userID, 'defenderAuthOn', true );
273
-
274
  return $isOn;
275
  }
276
-
277
  /**
278
  * @param $userID
279
  *
@@ -288,10 +285,10 @@ class Auth_API extends Component {
288
  }
289
  $email = $user->user_email;
290
  }
291
-
292
  return $email;
293
  }
294
-
295
  /**
296
  * Generate single code, use in case lost phone
297
  *
@@ -305,10 +302,10 @@ class Auth_API extends Component {
305
  'code' => $code,
306
  'time' => time()
307
  ) );
308
-
309
  return $code;
310
  }
311
-
312
  /**
313
  * @return bool
314
  */
@@ -326,7 +323,7 @@ class Auth_API extends Component {
326
  $options = get_blog_option( $id, 'jetpack_active_modules', array() );
327
  if ( array_search( 'sso', $options ) ) {
328
  $settings->markAsConflict( 'jetpack/jetpack.php' );
329
-
330
  return true;
331
  }
332
  }
@@ -334,7 +331,7 @@ class Auth_API extends Component {
334
  //get the data from cache
335
  return $isConflict;
336
  }
337
-
338
  } elseif ( is_plugin_active( 'jetpack/jetpack.php' ) ) {
339
  //ugly but faster
340
  $settings = Auth_Settings::instance();
@@ -343,18 +340,18 @@ class Auth_API extends Component {
343
  $options = get_option( 'jetpack_active_modules', array() );
344
  if ( array_search( 'sso', $options ) ) {
345
  $settings->markAsConflict( 'jetpack/jetpack.php' );
346
-
347
  return true;
348
  }
349
  } else {
350
  return $isConflict;
351
  }
352
-
353
  }
354
-
355
  return false;
356
  }
357
-
358
  /**
359
  * @return bool
360
  */
@@ -362,10 +359,10 @@ class Auth_API extends Component {
362
  if ( is_plugin_active( 'theme-my-login/theme-my-login.php' ) || is_plugin_active_for_network( 'theme-my-login/theme-my-login.php' ) ) {
363
  $settings = Auth_Settings::instance();
364
  $settings->markAsConflict( 'theme-my-login/theme-my-login.php' );
365
-
366
  return true;
367
  }
368
-
369
  return false;
370
  }
371
  }
5
 
6
  namespace WP_Defender\Module\Advanced_Tools\Component;
7
 
 
 
 
 
 
8
  use Hammer\Base\Component;
 
9
  use WP_Defender\Behavior\Utils;
10
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
11
 
21
  for ( $i = 0; $i < $length; $i ++ ) {
22
  $secret[] = $strings[ rand( 0, strlen( $strings ) - 1 ) ];
23
  }
24
+
25
  return implode( "", $secret );
26
  }
27
+
28
  /**
29
  * @param $name
30
  * @param $secret
40
  $chl .= ( '&issuer=' . rawurlencode( $title ) );
41
  }
42
  //manually include the autoload
43
+ require_once wp_defender()->getPluginPath() . 'vendor/phpqrcode/phpqrcode.php';
44
+
45
  $code = \QRcode::svg( $chl, false, QR_ECLEVEL_L, 4 );
46
+
47
  }
48
+
49
  /**
50
  * Calculate the TOTP code
51
  *
83
  $code = $value % pow( 10, 6 );
84
  //in some case we have the 0 before, so it become lesser than 6, make sure it always right
85
  $code = str_pad( $code, 6, '0', STR_PAD_LEFT );
86
+
87
  return $code;
88
  }
89
+
90
  /**
91
  * @param $secret
92
  * @param $userCode
98
  if ( strlen( $userCode ) != 6 ) {
99
  return false;
100
  }
101
+
102
  /**
103
  * window is 30 seconds, before and after
104
  */
109
  return true;
110
  }
111
  }
112
+
113
+
114
  return false;
115
  }
116
+
117
  /**
118
  * Timing attack safe string comparison, replacement of has_equals which only on 5.6+
119
  *
127
  if ( function_exists( 'hash_equals' ) ) {
128
  return hash_equals( $known_string, $user_string );
129
  }
130
+
131
  $ret = 0;
132
+
133
  if ( strlen( $known_string ) !== strlen( $user_string ) ) {
134
  $user_string = $known_string;
135
  $ret = 1;
136
  }
137
+
138
  $res = $known_string ^ $user_string;
139
+
140
  for ( $i = strlen( $res ) - 1; $i >= 0; -- $i ) {
141
  $ret |= ord( $res[ $i ] );
142
  }
143
+
144
  return ! $ret;
145
  }
146
+
147
  /**
148
  * @return bool
149
  */
158
  if ( 0 === count( $user->roles ) ) {
159
  return true;
160
  }
161
+
162
  if ( Utils::instance()->isActivatedSingle() ) {
163
+ $allowedForThisRole = array_intersect( $settings->user_roles, $user->roles );
164
+ if ( ! is_array( $allowedForThisRole ) ) {
165
+ $allowedForThisRole = [];
166
+ }
167
+
168
  return count( $allowedForThisRole ) > 0;
169
  } else {
170
  $blogs = get_blogs_of_user( $user->ID );
174
  $u = new \WP_User( $user->ID, '', $blog->userblog_id );
175
  $userRoles = array_merge( $u->roles, $userRoles );
176
  }
177
+ $allowedForThisRole = array_intersect( $settings->user_roles, $userRoles );
178
+
179
  return count( $allowedForThisRole ) > 0;
180
  }
181
  }
182
+
183
  /**
184
  * @param null $user
185
  *
197
  //this mean user just added but have no roles, we dnt force them
198
  return false;
199
  }
200
+
201
  if ( Utils::instance()->isActivatedSingle() ) {
202
+ $isForced = array_intersect( $settings->force_auth_roles, $user->roles );
203
+
204
  return count( $isForced ) > 0;
205
  } else {
206
  $blogs = get_blogs_of_user( $user->ID );
210
  $u = new \WP_User( $user->ID, '', $blog->userblog_id );
211
  $userRoles = array_merge( $u->roles, $userRoles );
212
  }
213
+ $isForced = array_intersect( $settings->force_auth_roles, $userRoles );
214
+
215
  return count( $isForced ) > 0;
216
  }
217
  }
218
+
219
  /**
220
  * @return bool|mixed|string
221
  */
223
  if ( ! is_user_logged_in() ) {
224
  return false;
225
  }
226
+
227
  $secret = get_user_meta( get_current_user_id(), 'defenderAuthSecret', true );
228
  if ( ! $secret ) {
229
  $secret = self::generateSecret();
230
  update_user_meta( get_current_user_id(), 'defenderAuthSecret', $secret );
231
  }
232
+
233
  return $secret;
234
  }
235
+
236
  /**
237
  * @param null $userID
238
  *
246
  if ( ! $secret ) {
247
  return false;
248
  }
249
+
250
  return $secret;
251
  }
252
+
253
  /**
254
  * @param $userID
255
  *
265
  if ( ! self::isEnableForCurrentRole( $user ) ) {
266
  return false;
267
  }
268
+
269
  $isOn = get_user_meta( $userID, 'defenderAuthOn', true );
270
+
271
  return $isOn;
272
  }
273
+
274
  /**
275
  * @param $userID
276
  *
285
  }
286
  $email = $user->user_email;
287
  }
288
+
289
  return $email;
290
  }
291
+
292
  /**
293
  * Generate single code, use in case lost phone
294
  *
302
  'code' => $code,
303
  'time' => time()
304
  ) );
305
+
306
  return $code;
307
  }
308
+
309
  /**
310
  * @return bool
311
  */
323
  $options = get_blog_option( $id, 'jetpack_active_modules', array() );
324
  if ( array_search( 'sso', $options ) ) {
325
  $settings->markAsConflict( 'jetpack/jetpack.php' );
326
+
327
  return true;
328
  }
329
  }
331
  //get the data from cache
332
  return $isConflict;
333
  }
334
+
335
  } elseif ( is_plugin_active( 'jetpack/jetpack.php' ) ) {
336
  //ugly but faster
337
  $settings = Auth_Settings::instance();
340
  $options = get_option( 'jetpack_active_modules', array() );
341
  if ( array_search( 'sso', $options ) ) {
342
  $settings->markAsConflict( 'jetpack/jetpack.php' );
343
+
344
  return true;
345
  }
346
  } else {
347
  return $isConflict;
348
  }
349
+
350
  }
351
+
352
  return false;
353
  }
354
+
355
  /**
356
  * @return bool
357
  */
359
  if ( is_plugin_active( 'theme-my-login/theme-my-login.php' ) || is_plugin_active_for_network( 'theme-my-login/theme-my-login.php' ) ) {
360
  $settings = Auth_Settings::instance();
361
  $settings->markAsConflict( 'theme-my-login/theme-my-login.php' );
362
+
363
  return true;
364
  }
365
+
366
  return false;
367
  }
368
  }
app/module/advanced-tools/component/auth-listener.php ADDED
@@ -0,0 +1,459 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Advanced_Tools\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use Hammer\WP\Component;
10
+ use WP_Defender\Behavior\Utils;
11
+ use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
12
+
13
+ class Auth_Listener extends Component {
14
+ protected $sessionToken;
15
+
16
+ public function __construct() {
17
+ $this->addAction( 'update_option_jetpack_active_modules', 'listenForJetpackOption', 10, 3 );
18
+ $setting = Auth_Settings::instance();
19
+ if ( $setting->enabled ) {
20
+ //prepare for the login part
21
+ $isJetpackSSO = Auth_API::isJetPackSSO();
22
+ $isTML = Auth_API::isTML();
23
+ if ( ! defined( 'DOING_AJAX' ) && ! $isJetpackSSO && ! $isTML ) {
24
+ /**
25
+ * hook into wordpress login, can't use authenticate hook as that badly conflict
26
+ */
27
+ $this->addAction( 'wp_login', 'maybeShowOTPLogin', 9, 2 );
28
+ $this->addAction( 'login_form_defenderVerifyOTP', 'defenderVerifyOTP' );
29
+ $this->addAction( 'set_logged_in_cookie', 'storeSessionKey' );
30
+ /**
31
+ * end
32
+ */
33
+ } else {
34
+ if ( $isJetpackSSO ) {
35
+ wp_defender()->global['compatibility'][] = __( "We've detected a conflict with Jetpack's Wordpress.com Log In feature. Please disable it and return to this page to continue setup.", "defender-security" );
36
+ }
37
+ if ( $isTML ) {
38
+ wp_defender()->global['compatibility'][] = __( "We've detected a conflict with Theme my login. Please disable it and return to this page to continue setup.", "defender-security" );
39
+ }
40
+ }
41
+ $this->addFilter( 'ms_shortcode_ajax_login', 'm2NoAjax' );
42
+ $this->addAction( 'show_user_profile', 'showUsers2FactorActivation' );
43
+ $this->addAction( 'profile_update', 'saveBackupEmail' );
44
+ //$this->add_action( 'wp_login', 'markAsForceAuth', 10, 2 );
45
+ $this->addFilter( 'login_redirect', 'loginRedirect', 99 );
46
+ $this->addAction( 'current_screen', 'forceProfilePage', 1 );
47
+ $this->addAjaxAction( 'defVerifyOTP', 'verifyConfigOTP' );
48
+ $this->addAjaxAction( 'defDisableOTP', 'disableOTP' );
49
+ $this->addAjaxAction( 'defRetrieveOTP', 'retrieveOTP', false, true );
50
+ if ( Utils::instance()->isActivatedSingle() ) {
51
+ $this->addFilter( 'manage_users_columns', 'alterUsersTable' );
52
+ $this->addFilter( 'manage_users_custom_column', 'alterUsersTableRow', 10, 3 );
53
+ } else {
54
+ $this->addFilter( 'wpmu_users_columns', 'alterUsersTable' );
55
+ $this->addFilter( 'manage_users_custom_column', 'alterUsersTableRow', 10, 3 );
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ * If user have flag then force enable
62
+ */
63
+ public function forceProfilePage() {
64
+ $user = wp_get_current_user();
65
+ if ( ! is_object( $user ) ) {
66
+ return;
67
+ }
68
+
69
+ $settings = Auth_Settings::instance();
70
+ if ( $settings->force_auth != true ) {
71
+ return;
72
+ }
73
+
74
+ //not enable for this role oass
75
+ if ( ! Auth_API::isEnableForCurrentRole( $user ) ) {
76
+ return;
77
+ }
78
+
79
+ //check if this role is forced
80
+ if ( ! Auth_API::isForcedRole( $user ) ) {
81
+ return;
82
+ }
83
+
84
+ //user already enable OTP
85
+ if ( Auth_API::isUserEnableOTP( $user->ID ) ) {
86
+ return;
87
+ }
88
+
89
+ $screen = get_current_screen();
90
+ if ( $screen->id != 'profile' ) {
91
+ wp_safe_redirect( admin_url( 'profile.php' ) . '#show2AuthActivator' );
92
+ exit;
93
+ }
94
+ }
95
+
96
+ public function loginRedirect( $url ) {
97
+ $settings = Auth_Settings::instance();
98
+ if ( $settings->force_auth != true ) {
99
+ return $url;
100
+ }
101
+
102
+ return $url;
103
+ }
104
+
105
+ /**
106
+ * @param $userLogin
107
+ * @param $user
108
+ */
109
+ public function markAsForceAuth( $userLogin, $user ) {
110
+ $settings = Auth_Settings::instance();
111
+ if ( $settings->force_auth != true ) {
112
+ return;
113
+ }
114
+ //not enable for this role oass
115
+ if ( ! Auth_API::isEnableForCurrentRole( $user ) ) {
116
+ return;
117
+ }
118
+ //user already enable OTP
119
+ if ( Auth_API::isUserEnableOTP( $user->ID ) ) {
120
+ return;
121
+ }
122
+ //if this is normal user, force them
123
+ // if ( ! current_user_can( 'subscriber' ) ) {
124
+ // return;
125
+ // }
126
+ $flag = get_user_meta( $user->ID, 'defenderForceAuth', true );
127
+ if ( $flag === '' ) {
128
+ update_user_meta( $user->ID, 'defenderForceAuth', 1 );
129
+ }
130
+ }
131
+
132
+ /**
133
+ * We have some feature conflict with jetpack, so listen to know when Defender can on
134
+ *
135
+ * @param $old_value
136
+ * @param $value
137
+ * @param $option
138
+ */
139
+ public function listenForJetpackOption( $old_value, $value, $option ) {
140
+ $settings = Auth_Settings::instance();
141
+ if ( array_search( 'sso', $value ) !== false ) {
142
+ $settings->markAsConflict( 'jetpack/jetpack.php' );
143
+ } else {
144
+ $settings->markAsUnConflict( 'jetpack/jetpack.php' );
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Stop ajax login on membership 2
150
+ * @return bool
151
+ */
152
+ public function m2NoAjax() {
153
+ return false;
154
+ }
155
+
156
+ /**
157
+ * Return 2 factor auth status
158
+ *
159
+ * @param $val
160
+ * @param $column_name
161
+ * @param $user_id
162
+ *
163
+ * @return string
164
+ */
165
+ public function alterUsersTableRow( $val, $column_name, $user_id ) {
166
+ if ( $column_name != 'defAuth' ) {
167
+ return $val;
168
+ }
169
+
170
+ if ( Auth_API::isUserEnableOTP( $user_id ) ) {
171
+ return '<span class="def-oval oval-green"></span>';
172
+ }
173
+
174
+ return '<span class="def-oval"></span>';
175
+ }
176
+
177
+ /**
178
+ * Add the auth column inside users on single site
179
+ *
180
+ * @param $columns
181
+ *
182
+ * @return mixed
183
+ *
184
+ */
185
+ public function alterUsersTable( $columns ) {
186
+ $columns = array_slice( $columns, 0, count( $columns ) - 1 ) + array(
187
+ 'defAuth' => __( "Two Factor", "defender-security" )
188
+ ) + array_slice( $columns, count( $columns ) - 1 );
189
+
190
+ return $columns;
191
+ }
192
+
193
+ /**
194
+ * Generate an email for backup otp
195
+ */
196
+ public function retrieveOTP() {
197
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( 'nonce' ), 'defRetrieveOTP' ) ) {
198
+ wp_send_json_error( array() );
199
+ }
200
+
201
+ $token = HTTP_Helper::retrieveGet( 'token' );
202
+ $query = new \WP_User_Query( array(
203
+ 'meta_key' => 'defOTPLoginToken',
204
+ 'meta_value' => $token
205
+ ) );
206
+ $res = $query->get_results();
207
+ if ( empty( $res ) ) {
208
+ //no user
209
+ wp_send_json_error( array(
210
+ 'message' => __( "Your token is invalid", "defender-security" )
211
+ ) );
212
+ }
213
+
214
+ $user = $res[0];
215
+ //create a backup code for this user
216
+ $code = Auth_API::createBackupCode( $user->ID );
217
+ //send email
218
+ $backupEmail = Auth_API::getBackupEmail( $user->ID );
219
+
220
+ $settings = Auth_Settings::instance();
221
+ $subject = ! empty( $settings->email_subject ) ? esc_attr( $settings->email_subject ) : __( 'Your OTP code', "defender-security" );
222
+ $sender = ! empty( $settings->email_sender ) ? esc_attr( $settings->email_sender ) : false;
223
+ $body = ! empty( $settings->email_body ) ? $settings->email_body : $settings->two_factor_opt_email_default_body();
224
+ $params = [
225
+ 'display_name' => $user->display_name,
226
+ 'passcode' => $code,
227
+ ];
228
+ foreach ( $params as $key => $val ) {
229
+ $body = str_replace( '{{' . $key . '}}', $val, $body );
230
+ }
231
+ $headers = array( 'Content-Type: text/html; charset=UTF-8' );
232
+ if ( $sender ) {
233
+ $from_email = get_bloginfo( 'admin_email' );
234
+ $headers[] = sprintf( 'From: %s <%s>', $sender, $from_email );
235
+ }
236
+
237
+ //send
238
+ wp_mail( $backupEmail, $subject, $body, $headers );
239
+
240
+ wp_send_json_success( array(
241
+ 'message' => __( "Your code has been sent to your email.", "defender-security" )
242
+ ) );
243
+ }
244
+
245
+ /**
246
+ * disable OTP feature
247
+ */
248
+ public function disableOTP() {
249
+ if ( ! is_user_logged_in() ) {
250
+ return;
251
+ }
252
+
253
+ update_user_meta( get_current_user_id(), 'defenderAuthOn', 0 );
254
+ delete_user_meta( get_current_user_id(), 'defenderAuthSecret' );
255
+ wp_send_json_success();
256
+ }
257
+
258
+ /**
259
+ * Saving backup email when profile saved
260
+ *
261
+ * @param $userID
262
+ */
263
+ public function saveBackupEmail( $userID ) {
264
+ $email = HTTP_Helper::retrievePost( 'def_backup_email' );
265
+ if ( $email && get_current_user_id() == $userID ) {
266
+ update_user_meta( $userID, 'defenderAuthEmail', $email );
267
+ }
268
+ }
269
+
270
+ /**
271
+ * An ajax function for verify the OTP user input when configuring the 2 factors
272
+ */
273
+ public function verifyConfigOTP() {
274
+ if ( ! wp_verify_nonce( HTTP_Helper::retrievePost( 'nonce' ), 'defVerifyOTP' ) ) {
275
+ return;
276
+ }
277
+
278
+ if ( ! is_user_logged_in() ) {
279
+ return;
280
+ }
281
+
282
+ $otp = HTTP_Helper::retrievePost( 'otp' );
283
+ $otp = trim( $otp );
284
+ if ( strlen( $otp ) == 0 ) {
285
+ wp_send_json_error( array(
286
+ 'message' => __( "Please input a valid OTP code", "defender-security" )
287
+ ) );
288
+ }
289
+
290
+ $secret = Auth_API::getUserSecret();
291
+ //at this stage, secret should have value, do not need to check
292
+ $res = Auth_API::compare( $secret, $otp );
293
+ if ( $res ) {
294
+ //save it
295
+ update_user_meta( get_current_user_id(), 'defenderAuthOn', 1 );
296
+ update_user_meta( get_current_user_id(), 'defenderForceAuth', 0 );
297
+ wp_send_json_success();
298
+ } else {
299
+ //now need to check if the current user have backup otp
300
+ wp_send_json_error( array(
301
+ 'message' => __( "Your OTP code is incorrect. Please try again.", "defender-security" )
302
+ ) );
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Show an section inside my profile page for user can activate 2 factor login
308
+ *
309
+ * @param $profileuser
310
+ */
311
+ public function showUsers2FactorActivation( $profileuser ) {
312
+ if ( ! Auth_API::isEnableForCurrentRole() ) {
313
+ return;
314
+ }
315
+
316
+ $isOn = get_user_meta( $profileuser->ID, 'defenderAuthOn', true );
317
+ wp_enqueue_style( 'defAuth', wp_defender()->getPluginUrl() . 'app/module/advanced-tools/css/login-admin.css' );
318
+ $secretKey = Auth_API::createSecretForCurrentUser();
319
+ if ( $isOn && $isOn == 1 ) {
320
+ $email = Auth_API::getBackupEmail( $profileuser->ID );
321
+ $this->renderPartial( 'login/enabled', array(
322
+ 'email' => $email
323
+ ) );
324
+ } else {
325
+ //show the screen
326
+ $this->renderPartial( 'login/disabled', array(
327
+ 'secretKey' => $secretKey
328
+ ) );
329
+ }
330
+ }
331
+
332
+ /**
333
+ * We will check and show the OTP screen if user signon successfully
334
+ *
335
+ * @param $userLogin
336
+ * @param $user
337
+ */
338
+ public function maybeShowOTPLogin( $userLogin, $user ) {
339
+ if ( ! Auth_API::isUserEnableOTP( $user->ID ) ) {
340
+ //no enable, then just return
341
+ return;
342
+ }
343
+
344
+ //clean up session and auth cookies for preventing
345
+ $token = $this->sessionToken;
346
+ if ( $token ) {
347
+ $sManager = \WP_Session_Tokens::get_instance( $user->ID );
348
+ $sManager->destroy( $token );
349
+ }
350
+ wp_clear_auth_cookie();
351
+
352
+ $this->showOTPScreen( $user );
353
+ }
354
+
355
+ /**
356
+ * verify OTP code which user input in order to login
357
+ */
358
+ public function defenderVerifyOTP() {
359
+ if ( ( $otp = HTTP_Helper::retrievePost( 'otp', null ) ) != null ) {
360
+ $params = array();
361
+ if ( ! wp_verify_nonce( HTTP_Helper::retrievePost( '_wpnonce' ), 'DefOtpCheck' ) ) {
362
+ $params['error'] = new \WP_Error( 'security_fail', __( "Some error happen", "defender-security" ) );
363
+ }
364
+
365
+ $login_token = HTTP_Helper::retrievePost( 'login_token' );
366
+ $query = new \WP_User_Query( array(
367
+ 'meta_key' => 'defOTPLoginToken',
368
+ 'meta_value' => $login_token,
369
+ 'blog_id' => 0
370
+ ) );
371
+ $res = $query->get_results();
372
+ if ( empty( $res ) ) {
373
+ //no users, redirect to the login page immediatly
374
+ wp_redirect( site_url( 'wp-login.php', 'login_post' ) );
375
+ exit;
376
+ } else {
377
+ $user = $res[0];
378
+ $secret = Auth_API::getUserSecret( $user->ID );
379
+ $redirect = HTTP_Helper::retrievePost( 'redirect_to', admin_url() );
380
+ if ( Auth_API::compare( $secret, $otp ) ) {
381
+ //sign in
382
+ delete_user_meta( $user->ID, 'defOTPLoginToken' );
383
+ wp_set_current_user( $user->ID, $user->user_login );
384
+ wp_set_auth_cookie( $user->ID, true );
385
+ $redirect = apply_filters( 'login_redirect', $redirect, isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '', $user );
386
+ wp_safe_redirect( $redirect );
387
+ exit;
388
+ } else {
389
+ $backupCode = get_user_meta( $user->ID, 'defenderBackupCode', true );
390
+ if ( $backupCode && $backupCode['code'] == $otp && strtotime( '+3 minutes', $backupCode['time'] ) > time() ) {
391
+ delete_user_meta( $user->ID, 'defOTPLoginToken' );
392
+ delete_user_meta( $user->ID, 'defenderBackupCode' );
393
+ wp_set_current_user( $user->ID, $user->user_login );
394
+ wp_set_auth_cookie( $user->ID, true );
395
+ $redirect = apply_filters( 'login_redirect', $redirect, isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '', $user );
396
+ wp_safe_redirect( $redirect );
397
+ exit;
398
+ } else {
399
+ $params['error'] = new \WP_Error( 'opt_fail', __( "Whoops, the passcode you entered was incorrect or expired.", "defender-security" ) );
400
+ $this->showOTPScreen( $user, $params );
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+
407
+ /**
408
+ * Show the OTP screen
409
+ *
410
+ * @param $user
411
+ * @param $params
412
+ */
413
+ private function showOTPScreen( $user, $params = array() ) {
414
+ //now show the OTP screen
415
+ $this->addAction( 'login_enqueue_scripts', 'includeAuthStyles' );
416
+ wp_enqueue_script( 'jquery' );
417
+ $params['loginToken'] = $this->createLoginToken( $user );
418
+ $params['redirect_to'] = HTTP_Helper::retrievePost( 'redirect_to' );
419
+ if ( ! isset( $params['error'] ) ) {
420
+ $params['error'] = null;
421
+ }
422
+ //if this goes here then the current user is ok, need to show the 2 auth
423
+ $this->renderPartial( 'login/otp', $params );
424
+ exit;
425
+ }
426
+
427
+ /**
428
+ * We will empty all auth cookies or session, so should not rely on wp_get_session_token
429
+ *
430
+ * @param $cookie
431
+ */
432
+ public function storeSessionKey( $cookie ) {
433
+ $cookie = wp_parse_auth_cookie( $cookie, 'logged_in' );
434
+ $this->sessionToken = ! empty( $cookie['token'] ) ? $cookie['token'] : '';
435
+ }
436
+
437
+ /**
438
+ * Create a unique token to retrieve user later
439
+ *
440
+ * @param $user
441
+ *
442
+ * @return string
443
+ */
444
+ private function createLoginToken( $user ) {
445
+ $tmp = uniqid();
446
+ // create and store a login token so we can query this user again
447
+ update_user_meta( $user->ID, 'defOTPLoginToken', $tmp );
448
+
449
+ return $tmp;
450
+ }
451
+
452
+ /**
453
+ * add css for OTP page
454
+ */
455
+ public function includeAuthStyles() {
456
+ //enqueue css here
457
+ wp_enqueue_style( 'defAuth', wp_defender()->getPluginUrl() . 'app/module/advanced-tools/css/login.css' );
458
+ }
459
+ }
app/module/advanced-tools/component/mask-api.php CHANGED
@@ -26,7 +26,7 @@ class Mask_Api extends Component {
26
  }
27
  //
28
  $requestUri = '/' . ltrim( $requestUri, '/' );
29
- $prefix = parse_url( self::site_url(), PHP_URL_PATH );
30
  $requestPath = parse_url( $requestUri, PHP_URL_PATH );
31
  //clean it a bit
32
  if ( Utils::instance()->isActivatedSingle() == false
@@ -34,7 +34,7 @@ class Mask_Api extends Component {
34
  && constant( 'SUBDOMAIN_INSTALL' ) == false
35
  && get_current_blog_id() != 1
36
  ) {
37
- $prefix = parse_url( self::network_site_url(), PHP_URL_PATH );
38
  //get the prefix
39
  $siteInfo = get_blog_details();
40
  $path = $siteInfo->path;
@@ -42,9 +42,9 @@ class Mask_Api extends Component {
42
  $requestPath = substr( $requestPath, strlen( $path ) );
43
  $requestPath = '/' . ltrim( $requestPath, '/' );
44
  }
45
- } elseif ( self::get_home_url() != self::site_url() && strpos( $requestPath, (string) $prefix . '/' ) !== 0 ) {
46
  //this case when a wp install inside a sub folder and domain changed into that subfolder
47
- $prefix = parse_url( self::get_home_url(), PHP_URL_PATH );
48
  }
49
  if ( strlen( $prefix ) && strpos( $requestPath, (string) $prefix ) === 0 ) {
50
  $requestPath = substr( $requestPath, strlen( $prefix ) );
@@ -65,7 +65,7 @@ class Mask_Api extends Component {
65
  *
66
  * @return string
67
  */
68
- private static function site_url( $path = '', $scheme = null ) {
69
  if ( empty( $blog_id ) || ! is_multisite() ) {
70
  $url = get_option( 'siteurl' );
71
  } else {
@@ -83,6 +83,73 @@ class Mask_Api extends Component {
83
  return $url;
84
  }
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  /**
87
  * A clone of network_site_url but remove the filter
88
  *
@@ -91,7 +158,7 @@ class Mask_Api extends Component {
91
  *
92
  * @return string
93
  */
94
- private static function network_site_url( $path = '', $scheme = null ) {
95
  $current_network = get_network();
96
 
97
  if ( 'relative' == $scheme ) {
@@ -116,7 +183,7 @@ class Mask_Api extends Component {
116
  *
117
  * @return mixed|void
118
  */
119
- private static function get_home_url( $blog_id = null, $path = '', $scheme = null ) {
120
  global $pagenow;
121
 
122
  $orig_scheme = $scheme;
@@ -152,7 +219,7 @@ class Mask_Api extends Component {
152
  public static function getRedirectUrl() {
153
  $settings = Mask_Settings::instance();
154
 
155
- return untrailingslashit( get_home_url( get_current_blog_id() ) ) . '/' . ltrim( $settings->redirectTrafficUrl, '/' );
156
  }
157
 
158
  /**
@@ -161,10 +228,10 @@ class Mask_Api extends Component {
161
  public static function getNewLoginUrl( $domain = null ) {
162
  $settings = Mask_Settings::instance();
163
  if ( $domain == null ) {
164
- $domain = self::site_url();
165
  }
166
 
167
- return untrailingslashit( $domain . '/' . ltrim( $settings->maskUrl, '/' ) );
168
  }
169
 
170
  /**
@@ -228,7 +295,7 @@ class Mask_Api extends Component {
228
  * @return bool
229
  */
230
  public static function verifyOTP( $otp ) {
231
- $key = HTTP_Helper::retrieve_get( 'otp' );
232
  $secret = Auth_API::getUserSecret();
233
  $key = md5( $key . $secret );
234
  $check = get_site_transient( $key );
26
  }
27
  //
28
  $requestUri = '/' . ltrim( $requestUri, '/' );
29
+ $prefix = parse_url( self::siteUrl(), PHP_URL_PATH );
30
  $requestPath = parse_url( $requestUri, PHP_URL_PATH );
31
  //clean it a bit
32
  if ( Utils::instance()->isActivatedSingle() == false
34
  && constant( 'SUBDOMAIN_INSTALL' ) == false
35
  && get_current_blog_id() != 1
36
  ) {
37
+ $prefix = parse_url( self::networkSiteUrl(), PHP_URL_PATH );
38
  //get the prefix
39
  $siteInfo = get_blog_details();
40
  $path = $siteInfo->path;
42
  $requestPath = substr( $requestPath, strlen( $path ) );
43
  $requestPath = '/' . ltrim( $requestPath, '/' );
44
  }
45
+ } elseif ( self::getHomeUrl() != self::siteUrl() && strpos( $requestPath, (string) $prefix . '/' ) !== 0 ) {
46
  //this case when a wp install inside a sub folder and domain changed into that subfolder
47
+ $prefix = parse_url( self::getHomeUrl(), PHP_URL_PATH );
48
  }
49
  if ( strlen( $prefix ) && strpos( $requestPath, (string) $prefix ) === 0 ) {
50
  $requestPath = substr( $requestPath, strlen( $prefix ) );
65
  *
66
  * @return string
67
  */
68
+ private static function siteUrl( $path = '', $scheme = null ) {
69
  if ( empty( $blog_id ) || ! is_multisite() ) {
70
  $url = get_option( 'siteurl' );
71
  } else {
83
  return $url;
84
  }
85
 
86
+ /**
87
+ * Generate a random unique onetime pass and store in user meta
88
+ * Laterly we can use it to by pass the mask admin
89
+ * @return bool
90
+ */
91
+ public static function generateTicket() {
92
+ $otp = wp_generate_password( 12, false );
93
+ $settings = Mask_Settings::instance();
94
+ $settings->otps[ $otp ] = [
95
+ 'otp' => $otp,
96
+ 'bind_to' => null,
97
+ 'expiry' => strtotime( '+3 day' ),
98
+ 'used' => 0
99
+ ];
100
+ $settings->save();
101
+
102
+ return $otp;
103
+ }
104
+
105
+ /**
106
+ * @param $ticket
107
+ *
108
+ * @return bool
109
+ */
110
+ public static function redeemTicket( $ticket ) {
111
+ $settings = Mask_Settings::instance();
112
+ $detail = isset( $settings->otps[ $ticket ] ) ? $settings->otps[ $ticket ] : false;
113
+ if ( $detail === false ) {
114
+ return false;
115
+ }
116
+
117
+ /**
118
+ * ticket expired
119
+ */
120
+ if ( $detail['expiry'] < time() ) {
121
+ unset( $settings->otps[ $ticket ] );
122
+ $settings->save();
123
+
124
+ return false;
125
+ }
126
+
127
+ $userIP = Utils::instance()->getUserIp();
128
+ if ( $detail['bind_to'] !== null && $detail['bind_to'] != $userIP ) {
129
+ //this is binded to an IP but current IP not the same, not allow
130
+ return false;
131
+ }
132
+
133
+ if ( $detail['bind_to'] === null ) {
134
+ $detail['bind_to'] = $userIP;
135
+ }
136
+ $detail['used'] += 1;
137
+ $settings->otps[ $ticket ] = $detail;
138
+ $settings->save();
139
+
140
+ return true;
141
+ }
142
+
143
+ /**
144
+ * @param $url
145
+ * @param $user_id
146
+ *
147
+ * @return string
148
+ */
149
+ public static function maybeAppendTicketToUrl( $url ) {
150
+ return add_query_arg( 'ticket', self::generateTicket(), $url );
151
+ }
152
+
153
  /**
154
  * A clone of network_site_url but remove the filter
155
  *
158
  *
159
  * @return string
160
  */
161
+ private static function networkSiteUrl( $path = '', $scheme = null ) {
162
  $current_network = get_network();
163
 
164
  if ( 'relative' == $scheme ) {
183
  *
184
  * @return mixed|void
185
  */
186
+ private static function getHomeUrl( $blog_id = null, $path = '', $scheme = null ) {
187
  global $pagenow;
188
 
189
  $orig_scheme = $scheme;
219
  public static function getRedirectUrl() {
220
  $settings = Mask_Settings::instance();
221
 
222
+ return untrailingslashit( get_home_url( get_current_blog_id() ) ) . '/' . ltrim( $settings->redirect_traffic_url, '/' );
223
  }
224
 
225
  /**
228
  public static function getNewLoginUrl( $domain = null ) {
229
  $settings = Mask_Settings::instance();
230
  if ( $domain == null ) {
231
+ $domain = site_url();
232
  }
233
 
234
+ return untrailingslashit( $domain . '/' . ltrim( $settings->mask_url, '/' ) );
235
  }
236
 
237
  /**
295
  * @return bool
296
  */
297
  public static function verifyOTP( $otp ) {
298
+ $key = HTTP_Helper::retrieveGet( 'otp' );
299
  $secret = Auth_API::getUserSecret();
300
  $key = md5( $key . $secret );
301
  $check = get_site_transient( $key );
app/module/advanced-tools/component/mask-login-listener.php ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Advanced_Tools\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use Hammer\WP\Component;
10
+ use WP_Defender\Module\Advanced_Tools\Model\Mask_Settings;
11
+
12
+ class Mask_Login_Listener extends Component {
13
+ public function __construct() {
14
+ $settings = Mask_Settings::instance();
15
+ $emergencySwitch = apply_filters( 'wpd_masklogin_disabled', 0 );
16
+
17
+ if ( $settings->isEnabled() == true && $emergencySwitch == 0 ) {
18
+ $isJetpackSSO = Auth_API::isJetPackSSO();
19
+ $isTML = Auth_API::isTML();
20
+ if ( ! $isJetpackSSO && ! $isTML ) {
21
+ $this->addAction( 'init', 'handleLoginRequest', 9999 );
22
+ $this->addFilter( 'wp_redirect', 'filterWPRedirect', 10, 2 );
23
+ $this->addFilter( 'site_url', 'filterSiteUrl', 9999, 4 );
24
+ $this->addFilter( 'network_site_url', 'filterNetworkSiteUrl', 9999, 3 );
25
+ // $this->add_filter( 'network_admin_url', 'filterAdminUrl', 9999, 2 );
26
+ // $this->add_filter( 'adminUrl', 'filterAdminUrl', 9999, 2 );
27
+ remove_action( 'template_redirect', 'wp_redirect_admin_locations' );
28
+ //if prosite is activate and useremail is not defined, we need to update the
29
+ //email to match the new login URL
30
+ $this->addFilter( 'update_welcome_email', 'updateWelcomeEmailPrositeCase', 10, 6 );
31
+ $this->addFilter( 'report_email_logs_link', 'updateReportLogsLink', 10, 2 );
32
+ } else {
33
+ if ( $isJetpackSSO ) {
34
+ wp_defender()->global['compatibility'][] = __( "We've detected a conflict with Jetpack's Wordpress.com Log In feature. Please disable it and return to this page to continue setup.", "defender-security" );
35
+ }
36
+ if ( $isTML ) {
37
+ wp_defender()->global['compatibility'][] = __( "We've detected a conflict with Theme my login. Please disable it and return to this page to continue setup.", "defender-security" );
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * @param $logs_url
45
+ * @param $email
46
+ *
47
+ * @return string
48
+ */
49
+ public function updateReportLogsLink( $logs_url, $email ) {
50
+ $user = get_user_by( 'email', $email );
51
+ if ( is_object( $user ) ) {
52
+ $logs_url = Mask_Api::maybeAppendTicketToUrl( $logs_url );
53
+ } else {
54
+ $logs_url = add_query_arg( 'redirect_to', $logs_url, Mask_Api::getNewLoginUrl() );
55
+ }
56
+
57
+ return $logs_url;
58
+ }
59
+
60
+ public function handleLoginRequest() {
61
+ //need to check if the current request is for signup, login, if those is not the slug, then we redirect
62
+ //to the 404 redirect, or 403 wp die
63
+ $requestPath = Mask_Api::getRequestPath();
64
+ $settings = Mask_Settings::instance();
65
+ $ticket = HTTP_Helper::retrieveGet( 'ticket', false );
66
+ if ( $ticket !== false && Mask_Api::redeemTicket( $ticket ) ) {
67
+ //we have an express ticket
68
+ return true;
69
+ }
70
+ if ( '/' . ltrim( $settings->mask_url, '/' ) == $requestPath ) {
71
+ //we need to redirect this one to wp-login and open it
72
+ $this->_showLoginPage();
73
+ } elseif ( substr( $requestPath, 0, 9 ) == '/wp-admin' ) {
74
+ //this one try to login to wp-admin, redirect or lock it
75
+ $this->_handleRequestToAdmin();
76
+ } elseif ( $requestPath == '/wp-login.php' || $requestPath == '/login' ) {
77
+ //this one want to login, redirect or lock
78
+ $this->_handleRequestToLoginPage();
79
+ }
80
+ }
81
+
82
+ /**
83
+ * @param $welcome_email
84
+ * @param $blog_id
85
+ * @param $user_id
86
+ * @param $password
87
+ * @param $title
88
+ * @param $meta
89
+ *
90
+ * @return mixed
91
+ */
92
+ public function updateWelcomeEmailPrositeCase( $welcome_email, $blog_id, $user_id, $password, $title, $meta ) {
93
+ $url = get_blogaddress_by_id( $blog_id );
94
+ $welcome_email = str_replace( $url . 'wp-login.php', Mask_Api::getNewLoginUrl( rtrim( $url, '/' ) ), $welcome_email );
95
+
96
+ return $welcome_email;
97
+ }
98
+
99
+ /**
100
+ * @param $url
101
+ * @param $path
102
+ * @param $scheme
103
+ *
104
+ * @return string
105
+ */
106
+ public function filterNetworkSiteUrl( $url, $path, $scheme ) {
107
+ return $this->alterLoginUrl( $url, $scheme );
108
+ }
109
+
110
+ /**
111
+ * @param $url
112
+ * @param $path
113
+ * @param $scheme
114
+ * @param $blog_id
115
+ *
116
+ * @return string
117
+ */
118
+ public function filterSiteUrl( $url, $path, $scheme, $blog_id ) {
119
+ return $this->alterLoginUrl( $url, $scheme );
120
+ }
121
+
122
+ /**
123
+ * @param $location
124
+ * @param $status
125
+ *
126
+ * @return string
127
+ */
128
+ public function filterWPRedirect( $location, $status ) {
129
+ return $this->alterLoginUrl( $location );
130
+ }
131
+
132
+ /**
133
+ * @param $currentUrl
134
+ * @param null $scheme
135
+ *
136
+ * @return string
137
+ */
138
+ private function alterLoginUrl( $currentUrl, $scheme = null ) {
139
+ if ( stristr( $currentUrl, 'wp-login.php' ) !== false ) {
140
+ //this is URL go to old wp-login.php
141
+ $parts = parse_url( $currentUrl );
142
+ if ( isset( $parts['query'] ) ) {
143
+ parse_str( $parts['query'], $strings );
144
+
145
+ return add_query_arg( $strings, Mask_Api::getNewLoginUrl() );
146
+ } else {
147
+ return Mask_Api::getNewLoginUrl();
148
+ }
149
+ } else {
150
+ //this case when admin map a domain into subsite, we need to update the new domain/masked-login into the list
151
+ if ( ! function_exists( 'get_current_screen' ) ) {
152
+ require_once( ABSPATH . 'wp-admin/includes/screen.php' );
153
+ }
154
+ $screen = get_current_screen();
155
+ if ( ! is_object( $screen ) ) {
156
+ return $currentUrl;
157
+ }
158
+ if ( $screen->id == 'sites-network' ) {
159
+ //case URLs inside sites list, need to check those with custom domain cause when redirect, it will require re-loggin
160
+ $requestPath = Mask_Api::getRequestPath( $currentUrl );
161
+ if ( $requestPath == '/wp-admin' ) {
162
+ $currentDomain = $_SERVER['HTTP_HOST'];
163
+ $subDomain = parse_url( $currentUrl, PHP_URL_HOST );
164
+ if ( stristr( $subDomain, $currentDomain ) === false ) {
165
+ return Mask_Api::getNewLoginUrl( $subDomain );
166
+ }
167
+ }
168
+ } elseif ( $screen->id == 'my-sites' ) {
169
+ //case inside my sites page, sometime the login session does not share between sites and we get block
170
+ //we will add an OTP key for redirect to wp-admin without get block
171
+ $otp = Mask_Api::createOTPKey();
172
+
173
+ return add_query_arg( array(
174
+ 'otp' => $otp
175
+ ), $currentUrl );
176
+ }
177
+ }
178
+
179
+ return $currentUrl;
180
+ }
181
+
182
+ /**
183
+ * Filter admin URL when sync with HUB
184
+ *
185
+ * @param $currentUrl
186
+ * @param null $scheme
187
+ *
188
+ * @return mixed
189
+ */
190
+ public function filterAdminUrl( $currentUrl, $scheme = null ) {
191
+
192
+ return $currentUrl;
193
+ }
194
+
195
+ /**
196
+ * Catch any request to wp-admin/*, block or redirect it base on settings.
197
+ * This wont apply for logged in user
198
+ */
199
+ private function _handleRequestToAdmin() {
200
+ global $pagenow;
201
+ if ( defined( 'DOING_AJAX' ) ) {
202
+ //we need to allow ajax access for other tasks
203
+ return;
204
+ }
205
+ if ( is_user_logged_in() ) {
206
+ return;
207
+ }
208
+
209
+ if ( ( $key = HTTP_Helper::retrieveGet( 'otp', false ) ) !== false
210
+ && Mask_Api::verifyOTP( $key ) ) {
211
+ return;
212
+ }
213
+
214
+ $this->_maybeLock();
215
+ }
216
+
217
+ private function _handleRequestToLoginPage() {
218
+ $this->_maybeLock();
219
+ }
220
+
221
+ private function _showLoginPage() {
222
+ global $error, $interim_login, $action, $user_login, $user, $redirect_to;
223
+ require_once ABSPATH . 'wp-login.php';
224
+ die;
225
+ }
226
+
227
+ private function _maybeLock() {
228
+ $settings = Mask_Settings::instance();
229
+ if ( $settings->isRedirect() == true ) {
230
+ wp_safe_redirect( Mask_Api::getRedirectUrl() );
231
+ die;
232
+ } else {
233
+ wp_die( __( "This feature is disabled", "defender-security" ) );
234
+ }
235
+ }
236
+ }
app/module/advanced-tools/controller/main.php CHANGED
@@ -10,474 +10,40 @@ use Hammer\Helper\WP_Helper;
10
  use WP_Defender\Behavior\Utils;
11
  use WP_Defender\Controller;
12
  use WP_Defender\Module\Advanced_Tools\Component\Auth_API;
 
 
 
13
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
 
14
 
15
  class Main extends Controller {
16
  protected $slug = 'wdf-advanced-tools';
17
- protected $sessionToken;
18
- public $layout = 'layout';
19
-
20
  /**
21
  * @return array
22
  */
23
  public function behaviors() {
24
- return array(
25
- 'utils' => '\WP_Defender\Behavior\Utils'
 
 
26
  );
 
 
27
  }
28
-
29
  public function __construct() {
30
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
31
- $this->add_action( 'network_admin_menu', 'adminMenu' );
32
- } else {
33
- $this->add_action( 'admin_menu', 'adminMenu' );
34
- }
35
-
36
- if ( $this->isInPage() || $this->isDashboard() ) {
37
- $this->add_action( 'defender_enqueue_assets', 'scripts', 12 );
38
- }
39
- $this->add_ajax_action( 'saveAdvancedSettings', 'saveSettings' );
40
- $this->add_ajax_action( 'saveTwoFactorOPTEmail', 'saveTwoFactorOPTEmail' );
41
- $this->add_ajax_action( 'testTwoFactorOPTEmail', 'testTwoFactorOPTEmail' );
42
- $this->add_action( 'update_option_jetpack_active_modules', 'listenForJetpackOption', 10, 3 );
43
- $setting = Auth_Settings::instance();
44
- if ( $setting->enabled ) {
45
- //prepare for the login part
46
- $isJetpackSSO = Auth_API::isJetPackSSO();
47
- $isTML = Auth_API::isTML();
48
- if ( ! defined( 'DOING_AJAX' ) && ! $isJetpackSSO && ! $isTML ) {
49
- /**
50
- * hook into wordpress login, can't use authenticate hook as that badly conflict
51
- */
52
- $this->add_action( 'wp_login', 'maybeShowOTPLogin', 9, 2 );
53
- $this->add_action( 'login_form_defenderVerifyOTP', 'defenderVerifyOTP' );
54
- $this->add_action( 'set_logged_in_cookie', 'storeSessionKey' );
55
- /**
56
- * end
57
- */
58
- } else {
59
- if ( $isJetpackSSO ) {
60
- wp_defender()->global['compatibility'][] = __( "We’ve detected a conflict with Jetpack’s Wordpress.com Log In feature. Please disable it and return to this page to continue setup.", "defender-security" );
61
- }
62
- if ( $isTML ) {
63
- wp_defender()->global['compatibility'][] = __( "We’ve detected a conflict with Theme my login. Please disable it and return to this page to continue setup.", "defender-security" );
64
- }
65
- }
66
- $this->add_filter( 'ms_shortcode_ajax_login', 'm2NoAjax' );
67
- $this->add_action( 'show_user_profile', 'showUsers2FactorActivation' );
68
- $this->add_action( 'profile_update', 'saveBackupEmail' );
69
- //$this->add_action( 'wp_login', 'markAsForceAuth', 10, 2 );
70
- $this->add_filter( 'login_redirect', 'login_redirect', 99 );
71
- $this->add_action( 'current_screen', 'forceProfilePage', 1 );
72
- $this->add_ajax_action( 'defVerifyOTP', 'verifyConfigOTP' );
73
- $this->add_ajax_action( 'defDisableOTP', 'disableOTP' );
74
- $this->add_ajax_action( 'defRetrieveOTP', 'retrieveOTP', false, true );
75
- if ( Utils::instance()->isActivatedSingle() ) {
76
- $this->add_filter( 'manage_users_columns', 'alterUsersTable' );
77
- $this->add_filter( 'manage_users_custom_column', 'alterUsersTableRow', 10, 3 );
78
- } else {
79
- $this->add_filter( 'wpmu_users_columns', 'alterUsersTable' );
80
- $this->add_filter( 'manage_users_custom_column', 'alterUsersTableRow', 10, 3 );
81
- }
82
- }
83
- }
84
-
85
- /**
86
- * If user have flag then force enable
87
- */
88
- public function forceProfilePage() {
89
- $user = wp_get_current_user();
90
- if ( ! is_object( $user ) ) {
91
- return;
92
- }
93
-
94
- $settings = Auth_Settings::instance();
95
- if ( $settings->forceAuth != true ) {
96
- return;
97
- }
98
-
99
- //not enable for this role oass
100
- if ( ! Auth_API::isEnableForCurrentRole( $user ) ) {
101
- return;
102
- }
103
-
104
- //check if this role is forced
105
- if ( ! Auth_API::isForcedRole( $user ) ) {
106
- return;
107
- }
108
-
109
- //user already enable OTP
110
- if ( Auth_API::isUserEnableOTP( $user->ID ) ) {
111
- return;
112
- }
113
-
114
- $screen = get_current_screen();
115
- if ( $screen->id != 'profile' ) {
116
- wp_safe_redirect( admin_url( 'profile.php' ) . '#show2AuthActivator' );
117
- exit;
118
- }
119
- }
120
-
121
- public function login_redirect( $url ) {
122
- $settings = Auth_Settings::instance();
123
- if ( $settings->forceAuth != true ) {
124
- return $url;
125
- }
126
-
127
- return $url;
128
- }
129
-
130
- /**
131
- * @param $userLogin
132
- * @param $user
133
- */
134
- public function markAsForceAuth( $userLogin, $user ) {
135
- $settings = Auth_Settings::instance();
136
- if ( $settings->forceAuth != true ) {
137
- return;
138
- }
139
- //not enable for this role oass
140
- if ( ! Auth_API::isEnableForCurrentRole( $user ) ) {
141
- return;
142
- }
143
- //user already enable OTP
144
- if ( Auth_API::isUserEnableOTP( $user->ID ) ) {
145
- return;
146
- }
147
- //if this is normal user, force them
148
- // if ( ! current_user_can( 'subscriber' ) ) {
149
- // return;
150
- // }
151
- $flag = get_user_meta( $user->ID, 'defenderForceAuth', true );
152
- if ( $flag === '' ) {
153
- update_user_meta( $user->ID, 'defenderForceAuth', 1 );
154
- }
155
- }
156
-
157
- /**
158
- * We have some feature conflict with jetpack, so listen to know when Defender can on
159
- *
160
- * @param $old_value
161
- * @param $value
162
- * @param $option
163
- */
164
- public function listenForJetpackOption( $old_value, $value, $option ) {
165
- $settings = Auth_Settings::instance();
166
- if ( array_search( 'sso', $value ) !== false ) {
167
- $settings->markAsConflict( 'jetpack/jetpack.php' );
168
- } else {
169
- $settings->markAsUnConflict( 'jetpack/jetpack.php' );
170
- }
171
- }
172
-
173
- /**
174
- * Stop ajax login on membership 2
175
- * @return bool
176
- */
177
- public function m2NoAjax() {
178
- return false;
179
- }
180
-
181
- /**
182
- * Return 2 factor auth status
183
- *
184
- * @param $val
185
- * @param $column_name
186
- * @param $user_id
187
- *
188
- * @return string
189
- */
190
- public function alterUsersTableRow( $val, $column_name, $user_id ) {
191
- if ( $column_name != 'defAuth' ) {
192
- return $val;
193
- }
194
-
195
- if ( Auth_API::isUserEnableOTP( $user_id ) ) {
196
- return '<span class="def-oval oval-green"></span>';
197
- }
198
-
199
- return '<span class="def-oval"></span>';
200
- }
201
-
202
- /**
203
- * Add the auth column inside users on single site
204
- *
205
- * @param $columns
206
- *
207
- * @return mixed
208
- *
209
- */
210
- public function alterUsersTable( $columns ) {
211
- $columns = array_slice( $columns, 0, count( $columns ) - 1 ) + array(
212
- 'defAuth' => __( "Two Factor", "defender-security" )
213
- ) + array_slice( $columns, count( $columns ) - 1 );
214
-
215
- return $columns;
216
- }
217
-
218
- /**
219
- * Generate an email for backup otp
220
- */
221
- public function retrieveOTP() {
222
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_get( 'nonce' ), 'defRetrieveOTP' ) ) {
223
- wp_send_json_error( array() );
224
- }
225
-
226
- $token = HTTP_Helper::retrieve_get( 'token' );
227
- $query = new \WP_User_Query( array(
228
- 'meta_key' => 'defOTPLoginToken',
229
- 'meta_value' => $token
230
- ) );
231
- $res = $query->get_results();
232
- if ( empty( $res ) ) {
233
- //no user
234
- wp_send_json_error( array(
235
- 'message' => __( "Your token is invalid", "defender-security" )
236
- ) );
237
- }
238
-
239
- $user = $res[0];
240
- //create a backup code for this user
241
- $code = Auth_API::createBackupCode( $user->ID );
242
- //send email
243
- $backupEmail = Auth_API::getBackupEmail( $user->ID );
244
-
245
- $settings = Auth_Settings::instance();
246
- $subject = ! empty( $settings->email_subject ) ? esc_attr( $settings->email_subject ) : __( 'Your OTP code', "defender-security" );
247
- $sender = ! empty( $settings->email_sender ) ? esc_attr( $settings->email_sender ) : false;
248
- $body = ! empty( $settings->email_body ) ? $settings->email_body : $settings->two_factor_opt_email_default_body();
249
- $body = $this->replace_email_vars( $body, array(
250
- 'display_name' => $user->display_name,
251
- 'passcode' => $code,
252
- ) );
253
- $headers = array( 'Content-Type: text/html; charset=UTF-8' );
254
- if ( $sender ) {
255
- $from_email = get_bloginfo( 'admin_email' );
256
- $headers[] = sprintf( 'From: %s <%s>', $sender, $from_email );
257
- }
258
-
259
- //send
260
- wp_mail( $backupEmail, $subject, $body, $headers );
261
-
262
- wp_send_json_success( array(
263
- 'message' => __( "Your code has been sent to your email.", "defender-security" )
264
- ) );
265
- }
266
-
267
- /**
268
- * disable OTP feature
269
- */
270
- public function disableOTP() {
271
- if ( ! is_user_logged_in() ) {
272
- return;
273
- }
274
-
275
- update_user_meta( get_current_user_id(), 'defenderAuthOn', 0 );
276
- wp_send_json_success();
277
- }
278
-
279
- /**
280
- * Saving backup email when profile saved
281
- *
282
- * @param $userID
283
- */
284
- public function saveBackupEmail( $userID ) {
285
- $email = HTTP_Helper::retrieve_post( 'def_backup_email' );
286
- if ( $email && get_current_user_id() == $userID ) {
287
- update_user_meta( $userID, 'defenderAuthEmail', $email );
288
- }
289
- }
290
-
291
- /**
292
- * An ajax function for verify the OTP user input when configuring the 2 factors
293
- */
294
- public function verifyConfigOTP() {
295
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( 'nonce' ), 'defVerifyOTP' ) ) {
296
- return;
297
- }
298
-
299
- if ( ! is_user_logged_in() ) {
300
- return;
301
- }
302
-
303
- $otp = HTTP_Helper::retrieve_post( 'otp' );
304
- $otp = trim( $otp );
305
- if ( strlen( $otp ) == 0 ) {
306
- wp_send_json_error( array(
307
- 'message' => __( "Please input a valid OTP code", "defender-security" )
308
- ) );
309
- }
310
-
311
- $secret = Auth_API::getUserSecret();
312
- //at this stage, secret should have value, do not need to check
313
- $res = Auth_API::compare( $secret, $otp );
314
- if ( $res ) {
315
- //save it
316
- update_user_meta( get_current_user_id(), 'defenderAuthOn', 1 );
317
- update_user_meta( get_current_user_id(), 'defenderForceAuth', 0 );
318
- wp_send_json_success();
319
- } else {
320
- //now need to check if the current user have backup otp
321
- wp_send_json_error( array(
322
- 'message' => __( "Your OTP code is incorrect. Please try again.", "defender-security" )
323
- ) );
324
- }
325
- }
326
-
327
- /**
328
- * Show an section inside my profile page for user can activate 2 factor login
329
- *
330
- * @param $profileuser
331
- */
332
- public function showUsers2FactorActivation( $profileuser ) {
333
- if ( ! Auth_API::isEnableForCurrentRole() ) {
334
- return;
335
- }
336
-
337
- $isOn = get_user_meta( $profileuser->ID, 'defenderAuthOn', true );
338
- wp_enqueue_style( 'defAuth', wp_defender()->getPluginUrl() . 'app/module/advanced-tools/css/login-admin.css' );
339
- $secretKey = Auth_API::createSecretForCurrentUser();
340
- if ( $isOn && $isOn == 1 ) {
341
- $email = Auth_API::getBackupEmail( $profileuser->ID );
342
- $this->renderPartial( 'login/enabled', array(
343
- 'email' => $email
344
- ) );
345
  } else {
346
- //show the screen
347
- $this->renderPartial( 'login/disabled', array(
348
- 'secretKey' => $secretKey
349
- ) );
350
  }
 
 
 
 
351
  }
352
-
353
- /**
354
- * We will check and show the OTP screen if user signon successfully
355
- *
356
- * @param $userLogin
357
- * @param $user
358
- */
359
- public function maybeShowOTPLogin( $userLogin, $user ) {
360
- if ( ! Auth_API::isUserEnableOTP( $user->ID ) ) {
361
- //no enable, then just return
362
- return;
363
- }
364
-
365
- //clean up session and auth cookies for preventing
366
- $token = $this->sessionToken;
367
- if ( $token ) {
368
- $sManager = \WP_Session_Tokens::get_instance( $user->ID );
369
- $sManager->destroy( $token );
370
- }
371
- wp_clear_auth_cookie();
372
-
373
- $this->showOTPScreen( $user );
374
- }
375
-
376
- /**
377
- * verify OTP code which user input in order to login
378
- */
379
- public function defenderVerifyOTP() {
380
- if ( ( $otp = HTTP_Helper::retrieve_post( 'otp', null ) ) != null ) {
381
- $params = array();
382
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'DefOtpCheck' ) ) {
383
- $params['error'] = new \WP_Error( 'security_fail', __( "Some error happen", "defender-security" ) );
384
- }
385
-
386
- $login_token = HTTP_Helper::retrieve_post( 'login_token' );
387
- $query = new \WP_User_Query( array(
388
- 'meta_key' => 'defOTPLoginToken',
389
- 'meta_value' => $login_token,
390
- 'blog_id' => 0
391
- ) );
392
- $res = $query->get_results();
393
- if ( empty( $res ) ) {
394
- //no users, redirect to the login page immediatly
395
- wp_redirect( site_url( 'wp-login.php', 'login_post' ) );
396
- exit;
397
- } else {
398
- $user = $res[0];
399
- $secret = Auth_API::getUserSecret( $user->ID );
400
- $redirect = HTTP_Helper::retrieve_post( 'redirect_to', admin_url() );
401
- if ( Auth_API::compare( $secret, $otp ) ) {
402
- //sign in
403
- delete_user_meta( $user->ID, 'defOTPLoginToken' );
404
- wp_set_current_user( $user->ID, $user->user_login );
405
- wp_set_auth_cookie( $user->ID, true );
406
- $redirect = apply_filters( 'login_redirect', $redirect, isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '', $user );
407
- wp_safe_redirect( $redirect );
408
- exit;
409
- } else {
410
- $backupCode = get_user_meta( $user->ID, 'defenderBackupCode', true );
411
- if ( $backupCode && $backupCode['code'] == $otp && strtotime( '+3 minutes', $backupCode['time'] ) > time() ) {
412
- delete_user_meta( $user->ID, 'defOTPLoginToken' );
413
- delete_user_meta( $user->ID, 'defenderBackupCode' );
414
- wp_set_current_user( $user->ID, $user->user_login );
415
- wp_set_auth_cookie( $user->ID, true );
416
- $redirect = apply_filters( 'login_redirect', $redirect, isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : '', $user );
417
- wp_safe_redirect( $redirect );
418
- exit;
419
- } else {
420
- $params['error'] = new \WP_Error( 'opt_fail', __( "Whoops, the passcode you entered was incorrect or expired.", "defender-security" ) );
421
- $this->showOTPScreen( $user, $params );
422
- }
423
- }
424
- }
425
- }
426
- }
427
-
428
- /**
429
- * Show the OTP screen
430
- *
431
- * @param $user
432
- * @param $params
433
- */
434
- private function showOTPScreen( $user, $params = array() ) {
435
- //now show the OTP screen
436
- $this->add_action( 'login_enqueue_scripts', 'includeAuthStyles' );
437
- wp_enqueue_script( 'jquery' );
438
- $params['loginToken'] = $this->createLoginToken( $user );
439
- $params['redirect_to'] = HTTP_Helper::retrieve_post( 'redirect_to' );
440
- if ( ! isset( $params['error'] ) ) {
441
- $params['error'] = null;
442
- }
443
- //if this goes here then the current user is ok, need to show the 2 auth
444
- $this->renderPartial( 'login/otp', $params );
445
- exit;
446
- }
447
-
448
- /**
449
- * We will empty all auth cookies or session, so should not rely on wp_get_session_token
450
- *
451
- * @param $cookie
452
- */
453
- public function storeSessionKey( $cookie ) {
454
- $cookie = wp_parse_auth_cookie( $cookie, 'logged_in' );
455
- $this->sessionToken = ! empty( $cookie['token'] ) ? $cookie['token'] : '';
456
- }
457
-
458
- /**
459
- * Create a unique token to retrieve user later
460
- *
461
- * @param $user
462
- *
463
- * @return string
464
- */
465
- private function createLoginToken( $user ) {
466
- $tmp = uniqid();
467
- // create and store a login token so we can query this user again
468
- update_user_meta( $user->ID, 'defOTPLoginToken', $tmp );
469
-
470
- return $tmp;
471
- }
472
-
473
- /**
474
- * add css for OTP page
475
- */
476
- public function includeAuthStyles() {
477
- //enqueue css here
478
- wp_enqueue_style( 'defAuth', wp_defender()->getPluginUrl() . 'app/module/advanced-tools/css/login.css' );
479
- }
480
-
481
  /**
482
  * Add submit admin page
483
  */
@@ -488,195 +54,63 @@ class Main extends Controller {
488
  'actionIndex'
489
  ) );
490
  }
491
-
492
  /**
493
  * a simple router
494
  */
495
  public function actionIndex() {
496
- $view = HTTP_Helper::retrieve_get( 'view' );
497
- switch ( $view ) {
498
- default:
499
- //todo move to another class
500
- $this->viewAuth();
501
- break;
502
- case 'mask-login':
503
- do_action( 'defenderATMaskLogin' );
504
- break;
505
- }
506
- }
507
-
508
- /**
509
- * View the 2 factor main admin page
510
- */
511
- public function viewAuth() {
512
- $settings = Auth_Settings::instance();
513
- if ( $settings->enabled == false ) {
514
- $this->render( 'disabled' );
515
- } else {
516
- wp_enqueue_media();
517
- $view = wp_defender()->isFree ? 'main-free' : 'main';
518
- $this->render( $view, array(
519
- 'settings' => $settings
520
- ) );
521
- }
522
  }
523
-
524
  /**
525
  * Enqueue scripts & styles
526
  */
527
  public function scripts() {
528
- if ( $this->isInPage() || $this->isDashboard() ) {
529
- wp_enqueue_script( 'wpmudev-sui' );
530
  wp_enqueue_style( 'wpmudev-sui' );
531
-
532
- wp_enqueue_script( 'defender' );
533
  wp_enqueue_style( 'defender' );
534
- wp_enqueue_script( 'adtools', wp_defender()->getPluginUrl() . 'app/module/advanced-tools/js/scripts.js' );
535
- $data = array(
536
- 'edit_email_title' => __( 'Edit Email', "defender-security" ),
537
- );
538
- wp_localize_script( 'adtools', 'defender_adtools', $data );
539
- }
540
- }
541
-
542
- /**
543
- * Saving settings in admin area
544
- */
545
- public function saveSettings() {
546
- if ( ! $this->checkPermission() ) {
547
- return;
548
- }
549
-
550
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'saveAdvancedSettings' ) ) {
551
- return;
552
- }
553
-
554
- $data = $_POST;
555
- if ( ! isset( $data['userRoles'] ) ) {
556
- $data['userRoles'] = array();
557
- }
558
- if ( ! isset( $data['forceAuthRoles'] ) ) {
559
- $data['forceAuthRoles'] = array();
560
- }
561
-
562
- $setting = Auth_Settings::instance();
563
- $setting->import( $data );
564
- $setting->save();
565
-
566
- $res = array(
567
- 'message' => __( "Your settings have been updated.", "defender-security" )
568
- );
569
- $res['reload'] = 1;
570
- Utils::instance()->submitStatsToDev();
571
- wp_send_json_success( $res );
572
- }
573
-
574
- /**
575
- * Saving email settings in admin area
576
- */
577
- public function saveTwoFactorOPTEmail() {
578
- if ( ! $this->checkPermission() ) {
579
- return;
580
- }
581
-
582
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'twoFactorOPTEmail' ) ) {
583
- return;
584
- }
585
-
586
- $data = $_POST;
587
- $subject = ! empty( $data['subject'] ) ? esc_attr( $data['subject'] ) : __( 'Your OTP code', "defender-security" );
588
- $sender = ! empty( $data['sender'] ) ? esc_attr( $data['sender'] ) : false;
589
- $body = ! empty( $data['body'] ) ? $data['body'] : false;
590
-
591
- if ( false === strpos( $body, '{{passcode}}' ) ) {
592
- wp_send_json_error( array(
593
- 'message' => sprintf( __( '%s variable was not found in mail body.', "defender-security" ), '{{passcode}}' ),
594
- ) );
595
  }
596
- $email_settings['email_subject'] = $subject;
597
- $email_settings['email_sender'] = $sender;
598
- $email_settings['email_body'] = wp_kses_post( $body );
599
-
600
- $setting = Auth_Settings::instance();
601
- $setting->import( $email_settings );
602
- $setting->save();
603
-
604
- $res = array(
605
- 'message' => __( 'Email settings has been saved.', "defender-security" )
606
- );
607
- $res['reload'] = 1;
608
- Utils::instance()->submitStatsToDev();
609
- wp_send_json_success( $res );
610
  }
611
-
612
  /**
613
- * Test OPT email.
614
  */
615
- public function testTwoFactorOPTEmail() {
616
  if ( ! $this->checkPermission() ) {
617
- return;
618
- }
619
-
620
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'twoFactorOPTEmail' ) ) {
621
- return;
622
- }
623
-
624
- $user = wp_get_current_user();
625
- //create a backup code for this user
626
- $code = Auth_API::createBackupCode( $user->ID );
627
- //send email
628
- $backup_email = Auth_API::getBackupEmail( $user->ID );
629
-
630
- $data = $_POST;
631
- $subject = ! empty( $data['subject'] ) ? esc_attr( $data['subject'] ) : __( 'Your OTP code', "defender-security" );
632
- $sender = ! empty( $data['sender'] ) ? esc_attr( $data['sender'] ) : false;
633
- $body = ! empty( $data['body'] ) ? $data['body'] : false;
634
-
635
- if ( false === strpos( $body, '{{passcode}}' ) ) {
636
- wp_send_json_error( array(
637
- 'message' => sprintf( __( '%s variable was not found in mail body.', "defender-security" ), '{{passcode}}' ),
638
- ) );
639
- }
640
- $body = $this->replace_email_vars( $body, array(
641
- 'display_name' => $user->display_name,
642
- 'passcode' => $code,
643
- ) );
644
- $headers = array( 'Content-Type: text/html; charset=UTF-8' );
645
- if ( $sender ) {
646
- $from_email = get_bloginfo( 'admin_email' );
647
- $headers[] = sprintf( 'From: %s <%s>', $sender, $from_email );
648
- }
649
-
650
- //send
651
- $send_mail = wp_mail( $backup_email, $subject, $body, $headers );
652
- if ( $send_mail ) {
653
- wp_send_json_success( array(
654
- 'message' => __( 'Test email has been sent to your email.', "defender-security" ),
655
- ) );
656
- } else {
657
- wp_send_json_error( array(
658
- 'message' => __( 'Test email failed.', "defender-security" ),
659
- ) );
660
- }
661
- }
662
-
663
- /**
664
- * Replace email variables.
665
- *
666
- * @param string $content Content to replace.
667
- * @param array $values Variables values.
668
- *
669
- * @return string
670
- */
671
- public function replace_email_vars( $content, $values ) {
672
- $content = apply_filters( 'the_content', $content );
673
- $tags = array( 'display_name', 'passcode' );
674
- foreach ( $tags as $key => $tag ) {
675
- $upper_tag = strtoupper( $tag );
676
- $content = str_replace( '{{' . $upper_tag . '}}', $values[ $tag ], $content );
677
- $content = str_replace( '{{' . $tag . '}}', $values[ $tag ], $content );
678
- }
679
-
680
- return $content;
681
  }
682
  }
10
  use WP_Defender\Behavior\Utils;
11
  use WP_Defender\Controller;
12
  use WP_Defender\Module\Advanced_Tools\Component\Auth_API;
13
+ use WP_Defender\Module\Advanced_Tools\Component\Auth_Listener;
14
+ use WP_Defender\Module\Advanced_Tools\Component\Mask_Api;
15
+ use WP_Defender\Module\Advanced_Tools\Component\Mask_Login_Listener;
16
  use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
17
+ use WP_Defender\Module\Advanced_Tools\Model\Mask_Settings;
18
 
19
  class Main extends Controller {
20
  protected $slug = 'wdf-advanced-tools';
21
+
 
 
22
  /**
23
  * @return array
24
  */
25
  public function behaviors() {
26
+ $behaviors = array(
27
+ 'utils' => '\WP_Defender\Behavior\Utils',
28
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
29
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
30
  );
31
+
32
+ return $behaviors;
33
  }
34
+
35
  public function __construct() {
36
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
37
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  } else {
39
+ $this->addAction( 'admin_menu', 'adminMenu' );
 
 
 
40
  }
41
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 12 );
42
+
43
+ new Auth_Listener();
44
+ new Mask_Login_Listener();
45
  }
46
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  /**
48
  * Add submit admin page
49
  */
54
  'actionIndex'
55
  ) );
56
  }
57
+
58
  /**
59
  * a simple router
60
  */
61
  public function actionIndex() {
62
+ $this->render( 'main' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
+
65
  /**
66
  * Enqueue scripts & styles
67
  */
68
  public function scripts() {
69
+ if ( $this->isInPage() ) {
 
70
  wp_enqueue_style( 'wpmudev-sui' );
71
+ wp_enqueue_media();
 
72
  wp_enqueue_style( 'defender' );
73
+ wp_register_script( 'defender-adtools', wp_defender()->getPluginUrl() . 'assets/app/advanced-tools.js', array(
74
+ 'vue',
75
+ 'defender',
76
+ 'wp-i18n'
77
+ ), wp_defender()->version, true );
78
+ wp_localize_script( 'defender-adtools', 'advanced_tools', $this->_scriptsData() );
79
+ Utils::instance()->createTranslationJson( 'defender-adtools' );
80
+ wp_set_script_translations( 'defender-adtools', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
81
+ wp_enqueue_script( 'defender-adtools' );
82
+ wp_enqueue_script( 'wpmudev-sui' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
+
86
  /**
87
+ * @return array
88
  */
89
+ public function _scriptsData() {
90
  if ( ! $this->checkPermission() ) {
91
+ return [];
92
+ }
93
+ $settings = Auth_Settings::instance();
94
+ $allRoles = get_editable_roles();
95
+ $ml_settings = Mask_Settings::instance();
96
+
97
+ return [
98
+ 'misc' => [
99
+ 'all_roles' => $allRoles,
100
+ 'compatibility' => isset( wp_defender()->global['compatibility'] ) && is_array( wp_defender()->global['compatibility'] ) ? wp_defender()->global['compatibility'] : false,
101
+ 'new_login_url' => Mask_Api::getNewLoginUrl(),
102
+ 'login_redirect_url' => Mask_Api::getRedirectUrl(),
103
+ 'home_url' => trailingslashit( network_home_url() )
104
+ ],
105
+ 'model' => [
106
+ 'two_factor' => $settings->export(),
107
+ 'mask_login' => $ml_settings->export( [ 'otp' ] )
108
+ ],
109
+ 'nonces' => [
110
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
111
+ 'sendTestEmail' => wp_create_nonce( 'sendTestEmail' )
112
+ ],
113
+ 'endpoints' => $this->getAllAvailableEndpoints( \WP_Defender\Module\Advanced_Tools::getClassName() ),
114
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  }
116
  }
app/module/advanced-tools/controller/rest.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Advanced_Tools\Controller;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Controller;
11
+ use WP_Defender\Module\Advanced_Tools;
12
+
13
+ class Rest extends Controller {
14
+ public function __construct() {
15
+ $namespace = 'wp-defender/v1';
16
+ $namespace .= '/advanced-tools';
17
+ $routes = [
18
+ $namespace . '/updateSettings' => 'updateSettings',
19
+ $namespace . '/sendTestEmail' => 'sendTestEmail',
20
+
21
+ ];
22
+ $this->registerEndpoints( $routes, Advanced_Tools::getClassName() );
23
+ }
24
+
25
+ /**
26
+ * Send test email
27
+ */
28
+ public function sendTestEmail() {
29
+ if ( ! $this->checkPermission() ) {
30
+ return;
31
+ }
32
+
33
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'sendTestEmail' ) ) {
34
+ return;
35
+ }
36
+
37
+ //get the backup email from current user
38
+ $backup_email = Advanced_Tools\Component\Auth_API::getBackupEmail( get_current_user_id() );
39
+ $subject = wp_kses_post( HTTP_Helper::retrievePost( 'email_subject' ) );
40
+ $sender = HTTP_Helper::retrievePost( 'email_sender' );
41
+ $body = wp_kses_post( HTTP_Helper::retrievePost( 'email_body' ) );
42
+ $params = [
43
+ 'pass_code' => '[a-sample-passcode]',
44
+ 'display_name' => Utils::instance()->getDisplayName()
45
+ ];
46
+ foreach ( $params as $key => $param ) {
47
+ $body = str_replace( "{{$key}}", $param, $body );
48
+ }
49
+ $headers = [ 'Content-Type: text/html; charset=UTF-8' ];
50
+ if ( $sender ) {
51
+ $from_email = get_bloginfo( 'admin_email' );
52
+ $headers[] = sprintf( 'From: %s <%s>', $sender, $from_email );
53
+ }
54
+
55
+ $send_mail = wp_mail( $backup_email, $subject, $body, $headers );
56
+ if ( $send_mail ) {
57
+ wp_send_json_success( array(
58
+ 'message' => __( 'Test email has been sent to your email.', "defender-security" ),
59
+ ) );
60
+ } else {
61
+ wp_send_json_error( array(
62
+ 'message' => __( 'Test email failed.', "defender-security" ),
63
+ ) );
64
+ }
65
+
66
+ }
67
+
68
+ /**
69
+ * An endpoint for update settings
70
+ */
71
+ public function updateSettings() {
72
+ if ( ! $this->checkPermission() ) {
73
+ return;
74
+ }
75
+
76
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'updateSettings' ) ) {
77
+ return;
78
+ }
79
+
80
+ $data = stripslashes( $_POST['data'] );
81
+ $data = json_decode( $data, true );
82
+ $module = $data['module'];
83
+ $settings = $data['settings'];
84
+ if ( $module == 'auth' ) {
85
+ $model = Advanced_Tools\Model\Auth_Settings::instance();
86
+ } else {
87
+ $model = Advanced_Tools\Model\Mask_Settings::instance();
88
+ }
89
+ $model->import( $settings );
90
+ if ( $model->validate() ) {
91
+ $model->save();
92
+ $res = array(
93
+ 'message' => __( "Your settings have been updated.", "defender-security" )
94
+ );
95
+ $this->submitStatsToDev();
96
+ wp_send_json_success( $res );
97
+ } else {
98
+ $res = array(
99
+ 'message' => implode( '<br/>', $model->getErrors() )
100
+ );
101
+ wp_send_json_error( $res );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Import Utils into the class
107
+ * @return array
108
+ */
109
+ public function behaviors() {
110
+ $behaviors = array(
111
+ 'utils' => '\WP_Defender\Behavior\Utils',
112
+ );
113
+
114
+ return $behaviors;
115
+ }
116
+ }
app/module/advanced-tools/model/auth-settings.php CHANGED
@@ -11,30 +11,52 @@ use Hammer\Helper\WP_Helper;
11
  class Auth_Settings extends \Hammer\WP\Settings {
12
  private static $_instance;
13
  public $enabled = false;
14
- public $lostPhone = true;
15
- public $forceAuth = false;
16
- public $forceAuthMess = "You are required to setup two-factor authentication to use this site.";
17
- public $userRoles = array();
18
- public $forceAuthRoles = array();
19
- public $customGraphic = 0;
20
- public $customGraphicURL = '';
21
- public $isConflict = array();
22
  public $email_subject = '';
23
  public $email_sender = '';
24
  public $email_body = '';
25
-
26
  public function __construct( $id, $is_multi ) {
27
  //fetch the userRoles
28
  if ( ! function_exists( 'get_editable_roles' ) ) {
29
  include_once ABSPATH . 'wp-admin/includes/user.php';
30
  }
31
- $this->userRoles = array_keys( get_editable_roles() );
32
  //remove subscriber from the list
33
- unset( $this->userRoles[ array_search( 'subscriber', $this->userRoles ) ] );
34
- $this->customGraphicURL = wp_defender()->getPluginUrl() . 'assets/img/2factor-disabled.svg';
 
 
 
 
 
 
 
 
 
 
35
  parent::__construct( $id, $is_multi );
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
-
38
  /**
39
  * @return Auth_Settings
40
  */
@@ -43,51 +65,51 @@ class Auth_Settings extends \Hammer\WP\Settings {
43
  $class = new Auth_Settings( 'wd_2auth_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
44
  self::$_instance = $class;
45
  }
46
-
47
  return self::$_instance;
48
  }
49
-
50
  /**
51
  * @param $plugin
52
  *
53
  * @return bool|int
54
  */
55
  public function isConflict( $plugin ) {
56
- if ( in_array( $plugin, $this->isConflict ) ) {
57
  return true;
58
- } elseif ( in_array( '!' . $plugin, $this->isConflict ) ) {
59
  return false;
60
  }
61
-
62
  return 0;
63
  }
64
-
65
  /**
66
  * @param $plugin
67
  */
68
  public function markAsConflict( $plugin ) {
69
- if ( ! in_array( $plugin, $this->isConflict ) ) {
70
- $this->isConflict [] = $plugin;
71
  $this->save();
72
  }
73
  }
74
-
75
  /**
76
  * @param $plugin
77
  */
78
  public function markAsUnConflict( $plugin ) {
79
- if ( ( $i = array_search( $plugin, $this->isConflict ) ) !== false ) {
80
- unset( $this->isConflict[ $i ] );
81
  }
82
- if ( ! in_array( '!' . $plugin, $this->isConflict ) ) {
83
- $this->isConflict [] = '!' . $plugin;
84
  }
85
  $this->save();
86
  }
87
-
88
  public function events() {
89
  $that = $this;
90
-
91
  return array(
92
  self::EVENT_AFTER_DELETED => array(
93
  array(
@@ -100,7 +122,7 @@ class Auth_Settings extends \Hammer\WP\Settings {
100
  )
101
  );
102
  }
103
-
104
  /**
105
  * Email default body.
106
  */
@@ -113,7 +135,37 @@ Copy and paste the passcode into the input field on the login screen to complete
113
 
114
  Regards,
115
  Administrator';
116
-
117
  return $content;
118
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  }
11
  class Auth_Settings extends \Hammer\WP\Settings {
12
  private static $_instance;
13
  public $enabled = false;
14
+ public $lost_phone = true;
15
+ public $force_auth = false;
16
+ public $force_auth_mess = "You are required to setup two-factor authentication to use this site.";
17
+ public $user_roles = array();
18
+ public $force_auth_roles = array();
19
+ public $custom_graphic = false;
20
+ public $custom_graphic_url = '';
21
+ public $is_conflict = array();
22
  public $email_subject = '';
23
  public $email_sender = '';
24
  public $email_body = '';
25
+
26
  public function __construct( $id, $is_multi ) {
27
  //fetch the userRoles
28
  if ( ! function_exists( 'get_editable_roles' ) ) {
29
  include_once ABSPATH . 'wp-admin/includes/user.php';
30
  }
31
+ $this->user_roles = array_keys( get_editable_roles() );
32
  //remove subscriber from the list
33
+ unset( $this->user_roles[ array_search( 'subscriber', $this->user_roles ) ] );
34
+ $this->custom_graphic_url = wp_defender()->getPluginUrl() . 'assets/img/2factor-disabled.svg';
35
+ $this->email_subject = 'Your OTP code';
36
+ $this->email_sender = 'admin';
37
+ $this->email_body = 'Hi {{display_name}},
38
+
39
+ Your temporary login passcode is <strong>{{passcode}}</strong>.
40
+
41
+ Copy and paste the passcode into the input field on the login screen to complete logging in.
42
+
43
+ Regards,
44
+ Administrator';
45
  parent::__construct( $id, $is_multi );
46
+ //have to force it here if it has not convert the new config
47
+ $this->enabled = ! ! $this->enabled;
48
+ $this->force_auth = ! ! $this->force_auth;
49
+ $this->custom_graphic = ! ! $this->custom_graphic;
50
+ if ( ! is_array( $this->user_roles ) ) {
51
+ $this->user_roles = [];
52
+ }
53
+ $this->user_roles = array_values( $this->user_roles );
54
+ if ( ! is_array( $this->force_auth_roles ) ) {
55
+ $this->force_auth_roles = [];
56
+ }
57
+ $this->force_auth_roles = array_values( $this->force_auth_roles );
58
  }
59
+
60
  /**
61
  * @return Auth_Settings
62
  */
65
  $class = new Auth_Settings( 'wd_2auth_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
66
  self::$_instance = $class;
67
  }
68
+
69
  return self::$_instance;
70
  }
71
+
72
  /**
73
  * @param $plugin
74
  *
75
  * @return bool|int
76
  */
77
  public function isConflict( $plugin ) {
78
+ if ( in_array( $plugin, $this->is_conflict ) ) {
79
  return true;
80
+ } elseif ( in_array( '!' . $plugin, $this->is_conflict ) ) {
81
  return false;
82
  }
83
+
84
  return 0;
85
  }
86
+
87
  /**
88
  * @param $plugin
89
  */
90
  public function markAsConflict( $plugin ) {
91
+ if ( ! in_array( $plugin, $this->is_conflict ) ) {
92
+ $this->is_conflict [] = $plugin;
93
  $this->save();
94
  }
95
  }
96
+
97
  /**
98
  * @param $plugin
99
  */
100
  public function markAsUnConflict( $plugin ) {
101
+ if ( ( $i = array_search( $plugin, $this->is_conflict ) ) !== false ) {
102
+ unset( $this->is_conflict[ $i ] );
103
  }
104
+ if ( ! in_array( '!' . $plugin, $this->is_conflict ) ) {
105
+ $this->is_conflict [] = '!' . $plugin;
106
  }
107
  $this->save();
108
  }
109
+
110
  public function events() {
111
  $that = $this;
112
+
113
  return array(
114
  self::EVENT_AFTER_DELETED => array(
115
  array(
122
  )
123
  );
124
  }
125
+
126
  /**
127
  * Email default body.
128
  */
135
 
136
  Regards,
137
  Administrator';
138
+
139
  return $content;
140
  }
141
+
142
+ /**
143
+ * Define labels for settings key, we will use it for HUB
144
+ *
145
+ * @param null $key
146
+ *
147
+ * @return array|mixed
148
+ */
149
+ public function labels( $key = null ) {
150
+ $labels = [
151
+ 'enabled' => __( 'Two Factor Authentication', "defender-security" ),
152
+ 'user_roles' => __( "User Roles", "defender-security" ),
153
+ 'lost_phone' => __( 'Lost Phone', "defender-security" ),
154
+ 'force_auth' => __( "Force Authentication", "defender-security" ),
155
+ 'force_auth_mess' => __( "Custom warning message", "defender-security" ),
156
+ 'force_auth_roles' => __( "Force Authentication", "defender-security" ),
157
+ 'custom_graphic' => __( "Custom Graphic", "defender-security" ),
158
+ 'custom_graphic_url' => __( "Custom Graphic Image", "defender-security" ),
159
+ 'email_subject' => __( "Subject", "defender-security" ),
160
+ 'email_sender' => __( "Sender", "defender-security" ),
161
+ 'email_body' => __( "Body", "defender-security" )
162
+
163
+ ];
164
+
165
+ if ( $key != null ) {
166
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
167
+ }
168
+
169
+ return $labels;
170
+ }
171
  }
app/module/advanced-tools/model/mask-settings.php CHANGED
@@ -8,16 +8,19 @@ namespace WP_Defender\Module\Advanced_Tools\Model;
8
  use Hammer\Helper\WP_Helper;
9
 
10
  class Mask_Settings extends \Hammer\WP\Settings {
11
- public $maskUrl = '';
12
- public $redirectTraffic = false;
13
- public $redirectTrafficUrl = '';
14
  public $enabled = false;
 
15
  private static $_instance;
16
-
17
  public function __construct( $id, $is_multi ) {
18
  parent::__construct( $id, $is_multi );
 
 
19
  }
20
-
21
  /**
22
  * @return Mask_Settings
23
  */
@@ -26,18 +29,57 @@ class Mask_Settings extends \Hammer\WP\Settings {
26
  $class = new Mask_Settings( 'wd_masking_login_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
27
  self::$_instance = $class;
28
  }
29
-
30
  return self::$_instance;
31
  }
32
-
33
  /**
34
  * @return bool
35
  */
36
  public function isEnabled() {
37
- return $this->enabled && ( strlen( trim( $this->maskUrl ) ) > 0 );
38
  }
39
-
40
  public function isRedirect() {
41
- return $this->redirectTraffic && ( strlen( trim( $this->redirectTrafficUrl ) ) > 0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
  }
8
  use Hammer\Helper\WP_Helper;
9
 
10
  class Mask_Settings extends \Hammer\WP\Settings {
11
+ public $mask_url = '';
12
+ public $redirect_traffic = false;
13
+ public $redirect_traffic_url = '';
14
  public $enabled = false;
15
+ public $otps = [];
16
  private static $_instance;
17
+
18
  public function __construct( $id, $is_multi ) {
19
  parent::__construct( $id, $is_multi );
20
+ $this->enabled = ! ! $this->enabled;
21
+ $this->redirect_traffic = ! ! $this->redirect_traffic;
22
  }
23
+
24
  /**
25
  * @return Mask_Settings
26
  */
29
  $class = new Mask_Settings( 'wd_masking_login_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
30
  self::$_instance = $class;
31
  }
32
+
33
  return self::$_instance;
34
  }
35
+
36
  /**
37
  * @return bool
38
  */
39
  public function isEnabled() {
40
+ return $this->enabled && ( strlen( trim( $this->mask_url ) ) > 0 );
41
  }
42
+
43
  public function isRedirect() {
44
+ return $this->redirect_traffic && ( strlen( trim( $this->redirect_traffic_url ) ) > 0 );
45
+ }
46
+
47
+ /**
48
+ * Return the attributes we will run an xss filters
49
+ * @return array
50
+ */
51
+ public function filters() {
52
+ return [
53
+ 'mask_url',
54
+ 'redirect_traffic_url'
55
+ ];
56
+ }
57
+
58
+ /**
59
+ * Define labels for settings key, we will use it for HUB
60
+ *
61
+ * @param null $key
62
+ *
63
+ * @return array|mixedß
64
+ */
65
+ public function labels( $key = null ) {
66
+ $labels = [
67
+ 'enabled' => __( 'Mask Login Area', "defender-security" ),
68
+ 'mask_url' => __( "Masking URL", "defender-security" ),
69
+ 'redirect_traffic' => __( 'Redirect traffic', "defender-security" ),
70
+ 'redirect_traffic_url' => __( "Redirection URL", "defender-security" ),
71
+ ];
72
+
73
+ if ( $key != null ) {
74
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
75
+ }
76
+
77
+ return $labels;
78
+ }
79
+
80
+ public function beforeValidate() {
81
+ if ( $this->mask_url === $this->redirect_traffic_url && strlen( $this->redirect_traffic_url ) > 0 ) {
82
+ $this->addError( 'redirect_traffic_url', __( "Redirect URL must different from Login URL", "defender-security" ) );
83
+ }
84
  }
85
  }
app/module/advanced-tools/view/login/disabled.php CHANGED
@@ -6,12 +6,12 @@
6
  <td aria-live="assertive">
7
  <?php
8
  $settings = \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance();
9
- if ( $settings->forceAuth ):
10
  ?>
11
  <div class="def-warning">
12
  <i class="dashicons dashicons-warning" aria-hidden="true"></i>
13
  <?php
14
- echo $settings->forceAuthMess
15
  ?>
16
  </div>
17
  <?php endif; ?>
@@ -51,12 +51,12 @@
51
  </a>
52
  <div class="line"></div>
53
  <p><strong><?php _e( "2. Scan the barcode", "defender-security" ) ?></strong></p>
54
- <p><?php _e( "Open the Google Authenticator app you just downloaded, tap the “+” symbol and then use your phones camera to scan the barcode below.", "defender-security" ) ?></p>
55
  <?php echo \WP_Defender\Module\Advanced_Tools\Component\Auth_API::generateQRCode( urlencode( get_bloginfo( 'name' ) ), $email, $secretKey, 149, 149, urlencode( get_bloginfo( 'name' ) ) ) ?>
56
  <div class="line"></div>
57
  <p><strong><?php _e( "3. Enter passcode", "defender-security" ) ?></strong></p>
58
  <p>
59
- <?php _e( "Enter the 6 digit passcode that is shown on your device into the input field below and hit Verify”.", "defender-security" ) ?>
60
  </p>
61
  <div class="well">
62
  <p class="error"></p>
@@ -141,7 +141,7 @@
141
  });
142
  })
143
  </script>
144
- <?php if ( $settings->forceAuth ): ?>
145
  <script type="text/javascript">
146
  jQuery(function ($) {
147
  $('html, body').animate({scrollTop: $("#show2AuthActivator").offset().top}, 1000);
6
  <td aria-live="assertive">
7
  <?php
8
  $settings = \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance();
9
+ if ( $settings->force_auth ):
10
  ?>
11
  <div class="def-warning">
12
  <i class="dashicons dashicons-warning" aria-hidden="true"></i>
13
  <?php
14
+ echo $settings->force_auth_mess
15
  ?>
16
  </div>
17
  <?php endif; ?>
51
  </a>
52
  <div class="line"></div>
53
  <p><strong><?php _e( "2. Scan the barcode", "defender-security" ) ?></strong></p>
54
+ <p><?php _e( "Open the Google Authenticator app you just downloaded, tap the \"+\" symbol and then use your phone's camera to scan the barcode below.", "defender-security" ) ?></p>
55
  <?php echo \WP_Defender\Module\Advanced_Tools\Component\Auth_API::generateQRCode( urlencode( get_bloginfo( 'name' ) ), $email, $secretKey, 149, 149, urlencode( get_bloginfo( 'name' ) ) ) ?>
56
  <div class="line"></div>
57
  <p><strong><?php _e( "3. Enter passcode", "defender-security" ) ?></strong></p>
58
  <p>
59
+ <?php _e( "Enter the 6 digit passcode that is shown on your device into the input field below and hit \"Verify\".", "defender-security" ) ?>
60
  </p>
61
  <div class="well">
62
  <p class="error"></p>
141
  });
142
  })
143
  </script>
144
+ <?php if ( $settings->force_auth ): ?>
145
  <script type="text/javascript">
146
  jQuery(function ($) {
147
  $('html, body').animate({scrollTop: $("#show2AuthActivator").offset().top}, 1000);
app/module/advanced-tools/view/login/otp.php CHANGED
@@ -223,7 +223,7 @@ do_action( 'login_header' );
223
  <form method="post"
224
  action="<?php echo esc_url( add_query_arg( 'action', 'defenderVerifyOTP', site_url( 'wp-login.php', 'login_post' ) ) ); ?>">
225
  <p><?php _e( "Open the Google Authenticator app and enter the 6 digit passcode.", "defender-security" ) ?></p>
226
- <input type="text" value="" autocomplete="off" name="otp">
227
  <button class="button button-primary float-r"
228
  type="submit"><?php _e( "Authenticate", "defender-security" ) ?></button>
229
  <input type="hidden" name="login_token" value="<?php echo $loginToken ?>"/>
@@ -233,17 +233,17 @@ do_action( 'login_header' );
233
  <?php
234
  $settings = \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance();
235
 
236
- if ( wp_defender()->isFree == false && $settings->customGraphic ) {
237
  ?>
238
  <style type="text/css">
239
  body.login div#login h1 a {
240
- background-image: url("<?php echo $settings->customGraphicURL ?>");
241
  }
242
  </style>
243
  <?php
244
  }
245
  ?>
246
- <?php if ( \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance()->lostPhone ): ?>
247
  <p id="nav">
248
  <a id="lostPhone"
249
  href="<?php echo admin_url( 'admin-ajax.php?action=defRetrieveOTP&token=' . $loginToken . '&nonce=' . wp_create_nonce( 'defRetrieveOTP' ) ) ?>">
223
  <form method="post"
224
  action="<?php echo esc_url( add_query_arg( 'action', 'defenderVerifyOTP', site_url( 'wp-login.php', 'login_post' ) ) ); ?>">
225
  <p><?php _e( "Open the Google Authenticator app and enter the 6 digit passcode.", "defender-security" ) ?></p>
226
+ <input type="text" autofocus value="" autocomplete="off" name="otp">
227
  <button class="button button-primary float-r"
228
  type="submit"><?php _e( "Authenticate", "defender-security" ) ?></button>
229
  <input type="hidden" name="login_token" value="<?php echo $loginToken ?>"/>
233
  <?php
234
  $settings = \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance();
235
 
236
+ if ( wp_defender()->isFree == false && $settings->custom_graphic ) {
237
  ?>
238
  <style type="text/css">
239
  body.login div#login h1 a {
240
+ background-image: url("<?php echo $settings->custom_graphic_url ?>");
241
  }
242
  </style>
243
  <?php
244
  }
245
  ?>
246
+ <?php if ( \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance()->lost_phone ): ?>
247
  <p id="nav">
248
  <a id="lostPhone"
249
  href="<?php echo admin_url( 'admin-ajax.php?action=defRetrieveOTP&token=' . $loginToken . '&nonce=' . wp_create_nonce( 'defRetrieveOTP' ) ) ?>">
app/module/advanced-tools/view/main-free.php CHANGED
@@ -11,7 +11,7 @@
11
  <?php _e( "Configure your two-factor authentication settings. Our recommendations are enabled by default.", "defender-security" ) ?>
12
  </p>
13
  <?php
14
- $enabledRoles = $settings->userRoles;
15
  $allRoles = get_editable_roles();
16
  ?>
17
  <?php if ( isset( wp_defender()->global['compatibility'] ) ): ?>
@@ -93,7 +93,7 @@
93
  <label class="sui-toggle">
94
  <input role="presentation" type="checkbox" name="lostPhone" class="toggle-checkbox"
95
  id="lostPhone" value="1"
96
- <?php checked( true, $settings->lostPhone ) ?>/>
97
  <span class="sui-toggle-slider"></span>
98
  </label>
99
  <label for="lostPhone" class="sui-toggle-label">
@@ -117,7 +117,7 @@
117
  <label class="sui-toggle">
118
  <input role="presentation" type="checkbox" name="forceAuth" class="toggle-checkbox"
119
  id="forceAuth" value="1"
120
- <?php checked( true, $settings->forceAuth ) ?>/>
121
  <span class="sui-toggle-slider"></span>
122
  </label>
123
  <label for="forceAuth" class="sui-toggle-label">
@@ -127,11 +127,11 @@
127
  <?php _e( "Note: Users will be forced to set up two-factor when they next login.", "defender-security" ) ?>
128
  </span>
129
  <div id="forceAuthRoles" class="sui-border-frame sui-toggle-content"
130
- aria-hidden="<?php echo ! $settings->forceAuth ?>">
131
  <strong><?php _e( "User Roles", "defender-security" ) ?></strong>
132
  <ul>
133
  <?php
134
- $forceAuthRoles = $settings->forceAuthRoles;
135
  foreach ( $allRoles as $role => $detail ):
136
  ?>
137
  <li>
@@ -147,7 +147,7 @@
147
  </ul>
148
  <strong><?php _e( "Custom warning message", "defender-security" ) ?></strong>
149
  <textarea class="sui-form-control"
150
- name="forceAuthMess"><?php echo $settings->forceAuthMess ?></textarea>
151
  <span class="sui-description">
152
  <?php _e( "Note: This is shown in the users Profile area indicating they must use two-factor authentication.", "defender-security" ) ?>
153
  </span>
11
  <?php _e( "Configure your two-factor authentication settings. Our recommendations are enabled by default.", "defender-security" ) ?>
12
  </p>
13
  <?php
14
+ $enabledRoles = $settings->user_roles;
15
  $allRoles = get_editable_roles();
16
  ?>
17
  <?php if ( isset( wp_defender()->global['compatibility'] ) ): ?>
93
  <label class="sui-toggle">
94
  <input role="presentation" type="checkbox" name="lostPhone" class="toggle-checkbox"
95
  id="lostPhone" value="1"
96
+ <?php checked( true, $settings->lost_phone ) ?>/>
97
  <span class="sui-toggle-slider"></span>
98
  </label>
99
  <label for="lostPhone" class="sui-toggle-label">
117
  <label class="sui-toggle">
118
  <input role="presentation" type="checkbox" name="forceAuth" class="toggle-checkbox"
119
  id="forceAuth" value="1"
120
+ <?php checked( true, $settings->force_auth ) ?>/>
121
  <span class="sui-toggle-slider"></span>
122
  </label>
123
  <label for="forceAuth" class="sui-toggle-label">
127
  <?php _e( "Note: Users will be forced to set up two-factor when they next login.", "defender-security" ) ?>
128
  </span>
129
  <div id="forceAuthRoles" class="sui-border-frame sui-toggle-content"
130
+ aria-hidden="<?php echo ! $settings->force_auth ?>">
131
  <strong><?php _e( "User Roles", "defender-security" ) ?></strong>
132
  <ul>
133
  <?php
134
+ $forceAuthRoles = $settings->force_auth_roles;
135
  foreach ( $allRoles as $role => $detail ):
136
  ?>
137
  <li>
147
  </ul>
148
  <strong><?php _e( "Custom warning message", "defender-security" ) ?></strong>
149
  <textarea class="sui-form-control"
150
+ name="forceAuthMess"><?php echo $settings->force_auth_mess ?></textarea>
151
  <span class="sui-description">
152
  <?php _e( "Note: This is shown in the users Profile area indicating they must use two-factor authentication.", "defender-security" ) ?>
153
  </span>
app/module/advanced-tools/view/main.php CHANGED
@@ -1,297 +1 @@
1
- <div class="sui-box">
2
- <div class="sui-box-header">
3
- <h3 class="sui-box-title">
4
- <?php _e( "Two-Factor Authentication", "defender-security" ) ?>
5
- </h3>
6
- </div>
7
- <form method="post" id="advanced-settings-frm" class="advanced-settings-frm">
8
- <div class="sui-box-body">
9
- <p>
10
- <?php _e( "Configure your two-factor authentication settings. Our recommendations are enabled by default.", "defender-security" ) ?>
11
- </p>
12
- <?php
13
- $enabledRoles = $settings->userRoles;
14
- $allRoles = get_editable_roles();
15
- ?>
16
- <?php if ( isset( wp_defender()->global['compatibility'] ) ): ?>
17
- <div class="sui-notice sui-notice-error">
18
- <p>
19
- <?php echo implode( '<br/>', array_unique( wp_defender()->global['compatibility'] ) ); ?>
20
- </p>
21
- </div>
22
- <?php endif; ?>
23
- <?php
24
- if ( count( $enabledRoles ) ):
25
- ?>
26
- <div class="sui-notice sui-notice-info">
27
- <p>
28
- <?php
29
- printf( __( "<strong>Two-factor authentication is now active.</strong> User roles with this feature enabled must visit their <a href='%s'>Profile page</a> to complete setup and sync their account with the Authenticator app.", "defender-security" ),
30
- admin_url( 'profile.php' ) );
31
- ?>
32
- </p>
33
- </div>
34
- <?php else: ?>
35
- <div class="sui-notice sui-notice-warning">
36
- <p>
37
- <?php
38
- _e( "<strong>Two-factor authentication is currently inactive.</strong> Configure and save your settings to complete setup.", "defender-security" )
39
- ?>
40
- </p>
41
- </div>
42
- <?php endif; ?>
43
- <div class="sui-box-settings-row">
44
- <div class="sui-box-settings-col-1">
45
- <span class="sui-settings-label">
46
- <?php esc_html_e( "User Roles", "defender-security" ) ?>
47
- </span>
48
- <span class="sui-description">
49
- <?php esc_html_e( "Choose the user roles you want to enable two-factor authentication for. Users with those roles will then be required to use the Google Authenticator app to login.", "defender-security" ) ?>
50
- </span>
51
- </div>
52
-
53
- <div class="sui-box-settings-col-2">
54
- <div class="sui-field-list">
55
- <div class="sui-field-list-header">
56
- <h3 class="sui-field-list-title"><?php _e( "User role", "defender-security" ) ?></h3>
57
- </div>
58
- <div class="sui-field-list-body">
59
- <?php
60
- foreach ( $allRoles as $role => $detail ):
61
- ?>
62
- <div class="sui-field-list-item">
63
- <label class="sui-field-list-item-label"
64
- for="toggle_<?php echo esc_attr( $role ) ?>_role">
65
- <?php echo $detail['name'] ?>
66
- </label>
67
- <label class="sui-toggle">
68
- <input type="checkbox" <?php echo in_array( $role, $enabledRoles ) ? 'checked="checked"' : null ?>
69
- name="userRoles[]" value="<?php echo esc_attr( $role ) ?>"
70
- id="toggle_<?php echo esc_attr( $role ) ?>_role"/>
71
- <span class="sui-toggle-slider"></span>
72
- </label>
73
- </div>
74
- <?php endforeach; ?>
75
- </div>
76
- </div>
77
- </div>
78
- </div>
79
- <div class="sui-box-settings-row">
80
- <div class="sui-box-settings-col-1">
81
- <span class="sui-settings-label">
82
- <?php _e( "Lost Phone", "defender-security" ) ?>
83
- </span>
84
- <span class="sui-description">
85
- <?php _e( "If a user is unable to access their phone, you can allow an option to send the one time password to their registered email.", "defender-security" ) ?>
86
- </span>
87
- </div>
88
- <div class="sui-box-settings-col-2">
89
- <div class="sui-form-field">
90
- <input type="hidden" name="lostPhone" value="0"/>
91
- <label class="sui-toggle">
92
- <input role="presentation" type="checkbox" name="lostPhone" class="toggle-checkbox"
93
- id="lostPhone" value="1"
94
- <?php checked( true, $settings->lostPhone ) ?>/>
95
- <span class="sui-toggle-slider"></span>
96
- </label>
97
- <label for="lostPhone" class="sui-toggle-label">
98
- <?php _e( "Enable lost phone option", "defender-security" ) ?>
99
- </label>
100
- </div>
101
- </div>
102
- </div>
103
- <div class="sui-box-settings-row">
104
- <div class="sui-box-settings-col-1">
105
- <span class="sui-settings-label">
106
- <?php _e( "Force Authentication", "defender-security" ) ?>
107
- </span>
108
- <span class="sui-description">
109
- <?php _e( "By default, two-factor authentication is optional for users. This setting forces users to activate two-factor.", "defender-security" ) ?>
110
- </span>
111
- </div>
112
- <div class="sui-box-settings-col-2">
113
- <div class="sui-form-field">
114
- <input type="hidden" name="forceAuth" value="0"/>
115
- <label class="sui-toggle">
116
- <input role="presentation" type="checkbox" name="forceAuth" class="toggle-checkbox"
117
- id="forceAuth" value="1"
118
- <?php checked( true, $settings->forceAuth ) ?>/>
119
- <span class="sui-toggle-slider"></span>
120
- </label>
121
- <label for="forceAuth" class="sui-toggle-label">
122
- <?php _e( "Force users to log in with two-factor authentication", "defender-security" ) ?>
123
- </label>
124
- <span class="sui-description sui-toggle-content">
125
- <?php _e( "Note: Users will be forced to set up two-factor when they next login.", "defender-security" ) ?>
126
- </span>
127
- <div id="forceAuthRoles" class="sui-border-frame sui-toggle-content"
128
- aria-hidden="<?php echo ! $settings->forceAuth ?>">
129
- <strong><?php _e( "User Roles", "defender-security" ) ?></strong>
130
- <ul>
131
- <?php
132
- $forceAuthRoles = $settings->forceAuthRoles;
133
- foreach ( $allRoles as $role => $detail ):
134
- ?>
135
- <li>
136
- <label for="forceAuth<?php echo esc_attr( $role ) ?>" class="sui-checkbox">
137
- <input id="forceAuth<?php echo esc_attr( $role ) ?>" type="checkbox"
138
- name="forceAuthRoles[]"
139
- value="<?php echo esc_attr( $role ) ?>" <?php echo in_array( $role, $forceAuthRoles ) ? 'checked="checked"' : null ?> />
140
- <span aria-hidden="true"></span>
141
- <span><?php echo $detail['name'] ?></span>
142
- </label>
143
- </li>
144
- <?php endforeach; ?>
145
- </ul>
146
- <strong><?php _e( "Custom warning message", "defender-security" ) ?></strong>
147
- <textarea class="sui-form-control"
148
- name="forceAuthMess"><?php echo $settings->forceAuthMess ?></textarea>
149
- <span class="sui-description">
150
- <?php _e( "Note: This is shown in the users Profile area indicating they must use two-factor authentication.", "defender-security" ) ?>
151
- </span>
152
- </div>
153
- </div>
154
- </div>
155
- </div>
156
- <div class="sui-box-settings-row">
157
- <div class="sui-box-settings-col-1">
158
- <span class="sui-settings-label">
159
- <?php _e( "Custom Graphic", "defender-security" ) ?>
160
- </span>
161
- <span class="sui-description">
162
- <?php _e( "By default, Defender’s icon appears above the login fields. You can upload your own branding, or turn this feature off.", "defender-security" ) ?>
163
- </span>
164
- </div>
165
- <div class="sui-box-settings-col-2">
166
- <input type="hidden" name="customGraphic" value="0"/>
167
- <label class="sui-toggle">
168
- <input role="presentation" type="checkbox" name="customGraphic" class="toggle-checkbox"
169
- id="customGraphic" value="1"
170
- <?php checked( true, $settings->customGraphic ) ?>/>
171
- <span class="sui-toggle-slider"></span>
172
- </label>
173
- <label for="customGraphic" class="sui-toggle-label">
174
- <?php _e( "Enable custom graphics above login fields", "defender-security" ) ?>
175
- </label>
176
- <div id="customGraphicContainer" class="sui-border-frame sui-toggle-content"
177
- aria-hidden="<?php echo $settings->customGraphic == false ? true : false ?>">
178
- <span class="sui-description">
179
- <strong><?php _e( "Custom Graphic", "defender-security" ) ?></strong>
180
- - <?php _e( "For best results use a 168x168px JPG or PNG.", "defender-security" ) ?></span>
181
- </span>
182
- <input type="hidden" id="customGraphicURL" name="customGraphicURL"
183
- value="<?php echo $settings->customGraphicURL ?>"/>
184
- <div class="sui-upload">
185
- <div class="sui-upload-image">
186
- <img id="customGraphicIMG" width="40" height="40"
187
- src="<?php echo $settings->customGraphicURL ?>">
188
- <div role="button"
189
- class="sui-image-preview"
190
- style="background-image: url('<?php echo $settings->customGraphicURL ?>');">
191
- </div>
192
- </div>
193
-
194
- <button type="button" class="sui-upload-button file-picker">
195
- <i class="sui-icon-upload-cloud" aria-hidden="true"></i>
196
- <?php _e( "Upload file", "defender-security" ) ?>
197
- </button>
198
-
199
- <div class="sui-upload-file">
200
- <span><?php echo pathinfo( $settings->customGraphicURL, PATHINFO_BASENAME ) ?></span>
201
- </div>
202
-
203
- </div>
204
- </div>
205
- </div>
206
- </div>
207
- <div class="sui-box-settings-row">
208
- <div class="sui-box-settings-col-1">
209
- <span class="sui-settings-label">
210
- <?php _e( "Emails", "defender-security" ) ?>
211
- </span>
212
- <span class="sui-description">
213
- <?php _e( 'Customize the default copy for emails the two-factor feature sends to users.', "defender-security" ); ?>
214
- </span>
215
- </div>
216
- <div class="sui-box-settings-col-2">
217
- <div class="sui-field-list">
218
- <div class="sui-field-list-header">
219
- <h3 class="sui-field-list-title"><?php _e( "Email", "defender-security" ) ?></h3>
220
- </div>
221
- <div class="sui-field-list-body">
222
- <div class="sui-field-list-item">
223
- <label class="sui-field-list-item-label" for="demo-table-2-toggle-5">
224
- <?php _e( "Lost phone one time password", "defender-security" ) ?>
225
- </label>
226
- <button type="button" class="sui-button-icon"
227
- data-a11y-dialog-show="edit-one-time-password-email">
228
- <i class="sui-icon-pencil" aria-hidden="true"></i>
229
- </button>
230
- </div>
231
- </div>
232
- </div>
233
- </div>
234
- </div>
235
- <div class="sui-box-settings-row">
236
- <div class="sui-box-settings-col-1">
237
- <span class="sui-settings-label">
238
- <?php _e( "App Download", "defender-security" ) ?>
239
- </span>
240
- <span class="sui-description">
241
- <?php _e( 'Need the app? Here’s links to the official Google Authenticator iOS and Android apps.', "defender-security" ); ?>
242
- </span>
243
- </div>
244
- <div class="sui-box-settings-col-2">
245
- <a href="https://itunes.apple.com/vn/app/google-authenticator/id388497605?mt=8">
246
- <img src="<?php echo wp_defender()->getPluginUrl() . 'assets/img/ios-download.svg' ?>"/>
247
- </a>
248
- <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2">
249
- <img src="<?php echo wp_defender()->getPluginUrl() . 'assets/img/android-download.svg' ?>"/>
250
- </a>
251
- </div>
252
- </div>
253
- <div class="sui-box-settings-row">
254
- <div class="sui-box-settings-col-1">
255
- <span class="sui-settings-label">
256
- <?php _e( "Active Users", "defender-security" ) ?>
257
- </span>
258
- <span class="sui-description">
259
- <?php _e( "Here’s a quick link to see which of your users have enabled two-factor authentication.", "defender-security" ) ?>
260
- </span>
261
- </div>
262
- <div class="sui-box-settings-col-2">
263
- <?php printf( __( "<a href=\"%s\">View users</a> who have enabled this feature.", "defender-security" ), network_admin_url( 'users.php' ) ) ?>
264
- </div>
265
- </div>
266
- <div class="sui-box-settings-row">
267
- <div class="sui-box-settings-col-1">
268
- <span class="sui-settings-label">
269
- <?php _e( "Deactivate", "defender-security" ) ?>
270
- </span>
271
- <span class="sui-description">
272
- <?php _e( "Disable two-factor authentication on your website.", "defender-security" ) ?>
273
- </span>
274
- </div>
275
- <div class="sui-box-settings-col-2">
276
- <button type="button" class="sui-button sui-button-ghost deactivate-2factor">
277
- <?php _e( "Deactivate", "defender-security" ) ?>
278
- </button>
279
- </div>
280
- </div>
281
- </div>
282
- <div class="sui-box-footer">
283
- <input type="hidden" name="action" value="saveAdvancedSettings"/>
284
- <?php wp_nonce_field( 'saveAdvancedSettings' ) ?>
285
- <div class="sui-actions-right">
286
- <button type="submit" class="sui-button sui-button-blue">
287
- <i class="sui-icon-save" aria-hidden="true"></i>
288
- <?php _e( "Save Changes", "defender-security" ) ?></button>
289
- </div>
290
- </div>
291
- </form>
292
- <?php
293
- $view = '2factor-otp-email-edit-from';
294
- $settings = array( 'settings' => $settings );
295
- $controller->renderPartial( $view, $settings );
296
- ?>
297
- </div>
1
+ <div id="defender"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/module/audit.php CHANGED
@@ -8,6 +8,7 @@ namespace WP_Defender\Module;
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Audit\Controller\Main;
10
  use WP_Defender\Module\Audit\Controller\Main_Free;
 
11
 
12
  class Audit extends Module {
13
  public function __construct() {
@@ -18,6 +19,7 @@ class Audit extends Module {
18
  new Main_Free();
19
  } else {
20
  new Main();
 
21
  }
22
  }
23
  }
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Audit\Controller\Main;
10
  use WP_Defender\Module\Audit\Controller\Main_Free;
11
+ use WP_Defender\Module\Audit\Controller\Rest;
12
 
13
  class Audit extends Module {
14
  public function __construct() {
19
  new Main_Free();
20
  } else {
21
  new Main();
22
+ new Rest();
23
  }
24
  }
25
  }
app/module/audit/controller/main-free.php CHANGED
@@ -8,23 +8,40 @@ namespace WP_Defender\Module\Audit\Controller;
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\Log_Helper;
10
  use Hammer\Helper\WP_Helper;
 
11
  use WP_Defender\Module\Audit\Component\Audit_API;
12
  use WP_Defender\Module\Audit\Component\Audit_Table;
13
  use WP_Defender\Module\Audit\Model\Settings;
14
- use WP_Defender\Vendor\Email_Search;
15
 
16
  class Main_Free extends \WP_Defender\Controller {
17
  protected $slug = 'wdf-logging';
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  public function __construct() {
20
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
21
- $this->add_action( 'network_admin_menu', 'adminMenu' );
22
  } else {
23
- $this->add_action( 'admin_menu', 'adminMenu' );
24
  }
25
 
26
  if ( $this->isInPage() || $this->isDashboard() ) {
27
- $this->add_action( 'defender_enqueue_assets', 'scripts', 11 );
28
  }
29
  }
30
 
@@ -41,14 +58,25 @@ class Main_Free extends \WP_Defender\Controller {
41
 
42
  public function scripts() {
43
  if ( $this->isInPage() ) {
44
- wp_enqueue_script( 'wpmudev-sui' );
45
- wp_enqueue_style( 'wpmudev-sui' );
46
- //wp_enqueue_script( 'defender' );
47
- wp_enqueue_style( 'defender' );
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
  }
50
 
51
  public function actionIndex() {
52
- $this->renderPartial( 'free' );
53
  }
54
  }
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\Log_Helper;
10
  use Hammer\Helper\WP_Helper;
11
+ use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Module\Audit\Component\Audit_API;
13
  use WP_Defender\Module\Audit\Component\Audit_Table;
14
  use WP_Defender\Module\Audit\Model\Settings;
 
15
 
16
  class Main_Free extends \WP_Defender\Controller {
17
  protected $slug = 'wdf-logging';
18
 
19
+ /**
20
+ * Declaring behaviors
21
+ * @return array
22
+ */
23
+ /**
24
+ * @return array
25
+ */
26
+ public function behaviors() {
27
+ $behaviors = [
28
+ 'utils' => '\WP_Defender\Behavior\Utils',
29
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
30
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
31
+ ];
32
+
33
+ return $behaviors;
34
+ }
35
+
36
  public function __construct() {
37
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
38
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
39
  } else {
40
+ $this->addAction( 'admin_menu', 'adminMenu' );
41
  }
42
 
43
  if ( $this->isInPage() || $this->isDashboard() ) {
44
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
45
  }
46
  }
47
 
58
 
59
  public function scripts() {
60
  if ( $this->isInPage() ) {
61
+ if ( $this->isInPage() ) {
62
+ wp_enqueue_style( 'wpmudev-sui' );
63
+ wp_enqueue_style( 'defender' );
64
+
65
+ wp_register_script( 'defender-audit', wp_defender()->getPluginUrl() . 'assets/app/audit.js', array(
66
+ 'vue',
67
+ 'defender',
68
+ 'wp-i18n'
69
+ ), wp_defender()->version, true );
70
+ Utils::instance()->createTranslationJson( 'defender-audit' );
71
+ wp_set_script_translations( 'defender-audit', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
72
+ wp_localize_script( 'defender-audit', 'auditData', [] );
73
+ wp_enqueue_script( 'defender-audit' );
74
+ wp_enqueue_script( 'wpmudev-sui' );
75
+ }
76
  }
77
  }
78
 
79
  public function actionIndex() {
80
+ $this->renderPartial( 'main-free' );
81
  }
82
  }
app/module/audit/view/main-free.php ADDED
@@ -0,0 +1 @@
 
1
+ <div id="defender"></div>
app/module/hardener.php CHANGED
@@ -5,49 +5,55 @@
5
 
6
  namespace WP_Defender\Module;
7
 
8
- use Hammer\Base\Container;
9
  use Hammer\Base\Module;
10
  use Hammer\Helper\HTTP_Helper;
11
- use Hammer\Helper\WP_Helper;
12
 
13
  use WP_Defender\Module\Hardener\Controller\Main;
 
14
  use WP_Defender\Module\Hardener\Model\Settings;
15
 
16
  class Hardener extends Module {
17
  const Settings = 'hardener_settings';
18
-
19
  public function __construct() {
20
  //init dependency
21
  $this->initRulesStats();
22
  //call the controller
23
  new Main();
 
24
  }
25
-
26
  /**
27
  * Init rules status
28
  */
29
  public function initRulesStats() {
30
- $settings = Settings::instance();
31
  /**
32
  * now we have a list of rules, and lists of their status
33
  */
34
- $interval = '+60 minutes';
35
- //only refresh if on admin, if not we just do the listening
36
-
37
- if ( ( ( is_admin() || is_network_admin() )
38
- && ( $settings->last_status_check == null || strtotime( $interval, $settings->last_status_check ) < time() )
39
- ) || HTTP_Helper::retrieve_get( 'page' ) == 'wdf-hardener'
40
- ) {
41
- //this mean we dont have any data, or data is overdue need to refresh
42
- //refetch those list
43
-
44
- $settings->refreshStatus();
 
 
 
 
 
 
45
  }
46
-
47
  //we will need to add every hooks needed
48
  foreach ( $settings->getDefinedRules( true ) as $rule ) {
49
  $rule->addHooks();
50
  }
51
  }
52
-
53
  }
5
 
6
  namespace WP_Defender\Module;
7
 
 
8
  use Hammer\Base\Module;
9
  use Hammer\Helper\HTTP_Helper;
 
10
 
11
  use WP_Defender\Module\Hardener\Controller\Main;
12
+ use WP_Defender\Module\Hardener\Controller\Rest;
13
  use WP_Defender\Module\Hardener\Model\Settings;
14
 
15
  class Hardener extends Module {
16
  const Settings = 'hardener_settings';
17
+
18
  public function __construct() {
19
  //init dependency
20
  $this->initRulesStats();
21
  //call the controller
22
  new Main();
23
+ new Rest();
24
  }
25
+
26
  /**
27
  * Init rules status
28
  */
29
  public function initRulesStats() {
30
+ $settings = Settings::instance( true );
31
  /**
32
  * now we have a list of rules, and lists of their status
33
  */
34
+ if ( ! defined( 'DOING_AJAX' ) ) {
35
+ //only init when page load
36
+ $interval = '+0 seconds';
37
+ //only refresh if on admin, if not we just do the listening
38
+
39
+ if ( ( ( is_admin() || is_network_admin() )
40
+ ) && ( HTTP_Helper::retrieveGet( 'page' ) == 'wdf-hardener' || HTTP_Helper::retrieveGet( 'page' ) == 'wp-defender' )
41
+ ) {
42
+ //this mean we dont have any data, or data is overdue need to refresh
43
+ //refetch those list
44
+
45
+ $settings->refreshStatus();
46
+ } elseif ( defined( 'DOING_CRON' ) ) {
47
+ //if this is in cronjob, we refresh it too
48
+ $settings->refreshStatus();
49
+ }
50
+ $settings->save();
51
  }
52
+
53
  //we will need to add every hooks needed
54
  foreach ( $settings->getDefinedRules( true ) as $rule ) {
55
  $rule->addHooks();
56
  }
57
  }
58
+
59
  }
app/module/hardener/component/change-admin-service.php CHANGED
@@ -2,6 +2,7 @@
2
  /**
3
  * Author: Hoang Ngo
4
  */
 
5
  namespace WP_Defender\Module\Hardener\Component;
6
 
7
  use WP_Defender\Component\Error_Code;
@@ -73,6 +74,7 @@ class Change_Admin_Service extends Rule_Service implements IRule_Service {
73
  }
74
  }
75
  clean_user_cache( $admin_data->ID );
 
76
 
77
  return true;
78
  }
2
  /**
3
  * Author: Hoang Ngo
4
  */
5
+
6
  namespace WP_Defender\Module\Hardener\Component;
7
 
8
  use WP_Defender\Component\Error_Code;
74
  }
75
  }
76
  clean_user_cache( $admin_data->ID );
77
+ $this->store( 'fixed', Change_Admin::$slug );
78
 
79
  return true;
80
  }
app/module/hardener/component/change-admin.php CHANGED
@@ -10,44 +10,54 @@ use WP_Defender\Module\Hardener\Model\Settings;
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Change_Admin extends Rule {
13
- static $slug = 'change_admin';
14
  static $service;
15
-
16
  public function getDescription() {
17
  $this->renderPartial( 'rules/change-admin' );
18
  }
19
-
20
- public function getSubDescription() {
 
 
 
 
 
21
  return __( "You have a user account with the admin username.", "defender-security" );
22
  }
23
-
 
 
 
 
 
 
 
 
24
  public function check() {
25
  return $this->getService()->check();
26
  }
27
-
28
  public function addHooks() {
29
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
30
  }
31
-
32
  public function revert() {
33
- // TODO: Implement revert() method.
34
  }
35
-
36
  /**
37
  * @return string
38
  */
39
  public function getTitle() {
40
  return __( "Change default admin user account", "defender-security" );
41
  }
42
-
43
  /**
44
  *
45
  */
46
  public function process() {
47
- if ( ! $this->verifyNonce() ) {
48
- return;
49
- }
50
- $username = HTTP_Helper::retrieve_post( 'username' );
51
  $this->getService()->setUsername( $username );
52
  $ret = $this->getService()->process();
53
  if ( is_wp_error( $ret ) ) {
@@ -55,15 +65,14 @@ class Change_Admin extends Rule {
55
  'message' => $ret->get_error_message()
56
  ) );
57
  } else {
58
- Settings::instance()->addToResolved( self::$slug );
59
  wp_send_json_success( array(
60
- 'message' => sprintf( __( "Your admin name has changed. You will need to <a href='%s'><strong>%s</strong></a>.<br/>This will auto reload after <span class='hardener-timer'>10</span> seconds.", "defender-security" ), wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) ), "re-login" ),
61
  'reload' => 5,
62
  'url' => wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) )
63
  ) );
64
  }
65
  }
66
-
67
  /**
68
  * @return Change_Admin_Service
69
  */
@@ -71,7 +80,7 @@ class Change_Admin extends Rule {
71
  if ( self::$service == null ) {
72
  self::$service = new Change_Admin_Service();
73
  }
74
-
75
  return self::$service;
76
  }
77
  }
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Change_Admin extends Rule {
13
+ static $slug = 'replace-admin-username';
14
  static $service;
15
+
16
  public function getDescription() {
17
  $this->renderPartial( 'rules/change-admin' );
18
  }
19
+
20
+ /**
21
+ * This will return the short summary why this rule show up as issue
22
+ *
23
+ * @return string
24
+ */
25
+ function getErrorReason() {
26
  return __( "You have a user account with the admin username.", "defender-security" );
27
  }
28
+
29
+ /**
30
+ * This will return a short summary to show why this rule works
31
+ * @return mixed
32
+ */
33
+ function getSuccessReason() {
34
+ return __( "You don't have a user account sporting the admin username, great.", "defender-security" );
35
+ }
36
+
37
  public function check() {
38
  return $this->getService()->check();
39
  }
40
+
41
  public function addHooks() {
42
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
43
  }
44
+
45
  public function revert() {
46
+
47
  }
48
+
49
  /**
50
  * @return string
51
  */
52
  public function getTitle() {
53
  return __( "Change default admin user account", "defender-security" );
54
  }
55
+
56
  /**
57
  *
58
  */
59
  public function process() {
60
+ $username = HTTP_Helper::retrievePost( 'username' );
 
 
 
61
  $this->getService()->setUsername( $username );
62
  $ret = $this->getService()->process();
63
  if ( is_wp_error( $ret ) ) {
65
  'message' => $ret->get_error_message()
66
  ) );
67
  } else {
 
68
  wp_send_json_success( array(
69
+ 'message' => sprintf( __( "Your admin name has changed. You will need to <a href='%s'><strong>%s</strong></a>.<br/>This will auto reload after <span class='hardener-timer'>5</span> seconds.", "defender-security" ), wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) ), "re-login" ),
70
  'reload' => 5,
71
  'url' => wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) )
72
  ) );
73
  }
74
  }
75
+
76
  /**
77
  * @return Change_Admin_Service
78
  */
80
  if ( self::$service == null ) {
81
  self::$service = new Change_Admin_Service();
82
  }
83
+
84
  return self::$service;
85
  }
86
  }
app/module/hardener/component/db-prefix-service.php CHANGED
@@ -11,39 +11,40 @@ use Hammer\Helper\WP_Helper;
11
  use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Component\Error_Code;
13
  use WP_Defender\Module\Hardener\IRule_Service;
 
14
  use WP_Defender\Module\Hardener\Rule_Service;
15
 
16
  class DB_Prefix_Service extends Rule_Service implements IRule_Service {
17
  public $new_prefix;
18
  protected $old_prefix;
19
-
20
  /**
21
  * @return bool
22
  */
23
  public function check() {
24
  global $wpdb;
25
-
26
  return $wpdb->prefix != 'wp_';
27
  }
28
-
29
  public function process() {
30
  $config_path = $this->retrieveWPConfigPath();
31
  if ( ! is_writeable( $config_path ) ) {
32
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
33
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
34
  }
35
-
36
  $hook_line = $this->findDefaultHookLine( file( $config_path ) );
37
  if ( $hook_line === false ) {
38
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Your wp-config.php was modified by a 3rd party, this will cause conflict with Defender. Please revert it to original for updating your database prefix", "defender-security" ) );
39
  }
40
-
41
  if ( ! Utils::instance()->isActivatedSingle() ) {
42
  //validate if this network is too big, then we will prevent it
43
  $sites = get_sites( array(
44
  'count' => true
45
  ) );
46
-
47
  if ( $sites >= 100 ) {
48
  return new \WP_Error( Error_Code::VALIDATE, __( "Unfortunately it's not safe to do this via a plugin for larger WordPress Multisite installs. You can ignore this step, or follow a tutorial online on how to use a scalable tool like WP-CLI.", "defender-security" ) );
49
  }
@@ -51,14 +52,18 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
51
  if ( is_wp_error( $is_valid = $this->validatePrefix() ) ) {
52
  return $is_valid;
53
  }
54
-
 
 
 
 
55
  $prefix = $this->new_prefix;
56
  set_time_limit( - 1 );
57
  //add trailing underscore if not present
58
  if ( substr( $prefix, - 1 ) != '_' ) {
59
  $this->new_prefix .= '_';
60
  }
61
-
62
  global $wpdb;
63
  $wpdb->query( 'BEGIN' );
64
  //run a query to change db prefix
@@ -72,15 +77,20 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
72
  //almost there, now just need to update wpconfig
73
  if ( is_wp_error( ( $err = $this->writeToWpConfig() ) ) ) {
74
  $wpdb->query( 'ROLLBACK' );
75
-
76
  return $err;
77
  }
78
  //all good
79
  $wpdb->query( 'COMMIT' );
80
-
 
 
 
 
 
81
  return true;
82
  }
83
-
84
  /**
85
  * @return bool|\WP_Error
86
  */
@@ -95,17 +105,17 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
95
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
96
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
97
  }
98
-
99
  return true;
100
  }
101
-
102
  /**
103
  * @return bool|\WP_Error
104
  */
105
  private function updateData() {
106
  global $wpdb;
107
- $prefix = $this->new_prefix;
108
- $old_prefix = $this->old_prefix;
109
  if ( is_multisite() ) {
110
  /**
111
  * case multiste
@@ -145,10 +155,10 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
145
  }
146
  }
147
  }
148
-
149
  return true;
150
  }
151
-
152
  /**
153
  * @param null $old_prefix
154
  *
@@ -162,7 +172,7 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
162
  //cache it
163
  $this->old_prefix = $old_prefix;
164
  $tables = $this->getTables();
165
-
166
  foreach ( $tables as $table ) {
167
  $new_table_name = substr_replace( $table, $this->new_prefix, 0, strlen( $this->old_prefix ) );
168
  $sql = "RENAME TABLE `{$table}` TO `{$new_table_name}`";
@@ -170,10 +180,10 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
170
  return new \WP_Error( Error_Code::SQL_ERROR, $wpdb->last_error );
171
  }
172
  }
173
-
174
  return true;
175
  }
176
-
177
  /**
178
  * Validate the current prefix
179
  *
@@ -181,48 +191,71 @@ class DB_Prefix_Service extends Rule_Service implements IRule_Service {
181
  */
182
  private function validatePrefix() {
183
  $new_prefix = trim( $this->new_prefix );
184
-
185
  global $wpdb;
186
  if ( $new_prefix == $wpdb->prefix ) {
187
  return new \WP_Error( Error_Code::VALIDATE, __( "You are currently using this prefix.", "defender-security" ) );
188
  }
189
-
190
  if ( strlen( $new_prefix ) == 0 ) {
191
  return new \WP_Error( Error_Code::VALIDATE, __( "Your prefix can't be empty!", "defender-security" ) );
192
  }
193
-
194
  if ( preg_match( '|[^a-z0-9_]|i', $new_prefix ) ) {
195
  return new \WP_Error( Error_Code::VALIDATE, __( "Table prefix can only contain numbers, letters, and underscores.", "defender-security" ) );
196
  }
197
-
198
  if ( count( $tables = $this->getTables( $new_prefix ) ) ) {
199
  return new \WP_Error( Error_Code::VALIDATE, __( "This prefix is already in use. Please choose a different prefix.", "defender-security" ) );
200
  }
201
-
202
  $this->new_prefix = $new_prefix;
203
-
204
  return true;
205
  }
206
-
207
  private function getTables( $prefix = null ) {
208
  global $wpdb;
209
-
210
  if ( ! $prefix ) {
211
  $prefix = $wpdb->base_prefix;
212
  }
213
-
214
  $results = $wpdb->get_col( $wpdb->prepare( "SHOW TABLES LIKE %s", $prefix . '%' ) );
215
  $results = array_unique( $results );
216
-
217
  return $results;
218
  }
219
-
220
  public function revert() {
221
  $this->new_prefix = 'wp_';
222
-
223
- return $this->process();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  }
225
-
226
  public function listen() {
227
  // TODO: Implement listen() method.
228
  }
11
  use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Component\Error_Code;
13
  use WP_Defender\Module\Hardener\IRule_Service;
14
+ use WP_Defender\Module\Hardener\Model\Settings;
15
  use WP_Defender\Module\Hardener\Rule_Service;
16
 
17
  class DB_Prefix_Service extends Rule_Service implements IRule_Service {
18
  public $new_prefix;
19
  protected $old_prefix;
20
+
21
  /**
22
  * @return bool
23
  */
24
  public function check() {
25
  global $wpdb;
26
+
27
  return $wpdb->prefix != 'wp_';
28
  }
29
+
30
  public function process() {
31
  $config_path = $this->retrieveWPConfigPath();
32
  if ( ! is_writeable( $config_path ) ) {
33
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
34
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
35
  }
36
+
37
  $hook_line = $this->findDefaultHookLine( file( $config_path ) );
38
  if ( $hook_line === false ) {
39
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Your wp-config.php was modified by a 3rd party, this will cause conflict with Defender. Please revert it to original for updating your database prefix", "defender-security" ) );
40
  }
41
+
42
  if ( ! Utils::instance()->isActivatedSingle() ) {
43
  //validate if this network is too big, then we will prevent it
44
  $sites = get_sites( array(
45
  'count' => true
46
  ) );
47
+
48
  if ( $sites >= 100 ) {
49
  return new \WP_Error( Error_Code::VALIDATE, __( "Unfortunately it's not safe to do this via a plugin for larger WordPress Multisite installs. You can ignore this step, or follow a tutorial online on how to use a scalable tool like WP-CLI.", "defender-security" ) );
50
  }
52
  if ( is_wp_error( $is_valid = $this->validatePrefix() ) ) {
53
  return $is_valid;
54
  }
55
+
56
+ //mark this here
57
+ Settings::instance()->is_prefix_changed = true;
58
+ Settings::instance()->save();
59
+
60
  $prefix = $this->new_prefix;
61
  set_time_limit( - 1 );
62
  //add trailing underscore if not present
63
  if ( substr( $prefix, - 1 ) != '_' ) {
64
  $this->new_prefix .= '_';
65
  }
66
+
67
  global $wpdb;
68
  $wpdb->query( 'BEGIN' );
69
  //run a query to change db prefix
77
  //almost there, now just need to update wpconfig
78
  if ( is_wp_error( ( $err = $this->writeToWpConfig() ) ) ) {
79
  $wpdb->query( 'ROLLBACK' );
80
+
81
  return $err;
82
  }
83
  //all good
84
  $wpdb->query( 'COMMIT' );
85
+ //because we write to db so the later query will be error as the table from $wpdb not there anymore
86
+ //replace the $wpdb isntance
87
+ $wpdb->set_prefix( $this->new_prefix );
88
+ //some time it can be gap time from io or so, just sleep it for 3s
89
+ sleep(3);
90
+
91
  return true;
92
  }
93
+
94
  /**
95
  * @return bool|\WP_Error
96
  */
105
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
106
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
107
  }
108
+
109
  return true;
110
  }
111
+
112
  /**
113
  * @return bool|\WP_Error
114
  */
115
  private function updateData() {
116
  global $wpdb;
117
+ $prefix = $this->new_prefix;
118
+ $old_prefix = $this->old_prefix;
119
  if ( is_multisite() ) {
120
  /**
121
  * case multiste
155
  }
156
  }
157
  }
158
+
159
  return true;
160
  }
161
+
162
  /**
163
  * @param null $old_prefix
164
  *
172
  //cache it
173
  $this->old_prefix = $old_prefix;
174
  $tables = $this->getTables();
175
+
176
  foreach ( $tables as $table ) {
177
  $new_table_name = substr_replace( $table, $this->new_prefix, 0, strlen( $this->old_prefix ) );
178
  $sql = "RENAME TABLE `{$table}` TO `{$new_table_name}`";
180
  return new \WP_Error( Error_Code::SQL_ERROR, $wpdb->last_error );
181
  }
182
  }
183
+
184
  return true;
185
  }
186
+
187
  /**
188
  * Validate the current prefix
189
  *
191
  */
192
  private function validatePrefix() {
193
  $new_prefix = trim( $this->new_prefix );
194
+
195
  global $wpdb;
196
  if ( $new_prefix == $wpdb->prefix ) {
197
  return new \WP_Error( Error_Code::VALIDATE, __( "You are currently using this prefix.", "defender-security" ) );
198
  }
199
+
200
  if ( strlen( $new_prefix ) == 0 ) {
201
  return new \WP_Error( Error_Code::VALIDATE, __( "Your prefix can't be empty!", "defender-security" ) );
202
  }
203
+
204
  if ( preg_match( '|[^a-z0-9_]|i', $new_prefix ) ) {
205
  return new \WP_Error( Error_Code::VALIDATE, __( "Table prefix can only contain numbers, letters, and underscores.", "defender-security" ) );
206
  }
207
+
208
  if ( count( $tables = $this->getTables( $new_prefix ) ) ) {
209
  return new \WP_Error( Error_Code::VALIDATE, __( "This prefix is already in use. Please choose a different prefix.", "defender-security" ) );
210
  }
211
+
212
  $this->new_prefix = $new_prefix;
213
+
214
  return true;
215
  }
216
+
217
  private function getTables( $prefix = null ) {
218
  global $wpdb;
219
+
220
  if ( ! $prefix ) {
221
  $prefix = $wpdb->base_prefix;
222
  }
223
+
224
  $results = $wpdb->get_col( $wpdb->prepare( "SHOW TABLES LIKE %s", $prefix . '%' ) );
225
  $results = array_unique( $results );
226
+
227
  return $results;
228
  }
229
+
230
  public function revert() {
231
  $this->new_prefix = 'wp_';
232
+ if ( Settings::instance()->is_prefix_changed == true ) {
233
+ set_time_limit( - 1 );
234
+ global $wpdb;
235
+ $wpdb->query( 'BEGIN' );
236
+ //run a query to change db prefix
237
+ if ( is_wp_error( ( $err = $this->changeDBPrefix() ) ) ) {
238
+ return $err;
239
+ }
240
+ //update data
241
+ if ( is_wp_error( ( $err = $this->updateData() ) ) ) {
242
+ return $err;
243
+ }
244
+ //almost there, now just need to update wpconfig
245
+ if ( is_wp_error( ( $err = $this->writeToWpConfig() ) ) ) {
246
+ $wpdb->query( 'ROLLBACK' );
247
+
248
+ return $err;
249
+ }
250
+ //all good
251
+ $wpdb->query( 'COMMIT' );
252
+ //restore back
253
+ $wpdb->set_prefix( 'wp_' );
254
+
255
+ return true;
256
+ }
257
  }
258
+
259
  public function listen() {
260
  // TODO: Implement listen() method.
261
  }
app/module/hardener/component/db-prefix.php CHANGED
@@ -11,7 +11,7 @@ use WP_Defender\Module\Hardener\Model\Settings;
11
  use WP_Defender\Module\Hardener\Rule;
12
 
13
  class DB_Prefix extends Rule {
14
- static $slug = 'db_prefix';
15
  static $service;
16
 
17
  function getDescription() {
@@ -22,22 +22,20 @@ class DB_Prefix extends Rule {
22
  return $this->getService()->check();
23
  }
24
 
25
- /**
26
- * @return mixed
27
- */
28
- function getSubDescription() {
29
- return __( "Your database prefix is the default wp_ prefix.", "defender-security" );
30
  }
31
 
32
- function addHooks() {
33
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
34
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
 
 
 
35
  }
36
 
37
  function revert() {
38
- if ( ! $this->verifyNonce() ) {
39
- return;
40
- }
41
  if ( Settings::instance()->is_prefix_changed == true ) {
42
  $ret = $this->getService()->revert();
43
  if ( ! is_wp_error( $ret ) ) {
@@ -55,19 +53,16 @@ class DB_Prefix extends Rule {
55
  }
56
 
57
  function process() {
58
- if ( ! $this->verifyNonce() ) {
59
- return;
60
- }
61
- $dbprefix = HTTP_Helper::retrieve_post( 'dbprefix' );
62
- $this->getService()->new_prefix = $dbprefix;
63
- $ret = $this->getService()->process();
64
- Settings::instance()->is_prefix_changed = true;
65
- Settings::instance()->save();
66
  if ( is_wp_error( $ret ) ) {
67
  wp_send_json_error( array(
68
  'message' => $ret->get_error_message()
69
  ) );
70
- };
 
 
71
  }
72
 
73
  /**
@@ -80,4 +75,23 @@ class DB_Prefix extends Rule {
80
 
81
  return static::$service;
82
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
11
  use WP_Defender\Module\Hardener\Rule;
12
 
13
  class DB_Prefix extends Rule {
14
+ static $slug = 'db-prefix';
15
  static $service;
16
 
17
  function getDescription() {
22
  return $this->getService()->check();
23
  }
24
 
25
+ function addHooks() {
26
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
27
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
 
 
28
  }
29
 
30
+ function getMiscData() {
31
+ $prefix = wp_generate_password( 6, false );
32
+
33
+ return [
34
+ 'prefix' => 'wp_' . $prefix . '_'
35
+ ];
36
  }
37
 
38
  function revert() {
 
 
 
39
  if ( Settings::instance()->is_prefix_changed == true ) {
40
  $ret = $this->getService()->revert();
41
  if ( ! is_wp_error( $ret ) ) {
53
  }
54
 
55
  function process() {
56
+ $dbprefix = HTTP_Helper::retrievePost( 'dbprefix' );
57
+ $this->getService()->new_prefix = $dbprefix;
58
+ $ret = $this->getService()->process();
 
 
 
 
 
59
  if ( is_wp_error( $ret ) ) {
60
  wp_send_json_error( array(
61
  'message' => $ret->get_error_message()
62
  ) );
63
+ } else {
64
+ //leave the rest to the @Rest.processTweak
65
+ }
66
  }
67
 
68
  /**
75
 
76
  return static::$service;
77
  }
78
+
79
+ /**
80
+ * This will return the short summary why this rule show up as issue
81
+ *
82
+ * @return string
83
+ */
84
+ function getErrorReason() {
85
+ return __( "Your database prefix is the default wp_ prefix.", "defender-security" );
86
+ }
87
+
88
+ /**
89
+ * This will return a short summary to show why this rule works
90
+ * @return mixed
91
+ */
92
+ function getSuccessReason() {
93
+ global $wpdb;
94
+
95
+ return sprintf( __( "Your database prefix is set to <strong>%s</strong> and is unique, %s would be proud.", "defender-security" ), $wpdb->prefix, \WP_Defender\Behavior\Utils::instance()->getDisplayName() );
96
+ }
97
  }
app/module/hardener/component/disable-file-editor.php CHANGED
@@ -9,17 +9,31 @@ use WP_Defender\Module\Hardener\Model\Settings;
9
  use WP_Defender\Module\Hardener\Rule;
10
 
11
  class Disable_File_Editor extends Rule {
12
- static $slug = 'disable_file_editor';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  static $service;
14
 
15
  function getDescription() {
16
  $this->renderPartial( 'rules/disable-file-editor' );
17
  }
18
 
19
- function getSubDescription() {
20
- return __( "The file editor is currently enabled.", "defender-security" );
21
- }
22
-
23
  function check() {
24
  return $this->getService()->check();
25
  }
@@ -29,26 +43,23 @@ class Disable_File_Editor extends Rule {
29
  }
30
 
31
  function addHooks() {
32
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
33
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
34
  //Extra hardener actions incase setup is messed
35
  if ( $this->check() ) {
36
- $this->add_action( 'current_screen', 'current_screen' );
37
  if ( is_network_admin() ) {
38
- $this->add_action( 'network_admin_menu', 'editor_admin_menu', 999 );
39
  } elseif ( is_user_admin() ) {
40
- $this->add_action( 'user_admin_menu', 'editor_admin_menu', 999 );
41
  } else {
42
- $this->add_action( 'admin_menu', 'editor_admin_menu', 999 );
43
  }
44
- $this->add_filter( 'plugin_action_links', 'action_links', 10, 4 );
45
  }
46
  }
47
 
48
  function revert() {
49
- if ( ! $this->verifyNonce() ) {
50
- return;
51
- }
52
  $ret = $this->getService()->revert();
53
  if ( ! is_wp_error( $ret ) ) {
54
  Settings::instance()->addToIssues( self::$slug );
@@ -60,10 +71,6 @@ class Disable_File_Editor extends Rule {
60
  }
61
 
62
  function process() {
63
- if ( ! $this->verifyNonce() ) {
64
- return;
65
- }
66
-
67
  $ret = $this->getService()->process();
68
  if ( ! is_wp_error( $ret ) ) {
69
  Settings::instance()->addToResolved( self::$slug );
@@ -91,29 +98,31 @@ class Disable_File_Editor extends Rule {
91
  */
92
  function current_screen() {
93
  $current_screen = get_current_screen();
94
- if( $current_screen->id == 'theme-editor-network' || $current_screen->id == 'theme-editor' ){
95
- wp_die('<p>'.__('Sorry, you are not allowed to edit templates for this site.').'</p>');
96
  }
97
- if( $current_screen->id == 'plugin-editor-network' || $current_screen->id == 'plugin-editor' ){
98
- wp_die('<p>'.__('Sorry, you are not allowed to edit plugins for this site.').'</p>');
99
  }
100
  }
101
-
102
  /**
103
  * Remove the edit in the admin menu
104
  */
105
  function editor_admin_menu() {
106
- remove_submenu_page( 'themes.php','theme-editor.php' );
107
- remove_submenu_page( 'plugins.php','plugin-editor.php' );
108
  }
109
 
110
  /**
111
- * Remove any edit links from the plugin list
112
  *
113
  */
114
- function action_links( $actions, $plugin_file, $plugin_data, $context) {
115
- if ( isset( $actions['edit'] ) )
116
  unset( $actions['edit'] );
 
 
117
  return $actions;
118
  }
119
  }
9
  use WP_Defender\Module\Hardener\Rule;
10
 
11
  class Disable_File_Editor extends Rule {
12
+ static $slug = 'disable-file-editor';
13
+
14
+ /**
15
+ * This will return the short summary why this rule show up as issue
16
+ *
17
+ * @return string
18
+ */
19
+ function getErrorReason() {
20
+ return __( "The file editor is currently enabled.", "defender-security" );
21
+ }
22
+
23
+ /**
24
+ * This will return a short summary to show why this rule works
25
+ * @return mixed
26
+ */
27
+ function getSuccessReason() {
28
+ return __( "You've disabled the file editor, winning.", "defender-security" );
29
+ }
30
+
31
  static $service;
32
 
33
  function getDescription() {
34
  $this->renderPartial( 'rules/disable-file-editor' );
35
  }
36
 
 
 
 
 
37
  function check() {
38
  return $this->getService()->check();
39
  }
43
  }
44
 
45
  function addHooks() {
46
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
47
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
48
  //Extra hardener actions incase setup is messed
49
  if ( $this->check() ) {
50
+ $this->addAction( 'current_screen', 'current_screen' );
51
  if ( is_network_admin() ) {
52
+ $this->addAction( 'network_admin_menu', 'editor_admin_menu', 999 );
53
  } elseif ( is_user_admin() ) {
54
+ $this->addAction( 'user_admin_menu', 'editor_admin_menu', 999 );
55
  } else {
56
+ $this->addAction( 'admin_menu', 'editor_admin_menu', 999 );
57
  }
58
+ $this->addFilter( 'plugin_action_links', 'action_links', 10, 4 );
59
  }
60
  }
61
 
62
  function revert() {
 
 
 
63
  $ret = $this->getService()->revert();
64
  if ( ! is_wp_error( $ret ) ) {
65
  Settings::instance()->addToIssues( self::$slug );
71
  }
72
 
73
  function process() {
 
 
 
 
74
  $ret = $this->getService()->process();
75
  if ( ! is_wp_error( $ret ) ) {
76
  Settings::instance()->addToResolved( self::$slug );
98
  */
99
  function current_screen() {
100
  $current_screen = get_current_screen();
101
+ if ( $current_screen->id == 'theme-editor-network' || $current_screen->id == 'theme-editor' ) {
102
+ wp_die( '<p>' . __( 'Sorry, you are not allowed to edit templates for this site.' ) . '</p>' );
103
  }
104
+ if ( $current_screen->id == 'plugin-editor-network' || $current_screen->id == 'plugin-editor' ) {
105
+ wp_die( '<p>' . __( 'Sorry, you are not allowed to edit plugins for this site.' ) . '</p>' );
106
  }
107
  }
108
+
109
  /**
110
  * Remove the edit in the admin menu
111
  */
112
  function editor_admin_menu() {
113
+ remove_submenu_page( 'themes.php', 'theme-editor.php' );
114
+ remove_submenu_page( 'plugins.php', 'plugin-editor.php' );
115
  }
116
 
117
  /**
118
+ * Remove any edit links from the plugin list
119
  *
120
  */
121
+ function action_links( $actions, $plugin_file, $plugin_data, $context ) {
122
+ if ( isset( $actions['edit'] ) ) {
123
  unset( $actions['edit'] );
124
+ }
125
+
126
  return $actions;
127
  }
128
  }
app/module/hardener/component/disable-trackback.php CHANGED
@@ -10,17 +10,13 @@ use WP_Defender\Module\Hardener\Model\Settings;
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Disable_Trackback extends Rule {
13
- static $slug = 'disable_trackback';
14
  static $service;
15
 
16
  function getDescription() {
17
  $this->renderPartial( 'rules/disable-trackback' );
18
  }
19
 
20
- function getSubDescription() {
21
- return __( "Trackbacks and pingbacks are currently enabled.", "defender-security" );
22
- }
23
-
24
  /**
25
  * @return bool
26
  */
@@ -28,15 +24,32 @@ class Disable_Trackback extends Rule {
28
  return $this->getService()->check();
29
  }
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  public function getTitle() {
32
  return __( "Disable trackbacks and pingbacks", "defender-security" );
33
  }
34
 
35
  function addHooks() {
36
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
37
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
38
- if ( in_array( self::$slug, Settings::instance()->fixed ) ) {
39
- $this->add_filter( 'wp_headers', 'removePingback' );
40
  }
41
  }
42
 
@@ -52,10 +65,6 @@ class Disable_Trackback extends Rule {
52
  }
53
 
54
  function revert() {
55
- if ( ! $this->verifyNonce() ) {
56
- return;
57
- }
58
-
59
  $ret = $this->getService()->revert();
60
  if ( ! is_wp_error( $ret ) ) {
61
  Settings::instance()->addToIssues( self::$slug );
@@ -67,10 +76,7 @@ class Disable_Trackback extends Rule {
67
  }
68
 
69
  function process() {
70
- if ( ! $this->verifyNonce() ) {
71
- return;
72
- }
73
- $process_posts = HTTP_Helper::retrieve_post( 'updatePosts' );
74
  $this->getService()->process_posts = $process_posts;
75
 
76
  $ret = $this->getService()->process();
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Disable_Trackback extends Rule {
13
+ static $slug = 'disable-trackback';
14
  static $service;
15
 
16
  function getDescription() {
17
  $this->renderPartial( 'rules/disable-trackback' );
18
  }
19
 
 
 
 
 
20
  /**
21
  * @return bool
22
  */
24
  return $this->getService()->check();
25
  }
26
 
27
+ /**
28
+ * This will return the short summary why this rule show up as issue
29
+ *
30
+ * @return string
31
+ */
32
+ function getErrorReason() {
33
+ return __( "Trackbacks and pingbacks are currently enabled.", "defender-security" );
34
+ }
35
+
36
+ /**
37
+ * This will return a short summary to show why this rule works
38
+ * @return mixed
39
+ */
40
+ function getSuccessReason() {
41
+ return __( "Trackbacks and pingbacks are disabled, nice work!", "defender-security" );
42
+ }
43
+
44
  public function getTitle() {
45
  return __( "Disable trackbacks and pingbacks", "defender-security" );
46
  }
47
 
48
  function addHooks() {
49
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
50
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
51
+ if ( in_array( self::$slug, (array) Settings::instance()->fixed ) ) {
52
+ $this->addFilter( 'wp_headers', 'removePingback' );
53
  }
54
  }
55
 
65
  }
66
 
67
  function revert() {
 
 
 
 
68
  $ret = $this->getService()->revert();
69
  if ( ! is_wp_error( $ret ) ) {
70
  Settings::instance()->addToIssues( self::$slug );
76
  }
77
 
78
  function process() {
79
+ $process_posts = HTTP_Helper::retrievePost( 'updatePosts' );
 
 
 
80
  $this->getService()->process_posts = $process_posts;
81
 
82
  $ret = $this->getService()->process();
app/module/hardener/component/disable-xml-rpc-service.php CHANGED
@@ -20,6 +20,7 @@ class Disable_Xml_Rpc_Service extends Rule_Service implements IRule_Service {
20
  public function process() {
21
  //first need to cache the status
22
  Settings::instance()->setDValues( self::CACHE_KEY, 1 );
 
23
  return true;
24
  }
25
 
@@ -28,6 +29,7 @@ class Disable_Xml_Rpc_Service extends Rule_Service implements IRule_Service {
28
  */
29
  public function revert() {
30
  Settings::instance()->setDValues( self::CACHE_KEY, 0 );
 
31
  return true;
32
  }
33
 
20
  public function process() {
21
  //first need to cache the status
22
  Settings::instance()->setDValues( self::CACHE_KEY, 1 );
23
+
24
  return true;
25
  }
26
 
29
  */
30
  public function revert() {
31
  Settings::instance()->setDValues( self::CACHE_KEY, 0 );
32
+
33
  return true;
34
  }
35
 
app/module/hardener/component/disable-xml-rpc.php CHANGED
@@ -10,17 +10,13 @@ use WP_Defender\Module\Hardener\Model\Settings;
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Disable_Xml_Rpc extends Rule {
13
- static $slug = 'disable_xml_rpc';
14
  static $service;
15
 
16
  function getDescription() {
17
  $this->renderPartial( 'rules/disable-xml-rpc' );
18
  }
19
 
20
- function getSubDescription() {
21
- return __( "XML-RPC is currently enabled.", "defender-security" );
22
- }
23
-
24
  /**
25
  * @return bool
26
  */
@@ -33,29 +29,22 @@ class Disable_Xml_Rpc extends Rule {
33
  }
34
 
35
  function addHooks() {
36
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
37
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
38
  if ( in_array( self::$slug, Settings::instance()->fixed ) ) {
39
- $this->add_filter( 'xmlrpc_enabled', 'return_false' );
40
- $this->add_filter( 'xmlrpc_methods', 'block_xmlrpc_attacks' );
41
  }
42
  }
43
 
44
- function return_false() {
45
- return false;
46
- }
47
-
48
  function block_xmlrpc_attacks( $methods ) {
49
  unset( $methods['pingback.ping'] );
50
  unset( $methods['pingback.extensions.getPingbacks'] );
 
51
  return $methods;
52
  }
53
 
54
  function revert() {
55
- if ( ! $this->verifyNonce() ) {
56
- return;
57
- }
58
-
59
  $ret = $this->getService()->revert();
60
  if ( ! is_wp_error( $ret ) ) {
61
  Settings::instance()->addToIssues( self::$slug );
@@ -67,10 +56,6 @@ class Disable_Xml_Rpc extends Rule {
67
  }
68
 
69
  function process() {
70
- if ( ! $this->verifyNonce() ) {
71
- return;
72
- }
73
-
74
  $ret = $this->getService()->process();
75
  if ( ! is_wp_error( $ret ) ) {
76
  Settings::instance()->addToResolved( self::$slug );
@@ -82,13 +67,30 @@ class Disable_Xml_Rpc extends Rule {
82
  }
83
 
84
  /**
85
- * @return Disable_Trackback_Service
86
  */
87
  public function getService() {
88
  if ( self::$service == null ) {
89
- self::$service = new Disable_Trackback_Service();
90
  }
91
 
92
  return self::$service;
93
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  }
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Disable_Xml_Rpc extends Rule {
13
+ static $slug = 'disable-xml-rpc';
14
  static $service;
15
 
16
  function getDescription() {
17
  $this->renderPartial( 'rules/disable-xml-rpc' );
18
  }
19
 
 
 
 
 
20
  /**
21
  * @return bool
22
  */
29
  }
30
 
31
  function addHooks() {
32
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
33
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
34
  if ( in_array( self::$slug, Settings::instance()->fixed ) ) {
35
+ add_filter( 'xmlrpc_enabled', '__return_false' );
36
+ $this->addFilter( 'xmlrpc_methods', 'block_xmlrpc_attacks' );
37
  }
38
  }
39
 
 
 
 
 
40
  function block_xmlrpc_attacks( $methods ) {
41
  unset( $methods['pingback.ping'] );
42
  unset( $methods['pingback.extensions.getPingbacks'] );
43
+
44
  return $methods;
45
  }
46
 
47
  function revert() {
 
 
 
 
48
  $ret = $this->getService()->revert();
49
  if ( ! is_wp_error( $ret ) ) {
50
  Settings::instance()->addToIssues( self::$slug );
56
  }
57
 
58
  function process() {
 
 
 
 
59
  $ret = $this->getService()->process();
60
  if ( ! is_wp_error( $ret ) ) {
61
  Settings::instance()->addToResolved( self::$slug );
67
  }
68
 
69
  /**
70
+ * @return Disable_Xml_Rpc_Service
71
  */
72
  public function getService() {
73
  if ( self::$service == null ) {
74
+ self::$service = new Disable_Xml_Rpc_Service();
75
  }
76
 
77
  return self::$service;
78
  }
79
+
80
+ /**
81
+ * This will return the short summary why this rule show up as issue
82
+ *
83
+ * @return string
84
+ */
85
+ function getErrorReason() {
86
+ return __( "XML-RPC is currently enabled.", "defender-security" );
87
+ }
88
+
89
+ /**
90
+ * This will return a short summary to show why this rule works
91
+ * @return mixed
92
+ */
93
+ function getSuccessReason() {
94
+ return __( "XML-RPC is disabled.", "defender-security" );
95
+ }
96
  }
app/module/hardener/component/hide-error-service.php CHANGED
@@ -12,42 +12,21 @@ use WP_Defender\Module\Hardener\IRule_Service;
12
  use WP_Defender\Module\Hardener\Rule_Service;
13
 
14
  class Hide_Error_Service extends Rule_Service implements IRule_Service {
15
-
16
  /**
17
  * @return bool
18
  */
19
  public function check() {
20
- //we will start a request to see if it is showing error
21
- $altCache = WP_Helper::getArrayCache();
22
- $cached = $altCache->get( 'Hide_Error_Service', null );
23
- if ( $cached === null ) {
24
- //tmp turn off error log
25
- $isLog = ini_get( 'log_errors' );
26
- if ( $isLog == 1 ) {
27
- ini_set( 'log_errors', 0 );
28
- }
29
- $url = site_url( 'wp-includes/theme-compat/embed.php', array(
30
- 'user-agent' => 'Defender self check'
31
- ) );
32
- $response = wp_remote_get( $url );
33
- $body = wp_remote_retrieve_body( $response );
34
- if ( $isLog == 1 ) {
35
- ini_set( 'log_errors', 1 );
36
- }
37
- if ( strpos( $body, ABSPATH . 'wp-includes/theme-compat/embed.php' ) !== false ||
38
- WP_DEBUG == true && ( ! defined( 'WP_DEBUG_DISPLAY' ) || WP_DEBUG_DISPLAY != false ) ) {
39
- $altCache->set( 'Hide_Error_Service', 0 );
40
-
41
- return false;
42
- }
43
- $altCache->set( 'Hide_Error_Service', 1 );
44
-
45
  return true;
46
- } else {
47
- return $cached;
48
  }
 
 
 
 
 
49
  }
50
-
51
  /**
52
  * Process to fix the wp-config base on scenario
53
  *
@@ -71,14 +50,14 @@ class Hide_Error_Service extends Rule_Service implements IRule_Service {
71
  }
72
  $config = Array_Helper::injectLine( $config, $hookline + 1, PHP_EOL . "define( 'WP_DEBUG', false );" . PHP_EOL );
73
  file_put_contents( $config_path, implode( null, $config ), LOCK_EX );
74
-
75
  return true;
76
  } else {
77
  //already somewhere
78
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Defender can't recognize your wp-config.php, please revert it to original state for further process.", "defender-security" ) );
79
  }
80
  }
81
-
82
  list( $value, $line ) = $info;
83
  if ( $value == 1 ) {
84
  if ( constant( 'WP_DEBUG_LOG' ) == true ) {
@@ -94,16 +73,16 @@ class Hide_Error_Service extends Rule_Service implements IRule_Service {
94
  $config[ $line ] = 'define( \'WP_DEBUG\', false );' . PHP_EOL;
95
  }
96
  file_put_contents( $config_path, implode( null, $config ), LOCK_EX );
97
-
98
  return true;
99
-
100
  } elseif ( $value == 0 ) {
101
  //debug already off
102
  //this is a rare case, debug is off, but error still showing up
103
  return new \WP_Error( 0, __( "WP_DEBUG get override somewhere, please check with your host provider", "defender-security" ) );
104
  }
105
  }
106
-
107
  /**
108
  * This will parse wpconfig lines and check if we do have any defined wp_debug
109
  * result code
@@ -127,10 +106,10 @@ class Hide_Error_Service extends Rule_Service implements IRule_Service {
127
  }
128
  }
129
  }
130
-
131
  return - 1;
132
  }
133
-
134
  /**
135
  * @param $config
136
  *
@@ -144,10 +123,10 @@ class Hide_Error_Service extends Rule_Service implements IRule_Service {
144
  return $key;
145
  }
146
  }
147
-
148
  return false;
149
  }
150
-
151
  public function revert() {
152
  $config_path = $this->retrieveWPConfigPath();
153
  //check if can write
@@ -156,10 +135,10 @@ class Hide_Error_Service extends Rule_Service implements IRule_Service {
156
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
157
  }
158
  $config = file( $config_path );
159
-
160
  }
161
-
162
  public function listen() {
163
-
164
  }
165
  }
12
  use WP_Defender\Module\Hardener\Rule_Service;
13
 
14
  class Hide_Error_Service extends Rule_Service implements IRule_Service {
15
+
16
  /**
17
  * @return bool
18
  */
19
  public function check() {
20
+ if ( defined( 'WP_DEBUG' ) && constant( 'WP_DEBUG' ) == false ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  return true;
 
 
22
  }
23
+ if ( defined( 'WP_DEBUG_DISPLAY' ) && constant( 'WP_DEBUG_DISPLAY' ) == false ) {
24
+ return true;
25
+ }
26
+
27
+ return false;
28
  }
29
+
30
  /**
31
  * Process to fix the wp-config base on scenario
32
  *
50
  }
51
  $config = Array_Helper::injectLine( $config, $hookline + 1, PHP_EOL . "define( 'WP_DEBUG', false );" . PHP_EOL );
52
  file_put_contents( $config_path, implode( null, $config ), LOCK_EX );
53
+
54
  return true;
55
  } else {
56
  //already somewhere
57
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Defender can't recognize your wp-config.php, please revert it to original state for further process.", "defender-security" ) );
58
  }
59
  }
60
+
61
  list( $value, $line ) = $info;
62
  if ( $value == 1 ) {
63
  if ( constant( 'WP_DEBUG_LOG' ) == true ) {
73
  $config[ $line ] = 'define( \'WP_DEBUG\', false );' . PHP_EOL;
74
  }
75
  file_put_contents( $config_path, implode( null, $config ), LOCK_EX );
76
+
77
  return true;
78
+
79
  } elseif ( $value == 0 ) {
80
  //debug already off
81
  //this is a rare case, debug is off, but error still showing up
82
  return new \WP_Error( 0, __( "WP_DEBUG get override somewhere, please check with your host provider", "defender-security" ) );
83
  }
84
  }
85
+
86
  /**
87
  * This will parse wpconfig lines and check if we do have any defined wp_debug
88
  * result code
106
  }
107
  }
108
  }
109
+
110
  return - 1;
111
  }
112
+
113
  /**
114
  * @param $config
115
  *
123
  return $key;
124
  }
125
  }
126
+
127
  return false;
128
  }
129
+
130
  public function revert() {
131
  $config_path = $this->retrieveWPConfigPath();
132
  //check if can write
135
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
136
  }
137
  $config = file( $config_path );
138
+
139
  }
140
+
141
  public function listen() {
142
+
143
  }
144
  }
app/module/hardener/component/hide-error.php CHANGED
@@ -2,6 +2,7 @@
2
  /**
3
  * Author: Hoang Ngo
4
  */
 
5
  namespace WP_Defender\Module\Hardener\Component;
6
 
7
  use Hammer\Helper\WP_Helper;
@@ -9,7 +10,7 @@ use WP_Defender\Module\Hardener\Model\Settings;
9
  use WP_Defender\Module\Hardener\Rule;
10
 
11
  class Hide_Error extends Rule {
12
- static $slug = 'hide_error';
13
  static $service;
14
 
15
  function getDescription() {
@@ -29,19 +30,28 @@ class Hide_Error extends Rule {
29
  return $stat;
30
  }
31
 
32
- function getSubDescription() {
 
 
 
 
 
33
  return __( "Error debugging is currently allowed.", "defender-security" );
34
  }
35
 
 
 
 
 
 
 
 
 
36
  public function getTitle() {
37
  return __( "Hide error reporting", "defender-security" );
38
  }
39
 
40
  function revert() {
41
- if ( ! $this->verifyNonce() ) {
42
- return;
43
- }
44
-
45
  $ret = $this->getService()->revert();
46
  if ( ! is_wp_error( $ret ) ) {
47
  Settings::instance()->addToIssues( self::$slug );
@@ -53,14 +63,11 @@ class Hide_Error extends Rule {
53
  }
54
 
55
  function addHooks() {
56
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
57
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
58
  }
59
 
60
  function process() {
61
- if ( ! $this->verifyNonce() ) {
62
- return;
63
- }
64
  $ret = $this->getService()->process();
65
  if ( ! is_wp_error( $ret ) ) {
66
  Settings::instance()->addToResolved( self::$slug );
2
  /**
3
  * Author: Hoang Ngo
4
  */
5
+
6
  namespace WP_Defender\Module\Hardener\Component;
7
 
8
  use Hammer\Helper\WP_Helper;
10
  use WP_Defender\Module\Hardener\Rule;
11
 
12
  class Hide_Error extends Rule {
13
+ static $slug = 'hide-error';
14
  static $service;
15
 
16
  function getDescription() {
30
  return $stat;
31
  }
32
 
33
+ /**
34
+ * This will return the short summary why this rule show up as issue
35
+ *
36
+ * @return string
37
+ */
38
+ function getErrorReason() {
39
  return __( "Error debugging is currently allowed.", "defender-security" );
40
  }
41
 
42
+ /**
43
+ * This will return a short summary to show why this rule works
44
+ * @return mixed
45
+ */
46
+ function getSuccessReason() {
47
+ return __( "You've disabled all error reporting, Houston will never report a problem.", "defender-security" );
48
+ }
49
+
50
  public function getTitle() {
51
  return __( "Hide error reporting", "defender-security" );
52
  }
53
 
54
  function revert() {
 
 
 
 
55
  $ret = $this->getService()->revert();
56
  if ( ! is_wp_error( $ret ) ) {
57
  Settings::instance()->addToIssues( self::$slug );
63
  }
64
 
65
  function addHooks() {
66
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
67
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
68
  }
69
 
70
  function process() {
 
 
 
71
  $ret = $this->getService()->process();
72
  if ( ! is_wp_error( $ret ) ) {
73
  Settings::instance()->addToResolved( self::$slug );
app/module/hardener/component/login-duration.php CHANGED
@@ -32,17 +32,32 @@ class Login_Duration extends Rule {
32
  $this->renderPartial( 'rules/login-duration' );
33
  }
34
 
35
- function getSubDescription() {
 
 
 
 
 
 
 
 
 
 
 
 
36
  $days = $this->getService()->getDuration();
37
 
38
  return sprintf( __( "Your current login duration is the default %d days.", "defender-security" ), $days );
39
  }
40
 
41
  /**
42
- * @return string
 
43
  */
44
- public function getTitle() {
45
- return __( "Manage Login Duration", "defender-security" );
 
 
46
  }
47
 
48
  /**
@@ -53,21 +68,17 @@ class Login_Duration extends Rule {
53
  }
54
 
55
  function addHooks() {
56
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
57
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
58
- $this->add_action( 'wp_login', 'login_action_handler', 9, 2 );
59
  if ( $this->check() ) {
60
- $this->add_filter( 'auth_cookie_expiration', 'cookie_duration', 10, 3 );
61
- $this->add_filter( 'login_message', 'login_message' );
62
- //$this->add_action( 'wp_loaded', 'check_login' );
63
  }
64
 
65
  }
66
 
67
  function revert() {
68
- if ( ! $this->verifyNonce() ) {
69
- return;
70
- }
71
  $settings = Settings::instance();
72
  $service = $this->getService();
73
  $ret = $service->revert();
@@ -81,11 +92,8 @@ class Login_Duration extends Rule {
81
  }
82
 
83
  function process() {
84
- if ( ! $this->verifyNonce() ) {
85
- return;
86
- }
87
  $service = $this->getService();
88
- $duration = HTTP_Helper::retrieve_post( 'duration' );
89
  if ( is_numeric( $duration ) && intval( $duration ) > 0 ) {
90
  $service->setDuration( $duration );
91
  $ret = $service->process();
@@ -117,86 +125,18 @@ class Login_Duration extends Rule {
117
  update_user_meta( $user->ID, 'last_login_time', $last_login_time );
118
  }
119
 
120
- /**
121
- * Check login of users
122
- * @deprecated since 2.1.3
123
- * We just need to alter the cookies time so wordpress will do the rest
124
- */
125
- function check_login() {
126
- $defender_logout = HTTP_Helper::retrieve_get( 'defender_logout', false );
127
- if ( is_user_logged_in() ) {
128
- $current_user = wp_get_current_user();
129
- $user_id = $current_user->ID;
130
- if ( ! $defender_logout ) {
131
- $current_time = current_time( 'mysql' );
132
- $last_login_time = get_user_meta( $user_id, 'last_login_time', true );
133
- $login_period = $this->getService()->getDuration( true );
134
- if ( $last_login_time ) {
135
- $current_time = strtotime( $current_time );
136
- $last_login_time = strtotime( $last_login_time );
137
- $diff = $current_time - $last_login_time;
138
- //Check if the current and login times are not the same
139
- //so we dont kick out someone who set it to 0
140
- if ( ( $current_time != $last_login_time ) && $diff > $login_period ) {
141
- $current_url = Utils::instance()->currentPageURL();
142
- $after_logout_payload = array( 'redirect_to' => $current_url, 'msg' => 'session_expired' );
143
- if ( is_multisite() ) {
144
- set_site_transient( 'defender_logout_payload', $after_logout_payload, 30 * 60 );
145
- }
146
- set_transient( 'defender_logout_payload', $after_logout_payload, 30 * 60 );
147
- $logout_url = add_query_arg( 'defender_logout', '1', site_url() );
148
- wp_safe_redirect( $logout_url );
149
- exit;
150
- }
151
- } else {
152
- //Incase the user already was logged in
153
- $last_login_time = current_time( 'mysql' );
154
- update_user_meta( $user_id, 'last_login_time', $last_login_time );
155
- }
156
- } else {
157
- delete_user_meta( $user_id, 'last_login_time' );
158
- wp_logout();
159
- $after_logout = HTTP_Helper::retrieve_get( 'after_logout', false );
160
- if ( $after_logout ) {
161
- $after_logout_url = esc_url( $after_logout );
162
- wp_safe_redirect( $after_logout_url );
163
- exit;
164
- }
165
- $login_url = wp_login_url();
166
- $logout_payload = ( is_multisite() ? get_site_transient( 'defender_logout_payload' ) : get_transient( 'defender_logout_payload' ) );
167
-
168
- $login_url = add_query_arg( array(
169
- 'redirect_to' => $logout_payload['redirect_to'],
170
- 'defender_login_message' => $logout_payload['msg'],
171
- ), $login_url );
172
- wp_safe_redirect( $login_url );
173
- exit;
174
- }
175
- } else if ( $defender_logout ) {
176
- $after_logout = HTTP_Helper::retrieve_get( 'after_logout', false );
177
- if ( $after_logout ) {
178
- $after_logout_url = esc_url( $after_logout );
179
- wp_safe_redirect( $after_logout_url );
180
- }
181
- $login_url = wp_login_url();
182
- $logout_payload = ( is_multisite() ? get_site_transient( 'defender_logout_payload' ) : get_transient( 'defender_logout_payload' ) );
183
-
184
- $login_url = add_query_arg( array(
185
- 'redirect_to' => $logout_payload['redirect_to'],
186
- 'defender_login_message' => $logout_payload['msg'],
187
- ), $login_url );
188
- wp_safe_redirect( $login_url );
189
- exit;
190
- }
191
  }
192
 
193
-
194
  /**
195
  * Handle the custom login message
196
  *
197
  */
198
  function login_message( $message = '' ) {
199
- $login_msg = HTTP_Helper::retrieve_get( 'defender_login_message', false );
200
  if ( $login_msg ) {
201
  $logout_msg = strip_tags( $login_msg );
202
  if ( $logout_msg == 'session_expired' ) {
32
  $this->renderPartial( 'rules/login-duration' );
33
  }
34
 
35
+ /**
36
+ * @return string
37
+ */
38
+ public function getTitle() {
39
+ return __( "Manage Login Duration", "defender-security" );
40
+ }
41
+
42
+ /**
43
+ * This will return the short summary why this rule show up as issue
44
+ *
45
+ * @return string
46
+ */
47
+ function getErrorReason() {
48
  $days = $this->getService()->getDuration();
49
 
50
  return sprintf( __( "Your current login duration is the default %d days.", "defender-security" ), $days );
51
  }
52
 
53
  /**
54
+ * This will return a short summary to show why this rule works
55
+ * @return mixed
56
  */
57
+ function getSuccessReason() {
58
+ $days = $this->getService()->getDuration();
59
+
60
+ return sprintf( __( "You've adjusted the default login duration to %d days.", "defender-security" ), $days );
61
  }
62
 
63
  /**
68
  }
69
 
70
  function addHooks() {
71
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
72
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
73
+ $this->addAction( 'wp_login', 'login_action_handler', 9, 2 );
74
  if ( $this->check() ) {
75
+ $this->addFilter( 'auth_cookie_expiration', 'cookie_duration', 10, 3 );
76
+ $this->addFilter( 'login_message', 'login_message' );
 
77
  }
78
 
79
  }
80
 
81
  function revert() {
 
 
 
82
  $settings = Settings::instance();
83
  $service = $this->getService();
84
  $ret = $service->revert();
92
  }
93
 
94
  function process() {
 
 
 
95
  $service = $this->getService();
96
+ $duration = HTTP_Helper::retrievePost( 'duration' );
97
  if ( is_numeric( $duration ) && intval( $duration ) > 0 ) {
98
  $service->setDuration( $duration );
99
  $ret = $service->process();
125
  update_user_meta( $user->ID, 'last_login_time', $last_login_time );
126
  }
127
 
128
+ public function getMiscData() {
129
+ return [
130
+ 'duration' => $this->getService()->getDuration()
131
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  }
133
 
 
134
  /**
135
  * Handle the custom login message
136
  *
137
  */
138
  function login_message( $message = '' ) {
139
+ $login_msg = HTTP_Helper::retrieveGet( 'defender_login_message', false );
140
  if ( $login_msg ) {
141
  $logout_msg = strip_tags( $login_msg );
142
  if ( $logout_msg == 'session_expired' ) {
app/module/hardener/component/php-version-service.php CHANGED
@@ -5,12 +5,13 @@
5
 
6
  namespace WP_Defender\Module\Hardener\Component;
7
 
 
8
  use WP_Defender\Module\Hardener\IRule_Service;
9
  use WP_Defender\Module\Hardener\Model\Settings;
10
  use WP_Defender\Module\Hardener\Rule_Service;
11
 
12
  class PHP_Version_Service extends Rule_Service implements IRule_Service {
13
-
14
  /**
15
  * @return bool
16
  */
@@ -19,54 +20,49 @@ class PHP_Version_Service extends Rule_Service implements IRule_Service {
19
  if ( version_compare( phpversion(), Settings::instance()->min_php_version, '<=' ) ) {
20
  return false;
21
  }
22
-
23
  return true;
24
  }
25
-
26
  public function process() {
27
-
28
  }
29
-
30
  public function revert() {
31
-
32
  }
33
-
34
  public function listen() {
35
-
36
  }
37
-
38
  protected function queryVersion() {
39
- $lastCheck = get_site_transient( 'defender_last_check_php_versions' );
40
- if ( ! $lastCheck || strtotime( '+24 hours', $lastCheck ) < time() ) {
41
- $html = wp_remote_get( 'http://php.net/supported-versions.php' );
42
- if ( is_wp_error( $html ) ) {
43
- delete_site_transient( 'defender_last_check_php_versions' );
44
-
45
- return false;
 
 
 
 
 
 
 
 
 
46
  }
47
- if ( class_exists( '\DOMDocument' ) ) {
48
- $dom = new \DOMDocument;
49
- libxml_use_internal_errors( true );
50
- $dom->loadHTML( $html['body'] );
51
- $finder = new \DOMXPath( $dom );
52
- $classname = "security";
53
- $securityNode = $finder->query( "//*[contains(@class, '$classname')]/td[1]/a" );
54
- $securityNode = $securityNode->item( 0 )->nodeValue;
55
- $classname = "stable";
56
- $lastStable = $finder->query( "//*[contains(@class, '$classname')][2]/td[1]/a" );;
57
- $lastStable = $lastStable->item( 0 )->nodeValue;
58
- $settings = Settings::instance();
59
- $settings->stable_php_version = $lastStable;
60
- $settings->min_php_version = $securityNode;
61
- $settings->save();
62
- set_site_transient( 'defender_last_check_php_versions', time(), 60 * 60 * 24 );
63
- } else {
64
- //do it manually
65
- $settings = Settings::instance();
66
- $settings->stable_php_version = 7.3;
67
- $settings->min_php_version = 7.1;
68
- $settings->save();
69
  }
70
  }
 
 
 
 
 
71
  }
72
  }
5
 
6
  namespace WP_Defender\Module\Hardener\Component;
7
 
8
+ use WP_Defender\Behavior\Utils;
9
  use WP_Defender\Module\Hardener\IRule_Service;
10
  use WP_Defender\Module\Hardener\Model\Settings;
11
  use WP_Defender\Module\Hardener\Rule_Service;
12
 
13
  class PHP_Version_Service extends Rule_Service implements IRule_Service {
14
+
15
  /**
16
  * @return bool
17
  */
20
  if ( version_compare( phpversion(), Settings::instance()->min_php_version, '<=' ) ) {
21
  return false;
22
  }
23
+
24
  return true;
25
  }
26
+
27
  public function process() {
28
+
29
  }
30
+
31
  public function revert() {
32
+
33
  }
34
+
35
  public function listen() {
36
+
37
  }
38
+
39
  protected function queryVersion() {
40
+ $infos = [
41
+ '7.2' => [ '30 Nov 2019', '30 Nov 2020' ],
42
+ '7.3' => [ '6 Dec 2020', '6 Dec 2021' ],
43
+ '7.4' => [ '28 Nov 2021', '28 Nov 2022' ]
44
+ ];
45
+ $minVersion = null;
46
+ $stableVersion = null;
47
+ foreach ( $infos as $php => $dates ) {
48
+ list( $active, $security ) = $dates;
49
+ //get the one still have security updates
50
+ if ( $minVersion == null && strtotime( $active ) < time() && strtotime( $security ) > time() ) {
51
+ $minVersion = $php;
52
+ }
53
+ //if no min available, we pick the current active
54
+ if ( $minVersion == null && strtotime( $active ) > time() ) {
55
+ $minVersion = $php;
56
  }
57
+ //pick the nearest the min version, we want stable, not features
58
+ if ( $stableVersion == null && $minVersion != null && version_compare( $php, $minVersion, '>' ) ) {
59
+ $stableVersion = $php;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
  }
62
+
63
+ $settings = Settings::instance();
64
+ $settings->stable_php_version = $stableVersion;
65
+ $settings->min_php_version = $minVersion;
66
+ $settings->save();
67
  }
68
  }
app/module/hardener/component/php-version.php CHANGED
@@ -9,19 +9,42 @@ use WP_Defender\Module\Hardener\Model\Settings;
9
  use WP_Defender\Module\Hardener\Rule;
10
 
11
  class PHP_Version extends Rule {
12
- static $slug = 'php_version';
13
  static $service;
14
 
15
  function getDescription() {
16
  $this->renderPartial( 'rules/php-version' );
17
  }
18
 
19
- function getSubDescription() {
 
 
 
 
 
20
  $settings = Settings::instance();
21
 
22
  return sprintf( __( "PHP versions older than %s are no longer supported. For security and stability we strongly recommend you upgrade your PHP version to version %s or newer as soon as possible. ", "defender-security" ), $settings->min_php_version, $settings->min_php_version );
23
  }
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  /**
26
  * @return bool
27
  */
9
  use WP_Defender\Module\Hardener\Rule;
10
 
11
  class PHP_Version extends Rule {
12
+ static $slug = 'php-version';
13
  static $service;
14
 
15
  function getDescription() {
16
  $this->renderPartial( 'rules/php-version' );
17
  }
18
 
19
+ /**
20
+ * This will return the short summary why this rule show up as issue
21
+ *
22
+ * @return string
23
+ */
24
+ function getErrorReason() {
25
  $settings = Settings::instance();
26
 
27
  return sprintf( __( "PHP versions older than %s are no longer supported. For security and stability we strongly recommend you upgrade your PHP version to version %s or newer as soon as possible. ", "defender-security" ), $settings->min_php_version, $settings->min_php_version );
28
  }
29
 
30
+ /**
31
+ * This will return a short summary to show why this rule works
32
+ * @return mixed
33
+ */
34
+ function getSuccessReason() {
35
+ return __( "You have the latest version of PHP installed, good stuff!", "defender-security" );
36
+ }
37
+
38
+ public function getMiscData() {
39
+ $settings = Settings::instance();
40
+
41
+ return [
42
+ 'min_php_version' => $settings->min_php_version,
43
+ 'stable_php_version' => $settings->stable_php_version,
44
+ 'php_version' => phpversion()
45
+ ];
46
+ }
47
+
48
  /**
49
  * @return bool
50
  */
app/module/hardener/component/prevent-enum-users-service.php ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use WP_Defender\Module\Hardener\IRule_Service;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule_Service;
11
+
12
+ class Prevent_Enum_Users_Service extends Rule_Service implements IRule_Service {
13
+ const CACHE_KEY = 'prevent_enum_users';
14
+
15
+ /**
16
+ * Check if current rule fixed or not
17
+ * @return bool
18
+ */
19
+ public function check() {
20
+ $flag = Settings::instance()->getDValues( Prevent_Enum_Users_Service::CACHE_KEY );
21
+ if ( $flag == 0 ) {
22
+ return false;
23
+ }
24
+
25
+ return true;
26
+ }
27
+
28
+ /**
29
+ * Process the rule
30
+ * @return bool|\WP_Error
31
+ */
32
+ public function process() {
33
+ Settings::instance()->setDValues( self::CACHE_KEY, 1 );
34
+
35
+ return true;
36
+ }
37
+
38
+ /**
39
+ * Revert if able
40
+ * @return bool|\WP_Error
41
+ */
42
+ public function revert() {
43
+ Settings::instance()->setDValues( self::CACHE_KEY, 0 );
44
+
45
+ return true;
46
+ }
47
+ }
app/module/hardener/component/prevent-enum-users.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use WP_Defender\Module\Hardener\Model\Settings;
9
+ use WP_Defender\Module\Hardener\Rule;
10
+
11
+ class Prevent_Enum_Users extends Rule {
12
+ static $slug = 'prevent-enum-users';
13
+ static $service;
14
+
15
+ /**
16
+ * Return this rule content, we will try to use renderPartial
17
+ *
18
+ * @return mixed
19
+ */
20
+ function getDescription() {
21
+
22
+ }
23
+
24
+ /**
25
+ * This will return the short summary why this rule show up as issue
26
+ *
27
+ * @return string
28
+ */
29
+ function getErrorReason() {
30
+ return __( "User enumeration is currently allowed.", "defender-security" );
31
+ }
32
+
33
+ /**
34
+ * This will return a short summary to show why this rule works
35
+ * @return mixed
36
+ */
37
+ function getSuccessReason() {
38
+ return __( "User enumeration is currently blocked, nice work!", "defender-security" );
39
+ }
40
+
41
+ /**
42
+ * @return mixed
43
+ */
44
+ function check() {
45
+ return $this->getService()->check();
46
+ }
47
+
48
+ /**
49
+ * implement the revert function
50
+ *
51
+ * @return mixed
52
+ */
53
+ function revert() {
54
+ $this->getService()->revert();
55
+ Settings::instance()->addToIssues( self::$slug );
56
+ }
57
+
58
+ /**
59
+ * implement the process function
60
+ * @return mixed
61
+ */
62
+ function process() {
63
+ $this->getService()->process();
64
+ Settings::instance()->addToResolved( self::$slug );
65
+ }
66
+
67
+ /**
68
+ * @return mixed
69
+ */
70
+ function getTitle() {
71
+ return __( "Prevent user enumeration", "defender-security" );
72
+ }
73
+
74
+ /**
75
+ * Return Service class
76
+ * @return Prevent_Enum_Users_Service
77
+ */
78
+ function getService() {
79
+ if ( self::$service == null ) {
80
+ self::$service = new Prevent_Enum_Users_Service();
81
+ }
82
+
83
+ return self::$service;
84
+ }
85
+
86
+ /**
87
+ * @return mixed
88
+ */
89
+ function addHooks() {
90
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
91
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
92
+ $flag = Settings::instance()->getDValues( Prevent_Enum_Users_Service::CACHE_KEY );
93
+ if ( php_sapi_name() == 'cli' ) {
94
+ //in cli, ignore this
95
+ $flag = 0;
96
+ }
97
+ if ( $flag == 1 ) {
98
+ if ( ! is_admin() ) {
99
+ // default URL format
100
+ if ( ! isset( $_SERVER['QUERY_STRING'] ) ) {
101
+ return;
102
+ }
103
+
104
+ if ( preg_match( '/author=([0-9]*)/i', $_SERVER['QUERY_STRING'] ) ) {
105
+ wp_die( __( 'Sorry, you are not allowed to access this page', "defender-security" ) );
106
+ }
107
+ $this->addFilter( 'redirect_canonical', 'checkEnum', 10, 2 );
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @param $redirect
114
+ * @param $request
115
+ *
116
+ * @return mixed
117
+ */
118
+ public function checkEnum( $redirect, $request ) {
119
+ if ( preg_match( '/\?author=([0-9]*)(\/*)/i', $request ) ) {
120
+ wp_die( __( 'Sorry, you are not allowed to access this page', "defender-security" ) );
121
+ }
122
+
123
+ return $redirect;
124
+ }
125
+ }
app/module/hardener/component/prevent-php-service.php CHANGED
@@ -13,7 +13,7 @@ use WP_Defender\Module\Hardener\IRule_Service;
13
  use WP_Defender\Module\Hardener\Rule_Service;
14
 
15
  class Prevent_PHP_Service extends Rule_Service implements IRule_Service {
16
-
17
  /**
18
  * @return bool
19
  */
@@ -22,35 +22,64 @@ class Prevent_PHP_Service extends Rule_Service implements IRule_Service {
22
  if ( $cache === null ) {
23
  //init upload dir and a php file
24
  Utils::instance()->getDefUploadDir();
25
- $url = WP_Helper::getUploadUrl();
26
- $url = $url . '/wp-defender/index.php';
27
- $ssl_verify = apply_filters( 'defender_ssl_verify', true ); //most hosts dont really have valid ssl or ssl still pending
28
- $status = wp_remote_head( $url, array( 'user-agent' => $_SERVER['HTTP_USER_AGENT'], 'timeout' => 10, 'sslverify' => $ssl_verify ) );
29
- if ( is_wp_error( $status ) ) {
30
- //General error
 
31
  return false;
32
- } else {
33
- if ( 200 == wp_remote_retrieve_response_code( $status ) ) {
34
- WP_Helper::getArrayCache()->set( 'Prevent_PHP_Service', false );
35
-
36
- return false;
37
- }
38
- WP_Helper::getArrayCache()->set( 'Prevent_PHP_Service', true );
39
-
40
- return true;
41
  }
 
 
 
42
  } else {
43
  return $cache;
44
  }
45
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
 
 
 
 
 
 
 
 
 
 
47
  /**
48
  * @return bool|\WP_Error
49
  */
50
  public function process() {
51
  return new \WP_Error( Error_Code::INVALID, __( "Process is not possible on your current server", "defender-security" ) );
52
  }
53
-
54
  /**
55
  * @return bool|\WP_Error
56
  */
13
  use WP_Defender\Module\Hardener\Rule_Service;
14
 
15
  class Prevent_PHP_Service extends Rule_Service implements IRule_Service {
16
+
17
  /**
18
  * @return bool
19
  */
22
  if ( $cache === null ) {
23
  //init upload dir and a php file
24
  Utils::instance()->getDefUploadDir();
25
+ $url = WP_Helper::getUploadUrl();
26
+ $url = $url . '/wp-defender/index.php';
27
+ $headers = $this->headRequest( $url, 'Prevent PHP Execution' );
28
+
29
+ if ( 200 == $headers['response_code'] ) {
30
+ WP_Helper::getArrayCache()->set( 'Prevent_PHP_Service', false );
31
+
32
  return false;
 
 
 
 
 
 
 
 
 
33
  }
34
+ WP_Helper::getArrayCache()->set( 'Prevent_PHP_Service', true );
35
+
36
+ return true;
37
  } else {
38
  return $cache;
39
  }
40
  }
41
+
42
+ /**
43
+ * Return the nginx rules use to put in site-enabled config files
44
+ * @return string
45
+ */
46
+ public function getNginxRules() {
47
+ if ( DIRECTORY_SEPARATOR == '\\' ) {
48
+ //Windows
49
+ $wp_includes = str_replace( ABSPATH, '', WPINC );
50
+ $wp_content = str_replace( ABSPATH, '', WP_CONTENT_DIR );
51
+ } else {
52
+ $wp_includes = str_replace( $_SERVER['DOCUMENT_ROOT'], '', ABSPATH . WPINC );
53
+ $wp_content = str_replace( $_SERVER['DOCUMENT_ROOT'], '', WP_CONTENT_DIR );
54
+ }
55
+
56
+ $rules = "# Stop php access except to needed files in wp-includes
57
+ location ~* ^$wp_includes/.*(?<!(js/tinymce/wp-tinymce))\.php$ {
58
+ internal; #internal allows ms-files.php rewrite in multisite to work
59
+ }
60
+
61
+ # Specifically locks down upload directories in case full wp-content rule below is skipped
62
+ location ~* /(?:uploads|files)/.*\.php$ {
63
+ deny all;
64
+ }
65
 
66
+ # Deny direct access to .php files in the /wp-content/ directory (including sub-folders).
67
+ # Note this can break some poorly coded plugins/themes, replace the plugin or remove this block if it causes trouble
68
+ location ~* ^$wp_content/.*\.php$ {
69
+ deny all;
70
+ }
71
+ ";
72
+
73
+ return $rules;
74
+ }
75
+
76
  /**
77
  * @return bool|\WP_Error
78
  */
79
  public function process() {
80
  return new \WP_Error( Error_Code::INVALID, __( "Process is not possible on your current server", "defender-security" ) );
81
  }
82
+
83
  /**
84
  * @return bool|\WP_Error
85
  */
app/module/hardener/component/prevent-php.php CHANGED
@@ -17,34 +17,54 @@ class Prevent_Php extends Rule {
17
  static $service;
18
  static $apache_service;
19
  static $iis_service;
20
-
21
  function getDescription() {
22
  $this->renderPartial( 'rules/prevent-php-executed' );
23
  }
24
-
25
- function getSubDescription() {
 
 
 
 
 
26
  return __( "PHP execution is currently allowed in all directories.", "defender-security" );
27
  }
28
-
 
 
 
 
 
 
 
 
29
  /**
30
  * @return bool|false|mixed|null
31
  */
32
  function check() {
33
  return $this->getService()->check();
34
  }
35
-
 
 
 
 
 
 
 
 
 
 
36
  /**
37
  * @return string|void
38
  */
39
  public function getTitle() {
40
  return __( "Prevent PHP execution", "defender-security" );
41
  }
42
-
43
-
44
  function revert() {
45
- if ( ! $this->verifyNonce() ) {
46
- return;
47
- }
48
  $settings = Settings::instance();
49
  $server = $settings->active_server;
50
  if ( in_array( $settings->active_server, array( 'apache', 'litespeed' ) ) ) {
@@ -61,6 +81,9 @@ class Prevent_Php extends Rule {
61
  $settings->saveExcludedFilePaths( array() );
62
  $settings->saveNewHtConfig( array() );
63
  }
 
 
 
64
  $settings->addToIssues( self::$slug );
65
  } else {
66
  wp_send_json_error( array(
@@ -68,25 +91,22 @@ class Prevent_Php extends Rule {
68
  ) );
69
  }
70
  }
71
-
72
  function addHooks() {
73
- $this->add_action( 'processingHardener' . self::$slug, 'process', 10, 2 );
74
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
75
- $this->add_action( 'processUpdate' . self::$slug, 'update', 10, 2 );
76
  }
77
-
78
  function process() {
79
- if ( ! $this->verifyNonce() ) {
80
- return;
81
- }
82
- $file_paths = HTTP_Helper::retrieve_post( 'file_paths' ); //File paths to ignore. Apache and litespeed mainly
83
  if ( $file_paths ) {
84
  $file_paths = sanitize_textarea_field( $file_paths );
85
  } else {
86
  $file_paths = '';
87
  }
88
- $server = HTTP_Helper::retrieve_post( 'current_server' ); //Current server
89
-
90
  if ( in_array( $server, array( 'apache', 'litespeed' ) ) ) {
91
  $service = $this->getApacheService();
92
  $service->setExcludeFilePaths( $file_paths ); //Set the paths
@@ -104,28 +124,31 @@ class Prevent_Php extends Rule {
104
  }
105
  $settings->setActiveServer( $server );
106
  $settings->addToResolved( self::$slug );
 
 
 
107
  } else {
108
  wp_send_json_error( array(
109
  'message' => $ret->get_error_message()
110
  ) );
111
  }
112
  }
113
-
114
  function update() {
115
  if ( ! $this->verifyNonce() ) {
116
  return;
117
  }
118
  $settings = Settings::instance();
119
-
120
- $file_paths = HTTP_Helper::retrieve_post( 'file_paths' ); //File paths to ignore. Apache and litespeed mainly
121
  if ( $file_paths ) {
122
  $file_paths = sanitize_textarea_field( $file_paths );
123
  } else {
124
  $file_paths = '';
125
  }
126
-
127
- $server = HTTP_Helper::retrieve_post( 'current_server' ); //Current server
128
-
129
  if ( in_array( $server, array( 'apache', 'litespeed' ) ) ) {
130
  $service = $this->getApacheService();
131
  $service->setHtConfig( $settings->getNewHtConfig() ); //Set the previous template
@@ -148,7 +171,7 @@ class Prevent_Php extends Rule {
148
  ) );
149
  }
150
  }
151
-
152
  /**
153
  * @return Prevent_PHP_Service
154
  */
@@ -156,10 +179,10 @@ class Prevent_Php extends Rule {
156
  if ( self::$service == null ) {
157
  self::$service = new Prevent_PHP_Service();
158
  }
159
-
160
  return self::$service;
161
  }
162
-
163
  /**
164
  * @return Apache_Service
165
  */
@@ -167,10 +190,10 @@ class Prevent_Php extends Rule {
167
  if ( self::$apache_service == null ) {
168
  self::$apache_service = new Apache_Service();
169
  }
170
-
171
  return self::$apache_service;
172
  }
173
-
174
  /**
175
  * @return Iis_Service
176
  */
@@ -178,7 +201,7 @@ class Prevent_Php extends Rule {
178
  if ( self::$iis_service == null ) {
179
  self::$iis_service = new Iis_Service();
180
  }
181
-
182
  return self::$iis_service;
183
  }
184
  }
17
  static $service;
18
  static $apache_service;
19
  static $iis_service;
20
+
21
  function getDescription() {
22
  $this->renderPartial( 'rules/prevent-php-executed' );
23
  }
24
+
25
+ /**
26
+ * This will return the short summary why this rule show up as issue
27
+ *
28
+ * @return string
29
+ */
30
+ function getErrorReason() {
31
  return __( "PHP execution is currently allowed in all directories.", "defender-security" );
32
  }
33
+
34
+ /**
35
+ * This will return a short summary to show why this rule works
36
+ * @return mixed
37
+ */
38
+ function getSuccessReason() {
39
+ return __( "You've disabled PHP execution, good stuff.", "defender-security" );
40
+ }
41
+
42
  /**
43
  * @return bool|false|mixed|null
44
  */
45
  function check() {
46
  return $this->getService()->check();
47
  }
48
+
49
+ public function getMiscData() {
50
+ $settings = Settings::instance();
51
+
52
+ return [
53
+ 'active_server' => $settings->active_server,
54
+ 'nginx_rules' => $this->getService()->getNginxRules(),
55
+ 'wp_content_dir' => WP_CONTENT_DIR
56
+ ];
57
+ }
58
+
59
  /**
60
  * @return string|void
61
  */
62
  public function getTitle() {
63
  return __( "Prevent PHP execution", "defender-security" );
64
  }
65
+
66
+
67
  function revert() {
 
 
 
68
  $settings = Settings::instance();
69
  $server = $settings->active_server;
70
  if ( in_array( $settings->active_server, array( 'apache', 'litespeed' ) ) ) {
81
  $settings->saveExcludedFilePaths( array() );
82
  $settings->saveNewHtConfig( array() );
83
  }
84
+ $url = WP_Helper::getUploadUrl();
85
+ $url = $url . '/wp-defender/index.php';
86
+ $this->getService()->clearHeadRequest( $url );
87
  $settings->addToIssues( self::$slug );
88
  } else {
89
  wp_send_json_error( array(
91
  ) );
92
  }
93
  }
94
+
95
  function addHooks() {
96
+ $this->addAction( 'processingHardener' . self::$slug, 'process', 10, 2 );
97
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
98
+ $this->addAction( 'processUpdate' . self::$slug, 'update', 10, 2 );
99
  }
100
+
101
  function process() {
102
+ $file_paths = HTTP_Helper::retrievePost( 'file_paths' ); //File paths to ignore. Apache and litespeed mainly
 
 
 
103
  if ( $file_paths ) {
104
  $file_paths = sanitize_textarea_field( $file_paths );
105
  } else {
106
  $file_paths = '';
107
  }
108
+ $server = HTTP_Helper::retrievePost( 'current_server' ); //Current server
109
+
110
  if ( in_array( $server, array( 'apache', 'litespeed' ) ) ) {
111
  $service = $this->getApacheService();
112
  $service->setExcludeFilePaths( $file_paths ); //Set the paths
124
  }
125
  $settings->setActiveServer( $server );
126
  $settings->addToResolved( self::$slug );
127
+ $url = WP_Helper::getUploadUrl();
128
+ $url = $url . '/wp-defender/index.php';
129
+ $this->getService()->clearHeadRequest( $url );
130
  } else {
131
  wp_send_json_error( array(
132
  'message' => $ret->get_error_message()
133
  ) );
134
  }
135
  }
136
+
137
  function update() {
138
  if ( ! $this->verifyNonce() ) {
139
  return;
140
  }
141
  $settings = Settings::instance();
142
+
143
+ $file_paths = HTTP_Helper::retrievePost( 'file_paths' ); //File paths to ignore. Apache and litespeed mainly
144
  if ( $file_paths ) {
145
  $file_paths = sanitize_textarea_field( $file_paths );
146
  } else {
147
  $file_paths = '';
148
  }
149
+
150
+ $server = HTTP_Helper::retrievePost( 'current_server' ); //Current server
151
+
152
  if ( in_array( $server, array( 'apache', 'litespeed' ) ) ) {
153
  $service = $this->getApacheService();
154
  $service->setHtConfig( $settings->getNewHtConfig() ); //Set the previous template
171
  ) );
172
  }
173
  }
174
+
175
  /**
176
  * @return Prevent_PHP_Service
177
  */
179
  if ( self::$service == null ) {
180
  self::$service = new Prevent_PHP_Service();
181
  }
182
+
183
  return self::$service;
184
  }
185
+
186
  /**
187
  * @return Apache_Service
188
  */
190
  if ( self::$apache_service == null ) {
191
  self::$apache_service = new Apache_Service();
192
  }
193
+
194
  return self::$apache_service;
195
  }
196
+
197
  /**
198
  * @return Iis_Service
199
  */
201
  if ( self::$iis_service == null ) {
202
  self::$iis_service = new Iis_Service();
203
  }
204
+
205
  return self::$iis_service;
206
  }
207
  }
app/module/hardener/component/protect-information-service.php CHANGED
@@ -12,32 +12,29 @@ use WP_Defender\Module\Hardener\IRule_Service;
12
  use WP_Defender\Module\Hardener\Rule_Service;
13
 
14
  class Protect_Information_Service extends Rule_Service implements IRule_Service {
15
-
16
  /**
17
  * @return bool
18
  */
19
  public function check() {
20
  $cache = WP_Helper::getArrayCache()->get( 'Protect_Information_Service', null );
21
  if ( $cache === null ) {
22
- $url = wp_defender()->getPluginUrl() . 'changelog.txt';
23
- $ssl_verify = apply_filters( 'defender_ssl_verify', true ); //most hosts dont really have valid ssl or ssl still pending
24
- $status = wp_remote_head( $url, array(
25
- 'user-agent' => $_SERVER['HTTP_USER_AGENT'],
26
- 'sslverify' => $ssl_verify
27
- ) );
28
- if ( 200 == wp_remote_retrieve_response_code( $status ) ) {
29
  WP_Helper::getArrayCache()->set( 'Protect_Information_Service', false );
30
-
31
  return false;
32
  }
33
  WP_Helper::getArrayCache()->set( 'Protect_Information_Service', true );
34
-
35
  return true;
36
  } else {
37
  return $cache;
38
  }
39
  }
40
-
41
  /**
42
  * @return bool|\WP_Error
43
  */
@@ -70,10 +67,12 @@ class Protect_Information_Service extends Rule_Service implements IRule_Service
70
  $htConfig = array_merge( $htConfig, $rules );
71
  file_put_contents( $htPath, implode( PHP_EOL, $htConfig ), LOCK_EX );
72
  }
73
-
 
 
74
  return true;
75
  }
76
-
77
  /**
78
  * @return bool|\WP_Error
79
  */
@@ -87,7 +86,7 @@ class Protect_Information_Service extends Rule_Service implements IRule_Service
87
  }
88
  $htConfig = file_get_contents( $htPath );
89
  $rules = $this->apache_rule();
90
-
91
  preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $htConfig, $matches );
92
  if ( is_array( $matches ) && count( $matches ) > 0 ) {
93
  $htConfig = str_replace( implode( '', $matches[0] ), '', $htConfig );
@@ -96,14 +95,48 @@ class Protect_Information_Service extends Rule_Service implements IRule_Service
96
  }
97
  $htConfig = trim( $htConfig );
98
  file_put_contents( $htPath, $htConfig, LOCK_EX );
99
-
 
 
100
  return true;
101
  } else {
102
  //Other servers we cant revert
103
  return new \WP_Error( Error_Code::INVALID, __( "Revert is not possible on your current server", "defender-security" ) );
104
  }
105
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
 
 
 
 
 
 
 
 
 
107
  /**
108
  * Get Apache rule depending on the version
109
  *
@@ -141,7 +174,7 @@ class Protect_Information_Service extends Rule_Service implements IRule_Service
141
  '## WP Defender - End ##'
142
  );
143
  }
144
-
145
  return $rules;
146
  }
147
  }
12
  use WP_Defender\Module\Hardener\Rule_Service;
13
 
14
  class Protect_Information_Service extends Rule_Service implements IRule_Service {
15
+
16
  /**
17
  * @return bool
18
  */
19
  public function check() {
20
  $cache = WP_Helper::getArrayCache()->get( 'Protect_Information_Service', null );
21
  if ( $cache === null ) {
22
+ $url = wp_defender()->getPluginUrl() . 'changelog.txt';
23
+ $headers = $this->headRequest( $url, 'Protect Information' );
24
+
25
+ if ( 200 == $headers['response_code'] ) {
 
 
 
26
  WP_Helper::getArrayCache()->set( 'Protect_Information_Service', false );
27
+
28
  return false;
29
  }
30
  WP_Helper::getArrayCache()->set( 'Protect_Information_Service', true );
31
+
32
  return true;
33
  } else {
34
  return $cache;
35
  }
36
  }
37
+
38
  /**
39
  * @return bool|\WP_Error
40
  */
67
  $htConfig = array_merge( $htConfig, $rules );
68
  file_put_contents( $htPath, implode( PHP_EOL, $htConfig ), LOCK_EX );
69
  }
70
+ $url = wp_defender()->getPluginUrl() . 'changelog.txt';
71
+ $this->clearHeadRequest( $url );
72
+
73
  return true;
74
  }
75
+
76
  /**
77
  * @return bool|\WP_Error
78
  */
86
  }
87
  $htConfig = file_get_contents( $htPath );
88
  $rules = $this->apache_rule();
89
+
90
  preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $htConfig, $matches );
91
  if ( is_array( $matches ) && count( $matches ) > 0 ) {
92
  $htConfig = str_replace( implode( '', $matches[0] ), '', $htConfig );
95
  }
96
  $htConfig = trim( $htConfig );
97
  file_put_contents( $htPath, $htConfig, LOCK_EX );
98
+ $url = wp_defender()->getPluginUrl() . 'changelog.txt';
99
+ $this->clearHeadRequest( $url );
100
+
101
  return true;
102
  } else {
103
  //Other servers we cant revert
104
  return new \WP_Error( Error_Code::INVALID, __( "Revert is not possible on your current server", "defender-security" ) );
105
  }
106
  }
107
+
108
+ public function getNginxRules() {
109
+ if ( DIRECTORY_SEPARATOR == '\\' ) {
110
+ //Windows
111
+ $wp_includes = str_replace( ABSPATH, '', WPINC );
112
+ $wp_content = str_replace( ABSPATH, '', WP_CONTENT_DIR );
113
+ } else {
114
+ $wp_includes = str_replace( $_SERVER['DOCUMENT_ROOT'], '', ABSPATH . WPINC );
115
+ $wp_content = str_replace( $_SERVER['DOCUMENT_ROOT'], '', WP_CONTENT_DIR );
116
+ }
117
+
118
+ $rules = "# Turn off directory indexing
119
+ autoindex off;
120
+
121
+ # Deny access to htaccess and other hidden files
122
+ location ~ /\. {
123
+ deny all;
124
+ }
125
+
126
+ # Deny access to wp-config.php file
127
+ location = /wp-config.php {
128
+ deny all;
129
+ }
130
 
131
+ # Deny access to revealing or potentially dangerous files in the /wp-content/ directory (including sub-folders)
132
+ location ~* ^$wp_content/.*\.(txt|md|exe|sh|bak|inc|pot|po|mo|log|sql)$ {
133
+ deny all;
134
+ }
135
+ ";
136
+
137
+ return $rules;
138
+ }
139
+
140
  /**
141
  * Get Apache rule depending on the version
142
  *
174
  '## WP Defender - End ##'
175
  );
176
  }
177
+
178
  return $rules;
179
  }
180
  }
app/module/hardener/component/protect-information.php CHANGED
@@ -17,10 +17,32 @@ class Protect_Information extends Rule {
17
  $this->renderPartial( 'rules/protect-information' );
18
  }
19
 
20
- function getSubDescription() {
 
 
 
 
 
21
  return __( "You don't have information disclosure protection active.", "defender-security" );
22
  }
23
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
  /**
25
  * @return bool|false|mixed|null
26
  */
@@ -33,9 +55,6 @@ class Protect_Information extends Rule {
33
  }
34
 
35
  function revert() {
36
- if ( ! $this->verifyNonce() ) {
37
- return;
38
- }
39
  $ret = $this->getService()->revert();
40
  if ( ! is_wp_error( $ret ) ) {
41
  Settings::instance()->addToIssues( self::$slug );
@@ -47,14 +66,11 @@ class Protect_Information extends Rule {
47
  }
48
 
49
  function addHooks() {
50
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
51
- $this->add_action( 'processRevert' . self::$slug, 'revert' );
52
  }
53
 
54
  function process() {
55
- if ( ! $this->verifyNonce() ) {
56
- return;
57
- }
58
  $ret = $this->getService()->process();
59
  if ( ! is_wp_error( $ret ) ) {
60
  Settings::instance()->addToResolved( self::$slug );
17
  $this->renderPartial( 'rules/protect-information' );
18
  }
19
 
20
+ /**
21
+ * This will return the short summary why this rule show up as issue
22
+ *
23
+ * @return string
24
+ */
25
+ function getErrorReason() {
26
  return __( "You don't have information disclosure protection active.", "defender-security" );
27
  }
28
 
29
+ /**
30
+ * This will return a short summary to show why this rule works
31
+ * @return mixed
32
+ */
33
+ function getSuccessReason() {
34
+ return __( "You've automatically enabled information disclosure protection.", "defender-security" );
35
+ }
36
+
37
+ public function getMiscData() {
38
+ $settings = Settings::instance();
39
+
40
+ return [
41
+ 'active_server' => $settings->active_server,
42
+ 'nginx_rules' => $this->getService()->getNginxRules(),
43
+ ];
44
+ }
45
+
46
  /**
47
  * @return bool|false|mixed|null
48
  */
55
  }
56
 
57
  function revert() {
 
 
 
58
  $ret = $this->getService()->revert();
59
  if ( ! is_wp_error( $ret ) ) {
60
  Settings::instance()->addToIssues( self::$slug );
66
  }
67
 
68
  function addHooks() {
69
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
70
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
71
  }
72
 
73
  function process() {
 
 
 
74
  $ret = $this->getService()->process();
75
  if ( ! is_wp_error( $ret ) ) {
76
  Settings::instance()->addToResolved( self::$slug );
app/module/hardener/component/security-key-service.php CHANGED
@@ -14,29 +14,39 @@ use WP_Defender\Module\Hardener\Rule_Service;
14
  class Security_Key_Service extends Rule_Service implements IRule_Service {
15
  const CACHE_KEY = 'security_key';
16
  const DEFAULT_DAYS = '60 days';
17
-
18
  /**
19
  * @return bool
20
  */
21
  public function check() {
22
- $last = Settings::instance()->getDValues( self::CACHE_KEY );
23
  $reminder = Settings::instance()->getDValues( 'securityReminderDate' );
24
- if ( $last ) {
25
- if ( $reminder == null ) {
26
- $reminder = strtotime( '+' . self::DEFAULT_DAYS, $last );
 
 
 
 
 
 
 
27
  }
 
 
 
 
 
28
  if ( $reminder < time() ) {
29
  return false;
30
  }
31
-
32
- return true;
33
- } elseif ( $reminder != null && $reminder < time() ) {
34
  return true;
35
  }
36
-
37
  return false;
38
  }
39
-
40
  /**
41
  * @return bool|\WP_Error
42
  */
@@ -47,16 +57,16 @@ class Security_Key_Service extends Rule_Service implements IRule_Service {
47
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
48
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
49
  }
50
-
51
  return $this->generateSalt( $config_path );
52
-
53
  }
54
-
55
  public function revert() {
56
  Settings::instance()->setDValues( self::CACHE_KEY, null );
57
  Settings::instance()->setDValues( 'securityReminderDate', null );
58
  }
59
-
60
  /**
61
  * This function will check & generate new salt if needed
62
  * Cover case
@@ -92,9 +102,9 @@ class Security_Key_Service extends Rule_Service implements IRule_Service {
92
  //replace
93
  foreach ( $config as $index => $line ) {
94
  $line = trim( $line );
95
-
96
  $pattern = '/^define\(\s*(\'|\")' . $key . '(\'|\")\s*,\s*(\'|\")' . preg_quote( $old_salt, '/' ) . '(\'|\")\s*\)/';
97
-
98
  if ( preg_match( $pattern, $line ) === 1 ) {
99
  //match
100
  $new_line = "define( '$key', '$salt' );" . PHP_EOL;
@@ -116,12 +126,12 @@ class Security_Key_Service extends Rule_Service implements IRule_Service {
116
  //for any reason we missing a security key, this mean wp-config altered by 3rd party, halt
117
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Defender can't recognize your wp-config.php, please revert it to original state for further process.", "defender-security" ) );
118
  }
119
-
120
  //we already check for perm above, no need to check again
121
  //lock the file
122
  file_put_contents( $path, implode( '', $config ), LOCK_EX );
123
  Settings::instance()->setDValues( self::CACHE_KEY, time() );
124
-
125
  return true;
126
  }
127
  }
14
  class Security_Key_Service extends Rule_Service implements IRule_Service {
15
  const CACHE_KEY = 'security_key';
16
  const DEFAULT_DAYS = '60 days';
17
+
18
  /**
19
  * @return bool
20
  */
21
  public function check() {
22
+ $last = Settings::instance()->getDValues( self::CACHE_KEY );
23
  $reminder = Settings::instance()->getDValues( 'securityReminderDate' );
24
+ $interval = Settings::instance()->getDValues( 'securityReminderDuration' );
25
+ if ( $interval == null ) {
26
+ $interval = self::DEFAULT_DAYS;
27
+ }
28
+ if ( ! $last ) {
29
+ if ( file_exists( ABSPATH . 'wp-config' ) ) {
30
+ $last = filemtime( ABSPATH . 'wp-config' );
31
+ } else {
32
+ //looks like the site is new instance, we should get the date from files
33
+ $last = filemtime( ABSPATH . WPINC . '/general-template.php' );
34
  }
35
+ }
36
+
37
+ if ( $last ) {
38
+ //date we should remind user
39
+ $reminder = strtotime( '+' . $interval, $last );
40
  if ( $reminder < time() ) {
41
  return false;
42
  }
43
+
 
 
44
  return true;
45
  }
46
+
47
  return false;
48
  }
49
+
50
  /**
51
  * @return bool|\WP_Error
52
  */
57
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
58
  sprintf( __( "The file %s is not writable", "defender-security" ), $config_path ) );
59
  }
60
+
61
  return $this->generateSalt( $config_path );
62
+
63
  }
64
+
65
  public function revert() {
66
  Settings::instance()->setDValues( self::CACHE_KEY, null );
67
  Settings::instance()->setDValues( 'securityReminderDate', null );
68
  }
69
+
70
  /**
71
  * This function will check & generate new salt if needed
72
  * Cover case
102
  //replace
103
  foreach ( $config as $index => $line ) {
104
  $line = trim( $line );
105
+
106
  $pattern = '/^define\(\s*(\'|\")' . $key . '(\'|\")\s*,\s*(\'|\")' . preg_quote( $old_salt, '/' ) . '(\'|\")\s*\)/';
107
+
108
  if ( preg_match( $pattern, $line ) === 1 ) {
109
  //match
110
  $new_line = "define( '$key', '$salt' );" . PHP_EOL;
126
  //for any reason we missing a security key, this mean wp-config altered by 3rd party, halt
127
  return new \WP_Error( Error_Code::UNKNOWN_WPCONFIG, __( "Defender can't recognize your wp-config.php, please revert it to original state for further process.", "defender-security" ) );
128
  }
129
+
130
  //we already check for perm above, no need to check again
131
  //lock the file
132
  file_put_contents( $path, implode( '', $config ), LOCK_EX );
133
  Settings::instance()->setDValues( self::CACHE_KEY, time() );
134
+
135
  return true;
136
  }
137
  }
app/module/hardener/component/security-key.php CHANGED
@@ -7,14 +7,17 @@ namespace WP_Defender\Module\Hardener\Component;
7
 
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\WP_Helper;
 
10
  use WP_Defender\Behavior\Utils;
 
 
11
  use WP_Defender\Module\Hardener\Model\Settings;
12
  use WP_Defender\Module\Hardener\Rule;
13
 
14
  class Security_Key extends Rule {
15
- static $slug = 'security_key';
16
  static $service;
17
-
18
  function getDescription() {
19
  $settings = Settings::instance();
20
  $time = $settings->getDValues( Security_Key_Service::CACHE_KEY );
@@ -27,55 +30,110 @@ class Security_Key extends Rule {
27
  } else {
28
  $daysAgo = __( "unknown", "defender-security" );
29
  }
30
-
31
  $this->renderPartial( 'rules/security-key', array(
32
  'interval' => $interval,
33
  'daysAgo' => $daysAgo
34
  ) );
35
  }
36
-
37
- function getSubDescription() {
38
- return sprintf( __( "Your current security keys are %s days old. Time to update them!", "defender-security" ), $this->check() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  }
40
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  /**
42
  * @return string
43
  */
44
  function getTitle() {
45
  return __( "Update old security keys", "defender-security" );
46
  }
47
-
48
  function check() {
49
  return $this->getService()->check();
50
  }
51
-
52
  function addHooks() {
53
- $this->add_action( 'processingHardener' . self::$slug, 'process' );
54
- $this->add_ajax_action( 'updateSecurityReminder', 'updateSecurityReminder' );
 
 
 
 
 
55
  }
56
-
 
 
 
 
 
 
 
 
 
 
 
 
57
  public function updateSecurityReminder() {
58
  if ( ! Utils::instance()->checkPermission() ) {
59
  return;
60
  }
61
-
62
- $reminder = HTTP_Helper::retrieve_post( 'remind_date', null );
 
63
  if ( $reminder ) {
64
  $settings = Settings::instance();
65
  $settings->setDValues( 'securityReminderDuration', $reminder );
66
  $settings->setDValues( 'securityReminderDate', strtotime( '+' . $reminder, current_time( 'timestamp' ) ) );
67
- die;
68
  }
69
  }
70
-
71
  function revert() {
72
-
73
  }
74
-
75
  function process() {
76
- if ( ! $this->verifyNonce() ) {
77
- return;
78
- }
79
  $ret = $this->getService()->process();
80
  if ( is_wp_error( $ret ) ) {
81
  wp_send_json_error( array(
@@ -83,14 +141,18 @@ class Security_Key extends Rule {
83
  ) );
84
  } else {
85
  Settings::instance()->addToResolved( self::$slug );
 
 
 
 
86
  wp_send_json_success( array(
87
- 'message' => sprintf( __( 'All key salts have been regenerated. You will now need to <a href="%s"><strong>re-login</strong></a>.<br/>This will auto reload after <span class="hardener-timer">10</span> seconds.', "defender-security" ), wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) ) ),
88
- 'reload' => 10,
89
- 'url' => wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) )
90
  ) );
91
  }
92
  }
93
-
94
  /**
95
  * @return Security_Key_Service
96
  */
@@ -98,7 +160,7 @@ class Security_Key extends Rule {
98
  if ( self::$service == null ) {
99
  self::$service = new Security_Key_Service();
100
  }
101
-
102
  return self::$service;
103
  }
104
  }
7
 
8
  use Hammer\Helper\HTTP_Helper;
9
  use Hammer\Helper\WP_Helper;
10
+ use WP_Defender\Behavior\Endpoint;
11
  use WP_Defender\Behavior\Utils;
12
+ use WP_Defender\Module\Advanced_Tools\Component\Mask_Api;
13
+ use WP_Defender\Module\Hardener;
14
  use WP_Defender\Module\Hardener\Model\Settings;
15
  use WP_Defender\Module\Hardener\Rule;
16
 
17
  class Security_Key extends Rule {
18
+ static $slug = 'security-key';
19
  static $service;
20
+
21
  function getDescription() {
22
  $settings = Settings::instance();
23
  $time = $settings->getDValues( Security_Key_Service::CACHE_KEY );
30
  } else {
31
  $daysAgo = __( "unknown", "defender-security" );
32
  }
33
+
34
  $this->renderPartial( 'rules/security-key', array(
35
  'interval' => $interval,
36
  'daysAgo' => $daysAgo
37
  ) );
38
  }
39
+
40
+ private function calculateDaysApplied() {
41
+ $settings = Settings::instance();
42
+ $time = $settings->getDValues( Security_Key_Service::CACHE_KEY );
43
+ $timestamp = filemtime( ABSPATH . '/' . WPINC . '/general-template.php' );
44
+ $interval = $settings->getDValues( 'securityReminderDuration' );
45
+ if ( ! $interval ) {
46
+ $interval = Security_Key_Service::DEFAULT_DAYS;
47
+ }
48
+ $daysAgo = __( "unknown", "defender-security" );
49
+ if ( $time ) {
50
+ $daysAgo = ( time() - $time ) / ( 60 * 60 * 24 );
51
+ } elseif ( $timestamp != false ) {
52
+ $daysAgo = ( time() - $timestamp ) / ( 60 * 60 * 24 );
53
+ }
54
+ if ( $daysAgo != __( "unknown", "defender-security" ) ) {
55
+ $daysAgo = round( $daysAgo );
56
+ if ( $daysAgo == 0 ) {
57
+ $daysAgo = 1;
58
+ }
59
+ }
60
+
61
+ return $daysAgo;
62
  }
63
+
64
+ /**
65
+ * This will return the short summary why this rule show up as issue
66
+ *
67
+ * @return string
68
+ */
69
+ function getErrorReason() {
70
+ if ( $this->calculateDaysApplied() == __( "unknown", "defender-security" ) ) {
71
+ return __( "We can’t tell how old your security keys are, perhaps it’s time to update them?", "defender-security" );
72
+ }
73
+
74
+ return sprintf( __( "Your current security keys are %s days old. Time to update them!", "defender-security" ), $this->calculateDaysApplied() );
75
+ }
76
+
77
+ /**
78
+ * This will return a short summary to show why this rule works
79
+ * @return mixed
80
+ */
81
+ function getSuccessReason() {
82
+ return sprintf( __( "Your security keys are less than %s days old, nice work.", "defender-security" ), $this->calculateDaysApplied() );
83
+ }
84
+
85
  /**
86
  * @return string
87
  */
88
  function getTitle() {
89
  return __( "Update old security keys", "defender-security" );
90
  }
91
+
92
  function check() {
93
  return $this->getService()->check();
94
  }
95
+
96
  function addHooks() {
97
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
98
+ $namespace = 'wp-defender/v1';
99
+ $namespace .= '/tweaks';
100
+ $routes = [
101
+ $namespace . '/updateSecurityReminder' => 'updateSecurityReminder',
102
+ ];
103
+ $this->registerEndpoints( $routes, Hardener::getClassName() );
104
  }
105
+
106
+ public function getMiscData() {
107
+ $settings = Settings::instance();
108
+ $reminder = $settings->getDValues( 'securityReminderDuration' );
109
+ if ( $reminder == null ) {
110
+ $reminder = Security_Key_Service::DEFAULT_DAYS;
111
+ }
112
+
113
+ return [
114
+ 'reminder' => $reminder,
115
+ ];
116
+ }
117
+
118
  public function updateSecurityReminder() {
119
  if ( ! Utils::instance()->checkPermission() ) {
120
  return;
121
  }
122
+
123
+ $reminder = HTTP_Helper::retrievePost( 'remind_date', null );
124
+
125
  if ( $reminder ) {
126
  $settings = Settings::instance();
127
  $settings->setDValues( 'securityReminderDuration', $reminder );
128
  $settings->setDValues( 'securityReminderDate', strtotime( '+' . $reminder, current_time( 'timestamp' ) ) );
 
129
  }
130
  }
131
+
132
  function revert() {
133
+
134
  }
135
+
136
  function process() {
 
 
 
137
  $ret = $this->getService()->process();
138
  if ( is_wp_error( $ret ) ) {
139
  wp_send_json_error( array(
141
  ) );
142
  } else {
143
  Settings::instance()->addToResolved( self::$slug );
144
+ $url = wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) );
145
+ if ( Mask_Api::isEnabled() ) {
146
+ $url = Mask_Api::getNewLoginUrl();
147
+ }
148
  wp_send_json_success( array(
149
+ 'message' => sprintf( __( 'All key salts have been regenerated. You will now need to <a href="%s"><strong>re-login</strong></a>.<br/>This will auto reload after <span class="hardener-timer">3</span> seconds.', "defender-security" ), wp_login_url( network_admin_url( 'admin.php?page=wdf-hardener' ) ) ),
150
+ 'reload' => 3,
151
+ 'url' => $url
152
  ) );
153
  }
154
  }
155
+
156
  /**
157
  * @return Security_Key_Service
158
  */
160
  if ( self::$service == null ) {
161
  self::$service = new Security_Key_Service();
162
  }
163
+
164
  return self::$service;
165
  }
166
  }
app/module/hardener/component/servers/apache-service.php CHANGED
@@ -11,28 +11,40 @@ use WP_Defender\Module\Hardener\IRule_Service;
11
  use WP_Defender\Module\Hardener\Rule_Service;
12
 
13
  class Apache_Service extends Rule_Service implements IRule_Service {
14
-
15
  /**
16
  * Exclude file paths
17
  *
18
  * @var array|bool|mixed
19
  */
20
  private $exclude_file_paths = array();
21
-
22
  /**
23
  * New htaccess file
24
  *
25
  * @var array|bool|mixed
26
  */
27
  private $new_htconfig = array();
28
-
 
 
 
 
 
 
 
 
 
 
 
 
29
  /**
30
  * @return bool
31
  */
32
  public function check() {
33
  return true;
34
  }
35
-
36
  /**
37
  * @return bool|\WP_Error
38
  */
@@ -41,286 +53,136 @@ class Apache_Service extends Rule_Service implements IRule_Service {
41
  if ( is_wp_error( $ret ) ) {
42
  return $ret;
43
  }
44
-
45
  $ret = $this->protectIncludesDir();
46
  if ( is_wp_error( $ret ) ) {
47
  return $ret;
48
  }
49
-
50
  $ret = $this->protectUploadsDir();
51
  if ( is_wp_error( $ret ) ) {
52
  return $ret;
53
  }
54
-
55
  return true;
56
  }
57
-
58
- /**
59
- * @return bool|\WP_Error
60
- */
61
- private function protectIncludesDir() {
62
- $htPath = ABSPATH . WPINC . '/' . '.htaccess';
63
-
64
- if ( ! is_file( $htPath ) ) {
65
- if ( file_put_contents( $htPath, '', LOCK_EX ) === false ) {
66
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
67
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
68
  }
69
- } elseif ( ! is_writeable( $htPath ) ) {
70
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
71
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
72
  }
73
- $htConfig = file( $htPath );
74
- $deny = $this->generateHtAccessRule( false );
75
- $allow = $this->generateHtAccessRule( true );
76
- $default = array(
77
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
78
- '<Files *.php>' . PHP_EOL .
79
- $deny .
80
- '</Files>' . PHP_EOL,
81
- '<Files wp-tinymce.php>' . PHP_EOL .
82
- $allow .
83
- '</Files>' . PHP_EOL,
84
- '<Files ms-files.php>' . PHP_EOL .
85
- $allow .
86
- '</Files>' . PHP_EOL,
87
- '## WP Defender - End ##' . PHP_EOL
88
- );
89
- /*$status = wp_remote_head( network_site_url() . 'wp-includes', array( 'user-agent' => $_SERVER['HTTP_USER_AGENT'] ) );
90
- if ( 200 == wp_remote_retrieve_response_code( $status ) ) {
91
- $default[] = 'Options -Indexes' . PHP_EOL;
92
- }*/
93
- $containsSearch = array_diff( $default, $htConfig );
94
- if ( count( $containsSearch ) == 0 || ( count( $containsSearch ) == count( $default ) ) ) {
95
- //append this
96
- $htConfig = array_merge( $htConfig, array( implode( '', $default ) ) );
97
- file_put_contents( $htPath, implode( '', $htConfig ), LOCK_EX );
98
- } elseif ( count( $containsSearch ) != count( $htConfig ) ) {
99
- //corrupt
100
- $htContent = file_get_contents( $htPath );
101
- preg_match( '/## WP Defender(.*?)## WP Defender - End ##/s', $htContent, $matches );
102
- if ( count( $matches ) ) {
103
- //remove the whole parts as it partial done
104
- $htContent = str_replace( $matches[0], '', $htContent );
105
- $htConfig = explode( PHP_EOL, $htContent );
106
- $htConfig = array_merge( $htConfig, array( implode( '', $default ) ) );
107
- file_put_contents( $htPath, implode( '', $htConfig ), LOCK_EX );
108
  }
109
  }
110
-
111
- return true;
112
  }
113
-
114
- /**
115
- * @return bool|\WP_Error
116
- */
117
- private function protectContentDir() {
118
- $htPath = WP_CONTENT_DIR . '/' . '.htaccess';
119
- if ( ! file_exists( $htPath ) ) {
120
- if ( file_put_contents( $htPath, '', LOCK_EX ) === false ) {
121
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
122
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
123
  }
124
- } elseif ( ! is_writeable( $htPath ) ) {
125
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
126
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
127
- }
128
- $htConfig = file( $htPath );
129
- $deny = $this->generateHtAccessRule( false );
130
- $allow = $this->generateHtAccessRule( true );
131
- $default = array(
132
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
133
- '<Files *.php>' . PHP_EOL .
134
- $deny .
135
- '</Files>' . PHP_EOL,
136
- '## WP Defender - End ##' . PHP_EOL
137
- );
138
-
139
- if ( ! empty( $this->exclude_file_paths ) ) {
140
-
141
- $custom_exclude = array();
142
-
143
- foreach ( $this->exclude_file_paths as $file_path ) {
144
- $file_path = trim( preg_replace( '/\s\s+/', ' ', $file_path ) ); //remove trailing new lines
145
- if ( ! empty( $file_path ) ) {
146
- $custom_exclude[] = '<Files ' . $file_path . '> ' . PHP_EOL .
147
- $allow .
148
- '</Files>' . PHP_EOL;
149
- }
150
- }
151
-
152
- if ( ! empty( $custom_exclude ) ) {
153
- array_splice( $default, 2, 0, $custom_exclude ); //Add the excludes before the ## WP Defender - End ##
154
- $this->new_htconfig = $default; //Set the new array structure for when we want to remove
155
- }
156
- }
157
-
158
- $containsSearch = array_diff( $default, $htConfig );
159
- if ( count( $containsSearch ) == 0 || ( count( $containsSearch ) == count( $default ) ) ) {
160
- //append this
161
- $htConfig = array_merge( $htConfig, array( implode( '', $default ) ) );
162
- file_put_contents( $htPath, implode( '', $htConfig ), LOCK_EX );
163
  }
164
-
165
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  }
167
-
168
  /**
169
  * Protect uploads directory
170
- * Sometimes a user will set a custom upload directory
171
- *
172
- * @return bool|\WP_Error
173
  */
174
  public function protectUploadsDir() {
175
  if ( defined( 'UPLOADS' ) ) {
176
- $htPath = ABSPATH . UPLOADS . '/' . '.htaccess';
177
- if ( ! file_exists( $htPath ) ) {
178
- if ( ! file_put_contents( $htPath, '', LOCK_EX ) ) {
179
- return new \WP_Error( Error_Code::NOT_WRITEABLE,
180
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
181
- }
182
- } elseif ( ! is_writeable( $htPath ) ) {
183
- return new \WP_Error( Error_Code::NOT_WRITEABLE,
184
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
185
- }
186
- $htConfig = file( $htPath );
187
- $deny = $this->generateHtAccessRule( false );
188
- $allow = $this->generateHtAccessRule( true );
189
- $default = array(
190
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
191
- '<Files *.php>' . PHP_EOL .
192
- $deny .
193
- '</Files>' . PHP_EOL,
194
- '## WP Defender - End ##' . PHP_EOL
195
- );
196
-
197
- if ( ! empty( $this->exclude_file_paths ) ) {
198
-
199
- $custom_exclude = array();
200
-
201
- foreach ( $this->exclude_file_paths as $file_path ) {
202
- $file_path = trim( preg_replace( '/\s\s+/', ' ', $file_path ) ); //remove trailing new lines
203
- if ( ! empty( $file_path ) ) {
204
- $custom_exclude[] = '<Files ' . $file_path . '> ' . PHP_EOL .
205
- $allow .
206
- '</Files>' . PHP_EOL;
207
- }
208
- }
209
-
210
- if ( ! empty( $custom_exclude ) ) {
211
- array_splice( $default, 2, 0, $custom_exclude ); //Add the excludes before the ## WP Defender - End ##
212
- $this->new_htconfig = $default; //Set the new array structure for when we want to remove
213
- }
214
- }
215
-
216
- $containsSearch = array_diff( $default, $htConfig );
217
- if ( count( $containsSearch ) == 0 || ( count( $containsSearch ) == count( $default ) ) ) {
218
- //append this
219
- $htConfig = array_merge( $htConfig, array( implode( '', $default ) ) );
220
- file_put_contents( $htPath, implode( '', $htConfig ), LOCK_EX );
221
- }
222
  }
223
-
224
- return true;
225
  }
226
-
227
  public function unProtectContentDir() {
228
- $htPath = WP_CONTENT_DIR . '/' . '.htaccess';
229
- if ( ! is_writeable( $htPath ) ) {
230
- return new \WP_Error( Error_Code::NOT_WRITEABLE,
231
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
232
  }
233
- $htConfig = file_get_contents( $htPath );
234
- $deny = $this->generateHtAccessRule( false );
235
- $default = array(
236
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
237
- '<Files *.php>' . PHP_EOL .
238
- $deny .
239
- '</Files>' . PHP_EOL,
240
- '## WP Defender - End ##' . PHP_EOL
241
- );
242
-
243
- if ( ! empty( $this->new_htconfig ) ) {
244
- $default = $this->new_htconfig;
245
  }
246
-
247
- //Introduced regex
248
- preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $htConfig, $matches );
249
- if ( is_array( $matches ) && count( $matches ) > 0 ) {
250
- $htConfig = str_replace( implode( '', $matches[0] ), '', $htConfig );
251
- } else {
252
- $htConfig = str_replace( implode( '', $default ), '', $htConfig );
253
  }
254
- $htConfig = trim( $htConfig );
255
- file_put_contents( $htPath, $htConfig, LOCK_EX );
 
256
  }
257
-
258
  public function unProtectIncludeDir() {
259
- $htPath = ABSPATH . WPINC . '/' . '.htaccess';
260
- if ( ! is_writeable( $htPath ) ) {
261
- return new \WP_Error( Error_Code::NOT_WRITEABLE,
262
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
263
  }
264
- $htConfig = file_get_contents( $htPath );
265
- $deny = $this->generateHtAccessRule( false );
266
- $allow = $this->generateHtAccessRule( true );
267
- $default = array(
268
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
269
- '<Files *.php>' . PHP_EOL .
270
- $deny .
271
- '</Files>' . PHP_EOL,
272
- '<Files wp-tinymce.php>' . PHP_EOL .
273
- $allow .
274
- '</Files>' . PHP_EOL,
275
- '<Files ms-files.php>' . PHP_EOL .
276
- $allow .
277
- '</Files>' . PHP_EOL,
278
- '## WP Defender - End ##' . PHP_EOL
279
- );
280
-
281
- preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $htConfig, $matches );
282
- if ( is_array( $matches ) && count( $matches ) > 0 ) {
283
- $htConfig = str_replace( implode( '', $matches[0] ), '', $htConfig );
284
- } else {
285
- $htConfig = str_replace( implode( '', $default ), '', $htConfig );
286
  }
287
- $htConfig = trim( $htConfig );
288
- file_put_contents( $htPath, $htConfig, LOCK_EX );
289
  }
290
-
291
  public function unProtectUploadDir() {
292
  if ( defined( 'UPLOADS' ) ) {
293
- $htPath = ABSPATH . UPLOADS . '/' . '.htaccess';
294
- if ( ! is_writeable( $htPath ) ) {
295
- return new \WP_Error( Error_Code::NOT_WRITEABLE,
296
- sprintf( __( "The file %s is not writable", "defender-security" ), $htPath ) );
297
- }
298
- $htConfig = file_get_contents( $htPath );
299
- $deny = $this->generateHtAccessRule( false );
300
- $default = array(
301
- PHP_EOL . '## WP Defender - Protect PHP Executed ##' . PHP_EOL,
302
- '<Files *.php>' . PHP_EOL .
303
- $deny .
304
- '</Files>' . PHP_EOL,
305
- '## WP Defender - End ##' . PHP_EOL
306
- );
307
-
308
- if ( ! empty( $this->new_htconfig ) ) {
309
- $default = $this->new_htconfig;
310
- }
311
-
312
- //Introduced regex
313
- preg_match_all( '/## WP Defender(.*?)## WP Defender - End ##/s', $htConfig, $matches );
314
- if ( is_array( $matches ) && count( $matches ) > 0 ) {
315
- $htConfig = str_replace( implode( '', $matches[0] ), '', $htConfig );
316
- } else {
317
- $htConfig = str_replace( implode( '', $default ), '', $htConfig );
318
- }
319
- $htConfig = trim( $htConfig );
320
- file_put_contents( $htPath, $htConfig, LOCK_EX );
321
  }
322
  }
323
-
324
  /**
325
  * @return bool|\WP_Error
326
  */
@@ -329,7 +191,7 @@ class Apache_Service extends Rule_Service implements IRule_Service {
329
  if ( is_wp_error( $ret ) ) {
330
  return $ret;
331
  }
332
-
333
  $ret = $this->unProtectIncludeDir();
334
  if ( is_wp_error( $ret ) ) {
335
  return $ret;
@@ -338,10 +200,10 @@ class Apache_Service extends Rule_Service implements IRule_Service {
338
  if ( is_wp_error( $ret ) ) {
339
  return $ret;
340
  }
341
-
342
  return true;
343
  }
344
-
345
  /**
346
  * Set the exclude file paths
347
  *
@@ -352,8 +214,8 @@ class Apache_Service extends Rule_Service implements IRule_Service {
352
  $this->exclude_file_paths = explode( "\n", $paths );
353
  }
354
  }
355
-
356
-
357
  /**
358
  * Get the exclude file paths
359
  *
@@ -362,7 +224,7 @@ class Apache_Service extends Rule_Service implements IRule_Service {
362
  public function getExcludedFilePaths() {
363
  return $this->exclude_file_paths;
364
  }
365
-
366
  /**
367
  * Set the exclude file paths
368
  *
@@ -373,8 +235,8 @@ class Apache_Service extends Rule_Service implements IRule_Service {
373
  $this->new_htconfig = $config;
374
  }
375
  }
376
-
377
-
378
  /**
379
  * Get the new HT config
380
  *
@@ -383,7 +245,26 @@ class Apache_Service extends Rule_Service implements IRule_Service {
383
  public function getNewHtConfig() {
384
  return $this->new_htconfig;
385
  }
386
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
  /**
388
  * Return the correct apache rules for allow/deny
389
  *
@@ -393,16 +274,16 @@ class Apache_Service extends Rule_Service implements IRule_Service {
393
  $version = Utils::instance()->determineApacheVersion();
394
  if ( floatval( $version ) >= 2.4 ) {
395
  if ( $allow ) {
396
- return 'Require all granted' . PHP_EOL;
397
  } else {
398
- return 'Require all denied' . PHP_EOL;
399
  }
400
  } else {
401
  if ( $allow ) {
402
- return 'Allow from all' . PHP_EOL;
403
  } else {
404
  return 'Order allow,deny' . PHP_EOL .
405
- 'Deny from all' . PHP_EOL;
406
  }
407
  }
408
  }
11
  use WP_Defender\Module\Hardener\Rule_Service;
12
 
13
  class Apache_Service extends Rule_Service implements IRule_Service {
14
+
15
  /**
16
  * Exclude file paths
17
  *
18
  * @var array|bool|mixed
19
  */
20
  private $exclude_file_paths = array();
21
+
22
  /**
23
  * New htaccess file
24
  *
25
  * @var array|bool|mixed
26
  */
27
  private $new_htconfig = array();
28
+
29
+ /**
30
+ * The htaccess inside wp-content
31
+ * @var string
32
+ */
33
+ public $contentdir_path = null;
34
+
35
+ /**
36
+ * The htaccess path inside wp-includes
37
+ * @var null
38
+ */
39
+ public $includedir_path = null;
40
+
41
  /**
42
  * @return bool
43
  */
44
  public function check() {
45
  return true;
46
  }
47
+
48
  /**
49
  * @return bool|\WP_Error
50
  */
53
  if ( is_wp_error( $ret ) ) {
54
  return $ret;
55
  }
56
+
57
  $ret = $this->protectIncludesDir();
58
  if ( is_wp_error( $ret ) ) {
59
  return $ret;
60
  }
61
+
62
  $ret = $this->protectUploadsDir();
63
  if ( is_wp_error( $ret ) ) {
64
  return $ret;
65
  }
66
+
67
  return true;
68
  }
69
+
70
+ public function protectContentDir() {
71
+ $ht_path = $this->contentdir_path;
72
+ if ( $ht_path == null ) {
73
+ $ht_path = WP_CONTENT_DIR . '/' . '.htaccess';
74
+ }
75
+ if ( ! file_exists( $ht_path ) ) {
76
+ if ( file_put_contents( $ht_path, '', LOCK_EX ) === false ) {
 
77
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
78
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
79
  }
80
+ } elseif ( ! is_writeable( $ht_path ) ) {
81
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
82
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
83
  }
84
+
85
+ $exists_rules = $this->cleanupOldRules( file_get_contents( $ht_path ) );
86
+ $rule = [
87
+ '## WP Defender - Protect PHP Executed ##',
88
+ '<Files *.php>',
89
+ $this->generateHtAccessRule( false ),
90
+ '</Files>',
91
+ ];
92
+ if ( ! empty( $this->exclude_file_paths ) ) {
93
+ foreach ( $this->exclude_file_paths as $file_path ) {
94
+ $rule[] = sprintf( "<Files %s>", sanitize_file_name( $file_path ) );
95
+ $rule[] = $this->generateHtAccessRule( true );
96
+ $rule[] = "</Files>";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  }
98
  }
99
+ $rule[] = '## WP Defender - End ##';
100
+ file_put_contents( $ht_path, $exists_rules . implode( PHP_EOL, $rule ), LOCK_EX );
101
  }
102
+
103
+ public function protectIncludesDir() {
104
+ $ht_path = $this->includedir_path;
105
+ if ( $ht_path == null ) {
106
+ $ht_path = ABSPATH . WPINC . '/' . '.htaccess';
107
+ }
108
+ if ( ! is_file( $ht_path ) ) {
109
+ if ( file_put_contents( $ht_path, '', LOCK_EX ) === false ) {
110
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
111
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
112
  }
113
+ } elseif ( ! is_writeable( $ht_path ) ) {
114
  return new \WP_Error( Error_Code::NOT_WRITEABLE,
115
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  }
117
+ $exists_rules = $this->cleanupOldRules( file_get_contents( $ht_path ) );
118
+
119
+ $rule = [
120
+ '## WP Defender - Protect PHP Executed ##',
121
+ '<Files *.php>',
122
+ $this->generateHtAccessRule( false ),
123
+ '</Files>',
124
+ '<Files wp-tinymce.php>',
125
+ $this->generateHtAccessRule( true ),
126
+ '</Files>',
127
+ '<Files ms-files.php>',
128
+ $this->generateHtAccessRule( true ),
129
+ '</Files>',
130
+ '## WP Defender - End ##',
131
+ ];
132
+ //no exclude here
133
+ file_put_contents( $ht_path, $exists_rules . implode( PHP_EOL, $rule ), LOCK_EX );
134
  }
135
+
136
  /**
137
  * Protect uploads directory
138
+ * This only when user provide a custom uploads
 
 
139
  */
140
  public function protectUploadsDir() {
141
  if ( defined( 'UPLOADS' ) ) {
142
+ $this->contentdir_path = ABSPATH . UPLOADS . '/' . '.htaccess';
143
+ //should be same with protect content dirs
144
+ $this->protectContentDir();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  }
 
 
146
  }
147
+
148
  public function unProtectContentDir() {
149
+ $ht_path = $this->contentdir_path;
150
+ if ( $ht_path == null ) {
151
+ $ht_path = WP_CONTENT_DIR . '/' . '.htaccess';
 
152
  }
153
+ if ( ! file_exists( $ht_path ) ) {
154
+ //do nothing
155
+ return;
 
 
 
 
 
 
 
 
 
156
  }
157
+ if ( ! is_writeable( $ht_path ) ) {
158
+ return new \WP_Error( Error_Code::NOT_WRITEABLE,
159
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
 
 
 
 
160
  }
161
+ $ht_config = $this->cleanupOldRules( file_get_contents( $ht_path ) );
162
+ $ht_config = trim( $ht_config );
163
+ file_put_contents( $ht_path, trim( $ht_config ), LOCK_EX );
164
  }
165
+
166
  public function unProtectIncludeDir() {
167
+ $ht_path = $this->includedir_path;
168
+ if ( $ht_path == null ) {
169
+ $ht_path = ABSPATH . WPINC . '/' . '.htaccess';
 
170
  }
171
+ if ( ! is_writeable( $ht_path ) ) {
172
+ return new \WP_Error( Error_Code::NOT_WRITEABLE,
173
+ sprintf( __( "The file %s is not writable", "defender-security" ), $ht_path ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
  }
175
+ $ht_config = $this->cleanupOldRules( file_get_contents( $ht_path ) );
176
+ file_put_contents( $ht_path, trim( $ht_config ), LOCK_EX );
177
  }
178
+
179
  public function unProtectUploadDir() {
180
  if ( defined( 'UPLOADS' ) ) {
181
+ $this->contentdir_path = ABSPATH . UPLOADS . '/' . '.htaccess';
182
+ $this->unProtectContentDir();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
  }
185
+
186
  /**
187
  * @return bool|\WP_Error
188
  */
191
  if ( is_wp_error( $ret ) ) {
192
  return $ret;
193
  }
194
+
195
  $ret = $this->unProtectIncludeDir();
196
  if ( is_wp_error( $ret ) ) {
197
  return $ret;
200
  if ( is_wp_error( $ret ) ) {
201
  return $ret;
202
  }
203
+
204
  return true;
205
  }
206
+
207
  /**
208
  * Set the exclude file paths
209
  *
214
  $this->exclude_file_paths = explode( "\n", $paths );
215
  }
216
  }
217
+
218
+
219
  /**
220
  * Get the exclude file paths
221
  *
224
  public function getExcludedFilePaths() {
225
  return $this->exclude_file_paths;
226
  }
227
+
228
  /**
229
  * Set the exclude file paths
230
  *
235
  $this->new_htconfig = $config;
236
  }
237
  }
238
+
239
+
240
  /**
241
  * Get the new HT config
242
  *
245
  public function getNewHtConfig() {
246
  return $this->new_htconfig;
247
  }
248
+
249
+ /**
250
+ * @param $exists_rules
251
+ *
252
+ * @return string|string[]|null
253
+ */
254
+ private function cleanupOldRules( $exists_rules ) {
255
+ $pattern = '/(## WP Defender - Protect PHP Executed ##((.|\n)*)## WP Defender - End ##)/';
256
+ if ( preg_match( $pattern, $exists_rules ) ) {
257
+ //replace it
258
+ $exists_rules = preg_replace( $pattern, '', $exists_rules );
259
+ }
260
+ $exists_rules = trim( $exists_rules );
261
+ if ( strlen( $exists_rules ) ) {
262
+ $exists_rules .= PHP_EOL;
263
+ }
264
+
265
+ return $exists_rules;
266
+ }
267
+
268
  /**
269
  * Return the correct apache rules for allow/deny
270
  *
274
  $version = Utils::instance()->determineApacheVersion();
275
  if ( floatval( $version ) >= 2.4 ) {
276
  if ( $allow ) {
277
+ return 'Require all granted';
278
  } else {
279
+ return 'Require all denied';
280
  }
281
  } else {
282
  if ( $allow ) {
283
+ return 'Allow from all';
284
  } else {
285
  return 'Order allow,deny' . PHP_EOL .
286
+ 'Deny from all';
287
  }
288
  }
289
  }
app/module/hardener/component/sh-content-security-service.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Component\Error_Code;
10
+ use WP_Defender\Module\Hardener\IRule_Service;
11
+ use WP_Defender\Module\Hardener\Model\Settings;
12
+ use WP_Defender\Module\Hardener\Rule_Service;
13
+
14
+ class Sh_Content_Security_Service extends Rule_Service implements IRule_Service {
15
+ //the data we use for real
16
+ const KEY_DATA = 'sh_content_security',
17
+ //this is the data showing in test pharse
18
+ KEY_TEMP_DATA = 'sh_content_security_temp',
19
+ //this is the data queue for approval, passing from test
20
+ KEY_STAGING_DATA = 'sh_content_security_staging';
21
+
22
+ /**
23
+ * @return bool
24
+ */
25
+ public function check() {
26
+ $is_test = isset( $_COOKIE[ Sh_Content_Security::$slug . '-testing' ] );
27
+ $is_staging = isset( $_COOKIE[ Sh_Content_Security::$slug . '-staging' ] );
28
+
29
+ if ( $is_staging || $is_test ) {
30
+ //this case we are in staging mode, so just return base on the current screen
31
+ $settings = Settings::instance();
32
+ $data = $settings->getDValues( self::KEY_DATA );
33
+ if ( is_array( $data ) ) {
34
+ $data = array_filter( $data );
35
+ }
36
+ unset( $data['somewhere'] );
37
+ if ( ! ! $data == false || empty( $data ) ) {
38
+ return false;
39
+ }
40
+
41
+ $keys = $this->getKeys();
42
+ foreach ( $keys as $key ) {
43
+ if ( isset( $data[ $key ] ) && $data[ $key ] == 1 ) {
44
+
45
+ return true;
46
+ }
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ $response = wp_remote_head( network_site_url() );
53
+ if ( is_wp_error( $response ) ) {
54
+ return false;
55
+ }
56
+ $headers = $response['headers'];
57
+ if ( isset( $headers['content-security-policy'] ) ) {
58
+ $settings = Settings::instance();
59
+ $data = $settings->getDValues( self::KEY_DATA );
60
+ if ( $data === null ) {
61
+ $data['somewhere'] = true;
62
+ $settings->setDValues( self::KEY_DATA, $data );
63
+ }
64
+
65
+ return true;
66
+ }
67
+
68
+ return false;
69
+ }
70
+
71
+ public function getKeys() {
72
+ $keys = [
73
+ 'base_uri',
74
+ 'child_src',
75
+ 'default_src',
76
+ 'font_src',
77
+ 'frame_ancestors',
78
+ 'form_action',
79
+ 'img_src',
80
+ 'media_src',
81
+ 'object_src',
82
+ 'plugin_types',
83
+ 'sandbox',
84
+ 'script_src',
85
+ 'style_src',
86
+ 'worker_src',
87
+ ];
88
+
89
+ return $keys;
90
+ }
91
+
92
+ /**
93
+ * @return bool|\WP_Error
94
+ */
95
+ public function process() {
96
+ $params = [
97
+ 'base_uri' => is_array( $_POST ) && isset( $_POST['base_uri'] ) ? $_POST['base_uri'] : 0,
98
+ 'child_src' => is_array( $_POST ) && isset( $_POST['child_src'] ) ? $_POST['child_src'] : 0,
99
+ 'default_src' => is_array( $_POST ) && isset( $_POST['default_src'] ) ? $_POST['default_src'] : 0,
100
+ 'font_src' => is_array( $_POST ) && isset( $_POST['font_src'] ) ? $_POST['font_src'] : 0,
101
+ 'frame_ancestors' => is_array( $_POST ) && isset( $_POST['frame_ancestors'] ) ? $_POST['frame_ancestors'] : 0,
102
+ 'form_action' => is_array( $_POST ) && isset( $_POST['form_action'] ) ? $_POST['form_action'] : 0,
103
+ 'img_src' => is_array( $_POST ) && isset( $_POST['img_src'] ) ? $_POST['img_src'] : 0,
104
+ 'media_src' => is_array( $_POST ) && isset( $_POST['media_src'] ) ? $_POST['media_src'] : 0,
105
+ 'object_src' => is_array( $_POST ) && isset( $_POST['object_src'] ) ? $_POST['object_src'] : 0,
106
+ 'plugin_types' => is_array( $_POST ) && isset( $_POST['plugin_types'] ) ? $_POST['plugin_types'] : 0,
107
+ 'sandbox' => is_array( $_POST ) && isset( $_POST['sandbox'] ) ? $_POST['sandbox'] : 0,
108
+ 'script_src' => is_array( $_POST ) && isset( $_POST['script_src'] ) ? $_POST['script_src'] : 0,
109
+ 'style_src' => is_array( $_POST ) && isset( $_POST['style_src'] ) ? $_POST['style_src'] : 0,
110
+ 'worker_src' => is_array( $_POST ) && isset( $_POST['worker_src'] ) ? $_POST['worker_src'] : 0,
111
+ 'url_ignores' => is_array( $_POST ) && isset( $_POST['url_ignores'] ) ? $_POST['url_ignores'] : "",
112
+ 'base_uri_values' => is_array( $_POST ) && isset( $_POST['base_uri_values'] ) ? $_POST['base_uri_values'] : [],
113
+ 'child_src_values' => is_array( $_POST ) && isset( $_POST['child_src_values'] ) ? $_POST['child_src_values'] : [],
114
+ 'frame_ancestors_values' => is_array( $_POST ) && isset( $_POST['frame_ancestors_values'] ) ? $_POST['frame_ancestors_values'] : [],
115
+ 'default_src_values' => is_array( $_POST ) && isset( $_POST['default_src_values'] ) ? $_POST['default_src_values'] : [],
116
+ 'font_src_values' => is_array( $_POST ) && isset( $_POST['font_src_values'] ) ? $_POST['font_src_values'] : [],
117
+ 'form_action_values' => is_array( $_POST ) && isset( $_POST['form_action_values'] ) ? $_POST['form_action_values'] : [],
118
+ 'img_src_values' => is_array( $_POST ) && isset( $_POST['img_src_values'] ) ? $_POST['img_src_values'] : [],
119
+ 'media_src_values' => is_array( $_POST ) && isset( $_POST['media_src_values'] ) ? $_POST['media_src_values'] : [],
120
+ 'object_src_values' => is_array( $_POST ) && isset( $_POST['object_src_values'] ) ? $_POST['object_src_values'] : [],
121
+ 'plugin_types_values' => is_array( $_POST ) && isset( $_POST['plugin_types_values'] ) ? $_POST['plugin_types_values'] : [],
122
+ 'sandbox_values' => is_array( $_POST ) && isset( $_POST['sandbox_values'] ) ? $_POST['sandbox_values'] : [],
123
+ 'script_src_values' => is_array( $_POST ) && isset( $_POST['script_src_values'] ) ? $_POST['script_src_values'] : [],
124
+ 'style_src_values' => is_array( $_POST ) && isset( $_POST['style_src_values'] ) ? $_POST['style_src_values'] : [],
125
+ 'worker_src_values' => is_array( $_POST ) && isset( $_POST['worker_src_values'] ) ? $_POST['worker_src_values'] : [],
126
+ ];
127
+ $scenario = HTTP_Helper::retrievePost( 'scenario' );
128
+ $settings = Settings::instance();
129
+ $data = $settings->getDValues( self::KEY_DATA );
130
+ foreach ( $params as $key => $value ) {
131
+ if ( is_array( $value ) ) {
132
+ $value = array_map( 'stripslashes', $value );
133
+ }
134
+ if ( is_array( $value ) && empty( $value ) ) {
135
+ //dont save this, and we remove the key of the trigger
136
+ $trigger = str_replace( '_values', '', $key );
137
+ unset( $data[ $trigger ] );
138
+ continue;
139
+ }
140
+
141
+ $data[ $key ] = $value;
142
+ }
143
+
144
+ if ( $scenario == 'enforce' ) {
145
+ unset( $data['somewhere'] );
146
+ }
147
+ //set enable flag
148
+ if ( $scenario == 'temp' ) {
149
+ /**
150
+ * If this is temp then it is flaging for test, the flow will be
151
+ * - store the flag in cookies
152
+ * - reload page
153
+ * - show a small banner for cancel, or apply the changes.
154
+ */
155
+ $settings->setDValues( self::KEY_TEMP_DATA, $data );
156
+ setcookie( Sh_Content_Security::$slug . '-testing', true, 0, '', '', true, true );
157
+ //clear the old staging
158
+ setcookie( Sh_Content_Security::$slug . '-staging', false, - 1, '', '', true, true );
159
+ //a simple flag to use while cookie being set
160
+ } else {
161
+ $settings->setDValues( self::KEY_DATA, $data );
162
+ }
163
+ }
164
+
165
+ public function revert() {
166
+ $settings = Settings::instance();
167
+ $settings->setDValues( self::KEY_TEMP_DATA, null );
168
+ }
169
+
170
+ public function listen() {
171
+
172
+ }
173
+ }
app/module/hardener/component/sh-content-security.php ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_Content_Security extends Rule {
13
+ static $slug = 'sh-content-security';
14
+ static $service;
15
+
16
+ public function getDescription() {
17
+
18
+ }
19
+
20
+ public function check() {
21
+ return $this->getService()->check();
22
+ }
23
+
24
+ /**
25
+ * @return array
26
+ */
27
+ public function getMiscData() {
28
+ $settings = Settings::instance();
29
+ $is_test = isset( $_COOKIE[ self::$slug . '-testing' ] );
30
+ $is_staging = isset( $_COOKIE[ self::$slug . '-staging' ] );
31
+ if ( $is_test ) {
32
+ $data = $settings->getDValues( Sh_Content_Security_Service::KEY_TEMP_DATA );
33
+ } elseif ( $is_staging ) {
34
+ $data = $settings->getDValues( Sh_Content_Security_Service::KEY_STAGING_DATA );
35
+ } else {
36
+ $data = $settings->getDValues( Sh_Content_Security_Service::KEY_DATA );
37
+ }
38
+
39
+ return [
40
+ 'is_opened' => $is_test || $is_staging,
41
+ 'text' => [
42
+ ///mix stirng with html should be parsed from php
43
+ 'base_uri_text' => esc_html__( "Restricts the URLs which can be used in a document's <base> element. You can read more about this directive", "defender-security" ),
44
+ 'base_uri_desc' => sprintf( __( "Example value for this directive can be <strong>%s</strong>. Press Enter to separate the values.", "defender-security" ), network_site_url() ),
45
+ 'child_src_text' => __( "Defines the valid sources for web workers and nested browsing contexts loaded using elements such as &#x3C;frame&#x3E; and &#x3C;iframe&#x3E;. Note, that Instead of child-src, authors who wish to regulate nested browsing contexts and workers should use the <strong>frame-src</strong> and <strong>worker-src</strong> directives, respectively.", "defender-security" ),
46
+ 'child_src_desc' => sprintf( __( "Example value for this directive can be <strong>%s</strong>. Press Enter to separate the values.", "defender-security" ), network_site_url() ),
47
+ 'default_src_text' => __( "" ),
48
+ 'default_src_desc' => sprintf( __( "Example value for this directive can be <strong>'unsafe-eval'</strong>; <strong>'unsafe-inline'</strong>; <strong>%s</strong>. Press Enter to separate the values.", "defender-security" ), network_site_url() ),
49
+ 'font_src_desc' => __( "Example value for this directive can be <strong>font.example.com.</strong> Press Enter to separate the values.", "defender-security" ),
50
+ 'form_action_desc' => __( "Example value for this directive can ba <strong>'self'</strong>. Press Enter to separate the values.", "defender-security" ),
51
+ 'frame_ancestors_desc' => __( "Example value for this directive can ba <strong>'self'</strong>. Press Enter to separate the values.", "defender-security" ),
52
+ 'img_src_desc' => __( "Example value for this directive can be <strong>'self'</strong>; <strong>img.example.com.</strong> Press Enter to separate the values.", "defender-security" ),
53
+ 'media_src_text' => __( "Specifies valid sources for loading media using the &lt;frame&gt; , &lt;frame&gt; and &lt;frame&gt; elements.", "defender-security" ),
54
+ 'media_src_desc' => __( "Example value for this directive can be <strong>media.example.com</strong>. Press Enter to separate the values.", "defender-security" ),
55
+ 'object_src_text' => __( "Specifies valid sources for the &lt;frame&gt;, &lt;frame&gt;, and &lt;frame&gt; elements. You can read more about this directive", "defender-security" ),
56
+ 'object_src_desc' => sprintf( __( "Example value for this directive can be <strong>%s</strong>. Press Enter to separate the values.", "defender-security" ), network_site_url() ),
57
+ 'sandbox_text' => __( "Enables a sandbox for the requested resource similar to the &lt;iframe&gt;, sandbox attribute. You can read more about this directive here.", "defender-security" ),
58
+ 'sandbox_desc' => __( "Example value for this directive can be <strong>allow-forms</strong>; <strong>allow-scripts</strong>. Press Enter to separate the values.", "defender-security" ),
59
+ 'script_src_desc' => __( "Example value for this directive can be <strong>'self' js.example.com</strong>. Press Enter to separate the values.", "defender-security" ),
60
+ 'style_src_desc' => __( "Example value for this directive can be <strong>'self' css.example.com</strong>. Press Enter to separate the values.", "defender-security" ),
61
+ 'worker_src_desc' => __( "Example value for this directive can be <strong>'self'</strong>. Press Enter to separate the values.", "defender-security" ),
62
+ 'plugin_type_desc' => __( "Example value for this directive can be <strong>application/pdf</strong>. Press Enter to separate the values.", "defender-security" ),
63
+ 'frame_ancestors_text' => esc_html__( "Specifies valid sources for nested browsing contexts loading using elements such as <frame> and <iframe>.You can read more about this directive", "defender-security" ),
64
+ ],
65
+ 'data' => [
66
+ 'base_uri' => is_array( $data ) && isset( $data['base_uri'] ) ? $data['base_uri'] : 0,
67
+ 'child_src' => is_array( $data ) && isset( $data['child_src'] ) ? $data['child_src'] : 0,
68
+ 'default_src' => is_array( $data ) && isset( $data['default_src'] ) ? $data['default_src'] : 0,
69
+ 'font_src' => is_array( $data ) && isset( $data['font_src'] ) ? $data['font_src'] : 0,
70
+ 'frame_ancestors' => is_array( $data ) && isset( $data['frame_ancestors'] ) ? $data['frame_ancestors'] : 0,
71
+ 'form_action' => is_array( $data ) && isset( $data['form_action'] ) ? $data['form_action'] : 0,
72
+ 'img_src' => is_array( $data ) && isset( $data['img_src'] ) ? $data['img_src'] : 0,
73
+ 'media_src' => is_array( $data ) && isset( $data['media_src'] ) ? $data['media_src'] : 0,
74
+ 'object_src' => is_array( $data ) && isset( $data['object_src'] ) ? $data['object_src'] : 0,
75
+ 'plugin_types' => is_array( $data ) && isset( $data['plugin_types'] ) ? $data['plugin_types'] : 0,
76
+ 'sandbox' => is_array( $data ) && isset( $data['sandbox'] ) ? $data['sandbox'] : 0,
77
+ 'script_src' => is_array( $data ) && isset( $data['script_src'] ) ? $data['script_src'] : 0,
78
+ 'style_src' => is_array( $data ) && isset( $data['style_src'] ) ? $data['style_src'] : 0,
79
+ 'worker_src' => is_array( $data ) && isset( $data['worker_src'] ) ? $data['worker_src'] : 0,
80
+ 'url_ignores' => is_array( $data ) && isset( $data['url_ignores'] ) ? $data['url_ignores'] : "",
81
+ 'base_uri_values' => is_array( $data ) && isset( $data['base_uri_values'] ) ? $data['base_uri_values'] : [ "'none'" ],
82
+ 'child_src_values' => is_array( $data ) && isset( $data['child_src_values'] ) ? $data['child_src_values'] : [],
83
+ 'frame_ancestors_values' => is_array( $data ) && isset( $data['frame_ancestors_values'] ) ? $data['frame_ancestors_values'] : [],
84
+ 'default_src_values' => is_array( $data ) && isset( $data['default_src_values'] ) ? $data['default_src_values'] : [],
85
+ 'font_src_values' => is_array( $data ) && isset( $data['font_src_values'] ) ? $data['font_src_values'] : [],
86
+ 'form_action_values' => is_array( $data ) && isset( $data['form_action_values'] ) ? $data['form_action_values'] : [],
87
+ 'img_src_values' => is_array( $data ) && isset( $data['img_src_values'] ) ? $data['img_src_values'] : [],
88
+ 'media_src_values' => is_array( $data ) && isset( $data['media_src_values'] ) ? $data['media_src_values'] : [],
89
+ 'object_src_values' => is_array( $data ) && isset( $data['object_src_values'] ) ? $data['object_src_values'] : [ "'none'" ],
90
+ 'plugin_types_values' => is_array( $data ) && isset( $data['plugin_types_values'] ) ? $data['plugin_types_values'] : [],
91
+ 'sandbox_values' => is_array( $data ) && isset( $data['sandbox_values'] ) ? $data['sandbox_values'] : [],
92
+ 'script_src_values' => is_array( $data ) && isset( $data['script_src_values'] ) ? $data['script_src_values'] : [
93
+ "'unsafe-inline'",
94
+ "'self'",
95
+ "'unsafe-eval'"
96
+ ],
97
+ 'style_src_values' => is_array( $data ) && isset( $data['style_src_values'] ) ? $data['style_src_values'] : [],
98
+ 'worker_src_values' => is_array( $data ) && isset( $data['worker_src_values'] ) ? $data['worker_src_values'] : [],
99
+ ]
100
+ ];
101
+ }
102
+
103
+ /**
104
+ * This will return the short summary why this rule show up as issue
105
+ *
106
+ * @return string
107
+ */
108
+ function getErrorReason() {
109
+ return __( "Content-Security-Policy isn't enforced. Your site is at risk of XSS attacks.", "defender-security" );
110
+ }
111
+
112
+ /**
113
+ * This will return a short summary to show why this rule works
114
+ * @return mixed
115
+ */
116
+ function getSuccessReason() {
117
+ return __( "You've enforced Content-Security-Policy, good job!", "defender-security" );
118
+ }
119
+
120
+ public function addHooks() {
121
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
122
+ $this->addAction( 'wp_loaded', 'appendHeader', 999 );
123
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
124
+ $is_test = isset( $_COOKIE[ self::$slug . '-testing' ] );
125
+ $is_staging = isset( $_COOKIE[ self::$slug . '-staging' ] );
126
+ if ( $is_test || $is_staging ) {
127
+ $this->addAction( 'admin_enqueue_scripts', 'enqueueCspDebugBar' );
128
+ if ( $is_test ) {
129
+ $this->addAction( 'admin_footer', 'debugBar', 9999 );
130
+ } elseif ( $is_staging ) {
131
+ $this->addAction( 'tweaks_footer', 'stagingNotification', 9999 );
132
+ }
133
+ $this->addAjaxAction( 'defender-csp-debug-cancel', 'cancelDebug' );
134
+ $this->addAjaxAction( 'defender-csp-debug-apply', 'applyDebug' );
135
+ $this->addAjaxAction( 'defender-csp-debug-staging', 'applyStaging' );
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Cancel the debug, redirect to security tweaks page
141
+ */
142
+ public function cancelDebug() {
143
+ setcookie( self::$slug . '-testing', false, - 1, '', '', true, true );
144
+ $status = $this->check();
145
+ wp_redirect( network_admin_url( 'admin.php?page=wdf-hardener&view=' . ( $status == true ? 'resolved' : 'issues' ) ) );
146
+ exit;
147
+ }
148
+
149
+ /**
150
+ * We applying the debug to a staging parameter before real apply to site
151
+ */
152
+ public function applyDebug() {
153
+ $settings = Settings::instance();
154
+ $test_data = $settings->getDValues( Sh_Content_Security_Service::KEY_TEMP_DATA );
155
+ $settings->setDValues( Sh_Content_Security_Service::KEY_STAGING_DATA, $test_data );
156
+ $settings->setDValues( Sh_Content_Security_Service::KEY_TEMP_DATA, null );
157
+ $status = $this->check();
158
+ setcookie( self::$slug . '-testing', false, - 1, '', '', true, true );
159
+ setcookie( self::$slug . '-staging', true, 0, '', '', true, true );
160
+ wp_redirect( network_admin_url( 'admin.php?page=wdf-hardener&view=' . ( $status == true ? 'resolved' : 'issues' ) ) );
161
+ exit;
162
+ }
163
+
164
+ public function applyStaging() {
165
+ $settings = Settings::instance();
166
+ $stagingData = $settings->getDValues( Sh_Content_Security_Service::KEY_STAGING_DATA );
167
+ $settings->setDValues( Sh_Content_Security_Service::KEY_DATA, $stagingData );
168
+ $settings->setDValues( Sh_Content_Security_Service::KEY_STAGING_DATA, null );
169
+ setcookie( self::$slug . '-testing', false, - 1, '', '', true, true );
170
+ setcookie( self::$slug . '-staging', false, - 1, '', '', true, true );
171
+ wp_redirect( network_admin_url( 'admin.php?page=wdf-hardener&view=resolved' ) );
172
+ exit;
173
+ }
174
+
175
+ public function enqueueCspDebugBar() {
176
+ wp_enqueue_style( 'defender-csp-debug-bar', wp_defender()->getPluginUrl() . '/assets/css/csp-debug-bar.css' );
177
+ }
178
+
179
+ public function debugBar() {
180
+ $this->renderPartial( '/tweaks/csp/debug-bar' );
181
+ }
182
+
183
+ public function stagingNotification() {
184
+ $this->renderPartial( '/tweaks/csp/notification-bar' );
185
+ }
186
+
187
+ public function revert() {
188
+ $this->getService()->revert();
189
+ Settings::instance()->addToIssues( Sh_Content_Security::$slug );
190
+ }
191
+
192
+ /**
193
+ *
194
+ */
195
+ public function appendHeader() {
196
+ if ( headers_sent() ) {
197
+ //header already sent, do nothing
198
+ return;
199
+ }
200
+
201
+ $settings = Settings::instance();
202
+ $is_test = isset( $_COOKIE[ self::$slug . '-testing' ] );
203
+ if ( $is_test ) {
204
+ $data = $settings->getDValues( Sh_Content_Security_Service::KEY_TEMP_DATA );
205
+ } else {
206
+ $data = $settings->getDValues( Sh_Content_Security_Service::KEY_DATA );
207
+ }
208
+
209
+ if ( ! $this->maybeSubmitHeader( 'Content-Security-Policy', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
210
+ //this mean Defender can't override the already output, marked to show notification
211
+ $data['overrideable'] = false;
212
+ $settings->setDValues( Sh_Content_Security_Service::KEY_DATA, $data );
213
+
214
+ return;
215
+ }
216
+
217
+ if ( is_array( $data ) ) {
218
+ $keys = [
219
+ 'base_uri',
220
+ 'child_src',
221
+ 'default_src',
222
+ 'font_src',
223
+ 'frame_ancestors',
224
+ 'form_action',
225
+ 'img_src',
226
+ 'media_src',
227
+ 'object_src',
228
+ 'plugin_types',
229
+ 'sandbox',
230
+ 'script_src',
231
+ 'style_src',
232
+ 'worker_src',
233
+ ];
234
+ $headers = [];
235
+ $data = array_filter( $data );
236
+ foreach ( $keys as $key ) {
237
+ if ( ! isset( $data[ $key ] ) || ( isset( $data[ $key ] ) && $data[ $key ] != 1 ) ) {
238
+ //this rule not enable, move forward
239
+ continue;
240
+ }
241
+
242
+ if ( isset( $data[ $key . '_values' ] ) ) {
243
+ $value = $data[ $key . '_values' ];
244
+ $value = implode( ' ', $value );
245
+ /**
246
+ * with the javascript rules, many place using inline especially in wp-admin, we should check
247
+ * and append that if it missing so everything wont broke down
248
+ */
249
+ if ( $key == 'script_src' && ( is_admin() || is_network_admin() ) ) {
250
+ $uris = [
251
+ str_replace( network_site_url(), '', network_admin_url( 'admin.php?page=wdf-hardener' ) ),
252
+ str_replace( network_site_url(), '', network_admin_url( 'admin.php?page=wdf-hardener&view=resolved' ) ),
253
+ str_replace( network_site_url(), '', network_admin_url( 'admin.php?page=wdf-hardener&view=issues' ) ),
254
+ str_replace( network_site_url(), '', network_admin_url( 'admin.php?page=wdf-hardener&view=ignore' ) )
255
+ ];
256
+ $uri = $_SERVER['REQUEST_URI'];
257
+ if ( in_array( $uri, $uris ) && current_user_can( 'manage_options' ) ) {
258
+ //read the data output from wp_localization
259
+ if ( stristr( $value, 'unsafe-inline' ) == false ) {
260
+ $value .= " 'unsafe-inline'";
261
+ }
262
+ /**
263
+ * underscore require new Function so we need this
264
+ */
265
+ if ( stristr( $value, 'unsafe-eval' ) == false ) {
266
+ $value .= " 'unsafe-eval'";
267
+ }
268
+ /**
269
+ * to load the needed js from core
270
+ */
271
+ if ( stristr( $value, 'self' ) == false ) {
272
+ $value .= " 'self'";
273
+ }
274
+ }
275
+ }
276
+
277
+ $headers[] = str_replace( '_', '-', $key ) . ' ' . $value;
278
+ }
279
+ }
280
+ $headers = array_filter( $headers );
281
+ if ( empty( $headers ) ) {
282
+ return;
283
+ }
284
+ $headers = implode( '; ', $headers );
285
+ $headers = "Content-Security-Policy: " . $headers;
286
+ header( $headers );
287
+ }
288
+ }
289
+
290
+ /**
291
+ * @return string
292
+ */
293
+ public function getTitle() {
294
+ return __( "Content-Security-Policy Security Header", "defender-security" );
295
+ }
296
+
297
+ /**
298
+ * Store a flag that we enable this
299
+ * @return mixed|void
300
+ */
301
+ public function process() {
302
+ //calling the service
303
+ $this->getService()->process();
304
+ Settings::instance()->addToResolved( Sh_Content_Security::$slug );
305
+ $scenario = HTTP_Helper::retrievePost( 'scenario' );
306
+ if ( $scenario == 'temp' ) {
307
+ // $status = $this->check();
308
+ // $url = network_admin_url( 'admin.php?page=wdf-hardener' );
309
+ // if ( $status == true ) {
310
+ // $url = add_query_arg( 'view', 'resolved', $url );
311
+ // } else {
312
+ // $url = add_query_arg( 'view', 'issues', $url );
313
+ // }
314
+ wp_send_json_success( [
315
+ 'reload' => 1,
316
+ //'url' => $url
317
+ ] );
318
+ }
319
+ }
320
+
321
+ /**
322
+ * @return Sh_Content_Security_Service
323
+ */
324
+ public function getService() {
325
+ if ( self::$service == null ) {
326
+ self::$service = new Sh_Content_Security_Service();
327
+ }
328
+
329
+ return self::$service;
330
+ }
331
+ }
app/module/hardener/component/sh-content-type-options-service.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_Content_Type_Options_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_content_type_options';
17
+ public $mode;
18
+ public $scenario;
19
+
20
+ /**
21
+ * @return bool
22
+ */
23
+ public function check() {
24
+ $settings = Settings::instance();
25
+
26
+ $data = $settings->getDValues( self::KEY );
27
+ if ( is_array( $data ) && $data['mode'] == 'nosniff' ) {
28
+ return true;
29
+ }
30
+
31
+ $headers = $this->headRequest( network_site_url(), self::KEY );
32
+ if ( is_wp_error( $headers ) ) {
33
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ),'tweaks' );
34
+
35
+ return false;
36
+ }
37
+
38
+ if ( isset( $headers['x-content-type-options'] ) ) {
39
+ $data = $settings->getDValues( self::KEY );
40
+ if ( $data === null ) {
41
+ $data['mode'] = 'nosniff';
42
+ $data['somewhere'] = 1;
43
+ $settings->setDValues( self::KEY, $data );
44
+ }
45
+
46
+ return true;
47
+ }
48
+
49
+ return false;
50
+ }
51
+
52
+ /**
53
+ * @return bool|\WP_Error
54
+ */
55
+ public function process() {
56
+ $mode = $this->mode;
57
+ $scenario = $this->scenario;
58
+ if ( empty( $mode ) || ! in_array( $mode, [ 'nosniff' ] ) ) {
59
+ return new \WP_Error( Error_Code::INVALID, __( "Mode empty or invalid", "defender-security" ) );
60
+ }
61
+ $settings = Settings::instance();
62
+ $data = $settings->getDValues( self::KEY );
63
+ $data['mode'] = $mode;
64
+ if ( $scenario == 'enforce' ) {
65
+ unset( $data['somewhere'] );
66
+ }
67
+ $settings->setDValues( self::KEY, $data );
68
+ }
69
+
70
+ public function revert() {
71
+ $settings = Settings::instance();
72
+ $settings->setDValues( self::KEY, null );
73
+ }
74
+
75
+ public function listen() {
76
+
77
+ }
78
+ }
app/module/hardener/component/sh-content-type-options.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_Content_Type_Options extends Rule {
13
+ static $slug = 'sh-content-type-options';
14
+ static $service;
15
+
16
+ public function getDescription() {
17
+
18
+ }
19
+
20
+ public function check() {
21
+ return $this->getService()->check();
22
+ }
23
+
24
+ /**
25
+ * @return array
26
+ */
27
+ public function getMiscData() {
28
+ $settings = Settings::instance();
29
+ $data = $settings->getDValues( Sh_Content_Type_Options_Service::KEY );
30
+
31
+ return [
32
+ 'mode' => is_array( $data ) && isset( $data['mode'] ) ? $data['mode'] : 'nosniff',
33
+ ];
34
+ }
35
+
36
+ /**
37
+ * This will return the short summary why this rule show up as issue
38
+ *
39
+ * @return string
40
+ */
41
+ function getErrorReason() {
42
+ return __( "The X-Content-Type-Options header isn't enforced. Your site is at risk of MIME type sniffing and XSS attacks.", "defender-security" );
43
+ }
44
+
45
+ /**
46
+ * This will return a short summary to show why this rule works
47
+ * @return mixed
48
+ */
49
+ function getSuccessReason() {
50
+ return __( "You've enforced X-Content-Type-Options, well done!", "defender-security" );
51
+ }
52
+
53
+ public function addHooks() {
54
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
55
+ $this->addAction( 'wp', 'appendHeader', PHP_INT_MAX );
56
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
57
+ }
58
+
59
+ public function revert() {
60
+ $this->getService()->revert();
61
+ Settings::instance()->addToIssues( self::$slug );
62
+ }
63
+
64
+ public function appendHeader() {
65
+ if ( headers_sent() ) {
66
+ //header already sent, do nothing
67
+ return;
68
+ }
69
+ $settings = Settings::instance();
70
+ $data = $settings->getDValues( Sh_Content_Type_Options_Service::KEY );
71
+ if ( ! $this->maybeSubmitHeader( 'X-Content-Type-Options', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
72
+ //this mean Defender can't override the already output, marked to show notification
73
+ $data['overrideable'] = false;
74
+ $settings->setDValues( Sh_Content_Type_Options_Service::KEY, $data );
75
+
76
+ return;
77
+ }
78
+ if ( is_array( $data ) && $data['mode'] == 'nosniff' ) {
79
+ header( 'X-Content-Type-Options: nosniff' );
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @return string
85
+ */
86
+ public function getTitle() {
87
+ return __( "X-Content-Type-Options Security Header", "defender-security" );
88
+ }
89
+
90
+ /**
91
+ * Store a flag that we enable this
92
+ * @return mixed|void
93
+ */
94
+ public function process() {
95
+ //calling the service
96
+ $mode = HTTP_Helper::retrievePost( 'mode' );
97
+ $scenario = HTTP_Helper::retrievePost( 'scenario' );
98
+ $service = $this->getService();
99
+ $service->mode = $mode;
100
+ $service->scenario = $scenario;
101
+ $ret = $service->process();
102
+ if ( is_wp_error( $ret ) ) {
103
+ wp_send_json_error( [
104
+ 'message' => $ret->get_error_message()
105
+ ] );
106
+ }
107
+ $this->getService()->process();
108
+ Settings::instance()->addToResolved( self::$slug );
109
+ }
110
+
111
+ /**
112
+ * @return Sh_Content_Type_Options_Service
113
+ */
114
+ public function getService() {
115
+ if ( self::$service == null ) {
116
+ self::$service = new Sh_Content_Type_Options_Service();
117
+ }
118
+
119
+ return self::$service;
120
+ }
121
+ }
app/module/hardener/component/sh-feature-policy-service.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_Feature_Policy_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_feature_policy';
17
+ public $mode;
18
+ public $scenario;
19
+ public $values;
20
+
21
+ /**
22
+ * @return bool
23
+ */
24
+ public function check() {
25
+ $settings = Settings::instance();
26
+ $headers = $this->headRequest( network_site_url(), self::KEY );
27
+ if ( is_wp_error( $headers ) ) {
28
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ), 'tweaks' );
29
+
30
+ return false;
31
+ }
32
+
33
+ if ( isset( $headers['feature-policy'] ) ) {
34
+ $data = $settings->getDValues( self::KEY );
35
+ if ( $data === null ) {
36
+ $data['somewhere'] = true;
37
+ $settings->setDValues( self::KEY, $data );
38
+ }
39
+
40
+ return true;
41
+ }
42
+
43
+ $data = $settings->getDValues( self::KEY );
44
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [
45
+ 'self',
46
+ 'allow',
47
+ 'origins',
48
+ 'none'
49
+ ] ) ) {
50
+ return true;
51
+ }
52
+
53
+ return false;
54
+ }
55
+
56
+ /**
57
+ * @param $mode
58
+ * @param $values
59
+ * @param $scenario
60
+ *
61
+ * @return bool|\WP_Error
62
+ */
63
+ public function process() {
64
+ $mode = $this->mode;
65
+ $scenario = $this->scenario;
66
+ $values = $this->values;
67
+ if ( empty( $mode ) || ! in_array( $mode, [ 'self', 'allow', 'origins', 'none' ] ) ) {
68
+ return new \WP_Error( Error_Code::INVALID, __( "Mode empty or invalid", "defender-security" ) );
69
+ }
70
+ $values = trim( $values );
71
+ $values = sanitize_textarea_field( $values );
72
+ $values = explode( PHP_EOL, $values );
73
+ foreach ( $values as $key => $url ) {
74
+ if ( filter_var( $url, FILTER_VALIDATE_URL ) == false ) {
75
+ unset( $values[ $key ] );
76
+ }
77
+ }
78
+ $settings = Settings::instance();
79
+ $data = $settings->getDValues( self::KEY );
80
+ $data['mode'] = $mode;
81
+ $data['values'] = $values;
82
+ if ( $scenario == 'enforce' ) {
83
+ unset( $data['somewhere'] );
84
+ }
85
+ $settings->setDValues( self::KEY, $data );
86
+ }
87
+
88
+ public function revert() {
89
+ $settings = Settings::instance();
90
+ $settings->setDValues( self::KEY, null );
91
+ }
92
+
93
+ public function listen() {
94
+
95
+ }
96
+ }
app/module/hardener/component/sh-feature-policy.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_Feature_Policy extends Rule {
13
+ static $slug = 'sh-feature-policy';
14
+ static $service;
15
+
16
+ public function getDescription() {
17
+
18
+ }
19
+
20
+ public function check() {
21
+ return $this->getService()->check();
22
+ }
23
+
24
+ /**
25
+ * @return array
26
+ */
27
+ public function getMiscData() {
28
+ $settings = Settings::instance();
29
+ $data = $settings->getDValues( Sh_Feature_Policy_Service::KEY );
30
+
31
+ return [
32
+ 'mode' => is_array( $data ) && isset( $data['mode'] ) ? $data['mode'] : 'self',
33
+ 'values' => is_array( $data ) && isset( $data['values'] ) ? $data['values'] : array(),
34
+ ];
35
+ }
36
+
37
+ /**
38
+ * This will return the short summary why this rule show up as issue
39
+ *
40
+ * @return string
41
+ */
42
+ function getErrorReason() {
43
+ return __( "The Feature-Policy header isn't enforced. All browser features are accessible when embedded through an iframe.", "defender-security" );
44
+ }
45
+
46
+ /**
47
+ * This will return a short summary to show why this rule works
48
+ * @return mixed
49
+ */
50
+ function getSuccessReason() {
51
+ return __( "You've enforced Feature-Policy, good job!", "defender-security" );
52
+ }
53
+
54
+ public function addHooks() {
55
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
56
+ $this->addAction( 'wp', 'appendHeader', PHP_INT_MAX );
57
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
58
+ }
59
+
60
+ public function revert() {
61
+ $this->getService()->revert();
62
+ Settings::instance()->addToIssues( self::$slug );
63
+ }
64
+
65
+ public function appendHeader() {
66
+ if ( headers_sent() ) {
67
+ //header already sent, do nothing
68
+ return;
69
+ }
70
+ $settings = Settings::instance();
71
+ $data = $settings->getDValues( Sh_Feature_Policy_Service::KEY );
72
+ if ( ! $this->maybeSubmitHeader( 'Feature-Policy', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
73
+ //this mean Defender can't override the already output, marked to show notification
74
+ $data['overrideable'] = false;
75
+ $settings->setDValues( Sh_Feature_Policy_Service::KEY, $data );
76
+
77
+ return;
78
+ }
79
+ if ( is_array( $data ) && in_array( $data['mode'], [ 'self', 'allow', 'origins', 'none' ] ) ) {
80
+ $headers = '';
81
+ $features = [
82
+ 'accelerometer',
83
+ 'ambient-light-sensor',
84
+ 'autoplay',
85
+ 'camera',
86
+ 'encrypted-media',
87
+ 'fullscreen',
88
+ 'geolocation',
89
+ 'gyroscope',
90
+ 'magnetometer',
91
+ 'microphone',
92
+ 'midi',
93
+ 'payment',
94
+ 'picture-in-picture',
95
+ 'speaker',
96
+ 'usb',
97
+ //'vibrate',
98
+ 'vr'
99
+ ];
100
+ switch ( $data['mode'] ) {
101
+ case 'self':
102
+ array_walk( $features, function ( &$value, $key ) {
103
+ $value .= " 'self'";
104
+ } );
105
+ $headers = 'Feature-Policy: ' . implode( '; ', $features );
106
+ break;
107
+ case 'allow':
108
+ array_walk( $features, function ( &$value, $key ) {
109
+ $value .= " *";
110
+ } );
111
+ $headers = 'Feature-Policy: ' . implode( '; ', $features );
112
+ break;
113
+ case 'origins':
114
+ $urls = implode( ' ', $data['values'] );
115
+ array_walk( $features, function ( &$value, $key ) use ( $urls ) {
116
+ $value .= " " . $urls;
117
+ } );
118
+ $headers = 'Feature-Policy: ' . implode( '; ', $features );
119
+ break;
120
+ case 'none':
121
+ array_walk( $features, function ( &$value, $key ) {
122
+ $value .= " 'none'";
123
+ } );
124
+ $headers = 'Feature-Policy: ' . implode( '; ', $features );
125
+ break;
126
+ default:
127
+ break;
128
+ }
129
+ if ( strlen( $headers ) > 0 ) {
130
+ header( trim( $headers ) );
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * @return string
137
+ */
138
+ public function getTitle() {
139
+ return __( "Feature-Policy Security Header", "defender-security" );
140
+ }
141
+
142
+ /**
143
+ * Store a flag that we enable this
144
+ * @return mixed|void
145
+ */
146
+ public function process() {
147
+ //calling the service
148
+ $mode = HTTP_Helper::retrievePost( 'mode' );
149
+ $values = HTTP_Helper::retrievePost( 'values' );
150
+ $scenario = HTTP_Helper::retrievePost( 'scenario' );
151
+ $service = $this->getService();
152
+ $service->mode = $mode;
153
+ $service->values = $values;
154
+ $service->scenario = $scenario;
155
+ $ret = $service->process();
156
+ if ( ! is_wp_error( $ret ) ) {
157
+ Settings::instance()->addToResolved( self::$slug );
158
+ } else {
159
+ wp_send_json_error( [
160
+ 'message' => $ret->get_error_message()
161
+ ] );
162
+ }
163
+ }
164
+
165
+ /**
166
+ * @return Sh_Feature_Policy_Service
167
+ */
168
+ public function getService() {
169
+ if ( self::$service == null ) {
170
+ self::$service = new Sh_Feature_Policy_Service();
171
+ }
172
+
173
+ return self::$service;
174
+ }
175
+ }
app/module/hardener/component/sh-referrer-policy-service.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_Referrer_Policy_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_referrer_policy';
17
+ public $mode;
18
+ public $scenario;
19
+
20
+ /**
21
+ * @return bool
22
+ */
23
+ public function check() {
24
+ $settings = Settings::instance();
25
+ $headers = $this->headRequest( network_site_url(), self::KEY );
26
+ if ( is_wp_error( $headers ) ) {
27
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ), 'tweaks' );
28
+
29
+ return false;
30
+ }
31
+
32
+ if ( isset( $headers['referrer-policy'] ) ) {
33
+ $data = $settings->getDValues( self::KEY );
34
+ if ( $data === null ) {
35
+ $data['somewhere'] = true;
36
+ $settings->setDValues( self::KEY, $data );
37
+ }
38
+
39
+ return true;
40
+ }
41
+
42
+ $data = $settings->getDValues( self::KEY );
43
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [
44
+ 'no-referrer',
45
+ 'no-referrer-when-downgrade',
46
+ 'origin',
47
+ 'origin-when-cross-origin',
48
+ 'same-origin',
49
+ 'strict-origin',
50
+ 'strict-origin-when-cross-origin',
51
+ 'unsafe-url'
52
+ ] ) ) {
53
+ return true;
54
+ }
55
+
56
+ return false;
57
+ }
58
+
59
+ /**
60
+ * @return bool|\WP_Error
61
+ */
62
+ public function process() {
63
+ $mode = $this->mode;
64
+ $scenario = $this->scenario;
65
+ if ( empty( $mode ) || ! in_array( $mode, [
66
+ 'no-referrer',
67
+ 'no-referrer-when-downgrade',
68
+ 'origin',
69
+ 'origin-when-cross-origin',
70
+ 'same-origin',
71
+ 'strict-origin',
72
+ 'strict-origin-when-cross-origin',
73
+ 'unsafe-url'
74
+ ] ) ) {
75
+ return new \WP_Error( Error_Code::INVALID, __( "Mode empty or invalid", "defender-security" ) );
76
+ }
77
+ $settings = Settings::instance();
78
+ $data = $settings->getDValues( self::KEY );
79
+ $data['mode'] = $mode;
80
+ $data = [
81
+ 'mode' => $mode
82
+ ];
83
+ if ( $scenario == 'enforce' ) {
84
+ unset( $data['somewhere'] );
85
+ }
86
+ $settings->setDValues( self::KEY, $data );
87
+ }
88
+
89
+ public function revert() {
90
+ $settings = Settings::instance();
91
+ $settings->setDValues( self::KEY, null );
92
+ }
93
+
94
+ public function listen() {
95
+
96
+ }
97
+ }
app/module/hardener/component/sh-referrer-policy.php ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_Referrer_Policy extends Rule {
13
+ static $slug = 'sh-referrer-policy';
14
+ static $service;
15
+
16
+ public function getDescription() {
17
+
18
+ }
19
+
20
+ public function check() {
21
+ return $this->getService()->check();
22
+ }
23
+
24
+ /**
25
+ * @return array
26
+ */
27
+ public function getMiscData() {
28
+ $settings = Settings::instance();
29
+ $data = $settings->getDValues( Sh_Referrer_Policy_Service::KEY );
30
+
31
+ return [
32
+ 'mode' => is_array( $data ) && isset( $data['mode'] ) ? $data['mode'] : 'origin-when-cross-origin',
33
+ 'values' => is_array( $data ) && isset( $data['values'] ) ? $data['values'] : [],
34
+ ];
35
+ }
36
+
37
+ /**
38
+ * This will return the short summary why this rule show up as issue
39
+ *
40
+ * @return string
41
+ */
42
+ function getErrorReason() {
43
+ return __( "Referrer policy header isn't active. We recommend you choose a policy from the options below.", "defender-security" );
44
+ }
45
+
46
+ /**
47
+ * This will return a short summary to show why this rule works
48
+ * @return mixed
49
+ */
50
+ function getSuccessReason() {
51
+ return __( "You've enforced Referrer policy, well done!", "defender-security" );
52
+ }
53
+
54
+ public function addHooks() {
55
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
56
+ $this->addAction( 'wp', 'appendHeader', PHP_INT_MAX );
57
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
58
+ }
59
+
60
+ public function revert() {
61
+ $this->getService()->revert();
62
+ Settings::instance()->addToIssues( self::$slug );
63
+ }
64
+
65
+ public function appendHeader() {
66
+ if ( headers_sent() ) {
67
+ //header already sent, do nothing
68
+ return;
69
+ }
70
+ $settings = Settings::instance();
71
+ $data = $settings->getDValues( Sh_Referrer_Policy_Service::KEY );
72
+ if ( ! $this->maybeSubmitHeader( 'Referrer-Policy', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
73
+ //this mean Defender can't override the already output, marked to show notification
74
+ $data['overrideable'] = false;
75
+ $settings->setDValues( Sh_Referrer_Policy_Service::KEY, $data );
76
+
77
+ return;
78
+ }
79
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [
80
+ 'no-referrer',
81
+ 'no-referrer-when-downgrade',
82
+ 'origin',
83
+ 'origin-when-cross-origin',
84
+ 'same-origin',
85
+ 'strict-origin',
86
+ 'strict-origin-when-cross-origin',
87
+ 'unsafe-url'
88
+ ] ) ) {
89
+ $headers = 'Referrer-Policy: ' . $data['mode'];
90
+ header( $headers );
91
+ }
92
+ }
93
+
94
+ /**
95
+ * @return string
96
+ */
97
+ public function getTitle() {
98
+ return __( "Referrer Policy Security Header", "defender-security" );
99
+ }
100
+
101
+ /**
102
+ * Store a flag that we enable this
103
+ * @return mixed|void
104
+ */
105
+ public function process() {
106
+ //calling the service
107
+ $mode = HTTP_Helper::retrievePost( 'mode' );
108
+ $scenario = HTTP_Helper::retrievePost( 'scenario' );
109
+ $service = $this->getService();
110
+ $service->mode = $mode;
111
+ $service->scenario = $scenario;
112
+ $ret = $service->process();
113
+ if ( ! is_wp_error( $ret ) ) {
114
+ Settings::instance()->addToResolved( self::$slug );
115
+ } else {
116
+ wp_send_json_error( [
117
+ 'message' => $ret->get_error_message()
118
+ ] );
119
+ }
120
+ }
121
+
122
+ /**
123
+ * @return Sh_Referrer_Policy_Service
124
+ */
125
+ public function getService() {
126
+ if ( self::$service == null ) {
127
+ self::$service = new Sh_Referrer_Policy_Service();
128
+ }
129
+
130
+ return self::$service;
131
+ }
132
+ }
app/module/hardener/component/sh-strict-transport-service.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_Strict_Transport_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_strict_transport';
17
+ public $hsts;
18
+ public $include_subdomain;
19
+ public $hsts_cache_duration;
20
+ public $scenario;
21
+
22
+ /**
23
+ * @return bool
24
+ */
25
+ public function check() {
26
+ $settings = Settings::instance();
27
+
28
+ $headers = $this->headRequest( network_site_url(), self::KEY );
29
+ if ( is_wp_error( $headers ) ) {
30
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ),'tweaks' );
31
+
32
+ return false;
33
+ }
34
+ if ( isset( $headers['strict-transport-security'] ) ) {
35
+ $data = $settings->getDValues( self::KEY );
36
+ if ( $data === null ) {
37
+ $data = [ 'somewhere' => true ];
38
+ //someone applied first, we need to get the current settings
39
+ $content = explode( ';', $headers['strict-transport-security'] );
40
+ foreach ( $content as $line ) {
41
+ if ( stristr( $line, 'max-age' ) ) {
42
+ $value = explode( '=', $line );
43
+ $arr = [
44
+ '1 hour' => 1 * 3600,
45
+ '24 hours' => 86400,
46
+ '7 days' => 7 * 86400,
47
+ '3 months' => ( 3 * 30 + 1 ) * 86400,
48
+ '6 months' => ( 6 * 30 + 3 ) * 86400,
49
+ '12 months' => 365 * 86400
50
+ ];
51
+ $seconds = $value[1];
52
+ $closest = null;
53
+ $key = null;
54
+ //get the closest
55
+ foreach ( $arr as $k => $item ) {
56
+ if ( $closest === null || abs( $seconds - $closest ) > abs( $item - $seconds ) ) {
57
+ $closest = $item;
58
+ $key = $k;
59
+ }
60
+ }
61
+ $data['hsts_cache_duration'] = $key;
62
+ } elseif ( stristr( $line, 'preload' ) ) {
63
+ $data['hsts_preload'] = 1;
64
+ } elseif ( stristr( $line, 'includeSubDomains' ) ) {
65
+ $data['include_subdomain'] = 1;
66
+ }
67
+ }
68
+ $settings->setDValues( self::KEY, $data );
69
+ }
70
+
71
+ return true;
72
+ }
73
+
74
+ $data = $settings->getDValues( self::KEY );
75
+ if ( is_array( $data ) && isset( $data['hsts_cache_duration'] ) ) {
76
+ return true;
77
+ }
78
+
79
+ return false;
80
+ }
81
+
82
+ /**
83
+ * @return bool|\WP_Error
84
+ */
85
+ public function process() {
86
+ $hsts = $this->hsts;
87
+ $include_subdomain = $this->include_subdomain;
88
+ $hsts_cache_duration = $this->hsts_cache_duration;
89
+ $scenario = $this->scenario;
90
+ $settings = Settings::instance();
91
+ $data = $settings->getDValues( self::KEY );
92
+ if ( ! is_array( $data ) ) {
93
+ $data = [];
94
+ }
95
+ $data['hsts_preload'] = $hsts;
96
+ $data['include_subdomain'] = $include_subdomain;
97
+ $data['hsts_cache_duration'] = $hsts_cache_duration;
98
+ if ( $scenario == 'enforce' ) {
99
+ unset( $data['somewhere'] );
100
+ }
101
+ $settings->setDValues( self::KEY, $data );
102
+ }
103
+
104
+ public function revert() {
105
+ $settings = Settings::instance();
106
+ $settings->setDValues( self::KEY, null );
107
+ }
108
+
109
+ public function listen() {
110
+
111
+ }
112
+ }
app/module/hardener/component/sh-strict-transport.php ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Module\Hardener\Model\Settings;
11
+ use WP_Defender\Module\Hardener\Rule;
12
+
13
+ class Sh_Strict_Transport extends Rule {
14
+ static $slug = 'sh-strict-transport';
15
+ static $service;
16
+
17
+ public function getDescription() {
18
+
19
+ }
20
+
21
+ public function check() {
22
+ return $this->getService()->check();
23
+ }
24
+
25
+ public function getMiscData() {
26
+ $settings = Settings::instance();
27
+ $data = $settings->getDValues( Sh_Strict_Transport_Service::KEY );
28
+ //we need to check if this is root domain to show the subdomain option
29
+ $site_url = network_site_url();
30
+ $domain_data = Utils::instance()->parseDomain( $site_url );
31
+ $allow_subdomain = false;
32
+ if ( is_array( $domain_data ) && ! isset( $domain_data['subdomain'] ) ) {
33
+ $allow_subdomain = true;
34
+ }
35
+
36
+ return [
37
+ 'hsts_preload' => is_array( $data ) && isset( $data['hsts_preload'] ) ? $data['hsts_preload'] : 0,
38
+ 'include_subdomain' => is_array( $data ) && isset( $data['include_subdomain'] ) ? $data['include_subdomain'] : 0,
39
+ 'hsts_cache_duration' => is_array( $data ) && isset( $data['hsts_cache_duration'] ) ? $data['hsts_cache_duration'] : '7 days',
40
+ 'somewhere' => is_array( $data ) && isset( $data['somewhere'] ) ? $data['somewhere'] : false,
41
+ 'allow_subdomain' => $allow_subdomain
42
+ ];
43
+ }
44
+
45
+ /**
46
+ * This will return the short summary why this rule show up as issue
47
+ *
48
+ * @return string
49
+ */
50
+ function getErrorReason() {
51
+ return __( "The Strict Transport Security header isn't enforced. Visitors and bots can access your site without https.", "defender-security" );
52
+ }
53
+
54
+ /**
55
+ * This will return a short summary to show why this rule works
56
+ * @return mixed
57
+ */
58
+ function getSuccessReason() {
59
+ return __( "You've enforced Strict Transport, well done!", "defender-security" );
60
+ }
61
+
62
+ public function addHooks() {
63
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
64
+ $this->addAction( 'wp', 'appendHeader', PHP_INT_MAX );
65
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
66
+ }
67
+
68
+ public function revert() {
69
+ $this->getService()->revert();
70
+ Settings::instance()->addToIssues( self::$slug );
71
+ }
72
+
73
+ public function appendHeader() {
74
+ if ( headers_sent() ) {
75
+ //header already sent, do nothing
76
+ return;
77
+ }
78
+
79
+ $settings = Settings::instance();
80
+ $data = $settings->getDValues( Sh_Strict_Transport_Service::KEY );
81
+ if ( ! $this->maybeSubmitHeader( 'Strict-Transport-Security', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
82
+ $data['overrideable'] = false;
83
+ $settings->setDValues( Sh_Strict_Transport_Service::KEY, $data );
84
+
85
+ return;
86
+ }
87
+ if ( is_array( $data ) && isset( $data['hsts_cache_duration'] ) ) {
88
+ $arr = [
89
+ '1 hour' => 1 * 3600,
90
+ '24 hours' => 86400,
91
+ '7 days' => 7 * 86400,
92
+ '3 months' => ( 3 * 30 + 1 ) * 86400,
93
+ '6 months' => ( 6 * 30 + 3 ) * 86400,
94
+ '12 months' => 365 * 86400
95
+ ];
96
+ $seconds = isset( $arr[ $data['hsts_cache_duration'] ] ) ? $arr[ $data['hsts_cache_duration'] ] : null;
97
+ if ( $seconds === null ) {
98
+ return;
99
+ }
100
+ $headers = 'Strict-Transport-Security: max-age=' . $seconds;
101
+ if ( $data['include_subdomain'] == 1 ) {
102
+ $headers .= ' ; includeSubDomains';
103
+ }
104
+ if ( $data['hsts_preload'] == 1 ) {
105
+ $headers .= ' ; preload';
106
+ }
107
+
108
+ header( $headers );
109
+ }
110
+ }
111
+
112
+ /**
113
+ * @return string
114
+ */
115
+ public function getTitle() {
116
+ return __( "Strict Transport Security Header", "defender-security" );
117
+ }
118
+
119
+ /**
120
+ * Store a flag that we enable this
121
+ * @return mixed|void
122
+ */
123
+ public function process() {
124
+ $service = $this->getService();
125
+ $service->hsts = HTTP_Helper::retrievePost( 'hsts_preload' );
126
+ $service->include_subdomain = HTTP_Helper::retrievePost( 'include_subdomain' );
127
+ $service->hsts_cache_duration = HTTP_Helper::retrievePost( 'hsts_cache_duration' );
128
+ $service->scenario = HTTP_Helper::retrievePost( 'scenario' );
129
+ $ret = $service->process();
130
+ if ( is_wp_error( $ret ) ) {
131
+ wp_send_json_error( [
132
+ 'message' => $ret->get_error_message()
133
+ ] );
134
+ } else {
135
+ Settings::instance()->addToResolved( Sh_Strict_Transport::$slug );
136
+ }
137
+ }
138
+
139
+ /**
140
+ * @return Sh_Strict_Transport_Service
141
+ */
142
+ public function getService() {
143
+ if ( self::$service == null ) {
144
+ self::$service = new Sh_Strict_Transport_Service();
145
+ }
146
+
147
+ return self::$service;
148
+ }
149
+ }
app/module/hardener/component/sh-x-frame-service.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_X_Frame_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_xframe';
17
+ public $mode;
18
+ public $values;
19
+ public $scenario;
20
+
21
+ /**
22
+ * @return bool
23
+ */
24
+ public function check() {
25
+ $settings = Settings::instance();
26
+
27
+ $headers = $this->headRequest( network_site_url(), self::KEY );
28
+
29
+ if ( is_wp_error( $headers ) ) {
30
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ),'tweaks' );
31
+
32
+ return false;
33
+ }
34
+ if ( isset( $headers['x-frame-options'] ) ) {
35
+ $settings = Settings::instance();
36
+ $data = $settings->getDValues( self::KEY );
37
+ if ( $data === null ) {
38
+ $data = [ 'somewhere' => true ];
39
+ $content = strtolower( trim( $headers['x-frame-options'] ) );
40
+ if ( stristr( $content, 'allow-from' ) ) {
41
+ $data['mode'] = 'allow-from';
42
+ $urls = explode( ' ', $content );
43
+ unset( $urls[0] );
44
+ $data['values'] = implode( PHP_EOL, $urls );
45
+ } elseif ( in_array( strtolower( $content ), [ 'sameorigin', 'deny' ] ) ) {
46
+ $data['mode'] = strtolower( $content );
47
+ }
48
+ $settings->setDValues( self::KEY, $data );
49
+ }
50
+
51
+ return true;
52
+ }
53
+ $data = $settings->getDValues( self::KEY );
54
+ if ( is_array( $data ) && in_array( $data['mode'], [ 'sameorigin', 'allow-from', 'deny' ] ) ) {
55
+ return true;
56
+ }
57
+
58
+ return false;
59
+ }
60
+
61
+ /**
62
+ * @return bool|\WP_Error
63
+ */
64
+ public function process() {
65
+ $mode = $this->mode;
66
+ $values = $this->values;
67
+ $scenario = $this->scenario;
68
+ if ( empty( $mode ) || ! in_array( $mode, [ 'sameorigin', 'allow-from', 'deny' ] ) ) {
69
+ return new \WP_Error( Error_Code::INVALID, __( "Mode empty or invalid", "defender-security" ) );
70
+ }
71
+ $values = trim( $values );
72
+ $values = sanitize_textarea_field( $values );
73
+ $values = explode( PHP_EOL, $values );
74
+ foreach ( $values as $key => $url ) {
75
+ if ( filter_var( $url, FILTER_VALIDATE_URL ) == false ) {
76
+ unset( $values[ $key ] );
77
+ }
78
+ }
79
+ $settings = Settings::instance();
80
+ $data = $settings->getDValues( self::KEY );
81
+ if ( ! is_array( $data ) ) {
82
+ $data = [];
83
+ }
84
+ $data['mode'] = $mode;
85
+ $data['values'] = implode( ' ', $values );
86
+ if ( $scenario == 'enforce' ) {
87
+ unset( $data['somewhere'] );
88
+ }
89
+ $settings->setDValues( self::KEY, $data );
90
+
91
+ return true;
92
+ }
93
+
94
+ public function revert() {
95
+ $settings = Settings::instance();
96
+ $settings->setDValues( self::KEY, null );
97
+ }
98
+
99
+ public function listen() {
100
+
101
+ }
102
+ }
app/module/hardener/component/sh-x-frame.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_X_Frame extends Rule {
13
+ const KEY = 'sh_xframe';
14
+ static $slug = 'sh-xframe';
15
+ static $service;
16
+
17
+ public function getDescription() {
18
+ $this->renderPartial( 'rules/security-headers-x-frame' );
19
+ }
20
+
21
+ public function check() {
22
+ return $this->getService()->check();
23
+ }
24
+
25
+ public function getMiscData() {
26
+ $settings = Settings::instance();
27
+ $data = $settings->getDValues( self::KEY );
28
+
29
+ return [
30
+ 'intro_text' => esc_html__( "The X-Frame-Options HTTP response header controls whether or not a browser can render a webpage inside a <frame>, <iframe> or <object> tag. Websites can avoid clickjacking attacks by ensuring that their content isn't embedded into other websites.", "defender-security" ),
31
+ 'mode' => is_array( $data ) && isset( $data['mode'] ) ? $data['mode'] : 'sameorigin',
32
+ 'values' => is_array( $data ) && isset( $data['values'] ) ? $data['values'] : array(),
33
+ ];
34
+ }
35
+
36
+ /**
37
+ * This will return the short summary why this rule show up as issue
38
+ *
39
+ * @return string
40
+ */
41
+ function getErrorReason() {
42
+ return __( "The X-Frame-Option header isn't enforced, so anyone can embed your web pages.", "defender-security" );
43
+ }
44
+
45
+ /**
46
+ * This will return a short summary to show why this rule works
47
+ * @return mixed
48
+ */
49
+ function getSuccessReason() {
50
+ return __( "You've enforced X-Frame-Options, good stuff.", "defender-security" );
51
+ }
52
+
53
+ public function addHooks() {
54
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
55
+ $this->addAction( 'wp', 'appendHeader', PHP_INT_MAX );
56
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
57
+ }
58
+
59
+ public function revert() {
60
+ $this->getService()->revert();
61
+ Settings::instance()->addToIssues( self::$slug );
62
+ }
63
+
64
+ public function appendHeader() {
65
+ if ( headers_sent() ) {
66
+ //header already sent, do nothing
67
+ return;
68
+ }
69
+ $settings = Settings::instance();
70
+ $data = $settings->getDValues( self::KEY );
71
+
72
+ if ( ! $this->maybeSubmitHeader( 'X-Frame-Options', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
73
+ $data['overrideable'] = false;
74
+
75
+ return;
76
+ }
77
+
78
+ if ( is_array( $data ) && in_array( $data['mode'], [ 'sameorigin', 'allow-from', 'deny' ] ) ) {
79
+ if ( ! isset( $data['values'] ) ) {
80
+ $data['values'] = '';
81
+ }
82
+ $headers = 'X-Frame-Options: ' . $data['mode'];
83
+ if ( $data['mode'] == 'allow-from' ) {
84
+ $headers .= ' ' . $data['values'];
85
+ }
86
+ header( trim( $headers ) );
87
+ }
88
+ }
89
+
90
+ /**
91
+ * @return string
92
+ */
93
+ public function getTitle() {
94
+ return __( "X-Frame-Options Security Header", "defender-security" );
95
+ }
96
+
97
+ /**
98
+ * Store a flag that we enable this
99
+ * @return mixed|void
100
+ */
101
+ public function process() {
102
+ $service = $this->getService();
103
+ $service->mode = HTTP_Helper::retrievePost( 'mode' );
104
+ $service->values = HTTP_Helper::retrievePost( 'values' );
105
+ $service->scenario = HTTP_Helper::retrievePost( 'scenario' );
106
+ $ret = $this->getService()->process();
107
+ if ( is_wp_error( $ret ) ) {
108
+ wp_send_json_error( [
109
+ 'message' => $ret->get_error_message()
110
+ ] );
111
+ }
112
+ Settings::instance()->addToResolved( self::$slug );
113
+ }
114
+
115
+ /**
116
+ * @return Sh_X_Frame_Service
117
+ */
118
+ public function getService() {
119
+ if ( self::$service == null ) {
120
+ self::$service = new Sh_X_Frame_Service();
121
+ }
122
+
123
+ return self::$service;
124
+ }
125
+ }
app/module/hardener/component/sh-xss-protection-service.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Component\Error_Code;
11
+ use WP_Defender\Module\Hardener\IRule_Service;
12
+ use WP_Defender\Module\Hardener\Model\Settings;
13
+ use WP_Defender\Module\Hardener\Rule_Service;
14
+
15
+ class Sh_XSS_Protection_Service extends Rule_Service implements IRule_Service {
16
+ const KEY = 'sh_xss_protection';
17
+ static $error;
18
+ public $mode;
19
+ public $scenario;
20
+
21
+ /**
22
+ * @return bool
23
+ */
24
+ public function check() {
25
+ //priority to get check from db first
26
+ $settings = Settings::instance();
27
+
28
+ $headers = $this->headRequest( network_site_url(), self::KEY );
29
+ if ( is_wp_error( $headers ) ) {
30
+ Utils::instance()->log( sprintf( 'Self ping error: %s', $headers->get_error_message() ),'tweaks' );
31
+
32
+ return false;
33
+ }
34
+ if ( isset( $headers['x-xss-protection'] ) ) {
35
+ $data = $settings->getDValues( self::KEY );
36
+ if ( $data === null ) {
37
+ $data['somewhere'] = true;
38
+ $content = strtolower( trim( $headers['x-xss-protection'] ) );
39
+ $content = explode( ';', $content );
40
+ if ( count( $content ) == 1 ) {
41
+ $data['mode'] = 'sanitize';
42
+ } else {
43
+ $content = explode( '=', $content[1] );
44
+ $data['mode'] = $content[1];
45
+ }
46
+ $settings->setDValues( self::KEY, $data );
47
+ }
48
+
49
+ return true;
50
+ }
51
+ $data = $settings->getDValues( self::KEY );
52
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [
53
+ 'sanitize',
54
+ 'block',
55
+ 'none'
56
+ ] ) ) {
57
+ //we can't check by request but we activate this
58
+ return true;
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * @return bool|\WP_Error
66
+ */
67
+ public function process() {
68
+ $mode = $this->mode;
69
+ $scenario = $this->scenario;
70
+ if ( empty( $mode ) || ! in_array( $mode, [ 'sanitize', 'block', 'none' ] ) ) {
71
+ return new \WP_Error( Error_Code::INVALID, __( "Mode empty or invalid", "defender-security" ) );
72
+ }
73
+ $settings = Settings::instance();
74
+ $data = $settings->getDValues( self::KEY );
75
+ $data['mode'] = $mode;
76
+ if ( $scenario == 'enforce' ) {
77
+ unset( $data['somewhere'] );
78
+ }
79
+ $settings->setDValues( self::KEY, $data );
80
+ }
81
+
82
+ public function revert() {
83
+ $settings = Settings::instance();
84
+ $settings->setDValues( self::KEY, null );
85
+ }
86
+ }
app/module/hardener/component/sh-xss-protection.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class Sh_XSS_Protection extends Rule {
13
+ const KEY = 'sh_xss_protection';
14
+ static $slug = 'sh-xss-protection';
15
+ static $service;
16
+
17
+ public function getDescription() {
18
+
19
+ }
20
+
21
+ public function check() {
22
+ return $this->getService()->check();
23
+ }
24
+
25
+ /**
26
+ * @return array
27
+ */
28
+ public function getMiscData() {
29
+ $settings = Settings::instance();
30
+ $data = $settings->getDValues( self::KEY );
31
+ $miscs = [
32
+ 'mode' => is_array( $data ) && isset( $data['mode'] ) ? $data['mode'] : 'sanitize',
33
+ ];
34
+
35
+ return $miscs;
36
+ }
37
+
38
+ /**
39
+ * This will return the short summary why this rule show up as issue
40
+ *
41
+ * @return string
42
+ */
43
+ function getErrorReason() {
44
+ return __( "The X-XSS-Protection header isn't enforced. Older browsers are at risk of XSS attacks.", "defender-security" );
45
+ }
46
+
47
+ /**
48
+ * This will return a short summary to show why this rule works
49
+ * @return mixed
50
+ */
51
+ function getSuccessReason() {
52
+ return __( "You've enforced X-XSS-Protection, good stuff.", "defender-security" );
53
+ }
54
+
55
+ public function addHooks() {
56
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
57
+ $this->addAction( 'wp_loaded', 'appendHeader', 999 );
58
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
59
+ }
60
+
61
+ public function revert() {
62
+ $this->getService()->revert();
63
+ Settings::instance()->addToIssues( self::$slug );
64
+ }
65
+
66
+ public function appendHeader() {
67
+ if ( headers_sent() ) {
68
+ //header already sent, do nothing
69
+ return;
70
+ }
71
+ $settings = Settings::instance();
72
+ $data = $settings->getDValues( self::KEY );
73
+ if ( ! $this->maybeSubmitHeader( 'X-XSS-Protection', isset( $data['somewhere'] ) ? $data['somewhere'] : false ) ) {
74
+ $data['overrideable'] = false;
75
+
76
+ return;
77
+ }
78
+ if ( is_array( $data ) && in_array( $data['mode'], [ 'sanitize', 'block', 'none' ] ) ) {
79
+ $headers = '';
80
+ switch ( $data['mode'] ) {
81
+ case 'sanitize':
82
+ $headers = 'X-XSS-Protection: 1';
83
+ break;
84
+ case 'block':
85
+ $headers = 'X-XSS-Protection: 1; mode=block';
86
+ break;
87
+ default:
88
+ break;
89
+ }
90
+ if ( strlen( $headers ) > 0 ) {
91
+ header( trim( $headers ) );
92
+ }
93
+ }
94
+ }
95
+
96
+ /**
97
+ * @return string
98
+ */
99
+ public function getTitle() {
100
+ return __( "X-XSS-Protection Security Header", "defender-security" );
101
+ }
102
+
103
+ /**
104
+ * Store a flag that we enable this
105
+ * @return mixed|void
106
+ */
107
+ public function process() {
108
+ $service = $this->getService();
109
+ $service->mode = HTTP_Helper::retrievePost( 'mode' );
110
+ $service->scenario = HTTP_Helper::retrievePost( 'scenario' );
111
+ $ret = $service->process();
112
+ if ( is_wp_error( $ret ) ) {
113
+ wp_send_json_error( [ 'message' => $ret->get_error_message() ] );
114
+ }
115
+ Settings::instance()->addToResolved( self::$slug );
116
+ }
117
+
118
+ /**
119
+ * @return Sh_XSS_Protection_Service
120
+ */
121
+ public function getService() {
122
+ if ( self::$service == null ) {
123
+ self::$service = new Sh_XSS_Protection_Service();
124
+ }
125
+
126
+ return self::$service;
127
+ }
128
+ }
app/module/hardener/component/wp-rest-api-service.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\IRule_Service;
10
+ use WP_Defender\Module\Hardener\Model\Settings;
11
+ use WP_Defender\Module\Hardener\Rule_Service;
12
+
13
+ class WP_Rest_API_Service extends Rule_Service implements IRule_Service {
14
+ const KEY = 'wp_rest_api';
15
+
16
+ /**
17
+ * @return bool
18
+ */
19
+ public function process() {
20
+ //$mode = HTTP_Helper::retrievePost( 'mode' );
21
+ //always this
22
+ $mode = 'allow-auth';
23
+ $settings = Settings::instance();
24
+ $data = $settings->getDValues( self::KEY );
25
+ if ( in_array( $mode, [ 'allow-auth', 'allow-all' ] ) ) {
26
+ $data['mode'] = $mode;
27
+ $settings->setDValues( self::KEY, $data );
28
+ }
29
+ }
30
+
31
+ /**
32
+ * @return bool
33
+ */
34
+ public function revert() {
35
+ $settings = Settings::instance();
36
+ $settings->setDValues( self::KEY, null );
37
+ }
38
+
39
+ /**
40
+ * @return mixed
41
+ */
42
+ public function check() {
43
+ $settings = Settings::instance();
44
+ $data = $settings->getDValues( self::KEY );
45
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [ 'allow-auth', 'allow-all' ] ) ) {
46
+ return true;
47
+ }
48
+
49
+ return false;
50
+ }
51
+ }
app/module/hardener/component/wp-rest-api.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Component;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use WP_Defender\Module\Hardener\Model\Settings;
10
+ use WP_Defender\Module\Hardener\Rule;
11
+
12
+ class WP_Rest_Api extends Rule {
13
+ static $slug = 'wp-rest-api';
14
+ static $service;
15
+
16
+ function getDescription() {
17
+
18
+ }
19
+
20
+ /**
21
+ * @return bool
22
+ */
23
+ function check() {
24
+ return $this->getService()->check();
25
+ }
26
+
27
+ /**
28
+ * This will return the short summary why this rule show up as issue
29
+ *
30
+ * @return string
31
+ */
32
+ function getErrorReason() {
33
+ return __( "The WordPress Rest API is publicly accessible. You may want to prevent unauthorized requests.", "defender-security" );
34
+ }
35
+
36
+ /**
37
+ * This will return a short summary to show why this rule works
38
+ * @return mixed
39
+ */
40
+ function getSuccessReason() {
41
+ $mode = $this->getMiscData()['mode'];
42
+ if ( $mode == 'allow-all' ) {
43
+ return __( "You are currently allowing all requests to the REST API, good job!", "defender-security" );
44
+ }
45
+
46
+ return __( "You are currently blocking unauthorized requests to the REST API, good job!", "defender-security" );
47
+ }
48
+
49
+ public function getTitle() {
50
+ return __( "WordPress REST API", "defender-security" );
51
+ }
52
+
53
+ function addHooks() {
54
+ $this->addAction( 'processingHardener' . self::$slug, 'process' );
55
+ $this->addAction( 'processRevert' . self::$slug, 'revert' );
56
+ if ( in_array( self::$slug, (array) Settings::instance()->fixed ) ) {
57
+ $this->addFilter( 'rest_authentication_errors', 'maybeAllow' );
58
+ }
59
+ }
60
+
61
+ public function maybeAllow( $result ) {
62
+ if ( ! empty( $result ) ) {
63
+ return $result;
64
+ }
65
+
66
+ $mode = $this->getMiscData()['mode'];
67
+ if ( $mode == 'allow-auth' && ! is_user_logged_in() ) {
68
+ return new \WP_Error( 'rest_not_logged_in', __( 'The WordPress Rest API has been locked to authorized access only. Log in to use the API.', "defender-security" ), array( 'status' => 401 ) );
69
+ }
70
+
71
+ //delegate to other
72
+
73
+ return $result;
74
+ }
75
+
76
+ function getMiscData() {
77
+ $data = Settings::instance()->getDValues( WP_Rest_API_Service::KEY );
78
+ $mode = 'allow-all';
79
+ if ( is_array( $data ) && isset( $data['mode'] ) && in_array( $data['mode'], [ 'allow-auth', 'block-all' ] ) ) {
80
+ $mode = $data['mode'];
81
+ }
82
+
83
+ return [
84
+ 'mode' => $mode
85
+ ];
86
+ }
87
+
88
+ function revert() {
89
+ $ret = $this->getService()->revert();
90
+ if ( ! is_wp_error( $ret ) ) {
91
+ Settings::instance()->addToIssues( self::$slug );
92
+ } else {
93
+ wp_send_json_error( array(
94
+ 'message' => $ret->get_error_message()
95
+ ) );
96
+ }
97
+ }
98
+
99
+ function process() {
100
+ $ret = $this->getService()->process();
101
+ if ( ! is_wp_error( $ret ) ) {
102
+ Settings::instance()->addToResolved( self::$slug );
103
+ } else {
104
+ wp_send_json_error( array(
105
+ 'message' => $ret->get_error_message()
106
+ ) );
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @return WP_Rest_API_Service
112
+ */
113
+ public function getService() {
114
+ if ( self::$service == null ) {
115
+ self::$service = new WP_Rest_API_Service();
116
+ }
117
+
118
+ return self::$service;
119
+ }
120
+ }
app/module/hardener/component/wp-version.php CHANGED
@@ -8,7 +8,7 @@ namespace WP_Defender\Module\Hardener\Component;
8
  use WP_Defender\Module\Hardener\Rule;
9
 
10
  class WP_Version extends Rule {
11
- static $slug = 'wp_version';
12
  static $service;
13
 
14
  function getDescription() {
@@ -22,10 +22,6 @@ class WP_Version extends Rule {
22
  return $this->getService()->check();
23
  }
24
 
25
- function getSubDescription() {
26
- return sprintf( __( "Your current WordPress version is out of date, which means you could be missing out on the latest security patches in v%s", "defender-security" ), $this->getService()->getLatestVersion() );
27
- }
28
-
29
  function revert() {
30
  // TODO: Implement revert() method.
31
  }
@@ -34,6 +30,30 @@ class WP_Version extends Rule {
34
  return __( "Update WordPress to latest version", "defender-security" );
35
  }
36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  function addHooks() {
38
  // TODO: Implement addHooks() method.
39
  }
8
  use WP_Defender\Module\Hardener\Rule;
9
 
10
  class WP_Version extends Rule {
11
+ static $slug = 'wp-version';
12
  static $service;
13
 
14
  function getDescription() {
22
  return $this->getService()->check();
23
  }
24
 
 
 
 
 
25
  function revert() {
26
  // TODO: Implement revert() method.
27
  }
30
  return __( "Update WordPress to latest version", "defender-security" );
31
  }
32
 
33
+ /**
34
+ * This will return the short summary why this rule show up as issue
35
+ *
36
+ * @return string
37
+ */
38
+ function getErrorReason() {
39
+ return sprintf( __( "Your current WordPress version is out of date, which means you could be missing out on the latest security patches in v%s", "defender-security" ), $this->getService()->getLatestVersion() );
40
+ }
41
+
42
+ public function getMiscData() {
43
+ return [
44
+ 'latest_wp' => $this->getService()->getLatestVersion(),
45
+ 'core_update_url' => network_admin_url( 'update-core.php' )
46
+ ];
47
+ }
48
+
49
+ /**
50
+ * This will return a short summary to show why this rule works
51
+ * @return mixed
52
+ */
53
+ function getSuccessReason() {
54
+ // TODO: Implement getSuccessReason() method.
55
+ }
56
+
57
  function addHooks() {
58
  // TODO: Implement addHooks() method.
59
  }
app/module/hardener/controller/main.php CHANGED
@@ -5,85 +5,78 @@
5
 
6
  namespace WP_Defender\Module\Hardener\Controller;
7
 
8
- use Hammer\Base\Container;
9
  use Hammer\Helper\HTTP_Helper;
10
- use Hammer\Helper\Log_Helper;
11
  use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Controller;
13
  use WP_Defender\Module\Hardener;
14
- use WP_Defender\Vendor\Email_Search;
15
 
16
  class Main extends Controller {
17
  protected $slug = 'wdf-hardener';
18
- public $layout = 'layout';
19
- public $email_search;
20
-
21
  /**
22
  * @return array
23
  */
24
  public function behaviors() {
25
- return array(
26
- 'utils' => '\WP_Defender\Behavior\Utils'
27
- );
 
 
 
 
28
  }
29
-
30
  /**
31
  * Main constructor.
32
  */
33
  public function __construct() {
34
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
35
- $this->add_action( 'network_admin_menu', 'adminMenu' );
36
  } else {
37
- $this->add_action( 'admin_menu', 'adminMenu' );
38
  }
39
-
40
  if ( $this->isInPage() ) {
41
- $this->add_action( 'defender_enqueue_assets', 'scripts', 11 );
42
  }
43
-
44
- $this->add_ajax_action( 'processHardener', 'processHardener' );
45
- $this->add_ajax_action( 'processRevert', 'processRevert' );
46
- $this->add_ajax_action( 'ignoreHardener', 'ignoreHardener' );
47
- $this->add_ajax_action( 'restoreHardener', 'restoreHardener' );
48
- $this->add_ajax_action( 'updateHardener', 'updateHardener' );
49
- $this->add_ajax_action( 'saveTweaksSettings', 'saveTweaksSettings' );
50
  if ( ! wp_next_scheduled( 'tweaksSendNotification' ) ) {
51
  wp_schedule_event( time(), 'twicedaily', 'tweaksSendNotification' );
52
  }
53
-
54
- $this->add_action( 'tweaksSendNotification', 'tweaksSendNotification' );
55
- if ( isset( $_GET['email'] ) ) {
56
- $this->tweaksSendNotification();
57
- }
58
-
59
- $view = HTTP_Helper::retrieve_get( 'view' );
60
- $id = isset( $_REQUEST['id'] ) ? $_REQUEST['id'] : 0;
61
- if ( $view == 'notification' && $this->isInPage() || ( defined( 'DOING_AJAX' ) && $id == 'tweaksNotification' ) ) {
62
- $this->email_search = new Email_Search();
63
- $this->email_search->eId = 'tweaksNotification';
64
- $this->email_search->settings = Hardener\Model\Settings::instance();
65
- $this->email_search->add_hooks();
66
- }
67
  }
68
-
69
- public function restoreHardener() {
70
- if ( ! $this->checkPermission() ) {
71
  return;
72
  }
73
-
74
- $slug = HTTP_Helper::retrieve_post( 'slug' );
75
- $rule = Hardener\Model\Settings::instance()->getRuleBySlug( $slug );
76
- if ( is_object( $rule ) ) {
77
- $rule->restore();
78
- wp_send_json_success( array(
79
- 'message' => __( "Security tweak successfully restored.", "defender-security" ),
80
- 'issues' => $this->getCount( 'issues' ),
81
- 'fixed' => $this->getCount( 'fixed' ),
82
- 'ignore' => $this->getCount( 'ignore' )
83
- ) );
 
 
 
 
 
 
 
 
 
 
 
84
  }
85
  }
86
-
87
  public function tweaksSendNotification() {
88
  $settings = Hardener\Model\Settings::instance();
89
  //if last seen very near, do no thing
@@ -92,14 +85,16 @@ class Main extends Controller {
92
  $settings->last_seen = time();
93
  $settings->save();
94
  }
95
-
96
- if ( strtotime( apply_filters( 'wd_tweaks_notification_interval', '+24 hours' ), apply_filters( 'wd_tweaks_last_action_time', $settings->last_seen ) ) > time() ) {
97
  return;
98
  }
99
-
100
- $tweaks = Hardener\Model\Settings::instance()->getIssues();
 
101
  if ( count( $tweaks ) == 0 ) {
102
- //no honey no email
 
103
  return;
104
  }
105
  $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
@@ -108,36 +103,34 @@ class Main extends Controller {
108
  'From: Defender <' . $no_reply_email . '>',
109
  'Content-Type: text/html; charset=UTF-8'
110
  );
111
-
112
  $subject = _n( 'Security Tweak Report for %s. %s tweak needs attention.', 'Security Tweak Report for %s. %s tweaks needs attention.', count( $tweaks ), "defender-security" );
113
  $subject = sprintf( $subject, network_site_url(), count( $tweaks ) );
114
-
115
  if ( $settings->last_sent == null ) {
116
  //this is the case user install this and never check the page
117
  //send report
118
- foreach ( $settings->receipts as $receipt ) {
119
- $email = $receipt['email'];
120
- wp_mail( $email, $subject, $this->prepareEmailContent( $receipt['first_name'] ), $headers );
121
- }
122
- $settings->last_sent = time();
123
- $settings->save();
124
  } elseif ( strtotime( apply_filters( 'wd_tweaks_notification_interval', '+24 hours' ), apply_filters( 'wd_tweaks_last_notification_sent', $settings->last_sent ) ) < time() ) {
125
  //this is the case email already sent once last 24 hours
126
- if ( $settings->notification == false ) {
127
  //no repeat
128
  return;
129
  }
130
-
 
 
 
131
  foreach ( $settings->receipts as $receipt ) {
132
  $email = $receipt['email'];
133
- wp_mail( $email, $subject, $this->prepareEmailContent( $receipt['first_name'] ), $headers );
134
  }
135
  $settings->last_sent = time();
136
  $settings->save();
137
  }
138
  }
139
-
140
- private function prepareEmailContent( $firstName ) {
141
  $issues = "";
142
  foreach ( Hardener\Model\Settings::instance()->getIssues() as $issue ) {
143
  $issue = '<tr style="border:none;padding:0;text-align:left;vertical-align:top">
@@ -149,7 +142,7 @@ class Main extends Controller {
149
  style="-ms-interpolation-mode:bicubic;clear:both;display:inline-block;margin-right:10px;max-width:100%;outline:0;text-decoration:none;vertical-align:middle;width:18px">
150
  ' . $issue->getTitle() . '
151
  <span style="color: #888888;font-family: \'Open Sans\';padding-left: 32px;font-size: 13px;font-weight:300;letter-spacing: -0.25px;line-height: 22px;display: block">
152
- ' . $issue->getSubDescription() . '
153
  </span>
154
  </td>
155
  <td class="wpmudev-table__row--warning text-right"
@@ -161,110 +154,14 @@ class Main extends Controller {
161
  $contents = $this->renderPartial( 'email/notification', array(
162
  'userName' => $firstName,
163
  'siteUrl' => network_site_url(),
164
- 'viewUrl' => network_admin_url( 'admin.php?page=wdf-hardener' ),
165
  'issues' => $issues,
166
  'count' => count( Hardener\Model\Settings::instance()->getIssues() )
167
  ), false );
168
-
169
  return $contents;
170
  }
171
-
172
- public function saveTweaksSettings() {
173
- if ( ! $this->checkPermission() ) {
174
- return;
175
- }
176
-
177
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'saveTweaksSettings' ) ) {
178
- return;
179
- }
180
-
181
- $settings = Hardener\Model\Settings::instance();
182
- $settings->import( $_POST );
183
- $settings->save();
184
- // if ( $this->hasMethod( 'scheduleReportTime' ) ) {
185
- // $this->scheduleReportTime( $settings );
186
- // $this->submitStatsToDev();
187
- // }
188
- wp_send_json_success( array(
189
- 'message' => __( "Your settings have been updated.", "defender-security" )
190
- ) );
191
- }
192
-
193
- public function ignoreHardener() {
194
- if ( ! $this->checkPermission() ) {
195
- return;
196
- }
197
-
198
- $slug = HTTP_Helper::retrieve_post( 'slug' );
199
- $rule = Hardener\Model\Settings::instance()->getRuleBySlug( $slug );
200
- if ( is_object( $rule ) ) {
201
- $rule->ignore();
202
- wp_send_json_success( array(
203
- 'message' => __( "Security tweak successfully ignored.", "defender-security" ),
204
- 'issues' => $this->getCount( 'issues' ),
205
- 'fixed' => $this->getCount( 'fixed' ),
206
- 'ignore' => $this->getCount( 'ignore' )
207
- ) );
208
- }
209
- }
210
-
211
- public function processRevert() {
212
- if ( ! $this->checkPermission() ) {
213
- return;
214
- }
215
- $slug = HTTP_Helper::retrieve_post( 'slug' );
216
- do_action( "processRevert" . $slug );
217
- //fall back
218
- wp_send_json_success( array(
219
- 'message' => __( "Security tweak successfully reverted.", "defender-security" ),
220
- 'issues' => $this->getCount( 'issues' ),
221
- 'fixed' => $this->getCount( 'fixed' ),
222
- 'ignore' => $this->getCount( 'ignore' )
223
- ) );
224
- }
225
-
226
- /**
227
- * Ajax to process or ignore a rule
228
- */
229
- public function processHardener() {
230
- if ( ! $this->checkPermission() ) {
231
- return;
232
- }
233
-
234
- $slug = HTTP_Helper::retrieve_post( 'slug' );
235
-
236
- do_action( "processingHardener" . $slug );
237
- //fall back
238
- wp_send_json_success( array(
239
- 'message' => __( "Security tweak successfully resolved.", "defender-security" ),
240
- 'issues' => $this->getCount( 'issues' ),
241
- 'fixed' => $this->getCount( 'fixed' ),
242
- 'ignore' => $this->getCount( 'ignore' )
243
- ) );
244
- }
245
-
246
- /**
247
- * Update Hardener
248
- * Update existing rules
249
- */
250
- public function updateHardener() {
251
- if ( ! $this->checkPermission() ) {
252
- return;
253
- }
254
-
255
- $slug = HTTP_Helper::retrieve_post( 'slug' );
256
-
257
- do_action( "processUpdate" . $slug );
258
- //fall back
259
- wp_send_json_success( array(
260
- 'message' => __( "Security tweak successfully updated.", "defender-security" ),
261
- 'issues' => $this->getCount( 'issues' ),
262
- 'fixed' => $this->getCount( 'fixed' ),
263
- 'ignore' => $this->getCount( 'ignore' ),
264
- 'update' => false
265
- ) );
266
- }
267
-
268
  /**
269
  * Add submit admin page
270
  */
@@ -275,68 +172,79 @@ class Main extends Controller {
275
  'actionIndex'
276
  ) );
277
  }
278
-
279
  /**
280
- *
281
  */
282
  public function actionIndex() {
283
  //update the last seen
284
  $settings = Hardener\Model\Settings::instance();
285
  $settings->last_seen = time();
286
  $settings->save();
287
- switch ( HTTP_Helper::retrieve_get( 'view' ) ) {
288
- case 'issues':
289
- default:
290
- $this->_renderIssues();
291
- break;
292
- case 'resolved':
293
- $this->_renderResolved();
294
- break;
295
- case 'ignored':
296
- $this->_renderIgnored();
297
- break;
298
- case 'notification':
299
- $this->_renderNotification();
300
- break;
301
- }
302
- }
303
-
304
- private function _renderIssues() {
305
- $this->render( 'issues' );
306
- }
307
-
308
- private function _renderResolved() {
309
- $this->render( 'resolved' );
310
- }
311
-
312
- private function _renderIgnored() {
313
- $this->render( 'ignore' );
314
- }
315
-
316
- private function _renderNotification() {
317
- $this->render( 'notification', array(
318
- 'setting' => Hardener\Model\Settings::instance(),
319
- 'email' => $this->email_search
320
- ) );
321
  }
322
-
323
  /**
324
  * Enqueue scripts & styles
325
  */
326
  public function scripts() {
327
- wp_enqueue_script( 'wpmudev-sui' );
328
- wp_enqueue_script( 'defender' );
329
- wp_enqueue_script( 'hardener', wp_defender()->getPluginUrl() . 'app/module/hardener/js/scripts.js', array(
330
- 'jquery-effects-core'
331
- ) );
332
- wp_enqueue_style( 'wpmudev-sui' );
333
- wp_enqueue_style( 'defender' );
334
- $view = HTTP_Helper::retrieve_get( 'view' );
335
- if ( $view == 'notification' ) {
336
- $this->email_search->add_script();
 
 
337
  }
338
  }
339
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
340
  /**
341
  *
342
  * @param $type
5
 
6
  namespace WP_Defender\Module\Hardener\Controller;
7
 
 
8
  use Hammer\Helper\HTTP_Helper;
 
9
  use WP_Defender\Behavior\Utils;
10
  use WP_Defender\Controller;
11
  use WP_Defender\Module\Hardener;
 
12
 
13
  class Main extends Controller {
14
  protected $slug = 'wdf-hardener';
15
+
 
 
16
  /**
17
  * @return array
18
  */
19
  public function behaviors() {
20
+ $behaviors = [
21
+ 'utils' => '\WP_Defender\Behavior\Utils',
22
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
23
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
24
+ ];
25
+
26
+ return $behaviors;
27
  }
28
+
29
  /**
30
  * Main constructor.
31
  */
32
  public function __construct() {
33
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
34
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
35
  } else {
36
+ $this->addAction( 'admin_menu', 'adminMenu' );
37
  }
38
+
39
  if ( $this->isInPage() ) {
40
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
41
  }
42
+
 
 
 
 
 
 
43
  if ( ! wp_next_scheduled( 'tweaksSendNotification' ) ) {
44
  wp_schedule_event( time(), 'twicedaily', 'tweaksSendNotification' );
45
  }
46
+
47
+ $this->addAction( 'tweaksSendNotification', 'tweaksSendNotification' );
48
+ $this->addAction( 'wp_loaded', 'maybeUnsubscribe' );
 
 
 
 
 
 
 
 
 
 
 
49
  }
50
+
51
+ public function maybeUnsubscribe() {
52
+ if ( ! is_user_logged_in() ) {
53
  return;
54
  }
55
+
56
+ $action = HTTP_Helper::retrieveGet( 'action' );
57
+ if ( $action == 'unsubscribe_notification' ) {
58
+ $user = get_user_by( 'id', get_current_user_id() );
59
+ if ( ! is_object( $user ) ) {
60
+ return;
61
+ }
62
+ $model = Hardener\Model\Settings::instance();
63
+ foreach ( $model->receipts as $key => $val ) {
64
+ if ( $val['email'] == $user->user_email ) {
65
+ unset( $model->receipts[ $key ] );
66
+ break;
67
+ }
68
+ }
69
+ $model->receipts = array_filter( $model->receipts );
70
+ //check if empty recipients, then we disable notification
71
+ if ( empty( $model->receipts ) ) {
72
+ $model->notification = false;
73
+ }
74
+ $model->save();
75
+ wp_redirect( network_admin_url( 'admin.php?page=wdf-hardener&view=notification' ) );
76
+ exit;
77
  }
78
  }
79
+
80
  public function tweaksSendNotification() {
81
  $settings = Hardener\Model\Settings::instance();
82
  //if last seen very near, do no thing
85
  $settings->last_seen = time();
86
  $settings->save();
87
  }
88
+
89
+ if ( strtotime( apply_filters( 'wd_tweaks_notification_interval', '+7 days' ), apply_filters( 'wd_tweaks_last_action_time', $settings->last_seen ) ) > time() ) {
90
  return;
91
  }
92
+ $settings->refreshStatus();
93
+ $tweaks = $settings->getIssues();
94
+
95
  if ( count( $tweaks ) == 0 ) {
96
+ //no issue no email
97
+
98
  return;
99
  }
100
  $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
103
  'From: Defender <' . $no_reply_email . '>',
104
  'Content-Type: text/html; charset=UTF-8'
105
  );
106
+
107
  $subject = _n( 'Security Tweak Report for %s. %s tweak needs attention.', 'Security Tweak Report for %s. %s tweaks needs attention.', count( $tweaks ), "defender-security" );
108
  $subject = sprintf( $subject, network_site_url(), count( $tweaks ) );
109
+ $canSend = false;
110
  if ( $settings->last_sent == null ) {
111
  //this is the case user install this and never check the page
112
  //send report
113
+ $canSend = true;
 
 
 
 
 
114
  } elseif ( strtotime( apply_filters( 'wd_tweaks_notification_interval', '+24 hours' ), apply_filters( 'wd_tweaks_last_notification_sent', $settings->last_sent ) ) < time() ) {
115
  //this is the case email already sent once last 24 hours
116
+ if ( $settings->notification_repeat == false ) {
117
  //no repeat
118
  return;
119
  }
120
+ $canSend = true;
121
+ }
122
+
123
+ if ( $canSend ) {
124
  foreach ( $settings->receipts as $receipt ) {
125
  $email = $receipt['email'];
126
+ $ret = wp_mail( $email, $subject, $this->prepareEmailContent( $receipt['first_name'], $email ), $headers );
127
  }
128
  $settings->last_sent = time();
129
  $settings->save();
130
  }
131
  }
132
+
133
+ private function prepareEmailContent( $firstName, $email = null ) {
134
  $issues = "";
135
  foreach ( Hardener\Model\Settings::instance()->getIssues() as $issue ) {
136
  $issue = '<tr style="border:none;padding:0;text-align:left;vertical-align:top">
142
  style="-ms-interpolation-mode:bicubic;clear:both;display:inline-block;margin-right:10px;max-width:100%;outline:0;text-decoration:none;vertical-align:middle;width:18px">
143
  ' . $issue->getTitle() . '
144
  <span style="color: #888888;font-family: \'Open Sans\';padding-left: 32px;font-size: 13px;font-weight:300;letter-spacing: -0.25px;line-height: 22px;display: block">
145
+ ' . $issue->getErrorReason() . '
146
  </span>
147
  </td>
148
  <td class="wpmudev-table__row--warning text-right"
154
  $contents = $this->renderPartial( 'email/notification', array(
155
  'userName' => $firstName,
156
  'siteUrl' => network_site_url(),
157
+ 'viewUrl' => apply_filters( 'report_email_logs_link', network_admin_url( 'admin.php?page=wdf-hardener' ), $email ),
158
  'issues' => $issues,
159
  'count' => count( Hardener\Model\Settings::instance()->getIssues() )
160
  ), false );
161
+
162
  return $contents;
163
  }
164
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  /**
166
  * Add submit admin page
167
  */
172
  'actionIndex'
173
  ) );
174
  }
175
+
176
  /**
177
+ * Main screen
178
  */
179
  public function actionIndex() {
180
  //update the last seen
181
  $settings = Hardener\Model\Settings::instance();
182
  $settings->last_seen = time();
183
  $settings->save();
184
+
185
+ return $this->render( 'main' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }
187
+
188
  /**
189
  * Enqueue scripts & styles
190
  */
191
  public function scripts() {
192
+ if ( $this->isInPage() ) {
193
+ wp_enqueue_style( 'defender' );
194
+ wp_register_script( 'defender-hardener', wp_defender()->getPluginUrl() . 'assets/app/security-tweaks.js', array(
195
+ 'vue',
196
+ 'defender',
197
+ 'wp-i18n'
198
+ ), false, true );
199
+ wp_localize_script( 'defender-hardener', 'security_tweaks', $this->_scriptsData() );
200
+ Utils::instance()->createTranslationJson( 'defender-hardener' );
201
+ wp_set_script_translations( 'defender-hardener', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
202
+ wp_enqueue_script( 'defender-hardener' );
203
+ wp_enqueue_script( 'wpmudev-sui' );
204
  }
205
  }
206
+
207
+ /**
208
+ * @return array
209
+ */
210
+ public function _scriptsData() {
211
+ if ( ! $this->checkPermission() ) {
212
+ return [];
213
+ }
214
+ global $wp_version;
215
+ $settings = Hardener\Model\Settings::instance();
216
+ $issues = $settings->getTweaksAsArray( 'issues', true );
217
+ $fixed = $settings->getTweaksAsArray( 'fixed', true );
218
+ $ignored = $settings->getTweaksAsArray( 'ignore', true );
219
+
220
+ return [
221
+ 'summary' => [
222
+ 'issues_count' => count( $issues ),
223
+ 'fixed_count' => count( $fixed ),
224
+ 'ignore_count' => count( $ignored ),
225
+ 'php_version' => phpversion(),
226
+ 'wp_version' => $wp_version
227
+ ],
228
+ 'issues' => $issues,
229
+ 'fixed' => $fixed,
230
+ 'ignored' => $ignored,
231
+ 'endpoints' => $this->getAllAvailableEndpoints( Hardener::getClassName() ),
232
+ 'nonces' => [
233
+ 'processTweak' => wp_create_nonce( 'processTweak' ),
234
+ 'ignoreTweak' => wp_create_nonce( 'ignoreTweak' ),
235
+ 'restoreTweak' => wp_create_nonce( 'restoreTweak' ),
236
+ 'revertTweak' => wp_create_nonce( 'revertTweak' ),
237
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
238
+ 'recheck' => wp_create_nonce( 'recheck' )
239
+ ],
240
+ 'model' => [
241
+ 'notification_repeat' => $settings->notification_repeat,
242
+ 'recipients' => $settings->receipts,
243
+ 'notification' => $settings->notification
244
+ ]
245
+ ];
246
+ }
247
+
248
  /**
249
  *
250
  * @param $type
app/module/hardener/controller/rest.php ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Hardener\Controller;
7
+
8
+ use Hammer\Helper\HTTP_Helper;
9
+ use Hammer\Helper\WP_Helper;
10
+ use WP_Defender\Behavior\Utils;
11
+ use WP_Defender\Controller;
12
+ use WP_Defender\Module\Hardener;
13
+ use WP_Defender\Module\Hardener\Model\Settings;
14
+
15
+ class Rest extends Controller {
16
+ public function __construct() {
17
+ $namespace = 'wp-defender/v1';
18
+ $namespace .= '/tweaks';
19
+ $routes = [
20
+ $namespace . '/processTweak' => 'processTweak',
21
+ $namespace . '/revertTweak' => 'revertTweak',
22
+ $namespace . '/ignoreTweak' => 'ignoreTweak',
23
+ $namespace . '/restoreTweak' => 'restoreTweak',
24
+ $namespace . '/updateSettings' => 'updateSettings',
25
+ $namespace . '/reCheck' => 'reCheck',
26
+ ];
27
+ $this->registerEndpoints( $routes, Hardener::getClassName() );
28
+ }
29
+
30
+ /**
31
+ * Save settings
32
+ */
33
+ public function updateSettings() {
34
+ if ( ! $this->checkPermission() ) {
35
+ return;
36
+ }
37
+
38
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'updateSettings' ) ) {
39
+ return;
40
+ }
41
+
42
+ $settings = Hardener\Model\Settings::instance();
43
+ $data = stripslashes( $_POST['data'] );
44
+ $data = json_decode( $data, true );
45
+ $settings->notification = $data['notification'];
46
+ $settings->notification_repeat = $data['notification_repeat'];
47
+ $recipients = [];
48
+ foreach ( $data['recipients'] as $key => $recipient ) {
49
+ if ( filter_var( $recipient['email'], FILTER_VALIDATE_EMAIL ) ) {
50
+ $recipients[] = $recipient;
51
+ }
52
+ }
53
+ $settings->receipts = $recipients;
54
+ $settings->save();
55
+ $this->submitStatsToDev();
56
+ wp_send_json_success( array(
57
+ 'message' => __( "Your settings have been updated.", "defender-security" )
58
+ ) );
59
+ }
60
+
61
+ public function reCheck() {
62
+ if ( ! $this->checkPermission() ) {
63
+ return;
64
+ }
65
+
66
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'recheck' ) ) {
67
+ return;
68
+ }
69
+ $type = HTTP_Helper::retrievePost( 'type' );
70
+ if ( $type == 'prevent-php' ) {
71
+ $url = WP_Helper::getUploadUrl();
72
+ $url = $url . '/wp-defender/index.php';
73
+ } else {
74
+ $url = wp_defender()->getPluginUrl() . 'changelog.txt';
75
+ }
76
+ $model = Settings::instance();
77
+ $cache = $model->getDValues( 'head_requests' );
78
+ Utils::instance()->log( sprintf( 'clean up %s', $url ), 'tweaks' );
79
+ unset( $cache[ $url ] );
80
+ $model->setDValues( 'head_requests', $cache );
81
+ wp_send_json_success( $this->tweaksSummary() );
82
+ }
83
+
84
+ /**
85
+ * Endpoint for recieve process request for all tweaks, we will dispatch the envelope to tweak from here
86
+ */
87
+ public function processTweak() {
88
+ if ( ! $this->checkPermission() ) {
89
+ return;
90
+ }
91
+
92
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'processTweak' ) ) {
93
+ return;
94
+ }
95
+
96
+ $slug = HTTP_Helper::retrievePost( 'slug' );
97
+ do_action( "processingHardener" . $slug );
98
+ //fall back
99
+ wp_send_json_success( array_merge( [
100
+ 'message' => __( "Security tweak successfully resolved.", "defender-security" ),
101
+ ], $this->tweaksSummary() ) );
102
+ }
103
+
104
+ /**
105
+ * Revert a tweak request, this will un-apply the affect of a tweak
106
+ */
107
+ public function revertTweak() {
108
+ if ( ! $this->checkPermission() ) {
109
+ return;
110
+ }
111
+
112
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'revertTweak' ) ) {
113
+ return;
114
+ }
115
+
116
+ $slug = HTTP_Helper::retrievePost( 'slug' );
117
+ do_action( "processRevert" . $slug );
118
+ //fall back
119
+ wp_send_json_success( array_merge( [
120
+ 'message' => __( "Security tweak successfully reverted.", "defender-security" ),
121
+ ], $this->tweaksSummary() ) );
122
+ }
123
+
124
+ /**
125
+ * Ignore a tweak request
126
+ */
127
+ public function ignoreTweak() {
128
+ if ( ! $this->checkPermission() ) {
129
+ return;
130
+ }
131
+
132
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'ignoreTweak' ) ) {
133
+ return;
134
+ }
135
+ $slug = HTTP_Helper::retrievePost( 'slug' );
136
+ $rule = Hardener\Model\Settings::instance()->getRuleBySlug( $slug );
137
+ if ( is_object( $rule ) ) {
138
+ $rule->ignore();
139
+ wp_send_json_success( array_merge( [
140
+ 'message' => __( "Security tweak successfully ignored.", "defender-security" ),
141
+ ], $this->tweaksSummary() ) );
142
+ }
143
+ }
144
+
145
+ /**
146
+ * Restore request, to move a tweak from ignored to issues section
147
+ */
148
+ public function restoreTweak() {
149
+ if ( ! $this->checkPermission() ) {
150
+ return;
151
+ }
152
+
153
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'restoreTweak' ) ) {
154
+ return;
155
+ }
156
+
157
+ $slug = HTTP_Helper::retrievePost( 'slug' );
158
+ $rule = Hardener\Model\Settings::instance()->getRuleBySlug( $slug );
159
+ if ( is_object( $rule ) ) {
160
+ $rule->restore();
161
+ wp_send_json_success( array_merge( [
162
+ 'message' => __( "Security tweak successfully restored.", "defender-security" ),
163
+ ], $this->tweaksSummary() ) );
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Shorthand for returning the count of tweaks, by each section
169
+ * @return array
170
+ */
171
+ private function tweaksSummary() {
172
+ $settings = Hardener\Model\Settings::instance( true );
173
+
174
+ return [
175
+ 'summary' => [
176
+ 'issues' => count( $settings->issues ),
177
+ 'fixed' => count( $settings->fixed ),
178
+ 'ignore' => count( $settings->ignore ),
179
+ ],
180
+ 'issues' => $settings->getTweaksAsArray( 'issues', true ),
181
+ 'fixed' => $settings->getTweaksAsArray( 'fixed', true ),
182
+ 'ignore' => $settings->getTweaksAsArray( 'ignore', true ),
183
+ ];
184
+ }
185
+
186
+ /**
187
+ * Declaring behaviors
188
+ * @return array
189
+ */
190
+ public function behaviors() {
191
+ $behaviors = array(
192
+ 'utils' => '\WP_Defender\Behavior\Utils',
193
+ );
194
+
195
+ return $behaviors;
196
+ }
197
+ }
app/module/hardener/model/settings.php CHANGED
@@ -15,12 +15,20 @@ use WP_Defender\Module\Hardener\Component\Disable_Xml_Rpc;
15
  use WP_Defender\Module\Hardener\Component\Hide_Error;
16
  use WP_Defender\Module\Hardener\Component\Login_Duration;
17
  use WP_Defender\Module\Hardener\Component\PHP_Version;
 
18
  use WP_Defender\Module\Hardener\Component\Prevent_Php;
19
  use WP_Defender\Module\Hardener\Component\Protect_Information;
20
  use WP_Defender\Module\Hardener\Component\Security_Key;
 
 
 
 
 
 
 
 
21
  use WP_Defender\Module\Hardener\Component\WP_Version;
22
  use WP_Defender\Module\Hardener\Rule;
23
- use WP_Defender\Module\Hardener\Rule_Service;
24
 
25
  class Settings extends \Hammer\WP\Settings {
26
  private static $_instance;
@@ -34,26 +42,26 @@ class Settings extends \Hammer\WP\Settings {
34
  * @var array
35
  */
36
  public $issues = array();
37
-
38
  /**
39
  * Contains fixed rules
40
  * @var array
41
  */
42
-
43
  public $fixed = array();
44
-
45
  /**
46
  * Contains ignored issue
47
  * @var array
48
  */
49
  public $ignore = array();
50
-
51
  /**
52
  * Store the last status check, we will check & fetch the status intervally, this can reduce load time.
53
  * @var null
54
  */
55
  public $last_status_check = null;
56
-
57
  /**
58
  * Toggle notification
59
  * @var bool
@@ -63,48 +71,53 @@ class Settings extends \Hammer\WP\Settings {
63
  * @var bool
64
  */
65
  public $notification_repeat = false;
66
-
67
  /**
68
- * Holding receipts info
69
  * @var array
70
  */
71
  public $receipts = array();
72
-
73
  /**
74
  * Contains all the data generated by rules
75
  * @var array
76
  */
77
  public $data = array();
78
-
79
  /**
80
  * Holding excluded file path info
81
  * @var array
82
  */
83
  public $exclude_file_paths = array();
84
-
85
  /**
86
  * Holds new htconfig structure for defender
87
  *
88
  * @var array
89
  */
90
  public $new_htconfig = array();
91
-
92
  /**
93
  * Current active server
94
  *
95
  * @var String
96
  */
97
  public $active_server = 'apache';
98
-
99
  /**
 
 
100
  * @var integer
101
  */
102
  public $last_seen;
 
103
  /**
 
 
104
  * @var integer
105
  */
106
  public $last_sent;
107
-
108
  /**
109
  * @var string
110
  */
@@ -113,16 +126,21 @@ class Settings extends \Hammer\WP\Settings {
113
  * @var string
114
  */
115
  public $stable_php_version = '';
116
-
 
 
 
 
 
117
  public $is_prefix_changed = false;
118
-
119
  /**
120
  * shorthand to add to a list
121
  *
122
  * @param $slug
123
  * @param $devPush
124
  */
125
-
126
  public function __construct( $id, $is_multi ) {
127
  if ( is_admin() || is_network_admin() && current_user_can( 'manage_options' ) ) {
128
  $user = wp_get_current_user();
@@ -133,13 +151,24 @@ class Settings extends \Hammer\WP\Settings {
133
  );
134
  }
135
  }
 
 
136
  parent::__construct( $id, $is_multi );
 
 
 
 
 
137
  }
138
-
 
 
 
 
139
  public function addToIssues( $slug, $devPush = true ) {
140
  $this->addToList( 'issues', $slug, $devPush );
141
  }
142
-
143
  /**
144
  * shorthand to add to a list
145
  *
@@ -149,7 +178,7 @@ class Settings extends \Hammer\WP\Settings {
149
  public function addToIgnore( $slug, $devPush = true ) {
150
  $this->addToList( 'ignore', $slug, $devPush );
151
  }
152
-
153
  /**
154
  * shorthand to add to a list
155
  *
@@ -159,7 +188,7 @@ class Settings extends \Hammer\WP\Settings {
159
  public function addToResolved( $slug, $devPush = true ) {
160
  $this->addToList( 'fixed', $slug, $devPush );
161
  }
162
-
163
  /**
164
  * @param $list
165
  * @param $slug
@@ -174,7 +203,7 @@ class Settings extends \Hammer\WP\Settings {
174
  if ( ! in_array( $list, $lists ) ) {
175
  return;
176
  }
177
-
178
  //remove from lists
179
  foreach ( $lists as $l ) {
180
  if ( $l == $list ) {
@@ -185,7 +214,7 @@ class Settings extends \Hammer\WP\Settings {
185
  unset( $this->{$l}[ $key ] );
186
  }
187
  }
188
-
189
  array_push( $this->$list, $slug );
190
  $this->$list = array_unique( $this->$list );
191
  $this->last_status_check = time();
@@ -194,18 +223,21 @@ class Settings extends \Hammer\WP\Settings {
194
  Utils::instance()->submitStatsToDev();
195
  }
196
  }
197
-
198
  /**
199
  * @return Settings
200
  */
201
- public static function instance() {
 
 
 
202
  if ( is_null( self::$_instance ) ) {
203
  self::$_instance = new Settings( 'wd_hardener_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
204
  }
205
-
206
  return self::$_instance;
207
  }
208
-
209
  /**
210
  * refresh rules status and store the index
211
  */
@@ -227,8 +259,9 @@ class Settings extends \Hammer\WP\Settings {
227
  $this->last_status_check = time();
228
  $this->save();
229
  }
230
-
231
  /**
 
232
  * @return Rule[]
233
  */
234
  public function getIssues() {
@@ -239,10 +272,44 @@ class Settings extends \Hammer\WP\Settings {
239
  $issues[] = $rules[ $issue ];
240
  }
241
  }
242
-
243
  return $issues;
244
  }
245
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  /**
247
  * @return array
248
  */
@@ -254,10 +321,10 @@ class Settings extends \Hammer\WP\Settings {
254
  $issues[] = $rules[ $issue ];
255
  }
256
  }
257
-
258
  return $issues;
259
  }
260
-
261
  /**
262
  * @return Rule[]
263
  */
@@ -269,10 +336,10 @@ class Settings extends \Hammer\WP\Settings {
269
  $issues[] = $rules[ $issue ];
270
  }
271
  }
272
-
273
  return $issues;
274
  }
275
-
276
  /**
277
  * @param $slug
278
  *
@@ -284,7 +351,7 @@ class Settings extends \Hammer\WP\Settings {
284
  return $rules[ $slug ];
285
  }
286
  }
287
-
288
  /**
289
  *
290
  * @param bool $init
@@ -293,21 +360,31 @@ class Settings extends \Hammer\WP\Settings {
293
  */
294
  public function getDefinedRules( $init = false ) {
295
  return array(
296
- Disable_Trackback::$slug => $init == true ? new Disable_Trackback() : Disable_Trackback::getClassName(),
297
- WP_Version::$slug => $init == true ? new WP_Version() : WP_Version::getClassName(),
298
- PHP_Version::$slug => $init == true ? new PHP_Version() : PHP_Version::getClassName(),
299
- Change_Admin::$slug => $init == true ? new Change_Admin() : Change_Admin::getClassName(),
300
- DB_Prefix::$slug => $init == true ? new DB_Prefix() : DB_Prefix::getClassName(),
301
- Disable_File_Editor::$slug => $init == true ? new Disable_File_Editor() : Disable_File_Editor::getClassName(),
302
- Hide_Error::$slug => $init == true ? new Hide_Error() : Hide_Error::getClassName(),
303
- Security_Key::$slug => $init == true ? new Security_Key() : Security_Key::getClassName(),
304
- Protect_Information::$slug => $init == true ? new Protect_Information() : Protect_Information::getClassName(),
305
- Prevent_Php::$slug => $init == true ? new Prevent_Php() : Prevent_Php::getClassName(),
306
- Login_Duration::$slug => $init == true ? new Login_Duration() : Login_Duration::getClassName(),
307
- Disable_Xml_Rpc::$slug => $init == true ? new Disable_Xml_Rpc() : Disable_Xml_Rpc::getClassName(),
 
 
 
 
 
 
 
 
 
 
308
  );
309
  }
310
-
311
  /**
312
  * @param $key
313
  *
@@ -317,10 +394,10 @@ class Settings extends \Hammer\WP\Settings {
317
  if ( is_array( $this->data ) && isset( $this->data[ $key ] ) ) {
318
  return $this->data[ $key ];
319
  }
320
-
321
  return null;
322
  }
323
-
324
  /**
325
  * @param $key
326
  * @param $value
@@ -333,7 +410,7 @@ class Settings extends \Hammer\WP\Settings {
333
  }
334
  $this->save();
335
  }
336
-
337
  /**
338
  * Save the exclude file paths
339
  *
@@ -342,7 +419,7 @@ class Settings extends \Hammer\WP\Settings {
342
  public function saveExcludedFilePaths( $paths = array() ) {
343
  $this->exclude_file_paths = $paths;
344
  }
345
-
346
  /**
347
  * Save the htconfig
348
  *
@@ -351,7 +428,7 @@ class Settings extends \Hammer\WP\Settings {
351
  public function saveNewHtConfig( $config = array() ) {
352
  $this->new_htconfig = $config;
353
  }
354
-
355
  /**
356
  * Get the exclude file paths
357
  *
@@ -360,7 +437,7 @@ class Settings extends \Hammer\WP\Settings {
360
  public function getExcludedFilePaths() {
361
  return $this->exclude_file_paths;
362
  }
363
-
364
  /**
365
  * Get the new htconfig
366
  *
@@ -369,7 +446,7 @@ class Settings extends \Hammer\WP\Settings {
369
  public function getNewHtConfig() {
370
  return $this->new_htconfig;
371
  }
372
-
373
  /**
374
  * Set the active server
375
  *
@@ -378,30 +455,24 @@ class Settings extends \Hammer\WP\Settings {
378
  public function setActiveServer( $server ) {
379
  $this->active_server = $server;
380
  }
381
-
 
 
 
382
  public function events() {
383
- $that = $this;
384
-
385
  return array(
386
  self::EVENT_BEFORE_SAVE => array(
387
  array(
388
- function () use ( $that ) {
389
  //need to turn off notification or report off if no recipients
390
- $keys = array(
391
- 'receipts' => 'notification',
392
- );
393
- foreach ( $keys as $key => $attr ) {
394
- if ( isset( $_POST[ $key ] ) ) {
395
- $recipients = $_POST[ $key ];
396
- $recipients = array_filter( $recipients );
397
- foreach ( $recipients as &$recipient ) {
398
- $recipient = array_map( 'wp_strip_all_tags', $recipient );
399
- }
400
- $this->$key = $recipients;
401
- }
402
- $this->$key = array_filter( $this->$key );
403
- if ( count( $this->$key ) == 0 ) {
404
- $this->$attr = 0;
405
  }
406
  }
407
  }
@@ -409,4 +480,25 @@ class Settings extends \Hammer\WP\Settings {
409
  )
410
  );
411
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  }
15
  use WP_Defender\Module\Hardener\Component\Hide_Error;
16
  use WP_Defender\Module\Hardener\Component\Login_Duration;
17
  use WP_Defender\Module\Hardener\Component\PHP_Version;
18
+ use WP_Defender\Module\Hardener\Component\Prevent_Enum_Users;
19
  use WP_Defender\Module\Hardener\Component\Prevent_Php;
20
  use WP_Defender\Module\Hardener\Component\Protect_Information;
21
  use WP_Defender\Module\Hardener\Component\Security_Key;
22
+ use WP_Defender\Module\Hardener\Component\Sh_Content_Security;
23
+ use WP_Defender\Module\Hardener\Component\Sh_Content_Type_Options;
24
+ use WP_Defender\Module\Hardener\Component\Sh_Feature_Policy;
25
+ use WP_Defender\Module\Hardener\Component\Sh_Referrer_Policy;
26
+ use WP_Defender\Module\Hardener\Component\Sh_Strict_Transport;
27
+ use WP_Defender\Module\Hardener\Component\Sh_X_Frame;
28
+ use WP_Defender\Module\Hardener\Component\Sh_XSS_Protection;
29
+ use WP_Defender\Module\Hardener\Component\WP_Rest_Api;
30
  use WP_Defender\Module\Hardener\Component\WP_Version;
31
  use WP_Defender\Module\Hardener\Rule;
 
32
 
33
  class Settings extends \Hammer\WP\Settings {
34
  private static $_instance;
42
  * @var array
43
  */
44
  public $issues = array();
45
+
46
  /**
47
  * Contains fixed rules
48
  * @var array
49
  */
50
+
51
  public $fixed = array();
52
+
53
  /**
54
  * Contains ignored issue
55
  * @var array
56
  */
57
  public $ignore = array();
58
+
59
  /**
60
  * Store the last status check, we will check & fetch the status intervally, this can reduce load time.
61
  * @var null
62
  */
63
  public $last_status_check = null;
64
+
65
  /**
66
  * Toggle notification
67
  * @var bool
71
  * @var bool
72
  */
73
  public $notification_repeat = false;
74
+
75
  /**
76
+ * Holding recipients info
77
  * @var array
78
  */
79
  public $receipts = array();
80
+
81
  /**
82
  * Contains all the data generated by rules
83
  * @var array
84
  */
85
  public $data = array();
86
+
87
  /**
88
  * Holding excluded file path info
89
  * @var array
90
  */
91
  public $exclude_file_paths = array();
92
+
93
  /**
94
  * Holds new htconfig structure for defender
95
  *
96
  * @var array
97
  */
98
  public $new_htconfig = array();
99
+
100
  /**
101
  * Current active server
102
  *
103
  * @var String
104
  */
105
  public $active_server = 'apache';
106
+
107
  /**
108
+ * Last time visit into the hardener page
109
+ *
110
  * @var integer
111
  */
112
  public $last_seen;
113
+
114
  /**
115
+ * Last notification sent out
116
+ *
117
  * @var integer
118
  */
119
  public $last_sent;
120
+
121
  /**
122
  * @var string
123
  */
126
  * @var string
127
  */
128
  public $stable_php_version = '';
129
+
130
+ /**
131
+ * We have to flag if the db prefix changed by us or not
132
+ *
133
+ * @var bool
134
+ */
135
  public $is_prefix_changed = false;
136
+
137
  /**
138
  * shorthand to add to a list
139
  *
140
  * @param $slug
141
  * @param $devPush
142
  */
143
+
144
  public function __construct( $id, $is_multi ) {
145
  if ( is_admin() || is_network_admin() && current_user_can( 'manage_options' ) ) {
146
  $user = wp_get_current_user();
151
  );
152
  }
153
  }
154
+ $this->active_server = Utils::instance()->determineServer();
155
+ $this->active_server = Utils::instance()->determineServer();
156
  parent::__construct( $id, $is_multi );
157
+ $this->notification = ! ! $this->notification;
158
+ if ( ! is_array( $this->receipts ) ) {
159
+ $this->receipts = [];
160
+ }
161
+ $this->receipts = array_values( $this->receipts );
162
  }
163
+
164
+ /**
165
+ * @param $slug
166
+ * @param bool $devPush
167
+ */
168
  public function addToIssues( $slug, $devPush = true ) {
169
  $this->addToList( 'issues', $slug, $devPush );
170
  }
171
+
172
  /**
173
  * shorthand to add to a list
174
  *
178
  public function addToIgnore( $slug, $devPush = true ) {
179
  $this->addToList( 'ignore', $slug, $devPush );
180
  }
181
+
182
  /**
183
  * shorthand to add to a list
184
  *
188
  public function addToResolved( $slug, $devPush = true ) {
189
  $this->addToList( 'fixed', $slug, $devPush );
190
  }
191
+
192
  /**
193
  * @param $list
194
  * @param $slug
203
  if ( ! in_array( $list, $lists ) ) {
204
  return;
205
  }
206
+
207
  //remove from lists
208
  foreach ( $lists as $l ) {
209
  if ( $l == $list ) {
214
  unset( $this->{$l}[ $key ] );
215
  }
216
  }
217
+
218
  array_push( $this->$list, $slug );
219
  $this->$list = array_unique( $this->$list );
220
  $this->last_status_check = time();
223
  Utils::instance()->submitStatsToDev();
224
  }
225
  }
226
+
227
  /**
228
  * @return Settings
229
  */
230
+ public static function instance( $refresh = false ) {
231
+ if ( $refresh == true ) {
232
+ self::$_instance = null;
233
+ }
234
  if ( is_null( self::$_instance ) ) {
235
  self::$_instance = new Settings( 'wd_hardener_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
236
  }
237
+
238
  return self::$_instance;
239
  }
240
+
241
  /**
242
  * refresh rules status and store the index
243
  */
259
  $this->last_status_check = time();
260
  $this->save();
261
  }
262
+
263
  /**
264
+ * Get Issues tweaks as object
265
  * @return Rule[]
266
  */
267
  public function getIssues() {
272
  $issues[] = $rules[ $issue ];
273
  }
274
  }
275
+
276
  return $issues;
277
  }
278
+
279
+ /**
280
+ * Filter the tweaks and return data as array
281
+ *
282
+ *
283
+ * @param $type
284
+ *
285
+ * @return array
286
+ */
287
+ public function getTweaksAsArray( $type, $sort = false ) {
288
+ $rules = $this->getDefinedRules( true );
289
+
290
+ $arr = $this->$type;
291
+ $data = array();
292
+ foreach ( $arr as $tweak ) {
293
+ if ( isset( $rules[ $tweak ] ) ) {
294
+ $curr = $rules[ $tweak ];
295
+ $data[ $curr::$slug ] = array(
296
+ 'slug' => $curr::$slug,
297
+ 'title' => $curr->getTitle(),
298
+ 'errorReason' => $curr->getErrorReason(),
299
+ 'successReason' => $curr->getSuccessReason(),
300
+ 'status' => $type,
301
+ 'misc' => $curr->getMiscData()
302
+ );
303
+ }
304
+ }
305
+
306
+ if ( $sort ) {
307
+ ksort( $data );
308
+ }
309
+
310
+ return $data;
311
+ }
312
+
313
  /**
314
  * @return array
315
  */
321
  $issues[] = $rules[ $issue ];
322
  }
323
  }
324
+
325
  return $issues;
326
  }
327
+
328
  /**
329
  * @return Rule[]
330
  */
336
  $issues[] = $rules[ $issue ];
337
  }
338
  }
339
+
340
  return $issues;
341
  }
342
+
343
  /**
344
  * @param $slug
345
  *
351
  return $rules[ $slug ];
352
  }
353
  }
354
+
355
  /**
356
  *
357
  * @param bool $init
360
  */
361
  public function getDefinedRules( $init = false ) {
362
  return array(
363
+ Disable_Trackback::$slug => $init == true ? new Disable_Trackback() : Disable_Trackback::getClassName(),
364
+ WP_Version::$slug => $init == true ? new WP_Version() : WP_Version::getClassName(),
365
+ PHP_Version::$slug => $init == true ? new PHP_Version() : PHP_Version::getClassName(),
366
+ Change_Admin::$slug => $init == true ? new Change_Admin() : Change_Admin::getClassName(),
367
+ DB_Prefix::$slug => $init == true ? new DB_Prefix() : DB_Prefix::getClassName(),
368
+ Disable_File_Editor::$slug => $init == true ? new Disable_File_Editor() : Disable_File_Editor::getClassName(),
369
+ Hide_Error::$slug => $init == true ? new Hide_Error() : Hide_Error::getClassName(),
370
+ Prevent_Enum_Users::$slug => $init == true ? new Prevent_Enum_Users() : Prevent_Enum_Users::getClassName(),
371
+ Security_Key::$slug => $init == true ? new Security_Key() : Security_Key::getClassName(),
372
+ Protect_Information::$slug => $init == true ? new Protect_Information() : Protect_Information::getClassName(),
373
+ Prevent_Php::$slug => $init == true ? new Prevent_Php() : Prevent_Php::getClassName(),
374
+ Login_Duration::$slug => $init == true ? new Login_Duration() : Login_Duration::getClassName(),
375
+ Disable_Xml_Rpc::$slug => $init == true ? new Disable_Xml_Rpc() : Disable_Xml_Rpc::getClassName(),
376
+ //WP_Rest_Api::$slug => $init == true ? new WP_Rest_Api() : WP_Rest_Api::getClassName(),
377
+ //============SECURITY HEADERS===================
378
+ Sh_X_Frame::$slug => $init == true ? new Sh_X_Frame() : Sh_X_Frame::getClassName(),
379
+ Sh_XSS_Protection::$slug => $init == true ? new Sh_XSS_Protection() : Sh_XSS_Protection::getClassName(),
380
+ Sh_Feature_Policy::$slug => $init == true ? new Sh_Feature_Policy() : Sh_Feature_Policy::getClassName(),
381
+ Sh_Referrer_Policy::$slug => $init == true ? new Sh_Referrer_Policy() : Sh_Referrer_Policy::getClassName(),
382
+ Sh_Strict_Transport::$slug => $init == true ? new Sh_Strict_Transport() : Sh_Strict_Transport::getClassName(),
383
+ Sh_Content_Type_Options::$slug => $init == true ? new Sh_Content_Type_Options() : Sh_Content_Type_Options::getClassName(),
384
+ //Sh_Content_Security::$slug => $init == true ? new Sh_Content_Security() : Sh_Content_Security::getClassName(),
385
  );
386
  }
387
+
388
  /**
389
  * @param $key
390
  *
394
  if ( is_array( $this->data ) && isset( $this->data[ $key ] ) ) {
395
  return $this->data[ $key ];
396
  }
397
+
398
  return null;
399
  }
400
+
401
  /**
402
  * @param $key
403
  * @param $value
410
  }
411
  $this->save();
412
  }
413
+
414
  /**
415
  * Save the exclude file paths
416
  *
419
  public function saveExcludedFilePaths( $paths = array() ) {
420
  $this->exclude_file_paths = $paths;
421
  }
422
+
423
  /**
424
  * Save the htconfig
425
  *
428
  public function saveNewHtConfig( $config = array() ) {
429
  $this->new_htconfig = $config;
430
  }
431
+
432
  /**
433
  * Get the exclude file paths
434
  *
437
  public function getExcludedFilePaths() {
438
  return $this->exclude_file_paths;
439
  }
440
+
441
  /**
442
  * Get the new htconfig
443
  *
446
  public function getNewHtConfig() {
447
  return $this->new_htconfig;
448
  }
449
+
450
  /**
451
  * Set the active server
452
  *
455
  public function setActiveServer( $server ) {
456
  $this->active_server = $server;
457
  }
458
+
459
+ /**
460
+ * @return array
461
+ */
462
  public function events() {
 
 
463
  return array(
464
  self::EVENT_BEFORE_SAVE => array(
465
  array(
466
+ function () {
467
  //need to turn off notification or report off if no recipients
468
+ if ( empty( $this->receipts ) ) {
469
+ $this->notification = false;
470
+ }
471
+ //sanitize
472
+ foreach ( $this->receipts as $key => &$receipt ) {
473
+ $receipt = array_map( 'sanitize_text_field', $receipt );
474
+ if ( ! filter_var( $receipt['email'], FILTER_VALIDATE_EMAIL ) ) {
475
+ unset( $this->receipts[ $key ] );
 
 
 
 
 
 
 
476
  }
477
  }
478
  }
480
  )
481
  );
482
  }
483
+
484
+ /**
485
+ * Define labels for settings key, we will use it for HUB
486
+ *
487
+ * @param null $key
488
+ *
489
+ * @return array|mixed
490
+ */
491
+ public function labels( $key = null ) {
492
+ $labels = [
493
+ 'notification' => __( 'Notification', "defender-security" ),
494
+ 'receipts' => __( 'Recipients', "defender-security" ),
495
+ 'notification_repeat' => __( "Send reminders", "defender-security" )
496
+ ];
497
+
498
+ if ( $key != null ) {
499
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
500
+ }
501
+
502
+ return $labels;
503
+ }
504
  }
app/module/hardener/rule-service.php CHANGED
@@ -21,7 +21,7 @@ class Rule_Service extends Component {
21
  'utils' => '\WP_Defender\Behavior\Utils'
22
  );
23
  }
24
-
25
  /**
26
  * @param $curr_status
27
  * @param $slug
@@ -44,14 +44,14 @@ class Rule_Service extends Component {
44
  break;
45
  }
46
  }
47
-
48
  /**
49
  * @param $slug
50
  */
51
  public function ignore( $slug ) {
52
  self::store( 'ignore', $slug );
53
  }
54
-
55
  /**
56
  * A helper function for child class
57
  * @return string
@@ -66,7 +66,7 @@ class Rule_Service extends Component {
66
  return '/tmp/wordpress-tests-lib/wp-tests-config.php';
67
  }
68
  }
69
-
70
  /**
71
  * @param $config
72
  *
@@ -80,7 +80,63 @@ class Rule_Service extends Component {
80
  return $k;
81
  }
82
  }
83
-
84
  return false;
85
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
21
  'utils' => '\WP_Defender\Behavior\Utils'
22
  );
23
  }
24
+
25
  /**
26
  * @param $curr_status
27
  * @param $slug
44
  break;
45
  }
46
  }
47
+
48
  /**
49
  * @param $slug
50
  */
51
  public function ignore( $slug ) {
52
  self::store( 'ignore', $slug );
53
  }
54
+
55
  /**
56
  * A helper function for child class
57
  * @return string
66
  return '/tmp/wordpress-tests-lib/wp-tests-config.php';
67
  }
68
  }
69
+
70
  /**
71
  * @param $config
72
  *
80
  return $k;
81
  }
82
  }
83
+
84
  return false;
85
  }
86
+
87
+ /**
88
+ * @param $url
89
+ * @param $origin
90
+ *
91
+ * @return array|mixed|\WP_Error
92
+ */
93
+ protected function headRequest( $url, $origin, $ttl = null ) {
94
+ $settings = Hardener\Model\Settings::instance();
95
+ $cached = $settings->getDValues( 'head_requests' );
96
+ if ( ! is_array( $cached ) ) {
97
+ $cached = [];
98
+ }
99
+ if ( isset( $cached[ $url ] ) ) {
100
+ $cache = $cached[ $url ];
101
+ if ( $cache['ttl'] > time() ) {
102
+ //we'll use the cache
103
+ Utils::instance()->log( sprintf( 'Header for %s return from cached', $url ) );
104
+
105
+ return $cache['data'];
106
+ }
107
+ }
108
+
109
+ //no cache or cache expired
110
+ $request = wp_remote_head( $url, [
111
+ 'user-agent' => 'WP Defender self ping - ' . $origin
112
+ ] );
113
+ if ( ! is_wp_error( $request ) ) {
114
+ $headers = wp_remote_retrieve_headers( $request );
115
+ $headers = $headers->getAll();
116
+ if ( $ttl === null ) {
117
+ $ttl = strtotime( '+1 day' );
118
+ }
119
+ $headers['response_code'] = wp_remote_retrieve_response_code( $request );
120
+ $cached[ $url ] = [
121
+ 'ttl' => apply_filters( 'wd_tweaks_head_request_ttl', $ttl ),
122
+ 'data' => $headers
123
+ ];
124
+ $settings->setDValues( 'head_requests', $cached );
125
+ Utils::instance()->log( sprintf( 'Fetched header for %s into cache', $url ), 'tweaks' );
126
+
127
+ return $headers;
128
+ }
129
+
130
+ return $request;
131
+ }
132
+
133
+ /**
134
+ * @param $url
135
+ */
136
+ public function clearHeadRequest( $url ) {
137
+ $settings = Hardener\Model\Settings::instance();
138
+ $cached = $settings->getDValues( 'head_requests' );
139
+ unset( $cached[ $url ] );
140
+ $settings->setDValues( 'head_requests', $cached );
141
+ }
142
  }
app/module/hardener/rule.php CHANGED
@@ -25,14 +25,29 @@ abstract class Rule extends Component {
25
  * Return this rule content, we will try to use renderPartial
26
  *
27
  * @return mixed
 
28
  */
29
  abstract function getDescription();
30
 
31
  /**
32
- * Return this rule reason
 
 
 
 
 
 
 
33
  * @return mixed
34
  */
35
- abstract function getSubDescription();
 
 
 
 
 
 
 
36
 
37
  /**
38
  * @return mixed
@@ -99,27 +114,11 @@ abstract class Rule extends Component {
99
  return false;
100
  }
101
 
102
- $nonce = HTTP_Helper::retrieve_post( '_wdnonce' );
103
 
104
  return wp_verify_nonce( $nonce, self::$slug );
105
  }
106
 
107
- /**
108
- * Show ignore form
109
- */
110
- public function showIgnoreForm() {
111
- ?>
112
- <form method="post" class="hardener-frm ignore-frm rule-process">
113
- <?php $this->createNonceField(); ?>
114
- <input type="hidden" name="action" value="ignoreHardener"/>
115
- <input type="hidden" name="slug" value="<?php echo static::$slug ?>"/>
116
- <button type="submit" name="ignore" value="ignore" class="sui-button sui-button-ghost">
117
- <i class="sui-icon-eye-hide" aria-hidden="true"></i> <?php _e( "Ignore", "defender-security" ) ?>
118
- </button>
119
- </form>
120
- <?php
121
- }
122
-
123
  /**
124
  * @return bool
125
  */
@@ -129,33 +128,6 @@ abstract class Rule extends Component {
129
  return in_array( static::$slug, $ignored );
130
  }
131
 
132
- public function showRestoreForm() {
133
- ?>
134
- <div class="sui-accordion sui-accordion-flushed">
135
- <div class="sui-accordion-item accordion-ignore">
136
- <div class="sui-accordion-item-header">
137
- <div class="sui-accordion-item-title">
138
- <i aria-hidden="true" class="sui-icon-eye-hide"></i>
139
- <?php echo $this->getTitle(); ?>
140
- <div class="sui-actions-right">
141
- <form method="post" class="float-r hardener-frm rule-process">
142
- <?php $this->createNonceField(); ?>
143
- <input type="hidden" name="action" value="restoreHardener"/>
144
- <input type="hidden" name="slug" value="<?php echo static::$slug ?>"/>
145
- <button type="submit" class="sui-button sui-button-ghost">
146
- <i class="sui-icon-update" aria-hidden="true"></i>
147
- <?php _e( "Restore", "defender-security" ) ?>
148
- </button>
149
- </form>
150
- </div>
151
- </div>
152
- </div>
153
- </div>
154
- </div>
155
-
156
- <?php
157
- }
158
-
159
  /**
160
  * @return string
161
  */
@@ -179,4 +151,27 @@ abstract class Rule extends Component {
179
  'utils' => '\WP_Defender\Behavior\Utils'
180
  );
181
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
182
  }
25
  * Return this rule content, we will try to use renderPartial
26
  *
27
  * @return mixed
28
+ * @deprecated since 2.2
29
  */
30
  abstract function getDescription();
31
 
32
  /**
33
+ * This will return the short summary why this rule show up as issue
34
+ *
35
+ * @return string
36
+ */
37
+ abstract function getErrorReason();
38
+
39
+ /**
40
+ * This will return a short summary to show why this rule works
41
  * @return mixed
42
  */
43
+ abstract function getSuccessReason();
44
+
45
+ /**
46
+ * @return array
47
+ */
48
+ public function getMiscData() {
49
+ return array();
50
+ }
51
 
52
  /**
53
  * @return mixed
114
  return false;
115
  }
116
 
117
+ $nonce = HTTP_Helper::retrievePost( '_wdnonce' );
118
 
119
  return wp_verify_nonce( $nonce, self::$slug );
120
  }
121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  /**
123
  * @return bool
124
  */
128
  return in_array( static::$slug, $ignored );
129
  }
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  /**
132
  * @return string
133
  */
151
  'utils' => '\WP_Defender\Behavior\Utils'
152
  );
153
  }
154
+
155
+ /**
156
+ * A helper for tweak with security header, we need to check if the header is out or not
157
+ *
158
+ * @param $header
159
+ * @param $somewhere
160
+ *
161
+ * @return bool
162
+ */
163
+ protected function maybeSubmitHeader( $header, $somewhere ) {
164
+ if ( $somewhere == false ) {
165
+ return true;
166
+ }
167
+ $list = headers_list();
168
+ $match = false;
169
+ foreach ( $list as $item ) {
170
+ if ( stristr( $item, $header ) ) {
171
+ $match = true;
172
+ }
173
+ }
174
+
175
+ return $match;
176
+ }
177
  }
app/module/hardener/view/email/notification.php CHANGED
@@ -316,12 +316,12 @@
316
  <tr style="padding:0;text-align:left;vertical-align:top">
317
  <th class="menu-item float-center"
318
  style="Margin:0 auto;color:#000;float:none;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:25px;margin:0 auto;padding:0;padding-right:10px;text-align:center">
319
- <a href="#"
320
  style="Margin:0;color:#888;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:30px;margin:0;padding:0;text-align:center;text-decoration:underline">Manage
321
  your email preferences</a></th>
322
  <th class="menu-item float-center"
323
  style="Margin:0 auto;color:#000;float:none;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:25px;margin:0 auto;padding:0;padding-right:10px;text-align:center">
324
- <a href="#"
325
  style="Margin:0;color:#888;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:30px;margin:0;padding:0;text-align:center;text-decoration:underline">Unsubscribe</a>
326
  </th>
327
  </tr>
316
  <tr style="padding:0;text-align:left;vertical-align:top">
317
  <th class="menu-item float-center"
318
  style="Margin:0 auto;color:#000;float:none;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:25px;margin:0 auto;padding:0;padding-right:10px;text-align:center">
319
+ <a href="<?php echo network_admin_url('admin.php?page=wdf-hardener&view=notification') ?>"
320
  style="Margin:0;color:#888;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:30px;margin:0;padding:0;text-align:center;text-decoration:underline">Manage
321
  your email preferences</a></th>
322
  <th class="menu-item float-center"
323
  style="Margin:0 auto;color:#000;float:none;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:18px;font-weight:400;line-height:25px;margin:0 auto;padding:0;padding-right:10px;text-align:center">
324
+ <a href="<?php echo network_admin_url('admin.php?page=wdf-hardener&action=unsubscribe_notification') ?>"
325
  style="Margin:0;color:#888;font-family:'Open Sans',Helvetica,Arial,sans-serif;font-size:14px;font-weight:400;line-height:30px;margin:0;padding:0;text-align:center;text-decoration:underline">Unsubscribe</a>
326
  </th>
327
  </tr>
app/module/hardener/view/main.php CHANGED
@@ -1,4 +1,2 @@
1
- <?php
2
- /**
3
- * Author: Hoang Ngo
4
- */
1
+ <?php do_action('tweaks_footer'); ?>
2
+ <div id="defender"></div>
 
 
app/module/hardener/view/tweaks/csp/debug-bar.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div id="defender-csp-debug">
2
+ <div>
3
+ <p>
4
+ <?php _e( "You are in Test Mode. Use your browser developer console check if the security header directives are working as expected before publishing the change lives, or revert if they are causing issues.", "defender-security" ) ?>
5
+ </p>
6
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ) ?>">
7
+ <input type="hidden" name="action" value="defender-csp-debug-cancel"/>
8
+ <?php wp_nonce_field( 'defender-csp-debug-cancel' ) ?>
9
+ <button type="submit">
10
+ <?php _e( "Cancel", "defender-security" ) ?>
11
+ </button>
12
+ </form>
13
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ) ?>">
14
+ <input type="hidden" name="action" value="defender-csp-debug-apply">
15
+ <?php wp_nonce_field( 'defender-csp-debug-apply' ) ?>
16
+ <button type="submit" class="is-button-blue">
17
+ <?php _e( "Apply", "defender-security" ) ?>
18
+ </button>
19
+ </form>
20
+ </div>
21
+ </div>
app/module/hardener/view/tweaks/csp/notification-bar.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="sui-wrap">
2
+ <form method="post" action="<?php echo admin_url( 'admin-ajax.php' ) ?>">
3
+ <?php wp_create_nonce( 'defender-csp-debug-staging' ) ?>
4
+ <input type="hidden" name="action" value="defender-csp-debug-staging"/>
5
+ <div class="sui-notice-top sui-notice-info sui-cant-dismiss defender-csp-debug-staging">
6
+ <div class="sui-notice-content">
7
+ <p>
8
+ <?php _e( "The values have been added. You can now enforce the header or keep adjusting other directives. If you add values be sure to test before enforcing the header.", "defender-security" ) ?>
9
+ </p>
10
+ </div>
11
+ <span class="sui-cnotice-dismiss">
12
+ <button type="submit" class="sui-button-icon"><i class="sui-icon-check"></i></button>
13
+ </span>
14
+ </div>
15
+ </form>
16
+ </div>
app/module/ip-lockout.php CHANGED
@@ -7,11 +7,13 @@ namespace WP_Defender\Module;
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\IP_Lockout\Controller\Main;
 
10
 
11
  class IP_Lockout extends Module {
12
  public function __construct() {
13
  $this->register_post_type();
14
  new Main();
 
15
  }
16
 
17
  public function register_post_type() {
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\IP_Lockout\Controller\Main;
10
+ use WP_Defender\Module\IP_Lockout\Controller\Rest;
11
 
12
  class IP_Lockout extends Module {
13
  public function __construct() {
14
  $this->register_post_type();
15
  new Main();
16
+ new Rest();
17
  }
18
 
19
  public function register_post_type() {
app/module/ip-lockout/component/ip-api.php CHANGED
@@ -18,10 +18,10 @@ class IP_API extends Component {
18
  } elseif ( self::isV6( $ip ) && self::isV6( $subnet ) && self::isV6Support() ) {
19
  return self::_compareCIDRV6( $ip, $block );
20
  }
21
-
22
  return false;
23
  }
24
-
25
  /**
26
  * @param $ip
27
  * @param $block
@@ -35,10 +35,10 @@ class IP_API extends Component {
35
  $subnet = ip2long( $subnet );
36
  $mask = - 1 << ( 32 - $bits );
37
  $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
38
-
39
  return ( $ip & $mask ) == $subnet;
40
  }
41
-
42
  /**
43
  * @param $ip
44
  * @param $block
@@ -53,13 +53,13 @@ class IP_API extends Component {
53
  $subnet = self::expandIPv6( $subnet );
54
  $subnet = inet_pton( $subnet );
55
  $bSubnet = self::ineToBits( $subnet );
56
-
57
  $ipNetBits = substr( $bIP, 0, $bits );
58
  $subnetBits = substr( $bSubnet, 0, $bits );
59
-
60
  return $ipNetBits === $subnetBits;
61
  }
62
-
63
  /**
64
  * @param $inet
65
  *
@@ -73,10 +73,10 @@ class IP_API extends Component {
73
  foreach ( $unpacked as $char ) {
74
  $binaryip .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
75
  }
76
-
77
  return $binaryip;
78
  }
79
-
80
  /**
81
  * @param $ip
82
  * @param $firstInRange
@@ -90,10 +90,10 @@ class IP_API extends Component {
90
  } elseif ( self::isV6( $firstInRange ) && self::isV6( $lastInRange ) && self::isV6Support() ) {
91
  self::_compareV6InRange( $ip, $firstInRange, $lastInRange );
92
  }
93
-
94
  return false;
95
  }
96
-
97
  /**
98
  * @param $ip
99
  * @param $fistInRange
@@ -104,15 +104,15 @@ class IP_API extends Component {
104
  private static function _compareV4InRange( $ip, $fistInRange, $lastInRange ) {
105
  $low = sprintf( "%u", ip2long( $fistInRange ) );
106
  $high = sprintf( "%u", ip2long( $lastInRange ) );
107
-
108
  $cip = sprintf( "%u", ip2long( $ip ) );
109
  if ( $high >= $cip && $cip >= $low ) {
110
  return true;
111
  }
112
-
113
  return false;
114
  }
115
-
116
  /**
117
  * @param $ip
118
  * @param $firstInRange
@@ -124,7 +124,7 @@ class IP_API extends Component {
124
  $firstInRange = inet_pton( self::expandIPv6( $firstInRange ) );
125
  $lastInRange = inet_pton( self::expandIPv6( $lastInRange ) );
126
  $ip = inet_pton( self::expandIPv6( $ip ) );
127
-
128
  if ( ( strlen( $ip ) == strlen( $firstInRange ) )
129
  && ( $ip >= $firstInRange && $ip <= $lastInRange ) ) {
130
  return true;
@@ -132,7 +132,7 @@ class IP_API extends Component {
132
  return false;
133
  }
134
  }
135
-
136
  /**
137
  * Compare ip2 to ip1, true if ip2>ip1, false if not
138
  *
@@ -149,24 +149,36 @@ class IP_API extends Component {
149
  } elseif ( self::isV6( $ip1 ) && self::isV6( $ip2 ) && self::isV6Support() ) {
150
  $ip1 = inet_pton( self::expandIPv6( $ip1 ) );
151
  $ip2 = inet_pton( self::expandIPv6( $ip2 ) );
152
-
153
  return $ip2 > $ip1;
154
  }
155
-
156
  return false;
157
  }
158
-
 
 
 
 
159
  public static function getCurrentCountry() {
160
  $settings = Settings::instance();
 
 
 
 
161
  if ( ! $settings->isGeoDBDownloaded() ) {
162
  return false;
163
  }
164
- $geoIP = new GeoIp( $settings->geoIP_db );
 
 
 
 
165
  $country = $geoIP->ipToCountry( Utils::instance()->getUserIp() );
166
-
167
  return $country;
168
  }
169
-
170
  /**
171
  * @param $ip
172
  *
@@ -175,7 +187,7 @@ class IP_API extends Component {
175
  private static function isV4( $ip ) {
176
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
177
  }
178
-
179
  /**
180
  * @param $ip
181
  *
@@ -184,7 +196,7 @@ class IP_API extends Component {
184
  private static function isV6( $ip ) {
185
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
186
  }
187
-
188
  /**
189
  * @param $ip
190
  *
@@ -193,10 +205,10 @@ class IP_API extends Component {
193
  public static function expandIPv6( $ip ) {
194
  $hex = unpack( "H*hex", inet_pton( $ip ) );
195
  $ip = substr( preg_replace( "/([A-f0-9]{4})/", "$1:", $hex['hex'] ), 0, - 1 );
196
-
197
  return $ip;
198
  }
199
-
200
  /**
201
  * @return bool
202
  */
18
  } elseif ( self::isV6( $ip ) && self::isV6( $subnet ) && self::isV6Support() ) {
19
  return self::_compareCIDRV6( $ip, $block );
20
  }
21
+
22
  return false;
23
  }
24
+
25
  /**
26
  * @param $ip
27
  * @param $block
35
  $subnet = ip2long( $subnet );
36
  $mask = - 1 << ( 32 - $bits );
37
  $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
38
+
39
  return ( $ip & $mask ) == $subnet;
40
  }
41
+
42
  /**
43
  * @param $ip
44
  * @param $block
53
  $subnet = self::expandIPv6( $subnet );
54
  $subnet = inet_pton( $subnet );
55
  $bSubnet = self::ineToBits( $subnet );
56
+
57
  $ipNetBits = substr( $bIP, 0, $bits );
58
  $subnetBits = substr( $bSubnet, 0, $bits );
59
+
60
  return $ipNetBits === $subnetBits;
61
  }
62
+
63
  /**
64
  * @param $inet
65
  *
73
  foreach ( $unpacked as $char ) {
74
  $binaryip .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
75
  }
76
+
77
  return $binaryip;
78
  }
79
+
80
  /**
81
  * @param $ip
82
  * @param $firstInRange
90
  } elseif ( self::isV6( $firstInRange ) && self::isV6( $lastInRange ) && self::isV6Support() ) {
91
  self::_compareV6InRange( $ip, $firstInRange, $lastInRange );
92
  }
93
+
94
  return false;
95
  }
96
+
97
  /**
98
  * @param $ip
99
  * @param $fistInRange
104
  private static function _compareV4InRange( $ip, $fistInRange, $lastInRange ) {
105
  $low = sprintf( "%u", ip2long( $fistInRange ) );
106
  $high = sprintf( "%u", ip2long( $lastInRange ) );
107
+
108
  $cip = sprintf( "%u", ip2long( $ip ) );
109
  if ( $high >= $cip && $cip >= $low ) {
110
  return true;
111
  }
112
+
113
  return false;
114
  }
115
+
116
  /**
117
  * @param $ip
118
  * @param $firstInRange
124
  $firstInRange = inet_pton( self::expandIPv6( $firstInRange ) );
125
  $lastInRange = inet_pton( self::expandIPv6( $lastInRange ) );
126
  $ip = inet_pton( self::expandIPv6( $ip ) );
127
+
128
  if ( ( strlen( $ip ) == strlen( $firstInRange ) )
129
  && ( $ip >= $firstInRange && $ip <= $lastInRange ) ) {
130
  return true;
132
  return false;
133
  }
134
  }
135
+
136
  /**
137
  * Compare ip2 to ip1, true if ip2>ip1, false if not
138
  *
149
  } elseif ( self::isV6( $ip1 ) && self::isV6( $ip2 ) && self::isV6Support() ) {
150
  $ip1 = inet_pton( self::expandIPv6( $ip1 ) );
151
  $ip2 = inet_pton( self::expandIPv6( $ip2 ) );
152
+
153
  return $ip2 > $ip1;
154
  }
155
+
156
  return false;
157
  }
158
+
159
+ /**
160
+ * @return array|bool
161
+ * @throws \MaxMind\Db\Reader\InvalidDatabaseException
162
+ */
163
  public static function getCurrentCountry() {
164
  $settings = Settings::instance();
165
+ if ( php_sapi_name() === 'cli' ) {
166
+ //never catch if from cli
167
+ return false;
168
+ }
169
  if ( ! $settings->isGeoDBDownloaded() ) {
170
  return false;
171
  }
172
+ $geoIP = new GeoIp( $settings->geoIP_db );
173
+ $ip = Utils::instance()->getUserIp();
174
+ if ( ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
175
+ return false;
176
+ }
177
  $country = $geoIP->ipToCountry( Utils::instance()->getUserIp() );
178
+
179
  return $country;
180
  }
181
+
182
  /**
183
  * @param $ip
184
  *
187
  private static function isV4( $ip ) {
188
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
189
  }
190
+
191
  /**
192
  * @param $ip
193
  *
196
  private static function isV6( $ip ) {
197
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
198
  }
199
+
200
  /**
201
  * @param $ip
202
  *
205
  public static function expandIPv6( $ip ) {
206
  $hex = unpack( "H*hex", inet_pton( $ip ) );
207
  $ip = substr( preg_replace( "/([A-f0-9]{4})/", "$1:", $hex['hex'] ), 0, - 1 );
208
+
209
  return $ip;
210
  }
211
+
212
  /**
213
  * @return bool
214
  */
app/module/ip-lockout/component/login-listener.php ADDED
@@ -0,0 +1,185 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\IP_Lockout\Component;
7
+
8
+ use WP_Defender\Behavior\Utils;
9
+ use WP_Defender\Controller;
10
+ use WP_Defender\Module\IP_Lockout\Model\IP_Model;
11
+ use WP_Defender\Module\IP_Lockout\Model\Log_Model;
12
+ use WP_Defender\Module\IP_Lockout\Model\Settings;
13
+
14
+ class Login_Listener extends Controller {
15
+ public function __construct() {
16
+ $settings = Settings::instance();
17
+ if ( $settings->login_protection ) {
18
+ $this->addAction( 'wp_login_failed', 'recordFailLogin', 9999 );
19
+ $this->addFilter( 'authenticate', 'showAttemptLeft', 9999, 3 );
20
+ $this->addAction( 'wp_login', 'clearAttemptStats', 10, 2 );
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Record fail login as log into db
26
+ *
27
+ * @param $username
28
+ */
29
+ public function recordFailLogin( $username ) {
30
+ if ( $_SERVER['REQUEST_METHOD'] == 'GET' ) {
31
+ //do nothing as wp-login.php wil trigger the wp_signon for cookie login, can cause trouble
32
+ return;
33
+ }
34
+ $settings = Settings::instance();
35
+ //first check if the username is fail to ban
36
+ $username = strtolower( $username );
37
+ $model = IP_Model::init();
38
+
39
+ if ( in_array( $username, $settings->getUsernameBlacklist() ) ) {
40
+ return $this->lock( $model, $username, 'blacklist_uname' );
41
+ }
42
+ //log for the event
43
+ $this->log( $username, Log_Model::AUTH_FAIL, sprintf( esc_html__( "Failed login attempt with username %s", "defender-security" ), $username ) );
44
+
45
+ //calculate if this one out of threshold
46
+ $window = strtotime( '- ' . $settings->login_protection_lockout_timeframe . ' seconds' );
47
+ if ( $window < $model->lock_time ) {
48
+ /**
49
+ * Case if it just banned and the lockout duration too short, we use the locktime instead
50
+ */
51
+ $window = $model->lock_time;
52
+ }
53
+
54
+ $attempt = Log_Model::count( [
55
+ 'ip' => Utils::instance()->getUserIp(),
56
+ 'date' => [ 'compare' => '>', 'value' => $window ],
57
+ 'type' => Log_Model::AUTH_FAIL,
58
+ 'blog_id' => get_current_blog_id()
59
+ ] );
60
+ $model->attempt = $attempt;
61
+ if ( $attempt >= $settings->login_protection_login_attempt ) {
62
+ $scenario = $settings->login_protection_lockout_ban ? 'ban' : 'normal';
63
+
64
+ return $this->lock( $model, $username, $scenario );
65
+ }
66
+ $model->save();
67
+ }
68
+
69
+ /**
70
+ * Reset the attempt counter
71
+ */
72
+ public function clearAttemptStats() {
73
+ $model = IP_Model::init();
74
+ $model->attempt = 1;
75
+ $model->save();
76
+ }
77
+
78
+ /**
79
+ * @param $user
80
+ * @param $username
81
+ * @param $password
82
+ *
83
+ * @return mixed
84
+ */
85
+ public function showAttemptLeft( $user, $username, $password ) {
86
+ if ( is_wp_error( $user ) && $_SERVER['REQUEST_METHOD'] == 'POST' && ! in_array( $user->get_error_code(), array(
87
+ 'empty_username',
88
+ 'empty_password'
89
+ ) )
90
+ ) {
91
+ $model = IP_Model::init();
92
+ $settings = Settings::instance();
93
+ if ( in_array( $username, $settings->getUsernameBlacklist() ) ) {
94
+ $user->add( 'def_warning', esc_html__( "You have been locked out by the administrator for attempting to login with a banned username", "defender-security" ) );
95
+ } else {
96
+ $attempt = $model->attempt + 1;
97
+ //because the action authenticate trigger before wp_login_failed, so we need to add 1 for the attemt
98
+ if ( $attempt >= $settings->login_protection_login_attempt ) {
99
+ //show lockout message
100
+ $user->add( 'def_warning', $settings->login_protection_lockout_message );
101
+ } else {
102
+ $user->add( 'def_warning', sprintf( esc_html__( "%d login attempts remaining", "defender-security" ), $settings->login_protection_login_attempt - $attempt ) );
103
+ }
104
+ }
105
+ }
106
+
107
+ return $user;
108
+ }
109
+
110
+ /**
111
+ * @param IP_Model $model
112
+ * @param $username
113
+ * @param $scenario
114
+ */
115
+ private function lock( IP_Model $model, $username, $scenario ) {
116
+ $settings = Settings::instance();
117
+ if ( $scenario === 'blacklist_uname' ) {
118
+ $model->lockout_message = esc_html__( "You have been locked out by the administrator for attempting to login with a banned username", "defender-security" );
119
+ $model->status = IP_Model::STATUS_BLOCKED;
120
+ $model->lock_time = time();
121
+ $model->save();
122
+
123
+ //add to blacklist
124
+ Settings::instance()->addIpToList( $model->ip, 'blacklist' );
125
+ $this->log( $username, Log_Model::AUTH_LOCK, sprintf( esc_html__( "Failed login attempt with a ban username %s", "defender-security" ), $username ) );
126
+ } else {
127
+ $model->status = IP_Model::STATUS_BLOCKED;
128
+ $model->release_time = strtotime( '+ ' . $settings->login_protection_lockout_duration . ' ' . $settings->login_protection_lockout_duration_unit );
129
+ $model->lockout_message = $settings->login_protection_lockout_message;
130
+ $model->lock_time = time();
131
+ $model->save();
132
+ if ( $scenario === 'ban' ) {
133
+ $settings->addIpToList( $model->ip, 'blacklist' );
134
+ }
135
+ $this->log( $username, Log_Model::AUTH_LOCK, __( "Lockout occurred: Too many failed login attempts", "defender-security" ) );
136
+ }
137
+ do_action( 'wd_login_lockout', $model, $scenario );
138
+ if ( $settings->login_lockout_notification ) {
139
+ $this->email( $model );
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Log the event into db, we will use the data in logs page later
145
+ *
146
+ * @param $username
147
+ * @param $type
148
+ * @param $log
149
+ */
150
+ private function log( $username, $type, $log ) {
151
+ $model = new Log_Model();
152
+ $model->ip = Utils::instance()->getUserIp();
153
+ $model->user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
154
+ $model->log = $log;
155
+ $model->date = time();
156
+ $model->type = $type;
157
+ $model->tried = $username;
158
+ $model->save();
159
+ }
160
+
161
+ private function email( IP_Model $model ) {
162
+ $settings = Settings::instance();
163
+
164
+ if ( ! Login_Protection_Api::maybeSendNotification( 'login', $model, $settings ) ) {
165
+ return;
166
+ }
167
+
168
+ $view = ( $settings->isBlacklist( $model->ip ) ) ? 'emails/login-username-ban' : 'emails/login-lockout';
169
+ foreach ( $settings->receipts as $item ) {
170
+ $content = $this->renderPartial( $view, array(
171
+ 'admin' => $item['first_name'],
172
+ 'ip' => $model->ip,
173
+ 'logs_url' => apply_filters( 'report_email_logs_link', apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ), $item['email'] ),
174
+ 'report_url' => apply_filters( 'report_email_logs_link', network_admin_url( 'admin.php?page=wdf-ip-lockout&view=reporting' ), $item['email'] ),
175
+ ), false );
176
+ $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
177
+ $no_reply_email = apply_filters( 'wd_lockout_noreply_email', $no_reply_email );
178
+ $headers = array(
179
+ 'From: Defender <' . $no_reply_email . '>',
180
+ 'Content-Type: text/html; charset=UTF-8'
181
+ );
182
+ wp_mail( $item['email'], sprintf( __( "Login lockout alert for %s", "defender-security" ), network_site_url() ), $content, $headers );
183
+ }
184
+ }
185
+ }
app/module/ip-lockout/component/login-protection-api.php CHANGED
@@ -14,8 +14,10 @@ use WP_Defender\Module\IP_Lockout\Model\Settings;
14
 
15
  class Login_Protection_Api extends Component {
16
  const COUNT_TOTAL = 'wdCountTotals';
17
-
18
  /**
 
 
19
  * @param Log_Model $log
20
  * @param bool $force
21
  */
@@ -24,12 +26,12 @@ class Login_Protection_Api extends Component {
24
  $model = IP_Model::findOne( array(
25
  'ip' => $log->ip
26
  ) );
27
-
28
  if ( is_object( $model ) && $model->status == IP_Model::STATUS_BLOCKED ) {
29
  //already locked, just return
30
  return;
31
  }
32
-
33
  $settings = Settings::instance();
34
  //find backward from log date, if there are only log & counter > max attempt, then lock
35
  $after = strtotime( '-' . $settings->login_protection_lockout_timeframe . ' seconds' );
@@ -39,14 +41,14 @@ class Login_Protection_Api extends Component {
39
  $after = $model->lock_time;
40
  }
41
  }
42
-
43
  $attempt = Log_Model::count( array(
44
  'ip' => $log->ip,
45
  'type' => Log_Model::AUTH_FAIL,
46
  'blog_id' => get_current_blog_id(),
47
  'date' => array( 'compare' => '>', 'value' => $after )
48
  ) );
49
-
50
  if ( ! is_object( $model ) ) {
51
  //no record, create one
52
  $model = new IP_Model();
@@ -82,14 +84,14 @@ class Login_Protection_Api extends Component {
82
  if ( $settings->login_protection_lockout_ban || $blacklist ) {
83
  $settings->addIpToList( $model->ip, 'blacklist' );
84
  }
85
-
86
  //trigger an action
87
  do_action( 'wd_login_lockout', $model, $force, $blacklist );
88
  } else {
89
  $model->save();
90
  }
91
  }
92
-
93
  /**
94
  * @param Log_Model $log
95
  */
@@ -98,16 +100,16 @@ class Login_Protection_Api extends Component {
98
  $model = IP_Model::findOne( array(
99
  'ip' => $log->ip
100
  ) );
101
-
102
  if ( is_object( $model ) && $model->status == IP_Model::STATUS_BLOCKED ) {
103
  //already locked, just return
104
  return;
105
  }
106
-
107
  $settings = Settings::instance();
108
  //find backward from log date, if there are only log & counter > max attempt, then lock
109
  $after = strtotime( '- ' . $settings->detect_404_timeframe . ' seconds' );
110
-
111
  if ( is_object( $model ) ) {
112
  //recal release time, if after time smaller than lock time,then we will use last locktime for check
113
  if ( $after < $model->lock_time_404 ) {
@@ -123,14 +125,14 @@ class Login_Protection_Api extends Component {
123
  'value' => $after
124
  )
125
  ) );
126
-
127
  if ( ! is_object( $model ) ) {
128
  //no record, create one
129
  $model = new IP_Model();
130
  $model->ip = $log->ip;
131
  $model->status = IP_Model::STATUS_NORMAL;
132
  }
133
-
134
  //filter out the extension
135
  $ignoresFileTypes = $settings->get404Ignorelist();
136
  foreach ( $logs as $k => $log ) {
@@ -139,7 +141,7 @@ class Login_Protection_Api extends Component {
139
  unset( $logs[ $k ] );
140
  }
141
  }
142
-
143
  if ( count( $logs ) >= $settings->detect_404_threshold ) {
144
  //we need to check the extension
145
  $model->status = IP_Model::STATUS_BLOCKED;
@@ -164,7 +166,7 @@ class Login_Protection_Api extends Component {
164
  do_action( 'wd_404_lockout', $model, $uri, $isBlacklist );
165
  }
166
  }
167
-
168
  /**
169
  * @param null $time - unix timestamp
170
  *
@@ -179,10 +181,10 @@ class Login_Protection_Api extends Component {
179
  'value' => $time
180
  )
181
  ) );
182
-
183
  return $logs;
184
  }
185
-
186
  /**
187
  * @param null $time - unix timestamp
188
  *
@@ -197,10 +199,10 @@ class Login_Protection_Api extends Component {
197
  'value' => $time
198
  )
199
  ) );
200
-
201
  return $logs;
202
  }
203
-
204
  /**
205
  * @param null $time - unix timestamp
206
  *
@@ -218,10 +220,10 @@ class Login_Protection_Api extends Component {
218
  'value' => $time
219
  )
220
  ) );
221
-
222
  return $logs;
223
  }
224
-
225
  /**
226
  * @return Log_Model
227
  * @deprecated
@@ -237,10 +239,10 @@ class Login_Protection_Api extends Component {
237
  if ( is_object( $log ) ) {
238
  return $log;
239
  }
240
-
241
  return null;
242
  }
243
-
244
  public static function time_since( $since ) {
245
  $since = time() - $since;
246
  if ( $since < 0 ) {
@@ -255,7 +257,7 @@ class Login_Protection_Api extends Component {
255
  array( 60, esc_html__( "minute" ) ),
256
  array( 1, esc_html__( "second" ) )
257
  );
258
-
259
  for ( $i = 0, $j = count( $chunks ); $i < $j; $i ++ ) {
260
  $seconds = $chunks[ $i ][0];
261
  $name = $chunks[ $i ][1];
@@ -263,12 +265,12 @@ class Login_Protection_Api extends Component {
263
  break;
264
  }
265
  }
266
-
267
  $print = ( $count == 1 ) ? '1 ' . $name : "$count {$name}s";
268
-
269
  return $print;
270
  }
271
-
272
  /**
273
  * @return string
274
  */
@@ -277,7 +279,7 @@ class Login_Protection_Api extends Component {
277
  $settings = Settings::instance();
278
  $blacklist = $settings->getIpBlacklist();
279
  $whitelist = $settings->getIpWhitelist();
280
-
281
  $ip = Utils::instance()->getUserIp();
282
  $nonce = wp_create_nonce( 'lockoutIPAction' );
283
  if ( $ip != $log->ip ) {
@@ -287,16 +289,16 @@ class Login_Protection_Api extends Component {
287
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-blue" data-type="unblacklist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#">' . __( "Unban IP", "defender-security" ) . '</a>';
288
  }
289
  }
290
-
291
  if ( ! in_array( $log->ip, $whitelist ) ) {
292
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-ghost" data-type="whitelist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#"><i class="sui-icon-check-tick" aria-hidden="true"></i>' . __( "Add Whitelist", "defender-security" ) . '</a>';
293
  } else {
294
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-ghost" data-type="unwhitelist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#">' . __( "Unwhitelist", "defender-security" ) . '</a>';
295
  }
296
-
297
  return implode( '', $links );
298
  }
299
-
300
  /**
301
  * Validate import file is in right format and usable for IP Lockout
302
  *
@@ -308,34 +310,33 @@ class Login_Protection_Api extends Component {
308
  $fp = fopen( $file, 'r' );
309
  $data = array();
310
  while ( ( $line = fgetcsv( $fp ) ) !== false ) {
311
-
312
  if ( count( $line ) != 2 ) {
313
  return false;
314
  }
315
-
316
  if ( ! in_array( $line[1], array( 'whitelist', 'blacklist' ) ) ) {
317
  return false;
318
  }
319
-
320
  if ( Settings::instance()->validateIp( $line[0] ) == false ) {
321
- return false;
322
  }
323
-
324
  $data[] = $line;
325
-
326
  }
327
  fclose( $fp );
328
-
329
  return $data;
330
  }
331
-
332
  /**
333
  * @return bool
334
  */
335
  public static function isActive() {
336
  return Settings::instance()->login_protection && Settings::instance()->detect_404;
337
  }
338
-
339
  /**
340
  * @param bool $clearCron
341
  *
@@ -363,7 +364,7 @@ class Login_Protection_Api extends Component {
363
  $nextTimeString = date( 'Y-m-d', strtotime( $settings->report_day . ' next month' ) ) . ' ' . $settings->report_time . ':00';
364
  break;
365
  }
366
-
367
  $toUTC = Utils::instance()->localToUtc( $timeString );
368
  if ( $toUTC <= time() ) {
369
  if ( $utc ) {
@@ -379,7 +380,7 @@ class Login_Protection_Api extends Component {
379
  }
380
  }
381
  }
382
-
383
  /**
384
  * Check if useragent is looks like from google
385
  *
@@ -396,14 +397,14 @@ class Login_Protection_Api extends Component {
396
  } else {
397
  $userAgent = strtolower( $userAgent );
398
  }
399
-
400
  if ( stristr( $userAgent, 'googlebot' ) !== false ) {
401
  return true;
402
  }
403
-
404
  return false;
405
  }
406
-
407
  /**
408
  * Check if IP is from google, base on https://support.google.com/webmasters/answer/80553?hl=en
409
  *
@@ -423,10 +424,10 @@ class Login_Protection_Api extends Component {
423
  }
424
  }
425
  }
426
-
427
  return false;
428
  }
429
-
430
  /**
431
  * Check if IP is from Bing, base on https://www.bing.com/webmaster/help/how-to-verify-bingbot-3905dc26
432
  *
@@ -443,10 +444,10 @@ class Login_Protection_Api extends Component {
443
  }
444
  }
445
  }
446
-
447
  return false;
448
  }
449
-
450
  public static function isBingUA( $userAgent = '' ) {
451
  if ( empty( $userAgent ) ) {
452
  $userAgent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
@@ -458,14 +459,14 @@ class Login_Protection_Api extends Component {
458
  }
459
  //MSN Bot Useragent https://www.bing.com/webmaster/help/which-crawlers-does-bing-use-8c184ec0
460
  $msnUA = "Bingbot|MSNBot|MSNBot-Media|AdIdxBot|BingPreview";
461
-
462
  if ( preg_match( '/' . $msnUA . '/i', $userAgent ) ) {
463
  return true;
464
  }
465
-
466
  return false;
467
  }
468
-
469
  public static function maybeSendNotification( $type, $model, $settings ) {
470
  $lastSentKey = $type == 'login' ? 'lastSentLockout' : 'lastSent404';
471
  $stopTimeKey = $type == 'login' ? 'stopTimeLockout' : 'stopTime404';
@@ -488,7 +489,7 @@ class Login_Protection_Api extends Component {
488
  if ( $stopTime && $lastSent < $stopTime ) {
489
  $lastSent = $stopTime;
490
  }
491
-
492
  $count = Log_Model::count( array(
493
  'type' => $type == 'login' ? Log_Model::AUTH_LOCK : Log_Model::LOCKOUT_404,
494
  'blog_id' => get_current_blog_id(),
@@ -502,16 +503,16 @@ class Login_Protection_Api extends Component {
502
  $model->updateMeta( $lastSentKey, time() );
503
  }
504
  }
505
-
506
  return true;
507
  }
508
-
509
  /**
510
  *
511
  */
512
  public static function createTables() {
513
  global $wpdb;
514
-
515
  $charsetCollate = $wpdb->get_charset_collate();
516
  $tableName1 = $wpdb->base_prefix . 'defender_lockout';
517
  $tableName2 = $wpdb->base_prefix . 'defender_lockout_log';
@@ -543,7 +544,7 @@ CREATE TABLE `{$tableName2}` (
543
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
544
  dbDelta( $sql );
545
  }
546
-
547
  public static function alterTableFor171() {
548
  global $wpdb;
549
  $tableName1 = $wpdb->base_prefix . 'defender_lockout_log';
@@ -561,7 +562,16 @@ CREATE TABLE `{$tableName2}` (
561
  $wpdb->query( $sql );
562
  }
563
  }
564
-
 
 
 
 
 
 
 
 
 
565
  /**
566
  * @param $ip
567
  *
@@ -574,21 +584,21 @@ CREATE TABLE `{$tableName2}` (
574
  if ( Settings::instance()->isBlacklist( $ip ) ) {
575
  return __( "Is blacklisted", "defender-security" );
576
  }
577
-
578
  $model = IP_Model::findOne( array(
579
  'ip' => $ip
580
  ) );
581
  if ( ! is_object( $model ) ) {
582
  return __( "Not banned", "defender-security" );
583
  }
584
-
585
  if ( $model->status == IP_Model::STATUS_BLOCKED ) {
586
  return __( "Banned", "defender-security" );
587
  } elseif ( $model->status == IP_Model::STATUS_NORMAL ) {
588
  return __( "Not banned", "defender-security" );
589
  }
590
  }
591
-
592
  /**
593
  * @return bool
594
  */
@@ -600,7 +610,34 @@ CREATE TABLE `{$tableName2}` (
600
  $wpdb->get_var( "SHOW TABLES LIKE '$tableName2'" ) != $tableName2 ) {
601
  return false;
602
  }
603
-
604
  return true;
605
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
  }
14
 
15
  class Login_Protection_Api extends Component {
16
  const COUNT_TOTAL = 'wdCountTotals';
17
+
18
  /**
19
+ * This function will be called when
20
+ *
21
  * @param Log_Model $log
22
  * @param bool $force
23
  */
26
  $model = IP_Model::findOne( array(
27
  'ip' => $log->ip
28
  ) );
29
+
30
  if ( is_object( $model ) && $model->status == IP_Model::STATUS_BLOCKED ) {
31
  //already locked, just return
32
  return;
33
  }
34
+
35
  $settings = Settings::instance();
36
  //find backward from log date, if there are only log & counter > max attempt, then lock
37
  $after = strtotime( '-' . $settings->login_protection_lockout_timeframe . ' seconds' );
41
  $after = $model->lock_time;
42
  }
43
  }
44
+
45
  $attempt = Log_Model::count( array(
46
  'ip' => $log->ip,
47
  'type' => Log_Model::AUTH_FAIL,
48
  'blog_id' => get_current_blog_id(),
49
  'date' => array( 'compare' => '>', 'value' => $after )
50
  ) );
51
+
52
  if ( ! is_object( $model ) ) {
53
  //no record, create one
54
  $model = new IP_Model();
84
  if ( $settings->login_protection_lockout_ban || $blacklist ) {
85
  $settings->addIpToList( $model->ip, 'blacklist' );
86
  }
87
+
88
  //trigger an action
89
  do_action( 'wd_login_lockout', $model, $force, $blacklist );
90
  } else {
91
  $model->save();
92
  }
93
  }
94
+
95
  /**
96
  * @param Log_Model $log
97
  */
100
  $model = IP_Model::findOne( array(
101
  'ip' => $log->ip
102
  ) );
103
+
104
  if ( is_object( $model ) && $model->status == IP_Model::STATUS_BLOCKED ) {
105
  //already locked, just return
106
  return;
107
  }
108
+
109
  $settings = Settings::instance();
110
  //find backward from log date, if there are only log & counter > max attempt, then lock
111
  $after = strtotime( '- ' . $settings->detect_404_timeframe . ' seconds' );
112
+
113
  if ( is_object( $model ) ) {
114
  //recal release time, if after time smaller than lock time,then we will use last locktime for check
115
  if ( $after < $model->lock_time_404 ) {
125
  'value' => $after
126
  )
127
  ) );
128
+
129
  if ( ! is_object( $model ) ) {
130
  //no record, create one
131
  $model = new IP_Model();
132
  $model->ip = $log->ip;
133
  $model->status = IP_Model::STATUS_NORMAL;
134
  }
135
+
136
  //filter out the extension
137
  $ignoresFileTypes = $settings->get404Ignorelist();
138
  foreach ( $logs as $k => $log ) {
141
  unset( $logs[ $k ] );
142
  }
143
  }
144
+
145
  if ( count( $logs ) >= $settings->detect_404_threshold ) {
146
  //we need to check the extension
147
  $model->status = IP_Model::STATUS_BLOCKED;
166
  do_action( 'wd_404_lockout', $model, $uri, $isBlacklist );
167
  }
168
  }
169
+
170
  /**
171
  * @param null $time - unix timestamp
172
  *
181
  'value' => $time
182
  )
183
  ) );
184
+
185
  return $logs;
186
  }
187
+
188
  /**
189
  * @param null $time - unix timestamp
190
  *
199
  'value' => $time
200
  )
201
  ) );
202
+
203
  return $logs;
204
  }
205
+
206
  /**
207
  * @param null $time - unix timestamp
208
  *
220
  'value' => $time
221
  )
222
  ) );
223
+
224
  return $logs;
225
  }
226
+
227
  /**
228
  * @return Log_Model
229
  * @deprecated
239
  if ( is_object( $log ) ) {
240
  return $log;
241
  }
242
+
243
  return null;
244
  }
245
+
246
  public static function time_since( $since ) {
247
  $since = time() - $since;
248
  if ( $since < 0 ) {
257
  array( 60, esc_html__( "minute" ) ),
258
  array( 1, esc_html__( "second" ) )
259
  );
260
+
261
  for ( $i = 0, $j = count( $chunks ); $i < $j; $i ++ ) {
262
  $seconds = $chunks[ $i ][0];
263
  $name = $chunks[ $i ][1];
265
  break;
266
  }
267
  }
268
+
269
  $print = ( $count == 1 ) ? '1 ' . $name : "$count {$name}s";
270
+
271
  return $print;
272
  }
273
+
274
  /**
275
  * @return string
276
  */
279
  $settings = Settings::instance();
280
  $blacklist = $settings->getIpBlacklist();
281
  $whitelist = $settings->getIpWhitelist();
282
+
283
  $ip = Utils::instance()->getUserIp();
284
  $nonce = wp_create_nonce( 'lockoutIPAction' );
285
  if ( $ip != $log->ip ) {
289
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-blue" data-type="unblacklist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#">' . __( "Unban IP", "defender-security" ) . '</a>';
290
  }
291
  }
292
+
293
  if ( ! in_array( $log->ip, $whitelist ) ) {
294
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-ghost" data-type="whitelist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#"><i class="sui-icon-check-tick" aria-hidden="true"></i>' . __( "Add Whitelist", "defender-security" ) . '</a>';
295
  } else {
296
  $links[] = '<a data-nonce="' . $nonce . '" class="ip-action sui-button sui-button-ghost" data-type="unwhitelist" data-id="' . esc_attr( $log->id ) . '" data-ip="' . esc_attr( $log->ip ) . '" href="#">' . __( "Unwhitelist", "defender-security" ) . '</a>';
297
  }
298
+
299
  return implode( '', $links );
300
  }
301
+
302
  /**
303
  * Validate import file is in right format and usable for IP Lockout
304
  *
310
  $fp = fopen( $file, 'r' );
311
  $data = array();
312
  while ( ( $line = fgetcsv( $fp ) ) !== false ) {
 
313
  if ( count( $line ) != 2 ) {
314
  return false;
315
  }
316
+
317
  if ( ! in_array( $line[1], array( 'whitelist', 'blacklist' ) ) ) {
318
  return false;
319
  }
320
+
321
  if ( Settings::instance()->validateIp( $line[0] ) == false ) {
322
+ continue;
323
  }
324
+
325
  $data[] = $line;
326
+
327
  }
328
  fclose( $fp );
329
+
330
  return $data;
331
  }
332
+
333
  /**
334
  * @return bool
335
  */
336
  public static function isActive() {
337
  return Settings::instance()->login_protection && Settings::instance()->detect_404;
338
  }
339
+
340
  /**
341
  * @param bool $clearCron
342
  *
364
  $nextTimeString = date( 'Y-m-d', strtotime( $settings->report_day . ' next month' ) ) . ' ' . $settings->report_time . ':00';
365
  break;
366
  }
367
+
368
  $toUTC = Utils::instance()->localToUtc( $timeString );
369
  if ( $toUTC <= time() ) {
370
  if ( $utc ) {
380
  }
381
  }
382
  }
383
+
384
  /**
385
  * Check if useragent is looks like from google
386
  *
397
  } else {
398
  $userAgent = strtolower( $userAgent );
399
  }
400
+
401
  if ( stristr( $userAgent, 'googlebot' ) !== false ) {
402
  return true;
403
  }
404
+
405
  return false;
406
  }
407
+
408
  /**
409
  * Check if IP is from google, base on https://support.google.com/webmasters/answer/80553?hl=en
410
  *
424
  }
425
  }
426
  }
427
+
428
  return false;
429
  }
430
+
431
  /**
432
  * Check if IP is from Bing, base on https://www.bing.com/webmaster/help/how-to-verify-bingbot-3905dc26
433
  *
444
  }
445
  }
446
  }
447
+
448
  return false;
449
  }
450
+
451
  public static function isBingUA( $userAgent = '' ) {
452
  if ( empty( $userAgent ) ) {
453
  $userAgent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
459
  }
460
  //MSN Bot Useragent https://www.bing.com/webmaster/help/which-crawlers-does-bing-use-8c184ec0
461
  $msnUA = "Bingbot|MSNBot|MSNBot-Media|AdIdxBot|BingPreview";
462
+
463
  if ( preg_match( '/' . $msnUA . '/i', $userAgent ) ) {
464
  return true;
465
  }
466
+
467
  return false;
468
  }
469
+
470
  public static function maybeSendNotification( $type, $model, $settings ) {
471
  $lastSentKey = $type == 'login' ? 'lastSentLockout' : 'lastSent404';
472
  $stopTimeKey = $type == 'login' ? 'stopTimeLockout' : 'stopTime404';
489
  if ( $stopTime && $lastSent < $stopTime ) {
490
  $lastSent = $stopTime;
491
  }
492
+
493
  $count = Log_Model::count( array(
494
  'type' => $type == 'login' ? Log_Model::AUTH_LOCK : Log_Model::LOCKOUT_404,
495
  'blog_id' => get_current_blog_id(),
503
  $model->updateMeta( $lastSentKey, time() );
504
  }
505
  }
506
+
507
  return true;
508
  }
509
+
510
  /**
511
  *
512
  */
513
  public static function createTables() {
514
  global $wpdb;
515
+
516
  $charsetCollate = $wpdb->get_charset_collate();
517
  $tableName1 = $wpdb->base_prefix . 'defender_lockout';
518
  $tableName2 = $wpdb->base_prefix . 'defender_lockout_log';
544
  require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
545
  dbDelta( $sql );
546
  }
547
+
548
  public static function alterTableFor171() {
549
  global $wpdb;
550
  $tableName1 = $wpdb->base_prefix . 'defender_lockout_log';
562
  $wpdb->query( $sql );
563
  }
564
  }
565
+
566
+ /**
567
+ * @return array
568
+ */
569
+ public static function getIpsLocked() {
570
+ return IP_Model::findAll( array(
571
+ 'status' => IP_Model::STATUS_BLOCKED
572
+ ) );
573
+ }
574
+
575
  /**
576
  * @param $ip
577
  *
584
  if ( Settings::instance()->isBlacklist( $ip ) ) {
585
  return __( "Is blacklisted", "defender-security" );
586
  }
587
+
588
  $model = IP_Model::findOne( array(
589
  'ip' => $ip
590
  ) );
591
  if ( ! is_object( $model ) ) {
592
  return __( "Not banned", "defender-security" );
593
  }
594
+
595
  if ( $model->status == IP_Model::STATUS_BLOCKED ) {
596
  return __( "Banned", "defender-security" );
597
  } elseif ( $model->status == IP_Model::STATUS_NORMAL ) {
598
  return __( "Not banned", "defender-security" );
599
  }
600
  }
601
+
602
  /**
603
  * @return bool
604
  */
610
  $wpdb->get_var( "SHOW TABLES LIKE '$tableName2'" ) != $tableName2 ) {
611
  return false;
612
  }
613
+
614
  return true;
615
  }
616
+
617
+ /**
618
+ * @return bool
619
+ */
620
+ public static function downloadGeoIP() {
621
+ $url = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz";
622
+ if ( ! function_exists( 'download_url' ) ) {
623
+ require_once ABSPATH . 'wp-admin/includes/file.php';
624
+ }
625
+ $tmp = download_url( $url );
626
+ if ( ! is_wp_error( $tmp ) ) {
627
+ $phar = new \PharData( $tmp );
628
+ $defPath = Utils::instance()->getDefUploadDir();
629
+ $path = $defPath . DIRECTORY_SEPARATOR . 'maxmind';
630
+ if ( ! is_dir( $path ) ) {
631
+ mkdir( $path );
632
+ }
633
+ $phar->extractTo( $path, null, true );
634
+ $settings = Settings::instance();
635
+ $settings->geoIP_db = $path . DIRECTORY_SEPARATOR . $phar->current()->getFileName() . DIRECTORY_SEPARATOR . 'GeoLite2-Country.mmdb';
636
+ $settings->save();
637
+
638
+ return true;
639
+ }
640
+
641
+ return false;
642
+ }
643
  }
app/module/ip-lockout/component/logs-table.php CHANGED
@@ -23,8 +23,8 @@ class Logs_Table extends \WP_List_Table {
23
  ), $args ) );
24
 
25
  $date_format = 'm/d/Y';
26
- $this->from = Http_Helper::retrieve_get( 'date_from', date( $date_format, strtotime( 'today midnight', strtotime( '-14 days', current_time( 'timestamp' ) ) ) ) );
27
- $this->to = Http_Helper::retrieve_get( 'date_to', date( $date_format, current_time( 'timestamp' ) ) );
28
  }
29
 
30
  /**
@@ -74,16 +74,16 @@ class Logs_Table extends \WP_List_Table {
74
  )
75
  );
76
 
77
- if ( ( $filter = Http_Helper::retrieve_get( 'type', null ) ) != null ) {
78
  $params['type'] = $filter;
79
  }
80
- if ( ( $ip = Http_Helper::retrieve_get( 'ip_address', null ) ) != null ) {
81
  $params['ip'] = $ip;
82
  }
83
 
84
  $logs = Log_Model::findAll( $params,
85
- HTTP_Helper::retrieve_get( 'orderby', 'id' ),
86
- HTTP_Helper::retrieve_get( 'order', 'desc' ),
87
  $offset . ',' . $per_page
88
  );
89
 
@@ -179,14 +179,14 @@ class Logs_Table extends \WP_List_Table {
179
  </label>
180
  <select name="type">
181
  <option value=""><?php esc_html_e( "All", "defender-security" ) ?></option>
182
- <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_FAIL, \Hammer\Helper\HTTP_Helper::retrieve_get( 'filter' ) ) ?>
183
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_FAIL ?>">
184
  <?php esc_html_e( "Failed login attempts", "defender-security" ) ?></option>
185
- <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_LOCK, \Hammer\Helper\HTTP_Helper::retrieve_get( 'filter' ) ) ?>
186
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_LOCK ?>"><?php esc_html_e( "Login lockout", "defender-security" ) ?></option>
187
- <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::ERROR_404, \Hammer\Helper\HTTP_Helper::retrieve_get( 'filter' ) ) ?>
188
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::ERROR_404 ?>"><?php esc_html_e( "404 error", "defender-security" ) ?></option>
189
- <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::LOCKOUT_404, \Hammer\Helper\HTTP_Helper::retrieve_get( 'filter' ) ) ?>
190
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::LOCKOUT_404 ?>"><?php esc_html_e( "404 lockout", "defender-security" ) ?></option>
191
  </select>
192
  </div>
23
  ), $args ) );
24
 
25
  $date_format = 'm/d/Y';
26
+ $this->from = Http_Helper::retrieveGet( 'date_from', date( $date_format, strtotime( 'today midnight', strtotime( '-14 days', current_time( 'timestamp' ) ) ) ) );
27
+ $this->to = Http_Helper::retrieveGet( 'date_to', date( $date_format, current_time( 'timestamp' ) ) );
28
  }
29
 
30
  /**
74
  )
75
  );
76
 
77
+ if ( ( $filter = Http_Helper::retrieveGet( 'type', null ) ) != null ) {
78
  $params['type'] = $filter;
79
  }
80
+ if ( ( $ip = Http_Helper::retrieveGet( 'ip_address', null ) ) != null ) {
81
  $params['ip'] = $ip;
82
  }
83
 
84
  $logs = Log_Model::findAll( $params,
85
+ HTTP_Helper::retrieveGet( 'orderby', 'id' ),
86
+ HTTP_Helper::retrieveGet( 'order', 'desc' ),
87
  $offset . ',' . $per_page
88
  );
89
 
179
  </label>
180
  <select name="type">
181
  <option value=""><?php esc_html_e( "All", "defender-security" ) ?></option>
182
+ <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_FAIL, \Hammer\Helper\HTTP_Helper::retrieveGet( 'filter' ) ) ?>
183
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_FAIL ?>">
184
  <?php esc_html_e( "Failed login attempts", "defender-security" ) ?></option>
185
+ <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_LOCK, \Hammer\Helper\HTTP_Helper::retrieveGet( 'filter' ) ) ?>
186
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::AUTH_LOCK ?>"><?php esc_html_e( "Login lockout", "defender-security" ) ?></option>
187
+ <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::ERROR_404, \Hammer\Helper\HTTP_Helper::retrieveGet( 'filter' ) ) ?>
188
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::ERROR_404 ?>"><?php esc_html_e( "404 error", "defender-security" ) ?></option>
189
+ <option <?php selected( \WP_Defender\Module\IP_Lockout\Model\Log_Model::LOCKOUT_404, \Hammer\Helper\HTTP_Helper::retrieveGet( 'filter' ) ) ?>
190
  value="<?php echo \WP_Defender\Module\IP_Lockout\Model\Log_Model::LOCKOUT_404 ?>"><?php esc_html_e( "404 lockout", "defender-security" ) ?></option>
191
  </select>
192
  </div>
app/module/ip-lockout/component/notfound-listener.php ADDED
@@ -0,0 +1,175 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\IP_Lockout\Component;
7
+
8
+ use WP_Defender\Behavior\Utils;
9
+ use WP_Defender\Controller;
10
+ use WP_Defender\Module\IP_Lockout\Model\IP_Model;
11
+ use WP_Defender\Module\IP_Lockout\Model\Log_Model;
12
+ use WP_Defender\Module\IP_Lockout\Model\Settings;
13
+
14
+ class Notfound_Listener extends Controller {
15
+ public function __construct() {
16
+ if ( Settings::instance()->detect_404 ) {
17
+ $this->addAction( 'template_redirect', 'record404' );
18
+ }
19
+ }
20
+
21
+ public function record404() {
22
+ if ( ! is_404() ) {
23
+ return;
24
+ }
25
+
26
+ if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
27
+ //only track subscriber
28
+ return;
29
+ }
30
+
31
+ //now check if this from google
32
+ if ( Login_Protection_Api::isGoogleUA() && Login_Protection_Api::isGoogleIP( Utils::instance()->getUserIp() ) ) {
33
+ return;
34
+ }
35
+
36
+ //or bing
37
+ if ( Login_Protection_Api::isBingUA() && Login_Protection_Api::isBingIP( Utils::instance()->getUserIp() ) ) {
38
+ return;
39
+ }
40
+
41
+ $settings = Settings::instance();
42
+ if ( $settings->detect_404_logged == false && is_user_logged_in() ) {
43
+ return;
44
+ }
45
+
46
+ $uri = $_SERVER['REQUEST_URI'];
47
+
48
+ /**
49
+ * Priorities
50
+ * - Whitelist
51
+ * extensions
52
+ * files & folders
53
+ * - Blacklist
54
+ * extensions
55
+ * files & folders
56
+ * - attemps inside a window
57
+ */
58
+
59
+ $ext = pathinfo( $uri, PATHINFO_EXTENSION );
60
+ if ( in_array( '.' . $ext, $settings->getDetect404IgnoredFiletypes() ) ) {
61
+ //ext is whitelist, log and return
62
+ $this->log( $uri, Log_Model::ERROR_404_IGNORE, sprintf( __( "Request for file %s which doesn't exist", "defender-security" ), $uri ) );
63
+
64
+ return;
65
+ }
66
+ foreach ( $settings->get404Whitelist() as $pattern ) {
67
+ $pattern = preg_quote( $pattern, '/' );
68
+ if ( preg_match( '/' . $pattern . '$/', $uri ) ) {
69
+ //whitelisted, just log and return
70
+ return;
71
+ }
72
+ }
73
+ $model = IP_Model::init();
74
+ if ( in_array( '.' . $ext, $settings->getDetect404FiletypesBlacklist() ) ) {
75
+ //block it
76
+ $this->lock( $model, 'blacklist', $uri );
77
+ $this->log( $uri, Log_Model::LOCKOUT_404, sprintf( __( "Lockout occurred: Too many 404 requests for %s" ) ) );
78
+
79
+ return;
80
+ }
81
+
82
+ foreach ( $settings->getDetect404Blacklist() as $pattern ) {
83
+ $pattern = preg_quote( $pattern, '/' );
84
+ if ( preg_match( '/' . $pattern . '$/', $uri ) ) {
85
+ $this->lock( $model, 'blacklist', $uri );
86
+ $this->log( $uri, Log_Model::LOCKOUT_404, sprintf( __( "Lockout occurred: Too many 404 requests for %s" ) ) );
87
+
88
+ return;
89
+ }
90
+ }
91
+ $this->log( $uri, Log_Model::ERROR_404, sprintf( __( "Request for file %s which doesn't exist", "defender-security" ), $uri ) );
92
+ //now we need to count the attempt
93
+ $window = strtotime( '- ' . $settings->detect_404_timeframe . ' seconds', time() );
94
+ if ( $window < $model->lock_time ) {
95
+ $window = $model->lock_time;
96
+ }
97
+ $attempts = Log_Model::count( [
98
+ 'ip' => Utils::instance()->getUserIp(),
99
+ 'type' => Log_Model::ERROR_404,
100
+ 'date' => [ 'compare' => '>', 'value' => $window ]
101
+ ] );
102
+
103
+ if ( $attempts > $settings->detect_404_threshold ) {
104
+ //lock it
105
+ $this->lock( $model, $uri );
106
+ $this->log( $uri, Log_Model::LOCKOUT_404, sprintf( __( "Lockout occurred: Too many 404 requests for %s" ), $uri ) );
107
+ }
108
+ }
109
+
110
+ /**
111
+ * @param IP_Model $model
112
+ * @param $scenario
113
+ * @param $uri
114
+ */
115
+ private function lock( IP_Model $model, $scenario = 'normal', $uri = '' ) {
116
+ $settings = Settings::instance();
117
+ $model->status = IP_Model::STATUS_BLOCKED;
118
+ $model->lock_time = time();
119
+ if ( $scenario == 'blacklist' ) {
120
+ $model->release_time = strtotime( '+5 years', time() );
121
+ } else {
122
+ $model->release_time = strtotime( '+ ' . $settings->detect_404_lockout_duration . ' ' . $settings->detect_404_lockout_duration_unit );
123
+ }
124
+ $model->lockout_message = $settings->detect_404_lockout_message;
125
+ $model->save();
126
+ if ( $scenario == 'blacklist' ) {
127
+ $settings->addIpToList( $model->ip, 'blacklist' );
128
+ }
129
+ $model->lock_time = time();
130
+
131
+ do_action( 'wd_404_lockout', $model, $scenario );
132
+ $this->email( $model, $uri );
133
+ }
134
+
135
+ /**
136
+ * @param $uri
137
+ * @param $scenario
138
+ */
139
+ private function log( $uri, $scenario, $text ) {
140
+ $log = new Log_Model();
141
+ $log->ip = Utils::instance()->getUserIp();
142
+ $log->user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
143
+ $log->log = $text;
144
+ $log->date = time();
145
+ $log->type = $scenario;
146
+ $log->tried = $uri;
147
+ $log->save();
148
+ }
149
+
150
+ /**
151
+ * @param IP_Model $model
152
+ * @param $uri
153
+ */
154
+ private function email( IP_Model $model, $uri ) {
155
+ $settings = Settings::instance();
156
+ if ( ! Login_Protection_Api::maybeSendNotification( '404', $model, $settings ) ) {
157
+ return;
158
+ }
159
+ $isBlacklisted = $settings->isBlacklist( $model->ip );
160
+ foreach ( $settings->receipts as $item ) {
161
+ $content = $this->renderPartial( $isBlacklisted == true ? 'emails/404-ban' : 'emails/404-lockout', array(
162
+ 'admin' => $item['first_name'],
163
+ 'ip' => $model->ip,
164
+ 'uri' => $uri
165
+ ), false );
166
+ $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
167
+ $no_reply_email = apply_filters( 'wd_lockout_noreply_email', $no_reply_email );
168
+ $headers = array(
169
+ 'From: Defender <' . $no_reply_email . '>',
170
+ 'Content-Type: text/html; charset=UTF-8'
171
+ );
172
+ wp_mail( $item['email'], sprintf( __( "404 lockout alert for %s", "defender-security" ), network_site_url() ), $content, $headers );
173
+ }
174
+ }
175
+ }
app/module/ip-lockout/controller/main.php CHANGED
@@ -6,206 +6,109 @@
6
  namespace WP_Defender\Module\IP_Lockout\Controller;
7
 
8
  use Hammer\Helper\HTTP_Helper;
9
- use Hammer\Helper\Log_Helper;
10
- use Hammer\Helper\WP_Helper;
11
  use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Controller;
13
- use WP_Defender\Module\Audit\Component\Audit_API;
14
- use WP_Defender\Module\IP_Lockout\Behavior\IP_Lockout;
15
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
16
- use WP_Defender\Module\IP_Lockout\Component\Logs_Table;
17
  use WP_Defender\Module\IP_Lockout\Model\IP_Model;
18
- use WP_Defender\Module\IP_Lockout\Model\IP_Model_Legacy;
19
  use WP_Defender\Module\IP_Lockout\Model\Log_Model;
20
- use WP_Defender\Module\IP_Lockout\Model\Log_Model_Legacy;
21
  use WP_Defender\Module\IP_Lockout\Model\Settings;
22
- use WP_Defender\Vendor\Email_Search;
23
 
24
  class Main extends Controller {
25
  protected $slug = 'wdf-ip-lockout';
26
- public $layout = 'layout';
27
- public $email_search;
28
-
29
  /**
30
  * @return array
31
  */
32
  public function behaviors() {
33
  $behaviors = array(
34
- 'utils' => '\WP_Defender\Behavior\Utils',
 
 
35
  );
36
  if ( wp_defender()->isFree == false ) {
37
  $behaviors['pro'] = '\WP_Defender\Module\IP_Lockout\Behavior\Pro\Reporting';
38
  }
39
-
40
  return $behaviors;
41
  }
42
-
43
  public function __construct() {
44
  $this->maybeLockouts();
45
-
46
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
47
- $this->add_action( 'network_admin_menu', 'adminMenu' );
48
  } else {
49
- $this->add_action( 'admin_menu', 'adminMenu' );
50
  }
51
-
52
  if ( $this->isInPage() || $this->isDashboard() ) {
53
- $this->add_action( 'defender_enqueue_assets', 'scripts', 11 );
54
  }
55
-
56
  $this->maybeExport();
57
-
58
- $this->add_ajax_action( 'saveLockoutSettings', 'saveLockoutSettings' );
59
- $this->add_ajax_action( 'wd_import_ips', 'importBWIPs' );
60
- $this->add_ajax_action( 'lockoutLoadLogs', 'lockoutLoadLogs' );
61
- $this->add_ajax_action( 'lockoutIPAction', 'lockoutIPAction' );
62
- $this->add_ajax_action( 'lockoutEmptyLogs', 'lockoutEmptyLogs' );
63
- $this->add_ajax_action( 'lockoutSummaryData', 'lockoutSummaryData' );
64
- $this->add_ajax_action( 'migrateData', 'movingDataToTable' );
65
- $this->add_ajax_action( 'lockoutExportAsCsv', 'exportAsCsv' );
66
- $this->add_ajax_action( 'bulkAction', 'bulkAction' );
67
- $this->add_ajax_action( 'downloadGeoIPDB', 'downloadGeoIPDB' );
68
-
69
- $this->handleIpAction();
70
- $this->handleUserSearch();
71
- }
72
-
73
- public function bulkAction() {
74
- if ( ! $this->checkPermission() ) {
75
  return;
76
  }
77
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'bulkAction' ) ) {
78
- return;
 
 
 
 
79
  }
80
-
81
- $ids = HTTP_Helper::retrieve_post( 'ids' );
82
- $ids = explode( ',', $ids );
83
- $type = HTTP_Helper::retrieve_post( 'type' );
84
- if ( count( $ids ) && $type ) {
85
- $settings = Settings::instance();
86
- foreach ( $ids as $id ) {
87
- $model = Log_Model::findByID( $id );
88
- switch ( $type ) {
89
- case 'whitelist':
90
- $settings->addIpToList( $model->ip, 'whitelist' );
91
- break;
92
- case 'ban':
93
- $settings->addIpToList( $model->ip, 'blacklist' );
94
- break;
95
- case 'delete':
96
- $model->delete();
97
- break;
98
- default:
99
- //param not from the button on frontend, log it
100
- error_log( sprintf( 'Unexpected value %s from IP %s', $type, Utils::instance()->getUserIp() ) );
101
- break;
102
- }
103
- }
104
-
105
- wp_send_json_success( array(
106
- 'reload' => 1,
107
- 'message' => ''
108
- ) );
109
  }
110
- }
111
-
112
- public function downloadGeoIPDB() {
113
- if ( ! $this->checkPermission() ) {
114
  return;
115
  }
116
-
117
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'downloadGeoIPDB' ) ) {
 
 
118
  return;
119
  }
120
-
121
- $url = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz";
122
- $tmp = download_url( $url );
123
- if ( ! is_wp_error( $tmp ) ) {
124
- $phar = new \PharData( $tmp );
125
- $defPath = Utils::instance()->getDefUploadDir();
126
- $path = $defPath . DIRECTORY_SEPARATOR . 'maxmind';
127
- if ( ! is_dir( $path ) ) {
128
- mkdir( $path );
129
- }
130
- $phar->extractTo( $path, null, true );
131
- $settings = Settings::instance();
132
- $settings->geoIP_db = $path . DIRECTORY_SEPARATOR . $phar->current()->getFileName() . DIRECTORY_SEPARATOR . 'GeoLite2-Country.mmdb';
133
- $settings->save();
134
- wp_send_json_success( array(
135
- 'message' => __( "Database downloaded", "defender-security" )
136
- ) );
137
- }
138
  }
139
-
140
- public function lockoutSummaryData() {
141
- if ( ! $this->checkPermission() ) {
142
- return;
143
- }
144
-
145
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( 'nonce' ), 'lockoutSummaryData' ) ) {
146
- return;
147
- }
148
-
149
- $lockouts = Log_Model::findAll( array(
150
- 'type' => array(
151
- Log_Model::LOCKOUT_404,
152
- Log_Model::AUTH_LOCK
153
- ),
154
- 'date' => array(
155
- 'compare' => '>=',
156
- 'value' => strtotime( '-30 days', current_time( 'timestamp' ) )
157
- )
158
- ), 'id', 'DESC' );
159
-
160
- if ( count( $lockouts ) == 0 ) {
161
- $data = array(
162
- 'lastLockout' => __( "Never", "defender-security" ),
163
- 'lockoutToday' => 0,
164
- 'lockoutThisMonth' => 0,
165
- 'loginLockoutThisWeek' => 0,
166
- 'lockout404ThisWeek' => 0,
167
- );
168
-
169
- wp_send_json_success( $data );
170
- }
171
-
172
- //init params
173
- $lastLockout = '';
174
- $lockoutToday = 0;
175
- $lockoutThisMonth = count( $lockouts );
176
- $loginLockoutThisWeek = 0;
177
- $lockout404ThisWeek = 0;
178
- //time
179
- $todayMidnight = strtotime( '-24 hours', current_time( 'timestamp' ) );
180
- $firstThisWeek = strtotime( '-7 days', current_time( 'timestamp' ) );
181
- foreach ( $lockouts as $k => $log ) {
182
- //the other as DESC, so first will be last lockout
183
- if ( $k == 0 ) {
184
- $lastLockout = $this->formatDateTime( date( 'Y-m-d H:i:s', $log->date ) );
185
- }
186
-
187
- if ( $log->date > $todayMidnight ) {
188
- $lockoutToday ++;
189
- }
190
-
191
- if ( $log->type == Log_Model::AUTH_LOCK && $log->date > $firstThisWeek ) {
192
- $loginLockoutThisWeek ++;
193
- } elseif ( $log->type == Log_Model::LOCKOUT_404 && $log->date > $firstThisWeek ) {
194
- $lockout404ThisWeek ++;
195
- }
196
- }
197
-
198
- $data = array(
199
- 'lastLockout' => $lastLockout,
200
- 'lockoutToday' => $lockoutToday,
201
- 'lockoutThisMonth' => $lockoutThisMonth,
202
- 'loginLockoutThisWeek' => $loginLockoutThisWeek,
203
- 'lockout404ThisWeek' => $lockout404ThisWeek,
204
- );
205
-
206
- wp_send_json_success( $data );
207
  }
208
-
 
 
 
209
  public function exportAsCsv() {
210
  if ( ! $this->checkPermission() ) {
211
  return;
@@ -230,7 +133,7 @@ class Main extends Controller {
230
  );
231
  fputcsv( $fp, $item );
232
  }
233
-
234
  $filename = 'wdf-lockout-logs-export-' . date( 'ymdHis' ) . '.csv';
235
  fseek( $fp, 0 );
236
  header( 'Content-Type: text/csv' );
@@ -239,83 +142,17 @@ class Main extends Controller {
239
  fpassthru( $fp );
240
  exit();
241
  }
242
-
243
- public function lockoutEmptyLogs() {
244
- if ( ! $this->checkPermission() ) {
245
- return;
246
- }
247
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( 'nonce' ), 'lockoutEmptyLogs' ) ) {
248
- return;
249
- }
250
-
251
- $perPage = 500;
252
- $count = Log_Model::deleteAll( array(), '0,' . $perPage );
253
- if ( $count == 0 ) {
254
- wp_send_json_success( array(
255
- 'message' => __( "Your logs have been successfully deleted.", "defender-security" )
256
- ) );
257
- }
258
-
259
- wp_send_json_error( array() );
260
- }
261
-
262
- /**
263
- * Ajax to add an IP to blacklist or whitelist
264
- */
265
- public function lockoutIPAction() {
266
- if ( ! $this->checkPermission() ) {
267
- return;
268
- }
269
-
270
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( 'nonce' ), 'lockoutIPAction' ) ) {
271
- return;
272
- }
273
-
274
- $id = HTTP_Helper::retrieve_post( 'id' );
275
- $ip = HTTP_Helper::retrieve_post( 'ip' );
276
- $type = HTTP_Helper::retrieve_post( 'type' );
277
- if ( $id ) {
278
- $log = Log_Model::findByID( $id );
279
- $ip = $log->ip;
280
- }
281
- if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
282
- if ( $type == 'unwhitelist' || $type == 'unblacklist' ) {
283
- $type = substr( $type, 2 );
284
- Settings::instance()->removeIpFromList( $ip, $type );
285
- } else {
286
- Settings::instance()->addIpToList( $ip, $type );
287
- }
288
-
289
- if ( isset( $log ) && is_object( $log ) ) {
290
- wp_send_json_success( array(
291
- 'message' => Login_Protection_Api::getLogsActionsText( $log )
292
- ) );
293
- } else {
294
- $item = new \StdClass();
295
- $item->ip = $ip;
296
- $item->id = null;
297
- wp_send_json_success( array(
298
- 'message' => sprintf( __( "IP %s has been added to your blacklist. You can control your blacklist in <a href=\"%s\">IP Lockouts.</a>", "defender-security" ), $ip, network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blacklist' ) ),
299
- 'buttons' => Login_Protection_Api::getLogsActionsText( $item )
300
- ) );
301
- }
302
- } else {
303
- wp_send_json_error( array(
304
- 'message' => __( "No record found", "defender-security" )
305
- ) );
306
- }
307
- }
308
-
309
  /**
310
  * Determine if an ip get lockout or not
311
  */
312
  public function maybeLockouts() {
313
- do_action('wd_before_lockout');
314
  $settings = Settings::instance();
315
- $isTest = HTTP_Helper::retrieve_get( 'def-lockout-demo', false ) == 1;
316
  if ( $isTest ) {
317
  $message = null;
318
- $type = HTTP_Helper::retrieve_get( 'type' );
319
  switch ( $type ) {
320
  case 'login':
321
  $message = $settings->login_protection_lockout_message;
@@ -334,8 +171,13 @@ class Main extends Controller {
334
  ) );
335
  die;
336
  }
337
-
338
- $ip = $this->getUserIp();
 
 
 
 
 
339
  if ( $settings->isWhitelist( $ip ) ) {
340
  return;
341
  } elseif ( $settings->isBlacklist( $ip ) ) {
@@ -363,7 +205,7 @@ class Main extends Controller {
363
  //if current user can logged in, and no blacklisted we don't need to check the ip
364
  return;
365
  }
366
-
367
  $model = IP_Model::findOne( array(
368
  'ip' => $ip
369
  ) );
@@ -380,121 +222,7 @@ class Main extends Controller {
380
  }
381
  }
382
  }
383
-
384
- /**
385
- * Prepare for Email_Search component
386
- */
387
- private function handleUserSearch() {
388
- $view = HTTP_Helper::retrieve_get( 'view' );
389
- $id = isset( $_REQUEST['id'] ) ? $_REQUEST['id'] : null;
390
- $this->email_search = new Email_Search();
391
- $this->email_search->settings = Settings::instance();
392
-
393
- if ( $view == 'notification' && $this->isInPage() || ( defined( 'DOING_AJAX' ) && $id == 'lockout-notification' ) ) {
394
- $this->email_search->eId = 'lockout-notification';
395
- $this->email_search->add_hooks();
396
- } elseif ( $view == 'reporting' || ( defined( 'DOING_AJAX' ) && $id == 'lockout-report' ) ) {
397
- $this->email_search->eId = 'lockout-report';
398
- $this->email_search->attribute = 'report_receipts';
399
- $this->email_search->add_hooks();
400
- }
401
- }
402
-
403
- public function lockoutLoadLogs() {
404
- if ( ! $this->checkPermission() ) {
405
- return;
406
- }
407
-
408
- $table = new Logs_Table();
409
- $table->prepare_items();
410
- ob_start();
411
- $table->display();
412
- $content = ob_get_clean();
413
- wp_send_json_success( array(
414
- 'html' => $content
415
- ) );
416
- }
417
-
418
- /**
419
- * Handle the logic here
420
- */
421
- private function handleIpAction() {
422
- if ( get_site_option( 'defenderLockoutNeedUpdateLog' ) == 1 ) {
423
- //we are migratng, so no record
424
- return;
425
- }
426
-
427
- if ( ! Login_Protection_Api::checkIfTableExists() ) {
428
- //no table logs, omething happen
429
- return;
430
- }
431
-
432
- $ip = $this->getUserIp();
433
- $settings = Settings::instance();
434
- if ( $settings->report && $this->hasMethod( 'lockoutReportCron' ) ) {
435
- //report
436
- $this->add_action( 'lockoutReportCron', 'lockoutReportCron' );
437
- }
438
-
439
- //cron for cleanup
440
- $nextCleanup = wp_next_scheduled( 'cleanUpOldLog' );
441
- if ( $nextCleanup === false || $nextCleanup > strtotime( '+90 minutes' ) ) {
442
- wp_clear_scheduled_hook( 'cleanUpOldLog' );
443
- wp_schedule_event( time(), 'hourly', 'cleanUpOldLog' );
444
- }
445
-
446
- $this->add_action( 'cleanUpOldLog', 'cleanUpOldLog' );
447
-
448
- if ( $settings->isWhitelist( $ip ) ) {
449
- return;
450
- }
451
-
452
- $arr = apply_filters( 'ip_lockout_default_whitelist_ip', array(
453
- '192.241.148.185',
454
- '104.236.132.222',
455
- '192.241.140.159',
456
- '192.241.228.89',
457
- '198.199.88.192',
458
- '54.197.28.242',
459
- '54.221.174.186',
460
- '54.236.233.244',
461
- '127.0.0.1',
462
- array_key_exists( 'SERVER_ADDR', $_SERVER ) ? $_SERVER['SERVER_ADDR'] : ( isset( $_SERVER['LOCAL_ADDR'] ) ? $_SERVER['LOCAL_ADDR'] : null )
463
- ) );
464
-
465
- if ( in_array( $ip, $arr ) ) {
466
- return;
467
- }
468
- //now check if this from google
469
- if ( Login_Protection_Api::isGoogleUA() && Login_Protection_Api::isGoogleIP( $ip ) ) {
470
- return;
471
- }
472
-
473
- //or bing
474
- if ( Login_Protection_Api::isBingUA() && Login_Protection_Api::isBingIP( $ip ) ) {
475
- return;
476
- }
477
-
478
- if ( $settings->login_protection ) {
479
- $this->add_action( 'wp_login_failed', 'recordFailLogin', 9999 );
480
- $this->add_filter( 'authenticate', 'showAttemptLeft', 9999, 3 );
481
- $this->add_action( 'wp_login', 'clearAttemptStats', 10, 2 );
482
- }
483
-
484
- if ( $settings->detect_404 ) {
485
- $this->add_action( 'template_redirect', 'record404' );
486
- }
487
-
488
- $this->add_action( 'wd_lockout_trigger', 'updateIpStats' );
489
- //sending email
490
- if ( $settings->login_lockout_notification ) {
491
- $this->add_action( 'wd_login_lockout', 'lockoutLoginNotification', 10, 3 );
492
- }
493
- if ( $settings->ip_lockout_notification ) {
494
- $this->add_action( 'wd_404_lockout', 'lockout404Notification', 10, 3 );
495
- }
496
- }
497
-
498
  /**
499
  * cron for delete old log
500
  */
@@ -507,60 +235,7 @@ class Main extends Controller {
507
  ),
508
  ), '0,1000' );
509
  }
510
-
511
- /**
512
- * sending email when any lockout triggerd
513
- *
514
- * @param $model
515
- * @param $uri
516
- */
517
- public function lockout404Notification( $model, $uri, $isBlacklisted ) {
518
- $settings = Settings::instance();
519
- if ( ! Login_Protection_Api::maybeSendNotification( '404', $model, $settings ) ) {
520
- return;
521
- }
522
- foreach ( $settings->receipts as $item ) {
523
- $content = $this->renderPartial( $isBlacklisted == true ? 'emails/404-ban' : 'emails/404-lockout', array(
524
- 'admin' => $item['first_name'],
525
- 'ip' => $model->ip,
526
- 'uri' => $uri
527
- ), false );
528
- $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
529
- $no_reply_email = apply_filters( 'wd_lockout_noreply_email', $no_reply_email );
530
- $headers = array(
531
- 'From: Defender <' . $no_reply_email . '>',
532
- 'Content-Type: text/html; charset=UTF-8'
533
- );
534
- wp_mail( $item['email'], sprintf( __( "404 lockout alert for %s", "defender-security" ), network_site_url() ), $content, $headers );
535
- }
536
- }
537
-
538
- /**
539
- * @param IP_Model $model
540
- */
541
- public function lockoutLoginNotification( IP_Model $model, $force, $blacklist ) {
542
- $settings = Settings::instance();
543
-
544
- if ( ! Login_Protection_Api::maybeSendNotification( 'login', $model, $settings ) ) {
545
- return;
546
- }
547
-
548
- foreach ( $settings->receipts as $item ) {
549
- $view = ( $force && $blacklist ) ? 'emails/login-username-ban' : 'emails/login-lockout';
550
- $content = $this->renderPartial( $view, array(
551
- 'admin' => $item['first_name'],
552
- 'ip' => $model->ip,
553
- ), false );
554
- $no_reply_email = "noreply@" . parse_url( get_site_url(), PHP_URL_HOST );
555
- $no_reply_email = apply_filters( 'wd_lockout_noreply_email', $no_reply_email );
556
- $headers = array(
557
- 'From: Defender <' . $no_reply_email . '>',
558
- 'Content-Type: text/html; charset=UTF-8'
559
- );
560
- wp_mail( $item['email'], sprintf( __( "Login lockout alert for %s", "defender-security" ), network_site_url() ), $content, $headers );
561
- }
562
- }
563
-
564
  /**
565
  * After each log recorded, we will check if the threshold is met for a lockout
566
  *
@@ -573,193 +248,17 @@ class Main extends Controller {
573
  Login_Protection_Api::maybe404Lock( $log );
574
  }
575
  }
576
-
577
- /**
578
- * Listen to 404 request and record it
579
- */
580
- public function record404() {
581
- if ( is_404() ) {
582
- $settings = Settings::instance();
583
- if ( is_user_logged_in() && current_user_can( 'edit_posts' ) ) {
584
- //we wont track 404 error if user can login and not subscriber
585
- return;
586
- }
587
-
588
- if ( $settings->detect_404_logged == false && is_user_logged_in() ) {
589
- return;
590
- }
591
-
592
- $uri = $_SERVER['REQUEST_URI'];
593
- $absUrl = parse_url( get_site_url(), PHP_URL_PATH );
594
- if ( strpos( $uri, $absUrl ) === 0 ) {
595
- $uri = substr( $uri, strlen( $absUrl ) );
596
- }
597
- $uri = rtrim( $uri, '/' );
598
- foreach ( $settings->get404Whitelist() as $whitelistedUrl ) {
599
- if ( substr( $whitelistedUrl, - 1 ) == '*' ) {
600
- $whitelistedUrl = substr( $whitelistedUrl, 0, - 1 );
601
- if ( strpos( $uri, $whitelistedUrl ) === 0 ) {
602
- return true;
603
- }
604
- } elseif ( pathinfo( $whitelistedUrl, PATHINFO_FILENAME ) == '*' ) {
605
- //get the ext
606
- $ext = pathinfo( $whitelistedUrl, PATHINFO_EXTENSION );
607
- $uriExt = pathinfo( $uri, PATHINFO_EXTENSION );
608
- $whitelistPath = pathinfo( $whitelistedUrl, PATHINFO_DIRNAME );
609
- $uriPath = pathinfo( $uri, PATHINFO_DIRNAME );
610
- if ( $ext == $uriExt && $whitelistPath == $uriPath ) {
611
- return true;
612
- }
613
- } elseif ( $uri == $whitelistedUrl ) {
614
- return true;
615
- }
616
- }
617
-
618
- $ext = pathinfo( $uri, PATHINFO_EXTENSION );
619
- $ext = trim( $ext );
620
- $model = new Log_Model();
621
- $model->ip = $this->getUserIp();
622
- $model->user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
623
- $model->log = esc_url( $uri );
624
- $model->date = time();
625
- if ( strlen( $ext ) > 0 && in_array( $ext, $settings->get404Ignorelist() ) ) {
626
- $model->type = Log_Model::ERROR_404_IGNORE;
627
- } else {
628
- $model->type = '404_error';
629
- }
630
- $model->save();
631
-
632
- //need to check if this is css,js or images 404 from missig link from a page
633
- $ref = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : "";
634
- if ( $ref && parse_url( $ref, PHP_URL_SCHEME ) . '://' . parse_url( $ref, PHP_URL_HOST ) == site_url() ) {
635
- //the only variable we allow is ver, bydefault of wordpress
636
- $args = parse_url( $uri, PHP_URL_QUERY );
637
- if ( ! empty( $args ) ) {
638
- //validate it
639
- if ( isset( $args['ver'] ) && is_numeric( $args['ver'] ) ) {
640
- unset( $args['ver'] );
641
- }
642
- }
643
- if ( count( $args ) == 0 ) {
644
- //check the extension is js, css, or image type
645
- $exts = apply_filters( 'wd_allow_ref_extensions', array(
646
- 'js',
647
- 'css',
648
- 'jpg',
649
- 'png',
650
- 'gif'
651
- ) );
652
- $ext = pathinfo( $uri, PATHINFO_EXTENSION );
653
- $ext = strtolower( $ext );
654
- if ( in_array( $ext, $exts ) ) {
655
- //log but no lock
656
- return;
657
- }
658
- }
659
- }
660
-
661
- do_action( 'wd_lockout_trigger', $model );
662
- }
663
- }
664
-
665
- /**
666
- * @param $username
667
- */
668
- public function recordFailLogin( $username ) {
669
- $model = new Log_Model();
670
- $model->ip = $this->getUserIp();
671
- $model->user_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : null;
672
- $model->log = sprintf( esc_html__( "Failed login attempt with username %s", "defender-security" ), $username );
673
- $model->date = time();
674
- $model->type = 'auth_fail';
675
- $model->tried = $username;
676
- $model->save();
677
-
678
- $settings = Settings::instance();
679
- $username = strtolower( $username );
680
- $unameBlacklisted = $settings->getUsernameBlacklist();
681
- if ( in_array( $username, $unameBlacklisted ) ) {
682
- Login_Protection_Api::maybeLock( $model, true, true );
683
-
684
- return;
685
- }
686
-
687
- do_action( 'wd_lockout_trigger', $model );
688
- }
689
-
690
- /**
691
- * When user get login successfully, we will reset the attempt count
692
- */
693
- public function clearAttemptStats( $user_login, $user = '' ) {
694
- $ip = $this->getUserIp();
695
- $model = IP_Model::findOne( array(
696
- 'ip' => $ip
697
- ) );
698
- if ( is_object( $model ) ) {
699
- $model->attempt = 1;
700
- $model->lock_time = current_time( 'timestamp' );
701
- $model->save();
702
- }
703
- }
704
-
705
- /**
706
- * @param $user
707
- *
708
- * @return mixed
709
- */
710
- public function showAttemptLeft( $user, $username, $password ) {
711
- if ( is_wp_error( $user ) && $_SERVER['REQUEST_METHOD'] == 'POST' && ! in_array( $user->get_error_code(), array(
712
- 'empty_username',
713
- 'empty_password'
714
- ) )
715
- ) {
716
- $ip = $this->getUserIp();
717
- $model = IP_Model::findOne( array(
718
- 'ip' => $ip
719
- ) );
720
- if ( is_object( $model ) ) {
721
- if ( $model->is_locked() ) {
722
- //redirect
723
- wp_redirect( get_site_url() );
724
- exit;
725
- }
726
- $settings = Settings::instance();
727
- $attempt = $model->attempt + 1;
728
- if ( $settings->login_protection_login_attempt - $attempt == 0 ) {
729
- $user->add( 'def_warning', $settings->login_protection_lockout_message );
730
- } else {
731
- $unameBlacklisted = $settings->getUsernameBlacklist();
732
- if ( in_array( $username, $unameBlacklisted ) ) {
733
- $user->add( 'def_warning', esc_html__( "You have been locked out by the administrator for attempting to login with a banned username", "defender-security" ) );
734
- } else {
735
- $user->add( 'def_warning', sprintf( esc_html__( "%d login attempts remaining", "defender-security" ), $settings->login_protection_login_attempt - $attempt ) );
736
- }
737
- }
738
- } else {
739
- $settings = Settings::instance();
740
- $unameBlacklisted = $settings->getUsernameBlacklist();
741
- if ( in_array( $username, $unameBlacklisted ) ) {
742
- $user->add( 'def_warning', esc_html__( "You have been locked out by the administrator for attempting to login with a banned username", "defender-security" ) );
743
- } else {
744
- //becase authenticate hook fire before wp_login_fail, so at this state, we dont have any data, we will decrease by one
745
- $user->add( 'def_warning', sprintf( esc_html__( "%d login attempts remaining", "defender-security" ), $settings->login_protection_login_attempt - 1 ) );
746
- }
747
- }
748
- }
749
-
750
- return $user;
751
- }
752
-
753
  /**
754
  * listener to process export Ips request
755
  */
756
  public function maybeExport() {
757
- if ( HTTP_Helper::retrieve_get( 'page' ) == 'wdf-ip-lockout' && HTTP_Helper::retrieve_get( 'view' ) == 'export' ) {
758
  if ( ! $this->checkPermission() ) {
759
  return;
760
  }
761
-
762
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_get( '_wpnonce' ), 'defipexport' ) ) {
763
  return;
764
  }
765
  $setting = Settings::instance();
@@ -789,344 +288,187 @@ class Main extends Controller {
789
  exit();
790
  }
791
  }
792
-
793
- /**
794
- * Ajax for saving settings
795
- */
796
- public function saveLockoutSettings() {
797
- if ( ! $this->checkPermission() ) {
798
- return;
799
- }
800
-
801
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'saveLockoutSettings' ) ) {
802
- return;
803
- }
804
-
805
- $settings = Settings::instance();
806
- $lastSettings = clone $settings;
807
- $data = wp_unslash( $_POST );
808
- //check and sanitize before save
809
- $textarea = array(
810
- 'username_blacklist',
811
- 'login_protection_lockout_message',
812
- 'detect_404_ignored_filetypes',
813
- 'detect_404_lockout_message',
814
- 'detect_404_whitelist',
815
- 'ip_lockout_message',
816
- 'ip_blacklist',
817
- 'ip_whitelist',
818
- 'ip_lockout_message'
819
- );
820
- foreach ( $data as $k => $v ) {
821
- if ( in_array( $k, $textarea ) ) {
822
- $data[ $k ] = wp_kses_post( $v );
823
- } elseif ( in_array( $k, array( 'receipts', 'report_receipts' ) ) ) {
824
- $v = array_values( $v );
825
- foreach ( $v as &$item ) {
826
- $item = array_map( 'wp_strip_all_tags', $item );
827
- }
828
- $data[ $k ] = $v;
829
- } else {
830
- if ( is_array( $v ) ) {
831
- $v = implode( ',', $v );
832
- }
833
- $data[ $k ] = sanitize_text_field( $v );
834
- }
835
- }
836
- $settings->import( $data );
837
- if ( $settings->validate() ) {
838
- $settings->save();
839
- $faultIps = WP_Helper::getArrayCache()->get( 'faultIps', array() );
840
- $isBLSelf = WP_Helper::getArrayCache()->get( 'isBlacklistSelf', false );
841
- if ( $faultIps || $isBLSelf ) {
842
- $res = array(
843
- 'message' => sprintf( __( "Your settings have been updated, however some IPs were removed because invalid format, or you blacklist yourself", "defender-security" ), implode( ',', $faultIps ) ),
844
- 'reload' => 1
845
- );
846
- } else {
847
- $res = array( 'message' => __( "Your settings have been updated.", "defender-security" ), );
848
- }
849
- if ( ( $lastSettings->login_protection != $settings->login_protection )
850
- || ( $lastSettings->detect_404 != $settings->detect_404 )
851
- ) {
852
- if ( isset( $data['login_protection'] ) ) {
853
- if ( $data['login_protection'] == 1 ) {
854
- $status = __( "Login Protection has been activated.", "defender-security" );
855
- } else {
856
- $status = __( "Login Protection has been deactivated.", "defender-security" );
857
- }
858
- }
859
- if ( isset( $data['detect_404'] ) ) {
860
- if ( $data['detect_404'] == 1 ) {
861
- $status = __( "404 Detection has been activated.", "defender-security" );
862
- } else {
863
- $status = __( "404 Detection has been deactivated.", "defender-security" );
864
- }
865
- }
866
- //mean enabled or disabled, reload
867
- $res['reload'] = 1;
868
- if ( isset( $status ) && strlen( $status ) ) {
869
- $res['message'] = $status;
870
- }
871
- }
872
- if ( $this->hasMethod( 'scheduleReport' ) ) {
873
- $this->scheduleReport();
874
- }
875
- Utils::instance()->submitStatsToDev();
876
- $res['reload'] = 1;
877
- wp_send_json_success( $res );
878
- } else {
879
- wp_send_json_error( array(
880
- 'message' => implode( '<br/>', $settings->getErrors() )
881
- ) );
882
- }
883
- }
884
-
885
  /**
886
  * Add submit admin page
887
  */
888
  public function adminMenu() {
889
  $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
890
  $action = "actionIndex";
891
- if ( get_site_option( 'defenderLockoutNeedUpdateLog' ) == 1 ) {
892
- $action = "actionMigration";
893
- }
894
  add_submenu_page( 'wp-defender', esc_html__( "IP Lockouts", "defender-security" ), esc_html__( "IP Lockouts", "defender-security" ), $cap, $this->slug, array(
895
  &$this,
896
  $action
897
  ) );
898
  }
899
-
900
- /**
901
- * Ajax function for handling importing IPs
902
- */
903
- public function importBWIPs() {
904
- if ( ! $this->checkPermission() ) {
905
- return;
906
- }
907
-
908
- $id = HTTP_Helper::retrieve_post( 'file' );
909
- if ( ! is_object( get_post( $id ) ) ) {
910
- wp_send_json_error( array(
911
- 'message' => __( "Your file is invalid!", "defender-security" )
912
- ) );
913
- }
914
- $file = get_attached_file( $id );
915
- if ( ! is_file( $file ) ) {
916
- wp_send_json_error( array(
917
- 'message' => __( "Your file is invalid!", "defender-security" )
918
- ) );
919
- }
920
-
921
- if ( ! ( $data = Login_Protection_Api::verifyImportFile( $file ) ) ) {
922
- wp_send_json_error( array(
923
- 'message' => __( "Your file content is invalid!", "defender-security" )
924
- ) );
925
- }
926
- $settings = Settings::instance();
927
- //all good, start to import
928
- foreach ( $data as $line ) {
929
- $settings->addIpToList( $line[0], $line[1] );
930
- }
931
- wp_send_json_success( array(
932
- 'message' => __( "Your whitelist/blacklist has been successfully imported.", "defender-security" ),
933
- 'reload' => 1
934
- ) );
935
- }
936
-
937
  /**
938
  * queue scripts
939
  */
940
  public function scripts() {
941
  if ( $this->isInPage() ) {
942
- wp_enqueue_script( 'wpmudev-sui' );
943
  wp_enqueue_style( 'wpmudev-sui' );
944
- if ( HTTP_Helper::retrieve_get( 'view' ) == 'blacklist' ) {
945
- remove_filter( 'admin_body_class', array( 'WDEV_Plugin_Ui', 'admin_body_class' ) );
946
- }
947
- wp_enqueue_script( 'defender' );
948
  wp_enqueue_style( 'defender' );
949
- wp_enqueue_script( 'iplockout', wp_defender()->getPluginUrl() . 'app/module/ip-lockout/js/script.js' );
950
- wp_enqueue_script( 'def-momentjs', wp_defender()->getPluginUrl() . 'assets/js/moment/moment.min.js' );
951
- wp_enqueue_style( 'def-daterangepicker', wp_defender()->getPluginUrl() . 'assets/js/daterangepicker/daterangepicker.css' );
952
- wp_enqueue_script( 'def-daterangepicker', wp_defender()->getPluginUrl() . 'assets/js/daterangepicker/daterangepicker.js' );
953
- } else {
954
- wp_enqueue_script( 'iplockout', wp_defender()->getPluginUrl() . 'app/module/ip-lockout/js/script.js' );
955
- }
956
- }
957
-
958
- /**
959
- * Internal route
960
- */
961
- public function actionIndex() {
962
- $view = HTTP_Helper::retrieve_get( 'view' );
963
- switch ( $view ) {
964
- case 'login':
965
- default:
966
- $this->_renderLoginProtection();
967
- break;
968
- case '404':
969
- $this->_render404Protection();
970
- break;
971
- case 'blacklist':
972
- $this->_renderBlacklist();
973
- break;
974
- case 'logs':
975
- $this->_renderLogs();
976
- break;
977
- case 'notification':
978
- $this->_renderNotification();
979
- break;
980
- case 'settings':
981
- $this->_renderSettings();
982
- break;
983
- case 'reporting':
984
- $this->_renderReport();
985
- break;
986
  }
987
  }
988
-
989
  /**
990
- * Show the updating screen
 
 
991
  */
992
- public function actionMigration() {
993
- $this->layout = null;
994
- $this->render( 'migration' );
995
- }
996
-
997
- private function _renderSettings() {
998
- $this->render( 'settings', array(
999
- 'settings' => Settings::instance()
1000
- ) );
1001
- }
1002
-
1003
- private function _renderLoginProtection() {
1004
- if ( Settings::instance()->login_protection ) {
1005
- $this->render( 'login-lockouts/enabled', array(
1006
- 'settings' => Settings::instance()
1007
- ) );
1008
  } else {
1009
- $this->render( 'login-lockouts/disabled' );
1010
  }
1011
- }
1012
-
1013
- private function _render404Protection() {
1014
- if ( Settings::instance()->detect_404 ) {
1015
- $this->render( 'detect-404/enabled', array(
1016
- 'settings' => Settings::instance()
1017
- ) );
1018
  } else {
1019
- $this->render( 'detect-404/disabled' );
1020
- }
1021
- }
1022
-
1023
- private function _renderBlacklist() {
1024
- wp_enqueue_media();
1025
- $this->render( 'blacklist/enabled', array(
1026
- 'settings' => Settings::instance()
1027
- ) );
1028
- }
1029
-
1030
- private function _renderLogs() {
1031
- $this->render( 'logging/enabled' );
1032
- }
1033
-
1034
- private function _renderNotification() {
1035
- $this->email_search->add_script();
1036
- $this->render( 'notification/enabled', array(
1037
- 'settings' => Settings::instance(),
1038
- 'email_search' => $this->email_search
1039
- ) );
1040
- }
1041
-
1042
- private function _renderReport() {
1043
- $this->email_search->add_script();
1044
- $view = wp_defender()->isFree ? 'notification/report-free' : 'notification/report';
1045
- $this->render( $view, array(
1046
- 'settings' => Settings::instance(),
1047
- 'email_search' => $this->email_search
1048
- ) );
1049
- }
1050
-
1051
- public function movingDataToTable() {
1052
- if ( ! $this->checkPermission() ) {
1053
- return;
1054
- }
1055
-
1056
- $totalItems = get_site_option( 'defenderLogsTotal' );
1057
- $resetFlag = get_site_option( 'defenderMigrateNeedReset' );
1058
- if ( $totalItems !== false && $resetFlag === false ) {
1059
- //reset it
1060
- delete_site_option( 'defenderLogsTotal' );
1061
- delete_site_option( 'defenderLogsMovedCount' );
1062
- update_site_option( 'defenderMigrateNeedReset', 1 );
1063
- wp_send_json_error( array(
1064
- 'progress' => 0
1065
- ) );
1066
- }
1067
- $params = array(
1068
- 'date' => array(
1069
- 'compare' => '>=',
1070
- 'value' => strtotime( '-30 days' )
1071
- )
1072
- );
1073
- if ( $totalItems === false ) {
1074
- //get the total
1075
- $totalLogs = Log_Model_Legacy::count( $params );
1076
- $totalsIPs = IP_Model_Legacy::count();
1077
- //get the 200 items and import each time
1078
- update_site_option( 'defenderLogsTotal', $totalLogs + $totalsIPs );
1079
- //prevent timeout so we end here at the first time
1080
- wp_send_json_error( array(
1081
- 'progress' => 0
1082
- ) );
1083
- }
1084
-
1085
- $logs = Log_Model_Legacy::findAll( $params, 'id', 'DESC', '0,50' );
1086
- $logs = array_filter( $logs );
1087
- $ips = IP_Model_Legacy::findAll( array(), 'id', 'DESC', '0,50' );
1088
- $ips = array_filter( $ips );
1089
- $internalCount = 0;
1090
- if ( is_array( $logs ) && count( $logs ) ) {
1091
- foreach ( $logs as $item ) {
1092
- $model = new Log_Model();
1093
- $data = $item->export();
1094
- unset( $data['id'] );
1095
- $model->import( $data );
1096
- $model->save();
1097
- $item->delete();
1098
- }
1099
- $internalCount += count( $logs );
1100
- }
1101
-
1102
- if ( is_array( $ips ) && count( $ips ) ) {
1103
- foreach ( $ips as $item ) {
1104
- $model = new IP_Model();
1105
- $data = $item->export();
1106
- unset( $data['id'] );
1107
- $model->import( $data );
1108
- $model->save();
1109
- $item->delete();
1110
- }
1111
-
1112
- $internalCount += count( $ips );
1113
- }
1114
-
1115
- if ( empty( $logs ) && empty( $ips ) ) {
1116
- //all moved
1117
- delete_site_option( 'defenderLogsTotal' );
1118
- delete_site_option( 'defenderLogsMovedCount' );
1119
- delete_site_option( 'defenderLockoutNeedUpdateLog' );
1120
- wp_send_json_success( array(
1121
- 'message' => __( "Thanks for your patience. All set.", "defender-security" )
1122
- ) );
1123
- }
1124
-
1125
- $count = get_site_option( 'defenderLogsMovedCount', 0 );
1126
- $count += $internalCount;
1127
- update_site_option( 'defenderLogsMovedCount', $count );
1128
- wp_send_json_error( array(
1129
- 'progress' => round( ( $count / $totalItems ) * 100, 2 ) > 100 ? 100 : round( ( $count / $totalItems ) * 100, 2 )
1130
- ) );
 
 
 
1131
  }
1132
  }
6
  namespace WP_Defender\Module\IP_Lockout\Controller;
7
 
8
  use Hammer\Helper\HTTP_Helper;
 
 
9
  use WP_Defender\Behavior\Utils;
10
  use WP_Defender\Controller;
11
+ use WP_Defender\Module\IP_Lockout\Component\IP_API;
12
+ use WP_Defender\Module\IP_Lockout\Component\Login_Listener;
13
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
14
+ use WP_Defender\Module\IP_Lockout\Component\Notfound_Listener;
15
  use WP_Defender\Module\IP_Lockout\Model\IP_Model;
 
16
  use WP_Defender\Module\IP_Lockout\Model\Log_Model;
 
17
  use WP_Defender\Module\IP_Lockout\Model\Settings;
 
18
 
19
  class Main extends Controller {
20
  protected $slug = 'wdf-ip-lockout';
21
+
 
 
22
  /**
23
  * @return array
24
  */
25
  public function behaviors() {
26
  $behaviors = array(
27
+ 'utils' => '\WP_Defender\Behavior\Utils',
28
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
29
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
30
  );
31
  if ( wp_defender()->isFree == false ) {
32
  $behaviors['pro'] = '\WP_Defender\Module\IP_Lockout\Behavior\Pro\Reporting';
33
  }
34
+
35
  return $behaviors;
36
  }
37
+
38
  public function __construct() {
39
  $this->maybeLockouts();
40
+
41
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
42
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
43
  } else {
44
+ $this->addAction( 'admin_menu', 'adminMenu' );
45
  }
46
+
47
  if ( $this->isInPage() || $this->isDashboard() ) {
48
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
49
  }
50
+
51
  $this->maybeExport();
52
+
53
+ $this->addAjaxAction( 'lockoutExportAsCsv', 'exportAsCsv' );
54
+
55
+ if ( ! Login_Protection_Api::checkIfTableExists() ) {
56
+ //no table logs, omething happen
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  return;
58
  }
59
+
60
+ $ip = $this->getUserIp();
61
+ $settings = Settings::instance();
62
+ if ( $settings->report && $this->hasMethod( 'lockoutReportCron' ) ) {
63
+ //report
64
+ $this->addAction( 'lockoutReportCron', 'lockoutReportCron' );
65
  }
66
+
67
+ //cron for cleanup
68
+ $nextCleanup = wp_next_scheduled( 'cleanUpOldLog' );
69
+ if ( $nextCleanup === false || $nextCleanup > strtotime( '+90 minutes' ) ) {
70
+ wp_clear_scheduled_hook( 'cleanUpOldLog' );
71
+ wp_schedule_event( time(), 'hourly', 'cleanUpOldLog' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
+
74
+ $this->addAction( 'cleanUpOldLog', 'cleanUpOldLog' );
75
+
76
+ if ( $settings->isWhitelist( $ip ) ) {
77
  return;
78
  }
79
+
80
+ $arr = $this->defaultWhiteListIps();
81
+
82
+ if ( in_array( $ip, $arr ) ) {
83
  return;
84
  }
85
+
86
+ $loginListener = new Login_Listener();
87
+ $notfoundListener = new Notfound_Listener();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
89
+
90
+ private function defaultWhiteListIps() {
91
+ return apply_filters( 'ip_lockout_default_whitelist_ip', array(
92
+ '192.241.148.185',
93
+ '104.236.132.222',
94
+ '192.241.140.159',
95
+ '192.241.228.89',
96
+ '198.199.88.192',
97
+ '54.197.28.242',
98
+ '54.221.174.186',
99
+ '54.236.233.244',
100
+ '18.204.159.253',
101
+ '66.135.60.59',
102
+ '34.196.51.17',
103
+ '52.57.5.20',
104
+ '127.0.0.1',
105
+ array_key_exists( 'SERVER_ADDR', $_SERVER ) ? $_SERVER['SERVER_ADDR'] : ( isset( $_SERVER['LOCAL_ADDR'] ) ? $_SERVER['LOCAL_ADDR'] : null )
106
+ ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
108
+
109
+ /**
110
+ *
111
+ */
112
  public function exportAsCsv() {
113
  if ( ! $this->checkPermission() ) {
114
  return;
133
  );
134
  fputcsv( $fp, $item );
135
  }
136
+
137
  $filename = 'wdf-lockout-logs-export-' . date( 'ymdHis' ) . '.csv';
138
  fseek( $fp, 0 );
139
  header( 'Content-Type: text/csv' );
142
  fpassthru( $fp );
143
  exit();
144
  }
145
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  /**
147
  * Determine if an ip get lockout or not
148
  */
149
  public function maybeLockouts() {
150
+ do_action( 'wd_before_lockout' );
151
  $settings = Settings::instance();
152
+ $isTest = HTTP_Helper::retrieveGet( 'def-lockout-demo', false ) == 1;
153
  if ( $isTest ) {
154
  $message = null;
155
+ $type = HTTP_Helper::retrieveGet( 'type' );
156
  switch ( $type ) {
157
  case 'login':
158
  $message = $settings->login_protection_lockout_message;
171
  ) );
172
  die;
173
  }
174
+
175
+ $ip = $this->getUserIp();
176
+ $arr = $this->defaultWhiteListIps();
177
+
178
+ if ( in_array( $ip, $arr ) ) {
179
+ return;
180
+ }
181
  if ( $settings->isWhitelist( $ip ) ) {
182
  return;
183
  } elseif ( $settings->isBlacklist( $ip ) ) {
205
  //if current user can logged in, and no blacklisted we don't need to check the ip
206
  return;
207
  }
208
+
209
  $model = IP_Model::findOne( array(
210
  'ip' => $ip
211
  ) );
222
  }
223
  }
224
  }
225
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  /**
227
  * cron for delete old log
228
  */
235
  ),
236
  ), '0,1000' );
237
  }
238
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  /**
240
  * After each log recorded, we will check if the threshold is met for a lockout
241
  *
248
  Login_Protection_Api::maybe404Lock( $log );
249
  }
250
  }
251
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
252
  /**
253
  * listener to process export Ips request
254
  */
255
  public function maybeExport() {
256
+ if ( HTTP_Helper::retrieveGet( 'page' ) == 'wdf-ip-lockout' && HTTP_Helper::retrieveGet( 'view' ) == 'export' ) {
257
  if ( ! $this->checkPermission() ) {
258
  return;
259
  }
260
+
261
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'exportIps' ) ) {
262
  return;
263
  }
264
  $setting = Settings::instance();
288
  exit();
289
  }
290
  }
291
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  /**
293
  * Add submit admin page
294
  */
295
  public function adminMenu() {
296
  $cap = is_multisite() ? 'manage_network_options' : 'manage_options';
297
  $action = "actionIndex";
 
 
 
298
  add_submenu_page( 'wp-defender', esc_html__( "IP Lockouts", "defender-security" ), esc_html__( "IP Lockouts", "defender-security" ), $cap, $this->slug, array(
299
  &$this,
300
  $action
301
  ) );
302
  }
303
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
304
  /**
305
  * queue scripts
306
  */
307
  public function scripts() {
308
  if ( $this->isInPage() ) {
 
309
  wp_enqueue_style( 'wpmudev-sui' );
 
 
 
 
310
  wp_enqueue_style( 'defender' );
311
+ wp_register_script( 'defender-iplockout', wp_defender()->getPluginUrl() . 'assets/app/ip-lockout.js', array(
312
+ 'vue',
313
+ 'defender',
314
+ 'wp-i18n'
315
+ ), wp_defender()->version, true );
316
+ //wp_register_script( 'iplockout', wp_defender()->getPluginUrl() . 'front/js/src/lockout.js', array(), wp_defender()->version, true );
317
+ wp_set_script_translations( 'defender-iplockout', 'wpdef' );
318
+ wp_localize_script( 'defender-iplockout', 'iplockout', $this->_scriptsData() );
319
+ Utils::instance()->createTranslationJson( 'defender-iplockout' );
320
+ wp_set_script_translations( 'defender-iplockout', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
321
+
322
+ wp_enqueue_media();
323
+ wp_enqueue_script( 'def-momentjs', wp_defender()->getPluginUrl() . 'assets/js/vendor/moment/moment.min.js' );
324
+ wp_enqueue_style( 'def-daterangepicker', wp_defender()->getPluginUrl() . 'assets/js/vendor/daterangepicker/daterangepicker.css' );
325
+ wp_enqueue_script( 'def-daterangepicker', wp_defender()->getPluginUrl() . 'assets/js/vendor/daterangepicker/daterangepicker.js' );
326
+ wp_enqueue_script( 'defender-iplockout' );
327
+ wp_enqueue_script( 'wpmudev-sui' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
328
  }
329
  }
330
+
331
  /**
332
+ * Define all the data passing to view layer
333
+ * @return array
334
+ * @since 2.2
335
  */
336
+ private function _scriptsData() {
337
+ if ( ! $this->checkPermission() ) {
338
+ return [];
339
+ }
340
+ $summaryData = Log_Model::getSummary();
341
+ $model = Settings::instance()->export( array( 'cache', 'geoIP_db' ) );
342
+ //base on view we will filter out the model data
343
+ $model['country_blacklist'] = Settings::instance()->getCountryBlacklist();
344
+ $model['country_whitelist'] = Settings::instance()->getCountryWhitelist();
345
+ $host = parse_url( get_site_url(), PHP_URL_HOST );
346
+ $host = str_replace( 'www.', '', $host );
347
+ $host = explode( '.', $host );
348
+ if ( is_array( $host ) ) {
349
+ $host = array_shift( $host );
 
 
350
  } else {
351
+ $host = null;
352
  }
353
+ $settings = Settings::instance();
354
+ $tz = get_option( 'gmt_offset' );
355
+ if ( substr( $tz, 0, 1 ) == '-' ) {
356
+ $tz = ' - ' . str_replace( '-', '', $tz );
 
 
 
357
  } else {
358
+ $tz = ' + ' . $tz;
359
+ }
360
+ $current_country = IP_API::getCurrentCountry();
361
+ $data = [
362
+ 'nonces' => [
363
+ 'fetchSummary' => wp_create_nonce( 'fetchSummary' ),
364
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
365
+ 'downloadGeoDB' => wp_create_nonce( 'downloadGeoDB' ),
366
+ 'importIPs' => wp_create_nonce( 'importIPs' ),
367
+ 'exportIPs' => wp_create_nonce( 'exportIPs' ),
368
+ 'queryLogs' => wp_create_nonce( 'queryLogs' ),
369
+ 'bulkAction' => wp_create_nonce( 'bulkAction' ),
370
+ 'toggleIpAction' => wp_create_nonce( 'toggleIpAction' ),
371
+ 'emptyLogs' => wp_create_nonce( 'emptyLogs' ),
372
+ 'queryLockedIps' => wp_create_nonce( 'queryLockedIps' ),
373
+ 'ipAction' => wp_create_nonce( 'ipAction' ),
374
+ 'exportIps' => wp_create_nonce( 'exportIps' )
375
+ ],
376
+ 'endpoints' => $this->getAllAvailableEndpoints( \WP_Defender\Module\IP_Lockout::getClassName() ),
377
+ 'whitelabel' => $this->whiteLabelStatus(),
378
+ 'highContrast' => $this->maybeHighContrast(),
379
+ //'model' => $model,
380
+ 'model' => [
381
+ 'ip_lockout' => $settings->exportByKeys(
382
+ [
383
+ 'login_protection',
384
+ 'login_protection_login_attempt',
385
+ 'login_protection_lockout_timeframe',
386
+ 'login_protection_lockout_ban',
387
+ 'login_protection_lockout_duration',
388
+ 'login_protection_lockout_duration_unit',
389
+ 'login_protection_lockout_message',
390
+ 'username_blacklist',
391
+ ] ),
392
+ 'nf_lockout' => $settings->exportByKeys( [
393
+ 'detect_404',
394
+ 'detect_404_threshold',
395
+ 'detect_404_timeframe',
396
+ 'detect_404_lockout_ban',
397
+ 'detect_404_lockout_duration',
398
+ 'detect_404_lockout_duration_unit',
399
+ 'detect_404_lockout_message',
400
+ 'detect_404_blacklist',
401
+ 'detect_404_whitelist',
402
+ 'detect_404_filetypes_blacklist',
403
+ 'detect_404_ignored_filetypes',
404
+ 'detect_404_logged',
405
+ ] ),
406
+ 'blacklist' => $settings->exportByKeys( [
407
+ 'ip_blacklist',
408
+ 'ip_whitelist',
409
+ 'country_blacklist',
410
+ 'country_whitelist',
411
+ 'ip_lockout_message',
412
+ ] ),
413
+ 'notification' => $settings->exportByKeys( [
414
+ 'login_lockout_notification',
415
+ 'ip_lockout_notification',
416
+ 'receipts',
417
+ 'cooldown_enabled',
418
+ 'cooldown_number_lockout',
419
+ 'cooldown_period'
420
+ ] ),
421
+ 'settings' => $settings->exportByKeys( [
422
+ 'storage_days'
423
+ ] ),
424
+ 'report' => $settings->exportByKeys( [
425
+ 'report',
426
+ 'report_receipts',
427
+ 'report_frequency',
428
+ 'report_day',
429
+ 'report_time'
430
+ ] )
431
+ ],
432
+ 'summaryData' => [
433
+ 'ip' => [
434
+ 'day' => $summaryData['loginLockoutToday'],
435
+ 'week' => $summaryData['loginLockoutThisWeek'],
436
+ ],
437
+ 'nf' => [
438
+ 'week' => $summaryData['lockout404ThisWeek'],
439
+ 'day' => $summaryData['lockout404Today'],
440
+ ],
441
+ 'month' => $summaryData['lockoutThisMonth'],
442
+ 'day' => $summaryData['lockoutToday'],
443
+ 'lastLockout' => $summaryData['lastLockout']
444
+ ],
445
+ 'misc' => [
446
+ 'geo_db_downloaded' => Settings::instance()->isGeoDBDownloaded(),
447
+ 'current_country' => isset( $current_country['iso'] ) ? $current_country['iso'] : null,
448
+ 'blacklist_countries' => array_merge( [ 'all' => __( "Block all", "defender-security" ) ], Utils::instance()->countriesList() ),
449
+ 'whitelist_countries' => array_merge( [ 'all' => __( "Allow all", "defender-security" ) ], Utils::instance()->countriesList() ),
450
+ 'days_of_weeks' => Utils::instance()->getDaysOfWeek(),
451
+ 'times_of_days' => Utils::instance()->getTimes(),
452
+ 'host' => $host,
453
+ 'user_ip' => Utils::instance()->getUserIp(),
454
+ 'geo_requirement' => version_compare( phpversion(), '5.4', '<' ),
455
+ 'tz' => $tz,
456
+ 'current_time' => \WP_Defender\Behavior\Utils::instance()->formatDateTime( current_time( 'timestamp' ), false )
457
+ ],
458
+ 'table' => [
459
+ 'date_from' => Http_Helper::retrieveGet( 'date_from', date( 'm/d/Y', strtotime( 'today midnight', strtotime( '-14 days', current_time( 'timestamp' ) ) ) ) ),
460
+ 'date_to' => Http_Helper::retrieveGet( 'date_to', date( 'm/d/Y', current_time( 'timestamp' ) ) )
461
+ ]
462
+ ];
463
+
464
+ return $data;
465
+ }
466
+
467
+ /**
468
+ * Internal route
469
+ */
470
+ public function actionIndex() {
471
+
472
+ return $this->render( 'main' );
473
  }
474
  }
app/module/ip-lockout/controller/rest.php ADDED
@@ -0,0 +1,385 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\IP_Lockout\Controller;
7
+
8
+ use Hammer\Helper\Array_Helper;
9
+ use Hammer\Helper\HTTP_Helper;
10
+ use Hammer\Helper\WP_Helper;
11
+ use WP_Defender\Behavior\Utils;
12
+ use WP_Defender\Controller;
13
+ use WP_Defender\Module\IP_Lockout;
14
+ use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
15
+ use WP_Defender\Module\IP_Lockout\Model\Log_Model;
16
+ use WP_Defender\Module\IP_Lockout\Model\Settings;
17
+
18
+ class Rest extends Controller {
19
+ public function __construct() {
20
+ $namespace = 'wp-defender/v1';
21
+ $namespace .= '/lockout';
22
+ $routes = [
23
+ $namespace . '/updateSettings' => 'updateSettings',
24
+ $namespace . '/downloadGeoDB' => 'downloadGeoDB',
25
+ $namespace . '/importIPs' => 'importIPs',
26
+ $namespace . '/exportIPs' => 'exportIPs',
27
+ $namespace . '/queryLogs' => 'queryLogs',
28
+ $namespace . '/bulkAction' => 'bulkAction',
29
+ $namespace . '/toggleIpAction' => 'toggleIpAction',
30
+ $namespace . '/emptyLogs' => 'emptyLogs',
31
+ $namespace . '/queryLockedIps' => 'queryLockedIps',
32
+ $namespace . '/ipAction' => 'ipAction',
33
+ ];
34
+
35
+ $this->registerEndpoints( $routes, IP_Lockout::getClassName() );
36
+ }
37
+
38
+ /**
39
+ * Endpoint for toggle IP status, use in IP Banning->ban IPs
40
+ */
41
+ public function ipAction() {
42
+ if ( ! $this->checkPermission() ) {
43
+ return;
44
+ }
45
+
46
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'ipAction' ) ) {
47
+ return;
48
+ }
49
+
50
+ $ip = HTTP_Helper::retrievePost( 'ip' );
51
+ $action = HTTP_Helper::retrievePost( 'behavior' );
52
+ $model = IP_Lockout\Model\IP_Model::findOne( [
53
+ 'ip' => $ip
54
+ ] );
55
+
56
+ if ( is_object( $model ) ) {
57
+ if ( $action === 'unban' ) {
58
+ $model->status = IP_Lockout\Model\IP_Model::STATUS_NORMAL;
59
+ $model->save();
60
+ } elseif ( $action === 'ban' ) {
61
+ $model->status = IP_Lockout\Model\IP_Model::STATUS_BLOCKED;
62
+ $model->save();
63
+ }
64
+ wp_send_json_success( [ 'data' => '' ] );
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Endpoint to query locked IPs, use in IP Banning->ban IPs
70
+ */
71
+ public function queryLockedIps() {
72
+ if ( ! $this->checkPermission() ) {
73
+ return;
74
+ }
75
+
76
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'queryLockedIps' ) ) {
77
+ return;
78
+ }
79
+
80
+ $results = IP_Lockout\Model\IP_Model::queryLockedIp();
81
+
82
+ wp_send_json_success( [
83
+ 'ips_locked' => $results,
84
+ ] );
85
+ }
86
+
87
+ /**
88
+ * Endpoint for toggle IP blacklist or whitelist, use on logs item content
89
+ */
90
+ public function toggleIpAction() {
91
+ if ( ! $this->checkPermission() ) {
92
+ return;
93
+ }
94
+
95
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'toggleIpAction' ) ) {
96
+ return;
97
+ }
98
+
99
+ $ip = HTTP_Helper::retrievePost( 'ip', false );
100
+ $type = HTTP_Helper::retrievePost( 'type' );
101
+ $type = sanitize_key( $type );
102
+
103
+ if ( $ip && filter_var( $ip, FILTER_VALIDATE_IP ) ) {
104
+ if ( $type == 'unwhitelist' || $type == 'unblacklist' ) {
105
+ $type = substr( $type, 2 );
106
+ Settings::instance()->removeIpFromList( $ip, $type );
107
+ wp_send_json_success( array(
108
+ 'message' => sprintf( __( "IP %s has been removed from your %s. You can control your %s in <a href=\"%s\">IP Lockouts.</a>", "defender-security" ), $ip, $type, $type, network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blacklist' ) ),
109
+ ) );
110
+ } else {
111
+ Settings::instance()->addIpToList( $ip, $type );
112
+ wp_send_json_success( array(
113
+ 'message' => sprintf( __( "IP %s has been added to your %s You can control your %s in <a href=\"%s\">IP Lockouts.</a>", "defender-security" ), $ip, $type, $type, network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blacklist' ) ),
114
+ ) );
115
+ }
116
+
117
+ } else {
118
+ wp_send_json_error( array(
119
+ 'message' => __( "No record found", "defender-security" )
120
+ ) );
121
+ }
122
+ }
123
+
124
+ public function emptyLogs() {
125
+ if ( ! $this->checkPermission() ) {
126
+ return;
127
+ }
128
+
129
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'emptyLogs' ) ) {
130
+ return;
131
+ }
132
+
133
+ $perPage = 500;
134
+ $count = Log_Model::deleteAll( array(), '0,' . $perPage );
135
+ if ( $count == 0 ) {
136
+ wp_send_json_success( array(
137
+ 'message' => __( "Your logs have been successfully deleted.", "defender-security" )
138
+ ) );
139
+ }
140
+
141
+ wp_send_json_error( array() );
142
+ }
143
+
144
+ /**
145
+ *
146
+ */
147
+ public function bulkAction() {
148
+ if ( ! $this->checkPermission() ) {
149
+ return;
150
+ }
151
+
152
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'bulkAction' ) ) {
153
+ return;
154
+ }
155
+
156
+ $ids = HTTP_Helper::retrievePost( 'ids', [] );
157
+ $type = HTTP_Helper::retrievePost( 'type' );
158
+ $messages = '';
159
+ $ips = [];
160
+ if ( count( $ids ) && $type ) {
161
+ $settings = Settings::instance();
162
+ switch ( $type ) {
163
+ case 'whitelist':
164
+ foreach ( $ids as $id ) {
165
+ $model = Log_Model::findByID( $id );
166
+ $ips[] = $model->ip;
167
+ $settings->addIpToList( $model->ip, 'whitelist' );
168
+ }
169
+ $messages = sprintf( __( "IP %s has been added to your whitelist. You can control your whitelist in <a href=\"%s\">IP Lockouts.</a>", "defender-security" ), implode( ',', $ips ), network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blacklist' ) );
170
+ break;
171
+ case 'ban':
172
+ foreach ( $ids as $id ) {
173
+ $model = Log_Model::findByID( $id );
174
+ $ips[] = $model->ip;
175
+ $settings->addIpToList( $model->ip, 'blacklist' );
176
+ }
177
+ $messages = sprintf( __( "IP %s has been added to your blacklist You can control your blacklist in <a href=\"%s\">IP Lockouts.</a>", "defender-security" ), implode( ',', $ips ), network_admin_url( 'admin.php?page=wdf-ip-lockout&view=blacklist' ) );
178
+ break;
179
+ case 'delete':
180
+ foreach ( $ids as $id ) {
181
+ $model = Log_Model::findByID( $id );
182
+ $ips[] = $model->ip;
183
+ $model->delete();
184
+ }
185
+ $messages = sprintf( __( "IP %s has been deleted", "defender-security" ), implode( ',', $ips ) );
186
+ break;
187
+ default:
188
+ //param not from the button on frontend, log it
189
+ //error_log( sprintf( 'Unexpected value %s from IP %s', $type, Utils::instance()->getUserIp() ) );
190
+ break;
191
+ }
192
+
193
+ wp_send_json_success( array(
194
+ 'reload' => 1,
195
+ 'message' => $messages
196
+ ) );
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Query the data
202
+ */
203
+ public function queryLogs() {
204
+ if ( ! $this->checkPermission() ) {
205
+ return;
206
+ }
207
+
208
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'queryLogs' ) ) {
209
+ return;
210
+ }
211
+ $data = $_POST;
212
+ $filters = [
213
+ 'dateFrom' => strtotime( 'midnight', strtotime( Array_Helper::getValue( $data, 'date_from' ) ) ),
214
+ 'dateTo' => strtotime( 'tomorrow', strtotime( Array_Helper::getValue( $data, 'date_to' ) ) ),
215
+ 'type' => Array_Helper::getValue( $data, 'type', false ),
216
+ 'ip' => Array_Helper::getValue( $data, 'ip', false )
217
+ ];
218
+
219
+ $paged = Array_Helper::getValue( $data, 'paged', 1 );
220
+ $orderBy = Array_Helper::getValue( $data, 'orderBy', 'id' );
221
+ $order = Array_Helper::getValue( $data, 'order', 'DESC' );
222
+
223
+ $pageSize = 20;
224
+
225
+ list( $logs, $countAllLogs ) = Log_Model::queryLogs( $filters, $paged, $orderBy, $order, $pageSize );
226
+ $ids = [];
227
+ foreach ( $logs as $log ) {
228
+ $log->date = $log->get_date();
229
+ $log->statusText = Login_Protection_Api::getIPStatusText( $log->ip );
230
+ $log->ip_status = $log->blackOrWhite();
231
+ $log->is_mine = Utils::instance()->getUserIp() == $log->ip;
232
+ $ids[] = $log->id;
233
+ }
234
+ $totalPages = ceil( $countAllLogs / $pageSize );
235
+ wp_send_json_success( array(
236
+ 'logs' => $logs,
237
+ 'countAll' => $countAllLogs,
238
+ 'totalPages' => $totalPages
239
+ ) );
240
+ }
241
+
242
+ /**
243
+ * Importing IPs from our exporter
244
+ */
245
+ public function importIPs() {
246
+ if ( ! $this->checkPermission() ) {
247
+ return;
248
+ }
249
+
250
+ $id = HTTP_Helper::retrievePost( 'id' );
251
+ if ( ! is_object( get_post( $id ) ) ) {
252
+ wp_send_json_error( array(
253
+ 'message' => __( "Your file is invalid!", "defender-security" )
254
+ ) );
255
+ }
256
+ $file = get_attached_file( $id );
257
+
258
+ if ( ! is_file( $file ) ) {
259
+ wp_send_json_error( array(
260
+ 'message' => __( "Your file is invalid!", "defender-security" )
261
+ ) );
262
+ }
263
+
264
+ if ( ! ( $data = Login_Protection_Api::verifyImportFile( $file ) ) ) {
265
+ wp_send_json_error( array(
266
+ 'message' => __( "Your file content is invalid!", "defender-security" )
267
+ ) );
268
+ }
269
+
270
+ $settings = Settings::instance();
271
+ //all good, start to import
272
+ foreach ( $data as $line ) {
273
+ $settings->addIpToList( $line[0], $line[1] );
274
+ }
275
+ wp_send_json_success( array(
276
+ 'message' => __( "Your whitelist/blacklist has been successfully imported.", "defender-security" ),
277
+ 'reload' => 1
278
+ ) );
279
+ }
280
+
281
+ /**
282
+ * Downloading GeoDB from maxmind
283
+ */
284
+ public function downloadGeoDB() {
285
+ if ( ! $this->checkPermission() ) {
286
+ return;
287
+ }
288
+
289
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'downloadGeoDB' ) ) {
290
+ return;
291
+ }
292
+
293
+ $url = "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz";
294
+ $tmp = download_url( $url );
295
+ if ( ! is_wp_error( $tmp ) ) {
296
+ $phar = new \PharData( $tmp );
297
+ $defPath = Utils::instance()->getDefUploadDir();
298
+ $path = $defPath . DIRECTORY_SEPARATOR . 'maxmind';
299
+ if ( ! is_dir( $path ) ) {
300
+ mkdir( $path );
301
+ }
302
+ $phar->extractTo( $path, null, true );
303
+ $settings = Settings::instance();
304
+ $settings->geoIP_db = $path . DIRECTORY_SEPARATOR . $phar->current()->getFileName() . DIRECTORY_SEPARATOR . 'GeoLite2-Country.mmdb';
305
+ $settings->save();
306
+ wp_send_json_success( array(
307
+ 'message' => __( "Database downloaded", "defender-security" )
308
+ ) );
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Update IP lockout settings, parameters sent via an ajax _POST request
314
+ */
315
+ public function updateSettings() {
316
+ if ( ! $this->checkPermission() ) {
317
+ return;
318
+ }
319
+
320
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'updateSettings' ) ) {
321
+ return;
322
+ }
323
+ $settings = Settings::instance();
324
+ $lastSettings = clone $settings;
325
+ $data = stripslashes( $_POST['data'] );
326
+ $data = json_decode( $data, true );
327
+
328
+ $settings->import( $data );
329
+
330
+ if ( $settings->validate() ) {
331
+ $settings->save();
332
+ $faultIps = WP_Helper::getArrayCache()->get( 'faultIps', array() );
333
+ $isBLSelf = WP_Helper::getArrayCache()->get( 'isBlacklistSelf', false );
334
+ if ( $faultIps || $isBLSelf ) {
335
+ $res = array(
336
+ 'message' => sprintf( __( "Your settings have been updated, however some IPs were removed because invalid format, or you blacklist yourself", "defender-security" ), implode( ',', $faultIps ) ),
337
+ 'reload' => 1
338
+ );
339
+ } else {
340
+ $res = array( 'message' => __( "Your settings have been updated.", "defender-security" ), );
341
+ }
342
+ if ( ( $lastSettings->login_protection != $settings->login_protection )
343
+ || ( $lastSettings->detect_404 != $settings->detect_404 )
344
+ ) {
345
+ if ( isset( $data['login_protection'] ) ) {
346
+ if ( $data['login_protection'] == 1 ) {
347
+ $status = __( "Login Protection has been activated.", "defender-security" );
348
+ } else {
349
+ $status = __( "Login Protection has been deactivated.", "defender-security" );
350
+ }
351
+ }
352
+ if ( isset( $data['detect_404'] ) ) {
353
+ if ( $data['detect_404'] == 1 ) {
354
+ $status = __( "404 Detection has been activated.", "defender-security" );
355
+ } else {
356
+ $status = __( "404 Detection has been deactivated.", "defender-security" );
357
+ }
358
+ }
359
+ //mean enabled or disabled, reload
360
+ $res['reload'] = 1;
361
+ if ( isset( $status ) && strlen( $status ) ) {
362
+ $res['message'] = $status;
363
+ }
364
+ }
365
+ if ( $this->hasMethod( 'scheduleReport' ) ) {
366
+ $this->scheduleReport();
367
+ }
368
+ Utils::instance()->submitStatsToDev();
369
+ $res['reload'] = 1;
370
+ wp_send_json_success( $res );
371
+ } else {
372
+ wp_send_json_error( array(
373
+ 'message' => implode( '<br/>', $settings->getErrors() )
374
+ ) );
375
+ }
376
+ }
377
+
378
+ public function behaviors() {
379
+ $behaviors = array(
380
+ 'utils' => '\WP_Defender\Behavior\Utils',
381
+ );
382
+
383
+ return $behaviors;
384
+ }
385
+ }
app/module/ip-lockout/model/ip-model.php CHANGED
@@ -7,6 +7,7 @@
7
  namespace WP_Defender\Module\IP_Lockout\Model;
8
 
9
  use Hammer\Base\DB_Model;
 
10
 
11
  class IP_Model extends DB_Model {
12
  const STATUS_BLOCKED = 'blocked', STATUS_NORMAL = 'normal';
@@ -44,6 +45,23 @@ class IP_Model extends DB_Model {
44
  return false;
45
  }
46
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  /**
48
  * @param $key
49
  * @param null $default
@@ -67,6 +85,26 @@ class IP_Model extends DB_Model {
67
  return $default;
68
  }
69
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  /**
71
  * @param $key
72
  * @param $value
7
  namespace WP_Defender\Module\IP_Lockout\Model;
8
 
9
  use Hammer\Base\DB_Model;
10
+ use WP_Defender\Behavior\Utils;
11
 
12
  class IP_Model extends DB_Model {
13
  const STATUS_BLOCKED = 'blocked', STATUS_NORMAL = 'normal';
45
  return false;
46
  }
47
 
48
+ /**
49
+ * Initiaize IP model if not exist, else return the current
50
+ * @return IP_Model|null
51
+ */
52
+ public static function init() {
53
+ $model = self::findOne( [ 'ip' => Utils::instance()->getUserIp() ] );
54
+ if ( ! is_object( $model ) ) {
55
+ $model = new IP_Model();
56
+ //create new
57
+ $model->ip = Utils::instance()->getUserIp();
58
+ $model->status = IP_Model::STATUS_NORMAL;
59
+ $model->save();
60
+ }
61
+
62
+ return $model;
63
+ }
64
+
65
  /**
66
  * @param $key
67
  * @param null $default
85
  return $default;
86
  }
87
 
88
+ /**
89
+ * @param $ip
90
+ * @param $paged
91
+ *
92
+ * @return array
93
+ */
94
+ public static function queryLockedIp() {
95
+ $params = array(
96
+ 'status' => IP_Model::STATUS_BLOCKED
97
+ );
98
+
99
+ $results = IP_Model::findAllOnlyColumns( $params, [
100
+ 'id',
101
+ 'ip',
102
+ 'status'
103
+ ], 'lock_time', 'DESC' );
104
+
105
+ return $results;
106
+ }
107
+
108
  /**
109
  * @param $key
110
  * @param $value
app/module/ip-lockout/model/log-model.php CHANGED
@@ -7,6 +7,7 @@
7
  namespace WP_Defender\Module\IP_Lockout\Model;
8
 
9
  use Hammer\Base\DB_Model;
 
10
  use WP_Defender\Behavior\Utils;
11
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
12
 
@@ -23,8 +24,19 @@ class Log_Model extends DB_Model {
23
  public $blog_id;
24
  public $tried;
25
 
 
 
 
 
 
 
 
 
 
 
26
  /**
27
  * @return string
 
28
  */
29
  public function get_ip() {
30
  return esc_html( $this->ip );
@@ -32,6 +44,7 @@ class Log_Model extends DB_Model {
32
 
33
  /**
34
  * @return string
 
35
  */
36
  public function get_log_text( $format = false ) {
37
  if ( ! $format ) {
@@ -51,6 +64,21 @@ class Log_Model extends DB_Model {
51
  $this->blog_id = get_current_blog_id();
52
  }
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  /**
55
  * @return string
56
  */
@@ -58,7 +86,7 @@ class Log_Model extends DB_Model {
58
  if ( strtotime( '-24 hours' ) > $this->date ) {
59
  return Utils::instance()->formatDateTime( date( 'Y-m-d H:i:s', $this->date ) );
60
  } else {
61
- return Login_Protection_Api::time_since( $this->date );
62
  }
63
  }
64
 
@@ -81,6 +109,122 @@ class Log_Model extends DB_Model {
81
  return null;
82
  }
83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  /**
85
  * @return array
86
  */
@@ -104,4 +248,11 @@ class Log_Model extends DB_Model {
104
  )
105
  );
106
  }
 
 
 
 
 
 
 
107
  }
7
  namespace WP_Defender\Module\IP_Lockout\Model;
8
 
9
  use Hammer\Base\DB_Model;
10
+ use Hammer\Helper\Array_Helper;
11
  use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Module\IP_Lockout\Component\Login_Protection_Api;
13
 
24
  public $blog_id;
25
  public $tried;
26
 
27
+ /**
28
+ * A helper attribute for storing status text to output to frontend
29
+ * This wont be save into db
30
+ * @var
31
+ */
32
+ public $ip_status;
33
+ public $is_mine;
34
+ public $statusText;
35
+ public $actionText;
36
+
37
  /**
38
  * @return string
39
+ * @deprecated 2.2
40
  */
41
  public function get_ip() {
42
  return esc_html( $this->ip );
44
 
45
  /**
46
  * @return string
47
+ * @deprecated 2.2
48
  */
49
  public function get_log_text( $format = false ) {
50
  if ( ! $format ) {
64
  $this->blog_id = get_current_blog_id();
65
  }
66
 
67
+ /**
68
+ * Get current status of this ip due to whitelist/blacklist data
69
+ * @return string
70
+ */
71
+ public function blackOrWhite() {
72
+ $settings = Settings::instance();
73
+ if ( in_array( $this->ip, $settings->getIpWhitelist() ) ) {
74
+ return 'whitelist';
75
+ } elseif ( in_array( $this->ip, $settings->getIpBlacklist() ) ) {
76
+ return 'blacklist';
77
+ }
78
+
79
+ return 'na';
80
+ }
81
+
82
  /**
83
  * @return string
84
  */
86
  if ( strtotime( '-24 hours' ) > $this->date ) {
87
  return Utils::instance()->formatDateTime( date( 'Y-m-d H:i:s', $this->date ) );
88
  } else {
89
+ return Login_Protection_Api::time_since( $this->date ) . ' ' . __( "ago", "defender-security" );
90
  }
91
  }
92
 
109
  return null;
110
  }
111
 
112
+ /**
113
+ * Return summary data
114
+ * @return array
115
+ */
116
+ public static function getSummary() {
117
+ $lockouts = Log_Model::findAll( array(
118
+ 'type' => array(
119
+ Log_Model::LOCKOUT_404,
120
+ Log_Model::AUTH_LOCK
121
+ ),
122
+ 'date' => array(
123
+ 'compare' => '>=',
124
+ 'value' => strtotime( '-30 days', current_time( 'timestamp' ) )
125
+ )
126
+ ), 'id', 'DESC' );
127
+
128
+ if ( count( $lockouts ) == 0 ) {
129
+ $data = array(
130
+ 'lastLockout' => __( "Never", "defender-security" ),
131
+ 'lockoutToday' => 0,
132
+ 'lockoutThisMonth' => 0,
133
+ 'loginLockoutToday' => 0,
134
+ 'loginLockoutThisWeek' => 0,
135
+ 'lockout404Today' => 0,
136
+ 'lockout404ThisWeek' => 0,
137
+ );
138
+
139
+ return $data;
140
+ }
141
+
142
+ //init params
143
+ $lastLockout = '';
144
+ $lockoutToday = 0;
145
+ $lockoutThisMonth = count( $lockouts );
146
+ $loginLockoutToday = 0;
147
+ $loginLockoutThisWeek = 0;
148
+ $lockout404ThisWeek = 0;
149
+ $lockout404Today = 0;
150
+ //time
151
+ $todayMidnight = strtotime( '-24 hours', current_time( 'timestamp' ) );
152
+ $firstThisWeek = strtotime( '-7 days', current_time( 'timestamp' ) );
153
+ foreach ( $lockouts as $k => $log ) {
154
+ //the other as DESC, so first will be last lockout
155
+ if ( $k == 0 ) {
156
+ $lastLockout = Utils::instance()->formatDateTime( date( 'Y-m-d H:i:s', $log->date ) );
157
+ }
158
+
159
+ if ( $log->date > $todayMidnight ) {
160
+ $lockoutToday ++;
161
+ if ( $log->type == self::LOCKOUT_404 ) {
162
+ $lockout404Today += 1;
163
+ } else {
164
+ $loginLockoutToday += 1;
165
+ }
166
+ }
167
+
168
+ if ( $log->type == Log_Model::AUTH_LOCK && $log->date > $firstThisWeek ) {
169
+ $loginLockoutThisWeek ++;
170
+ } elseif ( $log->type == Log_Model::LOCKOUT_404 && $log->date > $firstThisWeek ) {
171
+ $lockout404ThisWeek ++;
172
+ }
173
+ }
174
+
175
+ $data = array(
176
+ 'lastLockout' => $lastLockout,
177
+ 'lockoutToday' => $lockoutToday,
178
+ 'lockoutThisMonth' => $lockoutThisMonth,
179
+ 'loginLockoutToday' => $loginLockoutToday,
180
+ 'loginLockoutThisWeek' => $loginLockoutThisWeek,
181
+ 'lockout404ThisWeek' => $lockout404ThisWeek,
182
+ 'lockout404Today' => $lockout404Today
183
+ );
184
+
185
+ return $data;
186
+ }
187
+
188
+ /**
189
+ * Pulling the logs data, use in Logs tab
190
+ * $filters will have those params
191
+ * -date_from
192
+ * -date_to
193
+ * == Defaults is 7 days and always require
194
+ * -type: optional
195
+ * -ip: optional
196
+ *
197
+ * @param array $filters
198
+ * @param int $paged
199
+ * @param string $orderBy
200
+ * @param string $order
201
+ * @param int $pageSize
202
+ *
203
+ * @return Log_Model[]
204
+ */
205
+ public static function queryLogs( $filters = array(), $paged = 1, $orderBy = 'id', $order = 'DESC', $pageSize = 20 ) {
206
+ $params = [
207
+ 'date' => [
208
+ 'compare' => 'between',
209
+ 'from' => Array_Helper::getValue( $filters, 'dateFrom', strtotime( '-7 days midnight' ) ),
210
+ 'to' => Array_Helper::getValue( $filters, 'dateTo', strtotime( 'tomorrow' ) )
211
+ ],
212
+ ];
213
+
214
+ if ( ( $filter = Array_Helper::getValue( $filters, 'type', null ) ) != null ) {
215
+ $params['type'] = $filter;
216
+ }
217
+ if ( ( $ip = Array_Helper::getValue( $filters, 'ip', null ) ) != null ) {
218
+ $params['ip'] = $ip;
219
+ }
220
+
221
+ $offset = ( $paged - 1 ) * $pageSize;
222
+ $models = Log_Model::findAll( $params, $orderBy, $order, "$offset,$pageSize" );
223
+ $count = Log_Model::count( $params );
224
+
225
+ return [ $models, $count ];
226
+ }
227
+
228
  /**
229
  * @return array
230
  */
248
  )
249
  );
250
  }
251
+
252
+ /**
253
+ * @return array
254
+ */
255
+ public function notSaveFields() {
256
+ return array( 'statusText', 'actionText', 'ip_status', 'is_mine' );
257
+ }
258
  }
app/module/ip-lockout/model/settings.php CHANGED
@@ -13,7 +13,7 @@ use WP_Defender\Module\IP_Lockout\Component\IP_API;
13
 
14
  class Settings extends \Hammer\WP\Settings {
15
  private static $_instance;
16
-
17
  public $login_protection = false;
18
  public $login_protection_login_attempt = 5;
19
  public $login_protection_lockout_timeframe = 300;
@@ -23,47 +23,49 @@ class Settings extends \Hammer\WP\Settings {
23
  public $login_protection_ban_admin_brute = false;
24
  public $login_protection_lockout_ban = false;
25
  public $username_blacklist = '';
26
-
27
  public $detect_404 = false;
28
  public $detect_404_threshold = 20;
29
  public $detect_404_timeframe = 300;
30
  public $detect_404_lockout_duration = 300;
31
  public $detect_404_lockout_duration_unit = 'seconds';
32
  public $detect_404_whitelist;
 
33
  public $detect_404_ignored_filetypes;
34
- public $detect_404_lockout_message = "You have been locked out due to too many attempts to access a file that doesn’t exist.";
 
35
  public $detect_404_lockout_ban = false;
36
  public $detect_404_logged = true;
37
-
38
- public $ip_blacklist;
39
- public $ip_whitelist;
40
  public $ip_lockout_message = 'The administrator has blocked your IP from accessing this website.';
41
-
42
- public $country_blacklist;
43
- public $country_whitelist;
44
-
45
  public $login_lockout_notification = true;
46
  public $ip_lockout_notification = true;
47
-
48
  public $report = true;
49
  public $report_frequency = '7';
50
  public $report_day = 'sunday';
51
  public $report_time = '0:00';
52
-
53
  public $geoIP_db = null;
54
-
55
  public $storage_days = 30;
56
-
57
  public $receipts = array();
58
  public $report_receipts = array();
59
  public $lastReportSent;
60
-
61
  public $cooldown_enabled = false;
62
  public $cooldown_number_lockout = '3';
63
  public $cooldown_period = '24';
64
-
65
  public $cache = array();
66
-
67
  public function __construct( $id, $isMulti ) {
68
  if ( ( is_admin() || is_network_admin() ) && current_user_can( 'manage_options' ) ) {
69
  $user = wp_get_current_user();
@@ -77,21 +79,45 @@ class Settings extends \Hammer\WP\Settings {
77
  'email' => $user->user_email
78
  );
79
  }
80
-
81
  $this->ip_whitelist = $this->getUserIp() . PHP_EOL;
82
  //default is weekly
83
- $this->report_day = strtolower( date( 'l' ) );
84
- $hour = date( 'H', current_time( 'timestamp' ) );
85
- if ( $hour == '00' ) {
86
- $hour = 0;
87
- } else {
88
- $hour = ltrim( $hour, '0' );
89
- }
90
- $this->report_time = $hour . ':0';
91
  }
92
  parent::__construct( $id, $isMulti );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  }
94
-
95
  /**
96
  * @return array
97
  */
@@ -100,35 +126,56 @@ class Settings extends \Hammer\WP\Settings {
100
  'utils' => '\WP_Defender\Behavior\Utils'
101
  );
102
  }
103
-
104
  /**
105
  * @return array
106
  */
107
  public function rules() {
108
  return array(
109
- array(
110
- array(
111
  'login_protection_login_attempt',
112
  'login_protection_lockout_timeframe',
 
113
  'detect_404_threshold',
114
  'detect_404_timeframe',
 
115
  'storage_days',
116
- ),
117
  'integer',
118
- )
119
  );
120
  }
121
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  /**
123
  * @return array
124
  */
125
  public function get404Whitelist() {
126
  $arr = array_filter( explode( PHP_EOL, $this->detect_404_whitelist ) );;
127
  $arr = array_map( 'trim', $arr );
128
-
129
  return $arr;
130
  }
131
-
132
  /**
133
  * @return array
134
  */
@@ -136,50 +183,109 @@ class Settings extends \Hammer\WP\Settings {
136
  $arr = array_filter( explode( PHP_EOL, $this->detect_404_ignored_filetypes ) );
137
  $arr = array_map( 'trim', $arr );
138
  $arr = array_map( 'strtolower', $arr );
139
-
140
  return $arr;
141
  }
142
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
  /**
144
  * @return array
145
  */
146
  public function getIpBlacklist() {
147
- $arr = array_filter( explode( PHP_EOL, $this->ip_blacklist ) );
 
 
 
 
148
  $arr = array_map( 'trim', $arr );
149
-
150
  return $arr;
151
  }
152
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  /**
154
  * @return array
155
  */
156
  public function getIpWhitelist() {
157
- $arr = array_filter( explode( PHP_EOL, $this->ip_whitelist ) );
 
 
 
 
 
158
  $arr = array_map( 'trim', $arr );
159
-
160
  return $arr;
161
  }
162
-
163
  /**
164
  * @return array
165
  */
166
  public function getCountryBlacklist() {
 
 
 
 
167
  $arr = array_filter( explode( ',', $this->country_blacklist ) );
168
  $arr = array_map( 'trim', $arr );
169
-
170
  return $arr;
171
  }
172
-
173
  /**
174
  * @return array
175
  */
176
  public function getCountryWhitelist() {
 
 
 
 
177
  $arr = array_filter( explode( ',', $this->country_whitelist ) );
178
  $arr = array_map( 'trim', $arr );
179
-
180
  return $arr;
181
  }
182
-
183
  /**
184
  * @param $ip
185
  *
@@ -201,10 +307,10 @@ class Settings extends \Hammer\WP\Settings {
201
  return true;
202
  }
203
  }
204
-
205
  return false;
206
  }
207
-
208
  /**
209
  * @param $ip
210
  *
@@ -224,10 +330,10 @@ class Settings extends \Hammer\WP\Settings {
224
  return true;
225
  }
226
  }
227
-
228
  return false;
229
  }
230
-
231
  /**
232
  * @param $ip
233
  * @param $list
@@ -245,13 +351,13 @@ class Settings extends \Hammer\WP\Settings {
245
  if ( empty( $type ) ) {
246
  return;
247
  }
248
-
249
  $ips[] = $ip;
250
  $ips = array_unique( $ips );
251
  $this->$type = implode( PHP_EOL, $ips );
252
  $this->save();
253
  }
254
-
255
  /**
256
  * @param $ip
257
  * @param $list
@@ -269,7 +375,7 @@ class Settings extends \Hammer\WP\Settings {
269
  if ( empty( $type ) ) {
270
  return;
271
  }
272
-
273
  $key = array_search( $ip, $ips );
274
  if ( $key !== false ) {
275
  unset( $ips[ $key ] );
@@ -278,7 +384,7 @@ class Settings extends \Hammer\WP\Settings {
278
  $this->save();
279
  }
280
  }
281
-
282
  /**
283
  * @param $ip
284
  * @param $range
@@ -292,16 +398,16 @@ class Settings extends \Hammer\WP\Settings {
292
  $subnet = ip2long( $subnet );
293
  $mask = - 1 << ( 32 - $bits );
294
  $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
295
-
296
  return ( $ip & $mask ) == $subnet;
297
  }
298
-
299
  public function before_update() {
300
  //validate ips
301
  $remove_ips = array();
302
  $isSelf = false;
303
  if ( isset( $_POST['ip_blacklist'] ) ) {
304
- $blacklist = Http_Helper::retrieve_post( 'ip_blacklist' );
305
  $blacklist = explode( PHP_EOL, $blacklist );
306
  foreach ( $blacklist as $k => $ip ) {
307
  $ip = trim( $ip );
@@ -314,9 +420,9 @@ class Settings extends \Hammer\WP\Settings {
314
  }
315
  $this->ip_blacklist = implode( PHP_EOL, $blacklist );
316
  }
317
-
318
  if ( isset( $_POST['ip_whitelist'] ) ) {
319
- $whitelist = Http_Helper::retrieve_post( 'ip_whitelist' );
320
  $whitelist = explode( PHP_EOL, $whitelist );
321
  foreach ( $whitelist as $k => $ip ) {
322
  $ip = trim( $ip );
@@ -328,13 +434,13 @@ class Settings extends \Hammer\WP\Settings {
328
  $this->ip_whitelist = implode( PHP_EOL, $whitelist );
329
  }
330
  $remove_ips = array_filter( $remove_ips );
331
-
332
  if ( ! empty( $remove_ips ) && count( $remove_ips ) ) {
333
  WP_Helper::getArrayCache()->set( 'faultIps', $remove_ips );
334
  WP_Helper::getArrayCache()->set( 'isBlacklistSelf', $isSelf );
335
  }
336
  }
337
-
338
  /**
339
  * $ip an be single ip, or a range like xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx or CIDR
340
  *
@@ -369,10 +475,23 @@ class Settings extends \Hammer\WP\Settings {
369
  }
370
  }
371
  }
372
-
373
  return false;
374
  }
375
-
 
 
 
 
 
 
 
 
 
 
 
 
 
376
  /**
377
  * @param $ip
378
  *
@@ -381,7 +500,7 @@ class Settings extends \Hammer\WP\Settings {
381
  private function isIPV4( $ip ) {
382
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
383
  }
384
-
385
  /**
386
  * @param $ip
387
  *
@@ -390,34 +509,49 @@ class Settings extends \Hammer\WP\Settings {
390
  private function isIPV6( $ip ) {
391
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
392
  }
393
-
394
  /**
395
  * @return array
396
  */
397
  public function events() {
398
  $that = $this;
399
-
400
  return array(
401
  self::EVENT_BEFORE_SAVE => array(
402
  array(
403
  function () use ( $that ) {
404
  $that->before_update();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  //need to turn off notification or report off if no recipients
406
  $this->receipts = array_filter( $this->receipts );
407
  if ( count( $this->receipts ) == 0 ) {
408
- $this->ip_lockout_notification = 0;
409
- $this->login_lockout_notification = 0;
410
  }
411
  $this->report_receipts = array_filter( $this->report_receipts );
412
  if ( count( $this->report_receipts ) == 0 ) {
413
- $this->report = 0;
414
  }
415
  }
416
  )
417
  )
418
  );
419
  }
420
-
421
  /**
422
  * @return array|string
423
  */
@@ -427,10 +561,10 @@ class Settings extends \Hammer\WP\Settings {
427
  $usernames = array_map( 'trim', $usernames );
428
  $usernames = array_map( 'strtolower', $usernames );
429
  $usernames = array_filter( $usernames );
430
-
431
  return $usernames;
432
  }
433
-
434
  /**
435
  * @return bool
436
  */
@@ -438,10 +572,10 @@ class Settings extends \Hammer\WP\Settings {
438
  if ( is_null( $this->geoIP_db ) || ! is_file( $this->geoIP_db ) ) {
439
  return false;
440
  }
441
-
442
  return true;
443
  }
444
-
445
  /**
446
  * @return bool
447
  */
@@ -458,7 +592,7 @@ class Settings extends \Hammer\WP\Settings {
458
  if ( $this->isCountryWhitelist() ) {
459
  return false;
460
  }
461
-
462
  $blacklisted = $this->getCountryBlacklist();
463
  if ( empty( $blacklisted ) ) {
464
  return false;
@@ -466,16 +600,13 @@ class Settings extends \Hammer\WP\Settings {
466
  if ( in_array( 'all', $blacklisted ) ) {
467
  return true;
468
  }
469
-
470
- $country = IP_API::getCurrentCountry();
471
-
472
  if ( in_array( strtoupper( $country['iso'] ), $blacklisted ) ) {
473
  return true;
474
  }
475
-
476
  return false;
477
  }
478
-
479
  /**
480
  * @return bool
481
  */
@@ -485,14 +616,14 @@ class Settings extends \Hammer\WP\Settings {
485
  if ( empty( $whitelist ) ) {
486
  return false;
487
  }
488
-
489
  if ( in_array( strtoupper( $country['iso'] ), $whitelist ) ) {
490
  return true;
491
  }
492
-
493
  return false;
494
  }
495
-
496
  /**
497
  * @return Settings
498
  */
@@ -501,7 +632,63 @@ class Settings extends \Hammer\WP\Settings {
501
  $class = new Settings( 'wd_lockdown_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
502
  self::$_instance = $class;
503
  }
504
-
505
  return self::$_instance;
506
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
507
  }
13
 
14
  class Settings extends \Hammer\WP\Settings {
15
  private static $_instance;
16
+
17
  public $login_protection = false;
18
  public $login_protection_login_attempt = 5;
19
  public $login_protection_lockout_timeframe = 300;
23
  public $login_protection_ban_admin_brute = false;
24
  public $login_protection_lockout_ban = false;
25
  public $username_blacklist = '';
26
+
27
  public $detect_404 = false;
28
  public $detect_404_threshold = 20;
29
  public $detect_404_timeframe = 300;
30
  public $detect_404_lockout_duration = 300;
31
  public $detect_404_lockout_duration_unit = 'seconds';
32
  public $detect_404_whitelist;
33
+ public $detect_404_blacklist;
34
  public $detect_404_ignored_filetypes;
35
+ public $detect_404_filetypes_blacklist;
36
+ public $detect_404_lockout_message = "You have been locked out due to too many attempts to access a file that doesn't exist.";
37
  public $detect_404_lockout_ban = false;
38
  public $detect_404_logged = true;
39
+
40
+ public $ip_blacklist = array();
41
+ public $ip_whitelist = array();
42
  public $ip_lockout_message = 'The administrator has blocked your IP from accessing this website.';
43
+
44
+ public $country_blacklist = [];
45
+ public $country_whitelist = [];
46
+
47
  public $login_lockout_notification = true;
48
  public $ip_lockout_notification = true;
49
+
50
  public $report = true;
51
  public $report_frequency = '7';
52
  public $report_day = 'sunday';
53
  public $report_time = '0:00';
54
+
55
  public $geoIP_db = null;
56
+
57
  public $storage_days = 30;
58
+
59
  public $receipts = array();
60
  public $report_receipts = array();
61
  public $lastReportSent;
62
+
63
  public $cooldown_enabled = false;
64
  public $cooldown_number_lockout = '3';
65
  public $cooldown_period = '24';
66
+
67
  public $cache = array();
68
+
69
  public function __construct( $id, $isMulti ) {
70
  if ( ( is_admin() || is_network_admin() ) && current_user_can( 'manage_options' ) ) {
71
  $user = wp_get_current_user();
79
  'email' => $user->user_email
80
  );
81
  }
82
+
83
  $this->ip_whitelist = $this->getUserIp() . PHP_EOL;
84
  //default is weekly
85
+ $this->report_day = strtolower( date( 'l' ) );
86
+ $this->report_time = '4:00';
 
 
 
 
 
 
87
  }
88
  parent::__construct( $id, $isMulti );
89
+ /**
90
+ * Make sure those is boolen
91
+ */
92
+ $this->login_protection = ! ! $this->login_protection;
93
+ $this->detect_404 = ! ! $this->detect_404;
94
+ $this->login_protection_lockout_ban = ! ! $this->login_protection_lockout_ban;
95
+ $this->detect_404_lockout_ban = ! ! $this->detect_404_lockout_ban;
96
+ $this->report = ! ! $this->report;
97
+ $this->detect_404_logged = ! ! $this->detect_404_logged;
98
+
99
+ $times = Utils::instance()->getTimes();
100
+ if ( ! isset( $times[ $this->report_time ] ) ) {
101
+ $this->report_time = '4:00';
102
+ }
103
+ if ( ! is_array( $this->receipts ) ) {
104
+ $this->receipts = [];
105
+ }
106
+ $this->receipts = array_values( $this->receipts );
107
+ if ( ! is_array( $this->report_receipts ) ) {
108
+ $this->report_receipts = [];
109
+ }
110
+ $this->report_receipts = array_values( $this->report_receipts );
111
+ if ( is_string( $this->country_whitelist ) ) {
112
+ $this->country_whitelist = explode( ',', $this->country_whitelist );
113
+ $this->country_whitelist = ( array_values( array_filter( $this->country_whitelist ) ) );
114
+ }
115
+ if ( is_string( $this->country_blacklist ) ) {
116
+ $this->country_blacklist = explode( ',', $this->country_blacklist );
117
+ $this->country_blacklist = ( array_values( array_filter( $this->country_blacklist ) ) );
118
+ }
119
  }
120
+
121
  /**
122
  * @return array
123
  */
126
  'utils' => '\WP_Defender\Behavior\Utils'
127
  );
128
  }
129
+
130
  /**
131
  * @return array
132
  */
133
  public function rules() {
134
  return array(
135
+ [
136
+ [
137
  'login_protection_login_attempt',
138
  'login_protection_lockout_timeframe',
139
+ 'login_protection_lockout_duration',
140
  'detect_404_threshold',
141
  'detect_404_timeframe',
142
+ 'detect_404_lockout_duration',
143
  'storage_days',
144
+ ],
145
  'integer',
146
+ ],
147
  );
148
  }
149
+
150
+ /**
151
+ * @return array
152
+ */
153
+ public function filters() {
154
+ return [
155
+ 'username_blacklist',
156
+ 'login_protection_lockout_message',
157
+ 'detect_404_ignored_filetypes',
158
+ 'detect_404_filetypes_blacklist',
159
+ 'detect_404_lockout_message',
160
+ 'detect_404_whitelist',
161
+ 'detect_404_blacklist',
162
+ 'ip_lockout_message',
163
+ 'ip_blacklist',
164
+ 'ip_whitelist',
165
+ 'ip_lockout_message',
166
+ ];
167
+ }
168
+
169
  /**
170
  * @return array
171
  */
172
  public function get404Whitelist() {
173
  $arr = array_filter( explode( PHP_EOL, $this->detect_404_whitelist ) );;
174
  $arr = array_map( 'trim', $arr );
175
+
176
  return $arr;
177
  }
178
+
179
  /**
180
  * @return array
181
  */
183
  $arr = array_filter( explode( PHP_EOL, $this->detect_404_ignored_filetypes ) );
184
  $arr = array_map( 'trim', $arr );
185
  $arr = array_map( 'strtolower', $arr );
186
+
187
  return $arr;
188
  }
189
+
190
+ /**
191
+ * @return mixed
192
+ */
193
+ public function getDetect404Whitelist() {
194
+ $arr = array_filter( explode( PHP_EOL, $this->detect_404_whitelist ) );
195
+ $arr = array_map( 'trim', $arr );
196
+
197
+ return $arr;
198
+ }
199
+
200
+ /**
201
+ * @return mixed
202
+ */
203
+ public function getDetect404Blacklist() {
204
+ $arr = array_filter( explode( PHP_EOL, $this->detect_404_blacklist ) );
205
+ $arr = array_map( 'trim', $arr );
206
+
207
+ return $arr;
208
+ }
209
+
210
  /**
211
  * @return array
212
  */
213
  public function getIpBlacklist() {
214
+ if ( is_array( $this->ip_blacklist ) ) {
215
+ $arr = $this->ip_blacklist;
216
+ } else {
217
+ $arr = array_filter( explode( PHP_EOL, $this->ip_blacklist ) );
218
+ }
219
  $arr = array_map( 'trim', $arr );
220
+
221
  return $arr;
222
  }
223
+
224
+ /**
225
+ * @return array
226
+ */
227
+ public function getDetect404IgnoredFiletypes() {
228
+ $exts = explode( PHP_EOL, $this->detect_404_ignored_filetypes );
229
+ $exts = array_map( 'trim', $exts );
230
+ $exts = array_map( 'strtolower', $exts );
231
+
232
+ return $exts;
233
+ }
234
+
235
+ /**
236
+ * @return mixed
237
+ */
238
+ public function getDetect404FiletypesBlacklist() {
239
+ $exts = explode( PHP_EOL, $this->detect_404_filetypes_blacklist );
240
+ $exts = array_map( 'trim', $exts );
241
+ $exts = array_map( 'strtolower', $exts );
242
+
243
+ return $exts;
244
+ }
245
+
246
  /**
247
  * @return array
248
  */
249
  public function getIpWhitelist() {
250
+ //backward compatibility
251
+ if ( is_array( $this->ip_whitelist ) ) {
252
+ $arr = $this->ip_whitelist;
253
+ } else {
254
+ $arr = array_filter( explode( PHP_EOL, $this->ip_whitelist ) );
255
+ }
256
  $arr = array_map( 'trim', $arr );
257
+
258
  return $arr;
259
  }
260
+
261
  /**
262
  * @return array
263
  */
264
  public function getCountryBlacklist() {
265
+ if ( is_array( $this->country_blacklist ) ) {
266
+ return $this->country_blacklist;
267
+ }
268
+ //fallback to older version than 2.2
269
  $arr = array_filter( explode( ',', $this->country_blacklist ) );
270
  $arr = array_map( 'trim', $arr );
271
+
272
  return $arr;
273
  }
274
+
275
  /**
276
  * @return array
277
  */
278
  public function getCountryWhitelist() {
279
+ if ( is_array( $this->country_whitelist ) ) {
280
+ return $this->country_whitelist;
281
+ }
282
+ //fallback to older version than 2.2
283
  $arr = array_filter( explode( ',', $this->country_whitelist ) );
284
  $arr = array_map( 'trim', $arr );
285
+
286
  return $arr;
287
  }
288
+
289
  /**
290
  * @param $ip
291
  *
307
  return true;
308
  }
309
  }
310
+
311
  return false;
312
  }
313
+
314
  /**
315
  * @param $ip
316
  *
330
  return true;
331
  }
332
  }
333
+
334
  return false;
335
  }
336
+
337
  /**
338
  * @param $ip
339
  * @param $list
351
  if ( empty( $type ) ) {
352
  return;
353
  }
354
+
355
  $ips[] = $ip;
356
  $ips = array_unique( $ips );
357
  $this->$type = implode( PHP_EOL, $ips );
358
  $this->save();
359
  }
360
+
361
  /**
362
  * @param $ip
363
  * @param $list
375
  if ( empty( $type ) ) {
376
  return;
377
  }
378
+
379
  $key = array_search( $ip, $ips );
380
  if ( $key !== false ) {
381
  unset( $ips[ $key ] );
384
  $this->save();
385
  }
386
  }
387
+
388
  /**
389
  * @param $ip
390
  * @param $range
398
  $subnet = ip2long( $subnet );
399
  $mask = - 1 << ( 32 - $bits );
400
  $subnet &= $mask; # nb: in case the supplied subnet wasn't correctly aligned
401
+
402
  return ( $ip & $mask ) == $subnet;
403
  }
404
+
405
  public function before_update() {
406
  //validate ips
407
  $remove_ips = array();
408
  $isSelf = false;
409
  if ( isset( $_POST['ip_blacklist'] ) ) {
410
+ $blacklist = Http_Helper::retrievePost( 'ip_blacklist' );
411
  $blacklist = explode( PHP_EOL, $blacklist );
412
  foreach ( $blacklist as $k => $ip ) {
413
  $ip = trim( $ip );
420
  }
421
  $this->ip_blacklist = implode( PHP_EOL, $blacklist );
422
  }
423
+
424
  if ( isset( $_POST['ip_whitelist'] ) ) {
425
+ $whitelist = Http_Helper::retrievePost( 'ip_whitelist' );
426
  $whitelist = explode( PHP_EOL, $whitelist );
427
  foreach ( $whitelist as $k => $ip ) {
428
  $ip = trim( $ip );
434
  $this->ip_whitelist = implode( PHP_EOL, $whitelist );
435
  }
436
  $remove_ips = array_filter( $remove_ips );
437
+
438
  if ( ! empty( $remove_ips ) && count( $remove_ips ) ) {
439
  WP_Helper::getArrayCache()->set( 'faultIps', $remove_ips );
440
  WP_Helper::getArrayCache()->set( 'isBlacklistSelf', $isSelf );
441
  }
442
  }
443
+
444
  /**
445
  * $ip an be single ip, or a range like xxx.xxx.xxx.xxx - xxx.xxx.xxx.xxx or CIDR
446
  *
475
  }
476
  }
477
  }
478
+
479
  return false;
480
  }
481
+
482
+ public function beforeValidate() {
483
+ $emails = [];
484
+ foreach ( $this->receipts as $receipt ) {
485
+ if ( in_array( $receipt['email'], $emails ) ) {
486
+ $this->addError( 'recipients', __( "Recipients' emails can't be duplicate", "defender-security" ) );
487
+
488
+ return false;
489
+ } else {
490
+ $emails[] = $receipt['email'];
491
+ }
492
+ }
493
+ }
494
+
495
  /**
496
  * @param $ip
497
  *
500
  private function isIPV4( $ip ) {
501
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );
502
  }
503
+
504
  /**
505
  * @param $ip
506
  *
509
  private function isIPV6( $ip ) {
510
  return filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 );
511
  }
512
+
513
  /**
514
  * @return array
515
  */
516
  public function events() {
517
  $that = $this;
518
+
519
  return array(
520
  self::EVENT_BEFORE_SAVE => array(
521
  array(
522
  function () use ( $that ) {
523
  $that->before_update();
524
+
525
+ foreach ( $this->receipts as $k => &$receipt ) {
526
+ $receipt = array_map( 'sanitize_text_field', $receipt );
527
+ if ( ! filter_var( $receipt['email'], FILTER_VALIDATE_EMAIL ) ) {
528
+ unset( $this->receipts[ $k ] );
529
+ }
530
+ }
531
+
532
+ foreach ( $this->report_receipts as $k => &$receipt ) {
533
+ $receipt = array_map( 'sanitize_text_field', $receipt );
534
+ if ( ! filter_var( $receipt['email'], FILTER_VALIDATE_EMAIL ) ) {
535
+ unset( $this->report_receipts[ $k ] );
536
+ }
537
+ }
538
+
539
  //need to turn off notification or report off if no recipients
540
  $this->receipts = array_filter( $this->receipts );
541
  if ( count( $this->receipts ) == 0 ) {
542
+ $this->ip_lockout_notification = false;
543
+ $this->login_lockout_notification = false;
544
  }
545
  $this->report_receipts = array_filter( $this->report_receipts );
546
  if ( count( $this->report_receipts ) == 0 ) {
547
+ $this->report = false;
548
  }
549
  }
550
  )
551
  )
552
  );
553
  }
554
+
555
  /**
556
  * @return array|string
557
  */
561
  $usernames = array_map( 'trim', $usernames );
562
  $usernames = array_map( 'strtolower', $usernames );
563
  $usernames = array_filter( $usernames );
564
+
565
  return $usernames;
566
  }
567
+
568
  /**
569
  * @return bool
570
  */
572
  if ( is_null( $this->geoIP_db ) || ! is_file( $this->geoIP_db ) ) {
573
  return false;
574
  }
575
+
576
  return true;
577
  }
578
+
579
  /**
580
  * @return bool
581
  */
592
  if ( $this->isCountryWhitelist() ) {
593
  return false;
594
  }
595
+
596
  $blacklisted = $this->getCountryBlacklist();
597
  if ( empty( $blacklisted ) ) {
598
  return false;
600
  if ( in_array( 'all', $blacklisted ) ) {
601
  return true;
602
  }
 
 
 
603
  if ( in_array( strtoupper( $country['iso'] ), $blacklisted ) ) {
604
  return true;
605
  }
606
+
607
  return false;
608
  }
609
+
610
  /**
611
  * @return bool
612
  */
616
  if ( empty( $whitelist ) ) {
617
  return false;
618
  }
619
+
620
  if ( in_array( strtoupper( $country['iso'] ), $whitelist ) ) {
621
  return true;
622
  }
623
+
624
  return false;
625
  }
626
+
627
  /**
628
  * @return Settings
629
  */
632
  $class = new Settings( 'wd_lockdown_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
633
  self::$_instance = $class;
634
  }
635
+
636
  return self::$_instance;
637
  }
638
+
639
+
640
+ /**
641
+ * Define labels for settings key, we will use it for HUB
642
+ *
643
+ * @param null $key
644
+ *
645
+ * @return array|mixed
646
+ */
647
+ public function labels( $key = null ) {
648
+ $labels = [
649
+ 'login_protection' => __( "Login Protection", "defender-security" ),
650
+ 'login_protection_login_attempt' => __( "Threshold: Failed logins", "defender-security" ),
651
+ 'login_protection_lockout_timeframe' => __( "Threshold: Timeframe", "defender-security" ),
652
+ 'login_protection_lockout_ban' => __( "Duration", "defender-security" ),
653
+ 'login_protection_lockout_duration' => __( "Duration", "defender-security" ),
654
+ 'login_protection_lockout_duration_unit' => __( "Duration unit", "defender-security" ),
655
+ 'login_protection_lockout_message' => __( "Message", "defender-security" ),
656
+ 'username_blacklist' => __( "Banned usernames", "defender-security" ),
657
+ 'detect_404' => __( "404 Detection", "defender-security" ),
658
+ 'detect_404_threshold' => __( "Threshold: 404 hits", "defender-security" ),
659
+ 'detect_404_timeframe' => __( "Threshold: Timeframe", "defender-security" ),
660
+ 'detect_404_lockout_ban' => __( "Duration", "defender-security" ),
661
+ 'detect_404_lockout_duration' => __( "Duration", "defender-security" ),
662
+ 'detect_404_lockout_duration_unit' => __( "Duration unit", "defender-security" ),
663
+ 'detect_404_lockout_message' => __( "Message", "defender-security" ),
664
+ 'detect_404_blacklist' => __( "Files & Folders: Blacklist", "defender-security" ),
665
+ 'detect_404_whitelist' => __( "Files & Folders: Whitelist", "defender-security" ),
666
+ 'detect_404_filetypes_blacklist' => __( "Filetypes & Extensions: Blacklist", "defender-security" ),
667
+ 'detect_404_ignored_filetypes' => __( "Filetypes & Extensions: Whitelist", "defender-security" ),
668
+ 'detect_404_logged' => __( "Monitor 404s from logged in users", "defender-security" ),
669
+ 'ip_blacklist' => __( "IP Addresses: Blacklist", "defender-security" ),
670
+ 'ip_whitelist' => __( "IP Addresses: Whitelist", "defender-security" ),
671
+ 'country_blacklist' => __( "Country: Blacklist", "defender-security" ),
672
+ 'country_whitelist' => __( "Country: Whitelist", "defender-security" ),
673
+ 'ip_lockout_message' => __( "Country: Blacklist", "defender-security" ),
674
+ 'login_lockout_notification' => __( "Email Notifications: Login Protection Lockout", "defender-security" ),
675
+ 'ip_lockout_notification' => __( "Email Notifications: Login Protection Lockout", "defender-security" ),
676
+ 'receipts' => __( "Recipients for notification", "defender-security" ),
677
+ 'cooldown_enabled' => __( "Repeat Lockouts", "defender-security" ),
678
+ 'cooldown_number_lockout' => __( "Threshold", "defender-security" ),
679
+ 'cooldown_period' => __( "Cool Off Period", "defender-security" ),
680
+ 'storage_days' => __( "Storage", "defender-security" ),
681
+ 'report' => __( "Report", "defender-security" ),
682
+ 'report_receipts' => __( "Recipients for report", "defender-security" ),
683
+ 'report_frequency' => __( "Frequency", "defender-security" ),
684
+ 'report_day' => __( "Day of the week", "defender-security" ),
685
+ 'report_time' => __( "Time of day", "defender-security" )
686
+ ];
687
+
688
+ if ( $key != null ) {
689
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
690
+ }
691
+
692
+ return $labels;
693
+ }
694
  }
app/module/ip-lockout/view/emails/404-ban.php CHANGED
@@ -2,691 +2,692 @@
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
- <meta name="viewport" content="width=device-width">
7
- <title><?php _e( "New 404 Lockout", "defender-security" ) ?></title>
8
- <style>
9
- a.plugin-brand:hover {
10
- color: #e23717 !important;
11
- }
12
-
13
- table.top-content td a:hover {
14
- color: #ff5c28 !important;
15
- }
16
- </style>
17
  </head>
18
 
19
  <body
20
- style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
- @media only screen {
23
- html {
24
- min-height: 100%;
25
- background: #f3f3f3;
26
- }
27
- }
28
-
29
- @media only screen and (max-width: 596px) {
30
- .small-float-center {
31
- margin: 0 auto !important;
32
- float: none !important;
33
- text-align: center !important;
34
- }
35
-
36
- .small-text-center {
37
- text-align: center !important;
38
- }
39
-
40
- .small-text-left {
41
- text-align: left !important;
42
- }
43
-
44
- .small-text-right {
45
- text-align: right !important;
46
- }
47
- }
48
-
49
- @media only screen and (max-width: 596px) {
50
- .hide-for-large {
51
- display: block !important;
52
- width: auto !important;
53
- overflow: visible !important;
54
- max-height: none !important;
55
- font-size: inherit !important;
56
- line-height: inherit !important;
57
- }
58
- }
59
-
60
- @media only screen and (max-width: 596px) {
61
- table.body table.container .hide-for-large,
62
- table.body table.container .row.hide-for-large {
63
- display: table !important;
64
- width: 100% !important;
65
- }
66
- }
67
-
68
- @media only screen and (max-width: 596px) {
69
- table.body table.container .callout-inner.hide-for-large {
70
- display: table-cell !important;
71
- width: 100% !important;
72
- }
73
- }
74
-
75
- @media only screen and (max-width: 596px) {
76
- table.body table.container .show-for-large {
77
- display: none !important;
78
- width: 0;
79
- mso-hide: all;
80
- overflow: hidden;
81
- }
82
- }
83
-
84
- @media only screen and (max-width: 596px) {
85
- table.body img {
86
- width: auto;
87
- height: auto;
88
- }
89
-
90
- table.body center {
91
- min-width: 0 !important;
92
- }
93
-
94
- table.body .container {
95
- width: 95% !important;
96
- }
97
-
98
- table.body .columns,
99
- table.body .column {
100
- height: auto !important;
101
- -moz-box-sizing: border-box;
102
- -webkit-box-sizing: border-box;
103
- box-sizing: border-box;
104
- padding-left: 16px !important;
105
- padding-right: 16px !important;
106
- }
107
-
108
- table.body .columns .column,
109
- table.body .columns .columns,
110
- table.body .column .column,
111
- table.body .column .columns {
112
- padding-left: 0 !important;
113
- padding-right: 0 !important;
114
- }
115
-
116
- table.body .collapse .columns,
117
- table.body .collapse .column {
118
- padding-left: 0 !important;
119
- padding-right: 0 !important;
120
- }
121
-
122
- td.small-1,
123
- th.small-1 {
124
- display: inline-block !important;
125
- width: 8.33333% !important;
126
- }
127
-
128
- td.small-2,
129
- th.small-2 {
130
- display: inline-block !important;
131
- width: 16.66667% !important;
132
- }
133
-
134
- td.small-3,
135
- th.small-3 {
136
- display: inline-block !important;
137
- width: 25% !important;
138
- }
139
-
140
- td.small-4,
141
- th.small-4 {
142
- display: inline-block !important;
143
- width: 33.33333% !important;
144
- }
145
-
146
- td.small-5,
147
- th.small-5 {
148
- display: inline-block !important;
149
- width: 41.66667% !important;
150
- }
151
-
152
- td.small-6,
153
- th.small-6 {
154
- display: inline-block !important;
155
- width: 50% !important;
156
- }
157
-
158
- td.small-7,
159
- th.small-7 {
160
- display: inline-block !important;
161
- width: 58.33333% !important;
162
- }
163
-
164
- td.small-8,
165
- th.small-8 {
166
- display: inline-block !important;
167
- width: 66.66667% !important;
168
- }
169
-
170
- td.small-9,
171
- th.small-9 {
172
- display: inline-block !important;
173
- width: 75% !important;
174
- }
175
-
176
- td.small-10,
177
- th.small-10 {
178
- display: inline-block !important;
179
- width: 83.33333% !important;
180
- }
181
-
182
- td.small-11,
183
- th.small-11 {
184
- display: inline-block !important;
185
- width: 91.66667% !important;
186
- }
187
-
188
- td.small-12,
189
- th.small-12 {
190
- display: inline-block !important;
191
- width: 100% !important;
192
- }
193
-
194
- .columns td.small-12,
195
- .column td.small-12,
196
- .columns th.small-12,
197
- .column th.small-12 {
198
- display: block !important;
199
- width: 100% !important;
200
- }
201
-
202
- table.body td.small-offset-1,
203
- table.body th.small-offset-1 {
204
- margin-left: 8.33333% !important;
205
- Margin-left: 8.33333% !important;
206
- }
207
-
208
- table.body td.small-offset-2,
209
- table.body th.small-offset-2 {
210
- margin-left: 16.66667% !important;
211
- Margin-left: 16.66667% !important;
212
- }
213
-
214
- table.body td.small-offset-3,
215
- table.body th.small-offset-3 {
216
- margin-left: 25% !important;
217
- Margin-left: 25% !important;
218
- }
219
-
220
- table.body td.small-offset-4,
221
- table.body th.small-offset-4 {
222
- margin-left: 33.33333% !important;
223
- Margin-left: 33.33333% !important;
224
- }
225
-
226
- table.body td.small-offset-5,
227
- table.body th.small-offset-5 {
228
- margin-left: 41.66667% !important;
229
- Margin-left: 41.66667% !important;
230
- }
231
-
232
- table.body td.small-offset-6,
233
- table.body th.small-offset-6 {
234
- margin-left: 50% !important;
235
- Margin-left: 50% !important;
236
- }
237
-
238
- table.body td.small-offset-7,
239
- table.body th.small-offset-7 {
240
- margin-left: 58.33333% !important;
241
- Margin-left: 58.33333% !important;
242
- }
243
-
244
- table.body td.small-offset-8,
245
- table.body th.small-offset-8 {
246
- margin-left: 66.66667% !important;
247
- Margin-left: 66.66667% !important;
248
- }
249
-
250
- table.body td.small-offset-9,
251
- table.body th.small-offset-9 {
252
- margin-left: 75% !important;
253
- Margin-left: 75% !important;
254
- }
255
-
256
- table.body td.small-offset-10,
257
- table.body th.small-offset-10 {
258
- margin-left: 83.33333% !important;
259
- Margin-left: 83.33333% !important;
260
- }
261
-
262
- table.body td.small-offset-11,
263
- table.body th.small-offset-11 {
264
- margin-left: 91.66667% !important;
265
- Margin-left: 91.66667% !important;
266
- }
267
-
268
- table.body table.columns td.expander,
269
- table.body table.columns th.expander {
270
- display: none !important;
271
- }
272
-
273
- table.body .right-text-pad,
274
- table.body .text-pad-right {
275
- padding-left: 10px !important;
276
- }
277
-
278
- table.body .left-text-pad,
279
- table.body .text-pad-left {
280
- padding-right: 10px !important;
281
- }
282
-
283
- table.menu {
284
- width: 100% !important;
285
- }
286
-
287
- table.menu td,
288
- table.menu th {
289
- width: auto !important;
290
- display: inline-block !important;
291
- }
292
-
293
- table.menu.vertical td,
294
- table.menu.vertical th,
295
- table.menu.small-vertical td,
296
- table.menu.small-vertical th {
297
- display: block !important;
298
- }
299
-
300
- table.menu[align="center"] {
301
- width: auto !important;
302
- }
303
-
304
- table.button.small-expand,
305
- table.button.small-expanded {
306
- width: 100% !important;
307
- }
308
-
309
- table.button.small-expand table,
310
- table.button.small-expanded table {
311
- width: 100%;
312
- }
313
-
314
- table.button.small-expand table a,
315
- table.button.small-expanded table a {
316
- text-align: center !important;
317
- width: 100% !important;
318
- padding-left: 0 !important;
319
- padding-right: 0 !important;
320
- }
321
-
322
- table.button.small-expand center,
323
- table.button.small-expanded center {
324
- min-width: 0;
325
- }
326
- }
327
-
328
- @media screen and (max-width: 596px) {
329
- /* results list */
330
- table.results-list thead th {
331
- line-height: 34px !important;
332
- }
333
-
334
- /* top */
335
- table.top-content td {
336
- text-align: center !important;
337
- }
338
-
339
- /* related */
340
- table.related table.related-items .columns {
341
- padding-right: 0 !important;
342
- padding-bottom: 15px !important;
343
- padding-left: 0 !important;
344
- }
345
-
346
- table.related table.related-items .columns.last {
347
- padding-bottom: 0 !important;
348
- }
349
-
350
- table.related a.related-item .plugin-info {
351
- vertical-align: middle !important;
352
- }
353
-
354
- /* company info */
355
- table.company-info .columns {
356
- padding-right: 0 !important;
357
- padding-left: 0 !important;
358
- }
359
-
360
- table.company-info .columns.last {
361
- padding: 15px 0 0 !important;
362
- }
363
-
364
- table.company-info .logo,
365
- table.company-info .logo-link,
366
- table.company-info .logo img {
367
- text-align: left !important;
368
- }
369
- }
370
-
371
- @media screen and (max-width: 540px) {
372
- /* hero */
373
- table.hero table.hero-content {
374
- width: 100%;
375
- }
376
-
377
- table.hero td.hero-title h1,
378
- table.hero td.hero-title h2 {
379
- padding: 0 !important;
380
- text-align: center !important;
381
- }
382
-
383
- table.hero td.hero-image {
384
- display: none;
385
- }
386
- }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
- <tbody>
392
- <tr style="padding: 0; text-align: left; vertical-align: top;">
393
- <td class="center" align="center" valign="top"
394
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
-
396
- <center style="min-width: 600px; width: 100%;">
397
-
398
- <table class="container"
399
- style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
- <tbody>
401
- <tr style="padding: 0; text-align: left; vertical-align: top;">
402
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
- <table class="wrapper hero" align="left"
404
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
- <tbody>
406
- <tr style="padding: 0; text-align: left; vertical-align: top;">
407
- <td class="wrapper-inner hero-inner"
408
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
-
410
- <table class="hero-content" align="left"
411
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
- <tbody>
413
- <tr style="padding: 0; text-align: left; vertical-align: top;">
414
- <td class="hero-title"
415
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
- <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php esc_html_e( "Protected By", "defender-security" ) ?></h2>
418
- <h1 class="plugin-brand"
419
- style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php esc_html_e( "Defender!", "defender-security" ) ?></h1>
421
- </td>
422
- <td class="hero-image"
423
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
- <a href="https://premium.wpmudev.org/"
425
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
- alt="Defender"
428
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
- </td>
430
- </tr>
431
- </tbody>
432
- </table>
433
- <!-- end hero-content -->
434
-
435
- </td>
436
- </tr>
437
- </tbody>
438
- </table>
439
- <!-- end hero -->
440
-
441
- <table class="wrapper main" align="center"
442
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
- <tbody>
444
- <tr style="padding: 0; text-align: left; vertical-align: top;">
445
- <td class="wrapper-inner main-inner"
446
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
-
448
- <table class="main-intro"
449
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
- <tbody>
451
- <tr style="padding: 0; text-align: left; vertical-align: top;">
452
- <td class="main-intro-content"
453
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
- <h3 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 32px; font-weight: normal; line-height: 32px; margin: 0; margin-bottom: 0; padding: 0 0 28px; text-align: left; word-wrap: normal;">
455
  <?php printf( __( "Hi %s", "defender-security" ), $admin ) ?>
456
- ,</h3>
457
  <?php $setting = \WP_Defender\Module\IP_Lockout\Model\Settings::instance() ?>
458
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
459
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
460
  <?php printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> 404 requests for the file <strong>%s</strong>. They have been banned permanently.", "defender-security" ), $ip, network_site_url(), $setting->detect_404_threshold, $uri, $setting->detect_404_lockout_duration ) ?>
461
- </p>
462
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
463
- <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ?>
464
- .</p>
465
- </td>
466
- </tr>
467
- </tbody>
468
- </table>
469
- <!-- end main-intro -->
470
-
471
- <table class="main-signature"
472
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
- <tbody>
474
- <tr style="padding: 0; text-align: left; vertical-align: top;">
475
- <td class="main-signature-content"
476
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
477
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
478
  <?php esc_html_e( "Stay vigilant.", "defender-security" ) ?></p>
479
- <p class="last-item"
480
- style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
481
- <strong><?php esc_html_e( "WP Defender", "defender-security" ) ?></strong>
482
- <br>
483
  <?php esc_html_e( "Security Hero", "defender-security" ) ?>
484
- <br/>
485
  <?php esc_html_e( "WPMU DEV", "defender-security" ) ?>
486
- </p>
487
- </td>
488
- </tr>
489
- </tbody>
490
- </table>
491
- <!-- end main-signature -->
492
-
493
- </td>
494
- </tr>
495
- </tbody>
496
- </table>
497
- <!-- end main -->
498
-
499
- <table class="related" align="center"
500
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
501
- <tbody>
502
- <tr style="padding: 0; text-align: left; vertical-align: top;">
503
- <td class="related-inner"
504
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
505
- <table
506
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
507
- <tbody>
508
- <tr style="padding: 0; text-align: left; vertical-align: top;">
509
- <td class="related-items-title brand" align="left"
510
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
511
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
512
- </td>
513
- </tr>
514
- <tr style="padding: 0; text-align: left; vertical-align: top;">
515
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
516
- <table class="related-items row collapse" align="center"
517
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
518
- <tbody>
519
- <tr style="padding: 0; text-align: left; vertical-align: top;">
520
- <th class="small-12 large-6 columns first" align="left"
521
- valign="top"
522
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
523
- <a class="related-item plugin-link"
524
- href="https://premium.wpmudev.org/project/wp-hummingbird/"
525
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
526
- <img
527
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
528
- alt="Hummingbird" class="plugin-image"
529
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
530
- <span class="plugin-info"
531
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
532
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
533
  <span class="plugin-title hummingbird"
534
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
535
  </span>
536
- </a>
537
- </th>
538
- <th class="small-12 large-6 columns last" align="left"
539
- valign="top"
540
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
541
- <a class="related-item plugin-link"
542
- href="https://premium.wpmudev.org/project/snapshot/"
543
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
544
- <img
545
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
546
- alt="Snapshot"
547
- class="plugin-image"
548
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
549
- <span class="plugin-info"
550
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
551
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
552
  <span class="plugin-title snapshot"
553
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
554
  </span>
555
- </a>
556
- </th>
557
- <th class="expander"
558
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
559
- </tr>
560
- </tbody>
561
- </table>
562
- </td>
563
- </tr>
564
- </tbody>
565
- </table>
566
- <!-- end related-inner -->
567
- </td>
568
- </tr>
569
- </tbody>
570
- </table>
571
- <!-- end related -->
572
-
573
- <!-- Preferences -->
574
- <table class="email-preferences" align="center" valign="middle"
575
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
576
- <tbody>
577
- <tr style="padding: 0; text-align: center; vertical-align: top;">
578
- <td class="email-preferences-inner"
579
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
580
- <table class="email-preferences-content row collapse" align="center" valign="top"
581
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
582
- <tbody>
583
- <tr style="padding: 0; text-align: center; vertical-align: top;">
584
- <th class="small-12 large-8 columns first copy" align="center"
585
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
586
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
587
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ?>
588
- </p>
589
- </th>
590
- </tr>
591
- </tbody>
592
- </table>
593
- </td>
594
- </tr>
595
- </tbody>
596
- </table>
597
- <!-- End Preferences -->
598
-
599
- <table class="company-info" align="left" valign="middle"
600
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
601
- <tbody>
602
- <tr style="padding: 0; text-align: left; vertical-align: top;">
603
- <td class="company-info-inner"
604
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
605
- <table class="company-info-content row collapse" align="left" valign="top"
606
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
607
- <tbody>
608
- <tr style="padding: 0; text-align: left; vertical-align: top;">
609
- <th class="small-12 large-8 columns first copy" align="left"
610
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
611
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
612
- Copyright © Incsub, All rights reserved.</p>
613
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
614
- Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
615
- </th>
616
- <th class="small-12 large-4 columns last logo" align="right"
617
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
618
- <a href="https://premium.wpmudev.org" class="logo-link"
619
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
620
- <img
621
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
622
- alt="WPMU DEV"
623
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
624
- </a>
625
- </th>
626
- <th class="expander"
627
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
628
- </tr>
629
- </tbody>
630
- </table>
631
- </td>
632
- </tr>
633
- </tbody>
634
- </table>
635
- <!-- end company-info -->
636
-
637
- <table class="wrapper social" align="center"
638
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
639
- <tbody>
640
- <tr style="padding: 0; text-align: left; vertical-align: top;">
641
- <td class="wrapper-inner social-inner"
642
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
643
-
644
- <table class="social-content" align="center"
645
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
646
- <tbody>
647
- <tr style="padding: 0; text-align: left; vertical-align: top;">
648
- <td class="social-content-inner"
649
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
650
- <a href="https://plus.google.com/+wpmuorg/" target="_blank"
651
- class="gplus"
652
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
653
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
654
- alt="WPMU DEV on Google+"
655
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
656
- <a href="https://twitter.com/wpmudev" target="_blank"
657
- class="twitter"
658
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
659
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
660
- alt="WPMU DEV on Twitter"
661
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
662
- <a href="https://www.facebook.com/wpmudev" target="_blank"
663
- class="facebook"
664
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
665
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
666
- alt="WPMU DEV on Facebook"
667
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
668
- </td>
669
- </tr>
670
- </tbody>
671
- </table>
672
- <!-- end social-content -->
673
-
674
- </td>
675
- </tr>
676
- </tbody>
677
- </table>
678
- <!-- end top -->
679
- </td>
680
- </tr>
681
- </tbody>
682
- </table>
683
- <!-- end main container -->
684
-
685
- </center>
686
-
687
- </td>
688
- </tr>
689
- </tbody>
 
690
  </table>
691
  <!-- end body -->
692
  </body>
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
+ <meta name="viewport" content="width=device-width">
7
+ <title><?php _e( "New 404 Lockout", "defender-security" ) ?></title>
8
+ <style>
9
+ a.plugin-brand:hover {
10
+ color: #e23717 !important;
11
+ }
12
+
13
+ table.top-content td a:hover {
14
+ color: #ff5c28 !important;
15
+ }
16
+ </style>
17
  </head>
18
 
19
  <body
20
+ style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
+ @media only screen {
23
+ html {
24
+ min-height: 100%;
25
+ background: #f3f3f3;
26
+ }
27
+ }
28
+
29
+ @media only screen and (max-width: 596px) {
30
+ .small-float-center {
31
+ margin: 0 auto !important;
32
+ float: none !important;
33
+ text-align: center !important;
34
+ }
35
+
36
+ .small-text-center {
37
+ text-align: center !important;
38
+ }
39
+
40
+ .small-text-left {
41
+ text-align: left !important;
42
+ }
43
+
44
+ .small-text-right {
45
+ text-align: right !important;
46
+ }
47
+ }
48
+
49
+ @media only screen and (max-width: 596px) {
50
+ .hide-for-large {
51
+ display: block !important;
52
+ width: auto !important;
53
+ overflow: visible !important;
54
+ max-height: none !important;
55
+ font-size: inherit !important;
56
+ line-height: inherit !important;
57
+ }
58
+ }
59
+
60
+ @media only screen and (max-width: 596px) {
61
+ table.body table.container .hide-for-large,
62
+ table.body table.container .row.hide-for-large {
63
+ display: table !important;
64
+ width: 100% !important;
65
+ }
66
+ }
67
+
68
+ @media only screen and (max-width: 596px) {
69
+ table.body table.container .callout-inner.hide-for-large {
70
+ display: table-cell !important;
71
+ width: 100% !important;
72
+ }
73
+ }
74
+
75
+ @media only screen and (max-width: 596px) {
76
+ table.body table.container .show-for-large {
77
+ display: none !important;
78
+ width: 0;
79
+ mso-hide: all;
80
+ overflow: hidden;
81
+ }
82
+ }
83
+
84
+ @media only screen and (max-width: 596px) {
85
+ table.body img {
86
+ width: auto;
87
+ height: auto;
88
+ }
89
+
90
+ table.body center {
91
+ min-width: 0 !important;
92
+ }
93
+
94
+ table.body .container {
95
+ width: 95% !important;
96
+ }
97
+
98
+ table.body .columns,
99
+ table.body .column {
100
+ height: auto !important;
101
+ -moz-box-sizing: border-box;
102
+ -webkit-box-sizing: border-box;
103
+ box-sizing: border-box;
104
+ padding-left: 16px !important;
105
+ padding-right: 16px !important;
106
+ }
107
+
108
+ table.body .columns .column,
109
+ table.body .columns .columns,
110
+ table.body .column .column,
111
+ table.body .column .columns {
112
+ padding-left: 0 !important;
113
+ padding-right: 0 !important;
114
+ }
115
+
116
+ table.body .collapse .columns,
117
+ table.body .collapse .column {
118
+ padding-left: 0 !important;
119
+ padding-right: 0 !important;
120
+ }
121
+
122
+ td.small-1,
123
+ th.small-1 {
124
+ display: inline-block !important;
125
+ width: 8.33333% !important;
126
+ }
127
+
128
+ td.small-2,
129
+ th.small-2 {
130
+ display: inline-block !important;
131
+ width: 16.66667% !important;
132
+ }
133
+
134
+ td.small-3,
135
+ th.small-3 {
136
+ display: inline-block !important;
137
+ width: 25% !important;
138
+ }
139
+
140
+ td.small-4,
141
+ th.small-4 {
142
+ display: inline-block !important;
143
+ width: 33.33333% !important;
144
+ }
145
+
146
+ td.small-5,
147
+ th.small-5 {
148
+ display: inline-block !important;
149
+ width: 41.66667% !important;
150
+ }
151
+
152
+ td.small-6,
153
+ th.small-6 {
154
+ display: inline-block !important;
155
+ width: 50% !important;
156
+ }
157
+
158
+ td.small-7,
159
+ th.small-7 {
160
+ display: inline-block !important;
161
+ width: 58.33333% !important;
162
+ }
163
+
164
+ td.small-8,
165
+ th.small-8 {
166
+ display: inline-block !important;
167
+ width: 66.66667% !important;
168
+ }
169
+
170
+ td.small-9,
171
+ th.small-9 {
172
+ display: inline-block !important;
173
+ width: 75% !important;
174
+ }
175
+
176
+ td.small-10,
177
+ th.small-10 {
178
+ display: inline-block !important;
179
+ width: 83.33333% !important;
180
+ }
181
+
182
+ td.small-11,
183
+ th.small-11 {
184
+ display: inline-block !important;
185
+ width: 91.66667% !important;
186
+ }
187
+
188
+ td.small-12,
189
+ th.small-12 {
190
+ display: inline-block !important;
191
+ width: 100% !important;
192
+ }
193
+
194
+ .columns td.small-12,
195
+ .column td.small-12,
196
+ .columns th.small-12,
197
+ .column th.small-12 {
198
+ display: block !important;
199
+ width: 100% !important;
200
+ }
201
+
202
+ table.body td.small-offset-1,
203
+ table.body th.small-offset-1 {
204
+ margin-left: 8.33333% !important;
205
+ Margin-left: 8.33333% !important;
206
+ }
207
+
208
+ table.body td.small-offset-2,
209
+ table.body th.small-offset-2 {
210
+ margin-left: 16.66667% !important;
211
+ Margin-left: 16.66667% !important;
212
+ }
213
+
214
+ table.body td.small-offset-3,
215
+ table.body th.small-offset-3 {
216
+ margin-left: 25% !important;
217
+ Margin-left: 25% !important;
218
+ }
219
+
220
+ table.body td.small-offset-4,
221
+ table.body th.small-offset-4 {
222
+ margin-left: 33.33333% !important;
223
+ Margin-left: 33.33333% !important;
224
+ }
225
+
226
+ table.body td.small-offset-5,
227
+ table.body th.small-offset-5 {
228
+ margin-left: 41.66667% !important;
229
+ Margin-left: 41.66667% !important;
230
+ }
231
+
232
+ table.body td.small-offset-6,
233
+ table.body th.small-offset-6 {
234
+ margin-left: 50% !important;
235
+ Margin-left: 50% !important;
236
+ }
237
+
238
+ table.body td.small-offset-7,
239
+ table.body th.small-offset-7 {
240
+ margin-left: 58.33333% !important;
241
+ Margin-left: 58.33333% !important;
242
+ }
243
+
244
+ table.body td.small-offset-8,
245
+ table.body th.small-offset-8 {
246
+ margin-left: 66.66667% !important;
247
+ Margin-left: 66.66667% !important;
248
+ }
249
+
250
+ table.body td.small-offset-9,
251
+ table.body th.small-offset-9 {
252
+ margin-left: 75% !important;
253
+ Margin-left: 75% !important;
254
+ }
255
+
256
+ table.body td.small-offset-10,
257
+ table.body th.small-offset-10 {
258
+ margin-left: 83.33333% !important;
259
+ Margin-left: 83.33333% !important;
260
+ }
261
+
262
+ table.body td.small-offset-11,
263
+ table.body th.small-offset-11 {
264
+ margin-left: 91.66667% !important;
265
+ Margin-left: 91.66667% !important;
266
+ }
267
+
268
+ table.body table.columns td.expander,
269
+ table.body table.columns th.expander {
270
+ display: none !important;
271
+ }
272
+
273
+ table.body .right-text-pad,
274
+ table.body .text-pad-right {
275
+ padding-left: 10px !important;
276
+ }
277
+
278
+ table.body .left-text-pad,
279
+ table.body .text-pad-left {
280
+ padding-right: 10px !important;
281
+ }
282
+
283
+ table.menu {
284
+ width: 100% !important;
285
+ }
286
+
287
+ table.menu td,
288
+ table.menu th {
289
+ width: auto !important;
290
+ display: inline-block !important;
291
+ }
292
+
293
+ table.menu.vertical td,
294
+ table.menu.vertical th,
295
+ table.menu.small-vertical td,
296
+ table.menu.small-vertical th {
297
+ display: block !important;
298
+ }
299
+
300
+ table.menu[align="center"] {
301
+ width: auto !important;
302
+ }
303
+
304
+ table.button.small-expand,
305
+ table.button.small-expanded {
306
+ width: 100% !important;
307
+ }
308
+
309
+ table.button.small-expand table,
310
+ table.button.small-expanded table {
311
+ width: 100%;
312
+ }
313
+
314
+ table.button.small-expand table a,
315
+ table.button.small-expanded table a {
316
+ text-align: center !important;
317
+ width: 100% !important;
318
+ padding-left: 0 !important;
319
+ padding-right: 0 !important;
320
+ }
321
+
322
+ table.button.small-expand center,
323
+ table.button.small-expanded center {
324
+ min-width: 0;
325
+ }
326
+ }
327
+
328
+ @media screen and (max-width: 596px) {
329
+ /* results list */
330
+ table.results-list thead th {
331
+ line-height: 34px !important;
332
+ }
333
+
334
+ /* top */
335
+ table.top-content td {
336
+ text-align: center !important;
337
+ }
338
+
339
+ /* related */
340
+ table.related table.related-items .columns {
341
+ padding-right: 0 !important;
342
+ padding-bottom: 15px !important;
343
+ padding-left: 0 !important;
344
+ }
345
+
346
+ table.related table.related-items .columns.last {
347
+ padding-bottom: 0 !important;
348
+ }
349
+
350
+ table.related a.related-item .plugin-info {
351
+ vertical-align: middle !important;
352
+ }
353
+
354
+ /* company info */
355
+ table.company-info .columns {
356
+ padding-right: 0 !important;
357
+ padding-left: 0 !important;
358
+ }
359
+
360
+ table.company-info .columns.last {
361
+ padding: 15px 0 0 !important;
362
+ }
363
+
364
+ table.company-info .logo,
365
+ table.company-info .logo-link,
366
+ table.company-info .logo img {
367
+ text-align: left !important;
368
+ }
369
+ }
370
+
371
+ @media screen and (max-width: 540px) {
372
+ /* hero */
373
+ table.hero table.hero-content {
374
+ width: 100%;
375
+ }
376
+
377
+ table.hero td.hero-title h1,
378
+ table.hero td.hero-title h2 {
379
+ padding: 0 !important;
380
+ text-align: center !important;
381
+ }
382
+
383
+ table.hero td.hero-image {
384
+ display: none;
385
+ }
386
+ }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
+ <tbody>
392
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
393
+ <td class="center" align="center" valign="top"
394
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
+
396
+ <center style="min-width: 600px; width: 100%;">
397
+
398
+ <table class="container"
399
+ style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
+ <tbody>
401
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
402
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
+ <table class="wrapper hero" align="left"
404
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
+ <tbody>
406
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
407
+ <td class="wrapper-inner hero-inner"
408
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
+
410
+ <table class="hero-content" align="left"
411
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
+ <tbody>
413
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
414
+ <td class="hero-title"
415
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
+ <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php esc_html_e( "Protected By", "defender-security" ) ?></h2>
418
+ <h1 class="plugin-brand"
419
+ style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php esc_html_e( "Defender!", "defender-security" ) ?></h1>
421
+ </td>
422
+ <td class="hero-image"
423
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
+ <a href="https://premium.wpmudev.org/"
425
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
+ alt="Defender"
428
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
+ </td>
430
+ </tr>
431
+ </tbody>
432
+ </table>
433
+ <!-- end hero-content -->
434
+
435
+ </td>
436
+ </tr>
437
+ </tbody>
438
+ </table>
439
+ <!-- end hero -->
440
+
441
+ <table class="wrapper main" align="center"
442
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
+ <tbody>
444
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
445
+ <td class="wrapper-inner main-inner"
446
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
+
448
+ <table class="main-intro"
449
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
+ <tbody>
451
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
452
+ <td class="main-intro-content"
453
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
+ <h3 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 32px; font-weight: normal; line-height: 32px; margin: 0; margin-bottom: 0; padding: 0 0 28px; text-align: left; word-wrap: normal;">
455
  <?php printf( __( "Hi %s", "defender-security" ), $admin ) ?>
456
+ ,</h3>
457
  <?php $setting = \WP_Defender\Module\IP_Lockout\Model\Settings::instance() ?>
458
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
459
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
460
  <?php printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> 404 requests for the file <strong>%s</strong>. They have been banned permanently.", "defender-security" ), $ip, network_site_url(), $setting->detect_404_threshold, $uri, $setting->detect_404_lockout_duration ) ?>
461
+ </p>
462
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
463
+ <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ) ?>
464
+ .</p>
465
+ </td>
466
+ </tr>
467
+ </tbody>
468
+ </table>
469
+ <!-- end main-intro -->
470
+
471
+ <table class="main-signature"
472
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
+ <tbody>
474
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
475
+ <td class="main-signature-content"
476
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
477
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
478
  <?php esc_html_e( "Stay vigilant.", "defender-security" ) ?></p>
479
+ <p class="last-item"
480
+ style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
481
+ <strong><?php esc_html_e( "WP Defender", "defender-security" ) ?></strong>
482
+ <br>
483
  <?php esc_html_e( "Security Hero", "defender-security" ) ?>
484
+ <br/>
485
  <?php esc_html_e( "WPMU DEV", "defender-security" ) ?>
486
+ </p>
487
+ </td>
488
+ </tr>
489
+ </tbody>
490
+ </table>
491
+ <!-- end main-signature -->
492
+
493
+ </td>
494
+ </tr>
495
+ </tbody>
496
+ </table>
497
+ <!-- end main -->
498
+
499
+ <table class="related" align="center"
500
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
501
+ <tbody>
502
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
503
+ <td class="related-inner"
504
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
505
+ <table
506
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
507
+ <tbody>
508
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
509
+ <td class="related-items-title brand" align="left"
510
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
511
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
512
+ </td>
513
+ </tr>
514
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
515
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
516
+ <table class="related-items row collapse" align="center"
517
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
518
+ <tbody>
519
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
520
+ <th class="small-12 large-6 columns first" align="left"
521
+ valign="top"
522
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
523
+ <a class="related-item plugin-link"
524
+ href="https://premium.wpmudev.org/project/wp-hummingbird/"
525
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
526
+ <img
527
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
528
+ alt="Hummingbird" class="plugin-image"
529
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
530
+ <span class="plugin-info"
531
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
532
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
533
  <span class="plugin-title hummingbird"
534
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
535
  </span>
536
+ </a>
537
+ </th>
538
+ <th class="small-12 large-6 columns last" align="left"
539
+ valign="top"
540
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
541
+ <a class="related-item plugin-link"
542
+ href="https://premium.wpmudev.org/project/snapshot/"
543
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
544
+ <img
545
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
546
+ alt="Snapshot"
547
+ class="plugin-image"
548
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
549
+ <span class="plugin-info"
550
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
551
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
552
  <span class="plugin-title snapshot"
553
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
554
  </span>
555
+ </a>
556
+ </th>
557
+ <th class="expander"
558
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
559
+ </tr>
560
+ </tbody>
561
+ </table>
562
+ </td>
563
+ </tr>
564
+ </tbody>
565
+ </table>
566
+ <!-- end related-inner -->
567
+ </td>
568
+ </tr>
569
+ </tbody>
570
+ </table>
571
+ <!-- end related -->
572
+
573
+ <!-- Preferences -->
574
+ <table class="email-preferences" align="center" valign="middle"
575
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
576
+ <tbody>
577
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
578
+ <td class="email-preferences-inner"
579
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
580
+ <table class="email-preferences-content row collapse" align="center"
581
+ valign="top"
582
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
583
+ <tbody>
584
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
585
+ <th class="small-12 large-8 columns first copy" align="center"
586
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
587
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
588
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ) ?>
589
+ </p>
590
+ </th>
591
+ </tr>
592
+ </tbody>
593
+ </table>
594
+ </td>
595
+ </tr>
596
+ </tbody>
597
+ </table>
598
+ <!-- End Preferences -->
599
+
600
+ <table class="company-info" align="left" valign="middle"
601
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
602
+ <tbody>
603
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
604
+ <td class="company-info-inner"
605
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
606
+ <table class="company-info-content row collapse" align="left" valign="top"
607
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
608
+ <tbody>
609
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
610
+ <th class="small-12 large-8 columns first copy" align="left"
611
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
612
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
613
+ Copyright © Incsub, All rights reserved.</p>
614
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
615
+ Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
616
+ </th>
617
+ <th class="small-12 large-4 columns last logo" align="right"
618
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
619
+ <a href="https://premium.wpmudev.org" class="logo-link"
620
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
621
+ <img
622
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
623
+ alt="WPMU DEV"
624
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
625
+ </a>
626
+ </th>
627
+ <th class="expander"
628
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
629
+ </tr>
630
+ </tbody>
631
+ </table>
632
+ </td>
633
+ </tr>
634
+ </tbody>
635
+ </table>
636
+ <!-- end company-info -->
637
+
638
+ <table class="wrapper social" align="center"
639
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
640
+ <tbody>
641
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
642
+ <td class="wrapper-inner social-inner"
643
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
644
+
645
+ <table class="social-content" align="center"
646
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
647
+ <tbody>
648
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
649
+ <td class="social-content-inner"
650
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
651
+ <a href="https://plus.google.com/+wpmuorg/" target="_blank"
652
+ class="gplus"
653
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
654
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
655
+ alt="WPMU DEV on Google+"
656
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
657
+ <a href="https://twitter.com/wpmudev" target="_blank"
658
+ class="twitter"
659
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
660
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
661
+ alt="WPMU DEV on Twitter"
662
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
663
+ <a href="https://www.facebook.com/wpmudev" target="_blank"
664
+ class="facebook"
665
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
666
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
667
+ alt="WPMU DEV on Facebook"
668
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
669
+ </td>
670
+ </tr>
671
+ </tbody>
672
+ </table>
673
+ <!-- end social-content -->
674
+
675
+ </td>
676
+ </tr>
677
+ </tbody>
678
+ </table>
679
+ <!-- end top -->
680
+ </td>
681
+ </tr>
682
+ </tbody>
683
+ </table>
684
+ <!-- end main container -->
685
+
686
+ </center>
687
+
688
+ </td>
689
+ </tr>
690
+ </tbody>
691
  </table>
692
  <!-- end body -->
693
  </body>
app/module/ip-lockout/view/emails/404-lockout.php CHANGED
@@ -2,691 +2,692 @@
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
- <meta name="viewport" content="width=device-width">
7
- <title><?php _e( "New 404 Lockout", "defender-security" ) ?></title>
8
- <style>
9
- a.plugin-brand:hover {
10
- color: #e23717 !important;
11
- }
12
-
13
- table.top-content td a:hover {
14
- color: #ff5c28 !important;
15
- }
16
- </style>
17
  </head>
18
 
19
  <body
20
- style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
- @media only screen {
23
- html {
24
- min-height: 100%;
25
- background: #f3f3f3;
26
- }
27
- }
28
-
29
- @media only screen and (max-width: 596px) {
30
- .small-float-center {
31
- margin: 0 auto !important;
32
- float: none !important;
33
- text-align: center !important;
34
- }
35
-
36
- .small-text-center {
37
- text-align: center !important;
38
- }
39
-
40
- .small-text-left {
41
- text-align: left !important;
42
- }
43
-
44
- .small-text-right {
45
- text-align: right !important;
46
- }
47
- }
48
-
49
- @media only screen and (max-width: 596px) {
50
- .hide-for-large {
51
- display: block !important;
52
- width: auto !important;
53
- overflow: visible !important;
54
- max-height: none !important;
55
- font-size: inherit !important;
56
- line-height: inherit !important;
57
- }
58
- }
59
-
60
- @media only screen and (max-width: 596px) {
61
- table.body table.container .hide-for-large,
62
- table.body table.container .row.hide-for-large {
63
- display: table !important;
64
- width: 100% !important;
65
- }
66
- }
67
-
68
- @media only screen and (max-width: 596px) {
69
- table.body table.container .callout-inner.hide-for-large {
70
- display: table-cell !important;
71
- width: 100% !important;
72
- }
73
- }
74
-
75
- @media only screen and (max-width: 596px) {
76
- table.body table.container .show-for-large {
77
- display: none !important;
78
- width: 0;
79
- mso-hide: all;
80
- overflow: hidden;
81
- }
82
- }
83
-
84
- @media only screen and (max-width: 596px) {
85
- table.body img {
86
- width: auto;
87
- height: auto;
88
- }
89
-
90
- table.body center {
91
- min-width: 0 !important;
92
- }
93
-
94
- table.body .container {
95
- width: 95% !important;
96
- }
97
-
98
- table.body .columns,
99
- table.body .column {
100
- height: auto !important;
101
- -moz-box-sizing: border-box;
102
- -webkit-box-sizing: border-box;
103
- box-sizing: border-box;
104
- padding-left: 16px !important;
105
- padding-right: 16px !important;
106
- }
107
-
108
- table.body .columns .column,
109
- table.body .columns .columns,
110
- table.body .column .column,
111
- table.body .column .columns {
112
- padding-left: 0 !important;
113
- padding-right: 0 !important;
114
- }
115
-
116
- table.body .collapse .columns,
117
- table.body .collapse .column {
118
- padding-left: 0 !important;
119
- padding-right: 0 !important;
120
- }
121
-
122
- td.small-1,
123
- th.small-1 {
124
- display: inline-block !important;
125
- width: 8.33333% !important;
126
- }
127
-
128
- td.small-2,
129
- th.small-2 {
130
- display: inline-block !important;
131
- width: 16.66667% !important;
132
- }
133
-
134
- td.small-3,
135
- th.small-3 {
136
- display: inline-block !important;
137
- width: 25% !important;
138
- }
139
-
140
- td.small-4,
141
- th.small-4 {
142
- display: inline-block !important;
143
- width: 33.33333% !important;
144
- }
145
-
146
- td.small-5,
147
- th.small-5 {
148
- display: inline-block !important;
149
- width: 41.66667% !important;
150
- }
151
-
152
- td.small-6,
153
- th.small-6 {
154
- display: inline-block !important;
155
- width: 50% !important;
156
- }
157
-
158
- td.small-7,
159
- th.small-7 {
160
- display: inline-block !important;
161
- width: 58.33333% !important;
162
- }
163
-
164
- td.small-8,
165
- th.small-8 {
166
- display: inline-block !important;
167
- width: 66.66667% !important;
168
- }
169
-
170
- td.small-9,
171
- th.small-9 {
172
- display: inline-block !important;
173
- width: 75% !important;
174
- }
175
-
176
- td.small-10,
177
- th.small-10 {
178
- display: inline-block !important;
179
- width: 83.33333% !important;
180
- }
181
-
182
- td.small-11,
183
- th.small-11 {
184
- display: inline-block !important;
185
- width: 91.66667% !important;
186
- }
187
-
188
- td.small-12,
189
- th.small-12 {
190
- display: inline-block !important;
191
- width: 100% !important;
192
- }
193
-
194
- .columns td.small-12,
195
- .column td.small-12,
196
- .columns th.small-12,
197
- .column th.small-12 {
198
- display: block !important;
199
- width: 100% !important;
200
- }
201
-
202
- table.body td.small-offset-1,
203
- table.body th.small-offset-1 {
204
- margin-left: 8.33333% !important;
205
- Margin-left: 8.33333% !important;
206
- }
207
-
208
- table.body td.small-offset-2,
209
- table.body th.small-offset-2 {
210
- margin-left: 16.66667% !important;
211
- Margin-left: 16.66667% !important;
212
- }
213
-
214
- table.body td.small-offset-3,
215
- table.body th.small-offset-3 {
216
- margin-left: 25% !important;
217
- Margin-left: 25% !important;
218
- }
219
-
220
- table.body td.small-offset-4,
221
- table.body th.small-offset-4 {
222
- margin-left: 33.33333% !important;
223
- Margin-left: 33.33333% !important;
224
- }
225
-
226
- table.body td.small-offset-5,
227
- table.body th.small-offset-5 {
228
- margin-left: 41.66667% !important;
229
- Margin-left: 41.66667% !important;
230
- }
231
-
232
- table.body td.small-offset-6,
233
- table.body th.small-offset-6 {
234
- margin-left: 50% !important;
235
- Margin-left: 50% !important;
236
- }
237
-
238
- table.body td.small-offset-7,
239
- table.body th.small-offset-7 {
240
- margin-left: 58.33333% !important;
241
- Margin-left: 58.33333% !important;
242
- }
243
-
244
- table.body td.small-offset-8,
245
- table.body th.small-offset-8 {
246
- margin-left: 66.66667% !important;
247
- Margin-left: 66.66667% !important;
248
- }
249
-
250
- table.body td.small-offset-9,
251
- table.body th.small-offset-9 {
252
- margin-left: 75% !important;
253
- Margin-left: 75% !important;
254
- }
255
-
256
- table.body td.small-offset-10,
257
- table.body th.small-offset-10 {
258
- margin-left: 83.33333% !important;
259
- Margin-left: 83.33333% !important;
260
- }
261
-
262
- table.body td.small-offset-11,
263
- table.body th.small-offset-11 {
264
- margin-left: 91.66667% !important;
265
- Margin-left: 91.66667% !important;
266
- }
267
-
268
- table.body table.columns td.expander,
269
- table.body table.columns th.expander {
270
- display: none !important;
271
- }
272
-
273
- table.body .right-text-pad,
274
- table.body .text-pad-right {
275
- padding-left: 10px !important;
276
- }
277
-
278
- table.body .left-text-pad,
279
- table.body .text-pad-left {
280
- padding-right: 10px !important;
281
- }
282
-
283
- table.menu {
284
- width: 100% !important;
285
- }
286
-
287
- table.menu td,
288
- table.menu th {
289
- width: auto !important;
290
- display: inline-block !important;
291
- }
292
-
293
- table.menu.vertical td,
294
- table.menu.vertical th,
295
- table.menu.small-vertical td,
296
- table.menu.small-vertical th {
297
- display: block !important;
298
- }
299
-
300
- table.menu[align="center"] {
301
- width: auto !important;
302
- }
303
-
304
- table.button.small-expand,
305
- table.button.small-expanded {
306
- width: 100% !important;
307
- }
308
-
309
- table.button.small-expand table,
310
- table.button.small-expanded table {
311
- width: 100%;
312
- }
313
-
314
- table.button.small-expand table a,
315
- table.button.small-expanded table a {
316
- text-align: center !important;
317
- width: 100% !important;
318
- padding-left: 0 !important;
319
- padding-right: 0 !important;
320
- }
321
-
322
- table.button.small-expand center,
323
- table.button.small-expanded center {
324
- min-width: 0;
325
- }
326
- }
327
-
328
- @media screen and (max-width: 596px) {
329
- /* results list */
330
- table.results-list thead th {
331
- line-height: 34px !important;
332
- }
333
-
334
- /* top */
335
- table.top-content td {
336
- text-align: center !important;
337
- }
338
-
339
- /* related */
340
- table.related table.related-items .columns {
341
- padding-right: 0 !important;
342
- padding-bottom: 15px !important;
343
- padding-left: 0 !important;
344
- }
345
-
346
- table.related table.related-items .columns.last {
347
- padding-bottom: 0 !important;
348
- }
349
-
350
- table.related a.related-item .plugin-info {
351
- vertical-align: middle !important;
352
- }
353
-
354
- /* company info */
355
- table.company-info .columns {
356
- padding-right: 0 !important;
357
- padding-left: 0 !important;
358
- }
359
-
360
- table.company-info .columns.last {
361
- padding: 15px 0 0 !important;
362
- }
363
-
364
- table.company-info .logo,
365
- table.company-info .logo-link,
366
- table.company-info .logo img {
367
- text-align: left !important;
368
- }
369
- }
370
-
371
- @media screen and (max-width: 540px) {
372
- /* hero */
373
- table.hero table.hero-content {
374
- width: 100%;
375
- }
376
-
377
- table.hero td.hero-title h1,
378
- table.hero td.hero-title h2 {
379
- padding: 0 !important;
380
- text-align: center !important;
381
- }
382
-
383
- table.hero td.hero-image {
384
- display: none;
385
- }
386
- }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
- <tbody>
392
- <tr style="padding: 0; text-align: left; vertical-align: top;">
393
- <td class="center" align="center" valign="top"
394
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
-
396
- <center style="min-width: 600px; width: 100%;">
397
-
398
- <table class="container"
399
- style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
- <tbody>
401
- <tr style="padding: 0; text-align: left; vertical-align: top;">
402
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
- <table class="wrapper hero" align="left"
404
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
- <tbody>
406
- <tr style="padding: 0; text-align: left; vertical-align: top;">
407
- <td class="wrapper-inner hero-inner"
408
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
-
410
- <table class="hero-content" align="left"
411
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
- <tbody>
413
- <tr style="padding: 0; text-align: left; vertical-align: top;">
414
- <td class="hero-title"
415
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
- <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php esc_html_e( "Protected By", "defender-security" ) ?></h2>
418
- <h1 class="plugin-brand"
419
- style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php esc_html_e( "Defender!", "defender-security" ) ?></h1>
421
- </td>
422
- <td class="hero-image"
423
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
- <a href="https://premium.wpmudev.org/"
425
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
- alt="Defender"
428
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
- </td>
430
- </tr>
431
- </tbody>
432
- </table>
433
- <!-- end hero-content -->
434
-
435
- </td>
436
- </tr>
437
- </tbody>
438
- </table>
439
- <!-- end hero -->
440
-
441
- <table class="wrapper main" align="center"
442
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
- <tbody>
444
- <tr style="padding: 0; text-align: left; vertical-align: top;">
445
- <td class="wrapper-inner main-inner"
446
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
-
448
- <table class="main-intro"
449
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
- <tbody>
451
- <tr style="padding: 0; text-align: left; vertical-align: top;">
452
- <td class="main-intro-content"
453
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
- <h3 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 32px; font-weight: normal; line-height: 32px; margin: 0; margin-bottom: 0; padding: 0 0 28px; text-align: left; word-wrap: normal;">
455
  <?php printf( __( "Hi %s", "defender-security" ), $admin ) ?>
456
- ,</h3>
457
  <?php $setting = \WP_Defender\Module\IP_Lockout\Model\Settings::instance() ?>
458
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
459
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
460
  <?php printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> 404 requests for the file <strong>%s</strong>. They have been locked out for <strong>%s seconds.</strong>", "defender-security" ), $ip, network_site_url(), $setting->detect_404_threshold, $uri, $setting->detect_404_lockout_duration ) ?>
461
- </p>
462
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
463
- <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ?>
464
- .</p>
465
- </td>
466
- </tr>
467
- </tbody>
468
- </table>
469
- <!-- end main-intro -->
470
-
471
- <table class="main-signature"
472
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
- <tbody>
474
- <tr style="padding: 0; text-align: left; vertical-align: top;">
475
- <td class="main-signature-content"
476
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
477
- <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
478
  <?php esc_html_e( "Stay vigilant.", "defender-security" ) ?></p>
479
- <p class="last-item"
480
- style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
481
- <strong><?php esc_html_e( "WP Defender", "defender-security" ) ?></strong>
482
- <br>
483
  <?php esc_html_e( "Security Hero", "defender-security" ) ?>
484
- <br/>
485
  <?php esc_html_e( "WPMU DEV", "defender-security" ) ?>
486
- </p>
487
- </td>
488
- </tr>
489
- </tbody>
490
- </table>
491
- <!-- end main-signature -->
492
-
493
- </td>
494
- </tr>
495
- </tbody>
496
- </table>
497
- <!-- end main -->
498
-
499
- <table class="related" align="center"
500
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
501
- <tbody>
502
- <tr style="padding: 0; text-align: left; vertical-align: top;">
503
- <td class="related-inner"
504
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
505
- <table
506
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
507
- <tbody>
508
- <tr style="padding: 0; text-align: left; vertical-align: top;">
509
- <td class="related-items-title brand" align="left"
510
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
511
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
512
- </td>
513
- </tr>
514
- <tr style="padding: 0; text-align: left; vertical-align: top;">
515
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
516
- <table class="related-items row collapse" align="center"
517
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
518
- <tbody>
519
- <tr style="padding: 0; text-align: left; vertical-align: top;">
520
- <th class="small-12 large-6 columns first" align="left"
521
- valign="top"
522
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
523
- <a class="related-item plugin-link"
524
- href="https://premium.wpmudev.org/project/wp-hummingbird/"
525
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
526
- <img
527
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
528
- alt="Hummingbird" class="plugin-image"
529
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
530
- <span class="plugin-info"
531
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
532
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
533
  <span class="plugin-title hummingbird"
534
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
535
  </span>
536
- </a>
537
- </th>
538
- <th class="small-12 large-6 columns last" align="left"
539
- valign="top"
540
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
541
- <a class="related-item plugin-link"
542
- href="https://premium.wpmudev.org/project/snapshot/"
543
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
544
- <img
545
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
546
- alt="Snapshot"
547
- class="plugin-image"
548
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
549
- <span class="plugin-info"
550
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
551
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
552
  <span class="plugin-title snapshot"
553
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
554
  </span>
555
- </a>
556
- </th>
557
- <th class="expander"
558
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
559
- </tr>
560
- </tbody>
561
- </table>
562
- </td>
563
- </tr>
564
- </tbody>
565
- </table>
566
- <!-- end related-inner -->
567
- </td>
568
- </tr>
569
- </tbody>
570
- </table>
571
- <!-- end related -->
572
-
573
- <!-- Preferences -->
574
- <table class="email-preferences" align="center" valign="middle"
575
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
576
- <tbody>
577
- <tr style="padding: 0; text-align: center; vertical-align: top;">
578
- <td class="email-preferences-inner"
579
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
580
- <table class="email-preferences-content row collapse" align="center" valign="top"
581
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
582
- <tbody>
583
- <tr style="padding: 0; text-align: center; vertical-align: top;">
584
- <th class="small-12 large-8 columns first copy" align="center"
585
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
586
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
587
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ?>
588
- </p>
589
- </th>
590
- </tr>
591
- </tbody>
592
- </table>
593
- </td>
594
- </tr>
595
- </tbody>
596
- </table>
597
- <!-- End Preferences -->
598
-
599
- <table class="company-info" align="left" valign="middle"
600
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
601
- <tbody>
602
- <tr style="padding: 0; text-align: left; vertical-align: top;">
603
- <td class="company-info-inner"
604
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
605
- <table class="company-info-content row collapse" align="left" valign="top"
606
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
607
- <tbody>
608
- <tr style="padding: 0; text-align: left; vertical-align: top;">
609
- <th class="small-12 large-8 columns first copy" align="left"
610
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
611
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
612
- Copyright © Incsub, All rights reserved.</p>
613
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
614
- Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
615
- </th>
616
- <th class="small-12 large-4 columns last logo" align="right"
617
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
618
- <a href="https://premium.wpmudev.org" class="logo-link"
619
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
620
- <img
621
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
622
- alt="WPMU DEV"
623
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
624
- </a>
625
- </th>
626
- <th class="expander"
627
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
628
- </tr>
629
- </tbody>
630
- </table>
631
- </td>
632
- </tr>
633
- </tbody>
634
- </table>
635
- <!-- end company-info -->
636
-
637
- <table class="wrapper social" align="center"
638
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
639
- <tbody>
640
- <tr style="padding: 0; text-align: left; vertical-align: top;">
641
- <td class="wrapper-inner social-inner"
642
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
643
-
644
- <table class="social-content" align="center"
645
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
646
- <tbody>
647
- <tr style="padding: 0; text-align: left; vertical-align: top;">
648
- <td class="social-content-inner"
649
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
650
- <a href="https://plus.google.com/+wpmuorg/" target="_blank"
651
- class="gplus"
652
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
653
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
654
- alt="WPMU DEV on Google+"
655
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
656
- <a href="https://twitter.com/wpmudev" target="_blank"
657
- class="twitter"
658
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
659
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
660
- alt="WPMU DEV on Twitter"
661
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
662
- <a href="https://www.facebook.com/wpmudev" target="_blank"
663
- class="facebook"
664
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
665
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
666
- alt="WPMU DEV on Facebook"
667
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
668
- </td>
669
- </tr>
670
- </tbody>
671
- </table>
672
- <!-- end social-content -->
673
-
674
- </td>
675
- </tr>
676
- </tbody>
677
- </table>
678
- <!-- end top -->
679
- </td>
680
- </tr>
681
- </tbody>
682
- </table>
683
- <!-- end main container -->
684
-
685
- </center>
686
-
687
- </td>
688
- </tr>
689
- </tbody>
 
690
  </table>
691
  <!-- end body -->
692
  </body>
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
+ <meta name="viewport" content="width=device-width">
7
+ <title><?php _e( "New 404 Lockout", "defender-security" ) ?></title>
8
+ <style>
9
+ a.plugin-brand:hover {
10
+ color: #e23717 !important;
11
+ }
12
+
13
+ table.top-content td a:hover {
14
+ color: #ff5c28 !important;
15
+ }
16
+ </style>
17
  </head>
18
 
19
  <body
20
+ style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
+ @media only screen {
23
+ html {
24
+ min-height: 100%;
25
+ background: #f3f3f3;
26
+ }
27
+ }
28
+
29
+ @media only screen and (max-width: 596px) {
30
+ .small-float-center {
31
+ margin: 0 auto !important;
32
+ float: none !important;
33
+ text-align: center !important;
34
+ }
35
+
36
+ .small-text-center {
37
+ text-align: center !important;
38
+ }
39
+
40
+ .small-text-left {
41
+ text-align: left !important;
42
+ }
43
+
44
+ .small-text-right {
45
+ text-align: right !important;
46
+ }
47
+ }
48
+
49
+ @media only screen and (max-width: 596px) {
50
+ .hide-for-large {
51
+ display: block !important;
52
+ width: auto !important;
53
+ overflow: visible !important;
54
+ max-height: none !important;
55
+ font-size: inherit !important;
56
+ line-height: inherit !important;
57
+ }
58
+ }
59
+
60
+ @media only screen and (max-width: 596px) {
61
+ table.body table.container .hide-for-large,
62
+ table.body table.container .row.hide-for-large {
63
+ display: table !important;
64
+ width: 100% !important;
65
+ }
66
+ }
67
+
68
+ @media only screen and (max-width: 596px) {
69
+ table.body table.container .callout-inner.hide-for-large {
70
+ display: table-cell !important;
71
+ width: 100% !important;
72
+ }
73
+ }
74
+
75
+ @media only screen and (max-width: 596px) {
76
+ table.body table.container .show-for-large {
77
+ display: none !important;
78
+ width: 0;
79
+ mso-hide: all;
80
+ overflow: hidden;
81
+ }
82
+ }
83
+
84
+ @media only screen and (max-width: 596px) {
85
+ table.body img {
86
+ width: auto;
87
+ height: auto;
88
+ }
89
+
90
+ table.body center {
91
+ min-width: 0 !important;
92
+ }
93
+
94
+ table.body .container {
95
+ width: 95% !important;
96
+ }
97
+
98
+ table.body .columns,
99
+ table.body .column {
100
+ height: auto !important;
101
+ -moz-box-sizing: border-box;
102
+ -webkit-box-sizing: border-box;
103
+ box-sizing: border-box;
104
+ padding-left: 16px !important;
105
+ padding-right: 16px !important;
106
+ }
107
+
108
+ table.body .columns .column,
109
+ table.body .columns .columns,
110
+ table.body .column .column,
111
+ table.body .column .columns {
112
+ padding-left: 0 !important;
113
+ padding-right: 0 !important;
114
+ }
115
+
116
+ table.body .collapse .columns,
117
+ table.body .collapse .column {
118
+ padding-left: 0 !important;
119
+ padding-right: 0 !important;
120
+ }
121
+
122
+ td.small-1,
123
+ th.small-1 {
124
+ display: inline-block !important;
125
+ width: 8.33333% !important;
126
+ }
127
+
128
+ td.small-2,
129
+ th.small-2 {
130
+ display: inline-block !important;
131
+ width: 16.66667% !important;
132
+ }
133
+
134
+ td.small-3,
135
+ th.small-3 {
136
+ display: inline-block !important;
137
+ width: 25% !important;
138
+ }
139
+
140
+ td.small-4,
141
+ th.small-4 {
142
+ display: inline-block !important;
143
+ width: 33.33333% !important;
144
+ }
145
+
146
+ td.small-5,
147
+ th.small-5 {
148
+ display: inline-block !important;
149
+ width: 41.66667% !important;
150
+ }
151
+
152
+ td.small-6,
153
+ th.small-6 {
154
+ display: inline-block !important;
155
+ width: 50% !important;
156
+ }
157
+
158
+ td.small-7,
159
+ th.small-7 {
160
+ display: inline-block !important;
161
+ width: 58.33333% !important;
162
+ }
163
+
164
+ td.small-8,
165
+ th.small-8 {
166
+ display: inline-block !important;
167
+ width: 66.66667% !important;
168
+ }
169
+
170
+ td.small-9,
171
+ th.small-9 {
172
+ display: inline-block !important;
173
+ width: 75% !important;
174
+ }
175
+
176
+ td.small-10,
177
+ th.small-10 {
178
+ display: inline-block !important;
179
+ width: 83.33333% !important;
180
+ }
181
+
182
+ td.small-11,
183
+ th.small-11 {
184
+ display: inline-block !important;
185
+ width: 91.66667% !important;
186
+ }
187
+
188
+ td.small-12,
189
+ th.small-12 {
190
+ display: inline-block !important;
191
+ width: 100% !important;
192
+ }
193
+
194
+ .columns td.small-12,
195
+ .column td.small-12,
196
+ .columns th.small-12,
197
+ .column th.small-12 {
198
+ display: block !important;
199
+ width: 100% !important;
200
+ }
201
+
202
+ table.body td.small-offset-1,
203
+ table.body th.small-offset-1 {
204
+ margin-left: 8.33333% !important;
205
+ Margin-left: 8.33333% !important;
206
+ }
207
+
208
+ table.body td.small-offset-2,
209
+ table.body th.small-offset-2 {
210
+ margin-left: 16.66667% !important;
211
+ Margin-left: 16.66667% !important;
212
+ }
213
+
214
+ table.body td.small-offset-3,
215
+ table.body th.small-offset-3 {
216
+ margin-left: 25% !important;
217
+ Margin-left: 25% !important;
218
+ }
219
+
220
+ table.body td.small-offset-4,
221
+ table.body th.small-offset-4 {
222
+ margin-left: 33.33333% !important;
223
+ Margin-left: 33.33333% !important;
224
+ }
225
+
226
+ table.body td.small-offset-5,
227
+ table.body th.small-offset-5 {
228
+ margin-left: 41.66667% !important;
229
+ Margin-left: 41.66667% !important;
230
+ }
231
+
232
+ table.body td.small-offset-6,
233
+ table.body th.small-offset-6 {
234
+ margin-left: 50% !important;
235
+ Margin-left: 50% !important;
236
+ }
237
+
238
+ table.body td.small-offset-7,
239
+ table.body th.small-offset-7 {
240
+ margin-left: 58.33333% !important;
241
+ Margin-left: 58.33333% !important;
242
+ }
243
+
244
+ table.body td.small-offset-8,
245
+ table.body th.small-offset-8 {
246
+ margin-left: 66.66667% !important;
247
+ Margin-left: 66.66667% !important;
248
+ }
249
+
250
+ table.body td.small-offset-9,
251
+ table.body th.small-offset-9 {
252
+ margin-left: 75% !important;
253
+ Margin-left: 75% !important;
254
+ }
255
+
256
+ table.body td.small-offset-10,
257
+ table.body th.small-offset-10 {
258
+ margin-left: 83.33333% !important;
259
+ Margin-left: 83.33333% !important;
260
+ }
261
+
262
+ table.body td.small-offset-11,
263
+ table.body th.small-offset-11 {
264
+ margin-left: 91.66667% !important;
265
+ Margin-left: 91.66667% !important;
266
+ }
267
+
268
+ table.body table.columns td.expander,
269
+ table.body table.columns th.expander {
270
+ display: none !important;
271
+ }
272
+
273
+ table.body .right-text-pad,
274
+ table.body .text-pad-right {
275
+ padding-left: 10px !important;
276
+ }
277
+
278
+ table.body .left-text-pad,
279
+ table.body .text-pad-left {
280
+ padding-right: 10px !important;
281
+ }
282
+
283
+ table.menu {
284
+ width: 100% !important;
285
+ }
286
+
287
+ table.menu td,
288
+ table.menu th {
289
+ width: auto !important;
290
+ display: inline-block !important;
291
+ }
292
+
293
+ table.menu.vertical td,
294
+ table.menu.vertical th,
295
+ table.menu.small-vertical td,
296
+ table.menu.small-vertical th {
297
+ display: block !important;
298
+ }
299
+
300
+ table.menu[align="center"] {
301
+ width: auto !important;
302
+ }
303
+
304
+ table.button.small-expand,
305
+ table.button.small-expanded {
306
+ width: 100% !important;
307
+ }
308
+
309
+ table.button.small-expand table,
310
+ table.button.small-expanded table {
311
+ width: 100%;
312
+ }
313
+
314
+ table.button.small-expand table a,
315
+ table.button.small-expanded table a {
316
+ text-align: center !important;
317
+ width: 100% !important;
318
+ padding-left: 0 !important;
319
+ padding-right: 0 !important;
320
+ }
321
+
322
+ table.button.small-expand center,
323
+ table.button.small-expanded center {
324
+ min-width: 0;
325
+ }
326
+ }
327
+
328
+ @media screen and (max-width: 596px) {
329
+ /* results list */
330
+ table.results-list thead th {
331
+ line-height: 34px !important;
332
+ }
333
+
334
+ /* top */
335
+ table.top-content td {
336
+ text-align: center !important;
337
+ }
338
+
339
+ /* related */
340
+ table.related table.related-items .columns {
341
+ padding-right: 0 !important;
342
+ padding-bottom: 15px !important;
343
+ padding-left: 0 !important;
344
+ }
345
+
346
+ table.related table.related-items .columns.last {
347
+ padding-bottom: 0 !important;
348
+ }
349
+
350
+ table.related a.related-item .plugin-info {
351
+ vertical-align: middle !important;
352
+ }
353
+
354
+ /* company info */
355
+ table.company-info .columns {
356
+ padding-right: 0 !important;
357
+ padding-left: 0 !important;
358
+ }
359
+
360
+ table.company-info .columns.last {
361
+ padding: 15px 0 0 !important;
362
+ }
363
+
364
+ table.company-info .logo,
365
+ table.company-info .logo-link,
366
+ table.company-info .logo img {
367
+ text-align: left !important;
368
+ }
369
+ }
370
+
371
+ @media screen and (max-width: 540px) {
372
+ /* hero */
373
+ table.hero table.hero-content {
374
+ width: 100%;
375
+ }
376
+
377
+ table.hero td.hero-title h1,
378
+ table.hero td.hero-title h2 {
379
+ padding: 0 !important;
380
+ text-align: center !important;
381
+ }
382
+
383
+ table.hero td.hero-image {
384
+ display: none;
385
+ }
386
+ }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
+ <tbody>
392
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
393
+ <td class="center" align="center" valign="top"
394
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
+
396
+ <center style="min-width: 600px; width: 100%;">
397
+
398
+ <table class="container"
399
+ style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
+ <tbody>
401
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
402
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
+ <table class="wrapper hero" align="left"
404
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
+ <tbody>
406
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
407
+ <td class="wrapper-inner hero-inner"
408
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
+
410
+ <table class="hero-content" align="left"
411
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
+ <tbody>
413
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
414
+ <td class="hero-title"
415
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
+ <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php esc_html_e( "Protected By", "defender-security" ) ?></h2>
418
+ <h1 class="plugin-brand"
419
+ style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php esc_html_e( "Defender!", "defender-security" ) ?></h1>
421
+ </td>
422
+ <td class="hero-image"
423
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
+ <a href="https://premium.wpmudev.org/"
425
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
+ alt="Defender"
428
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
+ </td>
430
+ </tr>
431
+ </tbody>
432
+ </table>
433
+ <!-- end hero-content -->
434
+
435
+ </td>
436
+ </tr>
437
+ </tbody>
438
+ </table>
439
+ <!-- end hero -->
440
+
441
+ <table class="wrapper main" align="center"
442
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
+ <tbody>
444
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
445
+ <td class="wrapper-inner main-inner"
446
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
+
448
+ <table class="main-intro"
449
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
+ <tbody>
451
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
452
+ <td class="main-intro-content"
453
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
+ <h3 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 32px; font-weight: normal; line-height: 32px; margin: 0; margin-bottom: 0; padding: 0 0 28px; text-align: left; word-wrap: normal;">
455
  <?php printf( __( "Hi %s", "defender-security" ), $admin ) ?>
456
+ ,</h3>
457
  <?php $setting = \WP_Defender\Module\IP_Lockout\Model\Settings::instance() ?>
458
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
459
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
460
  <?php printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> 404 requests for the file <strong>%s</strong>. They have been locked out for <strong>%s seconds.</strong>", "defender-security" ), $ip, network_site_url(), $setting->detect_404_threshold, $uri, $setting->detect_404_lockout_duration ) ?>
461
+ </p>
462
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
463
+ <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ) ?>
464
+ .</p>
465
+ </td>
466
+ </tr>
467
+ </tbody>
468
+ </table>
469
+ <!-- end main-intro -->
470
+
471
+ <table class="main-signature"
472
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
+ <tbody>
474
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
475
+ <td class="main-signature-content"
476
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
477
+ <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
478
  <?php esc_html_e( "Stay vigilant.", "defender-security" ) ?></p>
479
+ <p class="last-item"
480
+ style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
481
+ <strong><?php esc_html_e( "WP Defender", "defender-security" ) ?></strong>
482
+ <br>
483
  <?php esc_html_e( "Security Hero", "defender-security" ) ?>
484
+ <br/>
485
  <?php esc_html_e( "WPMU DEV", "defender-security" ) ?>
486
+ </p>
487
+ </td>
488
+ </tr>
489
+ </tbody>
490
+ </table>
491
+ <!-- end main-signature -->
492
+
493
+ </td>
494
+ </tr>
495
+ </tbody>
496
+ </table>
497
+ <!-- end main -->
498
+
499
+ <table class="related" align="center"
500
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
501
+ <tbody>
502
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
503
+ <td class="related-inner"
504
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
505
+ <table
506
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
507
+ <tbody>
508
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
509
+ <td class="related-items-title brand" align="left"
510
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
511
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
512
+ </td>
513
+ </tr>
514
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
515
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
516
+ <table class="related-items row collapse" align="center"
517
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
518
+ <tbody>
519
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
520
+ <th class="small-12 large-6 columns first" align="left"
521
+ valign="top"
522
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
523
+ <a class="related-item plugin-link"
524
+ href="https://premium.wpmudev.org/project/wp-hummingbird/"
525
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
526
+ <img
527
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
528
+ alt="Hummingbird" class="plugin-image"
529
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
530
+ <span class="plugin-info"
531
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
532
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
533
  <span class="plugin-title hummingbird"
534
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
535
  </span>
536
+ </a>
537
+ </th>
538
+ <th class="small-12 large-6 columns last" align="left"
539
+ valign="top"
540
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
541
+ <a class="related-item plugin-link"
542
+ href="https://premium.wpmudev.org/project/snapshot/"
543
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
544
+ <img
545
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
546
+ alt="Snapshot"
547
+ class="plugin-image"
548
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
549
+ <span class="plugin-info"
550
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
551
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
552
  <span class="plugin-title snapshot"
553
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
554
  </span>
555
+ </a>
556
+ </th>
557
+ <th class="expander"
558
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
559
+ </tr>
560
+ </tbody>
561
+ </table>
562
+ </td>
563
+ </tr>
564
+ </tbody>
565
+ </table>
566
+ <!-- end related-inner -->
567
+ </td>
568
+ </tr>
569
+ </tbody>
570
+ </table>
571
+ <!-- end related -->
572
+
573
+ <!-- Preferences -->
574
+ <table class="email-preferences" align="center" valign="middle"
575
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
576
+ <tbody>
577
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
578
+ <td class="email-preferences-inner"
579
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
580
+ <table class="email-preferences-content row collapse" align="center"
581
+ valign="top"
582
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
583
+ <tbody>
584
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
585
+ <th class="small-12 large-8 columns first copy" align="center"
586
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
587
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
588
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ) ?>
589
+ </p>
590
+ </th>
591
+ </tr>
592
+ </tbody>
593
+ </table>
594
+ </td>
595
+ </tr>
596
+ </tbody>
597
+ </table>
598
+ <!-- End Preferences -->
599
+
600
+ <table class="company-info" align="left" valign="middle"
601
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
602
+ <tbody>
603
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
604
+ <td class="company-info-inner"
605
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
606
+ <table class="company-info-content row collapse" align="left" valign="top"
607
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
608
+ <tbody>
609
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
610
+ <th class="small-12 large-8 columns first copy" align="left"
611
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
612
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
613
+ Copyright © Incsub, All rights reserved.</p>
614
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
615
+ Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
616
+ </th>
617
+ <th class="small-12 large-4 columns last logo" align="right"
618
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
619
+ <a href="https://premium.wpmudev.org" class="logo-link"
620
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
621
+ <img
622
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
623
+ alt="WPMU DEV"
624
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
625
+ </a>
626
+ </th>
627
+ <th class="expander"
628
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
629
+ </tr>
630
+ </tbody>
631
+ </table>
632
+ </td>
633
+ </tr>
634
+ </tbody>
635
+ </table>
636
+ <!-- end company-info -->
637
+
638
+ <table class="wrapper social" align="center"
639
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
640
+ <tbody>
641
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
642
+ <td class="wrapper-inner social-inner"
643
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
644
+
645
+ <table class="social-content" align="center"
646
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
647
+ <tbody>
648
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
649
+ <td class="social-content-inner"
650
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
651
+ <a href="https://plus.google.com/+wpmuorg/" target="_blank"
652
+ class="gplus"
653
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
654
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
655
+ alt="WPMU DEV on Google+"
656
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
657
+ <a href="https://twitter.com/wpmudev" target="_blank"
658
+ class="twitter"
659
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
660
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
661
+ alt="WPMU DEV on Twitter"
662
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
663
+ <a href="https://www.facebook.com/wpmudev" target="_blank"
664
+ class="facebook"
665
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
666
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
667
+ alt="WPMU DEV on Facebook"
668
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
669
+ </td>
670
+ </tr>
671
+ </tbody>
672
+ </table>
673
+ <!-- end social-content -->
674
+
675
+ </td>
676
+ </tr>
677
+ </tbody>
678
+ </table>
679
+ <!-- end top -->
680
+ </td>
681
+ </tr>
682
+ </tbody>
683
+ </table>
684
+ <!-- end main container -->
685
+
686
+ </center>
687
+
688
+ </td>
689
+ </tr>
690
+ </tbody>
691
  </table>
692
  <!-- end body -->
693
  </body>
app/module/ip-lockout/view/emails/login-lockout.php CHANGED
@@ -457,12 +457,12 @@
457
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
458
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
459
  <?php
460
- $lockout_duration = $setting->login_protection_lockout_ban == true ? __( "They have been banned permanently.", "defender-security" ) : sprintf( __( "They have been locked out for <strong>%s seconds.</strong>", "defender-security" ), $setting->login_protection_lockout_duration );
461
  printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> failed login attempts. %s", "defender-security" ), $ip, network_site_url(), $setting->login_protection_login_attempt, $lockout_duration
462
  ) ?>
463
  </p>
464
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
465
- <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ?>
466
  .</p>
467
  </td>
468
  </tr>
@@ -573,30 +573,31 @@
573
  <!-- end related -->
574
 
575
  <!-- Preferences -->
576
- <table class="email-preferences" align="center" valign="middle"
577
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
578
- <tbody>
579
- <tr style="padding: 0; text-align: center; vertical-align: top;">
580
- <td class="email-preferences-inner"
581
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
582
- <table class="email-preferences-content row collapse" align="center" valign="top"
583
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
584
- <tbody>
585
- <tr style="padding: 0; text-align: center; vertical-align: top;">
586
- <th class="small-12 large-8 columns first copy" align="center"
587
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
588
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
589
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ?>
590
- </p>
591
- </th>
592
- </tr>
593
- </tbody>
594
- </table>
595
- </td>
596
- </tr>
597
- </tbody>
598
- </table>
599
- <!-- End Preferences -->
 
600
 
601
  <table class="company-info" align="left" valign="middle"
602
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
457
  <?php $utils = \WP_Defender\Behavior\Utils::instance() ?>
458
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
459
  <?php
460
+ $lockout_duration = $setting->login_protection_lockout_ban == true ? __( "They have been banned permanently.", "defender-security" ) : sprintf( __( "They have been locked out for <strong>%s %s.</strong>", "defender-security" ), $setting->login_protection_lockout_duration, $setting->login_protection_lockout_duration_unit );
461
  printf( __( "We've just locked out the host <strong>%s</strong> from %s due to more than <strong>%s</strong> failed login attempts. %s", "defender-security" ), $ip, network_site_url(), $setting->login_protection_login_attempt, $lockout_duration
462
  ) ?>
463
  </p>
464
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
465
+ <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), $logs_url ) ?>
466
  .</p>
467
  </td>
468
  </tr>
573
  <!-- end related -->
574
 
575
  <!-- Preferences -->
576
+ <table class="email-preferences" align="center" valign="middle"
577
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
578
+ <tbody>
579
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
580
+ <td class="email-preferences-inner"
581
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
582
+ <table class="email-preferences-content row collapse" align="center"
583
+ valign="top"
584
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
585
+ <tbody>
586
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
587
+ <th class="small-12 large-8 columns first copy" align="center"
588
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
589
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
590
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ) ?>
591
+ </p>
592
+ </th>
593
+ </tr>
594
+ </tbody>
595
+ </table>
596
+ </td>
597
+ </tr>
598
+ </tbody>
599
+ </table>
600
+ <!-- End Preferences -->
601
 
602
  <table class="company-info" align="left" valign="middle"
603
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
app/module/ip-lockout/view/emails/login-username-ban.php CHANGED
@@ -461,7 +461,7 @@
461
  printf( __( "We've just locked out the host <strong>%s</strong> from %s due to attempting to login with a banned username. They have been banned permanently.", "defender-security" ), $ip, network_site_url() ) ?>
462
  </p>
463
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
464
- <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ?>
465
  .</p>
466
  </td>
467
  </tr>
@@ -572,30 +572,31 @@
572
  <!-- end related -->
573
 
574
  <!-- Preferences -->
575
- <table class="email-preferences" align="center" valign="middle"
576
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
577
- <tbody>
578
- <tr style="padding: 0; text-align: center; vertical-align: top;">
579
- <td class="email-preferences-inner"
580
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
581
- <table class="email-preferences-content row collapse" align="center" valign="top"
582
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
583
- <tbody>
584
- <tr style="padding: 0; text-align: center; vertical-align: top;">
585
- <th class="small-12 large-8 columns first copy" align="center"
586
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
587
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
588
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ?>
589
- </p>
590
- </th>
591
- </tr>
592
- </tbody>
593
- </table>
594
- </td>
595
- </tr>
596
- </tbody>
597
- </table>
598
- <!-- End Preferences -->
 
599
 
600
  <table class="company-info" align="left" valign="middle"
601
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
461
  printf( __( "We've just locked out the host <strong>%s</strong> from %s due to attempting to login with a banned username. They have been banned permanently.", "defender-security" ), $ip, network_site_url() ) ?>
462
  </p>
463
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
464
+ <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ) ?>
465
  .</p>
466
  </td>
467
  </tr>
572
  <!-- end related -->
573
 
574
  <!-- Preferences -->
575
+ <table class="email-preferences" align="center" valign="middle"
576
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
577
+ <tbody>
578
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
579
+ <td class="email-preferences-inner"
580
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
581
+ <table class="email-preferences-content row collapse" align="center"
582
+ valign="top"
583
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
584
+ <tbody>
585
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
586
+ <th class="small-12 large-8 columns first copy" align="center"
587
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
588
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
589
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), $report_url ) ?>
590
+ </p>
591
+ </th>
592
+ </tr>
593
+ </tbody>
594
+ </table>
595
+ </td>
596
+ </tr>
597
+ </tbody>
598
+ </table>
599
+ <!-- End Preferences -->
600
 
601
  <table class="company-info" align="left" valign="middle"
602
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
app/module/ip-lockout/view/emails/report.php CHANGED
@@ -494,7 +494,7 @@
494
  <hr/>
495
  <?php endif; ?>
496
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
497
- <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ?>
498
  .</p>
499
  </td>
500
  </tr>
@@ -605,30 +605,31 @@
605
  <!-- end related -->
606
 
607
  <!-- Preferences -->
608
- <table class="email-preferences" align="center" valign="middle"
609
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
610
- <tbody>
611
- <tr style="padding: 0; text-align: center; vertical-align: top;">
612
- <td class="email-preferences-inner"
613
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
614
- <table class="email-preferences-content row collapse" align="center" valign="top"
615
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
616
- <tbody>
617
- <tr style="padding: 0; text-align: center; vertical-align: top;">
618
- <th class="small-12 large-8 columns first copy" align="center"
619
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
620
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
621
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ?>
622
- </p>
623
- </th>
624
- </tr>
625
- </tbody>
626
- </table>
627
- </td>
628
- </tr>
629
- </tbody>
630
- </table>
631
- <!-- End Preferences -->
 
632
 
633
  <table class="company-info" align="left" valign="middle"
634
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
494
  <hr/>
495
  <?php endif; ?>
496
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
497
+ <?php printf( __( "You can view the full lockout logs <a href=\"%s\">here</a>", "defender-security" ), apply_filters( 'wp_defeder/iplockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=logs" ) ) ) ?>
498
  .</p>
499
  </td>
500
  </tr>
605
  <!-- end related -->
606
 
607
  <!-- Preferences -->
608
+ <table class="email-preferences" align="center" valign="middle"
609
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
610
+ <tbody>
611
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
612
+ <td class="email-preferences-inner"
613
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
614
+ <table class="email-preferences-content row collapse" align="center"
615
+ valign="top"
616
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
617
+ <tbody>
618
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
619
+ <th class="small-12 large-8 columns first copy" align="center"
620
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
621
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
622
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), apply_filters( 'wp_defender/lockout/email_report_link', network_admin_url( "admin.php?page=wdf-ip-lockout&view=reporting" ) ) ) ?>
623
+ </p>
624
+ </th>
625
+ </tr>
626
+ </tbody>
627
+ </table>
628
+ </td>
629
+ </tr>
630
+ </tbody>
631
+ </table>
632
+ <!-- End Preferences -->
633
 
634
  <table class="company-info" align="left" valign="middle"
635
  style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
app/module/ip-lockout/view/locked.php CHANGED
@@ -1,80 +1,85 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <meta http-equiv="Cache-control" content="max-age=0">
7
- <title><?php bloginfo( 'name' ) ?></title>
8
- <link rel="stylesheet"
9
- href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Roboto:400,500,300,300italic">
10
- <style type="text/css">
11
- html, body {
12
- margin: 0;
13
- padding: 0;
 
 
 
 
 
 
 
14
 
15
- min-width: 100%;
16
- width: 100%;
17
- max-width: 100%;
18
 
19
- min-height: 100%;
20
- height: 100%;
21
- max-height: 100%;
22
- }
23
 
24
- .wp-defender {
25
- height: 100%;
26
- display: flex;
27
- align-items: center;
28
- font-family: Roboto;
29
- color: #000;
30
- font-size: 13px;
31
- line-height: 18px;
32
- }
33
 
34
- .container {
35
- margin: 0 auto;
36
- text-align: center;
37
- }
38
 
39
- .image {
40
- width: 128px;
41
- height: 128px;
42
- background-color: #F2F2F2;
43
- margin: 0 auto;
44
- border-radius: 50%;
45
- background-image: url("<?php echo wp_defender()->getPluginUrl().'assets/img/def-stand.svg' ?>");
46
- background-repeat: no-repeat;
47
- background-size: contain;
48
- margin-bottom: 30px;
49
- }
50
 
51
- .powered {
52
- position: absolute;
53
- bottom: 20px;
54
- display: block;
55
- text-align: center;
56
- width: 100%;
57
- font-size: 10px;
58
- color: #C0C0C0;
59
- }
60
 
61
- .powered strong {
62
- color: #8A8A8A;
63
- font-weight: normal;
64
- }
65
- </style>
66
  </head>
67
  <body>
68
  <div class="wp-defender">
69
- <div class="container">
70
- <?php if ( strlen( wp_defender()->heroImage ) == 0 ): ?>
71
- <div class="image">
72
- </div>
73
- <?php endif; ?>
74
- <p><?php echo $message ?></p>
75
- </div>
76
- <div class="powered"><?php esc_html_e( "Powered by", "defender-security" ) ?>
77
- <strong><?php esc_html_e( "Defender", "defender-security" ) ?></strong></div>
78
  </div>
79
  </body>
80
  </html>
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <meta http-equiv="Cache-control" content="max-age=0">
7
+ <title><?php use WP_Defender\Behavior\WPMUDEV;
8
+
9
+ $devmanImg = wp_defender()->getPluginUrl() . 'assets/img/def-stand.svg';
10
+ $info = ( new WPMUDEV() )->whiteLabelStatus();
11
+ if ( strlen( $info['hero_image'] ) > 0 ) {
12
+ $devmanImg = $info['hero_image'];
13
+ }
14
+ bloginfo( 'name' ) ?></title>
15
+ <link rel="stylesheet"
16
+ href="https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Roboto:400,500,300,300italic">
17
+ <style type="text/css">
18
+ html, body {
19
+ margin: 0;
20
+ padding: 0;
21
 
22
+ min-width: 100%;
23
+ width: 100%;
24
+ max-width: 100%;
25
 
26
+ min-height: 100%;
27
+ height: 100%;
28
+ max-height: 100%;
29
+ }
30
 
31
+ .wp-defender {
32
+ height: 100%;
33
+ display: flex;
34
+ align-items: center;
35
+ font-family: Roboto;
36
+ color: #000;
37
+ font-size: 13px;
38
+ line-height: 18px;
39
+ }
40
 
41
+ .container {
42
+ margin: 0 auto;
43
+ text-align: center;
44
+ }
45
 
46
+ .image {
47
+ width: 128px;
48
+ height: 128px;
49
+ background-color: #F2F2F2;
50
+ margin: 0 auto;
51
+ border-radius: 50%;
52
+ background-image: url("<?php echo $devmanImg ?>");
53
+ background-repeat: no-repeat;
54
+ background-size: contain;
55
+ margin-bottom: 30px;
56
+ }
57
 
58
+ .powered {
59
+ position: absolute;
60
+ bottom: 20px;
61
+ display: block;
62
+ text-align: center;
63
+ width: 100%;
64
+ font-size: 10px;
65
+ color: #C0C0C0;
66
+ }
67
 
68
+ .powered strong {
69
+ color: #8A8A8A;
70
+ font-weight: normal;
71
+ }
72
+ </style>
73
  </head>
74
  <body>
75
  <div class="wp-defender">
76
+ <div class="container">
77
+ <div class="image">
78
+ </div>
79
+ <p><?php echo $message ?></p>
80
+ </div>
81
+ <div class="powered"><?php esc_html_e( "Powered by", "defender-security" ) ?>
82
+ <strong><?php esc_html_e( "Defender", "defender-security" ) ?></strong></div>
 
 
83
  </div>
84
  </body>
85
  </html>
app/module/ip-lockout/view/main.php ADDED
@@ -0,0 +1 @@
 
1
+ <div id="defender"></div>
app/module/scan.php CHANGED
@@ -7,11 +7,13 @@ namespace WP_Defender\Module;
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Scan\Controller\Main;
 
10
 
11
  class Scan extends Module {
12
  public function __construct() {
13
  $this->_registerPostTpe();
14
  $main = new Main();
 
15
  }
16
 
17
  private function _registerPostTpe() {
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Scan\Controller\Main;
10
+ use WP_Defender\Module\Scan\Controller\Rest;
11
 
12
  class Scan extends Module {
13
  public function __construct() {
14
  $this->_registerPostTpe();
15
  $main = new Main();
16
+ $rest = new Rest();
17
  }
18
 
19
  private function _registerPostTpe() {
app/module/scan/behavior/core-result.php CHANGED
@@ -8,11 +8,32 @@ namespace WP_Defender\Module\Scan\Behavior;
8
  use Hammer\Base\Behavior;
9
  use Hammer\Helper\File_Helper;
10
  use Hammer\Helper\Log_Helper;
 
11
  use WP_Defender\Component\Error_Code;
12
  use WP_Defender\Module\Scan\Component\Scan_Api;
13
  use WP_Defender\Module\Scan\Model\Result_Item;
14
 
15
  class Core_Result extends Behavior {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  /**
17
  * @return string
18
  */
@@ -41,13 +62,6 @@ class Core_Result extends Behavior {
41
  return $raw['file'];
42
  }
43
 
44
- /**
45
- * @return bool
46
- */
47
- public function check() {
48
-
49
- }
50
-
51
  /**
52
  * @return string
53
  */
@@ -63,7 +77,7 @@ class Core_Result extends Behavior {
63
  if ( $raw['type'] == 'unknown' ) {
64
  return esc_html__( "Unknown file in WordPress core", "defender-security" );
65
  } elseif ( $raw['type'] == 'dir' ) {
66
- return esc_html__( "This directory doesn't belong to WordPress core", "defender-security" );
67
  } elseif ( $raw['type'] == 'modified' ) {
68
  return esc_html__( "This WordPress core file appears modified", "defender-security" );
69
  }
@@ -118,240 +132,6 @@ class Core_Result extends Behavior {
118
  return true;
119
  }
120
 
121
- /**
122
- * Render current issue content
123
- * @return false|string
124
- */
125
- public function renderIssueContent() {
126
- $raw = $this->getRaw();
127
- $string = $raw['type'] == 'unknown' ? __( "Defender found this stray file in your WordPress site directory. The current version of WordPress doesn't require it and as far as we can tell it's harmless (maybe even from an older WordPress install), so you can delete it or ignore it. Before deleting any files, be sure to back up your website." ) :
128
- ( $raw['type'] == 'modified' ? __( "Compare your file with the original file in the WordPress repository. Pieces highlighted in red will be removed when you patch the file, and pieces highlighted in green will be added.", "defender-security" ) :
129
- __( "We found this folder in your WordPress file list. Your current version of WordPress doesn’t use this folder so it might belong to another application. If you don’t recognize it, you can delete this folder (don’t forget to back up your website first!) or get in touch with the WPMU DEV support team for more information", "defender-security" ) );
130
- ob_start();
131
- ?>
132
- <div class="sui-box issue-content">
133
- <div class="sui-box-body">
134
- <strong><?php _e( "Issue Details", "defender-security" ) ?></strong>
135
- <div>
136
- <?php echo $string ?>
137
- </div>
138
- <?php echo $this->getSrcCode() ?>
139
- <table class="sui-table">
140
- <tbody>
141
- <tr>
142
- <td>
143
- <i class="sui-icon-folder-open"
144
- aria-hidden="true"></i><strong><?php _e( "Location", "defender-security" ) ?></strong>
145
- </td>
146
- <td>
147
- <?php echo $this->getSubtitle() ?>
148
- </td>
149
- </tr>
150
- <tr>
151
- <td>
152
- <i class="sui-icon-download-cloud" aria-hidden="true"></i>
153
- <strong>
154
- <?php _e( "Size", "defender-security" ) ?>
155
- </strong>
156
- </td>
157
- <td>
158
- <?php
159
- $bytes = filesize( $this->getSubtitle() );
160
- if ( $bytes ) {
161
- echo $this->getOwner()->makeReadable( $bytes );
162
- } else {
163
- echo 'N/A';
164
- }
165
- ?>
166
- </td>
167
- </tr>
168
- <tr>
169
- <td>
170
- <i class="sui-icon-calendar" aria-hidden="true"></i>
171
- <strong>
172
- <?php _e( "Date added", "defender-security" ) ?>
173
- </strong>
174
- </td>
175
- <td>
176
- <?php
177
- $filemtime = filemtime( $this->getSubtitle() );
178
- if ( $filemtime ) {
179
- echo $this->getOwner()->formatDateTime( $filemtime );
180
- } else {
181
- echo 'N/A';
182
- }
183
- ?>
184
- </td>
185
- </tr>
186
- </tbody>
187
- </table>
188
- </div>
189
- <div class="sui-box-footer">
190
- <div class="sui-actions-left">
191
- <form method="post" class="float-l ignore-item scan-frm">
192
- <input type="hidden" name="action" value="ignoreItem">
193
- <?php wp_nonce_field( 'ignoreItem' ) ?>
194
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
195
- <button type="submit" class="sui-button sui-button-ghost">
196
- <i class="sui-icon-eye-hide" aria-hidden="true"></i>
197
- <?php _e( "Ignore", "defender-security" ) ?></button>
198
- </form>
199
- </div>
200
- <div class="sui-actions-right">
201
- <?php if ( $raw['type'] == 'unknown' || $raw['type'] == 'dir' ): ?>
202
- <form method="post" class="scan-frm delete-item float-r">
203
- <input type="hidden" name="action" value="deleteItem"/>
204
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
205
- <?php wp_nonce_field( 'deleteItem' ) ?>
206
- <button type="button" class="sui-button sui-button-red delete-mitem">
207
- <i class="sui-icon-trash" aria-hidden="true"></i>
208
- <?php _e( "Delete", "defender-security" ) ?></button>
209
- <div class="confirm-box wd-hide">
210
- <span><?php _e( "This will permanently remove the selected file/folder. Are you sure you want to continue?", "defender-security" ) ?></span>
211
- <div>
212
- <button type="submit" class="sui-button sui-button-red">
213
- <?php _e( "Yes", "defender-security" ) ?>
214
- </button>
215
- <button type="button" class="sui-button sui-button-ghost">
216
- <?php _e( "No", "defender-security" ) ?>
217
- </button>
218
- </div>
219
- </div>
220
- </form>
221
- <?php elseif ( $raw['type'] == 'modified' ): ?>
222
- <form method="post" class="scan-frm float-r resolve-item">
223
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
224
- <input type="hidden" name="action" value="resolveItem"/>
225
- <?php wp_nonce_field( 'resolveItem' ) ?>
226
- <button type="submit" class="sui-button sui-button-blue">
227
- <?php _e( "Restore to Original", "defender-security" ) ?>
228
- </button>
229
- </form>
230
- <?php endif; ?>
231
- </div>
232
- </div>
233
- </div>
234
- <?php
235
- return ob_get_clean();
236
- }
237
-
238
- /**
239
- * Each item should have an dialog to show about itself description
240
- * return string
241
- * @deprecated 2.1
242
- */
243
- public function renderDialog() {
244
- ob_start();
245
- $raw = $this->getRaw();
246
- ?>
247
- <dialog title="<?php esc_attr_e( "Issue Details", "defender-security" ) ?>"
248
- id="dia_<?php echo $this->getOwner()->id ?>">
249
- <div class="wpmud">
250
- <div class="wp-defender">
251
- <div class="scan-dialog">
252
- <div class="well mline">
253
- <ul class="dev-list item-detail">
254
- <li>
255
- <div>
256
- <span class="list-label">
257
- <strong><?php _e( "Location", "defender-security" ) ?></strong>
258
- </span>
259
- <span class="list-detail">
260
- <?php echo $this->getSubtitle(); ?>
261
- </span>
262
- </div>
263
- </li>
264
- <li>
265
- <div>
266
- <span class="list-label">
267
- <strong><?php _e( "Size", "defender-security" ) ?></strong>
268
- </span>
269
- <span class="list-detail">
270
- <?php
271
- $bytes = filesize( $this->getSubtitle() );
272
- if ( $bytes ) {
273
- echo $this->getOwner()->makeReadable( $bytes );
274
- } else {
275
- echo 'N/A';
276
- }
277
- ?>
278
- </span>
279
- </div>
280
- </li>
281
- <li>
282
- <div>
283
- <span class="list-label">
284
- <strong><?php _e( "Date Added", "defender-security" ) ?></strong>
285
- </span>
286
- <span class="list-detail">
287
- <?php
288
- $filemtime = filemtime( $this->getSubtitle() );
289
- if ( $filemtime ) {
290
- echo $this->getOwner()->formatDateTime( $filemtime );
291
- } else {
292
- echo 'N/A';
293
- }
294
- ?>
295
- </span>
296
- </div>
297
- </li>
298
- </ul>
299
- </div>
300
- <?php if ( $raw['type'] == 'unknown' ) {
301
- $this->_dialogContentForAdded();
302
- } elseif ( $raw['type'] == 'modified' ) {
303
- $this->_dialogContentForModified();
304
- } elseif ( $raw['type'] == 'dir' ) {
305
- $this->_dialogContentForDir();
306
- } ?>
307
-
308
- <div class="well well-small">
309
- <form method="post" class="float-l ignore-item scan-frm">
310
- <input type="hidden" name="action" value="ignoreItem">
311
- <?php wp_nonce_field( 'ignoreItem' ) ?>
312
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
313
- <button type="submit" class="button button-secondary button-small">
314
- <?php _e( "Ignore", "defender-security" ) ?></button>
315
- </form>
316
- <?php if ( $raw['type'] == 'unknown' || $raw['type'] == 'dir' ): ?>
317
- <form method="post" class="scan-frm delete-item float-r">
318
- <input type="hidden" name="action" value="deleteItem"/>
319
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
320
- <?php wp_nonce_field( 'deleteItem' ) ?>
321
- <button type="button" class="button button-small delete-mitem button-grey">
322
- <?php _e( "Delete", "defender-security" ) ?></button>
323
- <div class="confirm-box wd-hide">
324
- <span><?php _e( "This will permanently remove the selected file/folder. Are you sure you want to continue?", "defender-security" ) ?></span>
325
- <div>
326
- <button type="submit" class="button button-small button-grey">
327
- <?php _e( "Yes", "defender-security" ) ?>
328
- </button>
329
- <button type="button" class="button button-small button-secondary">
330
- <?php _e( "No", "defender-security" ) ?>
331
- </button>
332
- </div>
333
- </div>
334
- </form>
335
- <?php elseif ( $raw['type'] == 'modified' ): ?>
336
- <form method="post" class="scan-frm float-r resolve-item">
337
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
338
- <input type="hidden" name="action" value="resolveItem"/>
339
- <?php wp_nonce_field( 'resolveItem' ) ?>
340
- <button type="submit" class="button button-small">
341
- <?php _e( "Restore to Original", "defender-security" ) ?>
342
- </button>
343
- </form>
344
- <?php endif; ?>
345
- <div class="clear"></div>
346
- </div>
347
- </div>
348
- </div>
349
- </div>
350
- </dialog>
351
- <?php
352
- return ob_get_clean();
353
- }
354
-
355
  /**
356
  * @return string
357
  */
@@ -359,114 +139,27 @@ class Core_Result extends Behavior {
359
  if ( is_file( $this->getSubtitle() ) || is_dir( $this->getSubtitle() ) ) {
360
  $raw = $this->getRaw();
361
  if ( $raw['type'] == 'unknown' ) {
362
- $ext = pathinfo( $this->getSubtitle(), PATHINFO_EXTENSION );
363
- $ext = strtolower( $ext );
364
- $allowed = wp_get_ext_types();
365
- $allowed = array_merge( $allowed['code'], array(
366
- 'sql',
367
- 'text',
368
- 'log'
369
- ) );
370
- if ( in_array( $ext, $allowed ) ) {
371
- $content = file_get_contents( $this->getSubtitle() );
372
- if ( function_exists( 'mb_convert_encoding' ) ) {
373
- $content = mb_convert_encoding( $content, 'UTF-8', 'ASCII' );
374
- }
375
-
376
- $entities = htmlentities( $content, null, 'UTF-8', false );
377
-
378
- return '<div><strong>' . __( "Current code", "defender-security" ) . '</strong><pre><code class="html">' . $entities . '</code></pre></div>';
379
  }
 
 
 
380
  } elseif ( $raw['type'] == 'modified' ) {
381
  $original = $this->getOriginalSource();
382
  $current = file_get_contents( $this->getSubtitle() );
383
  $diff = $this->textDiff( $original, $current );
384
 
385
- return '<div><strong>' . __( "Current code", "defender-security" ) . '</strong><pre><code class="html">' . $diff . '</code></pre></div>';
386
  } elseif ( $raw['type'] == 'dir' ) {
387
  $files = File_Helper::findFiles( $raw['file'], true, false );
388
 
389
- return '<div><strong>' . __( "Current code", "defender-security" ) . '</strong><pre><code class="html">' . implode( PHP_EOL, $files ) . '</code></pre></div>';
390
  }
391
  }
392
  }
393
 
394
- /**
395
- * Show more detail about unknown file
396
- */
397
- private function _dialogContentForAdded() {
398
- ?>
399
- <p class="line">
400
- <?php _e( "A stray file has been found in your site directory, which your version of WordPress doesn't need. As far as we can tell, the file is harmless (and maybe even from an older WordPress install) so it's safe to ignore it. If you choose to delete the file, we recommend backing up your website beforehand", "defender-security" ) ?>
401
- </p>
402
- <?php
403
- $ext = pathinfo( $this->getSubtitle(), PATHINFO_EXTENSION );
404
- $ext = strtolower( $ext );
405
- $allowed = wp_get_ext_types();
406
- $allowed = array_merge( $allowed['code'], array(
407
- 'sql',
408
- 'text',
409
- 'log'
410
- ) );
411
- if ( in_array( $ext, $allowed ) ) {
412
- ?>
413
- <div class="mline source-code">
414
- <img src="<?php echo wp_defender()->getPluginUrl() ?>assets/img/loading.gif" width="18"
415
- height="18"/>
416
- <?php _e( "Pulling source file...", "defender-security" ) ?>
417
- <form method="post" class="float-l pull-src scan-frm">
418
- <input type="hidden" name="action" value="pullSrcFile">
419
- <?php wp_nonce_field( 'pullSrcFile' ) ?>
420
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
421
- </form>
422
- </div>
423
- <?php
424
- }
425
- }
426
-
427
- /**
428
- *
429
- */
430
- private function _dialogContentForModified() {
431
- ?>
432
- <p class="line">
433
- <?php _e( "Compare your file with the original file in the WordPress repository. Pieces highlighted in red will be removed when you patch the file, and pieces highlighted in green will be added.", "defender-security" ) ?>
434
- </p>
435
- <div class="mline source-code">
436
- <img src="<?php echo wp_defender()->getPluginUrl() ?>assets/img/loading.gif" width="18"
437
- height="18"/>
438
- <?php _e( "Pulling source file...", "defender-security" ) ?>
439
- <form method="post" class="float-l pull-src scan-frm">
440
- <input type="hidden" name="action" value="pullSrcFile">
441
- <?php wp_nonce_field( 'pullSrcFile' ) ?>
442
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
443
- </form>
444
- </div>
445
- <?php
446
-
447
- }
448
-
449
- /**
450
- * Show more detail about modified file
451
- */
452
- private function _dialogContentForDir() {
453
- ?>
454
- <p>
455
- <?php _e( "We found this folder in your WordPress file list. Your current version of WordPress doesn’t use this folder so it might belong to another application. If you don’t recognize it, you can delete this folder (don’t forget to back up your website first!) or get in touch with the WPMU DEV support team for more information.", "defender-security" ) ?>
456
- </p>
457
- <div class="mline source-code">
458
- <img src="<?php echo wp_defender()->getPluginUrl() ?>assets/img/loading.gif" width="18"
459
- height="18"/>
460
- <?php _e( "Pulling source file...", "defender-security" ) ?>
461
- <form method="post" class="float-l pull-src scan-frm">
462
- <input type="hidden" name="action" value="pullSrcFile">
463
- <?php wp_nonce_field( 'pullSrcFile' ) ?>
464
- <input type="hidden" name="id" value="<?php echo $this->getOwner()->id ?>"/>
465
- </form>
466
- </div>
467
- <?php
468
- }
469
-
470
  /**
471
  * @param $left_string
472
  * @param $right_string
8
  use Hammer\Base\Behavior;
9
  use Hammer\Helper\File_Helper;
10
  use Hammer\Helper\Log_Helper;
11
+ use WP_Defender\Behavior\Utils;
12
  use WP_Defender\Component\Error_Code;
13
  use WP_Defender\Module\Scan\Component\Scan_Api;
14
  use WP_Defender\Module\Scan\Model\Result_Item;
15
 
16
  class Core_Result extends Behavior {
17
+
18
+ /**
19
+ * Query all the info to show up on frontend
20
+ * @return array
21
+ */
22
+ public function getInfo() {
23
+ $full_path = $this->getRaw()['file'];
24
+
25
+ return [
26
+ 'id' => $this->getOwner()->id,
27
+ 'type' => 'core',
28
+ 'file_name' => pathinfo( $full_path, PATHINFO_FILENAME ),
29
+ 'full_path' => $full_path,
30
+ 'date_added' => Utils::instance()->formatDateTime( filemtime( $full_path ) ),
31
+ 'size' => Utils::instance()->makeReadable( filesize( $full_path ) ),
32
+ 'scenario' => $this->getRaw()['type'],
33
+ 'short_desc' => $this->getIssueDetail()
34
+ ];
35
+ }
36
+
37
  /**
38
  * @return string
39
  */
62
  return $raw['file'];
63
  }
64
 
 
 
 
 
 
 
 
65
  /**
66
  * @return string
67
  */
77
  if ( $raw['type'] == 'unknown' ) {
78
  return esc_html__( "Unknown file in WordPress core", "defender-security" );
79
  } elseif ( $raw['type'] == 'dir' ) {
80
+ return esc_html__( "This directory does not belong to WordPress core", "defender-security" );
81
  } elseif ( $raw['type'] == 'modified' ) {
82
  return esc_html__( "This WordPress core file appears modified", "defender-security" );
83
  }
132
  return true;
133
  }
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  /**
136
  * @return string
137
  */
139
  if ( is_file( $this->getSubtitle() ) || is_dir( $this->getSubtitle() ) ) {
140
  $raw = $this->getRaw();
141
  if ( $raw['type'] == 'unknown' ) {
142
+ $content = file_get_contents( $this->getSubtitle() );
143
+ if ( function_exists( 'mb_convert_encoding' ) ) {
144
+ $content = mb_convert_encoding( $content, 'UTF-8', 'ASCII' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
  }
146
+ $entities = htmlentities( $content, null, 'UTF-8', false );
147
+
148
+ return $entities;
149
  } elseif ( $raw['type'] == 'modified' ) {
150
  $original = $this->getOriginalSource();
151
  $current = file_get_contents( $this->getSubtitle() );
152
  $diff = $this->textDiff( $original, $current );
153
 
154
+ return $diff;
155
  } elseif ( $raw['type'] == 'dir' ) {
156
  $files = File_Helper::findFiles( $raw['file'], true, false );
157
 
158
+ return implode( PHP_EOL, $files );
159
  }
160
  }
161
  }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  /**
164
  * @param $left_string
165
  * @param $right_string
app/module/scan/component/data-factory.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Scan\Component;
7
+
8
+
9
+ use WP_Defender\Behavior\Utils;
10
+ use WP_Defender\Module\Scan;
11
+ use WP_Defender\Module\Scan\Model\Result_Item;
12
+ use WP_Defender\Module\Scan\Model\Settings;
13
+
14
+ class Data_Factory {
15
+ /**
16
+ * Build the data we use to output when page load
17
+ * @return array
18
+ */
19
+ public static function buildData() {
20
+ $lastScan = Scan_Api::getLastScan();
21
+ $model = Scan_Api::getActiveScan();
22
+ if ( $lastScan === null && $model === null ) {
23
+ /**
24
+ * this case no scan
25
+ */
26
+ $scan = null;
27
+ } elseif ( is_object( $model ) ) {
28
+ /**
29
+ * This case there is a scan on progress, show status, status text and percent
30
+ */
31
+ $scan = [
32
+ 'status' => $model->status,
33
+ 'status_text' => $model->statusText,
34
+ 'percent' => Scan_Api::getScanProgress( true )
35
+ ];
36
+ } else {
37
+ $issuesItems = $lastScan->getItemsAsJson( 0, Result_Item::STATUS_ISSUE, null );
38
+ $scan = [
39
+ 'status' => $lastScan->status,
40
+ 'issues_items' => $issuesItems,
41
+ 'ignored_items' => $lastScan->getItemsAsJson( 0, Result_Item::STATUS_IGNORED, null ),
42
+ 'last_scan' => Utils::instance()->formatDateTime( $lastScan->dateFinished ),
43
+ 'count' => [
44
+ 'total' => count( $issuesItems ),
45
+ 'core' => (int) $lastScan->getCount( 'core' ),
46
+ 'content' => (int) $lastScan->getCount( 'content' ),
47
+ 'vuln' => (int) $lastScan->getCount( 'vuln' )
48
+ ]
49
+ ];
50
+ }
51
+ $settings = Settings::instance();
52
+
53
+ return [
54
+ 'scan' => $scan,
55
+ 'model' => [
56
+ 'settings' => $settings->exportByKeys( [
57
+ 'scan_core',
58
+ 'scan_vuln',
59
+ 'scan_content',
60
+ 'max_filesize'
61
+ ] ),
62
+ 'reporting' => $settings->exportByKeys( [
63
+ 'report',
64
+ 'always_send',
65
+ 'recipients',
66
+ 'day',
67
+ 'time',
68
+ 'frequency'
69
+ ] ),
70
+ 'notification' => $settings->exportByKeys( [
71
+ 'notification',
72
+ 'always_send_notification',
73
+ 'recipients_notification',
74
+ 'email_subject',
75
+ 'email_all_ok',
76
+ 'email_has_issue'
77
+ ] )
78
+ ],
79
+ 'misc' => [
80
+ "times_of_day" => Utils::instance()->getTimes(),
81
+ "days_of_week" => Utils::instance()->getDaysOfWeek()
82
+ ],
83
+ ];
84
+ }
85
+
86
+ /**
87
+ * Build data for dashboard page
88
+ * @return array
89
+ */
90
+ public static function buildLiteData() {
91
+ $data = self::buildData();
92
+ unset( $data['model'] );
93
+ unset( $data['misc'] );
94
+
95
+ return $data;
96
+ }
97
+ }
app/module/scan/component/scan-api.php CHANGED
@@ -22,37 +22,38 @@ use WP_Defender\Module\Scan\Model\Settings;
22
  class Scan_Api extends Component {
23
  const CACHE_CORE = 'wdfcore', CACHE_CONTENT = 'wdfcontent', CACHE_CHECKSUMS = 'wdfchecksum';
24
  const IGNORE_LIST = 'wdfscanignore', SCAN_PATTERN = 'wdfscanparttern';
25
-
26
  private static $ignoreList = false;
27
-
28
  public static $scanResults = array();
29
-
30
  /**
31
  * @return Scan|\WP_Error
32
  */
33
  public static function createScan() {
34
  if ( is_null( self::getActiveScan() ) ) {
35
- self::flushCache( false );
36
-
37
  $model = new Scan();
38
  $model->status = Scan::STATUS_INIT;
39
  $model->statusText = __( "Initializing...", "defender-security" );
40
  $model->save();
41
-
42
  return $model;
43
  } else {
44
  return new \WP_Error( Error_Code::INVALID, __( "A scan is already in progress", "defender-security" ) );
45
  }
46
  }
47
-
48
  /**
49
  * Check if this module is active
50
  */
51
  public static function isActive() {
52
  return Settings::instance()->notification;
53
  }
54
-
55
  /**
 
56
  * @return null|Scan
57
  */
58
  public static function getActiveScan() {
@@ -67,12 +68,12 @@ class Scan_Api extends Component {
67
  Scan::STATUS_PROCESS
68
  )
69
  ) );
70
-
71
  $cache->set( 'activeScan', $model );
72
-
73
  return $model;
74
  }
75
-
76
  /**
77
  * @return null|Scan
78
  */
@@ -86,13 +87,13 @@ class Scan_Api extends Component {
86
  Scan::STATUS_FINISH
87
  )
88
  ), 'ID', 'DESC' );
89
-
90
  $cache->set( 'lastScan', $model );
91
-
92
-
93
  return $model;
94
  }
95
-
96
  /**
97
  * @return array
98
  */
@@ -105,29 +106,32 @@ class Scan_Api extends Component {
105
  if ( is_array( $cached ) && ! empty( $cached ) ) {
106
  return $cached;
107
  }
108
-
109
  $settings = Settings::instance();
110
  $firstLevelFiles = File_Helper::findFiles( ABSPATH, true, true, array(
111
- 'dir' => array(
112
  ABSPATH . 'wp-content',
113
  ABSPATH . 'wp-admin',
114
  ABSPATH . 'wp-includes'
115
  ),
116
- 'path' => array(
117
- ABSPATH . 'wp-config.php'
118
  )
119
- ), array(), false, $settings->max_filesize );
120
- $coreFiles = File_Helper::findFiles( ABSPATH, true, false, array(), array(
 
121
  'dir' => array(
122
  ABSPATH . 'wp-admin',
123
  ABSPATH . 'wp-includes',
124
  )
125
- ), true, $settings->max_filesize );
126
- $cache->set( self::CACHE_CORE, array_merge( $firstLevelFiles, $coreFiles ), 0 );
127
-
128
- return array_merge( $firstLevelFiles, $coreFiles );
 
 
129
  }
130
-
131
  /**
132
  * @return array
133
  */
@@ -141,17 +145,14 @@ class Scan_Api extends Component {
141
  $files = File_Helper::findFiles( WP_CONTENT_DIR, true, false, array(), array(
142
  'ext' => array( 'php' )
143
  ), true, $settings->max_filesize, true );
144
- // $files = File_Helper::findFiles( ABSPATH . 'wp-content/randomly/a', true, false, array(), array(
145
- // 'ext' => array( 'php' )
146
- // ), true, $settings->max_filesize );
147
  //include wp-config.php here
148
  $files[] = ABSPATH . 'wp-config.php';
149
-
150
  $cache->set( self::CACHE_CONTENT, $files );
151
-
152
  return $files;
153
  }
154
-
155
  /**
156
  * Get checksums
157
  * @return array|bool
@@ -162,7 +163,7 @@ class Scan_Api extends Component {
162
  if ( is_array( $cached ) && ! empty( $cached ) ) {
163
  return $cached;
164
  }
165
-
166
  global $wp_version, $wp_local_package;
167
  $locale = 'en_US';
168
  if ( ! is_null( $wp_local_package ) && count( explode( '_', $wp_local_package ) ) == 2 ) {
@@ -175,16 +176,16 @@ class Scan_Api extends Component {
175
  if ( $checksum == false ) {
176
  return $checksum;
177
  }
178
-
179
  if ( isset( $checksum[ $wp_version ] ) ) {
180
  return $checksum = $checksum[ $wp_version ];
181
  }
182
-
183
  $cache->set( self::CACHE_CHECKSUMS, $checksum, 86400 );
184
-
185
  return $checksum;
186
  }
187
-
188
  /**
189
  * Processing a scan, return bool if done or not
190
  * @return bool|\WP_Error
@@ -195,14 +196,14 @@ class Scan_Api extends Component {
195
  if ( ! is_object( $model ) ) {
196
  return new \WP_Error( Error_Code::INVALID, __( "No scan record exists", "defender-security" ) );
197
  }
198
-
199
  if ( $model->status == Scan::STATUS_ERROR ) {
200
  //stop scan
201
  self::releaseLock();
202
-
203
  return new \WP_Error( Error_Code::SCAN_ERROR, $model->statusText );
204
  }
205
-
206
  $settings = Settings::instance();
207
  $steps = $settings->getScansAvailable();
208
  $done = 0;
@@ -213,7 +214,7 @@ class Scan_Api extends Component {
213
  //create a safe lock
214
  self::createLock();
215
  }
216
-
217
  /**
218
  * loop through scanning steps, instance scan step as queue and process
219
  */
@@ -222,14 +223,14 @@ class Scan_Api extends Component {
222
  'model' => $model,
223
  'ignoreList' => self::getIgnoreList()
224
  ) );
225
-
226
  if ( ! is_object( $queue ) || $queue->isEnd() ) {
227
  $done ++;
228
  continue;
229
  }
230
-
231
  $lastPost = $queue->key();
232
-
233
  if ( $lastPost == 0 ) {
234
  //this is newly, we will update the status text here
235
  switch ( $step ) {
@@ -260,17 +261,21 @@ class Scan_Api extends Component {
260
  $queue->next();
261
  $queue->saveProcess();
262
  self::releaseLock();
263
-
264
  return false;
265
  } else {
266
  //each request onlly allow 10s, or when reached to 64MB ram
267
- $est = microtime( true ) - $start;
268
- $currMem = ( memory_get_peak_usage( true ) / 1024 / 1024 );
 
 
 
 
269
  $memLimit = apply_filters( 'defender_scan_memory_alloc', 128 );
270
  if ( $est >= 15 || $currMem >= $memLimit || $queue->isEnd() || $queue->key() == 1 ) {
271
  //save current process and pause
272
  $queue->saveProcess();
273
-
274
  //unlock before return
275
  self::releaseLock();
276
  //we have to cache the checksum of content here
@@ -286,7 +291,7 @@ class Scan_Api extends Component {
286
  $cache->set( Content_Scan::FILES_TRIED, $tries );
287
  }
288
  }
289
-
290
  return false;
291
  }
292
  }
@@ -294,7 +299,7 @@ class Scan_Api extends Component {
294
  //break at the end to prevent it stuck so long when init, also the heavy part is in the while loop
295
  break;
296
  }
297
-
298
  if ( $done == count( $steps ) ) {
299
  //all done
300
  //remove all old records
@@ -306,19 +311,19 @@ class Scan_Api extends Component {
306
  $model->status = Scan::STATUS_FINISH;
307
  $model->save();
308
  if ( $model->logs == 'report' ) {
309
- $settings->lastReportSent = time();
310
  $settings->save();
311
  }
312
  self::flushCache();
313
  self::releaseLock();
314
-
315
  return true;
316
  }
317
  self::releaseLock();
318
-
319
  return false;
320
  }
321
-
322
  /**
323
  * remove all scan models
324
  */
@@ -328,7 +333,7 @@ class Scan_Api extends Component {
328
  $model->delete();
329
  }
330
  }
331
-
332
  /**
333
  * Ignoe list will be a global array, so it can share from each scan
334
  * @return Result_Item[]
@@ -337,7 +342,7 @@ class Scan_Api extends Component {
337
  if ( is_array( self::$ignoreList ) ) {
338
  return self::$ignoreList;
339
  }
340
-
341
  $ids = get_site_option( self::IGNORE_LIST );
342
  if ( $ids == false ) {
343
  $cache = Container::instance()->get( 'cache' );
@@ -346,23 +351,23 @@ class Scan_Api extends Component {
346
  } elseif ( ! is_array( $ids ) ) {
347
  $ids = unserialize( $ids );
348
  }
349
-
350
  if ( empty( $ids ) ) {
351
  self::$ignoreList = array();
352
-
353
  return array();
354
  }
355
-
356
  $ignoreList = Result_Item::findAll( array(
357
  'id' => $ids
358
  ) );
359
-
360
  self::$ignoreList = $ignoreList;
361
-
362
-
363
  return $ignoreList;
364
  }
365
-
366
  /**
367
  * Check if a file get ignored
368
  *
@@ -377,10 +382,10 @@ class Scan_Api extends Component {
377
  return $model->id;
378
  }
379
  }
380
-
381
  return false;
382
  }
383
-
384
  /**
385
  * Add an item to ignore list
386
  *
@@ -400,7 +405,7 @@ class Scan_Api extends Component {
400
  $ids[] = $id;
401
  update_site_option( self::IGNORE_LIST, $ids );
402
  }
403
-
404
  /**
405
  * Remove an item from ignore list
406
  *
@@ -420,12 +425,25 @@ class Scan_Api extends Component {
420
  unset( $ids[ array_search( $id, $ids ) ] );
421
  update_site_option( self::IGNORE_LIST, $ids );
422
  }
423
-
424
  /**
425
- * Get current percent of scan in decimal
426
- * @return float
 
 
 
427
  */
428
- public static function getScanProgress() {
 
 
 
 
 
 
 
 
 
 
429
  $settings = Settings::instance();
430
  $steps = $settings->getScansAvailable();
431
  $total = 0;
@@ -441,17 +459,16 @@ class Scan_Api extends Component {
441
  }
442
  }
443
  }
444
- if ( php_sapi_name() == "cli" ) {
445
- echo $total . PHP_EOL;
446
- }
447
-
448
  if ( $total > 0 ) {
449
- return round( ( $currentIndex / $total ) * 100, 2 );
450
- } else {
451
- return ( 0 );
452
  }
 
 
 
453
  }
454
-
455
  /**
456
  * flush all cache generated during scan process
457
  */
@@ -474,11 +491,14 @@ class Scan_Api extends Component {
474
  delete_site_option( self::SCAN_PATTERN );
475
  $cache->delete( 'filestried' );
476
  $cache->delete( self::CACHE_CHECKSUMS );
 
477
  $altCache = WP_Helper::getArrayCache();
478
  $altCache->delete( 'lastScan' );
 
 
479
  File_Helper::deleteFolder( Utils::instance()->getDefUploadDir() . '/md5-scan' );
480
  }
481
-
482
  /**
483
  * A function for dealing with windows host, as wordpress checksums path all in UNIX format
484
  *
@@ -500,11 +520,11 @@ class Scan_Api extends Component {
500
  if ( DIRECTORY_SEPARATOR == '\\' ) {
501
  $relative_path = str_replace( '\\', '', $relative_path ); //Make sure the files do not have a /filename.etension or checksum fails
502
  }
503
-
504
  return $relative_path;
505
  }
506
-
507
-
508
  /**
509
  * A function for dealing with windows host, Fixes the URL path on Windows
510
  *
@@ -517,18 +537,18 @@ class Scan_Api extends Component {
517
  if ( DIRECTORY_SEPARATOR == '\\' ) {
518
  $abs_path = rtrim( ABSPATH, '/' );
519
  $abs_path = $abs_path . '\\';
520
-
521
  //now getting the relative path
522
  $abs_path = str_replace( $abs_path, '', $file );
523
  $abs_path = str_replace( '\\', '/', $abs_path );
524
  $abs_path = str_replace( '//', '/', $abs_path );
525
-
526
  return $abs_path;
527
  }
528
-
529
  return $file;
530
  }
531
-
532
  /**
533
  * Get the schedule time for a scan
534
  *
@@ -565,7 +585,7 @@ class Scan_Api extends Component {
565
  return $toUTC;
566
  }
567
  }
568
-
569
  /**
570
  * Create a lock
571
  * @return int
@@ -575,12 +595,12 @@ class Scan_Api extends Component {
575
  if ( ! is_dir( $lockPath ) ) {
576
  wp_mkdir_p( $lockPath );
577
  }
578
-
579
  $lockFile = $lockPath . 'scan-lock';
580
-
581
  return file_put_contents( $lockFile, time(), LOCK_EX );
582
  }
583
-
584
  /**
585
  * @return bool
586
  */
@@ -590,18 +610,18 @@ class Scan_Api extends Component {
590
  if ( ! is_file( $lockFile ) ) {
591
  return false;
592
  }
593
-
594
  $time = file_get_contents( $lockFile );
595
  if ( strtotime( '+1 minutes', $time ) < time() ) {
596
  //this lock locked for too long, unlock it
597
  @unlink( $lockFile );
598
-
599
  return false;
600
  }
601
-
602
  return true;
603
  }
604
-
605
  /**
606
  * release a lock
607
  */
@@ -610,7 +630,7 @@ class Scan_Api extends Component {
610
  $lockFile = $lockPath . 'scan-lock';
611
  @unlink( $lockFile );
612
  }
613
-
614
  /**
615
  * @return array|mixed|object|\WP_Error
616
  */
@@ -619,13 +639,13 @@ class Scan_Api extends Component {
619
  if ( ! is_object( $activeScan ) ) {
620
  return array();
621
  }
622
-
623
  $patterns = get_site_option( Scan_Api::SCAN_PATTERN, null );
624
  if ( is_array( $patterns ) ) {
625
  //return pattern if that exists, no matter the content
626
  return $patterns;
627
  }
628
-
629
  $api_endpoint = "https://premium.wpmudev.org/api/defender/v1/signatures";
630
  $patterns = Utils::instance()->devCall( $api_endpoint, array(), array(
631
  'method' => 'GET'
@@ -633,10 +653,10 @@ class Scan_Api extends Component {
633
  if ( is_wp_error( $patterns ) || $patterns == false ) {
634
  $patterns = array();
635
  }
636
-
637
-
638
  update_site_option( Scan_Api::SCAN_PATTERN, $patterns );
639
-
640
  return $patterns;
641
  }
642
  }
22
  class Scan_Api extends Component {
23
  const CACHE_CORE = 'wdfcore', CACHE_CONTENT = 'wdfcontent', CACHE_CHECKSUMS = 'wdfchecksum';
24
  const IGNORE_LIST = 'wdfscanignore', SCAN_PATTERN = 'wdfscanparttern';
25
+
26
  private static $ignoreList = false;
27
+
28
  public static $scanResults = array();
29
+
30
  /**
31
  * @return Scan|\WP_Error
32
  */
33
  public static function createScan() {
34
  if ( is_null( self::getActiveScan() ) ) {
35
+ self::flushCache();
36
+
37
  $model = new Scan();
38
  $model->status = Scan::STATUS_INIT;
39
  $model->statusText = __( "Initializing...", "defender-security" );
40
  $model->save();
41
+
42
  return $model;
43
  } else {
44
  return new \WP_Error( Error_Code::INVALID, __( "A scan is already in progress", "defender-security" ) );
45
  }
46
  }
47
+
48
  /**
49
  * Check if this module is active
50
  */
51
  public static function isActive() {
52
  return Settings::instance()->notification;
53
  }
54
+
55
  /**
56
+ * Get the current scan on going
57
  * @return null|Scan
58
  */
59
  public static function getActiveScan() {
68
  Scan::STATUS_PROCESS
69
  )
70
  ) );
71
+
72
  $cache->set( 'activeScan', $model );
73
+
74
  return $model;
75
  }
76
+
77
  /**
78
  * @return null|Scan
79
  */
87
  Scan::STATUS_FINISH
88
  )
89
  ), 'ID', 'DESC' );
90
+
91
  $cache->set( 'lastScan', $model );
92
+
93
+
94
  return $model;
95
  }
96
+
97
  /**
98
  * @return array
99
  */
106
  if ( is_array( $cached ) && ! empty( $cached ) ) {
107
  return $cached;
108
  }
109
+
110
  $settings = Settings::instance();
111
  $firstLevelFiles = File_Helper::findFiles( ABSPATH, true, true, array(
112
+ 'dir' => array(
113
  ABSPATH . 'wp-content',
114
  ABSPATH . 'wp-admin',
115
  ABSPATH . 'wp-includes'
116
  ),
117
+ 'filename' => array(
118
+ 'wp-config.php'
119
  )
120
+ ), array(), false );
121
+
122
+ $coreFiles = File_Helper::findFiles( ABSPATH, true, false, array(), array(
123
  'dir' => array(
124
  ABSPATH . 'wp-admin',
125
  ABSPATH . 'wp-includes',
126
  )
127
+ ), true );
128
+ $files = array_merge( $firstLevelFiles, $coreFiles );
129
+ $files = apply_filters( 'wd_core_files', $files );
130
+ $cache->set( self::CACHE_CORE, $files, 0 );
131
+
132
+ return $files;
133
  }
134
+
135
  /**
136
  * @return array
137
  */
145
  $files = File_Helper::findFiles( WP_CONTENT_DIR, true, false, array(), array(
146
  'ext' => array( 'php' )
147
  ), true, $settings->max_filesize, true );
 
 
 
148
  //include wp-config.php here
149
  $files[] = ABSPATH . 'wp-config.php';
150
+ $files = apply_filters( 'wd_content_files', $files );
151
  $cache->set( self::CACHE_CONTENT, $files );
152
+
153
  return $files;
154
  }
155
+
156
  /**
157
  * Get checksums
158
  * @return array|bool
163
  if ( is_array( $cached ) && ! empty( $cached ) ) {
164
  return $cached;
165
  }
166
+
167
  global $wp_version, $wp_local_package;
168
  $locale = 'en_US';
169
  if ( ! is_null( $wp_local_package ) && count( explode( '_', $wp_local_package ) ) == 2 ) {
176
  if ( $checksum == false ) {
177
  return $checksum;
178
  }
179
+
180
  if ( isset( $checksum[ $wp_version ] ) ) {
181
  return $checksum = $checksum[ $wp_version ];
182
  }
183
+
184
  $cache->set( self::CACHE_CHECKSUMS, $checksum, 86400 );
185
+
186
  return $checksum;
187
  }
188
+
189
  /**
190
  * Processing a scan, return bool if done or not
191
  * @return bool|\WP_Error
196
  if ( ! is_object( $model ) ) {
197
  return new \WP_Error( Error_Code::INVALID, __( "No scan record exists", "defender-security" ) );
198
  }
199
+
200
  if ( $model->status == Scan::STATUS_ERROR ) {
201
  //stop scan
202
  self::releaseLock();
203
+
204
  return new \WP_Error( Error_Code::SCAN_ERROR, $model->statusText );
205
  }
206
+
207
  $settings = Settings::instance();
208
  $steps = $settings->getScansAvailable();
209
  $done = 0;
214
  //create a safe lock
215
  self::createLock();
216
  }
217
+
218
  /**
219
  * loop through scanning steps, instance scan step as queue and process
220
  */
223
  'model' => $model,
224
  'ignoreList' => self::getIgnoreList()
225
  ) );
226
+
227
  if ( ! is_object( $queue ) || $queue->isEnd() ) {
228
  $done ++;
229
  continue;
230
  }
231
+
232
  $lastPost = $queue->key();
233
+
234
  if ( $lastPost == 0 ) {
235
  //this is newly, we will update the status text here
236
  switch ( $step ) {
261
  $queue->next();
262
  $queue->saveProcess();
263
  self::releaseLock();
264
+
265
  return false;
266
  } else {
267
  //each request onlly allow 10s, or when reached to 64MB ram
268
+ $est = microtime( true ) - $start;
269
+ $currMem = ( memory_get_peak_usage( true ) / 1024 / 1024 );
270
+ if ( php_sapi_name() == 'cli' ) {
271
+ echo $queue->current() . PHP_EOL;
272
+ }
273
+ //echo $currMem . PHP_EOL;
274
  $memLimit = apply_filters( 'defender_scan_memory_alloc', 128 );
275
  if ( $est >= 15 || $currMem >= $memLimit || $queue->isEnd() || $queue->key() == 1 ) {
276
  //save current process and pause
277
  $queue->saveProcess();
278
+
279
  //unlock before return
280
  self::releaseLock();
281
  //we have to cache the checksum of content here
291
  $cache->set( Content_Scan::FILES_TRIED, $tries );
292
  }
293
  }
294
+
295
  return false;
296
  }
297
  }
299
  //break at the end to prevent it stuck so long when init, also the heavy part is in the while loop
300
  break;
301
  }
302
+
303
  if ( $done == count( $steps ) ) {
304
  //all done
305
  //remove all old records
311
  $model->status = Scan::STATUS_FINISH;
312
  $model->save();
313
  if ( $model->logs == 'report' ) {
314
+ $settings->last_report_sent = time();
315
  $settings->save();
316
  }
317
  self::flushCache();
318
  self::releaseLock();
319
+
320
  return true;
321
  }
322
  self::releaseLock();
323
+
324
  return false;
325
  }
326
+
327
  /**
328
  * remove all scan models
329
  */
333
  $model->delete();
334
  }
335
  }
336
+
337
  /**
338
  * Ignoe list will be a global array, so it can share from each scan
339
  * @return Result_Item[]
342
  if ( is_array( self::$ignoreList ) ) {
343
  return self::$ignoreList;
344
  }
345
+
346
  $ids = get_site_option( self::IGNORE_LIST );
347
  if ( $ids == false ) {
348
  $cache = Container::instance()->get( 'cache' );
351
  } elseif ( ! is_array( $ids ) ) {
352
  $ids = unserialize( $ids );
353
  }
354
+
355
  if ( empty( $ids ) ) {
356
  self::$ignoreList = array();
357
+
358
  return array();
359
  }
360
+
361
  $ignoreList = Result_Item::findAll( array(
362
  'id' => $ids
363
  ) );
364
+
365
  self::$ignoreList = $ignoreList;
366
+
367
+
368
  return $ignoreList;
369
  }
370
+
371
  /**
372
  * Check if a file get ignored
373
  *
382
  return $model->id;
383
  }
384
  }
385
+
386
  return false;
387
  }
388
+
389
  /**
390
  * Add an item to ignore list
391
  *
405
  $ids[] = $id;
406
  update_site_option( self::IGNORE_LIST, $ids );
407
  }
408
+
409
  /**
410
  * Remove an item from ignore list
411
  *
425
  unset( $ids[ array_search( $id, $ids ) ] );
426
  update_site_option( self::IGNORE_LIST, $ids );
427
  }
428
+
429
  /**
430
+ * Get current percent, if $getFromCache set to true, we will get the last cached value
431
+ *
432
+ * @param bool $getFromCache
433
+ *
434
+ * @return float|int
435
  */
436
+ public static function getScanProgress( $getFromCache = false ) {
437
+ $cache = WP_Helper::getCache();
438
+ if ( $getFromCache ) {
439
+ $cache = $cache->get( 'defenderScanPercent' );
440
+ if ( $cache == false ) {
441
+ return 0;
442
+ }
443
+
444
+ return $cache;
445
+ }
446
+
447
  $settings = Settings::instance();
448
  $steps = $settings->getScansAvailable();
449
  $total = 0;
459
  }
460
  }
461
  }
462
+
463
+ $percent = 0;
 
 
464
  if ( $total > 0 ) {
465
+ $percent = round( ( $currentIndex / $total ) * 100, 2 );
 
 
466
  }
467
+ $cache->set( 'defenderScanPercent', $percent, 3600 );
468
+
469
+ return $percent;
470
  }
471
+
472
  /**
473
  * flush all cache generated during scan process
474
  */
491
  delete_site_option( self::SCAN_PATTERN );
492
  $cache->delete( 'filestried' );
493
  $cache->delete( self::CACHE_CHECKSUMS );
494
+ $cache->delete( 'defenderScanPercent' );
495
  $altCache = WP_Helper::getArrayCache();
496
  $altCache->delete( 'lastScan' );
497
+ $altCache->delete( 'activeScan' );
498
+ Scan_Api::releaseLock();
499
  File_Helper::deleteFolder( Utils::instance()->getDefUploadDir() . '/md5-scan' );
500
  }
501
+
502
  /**
503
  * A function for dealing with windows host, as wordpress checksums path all in UNIX format
504
  *
520
  if ( DIRECTORY_SEPARATOR == '\\' ) {
521
  $relative_path = str_replace( '\\', '', $relative_path ); //Make sure the files do not have a /filename.etension or checksum fails
522
  }
523
+
524
  return $relative_path;
525
  }
526
+
527
+
528
  /**
529
  * A function for dealing with windows host, Fixes the URL path on Windows
530
  *
537
  if ( DIRECTORY_SEPARATOR == '\\' ) {
538
  $abs_path = rtrim( ABSPATH, '/' );
539
  $abs_path = $abs_path . '\\';
540
+
541
  //now getting the relative path
542
  $abs_path = str_replace( $abs_path, '', $file );
543
  $abs_path = str_replace( '\\', '/', $abs_path );
544
  $abs_path = str_replace( '//', '/', $abs_path );
545
+
546
  return $abs_path;
547
  }
548
+
549
  return $file;
550
  }
551
+
552
  /**
553
  * Get the schedule time for a scan
554
  *
585
  return $toUTC;
586
  }
587
  }
588
+
589
  /**
590
  * Create a lock
591
  * @return int
595
  if ( ! is_dir( $lockPath ) ) {
596
  wp_mkdir_p( $lockPath );
597
  }
598
+
599
  $lockFile = $lockPath . 'scan-lock';
600
+
601
  return file_put_contents( $lockFile, time(), LOCK_EX );
602
  }
603
+
604
  /**
605
  * @return bool
606
  */
610
  if ( ! is_file( $lockFile ) ) {
611
  return false;
612
  }
613
+
614
  $time = file_get_contents( $lockFile );
615
  if ( strtotime( '+1 minutes', $time ) < time() ) {
616
  //this lock locked for too long, unlock it
617
  @unlink( $lockFile );
618
+
619
  return false;
620
  }
621
+
622
  return true;
623
  }
624
+
625
  /**
626
  * release a lock
627
  */
630
  $lockFile = $lockPath . 'scan-lock';
631
  @unlink( $lockFile );
632
  }
633
+
634
  /**
635
  * @return array|mixed|object|\WP_Error
636
  */
639
  if ( ! is_object( $activeScan ) ) {
640
  return array();
641
  }
642
+
643
  $patterns = get_site_option( Scan_Api::SCAN_PATTERN, null );
644
  if ( is_array( $patterns ) ) {
645
  //return pattern if that exists, no matter the content
646
  return $patterns;
647
  }
648
+
649
  $api_endpoint = "https://premium.wpmudev.org/api/defender/v1/signatures";
650
  $patterns = Utils::instance()->devCall( $api_endpoint, array(), array(
651
  'method' => 'GET'
653
  if ( is_wp_error( $patterns ) || $patterns == false ) {
654
  $patterns = array();
655
  }
656
+
657
+
658
  update_site_option( Scan_Api::SCAN_PATTERN, $patterns );
659
+
660
  return $patterns;
661
  }
662
  }
app/module/scan/controller/main.php CHANGED
@@ -2,29 +2,28 @@
2
 
3
  namespace WP_Defender\Module\Scan\Controller;
4
 
5
- use Hammer\Helper\HTTP_Helper;
6
- use Hammer\Helper\Log_Helper;
7
  use WP_Defender\Module\Scan;
8
  use WP_Defender\Module\Scan\Component\Scan_Api;
9
  use WP_Defender\Module\Scan\Model\Settings;
10
  use WP_Defender\Module\Scan\Model\Result_Item;
11
- use WP_Defender\Vendor\Email_Search;
12
- use WP_Defender\Behavior\Utils;
13
 
14
  /**
15
  * Author: Hoang Ngo
16
  */
17
  class Main extends \WP_Defender\Controller {
18
  protected $slug = 'wdf-scan';
19
- public $layout = 'layout';
20
- private $email_search;
21
- private $emailSearchNotification;
22
 
23
  /**
24
  * @return array
25
  */
26
  public function behaviors() {
27
- $behaviors = array( 'utils' => '\WP_Defender\Behavior\Utils' );
 
 
 
 
 
28
  if ( wp_defender()->isFree == false ) {
29
  $behaviors['pro'] = '\WP_Defender\Module\Scan\Behavior\Pro\Reporting';
30
  }
@@ -36,49 +35,22 @@ class Main extends \WP_Defender\Controller {
36
  * Main constructor.
37
  */
38
  public function __construct() {
39
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
40
- $this->add_action( 'network_admin_menu', 'adminMenu' );
41
  } else {
42
- $this->add_action( 'admin_menu', 'adminMenu' );
43
  }
44
 
45
  if ( $this->isInPage() || $this->isDashboard() ) {
46
- $this->add_action( 'defender_enqueue_assets', 'scripts', 11 );
47
- }
48
-
49
- /**
50
- * ajax actions
51
- */
52
- $this->add_ajax_action( 'startAScan', 'startAScan' );
53
- $this->add_ajax_action( 'processScan', 'processScan' );
54
- $this->add_ajax_action( 'ignoreItem', 'ignoreItem' );
55
- $this->add_ajax_action( 'unIgnoreItem', 'unIgnoreItem' );
56
- $this->add_ajax_action( 'deleteItem', 'deleteItem' );
57
- $this->add_ajax_action( 'resolveItem', 'resolveItem' );
58
- $this->add_ajax_action( 'saveScanSettings', 'saveScanSettings' );
59
- $this->add_ajax_action( 'scanBulkAction', 'scanBulkAction' );
60
- $this->add_ajax_action( 'pullSrcFile', 'pullSrcFile' );
61
- $this->add_ajax_action( 'cancelScan', 'cancelScan' );
62
- $view = HTTP_Helper::retrieve_get( 'view' );
63
- $id = isset( $_REQUEST['id'] ) ? $_REQUEST['id'] : 0;
64
- //init receiption
65
- if ( $view == 'notification' && $this->isInPage() || ( defined( 'DOING_AJAX' ) && $id == 'scanNotificationReceipts' ) ) {
66
- $this->emailSearchNotification = new Email_Search();
67
- $this->emailSearchNotification->eId = 'scanNotificationReceipts';
68
- $this->emailSearchNotification->settings = Settings::instance();
69
- $this->emailSearchNotification->attribute = 'receiptsNotification';
70
- $this->emailSearchNotification->add_hooks();
71
- } elseif ( $view == 'reporting' || ( defined( 'DOING_AJAX' ) && $id == 'scan_receipts' ) ) {
72
- $this->email_search = new Email_Search();
73
- $this->email_search->eId = 'scan_receipts';
74
- $this->email_search->settings = Settings::instance();
75
- $this->email_search->add_hooks();
76
  }
77
 
78
  //process scan in background
79
- $this->add_action( 'processScanCron', 'processScanCron' );
80
  //scan as schedule
81
- $this->add_action( 'scanReportCron', 'scanReportCron' );
 
 
82
  }
83
 
84
  /**
@@ -90,12 +62,12 @@ class Main extends \WP_Defender\Controller {
90
  }
91
 
92
  $settings = Settings::instance();
93
- $lastReportSent = $settings->lastReportSent;
94
  if ( $lastReportSent == null ) {
95
  $model = Scan_Api::getLastScan();
96
  if ( is_object( $model ) ) {
97
- $lastReportSent = $model->dateFinished;
98
- $settings->lastReportSent = $lastReportSent;
99
  //init
100
  $settings->save();
101
  } else {
@@ -118,147 +90,6 @@ class Main extends \WP_Defender\Controller {
118
  }
119
  }
120
 
121
- public function cancelScan() {
122
- if ( ! $this->checkPermission() ) {
123
- return;
124
- }
125
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'cancelScan' ) ) {
126
- return;
127
- }
128
-
129
- $activeScan = Scan_Api::getActiveScan();
130
- if ( is_object( $activeScan ) ) {
131
- //remove it and it minions
132
- $activeScan->delete();
133
- Scan_Api::flushCache();
134
- wp_send_json_success( array(
135
- 'url' => network_admin_url( 'admin.php?page=wdf-scan' )
136
- ) );
137
- }
138
-
139
- wp_send_json_error( array(
140
- 'message' => ''
141
- ) );
142
- }
143
-
144
- public function pullSrcFile() {
145
- if ( ! $this->checkPermission() ) {
146
- return;
147
- }
148
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'pullSrcFile' ) ) {
149
- return;
150
- }
151
-
152
- $id = HTTP_Helper::retrieve_post( 'id' );
153
- $model = Result_Item::findByID( $id );
154
- if ( is_object( $model ) ) {
155
- wp_send_json_success( array(
156
- 'html' => $model->getSrcCode()
157
- ) );
158
- }
159
- }
160
-
161
- /**
162
- *
163
- */
164
- public function scanBulkAction() {
165
- if ( ! $this->checkPermission() ) {
166
- return;
167
- }
168
-
169
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'scanBulkAction' ) ) {
170
- return;
171
- }
172
-
173
- $items = HTTP_Helper::retrieve_post( 'items' );
174
- if ( ! is_array( $items ) ) {
175
- $items = array();
176
- }
177
- $bulk = HTTP_Helper::retrieve_post( 'bulk' );
178
- switch ( $bulk ) {
179
- case 'ignore':
180
- foreach ( $items as $id ) {
181
- $item = Result_Item::findByID( $id );
182
- if ( is_object( $item ) ) {
183
- $item->ignore();
184
- }
185
- }
186
- $this->submitStatsToDev();
187
- wp_send_json_success( array(
188
- 'message' => _n( "The suspicious file has been successfully ignored.",
189
- "The suspicious files have been successfully ignored.",
190
- count( $items ),
191
- "defender-security" )
192
- ) );
193
- break;
194
- case 'unignore':
195
- foreach ( $items as $id ) {
196
- $item = Result_Item::findByID( $id );
197
- if ( is_object( $item ) ) {
198
- $item->unignore();
199
- }
200
- }
201
- $this->submitStatsToDev();
202
- wp_send_json_success( array(
203
- 'message' => _n( "The suspicious file has been successfully restored.",
204
- "The suspicious files have been successfully restored.",
205
- count( $items ),
206
- "defender-security" )
207
- ) );
208
- break;
209
- case 'delete':
210
- $ids = array();
211
- foreach ( $items as $id ) {
212
- $item = Result_Item::findByID( $id );
213
- if ( is_object( $item ) ) {
214
- if ( $item->hasMethod( 'purge' ) && $item->purge() === true ) {
215
- $ids[] = $id;
216
- }
217
- }
218
- }
219
- if ( $ids ) {
220
- $this->submitStatsToDev();
221
- wp_send_json_success( array(
222
- 'message' => _n( "The suspicious file has been successfully deleted.",
223
- "The suspicious files have been successfully deleted.",
224
- count( $items ), "defender-security" )
225
- ) );
226
- } else {
227
- wp_send_json_error( array(
228
- 'message' => __( "No item has been deleted", "defender-security" )
229
- ) );
230
- }
231
- break;
232
- case 'resolve':
233
- $ids = array();
234
- foreach ( $items as $id ) {
235
- $item = Result_Item::findByID( $id );
236
- if ( is_object( $item ) ) {
237
- if ( $item->hasMethod( 'resolve' ) && $item->resolve() === true ) {
238
- $ids[] = $id;
239
- }
240
- }
241
- }
242
- if ( $ids ) {
243
- $this->submitStatsToDev();
244
- wp_send_json_success( array(
245
- 'message' => _n( "The suspicious file has been successfully resolved.",
246
- "The suspicious files have been successfully resolved.",
247
- count( $items ), "defender-security" )
248
- ) );
249
- } else {
250
- wp_send_json_error( array(
251
- 'message' => __( "No item has been resolved", "defender-security" )
252
- ) );
253
- }
254
- break;
255
- default:
256
- //param not from the button on frontend, log it
257
- error_log( sprintf( 'Unexpected value %s from IP %s', $bulk, Utils::instance()->getUserIp() ) );
258
- break;
259
- }
260
- }
261
-
262
  /**
263
  * Process a scan via cronjob, only use to process in background, not create a new scan
264
  */
@@ -287,246 +118,6 @@ class Main extends \WP_Defender\Controller {
287
  }
288
  }
289
 
290
- /**
291
- * process scan settings
292
- */
293
- public function saveScanSettings() {
294
- if ( ! $this->checkPermission() ) {
295
- return;
296
- }
297
-
298
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'saveScanSettings' ) ) {
299
- return;
300
- }
301
-
302
- $settings = Settings::instance();
303
- $data = $_POST;
304
- foreach ( $data as $key => $val ) {
305
- if ( in_array( $key, array( 'email_all_ok', 'email_has_issue' ) ) ) {
306
- $data[ $key ] = wp_kses_post( $val );
307
- } else {
308
- $data[ $key ] = sanitize_text_field( $val );
309
- }
310
- }
311
- $settings->import( $data );
312
- $settings->email_all_ok = stripslashes( $settings->email_all_ok );
313
- $settings->email_has_issue = stripslashes( $settings->email_has_issue );
314
- $settings->save();
315
- if ( $this->hasMethod( 'scheduleReportTime' ) ) {
316
- $this->scheduleReportTime( $settings );
317
- $this->submitStatsToDev();
318
- }
319
- wp_send_json_success( array(
320
- 'message' => __( "Your settings have been updated.", "defender-security" )
321
- ) );
322
- }
323
-
324
- /**
325
- * Resolve an item
326
- */
327
- public function resolveItem() {
328
- if ( ! $this->checkPermission() ) {
329
- return;
330
- }
331
-
332
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'resolveItem' ) ) {
333
- return;
334
- }
335
-
336
- $id = HTTP_Helper::retrieve_post( 'id', false );
337
-
338
- $model = Result_Item::findByID( $id );
339
- if ( is_object( $model ) ) {
340
- $ret = $model->resolve();
341
- if ( is_wp_error( $ret ) ) {
342
- wp_send_json_error( array(
343
- 'message' => $ret->get_error_message()
344
- ) );
345
- } else {
346
- if ( $ret === true ) {
347
- $this->submitStatsToDev();
348
- wp_send_json_success( array(
349
- 'mid' => 'mid-' . $model->id,
350
- 'message' => __( "This item has been resolved.", "defender-security" ),
351
- 'counts' => $this->getIssuesAndIgnoredCounts( $model->parentId )
352
- ) );
353
- } elseif ( $ret === false ) {
354
- wp_send_json_error( array(
355
- 'message' => __( "Please try again!", "defender-security" )
356
- ) );
357
- } elseif ( is_string( $ret ) ) {
358
- $this->submitStatsToDev();
359
- wp_send_json_success( array(
360
- 'url' => $ret
361
- ) );
362
- }
363
- }
364
- } else {
365
- wp_send_json_error( array(
366
- 'message' => __( "The item doesn't exist!", "defender-security" )
367
- ) );
368
- }
369
- }
370
-
371
- /**
372
- * Ajax process to remove an item
373
- */
374
- public function deleteItem() {
375
- if ( ! $this->checkPermission() ) {
376
- return;
377
- }
378
-
379
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'deleteItem' ) ) {
380
- return;
381
- }
382
-
383
- $id = HTTP_Helper::retrieve_post( 'id', false );
384
- $model = Result_Item::findByID( $id );
385
- if ( is_object( $model ) ) {
386
- $ret = $model->purge();
387
- $this->submitStatsToDev();
388
- if ( is_wp_error( $ret ) ) {
389
- wp_send_json_error( array(
390
- 'message' => $ret->get_error_message()
391
- ) );
392
-
393
- } else {
394
- wp_send_json_success( array(
395
- 'mid' => 'mid-' . $model->id,
396
- 'message' => __( "This item has been permanently removed", "defender-security" ),
397
- 'counts' => $this->getIssuesAndIgnoredCounts( $model->parentId )
398
- ) );
399
- }
400
- } else {
401
- wp_send_json_error( array(
402
- 'message' => __( "The item doesn't exist!", "defender-security" )
403
- ) );
404
- }
405
- }
406
-
407
- /**
408
- *
409
- */
410
- public function unIgnoreItem() {
411
- if ( ! $this->checkPermission() ) {
412
- return;
413
- }
414
-
415
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'unIgnoreItem' ) ) {
416
- return;
417
- }
418
-
419
- $id = HTTP_Helper::retrieve_post( 'id', false );
420
- $model = Result_Item::findByID( $id );
421
- if ( is_object( $model ) ) {
422
- $model->unignore();
423
- $this->submitStatsToDev();
424
- wp_send_json_success( array(
425
- 'mid' => 'mid-' . $model->id,
426
- 'message' => __( "The suspicious file has been successfully restored.", "defender-security" ),
427
- 'counts' => $this->getIssuesAndIgnoredCounts( $model->parentId )
428
- ) );
429
- } else {
430
- wp_send_json_error( array(
431
- 'message' => __( "The item doesn't exist!", "defender-security" )
432
- ) );
433
- }
434
- }
435
-
436
- /**
437
- * Ajax function for ignoring scan result item
438
- */
439
- public function ignoreItem() {
440
- if ( ! $this->checkPermission() ) {
441
- return;
442
- }
443
-
444
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'ignoreItem' ) ) {
445
- return;
446
- }
447
-
448
- $id = HTTP_Helper::retrieve_post( 'id', false );
449
- $model = Result_Item::findByID( $id );
450
- if ( is_object( $model ) ) {
451
- $model->ignore();
452
- $this->submitStatsToDev();
453
- wp_send_json_success( array(
454
- 'mid' => 'mid-' . $model->id,
455
- 'message' => __( "The suspicious file has been successfully ignored.", "defender-security" ),
456
- 'counts' => $this->getIssuesAndIgnoredCounts( $model->parentId )
457
- ) );
458
- } else {
459
- wp_send_json_error( array(
460
- 'message' => __( "The item doesn't exist!", "defender-security" )
461
- ) );
462
- }
463
- }
464
-
465
- /**
466
- * Ajax action for processing a scan on page
467
- */
468
- public function processScan() {
469
- if ( ! $this->checkPermission() ) {
470
- return;
471
- }
472
-
473
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'processScan' ) ) {
474
- return;
475
- }
476
-
477
- //clean all qron
478
- wp_clear_scheduled_hook( 'processScanCron' );
479
-
480
- $ret = Scan_Api::processActiveScan();
481
- $model = Scan_Api::getActiveScan();
482
- $data = array(
483
- 'percent' => Scan_Api::getScanProgress(),
484
- 'statusText' => is_object( $model ) ? $model->statusText : null
485
- );
486
- if ( $ret == true ) {
487
- $data['url'] = network_admin_url( 'admin.php?page=wdf-scan' );
488
- $referrer = HTTP_Helper::retrieve_post( '_wp_http_referer' );
489
- parse_str( parse_url( $referrer, PHP_URL_QUERY ), $urlComp );
490
- if ( isset( $urlComp['page'] ) && $urlComp['page'] == 'wp-defender' ) {
491
- //from dashboard
492
- $data['url'] = network_admin_url( 'admin.php?page=wp-defender' );
493
- }
494
- $this->sendEmailReport();
495
- $this->submitStatsToDev();
496
- wp_send_json_success( $data );
497
- } else {
498
- //not completed
499
- //we will schedule a cron here in case user close tthe page
500
- wp_schedule_single_event( strtotime( '+1 minutes' ), 'processScanCron' );
501
- wp_send_json_error( $data );
502
- }
503
- }
504
-
505
- /**
506
- * Ajax action, to start a new scan
507
- */
508
- public function startAScan() {
509
- if ( ! $this->checkPermission() ) {
510
- return;
511
- }
512
-
513
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'startAScan' ) ) {
514
- return;
515
- }
516
-
517
- $ret = Scan_Api::createScan();
518
- if ( ! is_wp_error( $ret ) ) {
519
- wp_send_json_success( array(
520
- 'url' => network_admin_url( 'admin.php?page=wdf-scan' )
521
- )
522
- );
523
- }
524
-
525
- wp_send_json_error( array(
526
- 'message' => $ret->get_error_message(),
527
- ) );
528
- }
529
-
530
  /**
531
  * Add submit admin page
532
  */
@@ -542,230 +133,52 @@ class Main extends \WP_Defender\Controller {
542
  * Enqueue scripts & styles
543
  */
544
  public function scripts() {
545
- $data = array(
546
- 'scanning_title' => __( "Scan In Progress", "defender-security" ) . '<form class="scan-frm float-r"><input type="hidden" name="action" value="cancelScan"/>' . wp_nonce_field( 'cancelScan', '_wpnonce', true, false ) . '<button type="submit" class="button button-small button-secondary">' . __( "Cancel", "defender-security" ) . '</button></form>',
547
- 'no_issues' => __( "Your code is currently clean! There were no issues found during the last scan, though you can always perform a new scan anytime.", "defender-security" ),
548
- 'url' => network_admin_url( 'admin.php?page=wdf-scan' )
549
- );
550
  if ( $this->isInPage() ) {
551
- $view = HTTP_Helper::retrieve_get( 'view' );
552
- //init receiption
553
- if ( $view == 'notification' ) {
554
- $this->emailSearchNotification->add_script();
555
- } elseif ( $view == 'reporting' ) {
556
- $this->email_search->add_script();
557
- }
558
- wp_enqueue_script( 'wpmudev-sui' );
559
- wp_enqueue_style( 'wpmudev-sui' );
560
-
561
- wp_enqueue_script( 'defender' );
562
- wp_enqueue_script( 'prism', wp_defender()->getPluginUrl() . 'app/module/scan/js/prism.js' );
563
  wp_enqueue_style( 'defender' );
564
- wp_enqueue_script( 'scan', wp_defender()->getPluginUrl() . 'app/module/scan/js/script.js' );
565
- wp_enqueue_style( 'prism', wp_defender()->getPluginUrl() . 'app/module/scan/js/prism.css' );
566
- wp_localize_script( 'scan', 'scan', $data );
567
- } else {
568
- wp_enqueue_script( 'scan', wp_defender()->getPluginUrl() . 'app/module/scan/js/script.js' );
569
- }
570
- }
571
-
572
- /**
573
- * Internal route for this module
574
- */
575
- public function actionIndex() {
576
- $view = HTTP_Helper::retrieve_get( 'view' );
577
- switch ( $view ) {
578
- case 'issue':
579
- default:
580
- $this->viewResult();
581
- break;
582
- case 'ignored':
583
- $this->viewIgnore();
584
- break;
585
- case 'cleaned':
586
- $this->viewCleaned();
587
- break;
588
- case 'settings':
589
- $this->viewSettings();
590
- break;
591
- case 'notification':
592
- $this->viewNotification();
593
- break;
594
- case 'reporting':
595
- $this->viewAutomation();
596
- break;
597
- }
598
- }
599
-
600
- /**
601
- * Render view when first start
602
- */
603
- private function viewBrandNew() {
604
- $this->renderPartial( 'new' );
605
- }
606
-
607
- private function viewNotification() {
608
- $model = Scan_Api::getLastScan();
609
- if ( ! is_object( $model ) ) {
610
- return $this->viewBrandNew();
611
- }
612
- $activeScan = Scan_Api::getActiveScan();
613
- if ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
614
- $this->viewScanning();
615
- } else {
616
- $setting = Scan\Model\Settings::instance();
617
- //$view = wp_defender()->isFree ? 'setting-free' : 'setting';
618
- $view = 'notification';
619
- $this->render( $view, array(
620
- 'setting' => $setting,
621
- 'model' => $model,
622
- 'lastScanDate' => $this->getLastScanDate(),
623
- 'email' => $this->emailSearchNotification
624
- ) );
625
- }
626
- }
627
-
628
- /**
629
- * Render scanning page, this will be move to behavior later due to free vs pro
630
- */
631
- private function viewScanning() {
632
- $model = Scan_Api::getActiveScan();
633
- $percent = Scan_Api::getScanProgress();
634
- $this->renderPartial( 'scanning', array(
635
- 'lastScanDate' => $this->getLastScanDate(),
636
- 'model' => $model,
637
- 'percent' => $percent
638
- ) );
639
- }
640
 
641
- /**
642
- * View all issues
643
- */
644
- private function viewResult() {
645
- $activeScan = Scan_Api::getActiveScan();
646
- $lastScan = Scan_Api::getLastScan();
647
- //no scan done, force to show new scan page
648
- if ( ! is_object( $activeScan ) && ! is_object( $lastScan ) ) {
649
- $this->viewBrandNew();
650
- } elseif ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
651
- $this->viewScanning();
652
- } elseif ( is_object( $activeScan ) && $activeScan->status == Scan\Model\Scan::STATUS_ERROR ) {
653
- $this->viewError( $activeScan );
654
- } else {
655
- $this->render( 'issues', array(
656
- 'lastScanDate' => $this->getLastScanDate(),
657
- 'model' => $lastScan
658
- ) );
659
- }
660
- }
661
-
662
- /**
663
- * @param Scan\Model\Scan $model
664
- */
665
- private function viewError( Scan\Model\Scan $model ) {
666
- //auto restart
667
- $model->status = Scan\Model\Scan::STATUS_PROCESS;
668
- $model->save();
669
- $this->viewScanning();
670
- }
671
-
672
- /**
673
- * View all ignored items
674
- */
675
- private function viewIgnore() {
676
- $activeScan = Scan_Api::getActiveScan();
677
- $lastScan = Scan_Api::getLastScan();
678
- //no scan done, force to show new scan page
679
- if ( ! is_object( $activeScan ) && ! is_object( $lastScan ) ) {
680
- $this->viewBrandNew();
681
- } elseif ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
682
- $this->viewScanning();
683
- } elseif ( is_object( $activeScan ) && $activeScan->status == Scan\Model\Scan::STATUS_ERROR ) {
684
- $this->viewError( $activeScan );
685
- } else {
686
- $this->render( 'ignored', array(
687
- 'lastScanDate' => $this->getLastScanDate(),
688
- 'model' => $lastScan
689
- ) );
690
- }
691
- }
692
-
693
- /**
694
- * View fixed item
695
- */
696
- private function viewCleaned() {
697
- $activeScan = Scan_Api::getActiveScan();
698
- $lastScan = Scan_Api::getLastScan();
699
- //no scan done, force to show new scan page
700
- if ( ! is_object( $activeScan ) && ! is_object( $lastScan ) ) {
701
- $this->viewBrandNew();
702
- } elseif ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
703
- $this->viewScanning();
704
- } elseif ( is_object( $activeScan ) && $activeScan->status == Scan\Model\Scan::STATUS_ERROR ) {
705
- $this->viewError( $activeScan );
706
- } else {
707
- $this->render( 'cleaned', array(
708
- 'lastScanDate' => $this->getLastScanDate(),
709
- 'model' => $lastScan
710
- ) );
711
- }
712
- }
713
-
714
- /**
715
- *
716
- */
717
- private function viewSettings() {
718
- $model = Scan_Api::getLastScan();
719
- if ( ! is_object( $model ) ) {
720
- return $this->viewBrandNew();
721
- }
722
- $activeScan = Scan_Api::getActiveScan();
723
- if ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
724
- $this->viewScanning();
725
- } else {
726
- $setting = Scan\Model\Settings::instance();
727
- $view = wp_defender()->isFree ? 'setting-free' : 'setting';
728
- $this->render( $view, array(
729
- 'setting' => $setting,
730
- 'model' => $model,
731
- 'lastScanDate' => $this->getLastScanDate(),
732
- 'email' => $this->email_search
733
- ) );
734
  }
735
  }
736
 
737
- private function viewAutomation() {
738
- $model = Scan_Api::getLastScan();
739
- $setting = Scan\Model\Settings::instance();
740
- if ( ! is_object( $model ) ) {
741
- return $this->viewBrandNew();
742
- }
743
- $activeScan = Scan_Api::getActiveScan();
744
- if ( is_object( $activeScan ) && $activeScan->status != Scan\Model\Scan::STATUS_ERROR ) {
745
- $this->viewScanning();
746
- } else {
747
- $this->email_search->add_script();
748
- $view = wp_defender()->isFree ? 'automation-free' : 'automation';
749
- $this->render( $view, array(
750
- 'setting' => $setting,
751
- 'model' => $model,
752
- 'lastScanDate' => $this->getLastScanDate(),
753
- 'email' => $this->email_search
754
- ) );
755
- }
 
 
 
756
  }
757
 
758
  /**
759
- * Get last scan date and format it with WP date time format
760
- * @return string|null
761
  */
762
- private function getLastScanDate() {
763
- $model = Scan_Api::getLastScan();
764
- if ( is_object( $model ) ) {
765
- return $this->formatDateTime( $model->dateFinished );
766
- }
767
-
768
- return null;
769
  }
770
 
771
  public function sendEmailReport( $force = false ) {
@@ -792,17 +205,17 @@ class Main extends \WP_Defender\Controller {
792
  return;
793
  }
794
 
795
- $recipients = $settings->receipts;
796
  } else {
797
  if ( $settings->notification == false ) {
798
  return;
799
  }
800
 
801
- if ( $settings->alwaysSendNotification == false && $count == 0 ) {
802
  return;
803
  }
804
 
805
- $recipients = $settings->receiptsNotification;
806
  }
807
 
808
  if ( empty( $recipients ) ) {
@@ -815,8 +228,8 @@ class Main extends \WP_Defender\Controller {
815
  $params = array(
816
  'USER_NAME' => $item['first_name'],
817
  'ISSUES_COUNT' => $count,
818
- 'SCAN_PAGE_LINK' => network_admin_url( 'admin.php?page=wdf-scan' ),
819
- 'ISSUES_LIST' => $this->issues_list_html( $model ),
820
  'SITE_URL' => network_site_url(),
821
  );
822
  $params = apply_filters( 'wd_notification_email_params', $params );
@@ -859,7 +272,7 @@ class Main extends \WP_Defender\Controller {
859
  * @access private
860
  * @since 1.0
861
  */
862
- private function issues_list_html( Scan\Model\Scan $model ) {
863
  ob_start();
864
  ?>
865
  <table class="results-list"
@@ -903,7 +316,7 @@ class Main extends \WP_Defender\Controller {
903
  style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 10px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
904
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
905
  <a class="plugin-brand" href="<?php echo network_admin_url( 'admin.php?page=wdf-scan' ) ?>"
906
- style="Margin: 0; color: #ff5c28; display: inline-block; font: inherit; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><?php esc_html_e( "Lets get your site patched up.", "defender-security" ) ?>
907
  <img class="icon-arrow-right"
908
  src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-arrow-right-defender.png"
909
  alt="Arrow"
@@ -916,70 +329,4 @@ class Main extends \WP_Defender\Controller {
916
  <?php
917
  return ob_get_clean();
918
  }
919
-
920
- /**
921
- * Get Counts of issues and ignored items
922
- *
923
- * @param Integer $parent_id - PArent id of the model
924
- *
925
- * @return Array
926
- */
927
- public function getIssuesAndIgnoredCounts( $parent_id ) {
928
- $total_issues = 0;
929
- $total_ignored = 0;
930
-
931
- $issues_wp = $this->countStatus( $parent_id, Result_Item::STATUS_ISSUE );
932
- $ignored_wp = $this->countStatus( $parent_id, Result_Item::STATUS_IGNORED );
933
-
934
- $total_issues = $issues_wp;
935
- $total_ignored = $ignored_wp;
936
-
937
- $premium_counts = array();
938
- if ( wp_defender()->isFree == false ) {
939
- $issues_vuln = $this->countStatus( $parent_id, Result_Item::STATUS_ISSUE, 'vuln' );
940
- $issues_content = $this->countStatus( $parent_id, Result_Item::STATUS_ISSUE, 'content' );
941
- $ignored_vuln = $this->countStatus( $parent_id, Result_Item::STATUS_IGNORED, 'vuln' );
942
- $ignored_content = $this->countStatus( $parent_id, Result_Item::STATUS_IGNORED, 'content' );
943
-
944
- $total_issues += $issues_vuln;
945
- $total_issues += $issues_content;
946
- $total_ignored += $ignored_vuln;
947
- $total_ignored += $ignored_content;
948
-
949
- $premium_counts = array(
950
- 'vuln_issues' => $issues_vuln == 0 ? '<i class="sui-icon-check-tick sui-success" aria-hidden="true"></i>' : $issues_vuln,
951
- 'content_issues' => $issues_content == 0 ? '<i class="sui-icon-check-tick sui-success" aria-hidden="true"></i>' : $issues_content
952
- );
953
- }
954
-
955
- $counts = array(
956
- 'issues' => $total_issues,
957
- 'issues_wp' => $issues_wp == 0 ? '<i class="sui-icon-check-tick sui-success" aria-hidden="true"></i>' : $issues_wp,
958
- 'ignored' => $total_ignored
959
- );
960
-
961
- $counts = array_merge( $counts, $premium_counts );
962
-
963
- return $counts;
964
- }
965
-
966
-
967
- /**
968
- * Count status based on the parent id and type
969
- *
970
- * @param Integer $parent_id - Parent id of the model
971
- * @param String $status - Status
972
- * @param String $type - Issue Type. Defaults to core
973
- *
974
- * @return Integer
975
- */
976
- private function countStatus( $parent_id, $status, $type = 'core' ) {
977
- $counts = Result_Item::count( array(
978
- 'status' => $status,
979
- 'parentId' => $parent_id,
980
- 'type' => $type
981
- ) );
982
-
983
- return $counts;
984
- }
985
  }
2
 
3
  namespace WP_Defender\Module\Scan\Controller;
4
 
5
+ use WP_Defender\Behavior\Utils;
 
6
  use WP_Defender\Module\Scan;
7
  use WP_Defender\Module\Scan\Component\Scan_Api;
8
  use WP_Defender\Module\Scan\Model\Settings;
9
  use WP_Defender\Module\Scan\Model\Result_Item;
 
 
10
 
11
  /**
12
  * Author: Hoang Ngo
13
  */
14
  class Main extends \WP_Defender\Controller {
15
  protected $slug = 'wdf-scan';
 
 
 
16
 
17
  /**
18
  * @return array
19
  */
20
  public function behaviors() {
21
+ $behaviors = [
22
+ 'utils' => '\WP_Defender\Behavior\Utils',
23
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
24
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
25
+ ];
26
+
27
  if ( wp_defender()->isFree == false ) {
28
  $behaviors['pro'] = '\WP_Defender\Module\Scan\Behavior\Pro\Reporting';
29
  }
35
  * Main constructor.
36
  */
37
  public function __construct() {
38
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
39
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
40
  } else {
41
+ $this->addAction( 'admin_menu', 'adminMenu' );
42
  }
43
 
44
  if ( $this->isInPage() || $this->isDashboard() ) {
45
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 11 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  }
47
 
48
  //process scan in background
49
+ $this->addAction( 'processScanCron', 'processScanCron' );
50
  //scan as schedule
51
+ $this->addAction( 'scanReportCron', 'scanReportCron' );
52
+
53
+ $this->addAction( 'sendScanEmail', 'sendEmailReport' );
54
  }
55
 
56
  /**
62
  }
63
 
64
  $settings = Settings::instance();
65
+ $lastReportSent = $settings->last_report_sent;
66
  if ( $lastReportSent == null ) {
67
  $model = Scan_Api::getLastScan();
68
  if ( is_object( $model ) ) {
69
+ $lastReportSent = $model->dateFinished;
70
+ $settings->last_report_sent = $lastReportSent;
71
  //init
72
  $settings->save();
73
  } else {
90
  }
91
  }
92
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  /**
94
  * Process a scan via cronjob, only use to process in background, not create a new scan
95
  */
118
  }
119
  }
120
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
121
  /**
122
  * Add submit admin page
123
  */
133
  * Enqueue scripts & styles
134
  */
135
  public function scripts() {
 
 
 
 
 
136
  if ( $this->isInPage() ) {
 
 
 
 
 
 
 
 
 
 
 
 
137
  wp_enqueue_style( 'defender' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
+ wp_register_script( 'defender-scan', wp_defender()->getPluginUrl() . 'assets/app/scan.js', [
140
+ 'vue',
141
+ 'defender',
142
+ 'wp-i18n'
143
+ ], wp_defender()->version, true );
144
+ wp_enqueue_script( 'defender-prism', wp_defender()->getPluginUrl() . 'assets/js/vendor/prism/prism.js' );
145
+ wp_localize_script( 'defender-scan', 'scanData', $this->_scriptsData() );
146
+ Utils::instance()->createTranslationJson( 'defender-scan' );
147
+ wp_set_script_translations( 'defender-scan', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
148
+ wp_enqueue_script( 'defender-scan' );
149
+ wp_enqueue_script( 'wpmudev-sui' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  }
151
  }
152
 
153
+ public function _scriptsData() {
154
+ if ( ! $this->checkPermission() ) {
155
+ return [];
156
+ }
157
+ $data = Scan\Component\Data_Factory::buildData();
158
+ $data = array_merge( $data, [
159
+ 'nonces' => [
160
+ 'newScan' => wp_create_nonce( 'newScan' ),
161
+ 'processScan' => wp_create_nonce( 'processScan' ),
162
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
163
+ 'cancelScan' => wp_create_nonce( 'cancelScan' ),
164
+ 'getFileSrcCode' => wp_create_nonce( 'getFileSrcCode' ),
165
+ 'ignoreIssue' => wp_create_nonce( 'ignoreIssue' ),
166
+ 'unignoreIssue' => wp_create_nonce( 'unignoreIssue' ),
167
+ 'deleteIssue' => wp_create_nonce( 'deleteIssue' ),
168
+ 'solveIssue' => wp_create_nonce( 'solveIssue' ),
169
+ 'bulkAction' => wp_create_nonce( 'bulkAction' )
170
+ ],
171
+ 'endpoints' => $this->getAllAvailableEndpoints( Scan::getClassName() ),
172
+ ] );
173
+
174
+ return $data;
175
  }
176
 
177
  /**
178
+ * Internal route for this module
 
179
  */
180
+ public function actionIndex() {
181
+ $this->render( 'main' );
 
 
 
 
 
182
  }
183
 
184
  public function sendEmailReport( $force = false ) {
205
  return;
206
  }
207
 
208
+ $recipients = $settings->recipients;
209
  } else {
210
  if ( $settings->notification == false ) {
211
  return;
212
  }
213
 
214
+ if ( $settings->always_send_notification == false && $count == 0 ) {
215
  return;
216
  }
217
 
218
+ $recipients = $settings->recipients_notification;
219
  }
220
 
221
  if ( empty( $recipients ) ) {
228
  $params = array(
229
  'USER_NAME' => $item['first_name'],
230
  'ISSUES_COUNT' => $count,
231
+ 'SCAN_PAGE_LINK' => apply_filters( 'report_email_logs_link', network_admin_url( 'admin.php?page=wdf-scan' ), $email ),
232
+ 'ISSUES_LIST' => $this->issuesListHtml( $model ),
233
  'SITE_URL' => network_site_url(),
234
  );
235
  $params = apply_filters( 'wd_notification_email_params', $params );
272
  * @access private
273
  * @since 1.0
274
  */
275
+ private function issuesListHtml( Scan\Model\Scan $model ) {
276
  ob_start();
277
  ?>
278
  <table class="results-list"
316
  style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 10px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
317
  <p style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; margin-bottom: 0; padding: 0 0 24px; text-align: left;">
318
  <a class="plugin-brand" href="<?php echo network_admin_url( 'admin.php?page=wdf-scan' ) ?>"
319
+ style="Margin: 0; color: #ff5c28; display: inline-block; font: inherit; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><?php esc_html_e( "Let's get your site patched up.", "defender-security" ) ?>
320
  <img class="icon-arrow-right"
321
  src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-arrow-right-defender.png"
322
  alt="Arrow"
329
  <?php
330
  return ob_get_clean();
331
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  }
app/module/scan/controller/rest.php ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Scan\Controller;
7
+
8
+
9
+ use Hammer\Helper\Array_Helper;
10
+ use Hammer\Helper\HTTP_Helper;
11
+ use WP_Defender\Behavior\Utils;
12
+ use WP_Defender\Controller;
13
+ use WP_Defender\Module\Scan;
14
+ use WP_Defender\Module\Scan\Component\Scan_Api;
15
+ use WP_Defender\Module\Scan\Model\Result_Item;
16
+
17
+ class Rest extends Controller {
18
+ public function __construct() {
19
+ $namespace = 'wp-defender/v1';
20
+ $namespace .= '/scan';
21
+ $routes = [
22
+ $namespace . '/newScan' => 'newScan',
23
+ $namespace . '/cancelScan' => 'cancelScan',
24
+ $namespace . '/processScan' => 'processScan',
25
+ $namespace . '/ignoreIssue' => 'ignoreIssue',
26
+ $namespace . '/unignoreIssue' => 'unignoreIssue',
27
+ $namespace . '/deleteIssue' => 'deleteIssue',
28
+ $namespace . '/solveIssue' => 'solveIssue',
29
+ $namespace . '/updateSettings' => 'updateSettings',
30
+ $namespace . '/getFileSrcCode' => 'getFileSrcCode',
31
+ $namespace . '/bulkAction' => 'bulkAction',
32
+ ];
33
+ $this->registerEndpoints( $routes, Scan::getClassName() );
34
+ }
35
+
36
+ public function bulkAction() {
37
+ if ( ! $this->checkPermission() ) {
38
+ return;
39
+ }
40
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'bulkAction' ) ) {
41
+ return;
42
+ }
43
+
44
+ $items = HTTP_Helper::retrievePost( 'items' );
45
+ if ( ! is_array( $items ) ) {
46
+ $items = array();
47
+ }
48
+ $bulk = HTTP_Helper::retrievePost( 'bulk' );
49
+ switch ( $bulk ) {
50
+ case 'ignore':
51
+ foreach ( $items as $id ) {
52
+ $item = Result_Item::findByID( $id );
53
+ if ( is_object( $item ) ) {
54
+ $item->ignore();
55
+ }
56
+ }
57
+ $this->submitStatsToDev();
58
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(), array(
59
+ 'message' => _n( "The suspicious file has been successfully ignored.",
60
+ "The suspicious files have been successfully ignored.",
61
+ count( $items ),
62
+ "defender-security" )
63
+ ) ) );
64
+ break;
65
+ case 'unignore':
66
+ foreach ( $items as $id ) {
67
+ $item = Result_Item::findByID( $id );
68
+ if ( is_object( $item ) ) {
69
+ $item->unignore();
70
+ }
71
+ }
72
+ $this->submitStatsToDev();
73
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(), array(
74
+ 'message' => _n( "The suspicious file has been successfully restored.",
75
+ "The suspicious files have been successfully restored.",
76
+ count( $items ),
77
+ "defender-security" )
78
+ ) ) );
79
+ break;
80
+
81
+ default:
82
+ //param not from the button on frontend, log it
83
+ error_log( sprintf( 'Unexpected value %s from IP %s', $bulk, Utils::instance()->getUserIp() ) );
84
+ break;
85
+ }
86
+ }
87
+
88
+ public function solveIssue() {
89
+ if ( ! $this->checkPermission() ) {
90
+ return;
91
+ }
92
+
93
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'solveIssue' ) ) {
94
+ return;
95
+ }
96
+ $id = HTTP_Helper::retrievePost( 'id', false );
97
+
98
+ $model = Result_Item::findByID( $id );
99
+ if ( is_object( $model ) ) {
100
+ $ret = $model->resolve();
101
+ if ( is_wp_error( $ret ) ) {
102
+ wp_send_json_error( array(
103
+ 'message' => $ret->get_error_message()
104
+ ) );
105
+ } else {
106
+ if ( $ret === true ) {
107
+ $this->submitStatsToDev();
108
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(),
109
+ [ 'message' => __( "This item has been resolved.", "defender-security" ), ] ) );
110
+ } elseif ( $ret === false ) {
111
+ wp_send_json_error( array(
112
+ 'message' => __( "Please try again!", "defender-security" )
113
+ ) );
114
+ } elseif ( is_string( $ret ) ) {
115
+ $this->submitStatsToDev();
116
+ wp_send_json_success( array(
117
+ 'url' => $ret
118
+ ) );
119
+ }
120
+ }
121
+ } else {
122
+ wp_send_json_error( array(
123
+ 'message' => __( "The item doesn't exist!", "defender-security" )
124
+ ) );
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Delete an issue
130
+ */
131
+ public function deleteIssue() {
132
+ if ( ! $this->checkPermission() ) {
133
+ return;
134
+ }
135
+
136
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'deleteIssue' ) ) {
137
+ return;
138
+ }
139
+
140
+ $id = HTTP_Helper::retrievePost( 'id', false );
141
+ $model = Result_Item::findByID( $id );
142
+ if ( is_object( $model ) ) {
143
+ $ret = $model->purge();
144
+ $this->submitStatsToDev();
145
+ if ( is_wp_error( $ret ) ) {
146
+ wp_send_json_error( array(
147
+ 'message' => $ret->get_error_message()
148
+ ) );
149
+
150
+ } else {
151
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(),
152
+ [ 'message' => __( "This item has been permanently removed", "defender-security" ), ] ) );
153
+ }
154
+ } else {
155
+ wp_send_json_error( array(
156
+ 'message' => __( "The item doesn't exist!", "defender-security" )
157
+ ) );
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Get source code of an issue
163
+ */
164
+ public function getFileSrcCode() {
165
+ if ( ! $this->checkPermission() ) {
166
+ return;
167
+ }
168
+
169
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'getFileSrcCode' ) ) {
170
+ return;
171
+ }
172
+ $id = HTTP_Helper::retrievePost( 'id' );
173
+ $model = Result_Item::findByID( $id );
174
+ if ( ! is_object( $model ) ) {
175
+ wp_send_json_error();
176
+ }
177
+
178
+ wp_send_json_success( array(
179
+ 'code' => $model->getSrcCode()
180
+ ) );
181
+
182
+ }
183
+
184
+ /**
185
+ * Ignore an issue
186
+ */
187
+ public function ignoreIssue() {
188
+ if ( ! $this->checkPermission() ) {
189
+ return;
190
+ }
191
+
192
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'ignoreIssue' ) ) {
193
+ return;
194
+ }
195
+
196
+ $id = HTTP_Helper::retrievePost( 'id', false );
197
+ $model = Result_Item::findByID( $id );
198
+ if ( is_object( $model ) ) {
199
+ $model->ignore();
200
+ $this->submitStatsToDev();
201
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(),
202
+ [ 'message' => __( "The suspicious file has been successfully ignored.", "defender-security" ), ] ) );
203
+ } else {
204
+ wp_send_json_error( array(
205
+ 'message' => __( "The item doesn't exist!", "defender-security" )
206
+ ) );
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Unignore an issue
212
+ */
213
+ public function unignoreIssue() {
214
+ if ( ! $this->checkPermission() ) {
215
+ return;
216
+ }
217
+
218
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'unignoreIssue' ) ) {
219
+ return;
220
+ }
221
+
222
+ $id = HTTP_Helper::retrievePost( 'id', false );
223
+ $model = Result_Item::findByID( $id );
224
+ if ( is_object( $model ) ) {
225
+ $model->unignore();
226
+ $this->submitStatsToDev();
227
+ wp_send_json_success( array_merge( Scan\Component\Data_Factory::buildData(),
228
+ [ 'message' => __( "The suspicious file has been successfully restored.", "defender-security" ), ] ) );
229
+ } else {
230
+ wp_send_json_error( array(
231
+ 'message' => __( "The item doesn't exist!", "defender-security" )
232
+ ) );
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Create a new scan
238
+ */
239
+ public function newScan() {
240
+ if ( ! $this->checkPermission() ) {
241
+ return;
242
+ }
243
+
244
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'newScan' ) ) {
245
+ return;
246
+ }
247
+ $ret = Scan\Component\Scan_Api::createScan();
248
+ if ( ! is_wp_error( $ret ) ) {
249
+ wp_send_json_success( [
250
+ 'status' => $ret->status,
251
+ 'status_text' => $ret->statusText,
252
+ 'percent' => 0
253
+ ] );
254
+ }
255
+
256
+ wp_send_json_error( array(
257
+ 'message' => $ret->get_error_message(),
258
+ ) );
259
+ }
260
+
261
+ /**
262
+ * Request to cancel current scan
263
+ */
264
+ public function cancelScan() {
265
+ if ( ! $this->checkPermission() ) {
266
+ return;
267
+ }
268
+
269
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'cancelScan' ) ) {
270
+ return;
271
+ }
272
+
273
+ $activeScan = Scan\Component\Scan_Api::getActiveScan();
274
+ if ( is_object( $activeScan ) ) {
275
+ $activeScan->delete();
276
+ Scan_Api::flushCache();
277
+ }
278
+ $data = Scan\Component\Data_Factory::buildData();
279
+
280
+ wp_send_json_success( $data );
281
+ }
282
+
283
+ /**
284
+ * Processing the scan
285
+ */
286
+ public function processScan() {
287
+ if ( ! $this->checkPermission() ) {
288
+ return;
289
+ }
290
+
291
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'processScan' ) ) {
292
+ return;
293
+ }
294
+
295
+ /**
296
+ * When we processing the scan by ajax, clear all the which does the same job
297
+ */
298
+ wp_clear_scheduled_hook( 'processScanCron' );
299
+
300
+ $ret = Scan\Component\Scan_Api::processActiveScan();
301
+ if ( $ret == true ) {
302
+ do_action( 'sendScanEmail' );
303
+
304
+ $this->submitStatsToDev();
305
+ $data = Scan\Component\Data_Factory::buildData();
306
+
307
+ wp_send_json_success( $data );
308
+ } else {
309
+ $model = Scan\Component\Scan_Api::getActiveScan();
310
+ $data = array(
311
+ 'status' => $model->status,
312
+ 'percent' => Scan\Component\Scan_Api::getScanProgress(),
313
+ 'status_text' => is_object( $model ) ? $model->statusText : null,
314
+ );
315
+ //not completed
316
+ //we will schedule a cron here in case user close tthe page, the scan still continue
317
+ wp_schedule_single_event( strtotime( '+1 minutes' ), 'processScanCron' );
318
+ wp_send_json_error( $data );
319
+ }
320
+ }
321
+
322
+ /**
323
+ * Update settings
324
+ */
325
+ public function updateSettings() {
326
+ if ( ! $this->checkPermission() ) {
327
+ return;
328
+ }
329
+
330
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'updateSettings' ) ) {
331
+ return;
332
+ }
333
+
334
+ $data = stripslashes( $_POST['data'] );
335
+ $data = json_decode( $data, true );
336
+ $settings = Scan\Model\Settings::instance();
337
+ foreach ( $data as $key => $val ) {
338
+ if ( in_array( $key, array( 'email_all_ok', 'email_has_issue' ) ) ) {
339
+ $data[ $key ] = wp_kses_post( $val );
340
+ } elseif ( is_string( $val ) ) {
341
+ $data[ $key ] = sanitize_text_field( $val );
342
+ }
343
+ }
344
+ $settings->import( $data );
345
+ $settings->email_all_ok = stripslashes( $settings->email_all_ok );
346
+ $settings->email_has_issue = stripslashes( $settings->email_has_issue );
347
+ $settings->save();
348
+ if ( $this->hasMethod( 'scheduleReportTime' ) ) {
349
+ $this->scheduleReportTime( $settings );
350
+ $this->submitStatsToDev();
351
+ }
352
+ wp_send_json_success( array(
353
+ 'message' => __( "Your settings have been updated.", "defender-security" )
354
+ ) );
355
+ }
356
+
357
+ /**
358
+ * Declaring behaviors
359
+ * @return array
360
+ */
361
+ /**
362
+ * @return array
363
+ */
364
+ public function behaviors() {
365
+ $behaviors = [
366
+ 'utils' => '\WP_Defender\Behavior\Utils',
367
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
368
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
369
+ ];
370
+
371
+ if ( wp_defender()->isFree == false ) {
372
+ $behaviors['pro'] = '\WP_Defender\Module\Scan\Behavior\Pro\Reporting';
373
+ }
374
+
375
+ return $behaviors;
376
+ }
377
+ }
app/module/scan/model/result-item.php CHANGED
@@ -37,7 +37,7 @@ class Result_Item extends Model {
37
  * @var
38
  */
39
  public $parentId;
40
-
41
  /**
42
  * @var int
43
  */
@@ -46,12 +46,12 @@ class Result_Item extends Model {
46
  * @var int
47
  */
48
  public $dateIgnored;
49
-
50
  /**
51
  * @var array
52
  */
53
  public $raw;
54
-
55
  protected static function maps() {
56
  return array(
57
  'id' => array(
@@ -84,7 +84,7 @@ class Result_Item extends Model {
84
  ),
85
  );
86
  }
87
-
88
  /**
89
  * Add this status to ignore, also we will need to cache the ignore as globally
90
  */
@@ -92,11 +92,11 @@ class Result_Item extends Model {
92
  $this->status = self::STATUS_IGNORED;
93
  $this->dateIgnored = date( 'Y-m-d H:i:s' );
94
  $this->save();
95
-
96
  //upadte to global ignore cache
97
  Scan_Api::indexIgnore( $this->id );
98
  }
99
-
100
  /**
101
  * mark this as resolved
102
  */
@@ -105,17 +105,17 @@ class Result_Item extends Model {
105
  $this->dateFixed = date( 'Y-m-d H:i:s' );
106
  $this->save();
107
  }
108
-
109
  /**
110
  * Un ignore this
111
  */
112
  public function unignore() {
113
  $this->status = self::STATUS_ISSUE;
114
  $this->save();
115
-
116
  Scan_Api::unIndexIgnore( $this->id );
117
  }
118
-
119
  public function behaviors() {
120
  switch ( $this->type ) {
121
  case 'core':
37
  * @var
38
  */
39
  public $parentId;
40
+
41
  /**
42
  * @var int
43
  */
46
  * @var int
47
  */
48
  public $dateIgnored;
49
+
50
  /**
51
  * @var array
52
  */
53
  public $raw;
54
+
55
  protected static function maps() {
56
  return array(
57
  'id' => array(
84
  ),
85
  );
86
  }
87
+
88
  /**
89
  * Add this status to ignore, also we will need to cache the ignore as globally
90
  */
92
  $this->status = self::STATUS_IGNORED;
93
  $this->dateIgnored = date( 'Y-m-d H:i:s' );
94
  $this->save();
95
+
96
  //upadte to global ignore cache
97
  Scan_Api::indexIgnore( $this->id );
98
  }
99
+
100
  /**
101
  * mark this as resolved
102
  */
105
  $this->dateFixed = date( 'Y-m-d H:i:s' );
106
  $this->save();
107
  }
108
+
109
  /**
110
  * Un ignore this
111
  */
112
  public function unignore() {
113
  $this->status = self::STATUS_ISSUE;
114
  $this->save();
115
+
116
  Scan_Api::unIndexIgnore( $this->id );
117
  }
118
+
119
  public function behaviors() {
120
  switch ( $this->type ) {
121
  case 'core':
app/module/scan/model/scan.php CHANGED
@@ -32,9 +32,9 @@ use WP_Defender\Module\Scan\Behavior\Core_Result;
32
  */
33
  class Scan extends Model {
34
  const STATUS_INIT = 'init', STATUS_PROCESS = 'process', STATUS_ERROR = 'error', STATUS_FINISH = 'finish';
35
-
36
  static $post_type = 'wdf_scan';
37
-
38
  /**
39
  * Id
40
  * @var int
@@ -73,7 +73,7 @@ class Scan extends Model {
73
  * @var int
74
  */
75
  public $dateFinished;
76
-
77
  /**
78
  * @return array
79
  */
@@ -113,10 +113,30 @@ class Scan extends Model {
113
  )
114
  );
115
  }
116
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  /**
118
  * @param $offset int
119
- * @param $type string issues|cleaned|ignored
120
  *
121
  * @return Result_Item[]
122
  */
@@ -126,10 +146,10 @@ class Scan extends Model {
126
  'status' => $status,
127
  'type' => $type
128
  ), null, null, $offset );
129
-
130
  return $items;
131
  }
132
-
133
  /**
134
  * @param $type
135
  *
@@ -145,10 +165,10 @@ class Scan extends Model {
145
  'status' => $type,
146
  'type' => $scanTypes
147
  ) );
148
-
149
  return $count;
150
  }
151
-
152
  /**
153
  * This is deifferent from all countAllxxx function, only for counting issues category
154
  *
@@ -182,13 +202,13 @@ class Scan extends Model {
182
  break;
183
  }
184
  }
185
-
186
  /**
187
  * @return array
188
  */
189
  public function events() {
190
  $that = $this;
191
-
192
  return array(
193
  self::EVENT_AFTER_DELETE => array(
194
  array(
32
  */
33
  class Scan extends Model {
34
  const STATUS_INIT = 'init', STATUS_PROCESS = 'process', STATUS_ERROR = 'error', STATUS_FINISH = 'finish';
35
+
36
  static $post_type = 'wdf_scan';
37
+
38
  /**
39
  * Id
40
  * @var int
73
  * @var int
74
  */
75
  public $dateFinished;
76
+
77
  /**
78
  * @return array
79
  */
113
  )
114
  );
115
  }
116
+
117
+ /**
118
+ * @param int $offset
119
+ * @param string $status
120
+ * @param null $type
121
+ *
122
+ * @return array
123
+ */
124
+ public function getItemsAsJson( $offset = 0, $status = Result_Item::STATUS_ISSUE, $type = null ) {
125
+ $items = $this->getItems( false, $status, $type );
126
+ $results = [];
127
+
128
+ foreach ( $items as $item ) {
129
+ if ( $item->hasMethod( 'getInfo' ) ) {
130
+ $results[] = $item->getInfo();
131
+ }
132
+ }
133
+
134
+ return $results;
135
+ }
136
+
137
  /**
138
  * @param $offset int
139
+ * @param $type string core|content|vuln
140
  *
141
  * @return Result_Item[]
142
  */
146
  'status' => $status,
147
  'type' => $type
148
  ), null, null, $offset );
149
+
150
  return $items;
151
  }
152
+
153
  /**
154
  * @param $type
155
  *
165
  'status' => $type,
166
  'type' => $scanTypes
167
  ) );
168
+
169
  return $count;
170
  }
171
+
172
  /**
173
  * This is deifferent from all countAllxxx function, only for counting issues category
174
  *
202
  break;
203
  }
204
  }
205
+
206
  /**
207
  * @return array
208
  */
209
  public function events() {
210
  $that = $this;
211
+
212
  return array(
213
  self::EVENT_AFTER_DELETE => array(
214
  array(
app/module/scan/model/settings.php CHANGED
@@ -17,7 +17,7 @@ use WP_Defender\Module\Scan\Behavior\Pro\Vuln_Scan;
17
  use WP_Defender\Module\Scan\Component\Scan_Api;
18
 
19
  class Settings extends \Hammer\WP\Settings {
20
-
21
  private static $_instance;
22
  /**
23
  * Scan WP core files
@@ -30,29 +30,29 @@ class Settings extends \Hammer\WP\Settings {
30
  * @var bool
31
  */
32
  public $scan_vuln = true;
33
-
34
  /**
35
  * @var bool
36
  */
37
  public $scan_content = true;
38
-
39
  /**
40
  * Receipts to sending notification
41
  * @var array
42
  */
43
- public $receipts = array();
44
-
45
  /**
46
  * @var array
47
  */
48
- public $receiptsNotification = array();
49
-
50
  /**
51
  * Toggle notification on or off
52
  * @var bool
53
  */
54
  public $notification = true;
55
-
56
  /**
57
  * @var bool
58
  */
@@ -63,18 +63,18 @@ class Settings extends \Hammer\WP\Settings {
63
  * @var bool
64
  */
65
  public $always_send = false;
66
-
67
  /**
68
  * @var bool
69
  */
70
- public $alwaysSendNotification = false;
71
-
72
  /**
73
  * Maximum filesize to scan, only apply for content scan
74
  * @var int
75
  */
76
  public $max_filesize = 1;
77
-
78
  /**
79
  * @var string
80
  */
@@ -87,7 +87,7 @@ class Settings extends \Hammer\WP\Settings {
87
  * @var string|void
88
  */
89
  public $email_all_ok = '';
90
-
91
  /**
92
  * @var string
93
  */
@@ -99,10 +99,13 @@ class Settings extends \Hammer\WP\Settings {
99
  /**
100
  * @var string
101
  */
102
- public $time = '0:00';
103
-
104
- public $lastReportSent;
105
-
 
 
 
106
  /**
107
  * @return array
108
  */
@@ -110,14 +113,14 @@ class Settings extends \Hammer\WP\Settings {
110
  $behaviors = array(
111
  'utils' => '\WP_Defender\Behavior\Utils'
112
  );
113
-
114
  if ( wp_defender()->isFree == false ) {
115
  $behaviors['pro'] = '\WP_Defender\Module\Scan\Behavior\Pro\Model';
116
  }
117
-
118
  return $behaviors;
119
  }
120
-
121
  public function __construct( $id, $is_multi ) {
122
  $this->email_subject = __( 'Scan of {SITE_URL} complete. {ISSUES_COUNT} issues found.', "defender-security" );
123
  $this->email_has_issue = __( 'Hi {USER_NAME},
@@ -145,30 +148,42 @@ Official WPMU DEV Superhero', "defender-security" );
145
  if ( ( is_admin() || is_network_admin() ) && current_user_can( 'manage_options' ) ) {
146
  $user = wp_get_current_user();
147
  if ( is_object( $user ) ) {
148
- $this->receipts[] = array(
149
  'first_name' => $user->display_name,
150
  'email' => $user->user_email
151
  );
152
- $this->receiptsNotification[] = array(
153
  'first_name' => $user->display_name,
154
  'email' => $user->user_email
155
  );
156
  }
157
-
158
  //default is weekly
159
- $this->day = date( 'l' );
160
- $hour = date( 'H', current_time( 'timestamp' ) );
161
- if ( $hour == '00' ) {
162
- $hour = 0;
163
- } else {
164
- $hour = ltrim( $hour, '0' );
165
- }
166
- $this->time = $hour . ':0';
167
  }
168
-
169
  parent::__construct( $id, $is_multi );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  }
171
-
172
  /**
173
  * Act like a factory, return available scans based on pro or not
174
  * @return array
@@ -178,19 +193,19 @@ Official WPMU DEV Superhero', "defender-security" );
178
  if ( $this->scan_core ) {
179
  $scans[] = 'core';
180
  }
181
-
182
  if ( $this->scan_vuln && wp_defender()->isFree != true ) {
183
  $scans[] = 'vuln';
184
  }
185
-
186
  if ( $this->scan_content && wp_defender()->isFree != true ) {
187
  //$scans[] = 'md5';
188
  $scans[] = 'content';
189
  }
190
-
191
  return $scans;
192
  }
193
-
194
  /**
195
  * @return Settings
196
  */
@@ -199,10 +214,10 @@ Official WPMU DEV Superhero', "defender-security" );
199
  $class = new Settings( 'wd_scan_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
200
  self::$_instance = $class;
201
  }
202
-
203
  return self::$_instance;
204
  }
205
-
206
  /**
207
  * @param $slug
208
  * @param array $args
@@ -220,21 +235,21 @@ Official WPMU DEV Superhero', "defender-security" );
220
  $queue->args = $args;
221
  $queue->args['owner'] = $queue;
222
  $queue->attachBehavior( 'core', new Core_Scan() );
223
-
224
  return $queue;
225
  case 'vuln':
226
  if ( ! class_exists( '\WP_Defender\Module\Scan\Behavior\Pro\Vuln_Scan' ) ) {
227
  return null;
228
  }
229
-
230
  $queue = new Queue( array(
231
  'dummy'
232
  ), 'vuln', true );
233
-
234
  $queue->args = $args;
235
  $queue->args['owner'] = $queue;
236
  $queue->attachBehavior( 'vuln', new Vuln_Scan() );
237
-
238
  return $queue;
239
  break;
240
  case 'md5':
@@ -250,7 +265,7 @@ Official WPMU DEV Superhero', "defender-security" );
250
  $queue->args = $args;
251
  $queue->args['owner'] = $queue;
252
  $queue->attachBehavior( 'md5', new MD5_Scan() );
253
-
254
  return $queue;
255
  break;
256
  case 'content':
@@ -264,7 +279,7 @@ Official WPMU DEV Superhero', "defender-security" );
264
  $patterns = Scan_Api::getPatterns();
265
  $queue->args['patterns'] = $patterns;
266
  $queue->attachBehavior( 'content', new Content_Scan() );
267
-
268
  return $queue;
269
  break;
270
  default:
@@ -273,35 +288,71 @@ Official WPMU DEV Superhero', "defender-security" );
273
  break;
274
  }
275
  }
276
-
277
  public function events() {
278
  $that = $this;
279
-
280
  return array(
281
  self::EVENT_BEFORE_SAVE => array(
282
  array(
283
  function () use ( $that ) {
284
  //need to turn off notification or report off if no recipients
285
  $keys = array(
286
- 'receipts' => 'report',
287
- 'receiptsNotification' => 'notification'
288
  );
289
  foreach ( $keys as $key => $attr ) {
290
- if ( isset( $_POST[ $key ] ) ) {
291
- $recipients = $_POST[ $key ];
292
- foreach ( $recipients as &$recipient ) {
293
- $recipient = array_map( 'wp_strip_all_tags', $recipient );
 
 
294
  }
295
- $this->$key = $recipients;
296
  }
 
297
  $this->$key = array_filter( $this->$key );
298
  if ( count( $this->$key ) == 0 ) {
299
- $this->$attr = 0;
300
  }
301
  }
 
302
  }
303
  )
304
  )
305
  );
306
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  }
17
  use WP_Defender\Module\Scan\Component\Scan_Api;
18
 
19
  class Settings extends \Hammer\WP\Settings {
20
+
21
  private static $_instance;
22
  /**
23
  * Scan WP core files
30
  * @var bool
31
  */
32
  public $scan_vuln = true;
33
+
34
  /**
35
  * @var bool
36
  */
37
  public $scan_content = true;
38
+
39
  /**
40
  * Receipts to sending notification
41
  * @var array
42
  */
43
+ public $recipients = array();
44
+
45
  /**
46
  * @var array
47
  */
48
+ public $recipients_notification = array();
49
+
50
  /**
51
  * Toggle notification on or off
52
  * @var bool
53
  */
54
  public $notification = true;
55
+
56
  /**
57
  * @var bool
58
  */
63
  * @var bool
64
  */
65
  public $always_send = false;
66
+
67
  /**
68
  * @var bool
69
  */
70
+ public $always_send_notification = false;
71
+
72
  /**
73
  * Maximum filesize to scan, only apply for content scan
74
  * @var int
75
  */
76
  public $max_filesize = 1;
77
+
78
  /**
79
  * @var string
80
  */
87
  * @var string|void
88
  */
89
  public $email_all_ok = '';
90
+
91
  /**
92
  * @var string
93
  */
99
  /**
100
  * @var string
101
  */
102
+ public $time = '4:00';
103
+
104
+ /**
105
+ * @var
106
+ */
107
+ public $last_report_sent;
108
+
109
  /**
110
  * @return array
111
  */
113
  $behaviors = array(
114
  'utils' => '\WP_Defender\Behavior\Utils'
115
  );
116
+
117
  if ( wp_defender()->isFree == false ) {
118
  $behaviors['pro'] = '\WP_Defender\Module\Scan\Behavior\Pro\Model';
119
  }
120
+
121
  return $behaviors;
122
  }
123
+
124
  public function __construct( $id, $is_multi ) {
125
  $this->email_subject = __( 'Scan of {SITE_URL} complete. {ISSUES_COUNT} issues found.', "defender-security" );
126
  $this->email_has_issue = __( 'Hi {USER_NAME},
148
  if ( ( is_admin() || is_network_admin() ) && current_user_can( 'manage_options' ) ) {
149
  $user = wp_get_current_user();
150
  if ( is_object( $user ) ) {
151
+ $this->recipients[] = array(
152
  'first_name' => $user->display_name,
153
  'email' => $user->user_email
154
  );
155
+ $this->recipients_notification[] = array(
156
  'first_name' => $user->display_name,
157
  'email' => $user->user_email
158
  );
159
  }
160
+
161
  //default is weekly
162
+ $this->day = strtolower( date( 'l' ) );
163
+ $this->time = '4:00';
 
 
 
 
 
 
164
  }
 
165
  parent::__construct( $id, $is_multi );
166
+ $this->notification = ! ! $this->notification;
167
+ $this->report = ! ! $this->report;
168
+ $this->scan_content = ! ! $this->scan_content;
169
+ $this->scan_core = ! ! $this->scan_core;
170
+ $this->scan_vuln = ! ! $this->scan_vuln;
171
+
172
+ if ( ! is_array( $this->recipients ) ) {
173
+ $this->recipients = [];
174
+ }
175
+ $this->recipients = array_values( $this->recipients );
176
+ if ( ! is_array( $this->recipients_notification ) ) {
177
+ $this->recipients_notification = [];
178
+ }
179
+ $this->recipients_notification = array_values( $this->recipients_notification );
180
+
181
+ $times = Utils::instance()->getTimes();
182
+ if ( ! isset( $times[ $this->time ] ) ) {
183
+ $this->time = '4:00';
184
+ }
185
  }
186
+
187
  /**
188
  * Act like a factory, return available scans based on pro or not
189
  * @return array
193
  if ( $this->scan_core ) {
194
  $scans[] = 'core';
195
  }
196
+
197
  if ( $this->scan_vuln && wp_defender()->isFree != true ) {
198
  $scans[] = 'vuln';
199
  }
200
+
201
  if ( $this->scan_content && wp_defender()->isFree != true ) {
202
  //$scans[] = 'md5';
203
  $scans[] = 'content';
204
  }
205
+
206
  return $scans;
207
  }
208
+
209
  /**
210
  * @return Settings
211
  */
214
  $class = new Settings( 'wd_scan_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
215
  self::$_instance = $class;
216
  }
217
+
218
  return self::$_instance;
219
  }
220
+
221
  /**
222
  * @param $slug
223
  * @param array $args
235
  $queue->args = $args;
236
  $queue->args['owner'] = $queue;
237
  $queue->attachBehavior( 'core', new Core_Scan() );
238
+
239
  return $queue;
240
  case 'vuln':
241
  if ( ! class_exists( '\WP_Defender\Module\Scan\Behavior\Pro\Vuln_Scan' ) ) {
242
  return null;
243
  }
244
+
245
  $queue = new Queue( array(
246
  'dummy'
247
  ), 'vuln', true );
248
+
249
  $queue->args = $args;
250
  $queue->args['owner'] = $queue;
251
  $queue->attachBehavior( 'vuln', new Vuln_Scan() );
252
+
253
  return $queue;
254
  break;
255
  case 'md5':
265
  $queue->args = $args;
266
  $queue->args['owner'] = $queue;
267
  $queue->attachBehavior( 'md5', new MD5_Scan() );
268
+
269
  return $queue;
270
  break;
271
  case 'content':
279
  $patterns = Scan_Api::getPatterns();
280
  $queue->args['patterns'] = $patterns;
281
  $queue->attachBehavior( 'content', new Content_Scan() );
282
+
283
  return $queue;
284
  break;
285
  default:
288
  break;
289
  }
290
  }
291
+
292
  public function events() {
293
  $that = $this;
294
+
295
  return array(
296
  self::EVENT_BEFORE_SAVE => array(
297
  array(
298
  function () use ( $that ) {
299
  //need to turn off notification or report off if no recipients
300
  $keys = array(
301
+ 'recipients' => 'report',
302
+ 'recipients_notification' => 'notification'
303
  );
304
  foreach ( $keys as $key => $attr ) {
305
+ $recipients = $this->$key;
306
+ $recipients = ! is_array( $recipients ) ? [] : $recipients;
307
+ foreach ( $recipients as $k => &$recipient ) {
308
+ $recipient = array_map( 'sanitize_text_field', $recipient );
309
+ if ( ! filter_var( $recipient['email'], FILTER_VALIDATE_EMAIL ) ) {
310
+ unset( $recipients[ $k ] );
311
  }
 
312
  }
313
+ $this->$key = $recipients;
314
  $this->$key = array_filter( $this->$key );
315
  if ( count( $this->$key ) == 0 ) {
316
+ $this->$attr = false;
317
  }
318
  }
319
+
320
  }
321
  )
322
  )
323
  );
324
  }
325
+
326
+ /**
327
+ * Define labels for settings key, we will use it for HUB
328
+ *
329
+ * @param null $key
330
+ *
331
+ * @return array|mixed
332
+ */
333
+ public function labels( $key = null ) {
334
+ $labels = [
335
+ 'scan_core' => __( "Scan Types: WordPress Core", "defender-security" ),
336
+ 'scan_vuln' => __( "Scan Types: Plugins & Themes", "defender-security" ),
337
+ 'scan_content' => __( "Scan Types: Suspicious Code", "defender-security" ),
338
+ 'max_filesize' => __( "Maximum included file size", "defender-security" ),
339
+ 'report' => __( "Report", "defender-security" ),
340
+ 'always_send' => __( "Also send report when no issues are detected.", "defender-security" ),
341
+ 'recipients' => __( "Recipients for report", "defender-security" ),
342
+ 'day' => __( "Day of the week", "defender-security" ),
343
+ 'time' => __( "Time of day", "defender-security" ),
344
+ 'frequency' => __( "Frequency", "defender-security" ),
345
+ 'notification' => __( "Notification", "defender-security" ),
346
+ 'always_send_notification' => __( "Also send notification when no issues are detected.", "defender-security" ),
347
+ 'recipients_notification' => __( "Recipients for notification", "defender-security" ),
348
+ 'email_subject' => __( "Email Subject", "defender-security" ),
349
+ 'email_all_ok' => __( "When no issues are found", "defender-security" ),
350
+ 'email_has_issue' => __( "When an issue is found", "defender-security" )
351
+ ];
352
+ if ( $key != null ) {
353
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
354
+ }
355
+
356
+ return $labels;
357
+ }
358
  }
app/module/scan/view/automation-free.php CHANGED
@@ -14,7 +14,7 @@
14
  <div class="sui-box-settings-col-1">
15
  <span class="sui-settings-label"><?php _e( "Enable reporting", "defender-security" ) ?></span>
16
  <span class="sui-description">
17
- <?php _e( "Enabling this option will ensure youre always the first to know when something suspicious is detected on your site.", "defender-security" ) ?>
18
  </span>
19
  </div>
20
  <div class="sui-box-settings-col-2">
14
  <div class="sui-box-settings-col-1">
15
  <span class="sui-settings-label"><?php _e( "Enable reporting", "defender-security" ) ?></span>
16
  <span class="sui-description">
17
+ <?php _e( "Enabling this option will ensure you're always the first to know when something suspicious is detected on your site.", "defender-security" ) ?>
18
  </span>
19
  </div>
20
  <div class="sui-box-settings-col-2">
app/module/scan/view/email-template.php CHANGED
@@ -2,657 +2,658 @@
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
- <meta name="viewport" content="width=device-width">
7
- <title><?php echo $subject ?></title>
8
- <style>
9
- a.plugin-brand:hover {
10
- color: #e23717 !important;
11
- }
12
-
13
- table.top-content td a:hover {
14
- color: #ff5c28 !important;
15
- }
16
- </style>
17
  </head>
18
 
19
  <body
20
- style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
- @media only screen {
23
- html {
24
- min-height: 100%;
25
- background: #f3f3f3;
26
- }
27
- }
28
-
29
- @media only screen and (max-width: 596px) {
30
- .small-float-center {
31
- margin: 0 auto !important;
32
- float: none !important;
33
- text-align: center !important;
34
- }
35
-
36
- .small-text-center {
37
- text-align: center !important;
38
- }
39
-
40
- .small-text-left {
41
- text-align: left !important;
42
- }
43
-
44
- .small-text-right {
45
- text-align: right !important;
46
- }
47
- }
48
-
49
- @media only screen and (max-width: 596px) {
50
- .hide-for-large {
51
- display: block !important;
52
- width: auto !important;
53
- overflow: visible !important;
54
- max-height: none !important;
55
- font-size: inherit !important;
56
- line-height: inherit !important;
57
- }
58
- }
59
-
60
- @media only screen and (max-width: 596px) {
61
- table.body table.container .hide-for-large,
62
- table.body table.container .row.hide-for-large {
63
- display: table !important;
64
- width: 100% !important;
65
- }
66
- }
67
-
68
- @media only screen and (max-width: 596px) {
69
- table.body table.container .callout-inner.hide-for-large {
70
- display: table-cell !important;
71
- width: 100% !important;
72
- }
73
- }
74
-
75
- @media only screen and (max-width: 596px) {
76
- table.body table.container .show-for-large {
77
- display: none !important;
78
- width: 0;
79
- mso-hide: all;
80
- overflow: hidden;
81
- }
82
- }
83
-
84
- @media only screen and (max-width: 596px) {
85
- table.body img {
86
- width: auto;
87
- height: auto;
88
- }
89
-
90
- table.body center {
91
- min-width: 0 !important;
92
- }
93
-
94
- table.body .container {
95
- width: 95% !important;
96
- }
97
-
98
- table.body .columns,
99
- table.body .column {
100
- height: auto !important;
101
- -moz-box-sizing: border-box;
102
- -webkit-box-sizing: border-box;
103
- box-sizing: border-box;
104
- padding-left: 16px !important;
105
- padding-right: 16px !important;
106
- }
107
-
108
- table.body .columns .column,
109
- table.body .columns .columns,
110
- table.body .column .column,
111
- table.body .column .columns {
112
- padding-left: 0 !important;
113
- padding-right: 0 !important;
114
- }
115
-
116
- table.body .collapse .columns,
117
- table.body .collapse .column {
118
- padding-left: 0 !important;
119
- padding-right: 0 !important;
120
- }
121
-
122
- td.small-1,
123
- th.small-1 {
124
- display: inline-block !important;
125
- width: 8.33333% !important;
126
- }
127
-
128
- td.small-2,
129
- th.small-2 {
130
- display: inline-block !important;
131
- width: 16.66667% !important;
132
- }
133
-
134
- td.small-3,
135
- th.small-3 {
136
- display: inline-block !important;
137
- width: 25% !important;
138
- }
139
-
140
- td.small-4,
141
- th.small-4 {
142
- display: inline-block !important;
143
- width: 33.33333% !important;
144
- }
145
-
146
- td.small-5,
147
- th.small-5 {
148
- display: inline-block !important;
149
- width: 41.66667% !important;
150
- }
151
-
152
- td.small-6,
153
- th.small-6 {
154
- display: inline-block !important;
155
- width: 50% !important;
156
- }
157
-
158
- td.small-7,
159
- th.small-7 {
160
- display: inline-block !important;
161
- width: 58.33333% !important;
162
- }
163
-
164
- td.small-8,
165
- th.small-8 {
166
- display: inline-block !important;
167
- width: 66.66667% !important;
168
- }
169
-
170
- td.small-9,
171
- th.small-9 {
172
- display: inline-block !important;
173
- width: 75% !important;
174
- }
175
-
176
- td.small-10,
177
- th.small-10 {
178
- display: inline-block !important;
179
- width: 83.33333% !important;
180
- }
181
-
182
- td.small-11,
183
- th.small-11 {
184
- display: inline-block !important;
185
- width: 91.66667% !important;
186
- }
187
-
188
- td.small-12,
189
- th.small-12 {
190
- display: inline-block !important;
191
- width: 100% !important;
192
- }
193
-
194
- .columns td.small-12,
195
- .column td.small-12,
196
- .columns th.small-12,
197
- .column th.small-12 {
198
- display: block !important;
199
- width: 100% !important;
200
- }
201
-
202
- table.body td.small-offset-1,
203
- table.body th.small-offset-1 {
204
- margin-left: 8.33333% !important;
205
- Margin-left: 8.33333% !important;
206
- }
207
-
208
- table.body td.small-offset-2,
209
- table.body th.small-offset-2 {
210
- margin-left: 16.66667% !important;
211
- Margin-left: 16.66667% !important;
212
- }
213
-
214
- table.body td.small-offset-3,
215
- table.body th.small-offset-3 {
216
- margin-left: 25% !important;
217
- Margin-left: 25% !important;
218
- }
219
-
220
- table.body td.small-offset-4,
221
- table.body th.small-offset-4 {
222
- margin-left: 33.33333% !important;
223
- Margin-left: 33.33333% !important;
224
- }
225
-
226
- table.body td.small-offset-5,
227
- table.body th.small-offset-5 {
228
- margin-left: 41.66667% !important;
229
- Margin-left: 41.66667% !important;
230
- }
231
-
232
- table.body td.small-offset-6,
233
- table.body th.small-offset-6 {
234
- margin-left: 50% !important;
235
- Margin-left: 50% !important;
236
- }
237
-
238
- table.body td.small-offset-7,
239
- table.body th.small-offset-7 {
240
- margin-left: 58.33333% !important;
241
- Margin-left: 58.33333% !important;
242
- }
243
-
244
- table.body td.small-offset-8,
245
- table.body th.small-offset-8 {
246
- margin-left: 66.66667% !important;
247
- Margin-left: 66.66667% !important;
248
- }
249
-
250
- table.body td.small-offset-9,
251
- table.body th.small-offset-9 {
252
- margin-left: 75% !important;
253
- Margin-left: 75% !important;
254
- }
255
-
256
- table.body td.small-offset-10,
257
- table.body th.small-offset-10 {
258
- margin-left: 83.33333% !important;
259
- Margin-left: 83.33333% !important;
260
- }
261
-
262
- table.body td.small-offset-11,
263
- table.body th.small-offset-11 {
264
- margin-left: 91.66667% !important;
265
- Margin-left: 91.66667% !important;
266
- }
267
-
268
- table.body table.columns td.expander,
269
- table.body table.columns th.expander {
270
- display: none !important;
271
- }
272
-
273
- table.body .right-text-pad,
274
- table.body .text-pad-right {
275
- padding-left: 10px !important;
276
- }
277
-
278
- table.body .left-text-pad,
279
- table.body .text-pad-left {
280
- padding-right: 10px !important;
281
- }
282
-
283
- table.menu {
284
- width: 100% !important;
285
- }
286
-
287
- table.menu td,
288
- table.menu th {
289
- width: auto !important;
290
- display: inline-block !important;
291
- }
292
-
293
- table.menu.vertical td,
294
- table.menu.vertical th,
295
- table.menu.small-vertical td,
296
- table.menu.small-vertical th {
297
- display: block !important;
298
- }
299
-
300
- table.menu[align="center"] {
301
- width: auto !important;
302
- }
303
-
304
- table.button.small-expand,
305
- table.button.small-expanded {
306
- width: 100% !important;
307
- }
308
-
309
- table.button.small-expand table,
310
- table.button.small-expanded table {
311
- width: 100%;
312
- }
313
-
314
- table.button.small-expand table a,
315
- table.button.small-expanded table a {
316
- text-align: center !important;
317
- width: 100% !important;
318
- padding-left: 0 !important;
319
- padding-right: 0 !important;
320
- }
321
-
322
- table.button.small-expand center,
323
- table.button.small-expanded center {
324
- min-width: 0;
325
- }
326
- }
327
-
328
- @media screen and (max-width: 596px) {
329
- /* results list */
330
- table.results-list thead th {
331
- line-height: 34px !important;
332
- }
333
-
334
- /* top */
335
- table.top-content td {
336
- text-align: center !important;
337
- }
338
-
339
- /* related */
340
- table.related table.related-items .columns {
341
- padding-right: 0 !important;
342
- padding-bottom: 15px !important;
343
- padding-left: 0 !important;
344
- }
345
-
346
- table.related table.related-items .columns.last {
347
- padding-bottom: 0 !important;
348
- }
349
-
350
- table.related a.related-item .plugin-info {
351
- vertical-align: middle !important;
352
- }
353
-
354
- /* company info */
355
- table.company-info .columns {
356
- padding-right: 0 !important;
357
- padding-left: 0 !important;
358
- }
359
-
360
- table.company-info .columns.last {
361
- padding: 15px 0 0 !important;
362
- }
363
-
364
- table.company-info .logo,
365
- table.company-info .logo-link,
366
- table.company-info .logo img {
367
- text-align: left !important;
368
- }
369
- }
370
-
371
- @media screen and (max-width: 540px) {
372
- /* hero */
373
- table.hero table.hero-content {
374
- width: 100%;
375
- }
376
-
377
- table.hero td.hero-title h1,
378
- table.hero td.hero-title h2 {
379
- padding: 0 !important;
380
- text-align: center !important;
381
- }
382
-
383
- table.hero td.hero-image {
384
- display: none;
385
- }
386
- }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
- <tbody>
392
- <tr style="padding: 0; text-align: left; vertical-align: top;">
393
- <td class="center" align="center" valign="top"
394
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
-
396
- <center style="min-width: 600px; width: 100%;">
397
-
398
- <table class="container"
399
- style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
- <tbody>
401
- <tr style="padding: 0; text-align: left; vertical-align: top;">
402
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
- <table class="wrapper hero" align="left"
404
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
- <tbody>
406
- <tr style="padding: 0; text-align: left; vertical-align: top;">
407
- <td class="wrapper-inner hero-inner"
408
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
-
410
- <table class="hero-content" align="left"
411
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
- <tbody>
413
- <tr style="padding: 0; text-align: left; vertical-align: top;">
414
- <td class="hero-title"
415
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
- <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php _e( "Protected By", "defender-security" ) ?></h2>
418
- <h1 class="plugin-brand"
419
- style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php _e( "Defender!", "defender-security" ) ?></h1>
421
- </td>
422
- <td class="hero-image"
423
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
- <a href="https://premium.wpmudev.org/"
425
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
- alt="Defender"
428
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
- </td>
430
- </tr>
431
- </tbody>
432
- </table>
433
- <!-- end hero-content -->
434
-
435
- </td>
436
- </tr>
437
- </tbody>
438
- </table>
439
- <!-- end hero -->
440
-
441
- <table class="wrapper main" align="center"
442
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
- <tbody>
444
- <tr style="padding: 0; text-align: left; vertical-align: top;">
445
- <td class="wrapper-inner main-inner"
446
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
-
448
- <table class="main-intro"
449
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
- <tbody>
451
- <tr style="padding: 0; text-align: left; vertical-align: top;">
452
- <td class="main-intro-content"
453
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
  <?php echo $message ?>
455
- </td>
456
- </tr>
457
- </tbody>
458
- </table>
459
- </td>
460
- </tr>
461
- </tbody>
462
- </table>
463
- <!-- end main -->
464
-
465
- <table class="related" align="center"
466
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
467
- <tbody>
468
- <tr style="padding: 0; text-align: left; vertical-align: top;">
469
- <td class="related-inner"
470
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
471
- <table
472
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
- <tbody>
474
- <tr style="padding: 0; text-align: left; vertical-align: top;">
475
- <td class="related-items-title brand" align="left"
476
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
477
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
478
- </td>
479
- </tr>
480
- <tr style="padding: 0; text-align: left; vertical-align: top;">
481
- <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
482
- <table class="related-items row collapse" align="center"
483
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
484
- <tbody>
485
- <tr style="padding: 0; text-align: left; vertical-align: top;">
486
- <th class="small-12 large-6 columns first" align="left"
487
- valign="top"
488
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
489
- <a class="related-item plugin-link"
490
- href="https://premium.wpmudev.org/project/wp-hummingbird/"
491
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
492
- <img
493
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
494
- alt="Hummingbird" class="plugin-image"
495
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
496
- <span class="plugin-info"
497
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
498
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
499
  <span class="plugin-title hummingbird"
500
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
501
  </span>
502
- </a>
503
- </th>
504
- <th class="small-12 large-6 columns last" align="left"
505
- valign="top"
506
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
507
- <a class="related-item plugin-link"
508
- href="https://premium.wpmudev.org/project/snapshot/"
509
- style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
510
- <img
511
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
512
- alt="Snapshot"
513
- class="plugin-image"
514
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
515
- <span class="plugin-info"
516
- style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
517
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
518
  <span class="plugin-title snapshot"
519
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
520
  </span>
521
- </a>
522
- </th>
523
- <th class="expander"
524
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
525
- </tr>
526
- </tbody>
527
- </table>
528
- </td>
529
- </tr>
530
- </tbody>
531
- </table>
532
- <!-- end related-inner -->
533
- </td>
534
- </tr>
535
- </tbody>
536
- </table>
537
- <!-- end related -->
538
-
539
- <!-- Preferences -->
540
- <table class="email-preferences" align="center" valign="middle"
541
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
542
- <tbody>
543
- <tr style="padding: 0; text-align: center; vertical-align: top;">
544
- <td class="email-preferences-inner"
545
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
546
- <table class="email-preferences-content row collapse" align="center" valign="top"
547
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
548
- <tbody>
549
- <tr style="padding: 0; text-align: center; vertical-align: top;">
550
- <th class="small-12 large-8 columns first copy" align="center"
551
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
552
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
553
- <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), network_admin_url( "admin.php?page=wdf-scan&view=reporting" ) ) ?>
554
- </p>
555
- </th>
556
- </tr>
557
- </tbody>
558
- </table>
559
- </td>
560
- </tr>
561
- </tbody>
562
- </table>
563
- <!-- End Preferences -->
564
-
565
- <table class="company-info" align="left" valign="middle"
566
- style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
567
- <tbody>
568
- <tr style="padding: 0; text-align: left; vertical-align: top;">
569
- <td class="company-info-inner"
570
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
571
- <table class="company-info-content row collapse" align="left" valign="top"
572
- style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
573
- <tbody>
574
- <tr style="padding: 0; text-align: left; vertical-align: top;">
575
- <th class="small-12 large-8 columns first copy" align="left"
576
- style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
577
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
578
- Copyright © Incsub, All rights reserved.</p>
579
- <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
580
- Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
581
- </th>
582
- <th class="small-12 large-4 columns last logo" align="right"
583
- style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
584
- <a href="https://premium.wpmudev.org" class="logo-link"
585
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
586
- <img
587
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
588
- alt="WPMU DEV"
589
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
590
- </a>
591
- </th>
592
- <th class="expander"
593
- style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
594
- </tr>
595
- </tbody>
596
- </table>
597
- </td>
598
- </tr>
599
- </tbody>
600
- </table>
601
- <!-- end company-info -->
602
-
603
- <table class="wrapper social" align="center"
604
- style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
605
- <tbody>
606
- <tr style="padding: 0; text-align: left; vertical-align: top;">
607
- <td class="wrapper-inner social-inner"
608
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
609
-
610
- <table class="social-content" align="center"
611
- style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
612
- <tbody>
613
- <tr style="padding: 0; text-align: left; vertical-align: top;">
614
- <td class="social-content-inner"
615
- style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
616
- <a href="https://plus.google.com/+wpmuorg/" target="_blank"
617
- class="gplus"
618
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
619
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
620
- alt="WPMU DEV on Google+"
621
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
622
- <a href="https://twitter.com/wpmudev" target="_blank"
623
- class="twitter"
624
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
625
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
626
- alt="WPMU DEV on Twitter"
627
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
628
- <a href="https://www.facebook.com/wpmudev" target="_blank"
629
- class="facebook"
630
- style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
631
- src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
632
- alt="WPMU DEV on Facebook"
633
- style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
634
- </td>
635
- </tr>
636
- </tbody>
637
- </table>
638
- <!-- end social-content -->
639
-
640
- </td>
641
- </tr>
642
- </tbody>
643
- </table>
644
- <!-- end top -->
645
- </td>
646
- </tr>
647
- </tbody>
648
- </table>
649
- <!-- end main container -->
650
-
651
- </center>
652
-
653
- </td>
654
- </tr>
655
- </tbody>
 
656
  </table>
657
  <!-- end body -->
658
  </body>
2
  <html xmlns="http://www.w3.org/1999/xhtml">
3
 
4
  <head>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
6
+ <meta name="viewport" content="width=device-width">
7
+ <title><?php echo $subject ?></title>
8
+ <style>
9
+ a.plugin-brand:hover {
10
+ color: #e23717 !important;
11
+ }
12
+
13
+ table.top-content td a:hover {
14
+ color: #ff5c28 !important;
15
+ }
16
+ </style>
17
  </head>
18
 
19
  <body
20
+ style="-moz-box-sizing: border-box; -ms-text-size-adjust: 100%; -webkit-box-sizing: border-box; -webkit-text-size-adjust: 100%; Margin: 0; background-color: #e9ebe7; box-sizing: border-box; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; min-width: 100%; padding: 0; text-align: left; width: 100% !important;">
21
  <style>
22
+ @media only screen {
23
+ html {
24
+ min-height: 100%;
25
+ background: #f3f3f3;
26
+ }
27
+ }
28
+
29
+ @media only screen and (max-width: 596px) {
30
+ .small-float-center {
31
+ margin: 0 auto !important;
32
+ float: none !important;
33
+ text-align: center !important;
34
+ }
35
+
36
+ .small-text-center {
37
+ text-align: center !important;
38
+ }
39
+
40
+ .small-text-left {
41
+ text-align: left !important;
42
+ }
43
+
44
+ .small-text-right {
45
+ text-align: right !important;
46
+ }
47
+ }
48
+
49
+ @media only screen and (max-width: 596px) {
50
+ .hide-for-large {
51
+ display: block !important;
52
+ width: auto !important;
53
+ overflow: visible !important;
54
+ max-height: none !important;
55
+ font-size: inherit !important;
56
+ line-height: inherit !important;
57
+ }
58
+ }
59
+
60
+ @media only screen and (max-width: 596px) {
61
+ table.body table.container .hide-for-large,
62
+ table.body table.container .row.hide-for-large {
63
+ display: table !important;
64
+ width: 100% !important;
65
+ }
66
+ }
67
+
68
+ @media only screen and (max-width: 596px) {
69
+ table.body table.container .callout-inner.hide-for-large {
70
+ display: table-cell !important;
71
+ width: 100% !important;
72
+ }
73
+ }
74
+
75
+ @media only screen and (max-width: 596px) {
76
+ table.body table.container .show-for-large {
77
+ display: none !important;
78
+ width: 0;
79
+ mso-hide: all;
80
+ overflow: hidden;
81
+ }
82
+ }
83
+
84
+ @media only screen and (max-width: 596px) {
85
+ table.body img {
86
+ width: auto;
87
+ height: auto;
88
+ }
89
+
90
+ table.body center {
91
+ min-width: 0 !important;
92
+ }
93
+
94
+ table.body .container {
95
+ width: 95% !important;
96
+ }
97
+
98
+ table.body .columns,
99
+ table.body .column {
100
+ height: auto !important;
101
+ -moz-box-sizing: border-box;
102
+ -webkit-box-sizing: border-box;
103
+ box-sizing: border-box;
104
+ padding-left: 16px !important;
105
+ padding-right: 16px !important;
106
+ }
107
+
108
+ table.body .columns .column,
109
+ table.body .columns .columns,
110
+ table.body .column .column,
111
+ table.body .column .columns {
112
+ padding-left: 0 !important;
113
+ padding-right: 0 !important;
114
+ }
115
+
116
+ table.body .collapse .columns,
117
+ table.body .collapse .column {
118
+ padding-left: 0 !important;
119
+ padding-right: 0 !important;
120
+ }
121
+
122
+ td.small-1,
123
+ th.small-1 {
124
+ display: inline-block !important;
125
+ width: 8.33333% !important;
126
+ }
127
+
128
+ td.small-2,
129
+ th.small-2 {
130
+ display: inline-block !important;
131
+ width: 16.66667% !important;
132
+ }
133
+
134
+ td.small-3,
135
+ th.small-3 {
136
+ display: inline-block !important;
137
+ width: 25% !important;
138
+ }
139
+
140
+ td.small-4,
141
+ th.small-4 {
142
+ display: inline-block !important;
143
+ width: 33.33333% !important;
144
+ }
145
+
146
+ td.small-5,
147
+ th.small-5 {
148
+ display: inline-block !important;
149
+ width: 41.66667% !important;
150
+ }
151
+
152
+ td.small-6,
153
+ th.small-6 {
154
+ display: inline-block !important;
155
+ width: 50% !important;
156
+ }
157
+
158
+ td.small-7,
159
+ th.small-7 {
160
+ display: inline-block !important;
161
+ width: 58.33333% !important;
162
+ }
163
+
164
+ td.small-8,
165
+ th.small-8 {
166
+ display: inline-block !important;
167
+ width: 66.66667% !important;
168
+ }
169
+
170
+ td.small-9,
171
+ th.small-9 {
172
+ display: inline-block !important;
173
+ width: 75% !important;
174
+ }
175
+
176
+ td.small-10,
177
+ th.small-10 {
178
+ display: inline-block !important;
179
+ width: 83.33333% !important;
180
+ }
181
+
182
+ td.small-11,
183
+ th.small-11 {
184
+ display: inline-block !important;
185
+ width: 91.66667% !important;
186
+ }
187
+
188
+ td.small-12,
189
+ th.small-12 {
190
+ display: inline-block !important;
191
+ width: 100% !important;
192
+ }
193
+
194
+ .columns td.small-12,
195
+ .column td.small-12,
196
+ .columns th.small-12,
197
+ .column th.small-12 {
198
+ display: block !important;
199
+ width: 100% !important;
200
+ }
201
+
202
+ table.body td.small-offset-1,
203
+ table.body th.small-offset-1 {
204
+ margin-left: 8.33333% !important;
205
+ Margin-left: 8.33333% !important;
206
+ }
207
+
208
+ table.body td.small-offset-2,
209
+ table.body th.small-offset-2 {
210
+ margin-left: 16.66667% !important;
211
+ Margin-left: 16.66667% !important;
212
+ }
213
+
214
+ table.body td.small-offset-3,
215
+ table.body th.small-offset-3 {
216
+ margin-left: 25% !important;
217
+ Margin-left: 25% !important;
218
+ }
219
+
220
+ table.body td.small-offset-4,
221
+ table.body th.small-offset-4 {
222
+ margin-left: 33.33333% !important;
223
+ Margin-left: 33.33333% !important;
224
+ }
225
+
226
+ table.body td.small-offset-5,
227
+ table.body th.small-offset-5 {
228
+ margin-left: 41.66667% !important;
229
+ Margin-left: 41.66667% !important;
230
+ }
231
+
232
+ table.body td.small-offset-6,
233
+ table.body th.small-offset-6 {
234
+ margin-left: 50% !important;
235
+ Margin-left: 50% !important;
236
+ }
237
+
238
+ table.body td.small-offset-7,
239
+ table.body th.small-offset-7 {
240
+ margin-left: 58.33333% !important;
241
+ Margin-left: 58.33333% !important;
242
+ }
243
+
244
+ table.body td.small-offset-8,
245
+ table.body th.small-offset-8 {
246
+ margin-left: 66.66667% !important;
247
+ Margin-left: 66.66667% !important;
248
+ }
249
+
250
+ table.body td.small-offset-9,
251
+ table.body th.small-offset-9 {
252
+ margin-left: 75% !important;
253
+ Margin-left: 75% !important;
254
+ }
255
+
256
+ table.body td.small-offset-10,
257
+ table.body th.small-offset-10 {
258
+ margin-left: 83.33333% !important;
259
+ Margin-left: 83.33333% !important;
260
+ }
261
+
262
+ table.body td.small-offset-11,
263
+ table.body th.small-offset-11 {
264
+ margin-left: 91.66667% !important;
265
+ Margin-left: 91.66667% !important;
266
+ }
267
+
268
+ table.body table.columns td.expander,
269
+ table.body table.columns th.expander {
270
+ display: none !important;
271
+ }
272
+
273
+ table.body .right-text-pad,
274
+ table.body .text-pad-right {
275
+ padding-left: 10px !important;
276
+ }
277
+
278
+ table.body .left-text-pad,
279
+ table.body .text-pad-left {
280
+ padding-right: 10px !important;
281
+ }
282
+
283
+ table.menu {
284
+ width: 100% !important;
285
+ }
286
+
287
+ table.menu td,
288
+ table.menu th {
289
+ width: auto !important;
290
+ display: inline-block !important;
291
+ }
292
+
293
+ table.menu.vertical td,
294
+ table.menu.vertical th,
295
+ table.menu.small-vertical td,
296
+ table.menu.small-vertical th {
297
+ display: block !important;
298
+ }
299
+
300
+ table.menu[align="center"] {
301
+ width: auto !important;
302
+ }
303
+
304
+ table.button.small-expand,
305
+ table.button.small-expanded {
306
+ width: 100% !important;
307
+ }
308
+
309
+ table.button.small-expand table,
310
+ table.button.small-expanded table {
311
+ width: 100%;
312
+ }
313
+
314
+ table.button.small-expand table a,
315
+ table.button.small-expanded table a {
316
+ text-align: center !important;
317
+ width: 100% !important;
318
+ padding-left: 0 !important;
319
+ padding-right: 0 !important;
320
+ }
321
+
322
+ table.button.small-expand center,
323
+ table.button.small-expanded center {
324
+ min-width: 0;
325
+ }
326
+ }
327
+
328
+ @media screen and (max-width: 596px) {
329
+ /* results list */
330
+ table.results-list thead th {
331
+ line-height: 34px !important;
332
+ }
333
+
334
+ /* top */
335
+ table.top-content td {
336
+ text-align: center !important;
337
+ }
338
+
339
+ /* related */
340
+ table.related table.related-items .columns {
341
+ padding-right: 0 !important;
342
+ padding-bottom: 15px !important;
343
+ padding-left: 0 !important;
344
+ }
345
+
346
+ table.related table.related-items .columns.last {
347
+ padding-bottom: 0 !important;
348
+ }
349
+
350
+ table.related a.related-item .plugin-info {
351
+ vertical-align: middle !important;
352
+ }
353
+
354
+ /* company info */
355
+ table.company-info .columns {
356
+ padding-right: 0 !important;
357
+ padding-left: 0 !important;
358
+ }
359
+
360
+ table.company-info .columns.last {
361
+ padding: 15px 0 0 !important;
362
+ }
363
+
364
+ table.company-info .logo,
365
+ table.company-info .logo-link,
366
+ table.company-info .logo img {
367
+ text-align: left !important;
368
+ }
369
+ }
370
+
371
+ @media screen and (max-width: 540px) {
372
+ /* hero */
373
+ table.hero table.hero-content {
374
+ width: 100%;
375
+ }
376
+
377
+ table.hero td.hero-title h1,
378
+ table.hero td.hero-title h2 {
379
+ padding: 0 !important;
380
+ text-align: center !important;
381
+ }
382
+
383
+ table.hero td.hero-image {
384
+ display: none;
385
+ }
386
+ }
387
  </style>
388
 
389
  <table class="body"
390
  style="Margin: 0; background: #f3f3f3; background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; height: 100%; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
391
+ <tbody>
392
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
393
+ <td class="center" align="center" valign="top"
394
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
395
+
396
+ <center style="min-width: 600px; width: 100%;">
397
+
398
+ <table class="container"
399
+ style="Margin: 0 auto; background: #fefefe; background-color: #fff; border-collapse: collapse; border-spacing: 0; margin: 0 auto; padding: 0; text-align: inherit; vertical-align: top; width: 600px;">
400
+ <tbody>
401
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
402
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
403
+ <table class="wrapper hero" align="left"
404
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
405
+ <tbody>
406
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
407
+ <td class="wrapper-inner hero-inner"
408
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 20px 0 0; text-align: left; vertical-align: top; word-wrap: break-word;">
409
+
410
+ <table class="hero-content" align="left"
411
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
412
+ <tbody>
413
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
414
+ <td class="hero-title"
415
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 18px; text-align: left; vertical-align: bottom; word-wrap: break-word;">
416
+ <h2 style="Margin: 0; Margin-bottom: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 30px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 9px; text-align: left; text-transform: uppercase; word-wrap: normal;">
417
  <?php _e( "Protected By", "defender-security" ) ?></h2>
418
+ <h1 class="plugin-brand"
419
+ style="Margin: 0; Margin-bottom: 0; color: #ff5c28; font-family: Helvetica, Arial, sans-serif; font-size: 60px; font-weight: 700; line-height: 1em; margin: 0; margin-bottom: 0; padding: 0; padding-left: 6px; text-align: left; text-transform: uppercase; word-wrap: normal;">
420
  <?php _e( "Defender!", "defender-security" ) ?></h1>
421
+ </td>
422
+ <td class="hero-image"
423
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
424
+ <a href="https://premium.wpmudev.org/"
425
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: left; text-decoration: none;"><img
426
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/hero-defender.png"
427
+ alt="Defender"
428
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
429
+ </td>
430
+ </tr>
431
+ </tbody>
432
+ </table>
433
+ <!-- end hero-content -->
434
+
435
+ </td>
436
+ </tr>
437
+ </tbody>
438
+ </table>
439
+ <!-- end hero -->
440
+
441
+ <table class="wrapper main" align="center"
442
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
443
+ <tbody>
444
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
445
+ <td class="wrapper-inner main-inner"
446
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
447
+
448
+ <table class="main-intro"
449
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
450
+ <tbody>
451
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
452
+ <td class="main-intro-content"
453
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
454
  <?php echo $message ?>
455
+ </td>
456
+ </tr>
457
+ </tbody>
458
+ </table>
459
+ </td>
460
+ </tr>
461
+ </tbody>
462
+ </table>
463
+ <!-- end main -->
464
+
465
+ <table class="related" align="center"
466
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
467
+ <tbody>
468
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
469
+ <td class="related-inner"
470
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 15px 30px 15px 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
471
+ <table
472
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
473
+ <tbody>
474
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
475
+ <td class="related-items-title brand" align="left"
476
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #3eb4e4; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; padding-bottom: 10px; text-align: left; vertical-align: top; word-wrap: break-word;">
477
  <?php esc_html_e( "Related plugins worth giving a try", "defender-security" ) ?>
478
+ </td>
479
+ </tr>
480
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
481
+ <td style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: left; vertical-align: top; word-wrap: break-word;">
482
+ <table class="related-items row collapse" align="center"
483
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
484
+ <tbody>
485
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
486
+ <th class="small-12 large-6 columns first" align="left"
487
+ valign="top"
488
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
489
+ <a class="related-item plugin-link"
490
+ href="https://premium.wpmudev.org/project/wp-hummingbird/"
491
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
492
+ <img
493
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-hummingbird.png"
494
+ alt="Hummingbird" class="plugin-image"
495
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
496
+ <span class="plugin-info"
497
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
498
  <span><?php esc_html_e( "Optimize your site with", "defender-security" ) ?></span>
499
  <span class="plugin-title hummingbird"
500
  style="color: #febd30; display: block;"><strong><?php esc_html_e( "Hummingbird", "defender-security" ) ?></strong></span>
501
  </span>
502
+ </a>
503
+ </th>
504
+ <th class="small-12 large-6 columns last" align="left"
505
+ valign="top"
506
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 298px;">
507
+ <a class="related-item plugin-link"
508
+ href="https://premium.wpmudev.org/project/snapshot/"
509
+ style="Margin: 0; color: #555555; display: table; font-family: Helvetica, Arial, sans-serif; font-size: 14px; font-weight: normal; line-height: 20px; margin: 0; padding: 0; text-align: left; text-decoration: none;">
510
+ <img
511
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/plugin-snapshot.png"
512
+ alt="Snapshot"
513
+ class="plugin-image"
514
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: table-cell; max-width: 100%; outline: none; text-decoration: none; width: auto;">
515
+ <span class="plugin-info"
516
+ style="display: table-cell; padding-left: 10px; vertical-align: bottom;">
517
  <span><?php esc_html_e( "Back up your hard work with", "defender-security" ) ?></span>
518
  <span class="plugin-title snapshot"
519
  style="color: #642486; display: block;"><strong><?php _e( "Snapshot", "defender-security" ) ?></strong></span>
520
  </span>
521
+ </a>
522
+ </th>
523
+ <th class="expander"
524
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
525
+ </tr>
526
+ </tbody>
527
+ </table>
528
+ </td>
529
+ </tr>
530
+ </tbody>
531
+ </table>
532
+ <!-- end related-inner -->
533
+ </td>
534
+ </tr>
535
+ </tbody>
536
+ </table>
537
+ <!-- end related -->
538
+
539
+ <!-- Preferences -->
540
+ <table class="email-preferences" align="center" valign="middle"
541
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
542
+ <tbody>
543
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
544
+ <td class="email-preferences-inner"
545
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
546
+ <table class="email-preferences-content row collapse" align="center"
547
+ valign="top"
548
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: center; vertical-align: top; width: 100%;">
549
+ <tbody>
550
+ <tr style="padding: 0; text-align: center; vertical-align: top;">
551
+ <th class="small-12 large-8 columns first copy" align="center"
552
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
553
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: center;">
554
+ <?php printf( __( "<a href=\"%s\">Configure reporting preferences</a>", "defender-security" ), apply_filters( 'wp_defender/scan/email_report_link', network_admin_url( "admin.php?page=wdf-scan&view=reporting" ) ) ) ?>
555
+ </p>
556
+ </th>
557
+ </tr>
558
+ </tbody>
559
+ </table>
560
+ </td>
561
+ </tr>
562
+ </tbody>
563
+ </table>
564
+ <!-- End Preferences -->
565
+
566
+ <table class="company-info" align="left" valign="middle"
567
+ style="border-collapse: collapse; border-spacing: 0; margin: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
568
+ <tbody>
569
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
570
+ <td class="company-info-inner"
571
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; border-top: 2px solid #e9ebe7; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px; text-align: left; vertical-align: top; word-wrap: break-word;">
572
+ <table class="company-info-content row collapse" align="left" valign="top"
573
+ style="border-collapse: collapse; border-spacing: 0; display: table; padding: 0; position: relative; text-align: left; vertical-align: top; width: 100%;">
574
+ <tbody>
575
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
576
+ <th class="small-12 large-8 columns first copy" align="left"
577
+ style="Margin: 0 auto; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: left; width: 394.66667px;">
578
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
579
+ Copyright © Incsub, All rights reserved.</p>
580
+ <p style="Margin: 0; Margin-bottom: 0; color: #707070; font-family: Helvetica, Arial, sans-serif; font-size: 12px; font-weight: normal; line-height: 20px; margin: 0; margin-bottom: 0; padding: 0; text-align: left;">
581
+ Incsub PO box 163 Albert Park, Victoria 3206 Australia</p>
582
+ </th>
583
+ <th class="small-12 large-4 columns last logo" align="right"
584
+ style="Margin: 0 auto; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0 auto; padding: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; text-align: right; width: 201.33333px;">
585
+ <a href="https://premium.wpmudev.org" class="logo-link"
586
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; padding: 0; text-align: right; text-decoration: none;">
587
+ <img
588
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/wpmudev-logo.png"
589
+ alt="WPMU DEV"
590
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: inline-block; max-width: 100%; outline: none; text-align: right; text-decoration: none; width: auto;">
591
+ </a>
592
+ </th>
593
+ <th class="expander"
594
+ style="Margin: 0; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; line-height: 26px; margin: 0; padding: 0 !important; text-align: left; visibility: hidden; width: 0;"></th>
595
+ </tr>
596
+ </tbody>
597
+ </table>
598
+ </td>
599
+ </tr>
600
+ </tbody>
601
+ </table>
602
+ <!-- end company-info -->
603
+
604
+ <table class="wrapper social" align="center"
605
+ style="background-color: #e9ebe7; border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top; width: 100%;">
606
+ <tbody>
607
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
608
+ <td class="wrapper-inner social-inner"
609
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 40px 60px 20px; text-align: left; vertical-align: top; word-wrap: break-word;">
610
+
611
+ <table class="social-content" align="center"
612
+ style="border-collapse: collapse; border-spacing: 0; padding: 0; text-align: left; vertical-align: top;">
613
+ <tbody>
614
+ <tr style="padding: 0; text-align: left; vertical-align: top;">
615
+ <td class="social-content-inner"
616
+ style="-moz-hyphens: auto; -webkit-hyphens: auto; Margin: 0; border-collapse: collapse !important; color: #555555; font-family: Helvetica, Arial, sans-serif; font-size: 15px; font-weight: normal; hyphens: auto; line-height: 26px; margin: 0; padding: 0; text-align: center; vertical-align: top; word-wrap: break-word;">
617
+ <a href="https://plus.google.com/+wpmuorg/" target="_blank"
618
+ class="gplus"
619
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
620
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-gplus.png"
621
+ alt="WPMU DEV on Google+"
622
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
623
+ <a href="https://twitter.com/wpmudev" target="_blank"
624
+ class="twitter"
625
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 4px; padding: 0; text-align: left; text-decoration: none;"><img
626
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-twitter.png"
627
+ alt="WPMU DEV on Twitter"
628
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
629
+ <a href="https://www.facebook.com/wpmudev" target="_blank"
630
+ class="facebook"
631
+ style="Margin: 0; color: #555555; display: inline-block; font-family: Helvetica, Arial, sans-serif; font-weight: normal; line-height: 1.3; margin: 0; margin-right: 0; padding: 0; text-align: left; text-decoration: none;"><img
632
+ src="<?php echo wp_defender()->getPluginUrl() ?>assets/email-images/icon-facebook.png"
633
+ alt="WPMU DEV on Facebook"
634
+ style="-ms-interpolation-mode: bicubic; border: none; clear: both; display: block; max-width: 100%; outline: none; text-decoration: none; width: auto;"></a>
635
+ </td>
636
+ </tr>
637
+ </tbody>
638
+ </table>
639
+ <!-- end social-content -->
640
+
641
+ </td>
642
+ </tr>
643
+ </tbody>
644
+ </table>
645
+ <!-- end top -->
646
+ </td>
647
+ </tr>
648
+ </tbody>
649
+ </table>
650
+ <!-- end main container -->
651
+
652
+ </center>
653
+
654
+ </td>
655
+ </tr>
656
+ </tbody>
657
  </table>
658
  <!-- end body -->
659
  </body>
app/module/scan/view/main.php ADDED
@@ -0,0 +1 @@
 
1
+ <div id="defender"></div>
app/module/scan/view/pro-feature.php CHANGED
@@ -1,6 +1,6 @@
1
  <dialog class="dev-team" id="pro-feature" title="<?php _e( "Defender Pro Features", "defender-security" ) ?>">
2
  <div class="wp-defender">
3
- <p class=""><?php _e( "Heres what youll get by upgrading to Defender Pro:", "defender-security" ) ?></p>
4
  <div class="well well-blank with-cap">
5
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
6
  <strong><?php _e( "Automatic Full File Scans & Notifications", "defender-security" ) ?></strong>
@@ -21,14 +21,14 @@
21
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
22
  <strong><?php _e( "Audit Logging", "defender-security" ) ?></strong>
23
  <p class="sub">
24
- <?php _e( "Track and log each and every event when changes are made to your website and get detailed reports on whats going on behind the scenes, including any hacking attempts on your site.", "defender-security" ) ?>
25
  </p>
26
  </div>
27
  <div class="well well-blank with-cap mline">
28
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
29
  <strong><?php _e( "Tailored Reporting", "defender-security" ) ?></strong>
30
  <p class="sub">
31
- <?php _e( "Get tailored security reports for File Scanning, Audit Logging and IP Lockouts delivered to your inbox so you dont have to worry about checking in.", "defender-security" ) ?>
32
  </p>
33
  </div>
34
  <div class="tc">
1
  <dialog class="dev-team" id="pro-feature" title="<?php _e( "Defender Pro Features", "defender-security" ) ?>">
2
  <div class="wp-defender">
3
+ <p class=""><?php _e( "Here's what you'll get by upgrading to Defender Pro:", "defender-security" ) ?></p>
4
  <div class="well well-blank with-cap">
5
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
6
  <strong><?php _e( "Automatic Full File Scans & Notifications", "defender-security" ) ?></strong>
21
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
22
  <strong><?php _e( "Audit Logging", "defender-security" ) ?></strong>
23
  <p class="sub">
24
+ <?php _e( "Track and log each and every event when changes are made to your website and get detailed reports on what's going on behind the scenes, including any hacking attempts on your site.", "defender-security" ) ?>
25
  </p>
26
  </div>
27
  <div class="well well-blank with-cap mline">
28
  <i class="def-icon icon-tick fill-blue" aria-hidden="true"></i>
29
  <strong><?php _e( "Tailored Reporting", "defender-security" ) ?></strong>
30
  <p class="sub">
31
+ <?php _e( "Get tailored security reports for File Scanning, Audit Logging and IP Lockouts delivered to your inbox so you don't have to worry about checking in.", "defender-security" ) ?>
32
  </p>
33
  </div>
34
  <div class="tc">
app/module/setting.php CHANGED
@@ -7,9 +7,11 @@ namespace WP_Defender\Module;
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Setting\Controller\Main;
 
10
 
11
  class Setting extends Module {
12
  public function __construct() {
13
  new Main();
 
14
  }
15
  }
7
 
8
  use Hammer\Base\Module;
9
  use WP_Defender\Module\Setting\Controller\Main;
10
+ use WP_Defender\Module\Setting\Controller\Rest;
11
 
12
  class Setting extends Module {
13
  public function __construct() {
14
  new Main();
15
+ new Rest();
16
  }
17
  }
app/module/setting/component/backup-settings.php ADDED
@@ -0,0 +1,242 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Setting\Component;
7
+
8
+ use WP_Defender\Behavior\Utils;
9
+ use WP_Defender\Module\Advanced_Tools\Model\Auth_Settings;
10
+ use WP_Defender\Module\Advanced_Tools\Model\Mask_Settings;
11
+ use WP_Defender\Module\Hardener\Model\Settings;
12
+
13
+ class Backup_Settings {
14
+ const KEY = 'defender_last_settings';
15
+
16
+ /**
17
+ * Gather settings from all modules
18
+ * @return array
19
+ */
20
+ public static function gatherData() {
21
+ $security_tweaks = Settings::instance()->exportByKeys( [
22
+ 'notification_repeat',
23
+ 'receipts',
24
+ 'notification'
25
+ ] );
26
+ $scan_model = \WP_Defender\Module\Scan\Model\Settings::instance();
27
+ $scan = $scan_model->exportByKeys( [
28
+ 'scan_core',
29
+ 'scan_vuln',
30
+ 'scan_content',
31
+ 'max_filesize',
32
+ 'report',
33
+ 'always_send',
34
+ 'recipients',
35
+ 'day',
36
+ 'time',
37
+ 'frequency',
38
+ 'notification',
39
+ 'always_send_notification',
40
+ 'recipients_notification',
41
+ 'email_subject',
42
+ 'email_all_ok',
43
+ 'email_has_issue'
44
+ ] );
45
+ if ( class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
46
+ $audit_model = \WP_Defender\Module\Audit\Model\Settings::instance();
47
+ $audit = $audit_model->exportByKeys( [
48
+ 'notification',
49
+ 'receipts',
50
+ 'frequency',
51
+ 'day',
52
+ 'time',
53
+ 'storage_days'
54
+ ] );
55
+ }
56
+ $iplockout_model = \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
57
+ $iplockout = $iplockout_model->exportByKeys(
58
+ [
59
+ 'login_protection',
60
+ 'login_protection_login_attempt',
61
+ 'login_protection_lockout_timeframe',
62
+ 'login_protection_lockout_ban',
63
+ 'login_protection_lockout_duration',
64
+ 'login_protection_lockout_duration_unit',
65
+ 'login_protection_lockout_message',
66
+ 'username_blacklist',
67
+ 'detect_404',
68
+ 'detect_404_threshold',
69
+ 'detect_404_timeframe',
70
+ 'detect_404_lockout_ban',
71
+ 'detect_404_lockout_duration',
72
+ 'detect_404_lockout_duration_unit',
73
+ 'detect_404_lockout_message',
74
+ 'detect_404_blacklist',
75
+ 'detect_404_whitelist',
76
+ 'detect_404_filetypes_blacklist',
77
+ 'detect_404_ignored_filetypes',
78
+ 'detect_404_logged',
79
+ 'ip_blacklist',
80
+ 'ip_whitelist',
81
+ 'country_blacklist',
82
+ 'country_whitelist',
83
+ 'ip_lockout_message',
84
+ 'login_lockout_notification',
85
+ 'ip_lockout_notification',
86
+ 'receipts',
87
+ 'cooldown_enabled',
88
+ 'cooldown_number_lockout',
89
+ 'cooldown_period',
90
+ 'report',
91
+ 'report_receipts',
92
+ 'report_frequency',
93
+ 'report_day',
94
+ 'report_time',
95
+ 'storage_days',
96
+ 'geoIP_db'
97
+ ] );
98
+ $advanced_tools = [
99
+ 'two_factor' => Auth_Settings::instance()->export( [ 'is_conflict' ] ),
100
+ 'mask_login' => Mask_Settings::instance()->export( [ 'otps' ] )
101
+ ];
102
+ $settings = \WP_Defender\Module\Setting\Model\Settings::instance()->export();
103
+ $ret = [
104
+ 'security_tweaks' => $security_tweaks,
105
+ 'scan' => $scan,
106
+ 'iplockout' => $iplockout,
107
+ 'advanced_tools' => $advanced_tools,
108
+ 'settings' => $settings
109
+ ];
110
+ if ( isset( $audit ) ) {
111
+ $ret['audit'] = $audit;
112
+ }
113
+
114
+ return $ret;
115
+ }
116
+
117
+ /**
118
+ * Backup the previous data before we process new versioon
119
+ */
120
+ public static function backupData() {
121
+ $data = self::gatherData();
122
+ $old_backup = get_site_option( self::KEY );
123
+ if ( ! is_array( $old_backup ) ) {
124
+ $old_backup = [];
125
+ }
126
+ if ( count( $old_backup ) > 50 ) {
127
+ //remove the oldest key
128
+ array_shift( $old_backup );
129
+ }
130
+ $version = get_site_option( 'wd_db_version' );
131
+ $old_backup[ $version . '_' . time() ] = $data;
132
+ update_site_option( self::KEY, $old_backup );
133
+ }
134
+
135
+ /**
136
+ * @param $data
137
+ */
138
+ public static function restoreData( $data ) {
139
+ foreach ( $data as $module => $module_data ) {
140
+ $model = self::moduleToModel( $module );
141
+ if ( is_object( $model ) ) {
142
+ foreach ( $module_data as &$value ) {
143
+ if ( ! is_array( $value ) && ! filter_var( $value, FILTER_VALIDATE_BOOLEAN ) ) {
144
+ $value = str_replace( '{nl}', PHP_EOL, $value );
145
+ }
146
+ }
147
+ $model->import( $module_data );
148
+ $model->save();
149
+ }
150
+ }
151
+ }
152
+
153
+ /**
154
+ * @return array
155
+ */
156
+ public static function parseDataForHub() {
157
+ $configs = self::gatherData();
158
+ //we have to move the 2 factor and mask login into parent, make sure we only have a 2 level array
159
+ $configs['two_factor'] = $configs['advanced_tools']['two_factor'];
160
+ $configs['mask_login'] = $configs['advanced_tools']['mask_login'];
161
+ unset( $configs['advanced_tools'] );
162
+ $labels = [];
163
+ foreach ( $configs as $module => $module_data ) {
164
+ $model = self::moduleToModel( $module );
165
+ $labels[ $module ]['name'] = ucfirst( str_replace( '_', ' ', $module ) );
166
+ foreach ( $module_data as $key => $value ) {
167
+ if ( $key == 'geoIP_db' ) {
168
+ continue;
169
+ }
170
+ $labels[ $module ]['value'][ $key ] = [
171
+ 'name' => $model->labels( $key ),
172
+ 'value' => self::parseDataValue( $value, $key )
173
+ ];
174
+ }
175
+ }
176
+
177
+ return [
178
+ 'configs' => $configs,
179
+ 'labels' => $labels,
180
+ ];
181
+ }
182
+
183
+ /**
184
+ * @param $value
185
+ *
186
+ * @return mixed|string|void
187
+ */
188
+ private static function parseDataValue( $value, $key = null ) {
189
+ if ( is_bool( $value ) ) {
190
+ return $value == true ? __( "Yes", "defender-security" ) : __( "No", "defender-security" );
191
+ }
192
+ /**
193
+ * parse recipients
194
+ */
195
+ if ( is_array( $value ) ) {
196
+ $ret = [];
197
+ foreach ( $value as $item ) {
198
+ if ( is_array( $item ) ) {
199
+ //this is recipients
200
+ $ret[] = implode( ': ', $item );
201
+ } else {
202
+ //this should be the case of roles picker
203
+ $ret[] = $item;
204
+ }
205
+ }
206
+
207
+ return implode( '; ', $ret );
208
+ }
209
+ //parse frequency
210
+ if ( $key == 'frequency' ) {
211
+ $value = Utils::instance()->frequencyToText( $value );
212
+ }
213
+
214
+ return $value;
215
+ }
216
+
217
+ /**
218
+ * @param $module
219
+ *
220
+ * @return Auth_Settings|Mask_Settings|\WP_Defender\Module\Audit\Model\Settings|Settings|\WP_Defender\Module\IP_Lockout\Model\Settings|\WP_Defender\Module\Scan\Model\Settings|\WP_Defender\Module\Setting\Model\Settings
221
+ */
222
+ private static function moduleToModel( $module ) {
223
+ switch ( $module ) {
224
+ case 'security_tweaks':
225
+ return Settings::instance();
226
+ case 'scan':
227
+ return \WP_Defender\Module\Scan\Model\Settings::instance();
228
+ case 'audit':
229
+ return \WP_Defender\Module\Audit\Model\Settings::instance();
230
+ case 'iplockout':
231
+ return \WP_Defender\Module\IP_Lockout\Model\Settings::instance();
232
+ case 'settings':
233
+ return \WP_Defender\Module\Setting\Model\Settings::instance();
234
+ case 'two_factor':
235
+ return Auth_Settings::instance();
236
+ case 'mask_login':
237
+ return Mask_Settings::instance();
238
+ default:
239
+ break;
240
+ }
241
+ }
242
+ }
app/module/setting/controller/main.php CHANGED
@@ -8,34 +8,35 @@ namespace WP_Defender\Module\Setting\Controller;
8
  use Hammer\Helper\HTTP_Helper;
9
  use WP_Defender\Behavior\Utils;
10
  use WP_Defender\Controller;
 
11
  use WP_Defender\Module\Setting\Model\Settings;
12
 
13
  class Main extends Controller {
14
  protected $slug = 'wdf-setting';
15
- public $layout = 'layout';
16
 
17
  /**
18
  * @return array
19
  */
20
  public function behaviors() {
21
- return array(
22
- 'utils' => '\WP_Defender\Behavior\Utils'
 
 
23
  );
 
 
24
  }
25
 
26
  public function __construct() {
27
- if ( $this->is_network_activate( wp_defender()->plugin_slug ) ) {
28
- $this->add_action( 'network_admin_menu', 'adminMenu' );
29
  } else {
30
- $this->add_action( 'admin_menu', 'adminMenu' );
31
  }
32
 
33
  if ( $this->isInPage() || $this->isDashboard() ) {
34
- $this->add_action( 'defender_enqueue_assets', 'scripts', 12 );
35
  }
36
-
37
- $this->add_ajax_action( 'saveSettings', 'saveSettings' );
38
- $this->add_ajax_action( 'wdResetSettings', 'resetSettings' );
39
  }
40
 
41
  /**
@@ -49,123 +50,63 @@ class Main extends Controller {
49
  ) );
50
  }
51
 
52
- /**
53
- * a simple router
54
- */
55
  public function actionIndex() {
56
- $view = HTTP_Helper::retrieve_get( 'view' );
57
- switch ( $view ) {
58
- case 'general':
59
- default:
60
- $this->viewGeneral();
61
- break;
62
- case 'data':
63
- $this->viewData();
64
- break;
65
- case 'accessibility':
66
- $this->viewAccessibility();
67
- break;
68
- }
69
- }
70
-
71
- protected function viewGeneral() {
72
- $this->render( 'general', array(
73
- 'settings' => Settings::instance()
74
- ) );
75
- }
76
-
77
- protected function viewData() {
78
- $this->render( 'data', array(
79
- 'settings' => Settings::instance()
80
- ) );
81
- }
82
-
83
- protected function viewAccessibility() {
84
- $this->render( 'accessibility', array(
85
- 'settings' => Settings::instance()
86
- ) );
87
  }
88
 
89
  /**
90
  * Enqueue scripts & styles
91
  */
92
  public function scripts() {
93
- if ( $this->isInPage() || $this->isDashboard() ) {
94
- wp_enqueue_script( 'wpmudev-sui' );
95
- wp_enqueue_style( 'wpmudev-sui' );
96
 
97
  wp_enqueue_script( 'defender' );
98
- wp_enqueue_style( 'defender' );
99
- wp_enqueue_script( 'wd-settings', wp_defender()->getPluginUrl() . 'app/module/setting/js/script.js' );
 
 
 
 
 
 
 
 
100
  }
101
  }
102
 
103
  /**
104
- * Saving settings in admin area
 
105
  */
106
- public function saveSettings() {
107
  if ( ! $this->checkPermission() ) {
108
- return;
109
  }
110
-
111
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'saveSettings' ) ) {
112
- return;
113
- }
114
-
115
- $data = $_POST;
116
- $setting = Settings::instance();
117
- $setting->import( $data );
118
- $setting->save();
119
-
120
- $res = array(
121
- 'message' => __( "Your settings have been updated.", "defender-security" )
122
- );
123
- $res['reload'] = 1;
124
- Utils::instance()->submitStatsToDev();
125
- wp_send_json_success( $res );
126
- }
127
-
128
- public function resetSettings() {
129
- if ( ! $this->checkPermission() ) {
130
- return;
131
- }
132
-
133
- if ( ! wp_verify_nonce( HTTP_Helper::retrieve_post( '_wpnonce' ), 'wdResetSettings' ) ) {
134
- return;
135
- }
136
-
137
- $tweakFixed = \WP_Defender\Module\Hardener\Model\Settings::instance()->getFixed();
138
-
139
- foreach ( $tweakFixed as $rule ) {
140
- $rule->getService()->revert();
141
- }
142
-
143
- $cache = \Hammer\Helper\WP_Helper::getCache();
144
- $cache->delete( 'isActivated' );
145
- $cache->delete( 'wdf_isActivated' );
146
- $cache->delete( 'wdfchecksum' );
147
- $cache->delete( 'cleanchecksum' );
148
-
149
- \WP_Defender\Module\Scan\Model\Settings::instance()->delete();
150
- if ( class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
151
- \WP_Defender\Module\Audit\Model\Settings::instance()->delete();
152
- }
153
- \WP_Defender\Module\Hardener\Model\Settings::instance()->delete();
154
- \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->delete();
155
- \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance()->delete();
156
- \WP_Defender\Module\Advanced_Tools\Model\Mask_Settings::instance()->delete();
157
- Settings::instance()->delete();
158
- //clear old stuff
159
- delete_site_option( 'wp_defender' );
160
- delete_option( 'wp_defender' );
161
- delete_option( 'wd_db_version' );
162
- delete_site_option( 'wd_db_version' );
163
-
164
- $res = array(
165
- 'message' => __( "Your settings have been reset.", "defender-security" )
166
- );
167
-
168
- Utils::instance()->submitStatsToDev();
169
- wp_send_json_success( $res );
170
  }
171
  }
8
  use Hammer\Helper\HTTP_Helper;
9
  use WP_Defender\Behavior\Utils;
10
  use WP_Defender\Controller;
11
+ use WP_Defender\Module\Setting;
12
  use WP_Defender\Module\Setting\Model\Settings;
13
 
14
  class Main extends Controller {
15
  protected $slug = 'wdf-setting';
 
16
 
17
  /**
18
  * @return array
19
  */
20
  public function behaviors() {
21
+ $behaviors = array(
22
+ 'utils' => '\WP_Defender\Behavior\Utils',
23
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
24
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
25
  );
26
+
27
+ return $behaviors;
28
  }
29
 
30
  public function __construct() {
31
+ if ( $this->isNetworkActivate( wp_defender()->plugin_slug ) ) {
32
+ $this->addAction( 'network_admin_menu', 'adminMenu' );
33
  } else {
34
+ $this->addAction( 'admin_menu', 'adminMenu' );
35
  }
36
 
37
  if ( $this->isInPage() || $this->isDashboard() ) {
38
+ $this->addAction( 'defender_enqueue_assets', 'scripts', 12 );
39
  }
 
 
 
40
  }
41
 
42
  /**
50
  ) );
51
  }
52
 
 
 
 
53
  public function actionIndex() {
54
+ $this->render( 'main' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
56
 
57
  /**
58
  * Enqueue scripts & styles
59
  */
60
  public function scripts() {
61
+ if ( $this->isInPage() ) {
62
+ wp_enqueue_style( 'defender' );
 
63
 
64
  wp_enqueue_script( 'defender' );
65
+ wp_register_script( 'defender-settings', wp_defender()->getPluginUrl() . 'assets/app/settings.js', [
66
+ 'vue',
67
+ 'defender',
68
+ 'wp-i18n'
69
+ ], wp_defender()->version, true );
70
+ wp_localize_script( 'defender-settings', 'wdSettings', $this->scriptsData() );
71
+ Utils::instance()->createTranslationJson( 'defender-settings' );
72
+ wp_set_script_translations( 'defender-settings', 'wpdef', wp_defender()->getPluginPath() . 'languages' );
73
+ wp_enqueue_script( 'defender-settings' );
74
+ wp_enqueue_script( 'wpmudev-sui' );
75
  }
76
  }
77
 
78
  /**
79
+ * Export the data we need for front-end
80
+ * @return array
81
  */
82
+ public function scriptsData() {
83
  if ( ! $this->checkPermission() ) {
84
+ return [];
85
  }
86
+ $settings = Settings::instance();
87
+
88
+ return [
89
+ 'model' => [
90
+ 'general' => $settings->exportByKeys( [
91
+ 'translate',
92
+ 'usage_tracking',
93
+ ] ),
94
+ 'data' => $settings->exportByKeys( [
95
+ 'uninstall_settings',
96
+ 'uninstall_data'
97
+ ] ),
98
+ 'accessibility' => $settings->exportByKeys( [
99
+ 'high_contrast_mode'
100
+ ] )
101
+ ],
102
+ 'nonces' => [
103
+ 'updateSettings' => wp_create_nonce( 'updateSettings' ),
104
+ 'resetSettings' => wp_create_nonce( 'resetSettings' )
105
+ ],
106
+ 'endpoints' => $this->getAllAvailableEndpoints( Setting::getClassName() ),
107
+ 'misc' => [
108
+ 'setting_url' => network_admin_url( is_multisite() ? 'settings.php' : 'options-general.php' )
109
+ ]
110
+ ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  }
112
  }
app/module/setting/controller/rest.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Author: Hoang Ngo
4
+ */
5
+
6
+ namespace WP_Defender\Module\Setting\Controller;
7
+
8
+
9
+ use Hammer\Helper\HTTP_Helper;
10
+ use WP_Defender\Behavior\Utils;
11
+ use WP_Defender\Controller;
12
+ use WP_Defender\Module\Setting;
13
+
14
+ class Rest extends Controller {
15
+ public function __construct() {
16
+ $namespace = 'wp-defender/v1';
17
+ $namespace .= '/settings';
18
+ $routes = [
19
+ $namespace . '/updateSettings' => 'updateSettings',
20
+ $namespace . '/resetSettings' => 'resetSettings'
21
+
22
+ ];
23
+ $this->registerEndpoints( $routes, Setting::getClassName() );
24
+ }
25
+
26
+ public function resetSettings() {
27
+ if ( ! $this->checkPermission() ) {
28
+ return;
29
+ }
30
+
31
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'resetSettings' ) ) {
32
+ return;
33
+ }
34
+
35
+ $tweakFixed = \WP_Defender\Module\Hardener\Model\Settings::instance()->getFixed();
36
+
37
+ foreach ( $tweakFixed as $rule ) {
38
+ $rule->getService()->revert();
39
+ }
40
+
41
+ $cache = \Hammer\Helper\WP_Helper::getCache();
42
+ $cache->delete( 'isActivated' );
43
+ $cache->delete( 'wdf_isActivated' );
44
+ $cache->delete( 'wdfchecksum' );
45
+ $cache->delete( 'cleanchecksum' );
46
+
47
+ \WP_Defender\Module\Scan\Model\Settings::instance()->delete();
48
+ if ( class_exists( '\WP_Defender\Module\Audit\Model\Settings' ) ) {
49
+ \WP_Defender\Module\Audit\Model\Settings::instance()->delete();
50
+ }
51
+ \WP_Defender\Module\Hardener\Model\Settings::instance()->delete();
52
+ \WP_Defender\Module\IP_Lockout\Model\Settings::instance()->delete();
53
+ \WP_Defender\Module\Advanced_Tools\Model\Auth_Settings::instance()->delete();
54
+ \WP_Defender\Module\Advanced_Tools\Model\Mask_Settings::instance()->delete();
55
+ Setting\Model\Settings::instance()->delete();
56
+ //clear old stuff
57
+ delete_site_option( 'wp_defender' );
58
+ delete_option( 'wp_defender' );
59
+ delete_option( 'wd_db_version' );
60
+ delete_site_option( 'wd_db_version' );
61
+
62
+ delete_site_transient( 'wp_defender_free_is_activated' );
63
+ delete_site_transient( 'wp_defender_is_activated' );
64
+ delete_transient( 'wp_defender_free_is_activated' );
65
+ delete_transient( 'wp_defender_is_activated' );
66
+
67
+ delete_site_option( 'wp_defender_free_is_activated' );
68
+ delete_site_option( 'wp_defender_is_activated' );
69
+ delete_option( 'wp_defender_free_is_activated' );
70
+ delete_option( 'wp_defender_is_activated' );
71
+
72
+ $res = array(
73
+ 'message' => __( "Your settings have been reset.", "defender-security" )
74
+ );
75
+
76
+ Utils::instance()->submitStatsToDev();
77
+ wp_send_json_success( $res );
78
+ }
79
+
80
+ public function updateSettings() {
81
+ if ( ! $this->checkPermission() ) {
82
+ return;
83
+ }
84
+
85
+ if ( ! wp_verify_nonce( HTTP_Helper::retrieveGet( '_wpnonce' ), 'updateSettings' ) ) {
86
+ return;
87
+ }
88
+ $settings = Setting\Model\Settings::instance();
89
+ $data = stripslashes( $_POST['data'] );
90
+ $data = json_decode( $data, true );
91
+ $settings->import( $data );
92
+ $settings->save();
93
+ $res = array(
94
+ 'message' => __( "Your settings have been updated.", "defender-security" )
95
+ );
96
+
97
+ $this->submitStatsToDev();
98
+ wp_send_json_success( $res );
99
+ }
100
+
101
+
102
+ /**
103
+ * @return array
104
+ */
105
+ public function behaviors() {
106
+ $behaviors = array(
107
+ 'utils' => '\WP_Defender\Behavior\Utils',
108
+ 'endpoints' => '\WP_Defender\Behavior\Endpoint',
109
+ 'wpmudev' => '\WP_Defender\Behavior\WPMUDEV'
110
+ );
111
+
112
+ return $behaviors;
113
+ }
114
+ }
app/module/setting/model/settings.php CHANGED
@@ -9,22 +9,25 @@ use Hammer\Helper\WP_Helper;
9
 
10
  class Settings extends \Hammer\WP\Settings {
11
  private static $_instance;
12
-
13
  public $translate;
14
- public $usage_tracking = 0;
15
  public $uninstall_data = 'remove';
16
  public $uninstall_settings = 'reset';
17
- public $high_contrast_mode = 0;
18
-
19
  public function behaviors() {
20
  return array(
21
  'utils' => '\WP_Defender\Behavior\Utils'
22
  );
23
  }
24
-
25
  public function __construct( $id, $is_multi ) {
26
- $site_locale = get_locale();
27
-
 
 
 
28
  if ( 'en_US' === $site_locale ) {
29
  $site_language = 'English';
30
  } else {
@@ -33,10 +36,8 @@ class Settings extends \Hammer\WP\Settings {
33
  $site_language = $translations[ $site_locale ]['native_name'];
34
  }
35
  $this->translate = $site_language;
36
-
37
- parent::__construct( $id, $is_multi );
38
  }
39
-
40
  /**
41
  * @return Settings
42
  */
@@ -45,7 +46,23 @@ class Settings extends \Hammer\WP\Settings {
45
  $class = new Settings( 'wd_main_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
46
  self::$_instance = $class;
47
  }
48
-
49
  return self::$_instance;
50
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  }
9
 
10
  class Settings extends \Hammer\WP\Settings {
11
  private static $_instance;
12
+
13
  public $translate;
14
+ public $usage_tracking = false;
15
  public $uninstall_data = 'remove';
16
  public $uninstall_settings = 'reset';
17
+ public $high_contrast_mode = false;
18
+
19
  public function behaviors() {
20
  return array(
21
  'utils' => '\WP_Defender\Behavior\Utils'
22
  );
23
  }
24
+
25
  public function __construct( $id, $is_multi ) {
26
+ parent::__construct( $id, $is_multi );
27
+ $this->high_contrast_mode = ! ! $this->high_contrast_mode;
28
+ $this->usage_tracking = ! ! $this->usage_tracking;
29
+ $site_locale = get_locale();
30
+
31
  if ( 'en_US' === $site_locale ) {
32
  $site_language = 'English';
33
  } else {
36
  $site_language = $translations[ $site_locale ]['native_name'];
37
  }
38
  $this->translate = $site_language;
 
 
39
  }
40
+
41
  /**
42
  * @return Settings
43
  */
46
  $class = new Settings( 'wd_main_settings', WP_Helper::is_network_activate( wp_defender()->plugin_slug ) );
47
  self::$_instance = $class;
48
  }
49
+
50
  return self::$_instance;
51
  }
52
+
53
+ public function labels( $key = null ) {
54
+ $labels = [
55
+ 'translate' => __( 'Translations', "defender-security" ),
56
+ 'usage_tracking' => __( "Usage Tracking", "defender-security" ),
57
+ 'uninstall_data' => __( 'Uninstall data', "defender-security" ),
58
+ 'uninstall_settings' => __( "Uninstall Settings", "defender-security" ),
59
+ 'high_contrast_mode' => __( "High Contrast Mode", "defender-security" ),
60
+ ];
61
+
62
+ if ( $key != null ) {
63
+ return isset( $labels[ $key ] ) ? $labels[ $key ] : null;
64
+ }
65
+
66
+ return $labels;
67
+ }
68
  }
app/module/setting/view/main.php ADDED
@@ -0,0 +1 @@
 
1
+ <div id="defender"></div>
app/view/debug.php CHANGED
@@ -1,19 +1,80 @@
1
- <div class="wrap">
2
- <p>
3
- Content: <?php echo count( \WP_Defender\Module\Scan\Component\Scan_Api::getContentFiles() ) ?>
4
- </p>
5
- <p>
6
- Core: <?php echo count( \WP_Defender\Module\Scan\Component\Scan_Api::getCoreFiles() ) ?>
7
- </p>
8
- <p>
9
- Progress: <?php echo \WP_Defender\Module\Scan\Component\Scan_Api::getScanProgress() ?>
10
- </p>
11
- <p>
12
- Time: <?php
13
- $model = \WP_Defender\Module\Scan\Component\Scan_Api::getLastScan();
14
- if ( is_object( $model ) ) {
15
- echo $model->dateFinished . '-' . $model->dateStart;
16
- }
17
- ?>
18
- </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </div>
1
+ <div class="sui-wrap">
2
+ <div class="sui-header">
3
+ <h1 class="sui-header-title">Debug</h1>
4
+ </div>
5
+ <div class="sui-box">
6
+ <div class="sui-box-header">
7
+ <h3 class="sui-box-title">
8
+ Scanning
9
+ </h3>
10
+ </div>
11
+ <div class="sui-box-body">
12
+ <p>
13
+ Content: <?php echo count( $content ) ?>
14
+ </p>
15
+ <p>
16
+ Core: <?php echo count( $core ) ?>
17
+ </p>
18
+ <p>
19
+ Progress: <?php echo $progress ?>
20
+ </p>
21
+ <p>
22
+ Time: <?php
23
+ ?>
24
+ </p>
25
+ <pre style="max-height: 300px;overflow-y: scroll"><?php echo \WP_Defender\Behavior\Utils::instance()->read_log( 'scan' ) ?></pre>
26
+ </div>
27
+ </div>
28
+ <div class="sui-box">
29
+ <div class="sui-box-header">
30
+ <h3 class="sui-box-title">
31
+ Tweaks
32
+ </h3>
33
+ <div class="sui-actions-left">
34
+ <form method="post">
35
+ <?php wp_nonce_field( 'flush_tweaks_cache','_defnonce' ) ?>
36
+ <button type="submit" class="sui-button">Flush cache</button>
37
+ </form>
38
+ </div>
39
+ </div>
40
+ <div class="sui-box-body">
41
+ <?php
42
+ $settings = \WP_Defender\Module\Hardener\Model\Settings::instance();
43
+ $cached = $settings->getDValues( 'head_requests' );
44
+ ?>
45
+ <table class="sui-table">
46
+ <thead>
47
+ <tr>
48
+ <th>URL</th>
49
+ <th>TTL</th>
50
+ <th>Data</th>
51
+ </tr>
52
+ </thead>
53
+ <tbody>
54
+ <?php foreach ( $cached as $url => $item ): ?>
55
+ <tr>
56
+ <td>
57
+ <?php echo $url ?>
58
+ </td>
59
+ <td>
60
+ <?php echo $item['ttl'] . ' - ' . \WP_Defender\Behavior\Utils::instance()->formatDateTime( $item['ttl'] ) ?>
61
+ </td>
62
+ <td>
63
+ <?php echo print_r( $item['data'], true ) ?>
64
+ </td>
65
+ </tr>
66
+ <?php endforeach; ?>
67
+ </tbody>
68
+ </table>
69
+ <pre style="max-height: 300px;overflow-y: scroll"><?php echo \WP_Defender\Behavior\Utils::instance()->read_log( 'tweaks' ) ?></pre>
70
+ </div>
71
+ </div>
72
+ <div class="sui-box">
73
+ <div class="sui-box-header">
74
+ <h3 class="sui-box-title">Export</h3>
75
+ </div>
76
+ <div class="sui-box-body">
77
+ <pre style="max-height: 300px;overflow-y: scroll"><?php echo \WP_Defender\Behavior\Utils::instance()->read_log( 'settings' ) ?></pre>
78
+ </div>
79
+ </div>
80
  </div>
app/view/main.php ADDED
@@ -0,0 +1 @@
 
1
+ <div id="defender"></div>
assets/app/advanced-tools.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){var e=window.webpackHotUpdate;window.webpackHotUpdate=function(t,s){!function(t,e){if(!y[t]||!w[t])return;for(var s in w[t]=!1,e)Object.prototype.hasOwnProperty.call(e,s)&&(m[s]=e[s]);0==--h&&0===g&&A()}(t,s),e&&e(t,s)};var s,i=!0,a="cf1fa9a26c8d0e3aa9bd",n=1e4,r={},o=[],l=[];function c(t){var e=S[t];if(!e)return E;var i=function(i){return e.hot.active?(S[i]?-1===S[i].parents.indexOf(t)&&S[i].parents.push(t):(o=[t],s=i),-1===e.children.indexOf(i)&&e.children.push(i)):(console.warn("[HMR] unexpected require("+i+") from disposed module "+t),o=[]),E(i)},a=function(t){return{configurable:!0,enumerable:!0,get:function(){return E[t]},set:function(e){E[t]=e}}};for(var n in E)Object.prototype.hasOwnProperty.call(E,n)&&"e"!==n&&"t"!==n&&Object.defineProperty(i,n,a(n));return i.e=function(t){return"ready"===f&&_("prepare"),g++,E.e(t).then(e,(function(t){throw e(),t}));function e(){g--,"prepare"===f&&(b[t]||k(t),0===g&&0===h&&A())}},i.t=function(t,e){return 1&e&&(t=i(t)),E.t(t,-2&e)},i}function u(t){var e={_acceptedDependencies:{},_declinedDependencies:{},_selfAccepted:!1,_selfDeclined:!1,_disposeHandlers:[],_main:s!==t,active:!0,accept:function(t,s){if(void 0===t)e._selfAccepted=!0;else if("function"==typeof t)e._selfAccepted=t;else if("object"==typeof t)for(var i=0;i<t.length;i++)e._acceptedDependencies[t[i]]=s||function(){};else e._acceptedDependencies[t]=s||function(){}},decline:function(t){if(void 0===t)e._selfDeclined=!0;else if("object"==typeof t)for(var s=0;s<t.length;s++)e._declinedDependencies[t[s]]=!0;else e._declinedDependencies[t]=!0},dispose:function(t){e._disposeHandlers.push(t)},addDisposeHandler:function(t){e._disposeHandlers.push(t)},removeDisposeHandler:function(t){var s=e._disposeHandlers.indexOf(t);s>=0&&e._disposeHandlers.splice(s,1)},check:x,apply:j,status:function(t){if(!t)return f;d.push(t)},addStatusHandler:function(t){d.push(t)},removeStatusHandler:function(t){var e=d.indexOf(t);e>=0&&d.splice(e,1)},data:r[t]};return s=void 0,e}var d=[],f="idle";function _(t){f=t;for(var e=0;e<d.length;e++)d[e].call(null,t)}var p,m,v,h=0,g=0,b={},w={},y={};function C(t){return+t+""===t?+t:t}function x(t){if("idle"!==f)throw new Error("check() is only allowed in idle status");return i=t,_("check"),(e=n,e=e||1e4,new Promise((function(t,s){if("undefined"==typeof XMLHttpRequest)return s(new Error("No browser support"));try{var i=new XMLHttpRequest,n=E.p+""+a+".hot-update.json";i.open("GET",n,!0),i.timeout=e,i.send(null)}catch(t){return s(t)}i.onreadystatechange=function(){if(4===i.readyState)if(0===i.status)s(new Error("Manifest request to "+n+" timed out."));else if(404===i.status)t();else if(200!==i.status&&304!==i.status)s(new Error("Manifest request to "+n+" failed."));else{try{var e=JSON.parse(i.responseText)}catch(t){return void s(t)}t(e)}}}))).then((function(t){if(!t)return _("idle"),null;w={},b={},y=t.c,v=t.h,_("prepare");var e=new Promise((function(t,e){p={resolve:t,reject:e}}));m={};return k(0),"prepare"===f&&0===g&&0===h&&A(),e}));var e}function k(t){y[t]?(w[t]=!0,h++,function(t){var e=document.createElement("script");e.charset="utf-8",e.src=E.p+""+t+"."+a+".hot-update.js",document.head.appendChild(e)}(t)):b[t]=!0}function A(){_("ready");var t=p;if(p=null,t)if(i)Promise.resolve().then((function(){return j(i)})).then((function(e){t.resolve(e)}),(function(e){t.reject(e)}));else{var e=[];for(var s in m)Object.prototype.hasOwnProperty.call(m,s)&&e.push(C(s));t.resolve(e)}}function j(e){if("ready"!==f)throw new Error("apply() is only allowed in ready status");var s,i,n,l,c;function u(t){for(var e=[t],s={},i=e.map((function(t){return{chain:[t],id:t}}));i.length>0;){var a=i.pop(),n=a.id,r=a.chain;if((l=S[n])&&!l.hot._selfAccepted){if(l.hot._selfDeclined)return{type:"self-declined",chain:r,moduleId:n};if(l.hot._main)return{type:"unaccepted",chain:r,moduleId:n};for(var o=0;o<l.parents.length;o++){var c=l.parents[o],u=S[c];if(u){if(u.hot._declinedDependencies[n])return{type:"declined",chain:r.concat([c]),moduleId:n,parentId:c};-1===e.indexOf(c)&&(u.hot._acceptedDependencies[n]?(s[c]||(s[c]=[]),d(s[c],[n])):(delete s[c],e.push(c),i.push({chain:r.concat([c]),id:c})))}}}}return{type:"accepted",moduleId:t,outdatedModules:e,outdatedDependencies:s}}function d(t,e){for(var s=0;s<e.length;s++){var i=e[s];-1===t.indexOf(i)&&t.push(i)}}e=e||{};var p={},h=[],g={},b=function(){console.warn("[HMR] unexpected require("+x.moduleId+") to disposed module")};for(var w in m)if(Object.prototype.hasOwnProperty.call(m,w)){var x;c=C(w);var k=!1,A=!1,j=!1,T="";switch((x=m[w]?u(c):{type:"disposed",moduleId:w}).chain&&(T="\nUpdate propagation: "+x.chain.join(" -> ")),x.type){case"self-declined":e.onDeclined&&e.onDeclined(x),e.ignoreDeclined||(k=new Error("Aborted because of self decline: "+x.moduleId+T));break;case"declined":e.onDeclined&&e.onDeclined(x),e.ignoreDeclined||(k=new Error("Aborted because of declined dependency: "+x.moduleId+" in "+x.parentId+T));break;case"unaccepted":e.onUnaccepted&&e.onUnaccepted(x),e.ignoreUnaccepted||(k=new Error("Aborted because "+c+" is not accepted"+T));break;case"accepted":e.onAccepted&&e.onAccepted(x),A=!0;break;case"disposed":e.onDisposed&&e.onDisposed(x),j=!0;break;default:throw new Error("Unexception type "+x.type)}if(k)return _("abort"),Promise.reject(k);if(A)for(c in g[c]=m[c],d(h,x.outdatedModules),x.outdatedDependencies)Object.prototype.hasOwnProperty.call(x.outdatedDependencies,c)&&(p[c]||(p[c]=[]),d(p[c],x.outdatedDependencies[c]));j&&(d(h,[x.moduleId]),g[c]=b)}var I,O=[];for(i=0;i<h.length;i++)c=h[i],S[c]&&S[c].hot._selfAccepted&&g[c]!==b&&O.push({module:c,errorHandler:S[c].hot._selfAccepted});_("dispose"),Object.keys(y).forEach((function(t){!1===y[t]&&function(t){delete installedChunks[t]}(t)}));for(var P,$,D=h.slice();D.length>0;)if(c=D.pop(),l=S[c]){var U={},R=l.hot._disposeHandlers;for(n=0;n<R.length;n++)(s=R[n])(U);for(r[c]=U,l.hot.active=!1,delete S[c],delete p[c],n=0;n<l.children.length;n++){var N=S[l.children[n]];N&&((I=N.parents.indexOf(c))>=0&&N.parents.splice(I,1))}}for(c in p)if(Object.prototype.hasOwnProperty.call(p,c)&&(l=S[c]))for($=p[c],n=0;n<$.length;n++)P=$[n],(I=l.children.indexOf(P))>=0&&l.children.splice(I,1);for(c in _("apply"),a=v,g)Object.prototype.hasOwnProperty.call(g,c)&&(t[c]=g[c]);var L=null;for(c in p)if(Object.prototype.hasOwnProperty.call(p,c)&&(l=S[c])){$=p[c];var H=[];for(i=0;i<$.length;i++)if(P=$[i],s=l.hot._acceptedDependencies[P]){if(-1!==H.indexOf(s))continue;H.push(s)}for(i=0;i<H.length;i++){s=H[i];try{s($)}catch(t){e.onErrored&&e.onErrored({type:"accept-errored",moduleId:c,dependencyId:$[i],error:t}),e.ignoreErrored||L||(L=t)}}}for(i=0;i<O.length;i++){var q=O[i];c=q.module,o=[c];try{E(c)}catch(t){if("function"==typeof q.errorHandler)try{q.errorHandler(t)}catch(s){e.onErrored&&e.onErrored({type:"self-accept-error-handler-errored",moduleId:c,error:s,originalError:t}),e.ignoreErrored||L||(L=s),L||(L=t)}else e.onErrored&&e.onErrored({type:"self-accept-errored",moduleId:c,error:t}),e.ignoreErrored||L||(L=t)}}return L?(_("fail"),Promise.reject(L)):(_("idle"),new Promise((function(t){t(h)})))}var S={};function E(e){if(S[e])return S[e].exports;var s=S[e]={i:e,l:!1,exports:{},hot:u(e),parents:(l=o,o=[],l),children:[]};return t[e].call(s.exports,s,s.exports,c(e)),s.l=!0,s.exports}E.m=t,E.c=S,E.d=function(t,e,s){E.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},E.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},E.t=function(t,e){if(1&e&&(t=E(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(E.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)E.d(s,i,function(e){return t[e]}.bind(null,i));return s},E.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return E.d(e,"a",e),e},E.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},E.p="",E.h=function(){return a},c("./src/advanced-tools.js")(E.s="./src/advanced-tools.js")}({"./node_modules/cssfilter/lib/css.js":function(t,e,s){var i=s("./node_modules/cssfilter/lib/default.js"),a=s("./node_modules/cssfilter/lib/parser.js");s("./node_modules/cssfilter/lib/util.js");function n(t){return null==t}function r(t){(t=function(t){var e={};for(var s in t)e[s]=t[s];return e}(t||{})).whiteList=t.whiteList||i.whiteList,t.onAttr=t.onAttr||i.onAttr,t.onIgnoreAttr=t.onIgnoreAttr||i.onIgnoreAttr,t.safeAttrValue=t.safeAttrValue||i.safeAttrValue,this.options=t}r.prototype.process=function(t){if(!(t=(t=t||"").toString()))return"";var e=this.options,s=e.whiteList,i=e.onAttr,r=e.onIgnoreAttr,o=e.safeAttrValue;return a(t,(function(t,e,a,l,c){var u=s[a],d=!1;if(!0===u?d=u:"function"==typeof u?d=u(l):u instanceof RegExp&&(d=u.test(l)),!0!==d&&(d=!1),l=o(a,l)){var f,_={position:e,sourcePosition:t,source:c,isWhite:d};return d?n(f=i(a,l,_))?a+":"+l:f:n(f=r(a,l,_))?void 0:f}}))},t.exports=r},"./node_modules/cssfilter/lib/default.js":function(t,e){function s(){var t={"align-content":!1,"align-items":!1,"align-self":!1,"alignment-adjust":!1,"alignment-baseline":!1,all:!1,"anchor-point":!1,animation:!1,"animation-delay":!1,"animation-direction":!1,"animation-duration":!1,"animation-fill-mode":!1,"animation-iteration-count":!1,"animation-name":!1,"animation-play-state":!1,"animation-timing-function":!1,azimuth:!1,"backface-visibility":!1,background:!0,"background-attachment":!0,"background-clip":!0,"background-color":!0,"background-image":!0,"background-origin":!0,"background-position":!0,"background-repeat":!0,"background-size":!0,"baseline-shift":!1,binding:!1,bleed:!1,"bookmark-label":!1,"bookmark-level":!1,"bookmark-state":!1,border:!0,"border-bottom":!0,"border-bottom-color":!0,"border-bottom-left-radius":!0,"border-bottom-right-radius":!0,"border-bottom-style":!0,"border-bottom-width":!0,"border-collapse":!0,"border-color":!0,"border-image":!0,"border-image-outset":!0,"border-image-repeat":!0,"border-image-slice":!0,"border-image-source":!0,"border-image-width":!0,"border-left":!0,"border-left-color":!0,"border-left-style":!0,"border-left-width":!0,"border-radius":!0,"border-right":!0,"border-right-color":!0,"border-right-style":!0,"border-right-width":!0,"border-spacing":!0,"border-style":!0,"border-top":!0,"border-top-color":!0,"border-top-left-radius":!0,"border-top-right-radius":!0,"border-top-style":!0,"border-top-width":!0,"border-width":!0,bottom:!1,"box-decoration-break":!0,"box-shadow":!0,"box-sizing":!0,"box-snap":!0,"box-suppress":!0,"break-after":!0,"break-before":!0,"break-inside":!0,"caption-side":!1,chains:!1,clear:!0,clip:!1,"clip-path":!1,"clip-rule":!1,color:!0,"color-interpolation-filters":!0,"column-count":!1,"column-fill":!1,"column-gap":!1,"column-rule":!1,"column-rule-color":!1,"column-rule-style":!1,"column-rule-width":!1,"column-span":!1,"column-width":!1,columns:!1,contain:!1,content:!1,"counter-increment":!1,"counter-reset":!1,"counter-set":!1,crop:!1,cue:!1,"cue-after":!1,"cue-before":!1,cursor:!1,direction:!1,display:!0,"display-inside":!0,"display-list":!0,"display-outside":!0,"dominant-baseline":!1,elevation:!1,"empty-cells":!1,filter:!1,flex:!1,"flex-basis":!1,"flex-direction":!1,"flex-flow":!1,"flex-grow":!1,"flex-shrink":!1,"flex-wrap":!1,float:!1,"float-offset":!1,"flood-color":!1,"flood-opacity":!1,"flow-from":!1,"flow-into":!1,font:!0,"font-family":!0,"font-feature-settings":!0,"font-kerning":!0,"font-language-override":!0,"font-size":!0,"font-size-adjust":!0,"font-stretch":!0,"font-style":!0,"font-synthesis":!0,"font-variant":!0,"font-variant-alternates":!0,"font-variant-caps":!0,"font-variant-east-asian":!0,"font-variant-ligatures":!0,"font-variant-numeric":!0,"font-variant-position":!0,"font-weight":!0,grid:!1,"grid-area":!1,"grid-auto-columns":!1,"grid-auto-flow":!1,"grid-auto-rows":!1,"grid-column":!1,"grid-column-end":!1,"grid-column-start":!1,"grid-row":!1,"grid-row-end":!1,"grid-row-start":!1,"grid-template":!1,"grid-template-areas":!1,"grid-template-columns":!1,"grid-template-rows":!1,"hanging-punctuation":!1,height:!0,hyphens:!1,icon:!1,"image-orientation":!1,"image-resolution":!1,"ime-mode":!1,"initial-letters":!1,"inline-box-align":!1,"justify-content":!1,"justify-items":!1,"justify-self":!1,left:!1,"letter-spacing":!0,"lighting-color":!0,"line-box-contain":!1,"line-break":!1,"line-grid":!1,"line-height":!1,"line-snap":!1,"line-stacking":!1,"line-stacking-ruby":!1,"line-stacking-shift":!1,"line-stacking-strategy":!1,"list-style":!0,"list-style-image":!0,"list-style-position":!0,"list-style-type":!0,margin:!0,"margin-bottom":!0,"margin-left":!0,"margin-right":!0,"margin-top":!0,"marker-offset":!1,"marker-side":!1,marks:!1,mask:!1,"mask-box":!1,"mask-box-outset":!1,"mask-box-repeat":!1,"mask-box-slice":!1,"mask-box-source":!1,"mask-box-width":!1,"mask-clip":!1,"mask-image":!1,"mask-origin":!1,"mask-position":!1,"mask-repeat":!1,"mask-size":!1,"mask-source-type":!1,"mask-type":!1,"max-height":!0,"max-lines":!1,"max-width":!0,"min-height":!0,"min-width":!0,"move-to":!1,"nav-down":!1,"nav-index":!1,"nav-left":!1,"nav-right":!1,"nav-up":!1,"object-fit":!1,"object-position":!1,opacity:!1,order:!1,orphans:!1,outline:!1,"outline-color":!1,"outline-offset":!1,"outline-style":!1,"outline-width":!1,overflow:!1,"overflow-wrap":!1,"overflow-x":!1,"overflow-y":!1,padding:!0,"padding-bottom":!0,"padding-left":!0,"padding-right":!0,"padding-top":!0,page:!1,"page-break-after":!1,"page-break-before":!1,"page-break-inside":!1,"page-policy":!1,pause:!1,"pause-after":!1,"pause-before":!1,perspective:!1,"perspective-origin":!1,pitch:!1,"pitch-range":!1,"play-during":!1,position:!1,"presentation-level":!1,quotes:!1,"region-fragment":!1,resize:!1,rest:!1,"rest-after":!1,"rest-before":!1,richness:!1,right:!1,rotation:!1,"rotation-point":!1,"ruby-align":!1,"ruby-merge":!1,"ruby-position":!1,"shape-image-threshold":!1,"shape-outside":!1,"shape-margin":!1,size:!1,speak:!1,"speak-as":!1,"speak-header":!1,"speak-numeral":!1,"speak-punctuation":!1,"speech-rate":!1,stress:!1,"string-set":!1,"tab-size":!1,"table-layout":!1,"text-align":!0,"text-align-last":!0,"text-combine-upright":!0,"text-decoration":!0,"text-decoration-color":!0,"text-decoration-line":!0,"text-decoration-skip":!0,"text-decoration-style":!0,"text-emphasis":!0,"text-emphasis-color":!0,"text-emphasis-position":!0,"text-emphasis-style":!0,"text-height":!0,"text-indent":!0,"text-justify":!0,"text-orientation":!0,"text-overflow":!0,"text-shadow":!0,"text-space-collapse":!0,"text-transform":!0,"text-underline-position":!0,"text-wrap":!0,top:!1,transform:!1,"transform-origin":!1,"transform-style":!1,transition:!1,"transition-delay":!1,"transition-duration":!1,"transition-property":!1,"transition-timing-function":!1,"unicode-bidi":!1,"vertical-align":!1,visibility:!1,"voice-balance":!1,"voice-duration":!1,"voice-family":!1,"voice-pitch":!1,"voice-range":!1,"voice-rate":!1,"voice-stress":!1,"voice-volume":!1,volume:!1,"white-space":!1,widows:!1,width:!0,"will-change":!1,"word-break":!0,"word-spacing":!0,"word-wrap":!0,"wrap-flow":!1,"wrap-through":!1,"writing-mode":!1,"z-index":!1};return t}var i=/javascript\s*\:/gim;e.whiteList=s(),e.getDefaultWhiteList=s,e.onAttr=function(t,e,s){},e.onIgnoreAttr=function(t,e,s){},e.safeAttrValue=function(t,e){return i.test(e)?"":e}},"./node_modules/cssfilter/lib/index.js":function(t,e,s){var i=s("./node_modules/cssfilter/lib/default.js"),a=s("./node_modules/cssfilter/lib/css.js");for(var n in(e=t.exports=function(t,e){return new a(e).process(t)}).FilterCSS=a,i)e[n]=i[n];"undefined"!=typeof window&&(window.filterCSS=t.exports)},"./node_modules/cssfilter/lib/parser.js":function(t,e,s){var i=s("./node_modules/cssfilter/lib/util.js");t.exports=function(t,e){";"!==(t=i.trimRight(t))[t.length-1]&&(t+=";");var s=t.length,a=!1,n=0,r=0,o="";function l(){if(!a){var s=i.trim(t.slice(n,r)),l=s.indexOf(":");if(-1!==l){var c=i.trim(s.slice(0,l)),u=i.trim(s.slice(l+1));if(c){var d=e(n,o.length,c,u,s);d&&(o+=d+"; ")}}}n=r+1}for(;r<s;r++){var c=t[r];if("/"===c&&"*"===t[r+1]){var u=t.indexOf("*/",r+2);if(-1===u)break;n=(r=u+1)+1,a=!1}else"("===c?a=!0:")"===c?a=!1:";"===c?a||l():"\n"===c&&l()}return i.trim(o)}},"./node_modules/cssfilter/lib/util.js":function(t,e){t.exports={indexOf:function(t,e){var s,i;if(Array.prototype.indexOf)return t.indexOf(e);for(s=0,i=t.length;s<i;s++)if(t[s]===e)return s;return-1},forEach:function(t,e,s){var i,a;if(Array.prototype.forEach)return t.forEach(e,s);for(i=0,a=t.length;i<a;i++)e.call(s,t[i],i,t)},trim:function(t){return String.prototype.trim?t.trim():t.replace(/(^\s*)|(\s*$)/g,"")},trimRight:function(t){return String.prototype.trimRight?t.trimRight():t.replace(/(\s*$)/g,"")}}},"./node_modules/vue-loader/lib/runtime/componentNormalizer.js":function(t,e,s){"use strict";function i(t,e,s,i,a,n,r,o){var l,c="function"==typeof t?t.options:t;if(e&&(c.render=e,c.staticRenderFns=s,c._compiled=!0),i&&(c.functional=!0),n&&(c._scopeId="data-v-"+n),r?(l=function(t){(t=t||this.$vnode&&this.$vnode.ssrContext||this.parent&&this.parent.$vnode&&this.parent.$vnode.ssrContext)||"undefined"==typeof __VUE_SSR_CONTEXT__||(t=__VUE_SSR_CONTEXT__),a&&a.call(this,t),t&&t._registeredComponents&&t._registeredComponents.add(r)},c._ssrRegister=l):a&&(l=o?function(){a.call(this,this.$root.$options.shadowRoot)}:a),l)if(c.functional){c._injectStyles=l;var u=c.render;c.render=function(t,e){return l.call(e),u(t,e)}}else{var d=c.beforeCreate;c.beforeCreate=d?[].concat(d,l):[l]}return{exports:t,options:c}}s.d(e,"a",(function(){return i}))},"./node_modules/xss/lib/default.js":function(t,e,s){var i=s("./node_modules/cssfilter/lib/index.js").FilterCSS,a=s("./node_modules/cssfilter/lib/index.js").getDefaultWhiteList,n=s("./node_modules/xss/lib/util.js");function r(){return{a:["target","href","title"],abbr:["title"],address:[],area:["shape","coords","href","alt"],article:[],aside:[],audio:["autoplay","controls","loop","preload","src"],b:[],bdi:["dir"],bdo:["dir"],big:[],blockquote:["cite"],br:[],caption:[],center:[],cite:[],code:[],col:["align","valign","span","width"],colgroup:["align","valign","span","width"],dd:[],del:["datetime"],details:["open"],div:[],dl:[],dt:[],em:[],font:["color","size","face"],footer:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],header:[],hr:[],i:[],img:["src","alt","title","width","height"],ins:["datetime"],li:[],mark:[],nav:[],ol:[],p:[],pre:[],s:[],section:[],small:[],span:[],sub:[],sup:[],strong:[],table:["width","border","align","valign"],tbody:["align","valign"],td:["width","rowspan","colspan","align","valign"],tfoot:["align","valign"],th:["width","rowspan","colspan","align","valign"],thead:["align","valign"],tr:["rowspan","align","valign"],tt:[],u:[],ul:[],video:["autoplay","controls","loop","preload","src","height","width"]}}var o=new i;function l(t){return t.replace(c,"&lt;").replace(u,"&gt;")}var c=/</g,u=/>/g,d=/"/g,f=/&quot;/g,_=/&#([a-zA-Z0-9]*);?/gim,p=/&colon;?/gim,m=/&newline;?/gim,v=/((j\s*a\s*v\s*a|v\s*b|l\s*i\s*v\s*e)\s*s\s*c\s*r\s*i\s*p\s*t\s*|m\s*o\s*c\s*h\s*a)\:/gi,h=/e\s*x\s*p\s*r\s*e\s*s\s*s\s*i\s*o\s*n\s*\(.*/gi,g=/u\s*r\s*l\s*\(.*/gi;function b(t){return t.replace(d,"&quot;")}function w(t){return t.replace(f,'"')}function y(t){return t.replace(_,(function(t,e){return"x"===e[0]||"X"===e[0]?String.fromCharCode(parseInt(e.substr(1),16)):String.fromCharCode(parseInt(e,10))}))}function C(t){return t.replace(p,":").replace(m," ")}function x(t){for(var e="",s=0,i=t.length;s<i;s++)e+=t.charCodeAt(s)<32?" ":t.charAt(s);return n.trim(e)}function k(t){return t=x(t=C(t=y(t=w(t))))}function A(t){return t=l(t=b(t))}var j=/<!--[\s\S]*?-->/g;e.whiteList={a:["target","href","title"],abbr:["title"],address:[],area:["shape","coords","href","alt"],article:[],aside:[],audio:["autoplay","controls","loop","preload","src"],b:[],bdi:["dir"],bdo:["dir"],big:[],blockquote:["cite"],br:[],caption:[],center:[],cite:[],code:[],col:["align","valign","span","width"],colgroup:["align","valign","span","width"],dd:[],del:["datetime"],details:["open"],div:[],dl:[],dt:[],em:[],font:["color","size","face"],footer:[],h1:[],h2:[],h3:[],h4:[],h5:[],h6:[],header:[],hr:[],i:[],img:["src","alt","title","width","height"],ins:["datetime"],li:[],mark:[],nav:[],ol:[],p:[],pre:[],s:[],section:[],small:[],span:[],sub:[],sup:[],strong:[],table:["width","border","align","valign"],tbody:["align","valign"],td:["width","rowspan","colspan","align","valign"],tfoot:["align","valign"],th:["width","rowspan","colspan","align","valign"],thead:["align","valign"],tr:["rowspan","align","valign"],tt:[],u:[],ul:[],video:["autoplay","controls","loop","preload","src","height","width"]},e.getDefaultWhiteList=r,e.onTag=function(t,e,s){},e.onIgnoreTag=function(t,e,s){},e.onTagAttr=function(t,e,s){},e.onIgnoreTagAttr=function(t,e,s){},e.safeAttrValue=function(t,e,s,i){if(s=k(s),"href"===e||"src"===e){if("#"===(s=n.trim(s)))return"#";if("http://"!==s.substr(0,7)&&"https://"!==s.substr(0,8)&&"mailto:"!==s.substr(0,7)&&"tel:"!==s.substr(0,4)&&"#"!==s[0]&&"/"!==s[0])return""}else if("background"===e){if(v.lastIndex=0,v.test(s))return""}else if("style"===e){if(h.lastIndex=0,h.test(s))return"";if(g.lastIndex=0,g.test(s)&&(v.lastIndex=0,v.test(s)))return"";!1!==i&&(s=(i=i||o).process(s))}return s=A(s)},e.escapeHtml=l,e.escapeQuote=b,e.unescapeQuote=w,e.escapeHtmlEntities=y,e.escapeDangerHtml5Entities=C,e.clearNonPrintableCharacter=x,e.friendlyAttrValue=k,e.escapeAttrValue=A,e.onIgnoreTagStripAll=function(){return""},e.StripTagBody=function(t,e){"function"!=typeof e&&(e=function(){});var s=!Array.isArray(t),i=[],a=!1;return{onIgnoreTag:function(r,o,l){if(function(e){return!!s||-1!==n.indexOf(t,e)}(r)){if(l.isClosing){var c="[/removed]",u=l.position+c.length;return i.push([!1!==a?a:l.position,u]),a=!1,c}return a||(a=l.position),"[removed]"}return e(r,o,l)},remove:function(t){var e="",s=0;return n.forEach(i,(function(i){e+=t.slice(s,i[0]),s=i[1]})),e+=t.slice(s)}}},e.stripCommentTag=function(t){return t.replace(j,"")},e.stripBlankChar=function(t){var e=t.split("");return(e=e.filter((function(t){var e=t.charCodeAt(0);return 127!==e&&(!(e<=31)||(10===e||13===e))}))).join("")},e.cssFilter=o,e.getDefaultCSSWhiteList=a},"./node_modules/xss/lib/index.js":function(t,e,s){var i=s("./node_modules/xss/lib/default.js"),a=s("./node_modules/xss/lib/parser.js"),n=s("./node_modules/xss/lib/xss.js");function r(t,e){return new n(e).process(t)}for(var o in(e=t.exports=r).filterXSS=r,e.FilterXSS=n,i)e[o]=i[o];for(var o in a)e[o]=a[o];"undefined"!=typeof window&&(window.filterXSS=t.exports),"undefined"!=typeof self&&"undefined"!=typeof DedicatedWorkerGlobalScope&&self instanceof DedicatedWorkerGlobalScope&&(self.filterXSS=t.exports)},"./node_modules/xss/lib/parser.js":function(t,e,s){var i=s("./node_modules/xss/lib/util.js");function a(t){var e=i.spaceIndex(t);if(-1===e)var s=t.slice(1,-1);else s=t.slice(1,e+1);return"/"===(s=i.trim(s).toLowerCase()).slice(0,1)&&(s=s.slice(1)),"/"===s.slice(-1)&&(s=s.slice(0,-1)),s}function n(t){return"</"===t.slice(0,2)}var r=/[^a-zA-Z0-9_:\.\-]/gim;function o(t,e){for(;e<t.length;e++){var s=t[e];if(" "!==s)return"="===s?e:-1}}function l(t,e){for(;e>0;e--){var s=t[e];if(" "!==s)return"="===s?e:-1}}function c(t){return function(t){return'"'===t[0]&&'"'===t[t.length-1]||"'"===t[0]&&"'"===t[t.length-1]}(t)?t.substr(1,t.length-2):t}e.parseTag=function(t,e,s){var i="",r=0,o=!1,l=!1,c=0,u=t.length,d="",f="";for(c=0;c<u;c++){var _=t.charAt(c);if(!1===o){if("<"===_){o=c;continue}}else if(!1===l){if("<"===_){i+=s(t.slice(r,c)),o=c,r=c;continue}if(">"===_){i+=s(t.slice(r,o)),d=a(f=t.slice(o,c+1)),i+=e(o,i.length,d,f,n(f)),r=c+1,o=!1;continue}if(('"'===_||"'"===_)&&"="===t.charAt(c-1)){l=_;continue}}else if(_===l){l=!1;continue}}return r<t.length&&(i+=s(t.substr(r))),i},e.parseAttr=function(t,e){var s=0,a=[],n=!1,u=t.length;function d(t,s){if(!((t=(t=i.trim(t)).replace(r,"").toLowerCase()).length<1)){var n=e(t,s||"");n&&a.push(n)}}for(var f=0;f<u;f++){var _,p=t.charAt(f);if(!1!==n||"="!==p)if(!1===n||f!==s||'"'!==p&&"'"!==p||"="!==t.charAt(f-1))if(/\s|\n|\t/.test(p)){if(t=t.replace(/\s|\n|\t/g," "),!1===n){if(-1===(_=o(t,f))){d(i.trim(t.slice(s,f))),n=!1,s=f+1;continue}f=_-1;continue}if(-1===(_=l(t,f-1))){d(n,c(i.trim(t.slice(s,f)))),n=!1,s=f+1;continue}}else;else{if(-1===(_=t.indexOf(p,f+1)))break;d(n,i.trim(t.slice(s+1,_))),n=!1,s=(f=_)+1}else n=t.slice(s,f),s=f+1}return s<t.length&&(!1===n?d(t.slice(s)):d(n,c(i.trim(t.slice(s))))),i.trim(a.join(" "))}},"./node_modules/xss/lib/util.js":function(t,e){t.exports={indexOf:function(t,e){var s,i;if(Array.prototype.indexOf)return t.indexOf(e);for(s=0,i=t.length;s<i;s++)if(t[s]===e)return s;return-1},forEach:function(t,e,s){var i,a;if(Array.prototype.forEach)return t.forEach(e,s);for(i=0,a=t.length;i<a;i++)e.call(s,t[i],i,t)},trim:function(t){return String.prototype.trim?t.trim():t.replace(/(^\s*)|(\s*$)/g,"")},spaceIndex:function(t){var e=/\s|\n|\t/.exec(t);return e?e.index:-1}}},"./node_modules/xss/lib/xss.js":function(t,e,s){var i=s("./node_modules/cssfilter/lib/index.js").FilterCSS,a=s("./node_modules/xss/lib/default.js"),n=s("./node_modules/xss/lib/parser.js"),r=n.parseTag,o=n.parseAttr,l=s("./node_modules/xss/lib/util.js");function c(t){return null==t}function u(t){(t=function(t){var e={};for(var s in t)e[s]=t[s];return e}(t||{})).stripIgnoreTag&&(t.onIgnoreTag&&console.error('Notes: cannot use these two options "stripIgnoreTag" and "onIgnoreTag" at the same time'),t.onIgnoreTag=a.onIgnoreTagStripAll),t.whiteList=t.whiteList||a.whiteList,t.onTag=t.onTag||a.onTag,t.onTagAttr=t.onTagAttr||a.onTagAttr,t.onIgnoreTag=t.onIgnoreTag||a.onIgnoreTag,t.onIgnoreTagAttr=t.onIgnoreTagAttr||a.onIgnoreTagAttr,t.safeAttrValue=t.safeAttrValue||a.safeAttrValue,t.escapeHtml=t.escapeHtml||a.escapeHtml,this.options=t,!1===t.css?this.cssFilter=!1:(t.css=t.css||{},this.cssFilter=new i(t.css))}u.prototype.process=function(t){if(!(t=(t=t||"").toString()))return"";var e=this.options,s=e.whiteList,i=e.onTag,n=e.onIgnoreTag,u=e.onTagAttr,d=e.onIgnoreTagAttr,f=e.safeAttrValue,_=e.escapeHtml,p=this.cssFilter;e.stripBlankChar&&(t=a.stripBlankChar(t)),e.allowCommentTag||(t=a.stripCommentTag(t));var m=!1;if(e.stripIgnoreTagBody){m=a.StripTagBody(e.stripIgnoreTagBody,n);n=m.onIgnoreTag}var v=r(t,(function(t,e,a,r,m){var v,h={sourcePosition:t,position:e,isClosing:m,isWhite:s.hasOwnProperty(a)};if(!c(v=i(a,r,h)))return v;if(h.isWhite){if(h.isClosing)return"</"+a+">";var g=function(t){var e=l.spaceIndex(t);if(-1===e)return{html:"",closing:"/"===t[t.length-2]};var s="/"===(t=l.trim(t.slice(e+1,-1)))[t.length-1];return s&&(t=l.trim(t.slice(0,-1))),{html:t,closing:s}}(r),b=s[a],w=o(g.html,(function(t,e){var s,i=-1!==l.indexOf(b,t);return c(s=u(a,t,e,i))?i?(e=f(a,t,e,p))?t+'="'+e+'"':t:c(s=d(a,t,e,i))?void 0:s:s}));r="<"+a;return w&&(r+=" "+w),g.closing&&(r+=" /"),r+=">"}return c(v=n(a,r,h))?_(r):v}),_);return m&&(v=m.remove(v)),v},t.exports=u},"./src/advanced-tools.js":function(t,e,s){"use strict";s.r(e);var i=s("vue"),a=s.n(i),n=s("./src/helper/base_hepler.js"),r={mixins:[n.a],data:function(){return{all_roles:advanced_tools.misc.all_roles,compatibility:advanced_tools.misc.compatibility,model:advanced_tools.model.two_factor,nonces:advanced_tools.nonces,endpoints:advanced_tools.endpoints,state:{on_saving:!1,waiting_save:!1,origin_state:!1}}},methods:{toggle:function(t){var e=this,s={};s.enabled=t,this.httpPostRequest("updateSettings",{data:JSON.stringify({settings:s,module:"auth"})},(function(){var s=this;e.model.enabled=t,!0===t&&e.$nextTick((function(){e.rebindSUI(),e.bindUploader(),s.state.waiting_save=!1}))}))},updateSettings:function(){var t=this.model;delete t.email_subject,delete t.email_sender,delete t.email_body,this.state.origin_state=this.model.user_roles.length>0,this.httpPostRequest("updateSettings",{data:JSON.stringify({settings:t,module:"auth"})})},saveEmailTemplate:function(){var t={email_subject:this.model.email_subject,email_sender:this.model.email_sender,email_body:this.model.email_body};this.httpPostRequest("updateSettings",{data:JSON.stringify({module:"auth",settings:t})},(function(t){!0===t.success&&SUI.dialogs["edit-one-time-password-email"].hide()}))},sendTestEmail:function(){var t={email_subject:this.model.email_subject,email_sender:this.model.email_sender,email_body:this.model.email_body};this.httpPostRequest("sendTestEmail",t)},bindUploader:function(){var t=void 0,e=this;jQuery(".file-picker").click((function(){t?t.open():((t=wp.media.frames.file_frame=wp.media({title:"Choose an image file",button:{text:"Choose File"},multiple:!1,library:{type:["image"]}})).on("select",(function(){var s=t.state().get("selection").first().toJSON();jQuery.inArray(s.mime,["image/jpeg","image/png","image/gif"])>-1?e.model.custom_graphic_url=s.url:Defender.showNotification("error","Invalid image file type")})),t.open())}))}},mounted:function(){var t=this;this.$nextTick((function(){t.bindUploader()})),this.state.origin_state=this.model.user_roles.length>0}},o=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),l=Object(o.a)(r,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return!1===t.model.enabled?s("div",{staticClass:"sui-box"},[s("div",{staticClass:"sui-box"},[s("div",{staticClass:"sui-box-header"},[s("h3",{staticClass:"sui-box-title"},[t._v("\n\t\t\t\t\t"+t._s(t.__("Two Factor Authentication"))+"\n\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-message"},[t.maybeHideBranding()?t._e():s("img",{staticClass:"sui-image",attrs:{src:t.assetUrl("assets/img/2factor-disabled.svg"),"aria-hidden":"true"}}),t._v(" "),s("div",{staticClass:"sui-message-content"},[s("p",[t._v("\n\t\t\t\t\t\t"+t._s(t.__("Beef up your website’s security with two-factor authentication. Add an extra step in the login process so that users are required to enter a password and an app-generated passcode using their phone – the best protection against brute force attacks."))+"\n\t\t\t\t\t")]),t._v(" "),s("form",{attrs:{method:"post"},on:{submit:function(e){return e.preventDefault(),t.toggle(!0)}}},[s("submit-button",{attrs:{type:"submit",state:t.state,"css-class":"sui-button-blue"}},[s("i",{staticClass:"sui-icon-save",attrs:{"aria-hidden":"true"}}),t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Activate"))+"\n\t\t\t\t\t\t")])],1)])])])]):s("div",{staticClass:"sui-box"},[s("div",{staticClass:"sui-box-header"},[s("h3",{staticClass:"sui-box-title"},[t._v("\n\t\t\t\t"+t._s(t.__("Two Factor Authentication"))+"\n\t\t\t")])]),t._v(" "),s("form",{staticClass:"advanced-settings-frm",attrs:{method:"post"},on:{submit:function(e){return e.preventDefault(),t.updateSettings(e)}}},[s("div",{staticClass:"sui-box-body"},[s("p",[t._v("\n\t\t\t\t\t"+t._s(t.__("Configure your two-factor authentication settings. Our recommendations are enabled by default."))+"\n\t\t\t\t")]),t._v(" "),!1!==t.compatibility?s("div",{staticClass:"sui-notice sui-notice-error"},[s("p",t._l(t.compatibility,(function(e){return s("span",[t._v("\n "+t._s(e)+"\n ")])})),0)]):t._e(),t._v(" "),t.state.origin_state?s("div",{staticClass:"sui-notice sui-notice-info"},[s("p",[s("strong",[t._v(t._s(t.__("Two-factor authentication is now active.")))]),t._v(" "+t._s(t.__("User roles with this feature enabled must visit their "))+"\n\t\t\t\t\t\t"),s("a",{attrs:{href:t.adminUrl("profile.php")}},[t._v(t._s(t.__("Profile page")))]),t._v(" "+t._s(t.__("to complete setup and sync their account with the Authenticator app."))+"\n\t\t\t\t\t")])]):s("div",{staticClass:"sui-notice sui-notice-warning"},[s("p",[s("strong",[t._v(t._s(t.__("Two-factor authentication is currently inactive.")))]),t._v(" "+t._s(t.__("Configure and save your settings to complete setup."))+"\n\t\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("User Roles"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Choose the user roles you want to enable two-factor authentication for. Users with those roles will then be required to use the Google Authenticator app to login."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("div",{staticClass:"sui-field-list"},[s("div",{staticClass:"sui-field-list-header"},[s("h3",{staticClass:"sui-field-list-title"},[t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("User role"))+"\n\t\t\t\t\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-field-list-body"},t._l(t.all_roles,(function(e,i){return s("div",{staticClass:"sui-field-list-item"},[s("label",{staticClass:"sui-field-list-item-label",attrs:{for:"toggle_"+i},domProps:{innerHTML:t._s(e.name)}}),t._v(" "),s("label",{staticClass:"sui-toggle"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.user_roles,expression:"model.user_roles"}],attrs:{type:"checkbox",id:"toggle_"+i},domProps:{value:i,checked:Array.isArray(t.model.user_roles)?t._i(t.model.user_roles,i)>-1:t.model.user_roles},on:{change:function(e){var s=t.model.user_roles,a=e.target,n=!!a.checked;if(Array.isArray(s)){var r=i,o=t._i(s,r);a.checked?o<0&&t.$set(t.model,"user_roles",s.concat([r])):o>-1&&t.$set(t.model,"user_roles",s.slice(0,o).concat(s.slice(o+1)))}else t.$set(t.model,"user_roles",n)}}}),t._v(" "),s("span",{staticClass:"sui-toggle-slider"})])])})),0)])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Lost Phone"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("If a user is unable to access their phone, you can allow an option to send the one time password to their registered email."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("div",{staticClass:"sui-form-field"},[s("label",{staticClass:"sui-toggle"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.lost_phone,expression:"model.lost_phone"}],staticClass:"toggle-checkbox",attrs:{role:"presentation",type:"checkbox",id:"lost_phone"},domProps:{checked:Array.isArray(t.model.lost_phone)?t._i(t.model.lost_phone,null)>-1:t.model.lost_phone},on:{change:function(e){var s=t.model.lost_phone,i=e.target,a=!!i.checked;if(Array.isArray(s)){var n=t._i(s,null);i.checked?n<0&&t.$set(t.model,"lost_phone",s.concat([null])):n>-1&&t.$set(t.model,"lost_phone",s.slice(0,n).concat(s.slice(n+1)))}else t.$set(t.model,"lost_phone",a)}}}),t._v(" "),s("span",{staticClass:"sui-toggle-slider"})]),t._v(" "),s("label",{staticClass:"sui-toggle-label",attrs:{for:"lost_phone"}},[t._v("\n\t\t\t\t\t\t\t\t"+t._s(t.__("Enable lost phone option"))+"\n\t\t\t\t\t\t\t")])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Force Authentication"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("By default, two-factor authentication is optional for users. This setting forces users to activate two-factor."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("div",{staticClass:"sui-form-field"},[s("label",{staticClass:"sui-toggle"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.force_auth,expression:"model.force_auth"}],staticClass:"toggle-checkbox",attrs:{role:"presentation",type:"checkbox",name:"force_auth",id:"force_auth"},domProps:{checked:Array.isArray(t.model.force_auth)?t._i(t.model.force_auth,null)>-1:t.model.force_auth},on:{change:function(e){var s=t.model.force_auth,i=e.target,a=!!i.checked;if(Array.isArray(s)){var n=t._i(s,null);i.checked?n<0&&t.$set(t.model,"force_auth",s.concat([null])):n>-1&&t.$set(t.model,"force_auth",s.slice(0,n).concat(s.slice(n+1)))}else t.$set(t.model,"force_auth",a)}}}),t._v(" "),s("span",{staticClass:"sui-toggle-slider"})]),t._v(" "),s("label",{staticClass:"sui-toggle-label",attrs:{for:"force_auth"}},[t._v("\n\t\t\t\t\t\t\t\t"+t._s(t.__("Force users to log in with two-factor authentication"))+"\n\t\t\t\t\t\t\t")]),t._v(" "),s("span",{staticClass:"sui-description sui-toggle-content"},[t._v("\n "+t._s(t.__("Note: Users will be forced to set up two-factor when they next login."))+"\n ")]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:!0===t.model.force_auth,expression:"model.force_auth===true"}],staticClass:"sui-border-frame sui-toggle-content",attrs:{id:"force_auth_roles"}},[s("strong",[t._v(t._s(t.__("User Roles")))]),t._v(" "),s("ul",t._l(t.all_roles,(function(e,i){return s("li",[s("label",{staticClass:"sui-checkbox",attrs:{for:"toggle_force_"+i}},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.force_auth_roles,expression:"model.force_auth_roles"}],attrs:{type:"checkbox",id:"toggle_force_"+i},domProps:{value:i,checked:Array.isArray(t.model.force_auth_roles)?t._i(t.model.force_auth_roles,i)>-1:t.model.force_auth_roles},on:{change:function(e){var s=t.model.force_auth_roles,a=e.target,n=!!a.checked;if(Array.isArray(s)){var r=i,o=t._i(s,r);a.checked?o<0&&t.$set(t.model,"force_auth_roles",s.concat([r])):o>-1&&t.$set(t.model,"force_auth_roles",s.slice(0,o).concat(s.slice(o+1)))}else t.$set(t.model,"force_auth_roles",n)}}}),t._v(" "),s("span",{attrs:{"aria-hidden":"true"}}),t._v(" "),s("span",[t._v(t._s(e.name))])])])})),0),t._v(" "),s("strong",[t._v(t._s(t.__("Custom warning message")))]),t._v(" "),s("textarea",{directives:[{name:"model",rawName:"v-model",value:t.model.force_auth_mess,expression:"model.force_auth_mess"}],staticClass:"sui-form-control",attrs:{name:"force_auth_mess"},domProps:{value:t.model.force_auth_mess},on:{input:function(e){e.target.composing||t.$set(t.model,"force_auth_mess",e.target.value)}}}),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Note: This is shown in the users Profile area indicating they must use two-factor authentication."))+"\n ")])])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Custom Graphic"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("By default, Defender’s icon appears above the login fields. You can upload your own branding, or turn this feature off."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("label",{staticClass:"sui-toggle"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.custom_graphic,expression:"model.custom_graphic"}],staticClass:"toggle-checkbox",attrs:{role:"presentation",type:"checkbox",name:"custom_graphic",id:"custom_graphic"},domProps:{checked:Array.isArray(t.model.custom_graphic)?t._i(t.model.custom_graphic,null)>-1:t.model.custom_graphic},on:{change:function(e){var s=t.model.custom_graphic,i=e.target,a=!!i.checked;if(Array.isArray(s)){var n=t._i(s,null);i.checked?n<0&&t.$set(t.model,"custom_graphic",s.concat([null])):n>-1&&t.$set(t.model,"custom_graphic",s.slice(0,n).concat(s.slice(n+1)))}else t.$set(t.model,"custom_graphic",a)}}}),t._v(" "),s("span",{staticClass:"sui-toggle-slider"})]),t._v(" "),s("label",{staticClass:"sui-toggle-label",attrs:{for:"custom_graphic"}},[t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Enable custom graphics above login fields"))+"\n\t\t\t\t\t\t")]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:!0===t.model.custom_graphic,expression:"model.custom_graphic === true"}],staticClass:"sui-border-frame sui-toggle-content"},[s("span",{staticClass:"sui-description"},[s("strong",[t._v(t._s(t.__("Custom Graphic")))]),t._v(" - "+t._s(t.__("For best results use a 168x168px JPG or PNG."))+"\n ")]),t._v(" "),s("div",{staticClass:"sui-upload"},[s("div",{staticClass:"sui-upload-image"},[s("img",{attrs:{id:"custom_graphicIMG",width:"40",height:"40",src:t.model.custom_graphic_url}}),t._v(" "),s("div",{staticClass:"sui-image-preview",style:{backgroundImage:"url("+t.model.custom_graphic_url+")"},attrs:{role:"button"}})]),t._v(" "),s("button",{staticClass:"sui-upload-button file-picker",attrs:{type:"button"}},[s("i",{staticClass:"sui-icon-upload-cloud",attrs:{"aria-hidden":"true"}}),t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("Upload file"))+"\n\t\t\t\t\t\t\t\t")]),t._v(" "),t._m(0)])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Emails"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Customize the default copy for emails the two-factor feature sends to users."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("div",{staticClass:"sui-field-list"},[s("div",{staticClass:"sui-field-list-header"},[s("h3",{staticClass:"sui-field-list-title"},[t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("Email"))+"\n\t\t\t\t\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-field-list-body"},[s("div",{staticClass:"sui-field-list-item"},[s("label",{staticClass:"sui-field-list-item-label"},[t._v("\n\t\t\t\t\t\t\t\t\t\t"+t._s(t.__("Lost phone one time password"))+"\n\t\t\t\t\t\t\t\t\t")]),t._v(" "),t._m(1)])])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("App Download"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Need the app? Here’s links to the official Google Authenticator iOS and Android apps."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("a",{attrs:{href:"https://itunes.apple.com/vn/app/google-authenticator/id388497605?mt=8"}},[s("img",{attrs:{src:t.assetUrl("assets/img/ios-download.svg")}})]),t._v(" "),s("a",{attrs:{href:"https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2"}},[s("img",{attrs:{src:t.assetUrl("assets/img/android-download.svg")}})])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Active Users"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Here’s a quick link to see which of your users have enabled two-factor authentication."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("a",{attrs:{href:t.adminUrl("users.php")}},[t._v(t._s(t.__("View users")))]),t._v(" "+t._s(t.__("who have enabled this feature."))+"\n\t\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Deactivate"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Disable two-factor authentication on your website."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("submit-button",{attrs:{"css-class":"sui-button-ghost",state:t.state},on:{click:function(e){return t.toggle(!1)}}},[t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Deactivate"))+"\n\t\t\t\t\t\t")])],1)])]),t._v(" "),s("div",{staticClass:"sui-box-footer"},[s("div",{staticClass:"sui-actions-right"},[s("submit-button",{attrs:{"css-class":"sui-button-blue",type:"submit",state:t.state}},[s("i",{staticClass:"sui-icon-save",attrs:{"aria-hidden":"true"}}),t._v("\n\t\t\t\t\t\t"+t._s(t.__("Save Changes"))+"\n\t\t\t\t\t")])],1)])]),t._v(" "),s("div",{staticClass:"sui-dialog",attrs:{"aria-hidden":"true",tabindex:"-1",id:"edit-one-time-password-email"}},[s("div",{staticClass:"sui-dialog-overlay",attrs:{"data-a11y-dialog-hide":""}}),t._v(" "),s("div",{staticClass:"sui-dialog-content",attrs:{"aria-labelledby":"dialogTitle","aria-describedby":"dialogDescription",role:"dialog"}},[s("div",{staticClass:"sui-box",attrs:{role:"document"}},[s("div",{staticClass:"sui-box-header"},[s("h3",{staticClass:"sui-box-title",attrs:{id:"dialogTitle"}},[t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Edit Email"))+"\n\t\t\t\t\t\t")]),t._v(" "),t._m(2)]),t._v(" "),s("form",{attrs:{method:"post"}},[s("div",{staticClass:"sui-box-body"},[s("p",{attrs:{id:"dialogDescription"}},[t._v("\n\t\t\t\t\t\t\t\t"+t._s(t.__("This email sends a temporary passcode when the user can’t access their phone."))+"\n\t\t\t\t\t\t\t")]),t._v(" "),s("div",{staticClass:"sui-row"},[s("div",{staticClass:"sui-col"},[s("div",{staticClass:"sui-form-field"},[s("label",{staticClass:"sui-label"},[t._v("\n\t\t\t\t\t\t\t\t\t\t\t"+t._s(t.__("Subject"))+"\n\t\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.email_subject,expression:"model.email_subject"}],staticClass:"sui-form-control",attrs:{name:"subject",type:"text",id:"email_subject"},domProps:{value:t.model.email_subject},on:{input:function(e){e.target.composing||t.$set(t.model,"email_subject",e.target.value)}}})])]),t._v(" "),s("div",{staticClass:"sui-col"},[s("div",{staticClass:"sui-form-field"},[s("label",{staticClass:"sui-label"},[t._v("\n\t\t\t\t\t\t\t\t\t\t\t"+t._s(t.__("Sender"))+"\n\t\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.email_sender,expression:"model.email_sender"}],staticClass:"sui-form-control",attrs:{name:"sender",type:"text",id:"email_sender"},domProps:{value:t.model.email_sender},on:{input:function(e){e.target.composing||t.$set(t.model,"email_sender",e.target.value)}}})])])]),t._v(" "),s("div",{staticClass:"sui-row"},[s("div",{staticClass:"sui-col"},[s("label",{staticClass:"sui-label"},[t._v("\n\t\t\t\t\t\t\t\t\t\t"+t._s(t.__("Body"))+"\n\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("textarea",{directives:[{name:"model",rawName:"v-model",value:t.model.email_body,expression:"model.email_body"}],staticClass:"sui-form-control",attrs:{name:"body",rows:"8",id:"email_body"},domProps:{value:t.model.email_body},on:{input:function(e){e.target.composing||t.$set(t.model,"email_body",e.target.value)}}})])]),t._v(" "),s("div",{staticClass:"sui-row"},[s("div",{staticClass:"sui-col"},[s("label",{staticClass:"sui-label"},[t._v("\n\t\t\t\t\t\t\t\t\t\t"+t._s(t.__("Available variables"))+"\n\t\t\t\t\t\t\t\t\t")]),t._v(" "),s("span",{staticClass:"sui-tag"},[s("strong",{domProps:{textContent:t._s(t.__("{{passcode}}"))}})]),t._v(" "),s("span",{staticClass:"sui-tag"},[s("strong",{domProps:{textContent:t._s(t.__("{{display_name}}"))}})])])])]),t._v(" "),s("div",{staticClass:"sui-box-footer"},[s("div",{staticClass:"sui-flex-child-right"},[s("button",{staticClass:"sui-button sui-button-ghost",attrs:{type:"button","data-a11y-dialog-hide":"my-accessible-dialog"}},[t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("Cancel"))+"\n\t\t\t\t\t\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-actions-right"},[s("submit-button",{staticClass:"sui-button",attrs:{type:"button",state:t.state},on:{click:t.saveEmailTemplate}},[t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("Save Template"))+"\n\t\t\t\t\t\t\t\t")]),t._v(" "),s("submit-button",{staticClass:"sui-button sui-button-blue",attrs:{type:"button",state:t.state},on:{click:t.sendTestEmail}},[t._v("\n\t\t\t\t\t\t\t\t\t"+t._s(t.__("Send Test"))+"\n\t\t\t\t\t\t\t\t")])],1)])])])])])])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"sui-upload-file"},[e("span")])},function(){var t=this.$createElement,e=this._self._c||t;return e("button",{staticClass:"sui-button-icon",attrs:{type:"button","data-a11y-dialog-show":"edit-one-time-password-email"}},[e("i",{staticClass:"sui-icon-pencil",attrs:{"aria-hidden":"true"}})])},function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"sui-actions-right"},[e("button",{staticClass:"sui-dialog-close",attrs:{"data-a11y-dialog-hide":"","aria-label":"Close this dialog window"}})])}],!1,null,null,null).exports,c={mixins:[n.a],name:"mask-login",data:function(){return{misc:advanced_tools.misc,model:advanced_tools.model.mask_login,nonces:advanced_tools.nonces,endpoints:advanced_tools.endpoints,state:{on_saving:!1,waiting_save:!1}}},watch:{"model.mask_url":function(t){t=this.convertToSlug(t),this.model.mask_url=t,this.misc.new_login_url=this.misc.home_url+t},"model.redirect_traffic_url":function(t){t=this.convertToSlug(t),this.model.redirect_traffic_url=t,this.misc.login_redirect_url=this.misc.home_url+t}},methods:{toggle:function(t){var e=this,s={};s.enabled=t,this.httpPostRequest("updateSettings",{data:JSON.stringify({settings:s,module:"mask-login"})},(function(){e.model.enabled=t}))},updateSettings:function(){var t=this.model;this.httpPostRequest("updateSettings",{data:JSON.stringify({settings:t,module:"mask-login"})})},convertToSlug:function(t){return t.toLowerCase().replace(/[^\w-/]+/g,"")}},computed:{new_mask_login:function(){return this.misc.new_login_url},login_redirect_url:function(){return this.misc.login_redirect_url}}},u=Object(o.a)(c,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return!1===t.model.enabled?s("div",{staticClass:"sui-box"},[s("div",{staticClass:"sui-box-header"},[s("h3",{staticClass:"sui-box-title"},[t._v("\n\t\t\t\t"+t._s(t.__("Mask Login Area"))+"\n\t\t\t")])]),t._v(" "),s("div",{staticClass:"sui-message"},[t.maybeHideBranding()?t._e():s("img",{staticClass:"sui-image",attrs:{src:t.assetUrl("assets/img/2factor-disabled.svg"),"aria-hidden":"true"}}),t._v(" "),s("div",{staticClass:"sui-message-content"},[s("p",[t._v("\n\t\t\t\t\t"+t._s(t.__("Change the location of WordPress's default login area, making it harder for automated bots to find and also more convenient for your users."))+"\n\t\t\t\t")]),t._v(" "),s("form",{attrs:{method:"post"}},[s("submit-button",{attrs:{type:"button","css-class":"sui-button-blue",state:t.state},on:{click:function(e){return t.toggle(!0)}}},[t._v("\n\t\t\t\t\t\t"+t._s(t.__("Active"))+"\n\t\t\t\t\t")])],1)])])]):s("div",{staticClass:"sui-box"},[s("div",{staticClass:"sui-box-header"},[s("h3",{staticClass:"sui-box-title"},[t._v("\n\t\t\t\t"+t._s(t.__("Mask Login Area"))+"\n\t\t\t")])]),t._v(" "),s("form",{attrs:{method:"post"},on:{submit:function(e){return e.preventDefault(),t.updateSettings(e)}}},[s("div",{staticClass:"sui-box-body"},[s("p",[t._v("\n\t\t\t\t\t"+t._s(t.__("Change your default WordPress login URL to hide your login area from hackers and bots."))+"\n\t\t\t\t")]),t._v(" "),!1!==t.misc.compatibility?s("div",{staticClass:"sui-notice sui-notice-error"},[s("p",t._l(t.misc.compatibility,(function(e){return s("span",[t._v("\n "+t._s(e)+"\n ")])})),0)]):t._e(),t._v(" "),0===t.model.mask_url.length?s("div",{staticClass:"sui-notice sui-notice-warning"},[s("p",[t._v("\n\t\t\t\t\t\t"+t._s(t.__("Masking is currently inactive. Choose your URL and save your settings to finish setup."))+"\n\t\t\t\t\t")])]):s("div",{staticClass:"sui-notice sui-notice-info"},[s("p",[t._v("\n\t\t\t\t\t\t"+t._s(t.__("Masking is currently active at "))+" "),s("strong",{domProps:{textContent:t._s(t.misc.new_login_url)}})])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Masking URL"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Choose the new URL slug where users of your website will now navigate to log in or register."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("You can specify any URLs. For security reasons, less obvious URLs are recommended as they are harder for bots to guess."))+"\n ")]),t._v(" "),s("div",{staticClass:"sui-form-field"},[s("label",{staticClass:"sui-label"},[t._v("\n\t\t\t\t\t\t\t\t"+t._s(t.__("New Login URL"))+"\n\t\t\t\t\t\t\t")]),t._v(" "),s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.mask_url,expression:"model.mask_url"}],staticClass:"sui-form-control",attrs:{type:"text",name:"mask_url",placeholder:"E.g. dashboard"},domProps:{value:t.model.mask_url},on:{input:function(e){e.target.composing||t.$set(t.model,"mask_url",e.target.value)}}}),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Users will login at"))+" "),s("a",{attrs:{href:t.new_mask_login}},[t._v(t._s(t.new_mask_login))]),t._v(". "+t._s(t.__("Note: Registration and Password Reset emails have hardcoded URLs in them. We will update them automatically to match your new login URL"))+"\n ")])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Redirect traffic"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("With this feature you can send visitors and bots who try to visit the default Wordpress login URLs to a separate URL to avoid 404s."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("label",{staticClass:"sui-toggle"},[s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.redirect_traffic,expression:"model.redirect_traffic"}],staticClass:"toggle-checkbox",attrs:{role:"presentation",type:"checkbox",name:"redirect_traffic",id:"redirect_traffic","true-value":!0,"false-value":!1},domProps:{checked:Array.isArray(t.model.redirect_traffic)?t._i(t.model.redirect_traffic,null)>-1:t.model.redirect_traffic},on:{change:function(e){var s=t.model.redirect_traffic,i=e.target,a=!!i.checked;if(Array.isArray(s)){var n=t._i(s,null);i.checked?n<0&&t.$set(t.model,"redirect_traffic",s.concat([null])):n>-1&&t.$set(t.model,"redirect_traffic",s.slice(0,n).concat(s.slice(n+1)))}else t.$set(t.model,"redirect_traffic",a)}}}),t._v(" "),s("span",{staticClass:"sui-toggle-slider"})]),t._v(" "),s("label",{staticClass:"sui-toggle-label",attrs:{for:"redirect_traffic"}},[t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Enable 404 redirection"))+"\n\t\t\t\t\t\t")]),t._v(" "),s("div",{directives:[{name:"show",rawName:"v-show",value:!0===t.model.redirect_traffic,expression:"model.redirect_traffic===true"}],staticClass:"sui-border-frame sui-toggle-content",attrs:{id:"redirectTrafficContainer"}},[s("label",{staticClass:"sui-label"},[t._v(t._s(t.__("Redirection URL")))]),t._v(" "),s("input",{directives:[{name:"model",rawName:"v-model",value:t.model.redirect_traffic_url,expression:"model.redirect_traffic_url"}],staticClass:"sui-form-control",attrs:{placeholder:"E.g. 404-error",type:"text",name:"redirect_traffic_url"},domProps:{value:t.model.redirect_traffic_url},on:{input:function(e){e.target.composing||t.$set(t.model,"redirect_traffic_url",e.target.value)}}}),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Visitors who visit the default login URLs will be redirected to"))+" "),s("a",{attrs:{href:t.login_redirect_url}},[t._v(t._s(t.login_redirect_url))])])])])]),t._v(" "),s("div",{staticClass:"sui-box-settings-row"},[s("div",{staticClass:"sui-box-settings-col-1"},[s("span",{staticClass:"sui-settings-label"},[t._v("\n "+t._s(t.__("Deactivate"))+"\n ")]),t._v(" "),s("span",{staticClass:"sui-description"},[t._v("\n "+t._s(t.__("Disable login area masking and return to the default wp-admin and wp-login URLS."))+"\n ")])]),t._v(" "),s("div",{staticClass:"sui-box-settings-col-2"},[s("submit-button",{attrs:{type:"button","css-class":"sui-button-ghost",state:t.state},on:{click:function(e){return t.toggle(!1)}}},[t._v("\n\t\t\t\t\t\t\t"+t._s(t.__("Deactivate"))+"\n\t\t\t\t\t\t")])],1)])]),t._v(" "),s("div",{staticClass:"sui-box-footer"},[s("submit-button",{attrs:{type:"submit",state:t.state,"css-class":"sui-button-blue"}},[s("i",{staticClass:"sui-icon-save",attrs:{"aria-hidden":"true"}}),t._v("\n\t\t\t\t\t"+t._s(t.__("Save Changes"))+"\n\t\t\t\t")])],1)])])}),[],!1,null,null,null).exports,d={mixins:[n.a],components:{"two-factors":l,"mask-login":u},data:function(){return{state:{on_saving:!1},whitelabel:defender.whitelabel,is_free:defender.is_free,view:""}},created:function(){var t=new URLSearchParams(window.location.search).get("view");null===t&&(t="two-factor-auth"),this.view=t},watch:{view:function(t,e){history.replaceState({},null,this.adminUrl()+"admin.php?page=wdf-advanced-tools&view="+this.view)}},mounted:function(){self=this,jQuery(".sui-mobile-nav").change((function(){self.view=jQuery(this).val()}))}},f=Object(o.a)(d,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"sui-wrap",class:[t.maybeHighContrast()]},[s("div",{staticClass:"advanced-tools"},[s("div",{staticClass:"sui-header"},[s("h1",{staticClass:"sui-header-title"},[t._v(t._s(t.__("Advanced Tools")))]),t._v(" "),s("doc-link",{attrs:{link:"https://premium.wpmudev.org/docs/wpmu-dev-plugins/defender/#advanced-tools"}})],1),t._v(" "),s("div",{staticClass:"sui-row-with-sidenav"},[s("div",{staticClass:"sui-sidenav"},[s("ul",{staticClass:"sui-vertical-tabs sui-sidenav-hide-md"},[s("li",{staticClass:"sui-vertical-tab",class:{current:"two-factor-auth"===t.view}},[s("a",{attrs:{"data-tab":"login_lockout",href:"#2factor"},on:{click:function(e){e.preventDefault(),t.view="two-factor-auth"}}},[t._v(t._s(t.__("Two-Factor Auth")))])]),t._v(" "),s("li",{staticClass:"sui-vertical-tab",class:{current:"mask-login"===t.view}},[s("a",{attrs:{"data-tab":"notfound_lockout",href:"#mask-login"},on:{click:function(e){e.preventDefault(),t.view="mask-login"}}},[t._v(t._s(t.__("Mask Login Area")))])])]),t._v(" "),s("div",{staticClass:"sui-sidenav-hide-lg"},[s("select",{staticClass:"sui-mobile-nav"},[s("option",{attrs:{value:"two-factor-auth"}},[t._v(t._s(t.__("Two-Factor Auth")))]),t._v(" "),s("option",{attrs:{value:"mask-login"}},[t._v(t._s(t.__("Mask Login Area")))])])])]),t._v(" "),s("two-factors",{directives:[{name:"show",rawName:"v-show",value:"two-factor-auth"===t.view,expression:"view==='two-factor-auth'"}]}),t._v(" "),s("mask-login",{directives:[{name:"show",rawName:"v-show",value:"mask-login"===t.view,expression:"view==='mask-login'"}]})],1)]),t._v(" "),s("app-footer")],1)}),[],!1,null,null,null).exports,_=s("./src/component/submit-button.vue"),p=s("./src/component/footer.vue"),m=s("./src/component/doc-link.vue");a.a.component("app-footer",p.a),a.a.component("doc-link",m.a),a.a.component("submit-button",_.a);new a.a({el:"#defender",components:{advanced_tools:f},render:function(t){return t(f)}})},"./src/component/doc-link.vue":function(t,e,s){"use strict";var i={mixins:[s("./src/helper/base_hepler.js").a],name:"doc-link",props:["link"],data:function(){return{whitelabel:defender.whitelabel}}},a=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),n=Object(a.a)(i,(function(){var t=this.$createElement,e=this._self._c||t;return!1===this.whitelabel.hide_doc_link?e("div",{staticClass:"sui-actions-right"},[e("a",{staticClass:"sui-button sui-button-ghost",attrs:{href:this.link,target:"_blank"}},[e("i",{staticClass:"sui-icon-academy"}),this._v(" "+this._s(this.__("View Documentation"))+"\n ")])]):this._e()}),[],!1,null,null,null);e.a=n.exports},"./src/component/footer.vue":function(t,e,s){"use strict";var i={data:function(){return{whitelabel:defender.whitelabel,is_free:defender.is_free}}},a=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),n=Object(a.a)(i,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",[!0===t.whitelabel.change_footer?s("div",{staticClass:"sui-footer"},[t._v("\n "+t._s(t.whitelabel.footer_text)+"\n ")]):s("div",{staticClass:"sui-footer"},[t._v("Made with "),s("i",{staticClass:"sui-icon-heart"}),t._v(" by WPMU DEV")]),t._v(" "),!1===t.whitelabel.hide_doc_link?s("div",[t.is_free?s("ul",{staticClass:"sui-footer-nav"},[t._m(0),t._v(" "),t._m(1),t._v(" "),t._m(2),t._v(" "),t._m(3),t._v(" "),t._m(4),t._v(" "),t._m(5),t._v(" "),t._m(6),t._v(" "),t._m(7)]):s("ul",{staticClass:"sui-footer-nav"},[t._m(8),t._v(" "),t._m(9),t._v(" "),t._m(10),t._v(" "),t._m(11),t._v(" "),t._m(12),t._v(" "),t._m(13),t._v(" "),t._m(14),t._v(" "),t._m(15)]),t._v(" "),t._m(16)]):t._e()])}),[function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://profiles.wordpress.org/wpmudev#content-plugins",target:"_blank"}},[this._v("Free\n Plugins")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/features/",target:"_blank"}},[this._v("Membership")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/roadmap/",target:"_blank"}},[this._v("Roadmap")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://wordpress.org/support/plugin/plugin-name",target:"_blank"}},[this._v("Support")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/docs/",target:"_blank"}},[this._v("Docs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/hub/",target:"_blank"}},[this._v("The Hub")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/terms-of-service/",target:"_blank"}},[this._v("Terms of Service")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://incsub.com/privacy-policy/",target:"_blank"}},[this._v("Privacy Policy")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/hub/",target:"_blank"}},[this._v("The Hub")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/projects/category/plugins/",target:"_blank"}},[this._v("Plugins")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/roadmap/",target:"_blank"}},[this._v("Roadmap")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/hub/support/",target:"_blank"}},[this._v("Support")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/docs/",target:"_blank"}},[this._v("Docs")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/hub/community/",target:"_blank"}},[this._v("Community")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://premium.wpmudev.org/terms-of-service/",target:"_blank"}},[this._v("Terms of Service")])])},function(){var t=this.$createElement,e=this._self._c||t;return e("li",[e("a",{attrs:{href:"https://incsub.com/privacy-policy/",target:"_blank"}},[this._v("Privacy Policy")])])},function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ul",{staticClass:"sui-footer-social"},[s("li",[s("a",{attrs:{href:"https://www.facebook.com/wpmudev",target:"_blank"}},[s("i",{staticClass:"sui-icon-social-facebook",attrs:{"aria-hidden":"true"}}),t._v(" "),s("span",{staticClass:"sui-screen-reader-text"},[t._v("Facebook")])])]),t._v(" "),s("li",[s("a",{attrs:{href:"https://twitter.com/wpmudev",target:"_blank"}},[s("i",{staticClass:"sui-icon-social-twitter",attrs:{"aria-hidden":"true"}})]),t._v(" "),s("span",{staticClass:"sui-screen-reader-text"},[t._v("Twitter")])]),t._v(" "),s("li",[s("a",{attrs:{href:"https://www.instagram.com/wpmu_dev/",target:"_blank"}},[s("i",{staticClass:"sui-icon-instagram",attrs:{"aria-hidden":"true"}}),t._v(" "),s("span",{staticClass:"sui-screen-reader-text"},[t._v("Instagram")])])])])}],!1,null,null,null);e.a=n.exports},"./src/component/submit-button.vue":function(t,e,s){"use strict";var i={name:"submit-button",props:["id","state","text","css-class","type"],computed:{getClass:function(){return"sui-button "+this.cssClass}}},a=s("./node_modules/vue-loader/lib/runtime/componentNormalizer.js"),n=Object(a.a)(i,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("button",{staticClass:"sui-button",class:[t.getClass,{"sui-button-onload":t.state.on_saving}],attrs:{id:t.id,type:t.type,disabled:t.state.on_saving},on:{click:function(e){return t.$emit("click")}}},[s("span",{staticClass:"sui-loading-text"},[t._t("default")],2),t._v(" "),s("i",{staticClass:"sui-icon-loader sui-loading",attrs:{"aria-hidden":"true"}})])}),[],!1,null,null,null);e.a=n.exports},"./src/helper/base_hepler.js":function(t,e,s){"use strict";var i=s("./node_modules/xss/lib/index.js"),a=s.n(i),n=function(t,e){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return function(t,e){var s=[],i=!0,a=!1,n=void 0;try{for(var r,o=t[Symbol.iterator]();!(i=(r=o.next()).done)&&(s.push(r.value),!e||s.length!==e);i=!0);}catch(t){a=!0,n=t}finally{try{!i&&o.return&&o.return()}finally{if(a)throw n}}return s}(t,e);throw new TypeError("Invalid attempt to destructure non-iterable instance")},r=wp.i18n,o=r.sprintf,l=r.__,c=[];e.a={methods:{__:function(t){return a()(l(t,"wpdef"))},xss:function(t){return a()(t)},vsprintf:function(t){return o.apply(null,arguments)},siteUrl:function(t){return void 0!==t?defender.site_url+t:defender.site_url},adminUrl:function(t){return void 0!==t?