iThemes Security (formerly Better WP Security) - Version 5.0.1

Version Description

  • Compatibility Fix: Added support for ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY. Setting it to true can bypass "SSL peer certificate or SSH remote key was not OK" errors on servers with bad SSL configurations.
Download this release

Release Info

Developer chrisjean
Plugin Icon 128x128 iThemes Security (formerly Better WP Security)
Version 5.0.1
Comparing to
See all releases

Version 5.0.1

Files changed (209) hide show
  1. better-wp-security.php +24 -0
  2. core/class-ithemes-sync-verb-itsec-get-everything.php +69 -0
  3. core/class-ithemes-sync-verb-itsec-get-lockouts.php +42 -0
  4. core/class-ithemes-sync-verb-itsec-get-temp-whitelist.php +17 -0
  5. core/class-ithemes-sync-verb-itsec-release-lockout.php +36 -0
  6. core/class-ithemes-sync-verb-itsec-set-temp-whitelist.php +52 -0
  7. core/class-itsec-core.php +1964 -0
  8. core/class-itsec-dashboard-admin.php +171 -0
  9. core/class-itsec-files.php +593 -0
  10. core/class-itsec-global-settings.php +1417 -0
  11. core/class-itsec-lib.php +980 -0
  12. core/class-itsec-lockout.php +1334 -0
  13. core/class-itsec-logger-all-logs.php +257 -0
  14. core/class-itsec-logger.php +659 -0
  15. core/class-itsec-notify.php +317 -0
  16. core/class-itsec-setup.php +538 -0
  17. core/class-itsec-sync.php +101 -0
  18. core/content/index.php +1 -0
  19. core/content/perms.php +260 -0
  20. core/content/status.php +235 -0
  21. core/content/system.php +323 -0
  22. core/css/index.php +1 -0
  23. core/css/ithemes.css +633 -0
  24. core/css/itsec_notice.css +120 -0
  25. core/history.txt +285 -0
  26. core/img/alert16.png +0 -0
  27. core/img/bb-ad.jpg +0 -0
  28. core/img/check16.png +0 -0
  29. core/img/flag16-blue.png +0 -0
  30. core/img/flag16-red.png +0 -0
  31. core/img/flag16-yellow.png +0 -0
  32. core/img/green-check16.png +0 -0
  33. core/img/index.php +0 -0
  34. core/img/return-to-top.png +0 -0
  35. core/index.php +1 -0
  36. core/js/admin-dashboard-footer.js +3 -0
  37. core/js/admin-dashboard.js +189 -0
  38. core/js/admin-global-settings.js +19 -0
  39. core/js/admin-logs.js +22 -0
  40. core/js/admin-modal.js +76 -0
  41. core/js/admin-whitelist.js +99 -0
  42. core/js/index.php +1 -0
  43. core/js/tracking.js +154 -0
  44. core/js/url.js +93 -0
  45. core/lib/class-itsec-lib-config-file.php +753 -0
  46. core/lib/class-itsec-lib-directory.php +265 -0
  47. core/lib/class-itsec-lib-file.php +368 -0
  48. core/lib/class-itsec-lib-utility.php +178 -0
  49. core/lib/class-itsec-wp-list-table.php +1055 -0
  50. core/lib/index.php +1 -0
  51. core/modules/admin-user/class-itsec-admin-user-admin.php +372 -0
  52. core/modules/admin-user/index.php +1 -0
  53. core/modules/admin-user/js/admin-admin-user.js +15 -0
  54. core/modules/admin-user/js/index.php +1 -0
  55. core/modules/away-mode/class-ithemes-sync-verb-itsec-get-away-mode.php +34 -0
  56. core/modules/away-mode/class-ithemes-sync-verb-itsec-override-away-mode.php +78 -0
  57. core/modules/away-mode/class-itsec-away-mode-admin.php +603 -0
  58. core/modules/away-mode/class-itsec-away-mode.php +268 -0
  59. core/modules/away-mode/css/index.php +1 -0
  60. core/modules/away-mode/css/smoothness/images/animated-overlay.gif +0 -0
  61. core/modules/away-mode/css/smoothness/images/index.php +1 -0
  62. core/modules/away-mode/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  63. core/modules/away-mode/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  64. core/modules/away-mode/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  65. core/modules/away-mode/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  66. core/modules/away-mode/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  67. core/modules/away-mode/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  68. core/modules/away-mode/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  69. core/modules/away-mode/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  70. core/modules/away-mode/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  71. core/modules/away-mode/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  72. core/modules/away-mode/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  73. core/modules/away-mode/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  74. core/modules/away-mode/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  75. core/modules/away-mode/css/smoothness/index.php +1 -0
  76. core/modules/away-mode/css/smoothness/jquery-ui-1.10.4.custom.css +550 -0
  77. core/modules/away-mode/index.php +1 -0
  78. core/modules/away-mode/js/admin-away-mode.js +33 -0
  79. core/modules/away-mode/js/index.php +1 -0
  80. core/modules/away-mode/setup.php +164 -0
  81. core/modules/backup/class-itsec-backup-admin.php +1026 -0
  82. core/modules/backup/class-itsec-backup.php +449 -0
  83. core/modules/backup/css/admin-backup.css +16 -0
  84. core/modules/backup/css/index.php +1 -0
  85. core/modules/backup/css/multi-select.css +94 -0
  86. core/modules/backup/img/index.php +1 -0
  87. core/modules/backup/img/switch.png +0 -0
  88. core/modules/backup/index.php +1 -0
  89. core/modules/backup/js/admin-backup.js +33 -0
  90. core/modules/backup/js/index.php +1 -0
  91. core/modules/backup/js/jquery.multi-select.js +552 -0
  92. core/modules/backup/setup.php +127 -0
  93. core/modules/ban-users/class-itsec-ban-users-admin.php +771 -0
  94. core/modules/ban-users/class-itsec-ban-users.php +140 -0
  95. core/modules/ban-users/index.php +1 -0
  96. core/modules/ban-users/js/admin-ban_users.js +17 -0
  97. core/modules/ban-users/js/index.php +1 -0
  98. core/modules/ban-users/lists/hackrepair-apache.inc +184 -0
  99. core/modules/ban-users/lists/hackrepair-litespeed.inc +184 -0
  100. core/modules/ban-users/lists/hackrepair-nginx.inc +182 -0
  101. core/modules/ban-users/lists/index.php +1 -0
  102. core/modules/ban-users/setup.php +158 -0
  103. core/modules/brute-force/class-itsec-brute-force-admin.php +490 -0
  104. core/modules/brute-force/class-itsec-brute-force-log.php +183 -0
  105. core/modules/brute-force/class-itsec-brute-force.php +270 -0
  106. core/modules/brute-force/index.php +1 -0
  107. core/modules/brute-force/js/admin-brute-force.js +17 -0
  108. core/modules/brute-force/js/index.php +1 -0
  109. core/modules/brute-force/setup.php +121 -0
  110. core/modules/content-directory/class-itsec-content-directory-admin.php +500 -0
  111. core/modules/content-directory/index.php +1 -0
  112. core/modules/content-directory/js/admin-content_directory.js +17 -0
  113. core/modules/content-directory/js/index.php +1 -0
  114. core/modules/core/class-itsec-core-admin.php +390 -0
  115. core/modules/core/img/backupbuddy-logo.png +0 -0
  116. core/modules/core/img/index.php +1 -0
  117. core/modules/core/img/security-ebook.png +0 -0
  118. core/modules/core/img/sync-logo.png +0 -0
  119. core/modules/core/img/video.png +0 -0
  120. core/modules/core/index.php +1 -0
  121. core/modules/core/setup.php +84 -0
  122. core/modules/database-prefix/class-itsec-database-prefix-admin.php +369 -0
  123. core/modules/database-prefix/index.php +1 -0
  124. core/modules/file-change/class-ithemes-sync-verb-itsec-perform-file-scan.php +57 -0
  125. core/modules/file-change/class-itsec-file-change-admin.php +1001 -0
  126. core/modules/file-change/class-itsec-file-change-log.php +255 -0
  127. core/modules/file-change/class-itsec-file-change.php +708 -0
  128. core/modules/file-change/css/admin-file-change.css +92 -0
  129. core/modules/file-change/css/index.php +1 -0
  130. core/modules/file-change/filetree/images/application.png +0 -0
  131. core/modules/file-change/filetree/images/code.png +0 -0
  132. core/modules/file-change/filetree/images/css.png +0 -0
  133. core/modules/file-change/filetree/images/db.png +0 -0
  134. core/modules/file-change/filetree/images/directory.png +0 -0
  135. core/modules/file-change/filetree/images/doc.png +0 -0
  136. core/modules/file-change/filetree/images/file.png +0 -0
  137. core/modules/file-change/filetree/images/film.png +0 -0
  138. core/modules/file-change/filetree/images/flash.png +0 -0
  139. core/modules/file-change/filetree/images/folder_open.png +0 -0
  140. core/modules/file-change/filetree/images/html.png +0 -0
  141. core/modules/file-change/filetree/images/index.php +1 -0
  142. core/modules/file-change/filetree/images/java.png +0 -0
  143. core/modules/file-change/filetree/images/linux.png +0 -0
  144. core/modules/file-change/filetree/images/music.png +0 -0
  145. core/modules/file-change/filetree/images/pdf.png +0 -0
  146. core/modules/file-change/filetree/images/php.png +0 -0
  147. core/modules/file-change/filetree/images/picture.png +0 -0
  148. core/modules/file-change/filetree/images/ppt.png +0 -0
  149. core/modules/file-change/filetree/images/psd.png +0 -0
  150. core/modules/file-change/filetree/images/ruby.png +0 -0
  151. core/modules/file-change/filetree/images/script.png +0 -0
  152. core/modules/file-change/filetree/images/spinner.gif +0 -0
  153. core/modules/file-change/filetree/images/txt.png +0 -0
  154. core/modules/file-change/filetree/images/xls.png +0 -0
  155. core/modules/file-change/filetree/images/zip.png +0 -0
  156. core/modules/file-change/filetree/index.php +1 -0
  157. core/modules/file-change/filetree/jqueryFileTree.css +276 -0
  158. core/modules/file-change/filetree/jqueryFileTree.js +117 -0
  159. core/modules/file-change/images/index.php +1 -0
  160. core/modules/file-change/images/redminus.png +0 -0
  161. core/modules/file-change/index.php +1 -0
  162. core/modules/file-change/js/admin-file-change-warning.js +33 -0
  163. core/modules/file-change/js/admin-file-change.js +120 -0
  164. core/modules/file-change/js/index.php +1 -0
  165. core/modules/file-change/setup.php +181 -0
  166. core/modules/four-oh-four/class-itsec-four-oh-four-admin.php +520 -0
  167. core/modules/four-oh-four/class-itsec-four-oh-four-log.php +233 -0
  168. core/modules/four-oh-four/class-itsec-four-oh-four.php +113 -0
  169. core/modules/four-oh-four/index.php +1 -0
  170. core/modules/four-oh-four/js/admin-four-oh-four.js +22 -0
  171. core/modules/four-oh-four/js/index.php +1 -0
  172. core/modules/four-oh-four/setup.php +142 -0
  173. core/modules/help/class-itsec-help-admin.php +53 -0
  174. core/modules/help/index.php +1 -0
  175. core/modules/hide-backend/class-itsec-hide-backend-admin.php +724 -0
  176. core/modules/hide-backend/class-itsec-hide-backend.php +356 -0
  177. core/modules/hide-backend/index.php +1 -0
  178. core/modules/hide-backend/js/admin-hide-backend.js +27 -0
  179. core/modules/hide-backend/js/index.php +1 -0
  180. core/modules/hide-backend/setup.php +215 -0
  181. core/modules/index.php +1 -0
  182. core/modules/ipcheck/class-itsec-ipcheck-admin.php +448 -0
  183. core/modules/ipcheck/class-itsec-ipcheck.php +394 -0
  184. core/modules/ipcheck/index.php +1 -0
  185. core/modules/ipcheck/js/admin-ipcheck.js +35 -0
  186. core/modules/ipcheck/js/index.php +1 -0
  187. core/modules/ipcheck/setup.php +100 -0
  188. core/modules/malware/class-ithemes-sync-verb-itsec-do-malware-scan.php +17 -0
  189. core/modules/malware/class-ithemes-sync-verb-itsec-get-malware-scan-log.php +36 -0
  190. core/modules/malware/class-itsec-malware-admin.php +218 -0
  191. core/modules/malware/class-itsec-malware-log.php +340 -0
  192. core/modules/malware/class-itsec-malware-scan-results-template.php +178 -0
  193. core/modules/malware/class-itsec-malware-scanner.php +116 -0
  194. core/modules/malware/class-itsec-malware.php +56 -0
  195. core/modules/malware/css/index.php +2 -0
  196. core/modules/malware/css/malware.css +73 -0
  197. core/modules/malware/index.php +1 -0
  198. core/modules/malware/js/index.php +1 -0
  199. core/modules/malware/js/malware.js +94 -0
  200. core/modules/malware/setup.php +97 -0
  201. core/modules/salts/class-itsec-salts-admin.php +358 -0
  202. core/modules/salts/index.php +1 -0
  203. core/modules/salts/setup.php +86 -0
  204. core/modules/ssl/class-itsec-ssl-admin.php +425 -0
  205. core/modules/ssl/class-itsec-ssl.php +139 -0
  206. core/modules/ssl/index.php +1 -0
  207. core/modules/ssl/js/admin-ssl.js +19 -0
  208. core/modules/ssl/js/index.php +1 -0
  209. core/modules/ssl/setup.php +73 -0
better-wp-security.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: iThemes Security
4
+ Plugin URI: https://ithemes.com/security
5
+ Description: Protect your WordPress site by hiding vital areas of your site, protecting access to important files, preventing brute-force login attempts, detecting attack attempts and more.
6
+ Version: 5.0.1
7
+ Text Domain: better-wp-security
8
+ Domain Path: /lang
9
+ Author: iThemes.com
10
+ Author URI: https://ithemes.com
11
+ Network: True
12
+ License: GPLv2
13
+ Copyright 2015 iThemes (email : info@ithemes.com)
14
+ */
15
+
16
+ if ( is_admin() ) {
17
+
18
+ require( dirname( __FILE__ ) . '/lib/icon-fonts/load.php' ); //Loads iThemes fonts
19
+ require( dirname( __FILE__ ) . '/lib/one-version/index.php' ); //Only have one version of the plugin
20
+
21
+ }
22
+
23
+ require_once( dirname( __FILE__ ) . '/core/class-itsec-core.php' );
24
+ new ITSEC_Core( __FILE__, __( 'iThemes Security', 'better-wp-security' ) );
core/class-ithemes-sync-verb-itsec-get-everything.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Get_Everything extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-get-everything';
6
+ public static $description = 'Retrieve iThemes Security Status and other information.';
7
+
8
+ private $default_arguments = array();
9
+
10
+ public function run( $arguments ) {
11
+
12
+ global $itsec_sync;
13
+
14
+ $modules = $itsec_sync->get_modules();
15
+ $module_results = array();
16
+
17
+ //return $modules;
18
+
19
+ foreach ( $modules as $name => $module ) {
20
+
21
+ if ( isset( $module['verbs'] ) && isset( $module['path'] ) && isset( $module['everything'] ) ) {
22
+
23
+ $everything = array();
24
+
25
+ if ( is_array( $module['everything'] ) ) {
26
+
27
+ foreach ( $module['everything'] as $item ) {
28
+
29
+ if ( isset( $module['verbs'][ $item ] ) ) {
30
+ $everything[] = $item;
31
+ }
32
+
33
+ }
34
+
35
+ } elseif ( isset( $module['verbs'][ $module['everything'] ] ) ) {
36
+
37
+ $everything[] = $module['everything'];
38
+
39
+ }
40
+
41
+ foreach ( $everything as $verb ) {
42
+
43
+ $class = $module['verbs'][ $verb ];
44
+
45
+ if ( ! class_exists( $class ) ) {
46
+
47
+ require( trailingslashit( $module['path'] ) . 'class-ithemes-sync-verb-' . $verb . '.php' );
48
+
49
+ }
50
+
51
+ $obj = new $class;
52
+
53
+ $module_results[ $name ][ $verb ] = $obj->run( array() );
54
+
55
+ }
56
+
57
+ }
58
+
59
+ }
60
+
61
+ return array_merge( array(
62
+ 'api' => '1',
63
+ ),
64
+ $module_results
65
+ );
66
+
67
+ }
68
+
69
+ }
core/class-ithemes-sync-verb-itsec-get-lockouts.php ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Get_Lockouts extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-get-lockouts';
6
+ public static $description = 'Retrieve a list of current lockouts in iThemes Security.';
7
+
8
+ public $default_arguments = array();
9
+
10
+ public function run( $arguments ) {
11
+
12
+ global $itsec_lockout;
13
+
14
+ $lockouts = $itsec_lockout->get_lockouts( 'all', true ); //Gets all lockouts, host and user
15
+
16
+ //Send the user name or false
17
+ foreach ( $lockouts as $key => $lockout ) {
18
+
19
+ $userdata = get_userdata( intval( $lockout['lockout_user'] ) );
20
+
21
+ if ( $userdata === false ) {
22
+
23
+ $lockout['lockout_user'] = false;
24
+
25
+ } else {
26
+
27
+ $lockout['lockout_username'] = $userdata->user_login;
28
+
29
+ }
30
+
31
+ $lockouts[$key] = $lockout;
32
+
33
+ }
34
+
35
+ return array(
36
+ 'api' => '0',
37
+ 'lockouts' => $lockouts,
38
+ );
39
+
40
+ }
41
+
42
+ }
core/class-ithemes-sync-verb-itsec-get-temp-whitelist.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Get_Temp_Whitelist extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-get-temp-whitelist';
6
+ public static $description = 'Retrieve and report temporarily whitelisted IP.';
7
+
8
+ public $default_arguments = array(
9
+ );
10
+
11
+ public function run( $arguments ) {
12
+
13
+ return array( 'temp_whitelist' => get_site_option( 'itsec_temp_whitelist_ip' ) );
14
+
15
+ }
16
+
17
+ }
core/class-ithemes-sync-verb-itsec-release-lockout.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Release_Lockout extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-release-lockout';
6
+ public static $description = 'Release a lockout set by iThemes Security.';
7
+
8
+ public $default_arguments = array(
9
+ 'id' => '', //lockout id to release
10
+ );
11
+
12
+ public function run( $arguments ) {
13
+
14
+ global $itsec_lockout;
15
+
16
+ $id = intval( $arguments['id'] );
17
+ $result = $itsec_lockout->release_lockout( $id );
18
+
19
+ if ( $result === false ) {
20
+
21
+ $status = 'error';
22
+
23
+ } else {
24
+
25
+ $status = 'ok';
26
+
27
+ }
28
+
29
+ return array(
30
+ 'api' => '0',
31
+ 'status' => $status
32
+ );
33
+
34
+ }
35
+
36
+ }
core/class-ithemes-sync-verb-itsec-set-temp-whitelist.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Set_Temp_Whitelist extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-set-temp-whitelist';
6
+ public static $description = 'Set temporarily whitelisted IP.';
7
+
8
+ public $default_arguments = array(
9
+ 'direction' => 'add', //whether to "add" or "remove" whitelist
10
+ 'ip' => '' //IP to add or remove
11
+ );
12
+
13
+ public function run( $arguments ) {
14
+
15
+ global $itsec_globals;
16
+
17
+ $direction = isset( $arguments['direction'] ) ? $arguments['direction'] : 'add';
18
+
19
+ if ( $direction === 'add' ) {
20
+
21
+ if ( get_site_option( 'itsec_temp_whitelist_ip' ) !== false || ! isset( $arguments['ip'] ) ) {
22
+ return false;
23
+ }
24
+
25
+ $ip = sanitize_text_field( $arguments['ip'] );
26
+
27
+ if ( ITSEC_Lib::validates_ip_address( $ip ) ) {
28
+
29
+ $response = array(
30
+ 'ip' => $ip,
31
+ 'exp' => $itsec_globals['current_time'] + 86400,
32
+ );
33
+
34
+ add_site_option( 'itsec_temp_whitelist_ip', $response );
35
+
36
+ return true;
37
+
38
+ }
39
+
40
+ } elseif ( $direction === 'remove' ) {
41
+
42
+ delete_site_option( 'itsec_temp_whitelist_ip' );
43
+
44
+ return true;
45
+
46
+ }
47
+
48
+ return false;
49
+
50
+ }
51
+
52
+ }
core/class-itsec-core.php ADDED
@@ -0,0 +1,1964 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iThemes Security Core.
4
+ *
5
+ * Core class for iThemes Security sets up globals and other items and dispatches modules.
6
+ *
7
+ * @package iThemes_Security
8
+ *
9
+ * @since 4.0
10
+ *
11
+ * @global array $itsec_globals Global variables for use throughout iThemes Security.
12
+ * @global object $itsec_files iThemes Security file writer.
13
+ * @global object $itsec_logger iThemes Security logging class.
14
+ * @global object $itsec_lockout Class for handling lockouts.
15
+ *
16
+ */
17
+ if ( ! class_exists( 'ITSEC_Core' ) ) {
18
+
19
+ final class ITSEC_Core {
20
+
21
+ private
22
+ $tooltip_modules,
23
+ $one_click,
24
+ $pages,
25
+ $pro_toc_items,
26
+ $tracking_vars,
27
+ $toc_items;
28
+
29
+ public
30
+ $available_pages;
31
+
32
+ /**
33
+ * Loads core functionality across both admin and frontend.
34
+ *
35
+ * Creates all plugin globals, registers activation and related hooks,
36
+ * loads the text domain and loads all plugin modules
37
+ *
38
+ * @since 4.0
39
+ *
40
+ * @access private
41
+ *
42
+ * @param string $plugin_file the main plugin file
43
+ * @param string @plugin_name The plugin name
44
+ *
45
+ */
46
+ function __construct( $plugin_file, $plugin_name ) {
47
+
48
+ global $itsec_globals, $itsec_files, $itsec_logger, $itsec_lockout, $itsec_notify, $itsec_sync;
49
+
50
+ $this->tooltip_modules = array(); //initialize tooltip modules.
51
+ $this->one_click = array(); //initialize one-click settings
52
+
53
+ if ( false !== ( $upload_dir = get_site_transient( 'itsec_upload_dir' ) ) ) {
54
+ if ( ! is_dir( $upload_dir['basedir'] ) ) {
55
+ unset( $upload_dir );
56
+ }
57
+ }
58
+
59
+ if ( ! isset( $upload_dir ) || ! is_array( $upload_dir ) ) {
60
+
61
+ if ( is_multisite() ) {
62
+
63
+ switch_to_blog( 1 );
64
+ $upload_dir = wp_upload_dir(); //get the full upload directory array so we can grab the base directory.
65
+ restore_current_blog();
66
+
67
+ } else {
68
+
69
+ $upload_dir = wp_upload_dir(); //get the full upload directory array so we can grab the base directory.
70
+
71
+ }
72
+
73
+ set_site_transient( 'itsec_upload_dir', $upload_dir, 86400 );
74
+
75
+ }
76
+
77
+ //Set plugin defaults
78
+ $itsec_globals = array(
79
+ 'plugin_build' => 4038, //plugin build number - used to trigger updates
80
+ 'plugin_access_lvl' => 'manage_options', //Access level required to access plugin options
81
+ 'plugin_name' => sanitize_text_field( $plugin_name ), //the name of the plugin
82
+ 'plugin_base' => str_replace( WP_PLUGIN_DIR . '/', '', $plugin_file ),
83
+ 'plugin_file' => $plugin_file, //the main plugin file
84
+ 'plugin_dir' => dirname( $plugin_file ), //the path of the plugin directory
85
+ 'plugin_url' => plugin_dir_url( $plugin_file ), //the URL of the plugin directory
86
+ 'is_iwp_call' => false,
87
+ 'ithemes_dir' => $upload_dir['basedir'] . '/ithemes-security',
88
+ //folder for saving iThemes Security files
89
+ 'ithemes_log_dir' => $upload_dir['basedir'] . '/ithemes-security/logs',
90
+ //folder for saving iThemes Security logs
91
+ 'ithemes_backup_dir' => $upload_dir['basedir'] . '/ithemes-security/backups',
92
+ //folder for saving iThemes Backup files
93
+ 'current_time' => current_time( 'timestamp' ), //the current local time in unix timestamp format
94
+ 'current_time_gmt' => current_time( 'timestamp', 1 ), //the current gmt time in unix timestamp format
95
+ 'settings' => get_site_option( 'itsec_global' ),
96
+ 'free_modules' => array(
97
+ 'ipcheck' => array(
98
+ 'has_front' => true,
99
+ 'option' => 'itsec_ipcheck',
100
+ 'setting' => 'api_s',
101
+ 'value' => 'present',
102
+ 'class_id' => 'ipcheck',
103
+ ),
104
+ 'four-oh-four' => array(
105
+ 'has_front' => true,
106
+ 'option' => 'itsec_four_oh_four',
107
+ 'setting' => 'enabled',
108
+ 'value' => true,
109
+ 'class_id' => 'Four_Oh_Four',
110
+ ),
111
+ 'admin-user' => array(
112
+ 'has_front' => false,
113
+ 'class_id' => 'Admin_User',
114
+ ),
115
+ 'away-mode' => array(
116
+ 'has_front' => true,
117
+ 'option' => 'itsec_away_mode',
118
+ 'setting' => 'enabled',
119
+ 'value' => true,
120
+ 'class_id' => 'Away_Mode',
121
+ ),
122
+ 'ban-users' => array(
123
+ 'has_front' => true,
124
+ 'option' => 'itsec_global',
125
+ 'setting' => 'blacklist',
126
+ 'value' => true,
127
+ 'class_id' => 'Ban_Users',
128
+ ),
129
+ 'brute-force' => array(
130
+ 'has_front' => true,
131
+ 'option' => 'itsec_brute_force',
132
+ 'setting' => 'enabled',
133
+ 'value' => true,
134
+ 'class_id' => 'Brute_Force',
135
+ ),
136
+ 'backup' => array(
137
+ 'has_front' => true,
138
+ 'option' => 'itsec_backup',
139
+ 'setting' => 'enabled',
140
+ 'value' => true,
141
+ 'class_id' => 'Backup',
142
+ ),
143
+ 'salts' => array(
144
+ 'has_front' => false,
145
+ 'class_id' => 'Salts',
146
+ ),
147
+ 'file-change' => array(
148
+ 'has_front' => true,
149
+ 'option' => 'itsec_file_change',
150
+ 'setting' => 'enabled',
151
+ 'value' => true,
152
+ 'class_id' => 'File_Change',
153
+ ),
154
+ 'hide-backend' => array(
155
+ 'has_front' => true,
156
+ 'option' => 'itsec_hide_backend',
157
+ 'setting' => 'enabled',
158
+ 'value' => true,
159
+ 'class_id' => 'Hide_Backend',
160
+ ),
161
+ 'malware' => array(
162
+ 'has_front' => true,
163
+ 'class_id' => 'Malware',
164
+ ),
165
+ 'ssl' => array(
166
+ 'has_front' => true,
167
+ 'option' => 'itsec_ssl',
168
+ 'setting' => 'frontend',
169
+ 'value' => array( 1, 2 ),
170
+ 'class_id' => 'SSL',
171
+ ),
172
+ 'strong-passwords' => array(
173
+ 'has_front' => true,
174
+ 'option' => 'itsec_strong_passwords',
175
+ 'setting' => 'enabled',
176
+ 'value' => true,
177
+ 'class_id' => 'Strong_Passwords',
178
+ ),
179
+ 'tweaks' => array(
180
+ 'has_front' => true,
181
+ 'class_id' => 'Tweaks',
182
+ ),
183
+ 'content-directory' => array(
184
+ 'has_front' => false,
185
+ 'class_id' => 'Content_Directory',
186
+ ),
187
+ 'database-prefix' => array(
188
+ 'has_front' => false,
189
+ 'class_id' => 'Database_Prefix',
190
+ ),
191
+ 'help' => array(
192
+ 'has_front' => false,
193
+ 'class_id' => 'Help',
194
+ ),
195
+ 'core' => array(
196
+ 'has_front' => false,
197
+ 'class_id' => 'Core',
198
+ ),
199
+ ),
200
+ 'pro_modules' => array(
201
+ 'malware-scheduling' => array(
202
+ 'has_front' => true,
203
+ 'option' => 'itsec_malware_scheduling',
204
+ 'setting' => 'enabled',
205
+ 'value' => true,
206
+ 'class_id' => 'Malware_Scheduling',
207
+ ),
208
+ 'online-files' => array(
209
+ 'has_front' => true,
210
+ 'option' => 'itsec_online_files',
211
+ 'setting' => 'enabled',
212
+ 'value' => true,
213
+ 'class_id' => 'Online_Files',
214
+ ),
215
+ 'privilege' => array(
216
+ 'has_front' => true,
217
+ 'option' => 'itsec_privilege',
218
+ 'setting' => 'enabled',
219
+ 'value' => true,
220
+ 'class_id' => 'Privilege',
221
+ ),
222
+ 'password' => array(
223
+ 'has_front' => true,
224
+ 'option' => 'itsec_password',
225
+ 'setting' => 'enabled',
226
+ 'value' => true,
227
+ 'class_id' => 'Password',
228
+ ),
229
+ 'recaptcha' => array(
230
+ 'has_front' => true,
231
+ 'option' => 'itsec_recaptcha',
232
+ 'setting' => 'enabled',
233
+ 'value' => true,
234
+ 'class_id' => 'Recaptcha',
235
+ ),
236
+ 'settings' => array(
237
+ 'has_front' => false,
238
+ 'class_id' => 'Settings',
239
+ ),
240
+ 'two-factor' => array(
241
+ 'has_front' => true,
242
+ 'option' => 'itsec_two_factor',
243
+ 'setting' => 'enabled',
244
+ 'value' => true,
245
+ 'class_id' => 'Two_Factor',
246
+ ),
247
+ 'user-logging' => array(
248
+ 'has_front' => true,
249
+ 'option' => 'itsec_user_logging',
250
+ 'setting' => 'enabled',
251
+ 'value' => true,
252
+ 'class_id' => 'User_Logging',
253
+ ),
254
+ 'help' => array(
255
+ 'has_front' => false,
256
+ 'class_id' => 'Help',
257
+ ),
258
+ 'core' => array(
259
+ 'has_front' => false,
260
+ 'class_id' => 'Core',
261
+ ),
262
+ 'dashboard-widget' => array(
263
+ 'has_front' => false,
264
+ 'class_id' => 'Dashboard_Widget',
265
+ ),
266
+ 'wp-cli' => array(
267
+ 'has_front' => true,
268
+ 'class_id' => 'WP_ClI',
269
+ ),
270
+ ),
271
+ );
272
+
273
+ $free_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/modules';
274
+ $pro_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'pro';
275
+
276
+ $itsec_globals['has_pro'] = is_dir( $pro_modules_folder );
277
+
278
+ $this->pages = array(
279
+ array(
280
+ 'priority' => 1,
281
+ 'title' => __( 'Settings', 'better-wp-security' ),
282
+ 'slug' => 'settings',
283
+ 'has_tab' => true,
284
+ 'admin_bar' => false,
285
+ ),
286
+ array(
287
+ 'priority' => 5,
288
+ 'title' => __( 'Advanced', 'better-wp-security' ),
289
+ 'slug' => 'advanced',
290
+ 'has_tab' => true,
291
+ 'admin_bar' => false,
292
+ ),
293
+ array(
294
+ 'priority' => 15,
295
+ 'title' => __( 'Logs', 'better-wp-security' ),
296
+ 'slug' => 'logs',
297
+ 'has_tab' => true,
298
+ 'admin_bar' => true,
299
+ ),
300
+ array(
301
+ 'priority' => 20,
302
+ 'title' => __( 'Help', 'better-wp-security' ),
303
+ 'slug' => 'help',
304
+ 'has_tab' => true,
305
+ 'admin_bar' => false,
306
+ ),
307
+ );
308
+
309
+ if ( isset( $itsec_globals['has_pro'] ) && $itsec_globals['has_pro'] === true && sizeof( $itsec_globals['pro_modules'] ) > 2 ) {
310
+
311
+ $this->pages[] = array(
312
+ 'priority' => 8,
313
+ 'title' => __( 'Pro', 'better-wp-security' ),
314
+ 'slug' => 'pro',
315
+ 'has_tab' => true,
316
+ 'admin_bar' => false,
317
+ );
318
+
319
+ } elseif ( ! isset( $itsec_globals['has_pro'] ) || $itsec_globals['has_pro'] !== true ) {
320
+
321
+ $this->pages[] = array(
322
+ 'priority' => 25,
323
+ 'title' => '<span style="color:#2EA2CC">' . __( 'Go Pro', 'better-wp-security' ) . '</span>',
324
+ 'slug' => 'go_pro_link',
325
+ 'external' => true,
326
+ 'has_tab' => false,
327
+ 'admin_bar' => false,
328
+ );
329
+
330
+ }
331
+
332
+ if ( class_exists( 'backupbuddy_api' ) && ! is_multisite() ) {
333
+
334
+ $this->pages[] = array(
335
+ 'priority' => 10,
336
+ 'title' => __( 'Backups', 'better-wp-security' ),
337
+ 'link' => 'pb_backupbuddy_backup',
338
+ 'slug' => 'backups',
339
+ 'has_tab' => true,
340
+ 'admin_bar' => true,
341
+ );
342
+
343
+ } else {
344
+
345
+ $this->pages[] = array(
346
+ 'priority' => 10,
347
+ 'title' => __( 'Backups', 'better-wp-security' ),
348
+ 'slug' => 'backups',
349
+ 'has_tab' => true,
350
+ 'admin_bar' => true,
351
+ );
352
+
353
+ }
354
+
355
+ //Determine if we need to run upgrade scripts
356
+ $plugin_data = get_site_option( 'itsec_data' );
357
+
358
+ if ( $plugin_data === false ) { //if plugin data does exist
359
+ $plugin_data = $this->save_plugin_data();
360
+ }
361
+
362
+ $itsec_globals['data'] = $plugin_data; //adds plugin data to $itsec_globals
363
+
364
+ //Add Javascripts script
365
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
366
+
367
+ //load utility functions
368
+ if ( ! class_exists( 'ITSEC_Lib' ) ) {
369
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-lib.php' );
370
+ }
371
+
372
+ //load logging functions
373
+ if ( ! class_exists( 'ITSEC_Logger' ) ) {
374
+
375
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-logger.php' );
376
+ $itsec_logger = new ITSEC_Logger();
377
+
378
+ }
379
+
380
+ //load lockout functions
381
+ if ( ! class_exists( 'ITSEC_Lockout' ) ) {
382
+
383
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-lockout.php' );
384
+ $itsec_lockout = new ITSEC_Lockout( $this );
385
+
386
+ }
387
+
388
+ //load file utility functions
389
+ if ( ! class_exists( 'ITSEC_Files' ) ) {
390
+
391
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-files.php' );
392
+ $itsec_files = new ITSEC_Files();
393
+
394
+ }
395
+
396
+ //Load itsec notification class
397
+
398
+ if ( ! class_exists( 'ITSEC_Notify' ) ) {
399
+
400
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-notify.php' );
401
+ $itsec_notify = new ITSEC_Notify();
402
+
403
+ }
404
+
405
+ //Load Sync integration
406
+ if ( get_site_option( 'ithemes-sync-authenticated' ) !== false && ! class_exists( 'ITSEC_Sync' ) ) {
407
+
408
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-sync.php' );
409
+ $itsec_sync = new ITSEC_Sync();
410
+
411
+ }
412
+
413
+ $this->load_textdomain();
414
+
415
+ //builds admin menus after modules are loaded
416
+ if ( is_admin() ) {
417
+
418
+ //load logging functions
419
+ if ( ! class_exists( 'ITSEC_Dashboard_Admin' ) ) {
420
+
421
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-dashboard-admin.php' );
422
+ new ITSEC_Dashboard_Admin( $this );
423
+
424
+ }
425
+
426
+ //load logging functions
427
+ if ( ! class_exists( 'ITSEC_Global_Settings' ) ) {
428
+
429
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-global-settings.php' );
430
+ new ITSEC_Global_Settings( $this );
431
+
432
+ }
433
+
434
+ //Process support plugin nag
435
+ add_action( 'itsec_admin_init', array( $this, 'upgrade_nag' ) );
436
+
437
+ //Process plugin api nag
438
+ add_action( 'itsec_admin_init', array( $this, 'api_nag' ) );
439
+
440
+ //add action link
441
+ add_filter( 'plugin_action_links', array( $this, 'add_action_link' ), 10, 2 );
442
+
443
+ //add plugin meta links
444
+ add_filter( 'plugin_row_meta', array( $this, 'add_plugin_meta_links' ), 10, 4 );
445
+
446
+ //Register all plugin modules and register sync
447
+ add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
448
+
449
+ //register one-click tooltip
450
+ add_filter( 'itsec_tooltip_modules', array( $this, 'register_tooltip' ) ); //register tooltip action
451
+
452
+ //Run ajax for tooltips
453
+ add_action( 'wp_ajax_itsec_tooltip_ajax', array( $this, 'admin_tooltip_ajax' ) );
454
+ add_action( 'wp_ajax_itsec_tracking_ajax', array( $this, 'admin_tracking_ajax' ) );
455
+
456
+ $this->build_admin(); //call before function
457
+
458
+ register_activation_hook( $itsec_globals['plugin_file'], array( 'ITSEC_Core', 'on_activate' ) );
459
+ register_deactivation_hook( $itsec_globals['plugin_file'], array( 'ITSEC_Core', 'on_deactivate' ) );
460
+ register_uninstall_hook( $itsec_globals['plugin_file'], array( 'ITSEC_Core', 'on_uninstall' ) );
461
+
462
+ }
463
+
464
+ //Admin bar links
465
+ if ( ! isset( $itsec_globals['settings']['hide_admin_bar'] ) || $itsec_globals['settings']['hide_admin_bar'] === false ) {
466
+ add_action( 'admin_bar_menu', array( $this, 'admin_bar_links' ), 99 );
467
+ }
468
+
469
+ if ( isset( $itsec_globals['settings']['infinitewp_compatibility'] ) && $itsec_globals['settings']['infinitewp_compatibility'] === true ) {
470
+
471
+ $HTTP_RAW_POST_DATA = @file_get_contents( 'php://input' );
472
+
473
+ if ( $HTTP_RAW_POST_DATA !== false && strlen( $HTTP_RAW_POST_DATA ) > 0 ) {
474
+
475
+ $data = base64_decode( $HTTP_RAW_POST_DATA );
476
+
477
+ if ( strpos( $data, 's:10:"iwp_action";' ) !== false ) {
478
+ $itsec_globals['is_iwp_call'] = true;
479
+ }
480
+
481
+ }
482
+
483
+ }
484
+
485
+ //load all present modules
486
+ $this->load_modules( $free_modules_folder, $pro_modules_folder, $itsec_globals['has_pro'] );
487
+
488
+ //see if the saved build version is older than the current build version
489
+ if ( isset( $plugin_data['build'] ) && $plugin_data['build'] !== $itsec_globals['plugin_build'] ) {
490
+ add_action( 'plugins_loaded', array( $this, 'execute_upgrade' ) );
491
+ }
492
+
493
+ //See if they're upgrade from Better WP Security
494
+ if ( is_multisite() && isset( $itsec_globals['settings']['did_upgrade'] ) && $itsec_globals['settings']['did_upgrade'] === true ) {
495
+
496
+ switch_to_blog( 1 );
497
+
498
+ $bwps_options = get_option( 'bit51_bwps' );
499
+
500
+ restore_current_blog();
501
+
502
+ } else {
503
+
504
+ $bwps_options = get_option( 'bit51_bwps' );
505
+
506
+ }
507
+
508
+ if ( $bwps_options !== false ) {
509
+ add_action( 'plugins_loaded', array( $this, 'do_upgrade' ) );
510
+ }
511
+
512
+ add_action( 'itsec_wpconfig_metabox', array( $itsec_files, 'config_metabox_contents' ) );
513
+ add_action( 'itsec_rewrite_metabox', array( $itsec_files, 'rewrite_metabox_contents' ) );
514
+ }
515
+
516
+ /**
517
+ * Load the text translations.
518
+ *
519
+ * The translations are loaded from WP_LANG_DIR/plugins/
520
+ */
521
+ private function load_textdomain() {
522
+ $plugin_dir = dirname( dirname( __FILE__ ) );
523
+
524
+ if ( is_dir( "$plugin_dir/pro" ) ) {
525
+ $plugin_name = 'ithemes-security-pro';
526
+ $domain = 'it-l10n-ithemes-security-pro';
527
+ } else {
528
+ $plugin_name = 'better-wp-security';
529
+ $domain = 'better-wp-security';
530
+ }
531
+
532
+ $locale = apply_filters( 'plugin_locale', get_locale(), 'better-wp-security' );
533
+
534
+ load_textdomain( 'better-wp-security', WP_LANG_DIR . "/plugins/$plugin_name/$domain-$locale.mo" );
535
+ load_plugin_textdomain( 'better-wp-security', false, basename( $plugin_dir ) . '/lang/' );
536
+ }
537
+
538
+ /**
539
+ * Add action link to plugin page.
540
+ *
541
+ * Adds plugin settings link to plugin page in WordPress admin area.
542
+ *
543
+ * @since 4.0
544
+ *
545
+ * @param object $links Array of WordPress links
546
+ * @param string $file String name of current file
547
+ *
548
+ * @return object Array of WordPress links
549
+ *
550
+ */
551
+ function add_action_link( $links, $file ) {
552
+
553
+ static $this_plugin;
554
+
555
+ global $itsec_globals;
556
+
557
+ if ( empty( $this_plugin ) ) {
558
+ $this_plugin = $itsec_globals['plugin_base'];
559
+ }
560
+
561
+ if ( $file == $this_plugin ) {
562
+ $settings_link = '<a href="admin.php?page=itsec">' . __( 'Dashboard', 'better-wp-security' ) . '</a>';
563
+ array_unshift( $links, $settings_link );
564
+ }
565
+
566
+ return $links;
567
+ }
568
+
569
+ /**
570
+ * Adds links to the plugin row meta
571
+ *
572
+ * @since 4.0
573
+ *
574
+ * @param array $meta Existing meta
575
+ * @param string $plugin_file the wp plugin slug (path)
576
+ *
577
+ * @return array
578
+ */
579
+ public function add_plugin_meta_links( $meta, $plugin_file ) {
580
+
581
+ global $itsec_globals;
582
+
583
+ if ( $itsec_globals['plugin_base'] == $plugin_file ) {
584
+
585
+ $meta = apply_filters( 'itsec_meta_links', $meta );
586
+
587
+ }
588
+
589
+ return $meta;
590
+ }
591
+
592
+ /**
593
+ * Add items to the table of contents
594
+ *
595
+ * @since 4.0
596
+ *
597
+ * @param array $item the item to add to the table of content
598
+ *
599
+ * @return void
600
+ */
601
+ public function add_toc_item( $item ) {
602
+
603
+ $this->toc_items[] = $item;
604
+
605
+ }
606
+
607
+ /**
608
+ * Add items to the table of contents
609
+ *
610
+ * @since 4.1
611
+ *
612
+ * @param array $item the item to add to the table of content
613
+ *
614
+ * @return void
615
+ */
616
+ public function add_pro_toc_item( $item ) {
617
+
618
+ $this->pro_toc_items[] = $item;
619
+
620
+ }
621
+
622
+ /**
623
+ * Add admin bar item
624
+ *
625
+ * @since 4.0
626
+ *
627
+ * @return void
628
+ */
629
+ public function admin_bar_links() {
630
+
631
+ global $wp_admin_bar, $itsec_globals;
632
+
633
+ if ( ( ! is_multisite() && ! current_user_can( $itsec_globals['plugin_access_lvl'] ) ) || ( is_multisite() && ! current_user_can( 'manage_network' ) ) ) {
634
+ return;
635
+ }
636
+
637
+ if ( is_multisite() ) {
638
+ $network = 'network/';
639
+ } else {
640
+ $network = '';
641
+ }
642
+
643
+ // Add the Parent link.
644
+ $wp_admin_bar->add_menu(
645
+ array(
646
+ 'title' => __( 'Security', 'better-wp-security' ),
647
+ 'href' => admin_url( $network . 'admin.php?page=itsec' ),
648
+ 'id' => 'itsec_admin_bar_menu',
649
+ )
650
+ );
651
+
652
+ $wp_admin_bar->add_menu(
653
+ array(
654
+ 'id' => 'itsec_admin_bar_dashboard',
655
+ 'title' => __( 'Dashboard', 'better-wp-security' ),
656
+ 'href' => admin_url( $network . 'admin.php?page=itsec' ),
657
+ 'parent' => 'itsec_admin_bar_menu',
658
+ )
659
+ );
660
+
661
+ /**
662
+ * Add the submenu links.
663
+ */
664
+ foreach ( $this->pages as $key => $page ) {
665
+
666
+ if ( isset( $page['admin_bar'] ) && $page['admin_bar'] === true ) {
667
+
668
+ if ( isset( $page['link'] ) ) {
669
+
670
+ $wp_admin_bar->add_menu(
671
+ array(
672
+ 'id' => 'test_' . $page['slug'],
673
+ 'title' => $page['title'],
674
+ 'href' => admin_url( $network . 'admin.php?page=' . $page['link'] ),
675
+ 'parent' => 'itsec_admin_bar_menu',
676
+ )
677
+ );
678
+
679
+ } else {
680
+
681
+ $wp_admin_bar->add_menu(
682
+ array(
683
+ 'id' => 'test_' . $page['slug'],
684
+ 'title' => $page['title'],
685
+ 'href' => admin_url( $network . 'admin.php?page=toplevel_page_itsec_' . $page['slug'] ),
686
+ 'parent' => 'itsec_admin_bar_menu',
687
+ )
688
+ );
689
+
690
+ }
691
+
692
+ }
693
+
694
+ }
695
+
696
+ }
697
+
698
+ /**
699
+ * Process the ajax call for the tooltip.
700
+ *
701
+ * @since 4.0
702
+ *
703
+ * @return void
704
+ */
705
+ public function admin_tooltip_ajax() {
706
+
707
+ global $itsec_globals;
708
+
709
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_tooltip_nonce' ) ) {
710
+ die ();
711
+ }
712
+
713
+ if ( sanitize_text_field( $_POST['module'] ) == 'close' ) {
714
+
715
+ $data = $itsec_globals['data'];
716
+ $data['tooltips_dismissed'] = true;
717
+ update_site_option( 'itsec_data', $data );
718
+
719
+ } else {
720
+
721
+ call_user_func_array( $this->tooltip_modules[ sanitize_text_field( $_POST['module'] ) ]['callback'], array() );
722
+
723
+ }
724
+
725
+ die(); // this is required to return a proper result
726
+
727
+ }
728
+
729
+ /**
730
+ * Process the ajax call for the tracking script.
731
+ *
732
+ * @since 4.0
733
+ *
734
+ * @return void
735
+ */
736
+ public function admin_tracking_ajax() {
737
+
738
+ global $itsec_globals;
739
+
740
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_tracking_nonce' ) ) {
741
+ die ();
742
+ }
743
+
744
+ if ( sanitize_text_field( $_POST['module'] ) == 'close' ) {
745
+
746
+ $data = $itsec_globals['data'];
747
+ $data['tooltips_dismissed'] = true;
748
+ update_site_option( 'itsec_data', $data );
749
+
750
+ } else {
751
+
752
+ call_user_func_array( $this->tooltip_modules[ sanitize_text_field( $_POST['module'] ) ]['callback'], array() );
753
+
754
+ }
755
+
756
+ die(); // this is required to return a proper result
757
+
758
+ }
759
+
760
+ /**
761
+ * Echos intro modal box.
762
+ *
763
+ * @since 4.0
764
+ *
765
+ * @return void
766
+ */
767
+ private function admin_modal() {
768
+
769
+ echo '<ol id="itsec_intro_modal" style="display:none;">';
770
+
771
+ if ( sizeof( $this->tooltip_modules ) > 0 ) {
772
+
773
+ uasort( $this->tooltip_modules, array( $this, 'sort_tooltips' ) );
774
+
775
+ foreach ( $this->tooltip_modules as $module => $tip ) {
776
+
777
+ echo '<li class="tooltip_' . $module . '" id="' . $tip['class'] . '">';
778
+
779
+ if ( isset( $tip['link'] ) ) {
780
+
781
+ echo '<h4>' . $tip['heading'] . '</h4><p>' . $tip['text'] . '</p><a href="' . $tip['link'] . '" class="button-primary">' . $tip['link_text'] . '</a>';
782
+
783
+ } else {
784
+
785
+ echo '<h4>' . $tip['heading'] . '</h4><p>' . $tip['text'] . '</p><a href="' . $module . '" class="itsec_tooltip_ajax button-primary">' . $tip['link_text'] . '</a>';
786
+
787
+ }
788
+
789
+ echo '</li>';
790
+
791
+ }
792
+
793
+ }
794
+
795
+ echo '<a href="javascript:void(0);" class="itsec-intro-close">' . __( 'Dismiss', 'better-wp-security' ) . '</a>';
796
+
797
+ echo '</ol>';
798
+
799
+ }
800
+
801
+ /**
802
+ * Displays plugin admin notices.
803
+ *
804
+ * @since 4.0
805
+ *
806
+ * @return void
807
+ */
808
+ public function admin_notices() {
809
+
810
+ if ( isset( get_current_screen()->id ) && ( strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false || strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_advanced' ) !== false || strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_pro' ) !== false ) ) {
811
+
812
+ $errors = get_settings_errors( 'itsec' );
813
+
814
+ $updated = '';
815
+
816
+ if ( get_site_option( 'itsec_manual_update' ) == true ) {
817
+
818
+ delete_site_option( 'itsec_manual_update' );
819
+
820
+ if ( ITSEC_Lib::get_server() == 'nginx' ) {
821
+
822
+ $server = __( 'NGINX conf file and/or restart your NGINX server', 'better-wp-security' );
823
+
824
+ } else {
825
+
826
+ $server = __( '.htaccess file', 'better-wp-security' );
827
+
828
+ }
829
+
830
+ $updated = sprintf(
831
+ '<br />%s %s %s <a href="%s">%s</a> %s',
832
+ __( 'As you have not allowed this plugin to update system files you must update your', 'better-wp-security' ),
833
+ $server,
834
+ __( 'as well as your wp-config.php file manually. Rules to insert in both files can be found on the Dashboard page.', 'better-wp-security' ),
835
+ '?page=toplevel_page_itsec_settings#itsec_global_write_files',
836
+ __( 'Click here', 'better-wp-security' ),
837
+ __( 'to allow this plugin to write to these files.', 'better-wp-security' )
838
+ );
839
+
840
+ }
841
+
842
+ if ( sizeof( $errors ) === 0 && isset ( $_GET['settings-updated'] ) && sanitize_text_field( $_GET['settings-updated'] ) == 'true' ) {
843
+
844
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), __( 'Settings Updated', 'better-wp-security' ) . $updated, 'updated' );
845
+
846
+ }
847
+
848
+ }
849
+
850
+ settings_errors( 'itsec' );
851
+
852
+ }
853
+
854
+ /**
855
+ * Add Tracking Javascript.
856
+ *
857
+ * Adds javascript for tracking settings to all itsec admin pages
858
+ *
859
+ * @since 4.0
860
+ *
861
+ * @return void
862
+ */
863
+ public function admin_script() {
864
+
865
+ global $itsec_globals;
866
+
867
+ $messages = array();
868
+
869
+ if ( sizeof( $this->tooltip_modules ) > 0 ) {
870
+
871
+ uasort( $this->tooltip_modules, array( $this, 'sort_tooltips' ) );
872
+
873
+ foreach ( $this->tooltip_modules as $module => $tip ) {
874
+
875
+ $messages[ $module ] = array(
876
+ 'success' => $tip['success'],
877
+ 'failure' => $tip['failure'],
878
+ );
879
+
880
+ }
881
+
882
+ }
883
+
884
+ wp_register_style( 'itsec_notice_css', $itsec_globals['plugin_url'] . 'core/css/itsec_notice.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
885
+ wp_enqueue_style( 'itsec_notice_css' );
886
+
887
+ //scripts for all itsec pages
888
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'itsec' ) !== false ) {
889
+
890
+ if ( ( isset( $itsec_globals['settings']['allow_tracking'] ) && $itsec_globals['settings']['allow_tracking'] === true && strpos( get_current_screen()->id, 'itsec' ) !== false ) || get_option( 'bit51_bwps' ) !== false ) {
891
+
892
+ wp_enqueue_script( 'itsec_tracking', $itsec_globals['plugin_url'] . 'core/js/tracking.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
893
+ wp_localize_script( 'itsec_tracking', 'itsec_tracking_vars', array(
894
+ 'vars' => $this->tracking_vars,
895
+ 'nonce' => wp_create_nonce( 'itsec_tracking_nonce' )
896
+ ) );
897
+
898
+ }
899
+
900
+ wp_enqueue_script( 'itsec_global_settings_js', $itsec_globals['plugin_url'] . 'core/js/admin-global-settings.js', array( 'jquery' ) );
901
+ wp_localize_script( 'itsec_global_settings_js', 'itsec_global_settings', array(
902
+ 'location' => $itsec_globals['ithemes_log_dir'],
903
+ ) );
904
+
905
+ wp_enqueue_script( 'jquery-ui-tabs' );
906
+ wp_enqueue_script( 'jquery-ui-dialog' );
907
+ wp_enqueue_style( 'jquery-ui-tabs' );
908
+ wp_enqueue_style( 'wp-jquery-ui-dialog' );
909
+ wp_enqueue_script( 'itsec_dashboard_js', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard.js', array( 'jquery' ) );
910
+ wp_localize_script( 'itsec_dashboard_js', 'itsec_dashboard', array(
911
+ 'text' => __( 'Show Intro', 'better-wp-security' ),
912
+ ) );
913
+ wp_enqueue_script( 'itsec_footer', $itsec_globals['plugin_url'] . 'core/js/admin-dashboard-footer.js', array( 'jquery' ), $itsec_globals['plugin_build'], true );
914
+
915
+ if ( ! isset( $itsec_globals['data']['tooltips_dismissed'] ) || $itsec_globals['data']['tooltips_dismissed'] === false || ( isset( $_GET['show_admin_modal'] ) && $_GET['show_admin_modal'] == 'true' ) ) {
916
+
917
+ wp_enqueue_script( 'itsec_modal', $itsec_globals['plugin_url'] . 'core/js/admin-modal.js', array( 'jquery' ), $itsec_globals['plugin_build'], true );
918
+ wp_localize_script( 'itsec_modal', 'itsec_tooltip_text', array(
919
+ 'nonce' => wp_create_nonce( 'itsec_tooltip_nonce' ),
920
+ 'messages' => $messages,
921
+ 'title' => __( 'Important First Steps', 'better-wp-security' ),
922
+ ) );
923
+
924
+ }
925
+
926
+ }
927
+
928
+ }
929
+
930
+ /**
931
+ * Creates admin tabs.
932
+ *
933
+ * Used to display module tabs across all iThemes Security admin pages.
934
+ *
935
+ * @since 4.0
936
+ *
937
+ * @param string $current current tab id
938
+ *
939
+ * @return void
940
+ */
941
+ public function admin_tabs( $current = null ) {
942
+
943
+ if ( $current == null ) {
944
+ $current = 'itsec';
945
+ }
946
+
947
+ echo '<div id="icon-themes" class="icon32"><br></div>';
948
+ echo '<h2 class="nav-tab-wrapper">';
949
+
950
+ $class = ( $current == 'itsec' ) ? ' nav-tab-active' : '';
951
+ echo '<a class="nav-tab' . $class . '" href="?page=itsec">' . __( 'Dashboard', 'better-wp-security' ) . '</a>';
952
+
953
+ foreach ( $this->pages as $page ) {
954
+
955
+ if ( $page['has_tab'] === true ) {
956
+
957
+ if ( isset( $page['link'] ) ) {
958
+ $link = $page['link'];
959
+ } else {
960
+ $link = 'toplevel_page_itsec_' . $page['slug'];
961
+ }
962
+
963
+ $class = ( $current == 'toplevel_page_itsec_' . $page['slug'] ) ? ' nav-tab-active' : '';
964
+ echo '<a class="nav-tab' . $class . '" href="?page=' . $link . '">' . $page['title'] . '</a>';
965
+
966
+ }
967
+
968
+ }
969
+
970
+ echo '</h2>';
971
+
972
+ }
973
+
974
+ /**
975
+ * Display (and hide) api reminder.
976
+ *
977
+ * This will display a notice to the admin of the site asking them to activate an iThemes API key (if they haven't already)
978
+ *
979
+ * @since 4.4
980
+ *
981
+ * @return void
982
+ */
983
+ public function api_nag() {
984
+
985
+ global $blog_id;
986
+
987
+ if ( is_multisite() && ( $blog_id != 1 || ! current_user_can( 'manage_network_options' ) ) ) { //only display to network admin if in multisite
988
+ return;
989
+ }
990
+
991
+ //display the notifcation if they haven't turned it off and they've been using the plugin at least 30 days
992
+ if ( get_site_option( 'itsec_api_nag' ) !== false ) {
993
+
994
+ if ( ! function_exists( 'ithemes_plugin_api_notice' ) ) {
995
+
996
+ function ithemes_plugin_api_notice() {
997
+
998
+ global $itsec_globals;
999
+
1000
+ echo '<div class="updated" id="itsec_api_notice"><span class="it-icon-itsec"></span>'
1001
+ . __( 'New! Take your site security to the next level by activating iThemes Brute Force Network Protection.', 'better-wp-security' ) . '<a class="itsec-notice-button" onclick="document.location.href=\'?itsec_no_api_nag=off&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">' . __( 'Get Free API Key', 'better-wp-security' ) . '</a><a class="itsec-notice-hide" onclick="document.location.href=\'?itsec_no_api_nag=on&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">&times;</a>
1002
+ </div>';
1003
+
1004
+ }
1005
+
1006
+ }
1007
+
1008
+ if ( is_multisite() ) {
1009
+ add_action( 'network_admin_notices', 'ithemes_plugin_api_notice' ); //register notification
1010
+ } else {
1011
+ add_action( 'admin_notices', 'ithemes_plugin_api_notice' ); //register notification
1012
+ }
1013
+
1014
+ }
1015
+
1016
+ //if they've clicked a button hide the notice
1017
+ if ( isset( $_GET['itsec_no_api_nag'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'itsec-nag' ) ) {
1018
+
1019
+ delete_site_option( 'itsec_api_nag' );
1020
+
1021
+ if ( is_multisite() ) {
1022
+ remove_action( 'network_admin_notices', 'ithemes_plugin_api_notice' );
1023
+ } else {
1024
+ remove_action( 'admin_notices', 'ithemes_plugin_api_notice' );
1025
+ }
1026
+
1027
+ if ( sanitize_text_field( $_GET['itsec_no_api_nag'] ) == 'on' && isset( $_SERVER['HTTP_REFERER'] ) ) {
1028
+
1029
+ wp_redirect( $_SERVER['HTTP_REFERER'], '302' );
1030
+
1031
+ } else {
1032
+
1033
+ wp_redirect( 'admin.php?page=toplevel_page_itsec_settings#itsec_ipcheck_email', '302' );
1034
+
1035
+ }
1036
+
1037
+ }
1038
+
1039
+ }
1040
+
1041
+ /**
1042
+ * Enqueue actions to build the admin pages.
1043
+ *
1044
+ * Calls all the needed actions to build any given admin page.
1045
+ *
1046
+ * @since 4.0
1047
+ *
1048
+ * @return void
1049
+ */
1050
+ public function build_admin() {
1051
+
1052
+ add_action( 'admin_notices', array( $this, 'admin_notices' ) );
1053
+
1054
+ add_action( 'admin_init', array( $this, 'execute_admin_init' ) );
1055
+
1056
+ if ( is_multisite() ) { //must be network admin in multisite
1057
+ add_action( 'network_admin_menu', array( $this, 'setup_primary_admin' ) );
1058
+ } else {
1059
+ add_action( 'admin_menu', array( $this, 'setup_primary_admin' ) );
1060
+ }
1061
+
1062
+ }
1063
+
1064
+ /**
1065
+ * Prints out all settings sections added to a particular settings page.
1066
+ *
1067
+ * adapted from core function for better styling within meta_box.
1068
+ *
1069
+ * @since 4.0
1070
+ *
1071
+ * @param string $page The slug name of the page whos settings sections you want to output
1072
+ * @param string $section the section to show
1073
+ * @param boolean $show_title Whether or not the title of the section should display: default true.
1074
+ *
1075
+ * @return void
1076
+ */
1077
+ public function do_settings_section( $page, $section, $show_title = true ) {
1078
+
1079
+ global $wp_settings_sections, $wp_settings_fields;
1080
+
1081
+ if ( ! isset( $wp_settings_sections ) || ! isset( $wp_settings_sections[ $page ] ) || ! isset( $wp_settings_sections[ $page ][ $section ] ) ) {
1082
+ return;
1083
+ }
1084
+
1085
+ $section = $wp_settings_sections[ $page ][ $section ];
1086
+
1087
+ if ( $section['title'] && $show_title === true ) {
1088
+ echo "<h4>{$section['title']}</h4>\n";
1089
+ }
1090
+
1091
+ if ( $section['callback'] ) {
1092
+ call_user_func( $section['callback'], $section );
1093
+ }
1094
+
1095
+ if ( ! isset( $wp_settings_fields ) || ! isset( $wp_settings_fields[ $page ] ) || ! isset( $wp_settings_fields[ $page ][ $section['id'] ] ) ) {
1096
+ return;
1097
+ }
1098
+
1099
+ echo '<table class="form-table" id="' . $section['id'] . '">';
1100
+ do_settings_fields( $page, $section['id'] );
1101
+ echo '</table>';
1102
+
1103
+ }
1104
+
1105
+ /**
1106
+ * Calls upgrade script for older versions (pre 4.x).
1107
+ *
1108
+ * @since 4.0
1109
+ *
1110
+ * @return void
1111
+ */
1112
+ public function do_upgrade() {
1113
+
1114
+ global $itsec_globals;
1115
+
1116
+ //require plugin setup information
1117
+ if ( ! class_exists( 'ITSEC_Setup' ) ) {
1118
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-setup.php' );
1119
+ }
1120
+
1121
+ new ITSEC_Setup( 'upgrade', 3064 ); //run upgrade scripts
1122
+
1123
+ }
1124
+
1125
+ /**
1126
+ * Enqueue the styles for the admin area so WordPress can load them.
1127
+ *
1128
+ * @since 4.0
1129
+ *
1130
+ * @return void
1131
+ */
1132
+ public function enqueue_admin_styles() {
1133
+
1134
+ global $itsec_globals;
1135
+
1136
+ wp_enqueue_style( 'itsec_admin_styles' );
1137
+ do_action( $itsec_globals['plugin_url'] . 'enqueue_admin_styles' );
1138
+
1139
+ }
1140
+
1141
+ /**
1142
+ * Registers admin styles and handles other items required at admin_init
1143
+ *
1144
+ * @since 4.0
1145
+ *
1146
+ * @return void
1147
+ */
1148
+ public function execute_admin_init() {
1149
+
1150
+ global $itsec_globals;
1151
+
1152
+ if ( current_user_can( $itsec_globals['plugin_access_lvl'] ) ) {
1153
+
1154
+ wp_register_style( 'itsec_admin_styles', $itsec_globals['plugin_url'] . 'core/css/ithemes.css', array(), $itsec_globals['plugin_build'] );
1155
+ do_action( 'itsec_admin_init' ); //execute modules init scripts
1156
+
1157
+ }
1158
+
1159
+ if ( isset( $_GET['page'] ) && $_GET['page'] === 'toplevel_page_itsec_go_pro_link' ) {
1160
+
1161
+ wp_redirect( 'https://ithemes.com/security/?utm_source=wordpressadmin&utm_medium=wpmenu&utm_campaign=itsecfreecta', 301 );
1162
+ exit();
1163
+
1164
+ }
1165
+
1166
+ }
1167
+
1168
+ /**
1169
+ * Execute upgrade for version after 4.0
1170
+ *
1171
+ * @since 4.0.6
1172
+ *
1173
+ * @return void
1174
+ */
1175
+ public function execute_upgrade() {
1176
+
1177
+ global $itsec_globals;
1178
+
1179
+ //require plugin setup information
1180
+ if ( ! class_exists( 'ITSEC_Setup' ) ) {
1181
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-setup.php' );
1182
+ }
1183
+
1184
+ new ITSEC_Setup( 'upgrade', $itsec_globals['data']['build'] ); //run upgrade scripts
1185
+
1186
+ $modules = $itsec_globals['free_modules'];
1187
+ $has_pro = $itsec_globals['has_pro'];
1188
+ $free_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/modules';
1189
+ $pro_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'pro';
1190
+
1191
+ if ( $has_pro ) {
1192
+
1193
+ $modules = array_merge( $modules, $itsec_globals['pro_modules'] );
1194
+
1195
+ }
1196
+
1197
+ foreach ( $modules as $module => $info ) {
1198
+
1199
+ if ( $has_pro === false || ! array_key_exists( $module, $itsec_globals['pro_modules'] ) ) { //don't duplicate module if pro version already loaded
1200
+
1201
+ $setup_file = $pro_modules_folder . '/' . $module . '/setup.php';
1202
+
1203
+ } else {
1204
+
1205
+ $setup_file = $free_modules_folder . '/' . $module . '/setup.php';
1206
+
1207
+ }
1208
+
1209
+ if ( file_exists( $setup_file ) ) {
1210
+
1211
+ $setup_class = 'ITSEC_' . $info['class_id'] . '_SETUP';
1212
+
1213
+ if ( ! class_exists( $setup_class ) ) {
1214
+ require( $setup_file );
1215
+ }
1216
+
1217
+ new $setup_class;
1218
+
1219
+ }
1220
+
1221
+ }
1222
+
1223
+ }
1224
+
1225
+ /**
1226
+ * Getter for Table of Contents items.
1227
+ *
1228
+ * @since 4.0
1229
+ *
1230
+ * @return mixed array of toc items
1231
+ */
1232
+ public function get_toc_items() {
1233
+
1234
+ //Make sure global settings are in toc
1235
+ $global_toc = array(
1236
+ 'id' => 'global_options',
1237
+ 'title' => 'Global Settings',
1238
+ );
1239
+
1240
+ array_unshift( $this->toc_items, $global_toc );
1241
+
1242
+ return $this->toc_items;
1243
+
1244
+ }
1245
+
1246
+ /**
1247
+ * Loads required plugin modules.
1248
+ *
1249
+ *
1250
+ * Recursively loads all modules in the modules/ folder by calling their index.php.
1251
+ * Note: Do not modify this area other than to specify modules to load.
1252
+ * Build all functionality into the appropriate module.
1253
+ *
1254
+ * @since 4.0
1255
+ *
1256
+ * @param string $free_modules_folder location of free modules
1257
+ * @param string $pro_modules_folder location of pro modules
1258
+ * @param bool $has_pro whether or not pro is present
1259
+ *
1260
+ * @return void
1261
+ */
1262
+ public function load_modules( $free_modules_folder, $pro_modules_folder, $has_pro ) {
1263
+
1264
+ global $itsec_globals;
1265
+
1266
+ $modules = $itsec_globals['free_modules'];
1267
+
1268
+ if ( $has_pro ) {
1269
+
1270
+ $modules = array_merge( $modules, $itsec_globals['pro_modules'] );
1271
+
1272
+ }
1273
+
1274
+ foreach ( $modules as $module => $info ) {
1275
+
1276
+ if ( $has_pro === false || ! array_key_exists( $module, $itsec_globals['pro_modules'] ) ) { //don't duplicate module if pro version already loaded
1277
+
1278
+ $this->module_loader( $free_modules_folder, $module, $info );
1279
+
1280
+ } else {
1281
+
1282
+ $this->module_loader( $pro_modules_folder, $module, $info );
1283
+
1284
+ }
1285
+
1286
+ }
1287
+
1288
+ }
1289
+
1290
+ /**
1291
+ * Actually does the module loading
1292
+ *
1293
+ * @since 4.0.27
1294
+ *
1295
+ * @param string $module_folder the location of the module
1296
+ * @param string $module the name of the module
1297
+ * @param $info array of module info for loading
1298
+ *
1299
+ * @return void
1300
+ */
1301
+ private function module_loader( $module_folder, $module, $info ) {
1302
+
1303
+ $run_front = false;
1304
+ $run_admin = false;
1305
+ $front = null;
1306
+
1307
+ if ( is_admin() ) {
1308
+
1309
+ $run_admin = true;
1310
+
1311
+ }
1312
+
1313
+ //Front end class loading
1314
+ if ( isset( $info['has_front'] ) && $info['has_front'] === true ) {
1315
+
1316
+ $option = isset( $info['option'] ) ? get_site_option( $info['option'] ) : false;
1317
+
1318
+ //If there is a setting to check and it is write then load it
1319
+ if (
1320
+ isset( $info['setting'] ) &&
1321
+ ! is_array( $info['value'] ) &&
1322
+ isset( $option[ $info['setting'] ] ) &&
1323
+ (
1324
+ $info['value'] === 'present' ||
1325
+ $option[ $info['setting'] ] == $info['value']
1326
+ )
1327
+ ) {
1328
+
1329
+ $run_front = true;
1330
+
1331
+ //check an array of settings
1332
+ } elseif ( isset( $info['setting'] ) && is_array( $info['value'] ) ) {
1333
+
1334
+ foreach ( $info['value'] as $value ) {
1335
+
1336
+ if ( isset( $option[ $info['setting'] ] ) && $option[ $info['setting'] ] == $value ) {
1337
+
1338
+ $run_front = true;
1339
+
1340
+ }
1341
+
1342
+ }
1343
+
1344
+ //Always load front-end class, not setting dependent
1345
+ } elseif ( ! isset( $info['setting'] ) ) {
1346
+
1347
+ $run_front = true;
1348
+
1349
+ }
1350
+
1351
+ }
1352
+
1353
+ if ( $run_front === true ) { //load the front end class
1354
+
1355
+ $front_file = $module_folder . '/' . $module . '/class-itsec-' . $module . '.php';
1356
+
1357
+ if ( file_exists( $front_file ) ) {
1358
+
1359
+ $front_class = 'ITSEC_' . $info['class_id'];
1360
+
1361
+ if ( ! class_exists( $front_class ) ) {
1362
+ require( $front_file );
1363
+ }
1364
+
1365
+ $front = new $front_class( $this );
1366
+
1367
+ if ( method_exists( $front, 'run' ) ) {
1368
+ $front->run( $this );
1369
+ }
1370
+
1371
+ }
1372
+
1373
+ }
1374
+
1375
+ if ( $run_admin === true ) { //load the admin class
1376
+
1377
+ $admin_file = $module_folder . '/' . $module . '/class-itsec-' . $module . '-admin.php';
1378
+
1379
+ if ( file_exists( $admin_file ) ) {
1380
+
1381
+ //Always load the admin class
1382
+ $admin_class = 'ITSEC_' . $info['class_id'] . '_Admin';
1383
+
1384
+ if ( ! class_exists( $admin_class ) ) {
1385
+ require( $admin_file );
1386
+ }
1387
+
1388
+ $admin = new $admin_class;
1389
+
1390
+ if ( method_exists( $admin, 'run' ) ) {
1391
+ $admin->run( $this );
1392
+ }
1393
+
1394
+ }
1395
+
1396
+ }
1397
+
1398
+ }
1399
+
1400
+ /**
1401
+ * Call activation script
1402
+ *
1403
+ * @since 4.5
1404
+ *
1405
+ * @return void
1406
+ */
1407
+ public static function on_activate() {
1408
+
1409
+ global $itsec_globals;
1410
+
1411
+ //require plugin setup information
1412
+ if ( ! class_exists( 'ITSEC_Setup' ) ) {
1413
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-setup.php' );
1414
+ }
1415
+
1416
+ ITSEC_Setup::on_activate();
1417
+
1418
+ }
1419
+
1420
+ /**
1421
+ * Call deactivation script
1422
+ *
1423
+ * @since 4.5
1424
+ *
1425
+ * @return void
1426
+ */
1427
+ public static function on_deactivate() {
1428
+
1429
+ global $itsec_globals;
1430
+
1431
+ //require plugin setup information
1432
+ if ( ! class_exists( 'ITSEC_Setup' ) ) {
1433
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-setup.php' );
1434
+ }
1435
+
1436
+ ITSEC_Setup::on_deactivate();
1437
+
1438
+ }
1439
+
1440
+ /**
1441
+ * Call uninstall script
1442
+ *
1443
+ * @since 4.5
1444
+ *
1445
+ * @return void
1446
+ */
1447
+ public static function on_uninstall() {
1448
+
1449
+ global $itsec_globals;
1450
+
1451
+ //require plugin setup information
1452
+ if ( ! class_exists( 'ITSEC_Setup' ) ) {
1453
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-setup.php' );
1454
+ }
1455
+
1456
+ ITSEC_Setup::on_uninstall();
1457
+
1458
+ }
1459
+
1460
+ /**
1461
+ * Enqueue JavaScripts for admin page rendering amd execute calls to add further meta_boxes.
1462
+ *
1463
+ * @since 4.0
1464
+ *
1465
+ * @return void
1466
+ */
1467
+ public function page_actions() {
1468
+
1469
+ do_action( 'itsec_add_admin_meta_boxes', $this->available_pages );
1470
+
1471
+ //Set two columns for all plugins using this framework
1472
+ add_screen_option( 'layout_columns', array( 'max' => 2, 'default' => 2 ) );
1473
+
1474
+ //Enqueue common scripts and try to keep it simple
1475
+ wp_enqueue_script( 'common' );
1476
+ wp_enqueue_script( 'wp-lists' );
1477
+ wp_enqueue_script( 'postbox' );
1478
+
1479
+ }
1480
+
1481
+ /**
1482
+ * Prints network admin notices.
1483
+ *
1484
+ * @since 4.0
1485
+ *
1486
+ * @return void
1487
+ */
1488
+ public function print_network_admin_notice() {
1489
+
1490
+ global $itsec_saved_network_notices;
1491
+
1492
+ echo $itsec_saved_network_notices;
1493
+
1494
+ unset( $itsec_saved_network_notices ); //delete any saved messages
1495
+
1496
+ }
1497
+
1498
+ /**
1499
+ * Register modules that will use the lockout service
1500
+ *
1501
+ * @return void
1502
+ */
1503
+ public function register_modules() {
1504
+
1505
+ $this->tooltip_modules = apply_filters( 'itsec_tooltip_modules', $this->tooltip_modules );
1506
+ $this->tracking_vars = apply_filters( 'itsec_tracking_vars', $this->tracking_vars );
1507
+ $this->one_click = apply_filters( 'itsec_one_click_settings', $this->one_click );
1508
+ $this->pages = apply_filters( 'itsec_pages', $this->pages );
1509
+
1510
+ uasort( $this->pages, array( $this, 'sort_pages' ) );
1511
+
1512
+ }
1513
+
1514
+ /**
1515
+ * Register backups for tooltips
1516
+ *
1517
+ * @param array $tooltip_modules array of tooltip modules
1518
+ *
1519
+ * @return array array of tooltip modules
1520
+ */
1521
+ public function register_tooltip( $tooltip_modules ) {
1522
+
1523
+ $tooltip_modules['one-click'] = array(
1524
+ 'priority' => 1,
1525
+ 'class' => 'itsec_tooltip_one-click',
1526
+ 'heading' => __( 'Secure Your Site', 'better-wp-security' ),
1527
+ 'text' => __( 'Use the button below to enable default settings. This feature will enable all settings that cannot conflict with other plugins or themes.', 'better-wp-security' ),
1528
+ 'link_text' => __( 'One-Click Secure', 'better-wp-security' ),
1529
+ 'callback' => array( $this, 'tooltip_ajax' ),
1530
+ 'success' => __( 'Site Secured. Check the dashboard for further suggestions on securing your site.', 'better-wp-security' ),
1531
+ 'failure' => __( 'Whoops. Something went wrong. Please contact support if the problem persists.', 'better-wp-security' ),
1532
+ );
1533
+
1534
+ return $tooltip_modules;
1535
+
1536
+ }
1537
+
1538
+ /**
1539
+ * Render basic structure of the settings page.
1540
+ *
1541
+ * @since 4.0
1542
+ *
1543
+ * @return void
1544
+ */
1545
+ public function render_page() {
1546
+
1547
+ global $itsec_globals;
1548
+
1549
+ if ( is_multisite() ) {
1550
+ $screen = substr( get_current_screen()->id, 0, strpos( get_current_screen()->id, '-network' ) );
1551
+ } else {
1552
+ $screen = get_current_screen()->id; //the current screen id
1553
+ }
1554
+
1555
+ ?>
1556
+
1557
+ <div class="wrap">
1558
+
1559
+ <?php
1560
+ if ( ! isset( $itsec_globals['data']['tooltips_dismissed'] ) || $itsec_globals['data']['tooltips_dismissed'] === false || ( isset( $_GET['show_admin_modal'] ) && $_GET['show_admin_modal'] == 'true' ) ) {
1561
+ $this->admin_modal();
1562
+ }
1563
+ ?>
1564
+
1565
+ <h2><?php echo $itsec_globals['plugin_name'] . ' - ' . get_admin_page_title(); ?></h2>
1566
+ <?php
1567
+ if ( isset ( $_GET['page'] ) ) {
1568
+ $this->admin_tabs( $_GET['page'] );
1569
+ } else {
1570
+ $this->admin_tabs();
1571
+ }
1572
+ ?>
1573
+
1574
+ <?php
1575
+ wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
1576
+ wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
1577
+ ?>
1578
+
1579
+ <div id="poststuff">
1580
+
1581
+ <?php
1582
+ //set appropriate action for multisite or standard site
1583
+ if ( is_multisite() ) {
1584
+ $action = '';
1585
+ } else {
1586
+ $action = 'options.php';
1587
+ }
1588
+ ?>
1589
+ <?php if ($screen == 'security_page_toplevel_page_itsec_settings' || $screen == 'security_page_toplevel_page_itsec_pro') { ?>
1590
+ <form name="<?php echo $screen; ?>" method="post"
1591
+ action="<?php echo $action; ?>" class="itsec-settings-form">
1592
+ <?php } ?>
1593
+
1594
+ <div id="post-body"
1595
+ class="metabox-holder columns-<?php echo 1 == get_current_screen()->get_columns() ? '1' : '2'; ?>">
1596
+
1597
+ <div id="postbox-container-2" class="postbox-container">
1598
+ <?php do_action( 'itsec_page_top', $screen ); ?>
1599
+ <?php do_meta_boxes( $screen, 'top', null ); ?>
1600
+ <?php do_meta_boxes( $screen, 'normal', null ); ?>
1601
+ <?php do_action( 'itsec_page_middle', $screen ); ?>
1602
+ <?php do_meta_boxes( $screen, 'advanced', null ); ?>
1603
+ <?php do_meta_boxes( $screen, 'bottom', null ); ?>
1604
+ <?php do_action( 'itsec_page_bottom', $screen ); ?>
1605
+ </div>
1606
+
1607
+ <div id="postbox-container-1" class="postbox-container">
1608
+ <?php do_meta_boxes( $screen, 'priority_side', null ); ?>
1609
+ <?php do_meta_boxes( $screen, 'side', null ); ?>
1610
+ <?php if ( $screen == 'security_page_toplevel_page_itsec_settings' || $screen == 'security_page_toplevel_page_itsec_pro' ) { ?>
1611
+ <a href="#"
1612
+ class="itsec_return_to_top"><?php _e( 'Return to top', 'better-wp-security' ); ?></a>
1613
+ <?php } ?>
1614
+ </div>
1615
+
1616
+ </div>
1617
+
1618
+ <?php if ($screen == 'security_page_toplevel_page_itsec_settings' || $screen == 'security_page_toplevel_page_itsec_pro') { ?>
1619
+ </form>
1620
+ <?php } ?>
1621
+ <!-- #post-body -->
1622
+
1623
+ </div>
1624
+ <!-- #poststuff -->
1625
+
1626
+ </div><!-- .wrap -->
1627
+
1628
+ <?php
1629
+ }
1630
+
1631
+ /**
1632
+ * Saves general plugin data to determine global items.
1633
+ *
1634
+ * Sets up general plugin data such as build, and others.
1635
+ *
1636
+ * @since 4.0
1637
+ *
1638
+ * @return array plugin data
1639
+ */
1640
+ public function save_plugin_data() {
1641
+
1642
+ global $itsec_globals;
1643
+
1644
+ $save_data = false; //flag to avoid saving data if we don't have to
1645
+
1646
+ $plugin_data = get_site_option( 'itsec_data' );
1647
+
1648
+ //Update the build number if we need to
1649
+ if ( ! isset( $plugin_data['build'] ) || ( isset( $plugin_data['build'] ) && $plugin_data['build'] !== $itsec_globals['plugin_build'] ) ) {
1650
+ $plugin_data['build'] = $itsec_globals['plugin_build'];
1651
+ $save_data = true;
1652
+ }
1653
+
1654
+ //update the activated time if we need to in order to tell when the plugin was installed
1655
+ if ( ! isset( $plugin_data['activation_timestamp'] ) ) {
1656
+ $plugin_data['activation_timestamp'] = $itsec_globals['current_time_gmt'];
1657
+ $save_data = true;
1658
+ }
1659
+
1660
+ //update the activated time if we need to in order to tell when the plugin was installed
1661
+ if ( ! isset( $plugin_data['already_supported'] ) ) {
1662
+ $plugin_data['already_supported'] = false;
1663
+ $save_data = true;
1664
+ }
1665
+
1666
+ //update the activated time if we need to in order to tell when the plugin was installed
1667
+ if ( ! isset( $plugin_data['setup_completed'] ) ) {
1668
+ $plugin_data['setup_completed'] = false;
1669
+ $save_data = true;
1670
+ }
1671
+
1672
+ //update the tooltips dismissed
1673
+ if ( ! isset( $plugin_data['tooltips_dismissed'] ) ) {
1674
+ $plugin_data['tooltips_dismissed'] = false;
1675
+ $save_data = true;
1676
+ }
1677
+
1678
+ //update the options table if we have to
1679
+ if ( $save_data === true ) {
1680
+ update_site_option( 'itsec_data', $plugin_data );
1681
+ }
1682
+
1683
+ return $plugin_data;
1684
+
1685
+ }
1686
+
1687
+ /**
1688
+ * Handles the building of admin menus and calls required functions to render admin pages.
1689
+ *
1690
+ * @since 4.0
1691
+ *
1692
+ * @return void
1693
+ */
1694
+ public function setup_primary_admin() {
1695
+
1696
+ global $itsec_globals;
1697
+
1698
+ $this->available_pages[] = add_menu_page(
1699
+ __( 'Dashboard', 'better-wp-security' ),
1700
+ __( 'Security', 'better-wp-security' ),
1701
+ $itsec_globals['plugin_access_lvl'],
1702
+ 'itsec',
1703
+ array( $this, 'render_page' )
1704
+ );
1705
+
1706
+ foreach ( $this->pages as $page ) {
1707
+
1708
+ if ( isset( $page['link'] ) ) {
1709
+
1710
+ $this->available_pages[] = add_submenu_page(
1711
+ 'itsec',
1712
+ $page['title'],
1713
+ $page['title'],
1714
+ $itsec_globals['plugin_access_lvl'],
1715
+ $page['link'],
1716
+ '__return_empty_string'
1717
+ );
1718
+
1719
+ } else {
1720
+
1721
+ $this->available_pages[] = add_submenu_page(
1722
+ 'itsec',
1723
+ $page['title'],
1724
+ $page['title'],
1725
+ $itsec_globals['plugin_access_lvl'],
1726
+ $this->available_pages[0] . '_' . $page['slug'],
1727
+ array( $this, 'render_page' )
1728
+ );
1729
+
1730
+ }
1731
+
1732
+ }
1733
+
1734
+ //Make the dashboard is named correctly
1735
+ global $submenu;
1736
+
1737
+ if ( isset( $submenu['itsec'] ) ) {
1738
+ $submenu['itsec'][0][0] = __( 'Dashboard', 'better-wp-security' );
1739
+ }
1740
+
1741
+ foreach ( $this->available_pages as $page ) {
1742
+
1743
+ add_action( 'load-' . $page, array( $this, 'page_actions' ) ); //Load page structure
1744
+ add_action( 'admin_print_styles-' . $page, array( $this, 'enqueue_admin_styles' ) ); //Load admin styles
1745
+
1746
+ }
1747
+
1748
+ }
1749
+
1750
+ /**
1751
+ * Setup and call admin messages.
1752
+ *
1753
+ * Sets up messages and registers actions for WordPress admin messages.
1754
+ *
1755
+ * @since 4.0
1756
+ *
1757
+ * @param object $messages WordPress error object or string of message to display
1758
+ *
1759
+ * @return void
1760
+ */
1761
+ public function show_network_admin_notice( $errors ) {
1762
+
1763
+ global $itsec_saved_network_notices; //use global to transfer to add_action callback
1764
+
1765
+ $itsec_saved_network_notices = ''; //initialize so we can get multiple error messages (if needed)
1766
+
1767
+ if ( function_exists( 'apc_store' ) ) {
1768
+ apc_clear_cache(); //Let's clear APC (if it exists) when big stuff is saved.
1769
+ }
1770
+
1771
+ if ( ( ! defined( 'DOING_AJAX' ) || DOING_AJAX == false ) && function_exists( 'get_current_screen' ) && isset( get_current_screen()->id ) && ( strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false || strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_advanced' ) !== false || strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_pro' ) !== false ) ) {
1772
+
1773
+ if ( $errors === false && isset ( $_GET['settings-updated'] ) && sanitize_text_field( $_GET['settings-updated'] ) == 'true' ) {
1774
+
1775
+ $updated = '';
1776
+
1777
+ if ( get_site_option( 'itsec_manual_update' ) == true ) {
1778
+
1779
+ delete_site_option( 'itsec_manual_update' );
1780
+
1781
+ if ( ITSEC_Lib::get_server() == 'nginx' ) {
1782
+
1783
+ $server = __( 'NGINX conf file and/or restart your NGINX server', 'better-wp-security' );
1784
+
1785
+ } else {
1786
+
1787
+ $server = __( '.htaccess file', 'better-wp-security' );
1788
+
1789
+ }
1790
+
1791
+ $updated = sprintf(
1792
+ '<br />%s %s %s',
1793
+ __( 'As you have not allowed this plugin to update system files you must update your', 'better-wp-security' ),
1794
+ $server,
1795
+ __( 'as well as your wp-config.php file manually. Rules to insert in both files can be found on the Dashboard page.', 'better-wp-security' )
1796
+ );
1797
+
1798
+ }
1799
+
1800
+ $itsec_saved_network_notices = '<div id="setting-error-settings_updated" class="updated settings-error"><p><strong>' . __( 'Settings Updated', 'better-wp-security' ) . $updated . '</strong></p></div>';
1801
+
1802
+ } elseif ( is_wp_error( $errors ) ) { //see if object is even an error
1803
+
1804
+ $error_messages = $errors->get_error_messages(); //get all errors if it is
1805
+
1806
+ $type = key( $errors->errors );
1807
+
1808
+ foreach ( $error_messages as $error ) {
1809
+
1810
+ $itsec_saved_network_notices .= '<div id="setting-error-settings_updated" class="' . sanitize_text_field( $type ) . ' settings-error"><p><strong>' . sanitize_text_field( $error ) . '</strong></p></div>';
1811
+ }
1812
+
1813
+ }
1814
+
1815
+ //register appropriate message actions
1816
+ add_action( 'admin_notices', array( $this, 'print_network_admin_notice' ) );
1817
+ add_action( 'network_admin_notices', array( $this, 'print_network_admin_notice' ) );
1818
+
1819
+ }
1820
+
1821
+ }
1822
+
1823
+ /**
1824
+ * Sorts pages from lowest priority to highest.
1825
+ *
1826
+ * @since 4.0
1827
+ *
1828
+ * @param array $a page
1829
+ * @param array $b page
1830
+ *
1831
+ * @return int 1 if a is a lower priority, -1 if b is a lower priority, 0 if equal
1832
+ */
1833
+ public function sort_pages( $a, $b ) {
1834
+
1835
+ if ( $a['priority'] == $b['priority'] ) {
1836
+ return 0;
1837
+ }
1838
+
1839
+ return ( $a['priority'] > $b['priority'] ? 1 : - 1 );
1840
+
1841
+ }
1842
+
1843
+ /**
1844
+ * Sorts tooltips from highest priority to lowest.
1845
+ *
1846
+ * @since 4.0
1847
+ *
1848
+ * @param array $a tooltip
1849
+ * @param array $b tooltip
1850
+ *
1851
+ * @return int 1 if a is a lower priority, -1 if b is a lower priority, 0 if equal
1852
+ */
1853
+ public function sort_tooltips( $a, $b ) {
1854
+
1855
+ if ( $a['priority'] == $b['priority'] ) {
1856
+ return 0;
1857
+ }
1858
+
1859
+ return ( $a['priority'] < $b['priority'] ? 1 : - 1 );
1860
+
1861
+ }
1862
+
1863
+ /**
1864
+ * Performs actions for tooltip function.
1865
+ *
1866
+ * @since 4.0
1867
+ *
1868
+ * return void
1869
+ */
1870
+ public function tooltip_ajax() {
1871
+
1872
+ foreach ( $this->one_click as $setting => $option_pair ) {
1873
+
1874
+ $saved_setting = get_site_option( $setting );
1875
+
1876
+ foreach ( $option_pair as $option ) {
1877
+ $saved_setting[ $option['option'] ] = $option['value'];
1878
+ }
1879
+
1880
+ update_site_option( $setting, $saved_setting );
1881
+
1882
+ }
1883
+
1884
+ echo 'true';
1885
+
1886
+ }
1887
+
1888
+ /**
1889
+ * Display (and hide) upgrade reminder.
1890
+ *
1891
+ * This will display a notice to the admin of the site only asking them to support
1892
+ * the plugin after they have used it for 30 days.
1893
+ *
1894
+ * @since 4.0
1895
+ *
1896
+ * @return void
1897
+ */
1898
+ public function upgrade_nag() {
1899
+
1900
+ global $blog_id;
1901
+
1902
+ if ( is_multisite() && ( $blog_id != 1 || ! current_user_can( 'manage_network_options' ) ) ) { //only display to network admin if in multisite
1903
+ return;
1904
+ }
1905
+
1906
+ //display the notifcation if they haven't turned it off and they've been using the plugin at least 30 days
1907
+ if ( get_site_option( 'itsec_had_other_version' ) !== false ) {
1908
+
1909
+ if ( ! function_exists( 'ithemes_plugin_upgrade_notice' ) ) {
1910
+
1911
+ function ithemes_plugin_upgrade_notice() {
1912
+
1913
+ global $itsec_globals;
1914
+
1915
+ echo '<div class="updated" id="itsec_upgrade_notice">
1916
+ <a class="itsec-notice-hide" onclick="document.location.href=\'?itsec_no_upgrade_nag=off&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">&times;</a><span class="itsec_notice_text">' . __( 'Thank you for activating', 'better-wp-security' ) . ' ' . $itsec_globals['plugin_name'] . '. ' . __( 'It looks like you had another version of this plugin activated. To avoid conflicts the extra version has been deactivated and we recommend you delete it.', 'better-wp-security' ) . '</span>
1917
+ </div>';
1918
+
1919
+ }
1920
+
1921
+ }
1922
+
1923
+ if ( is_multisite() ) {
1924
+ add_action( 'network_admin_notices', 'ithemes_plugin_upgrade_notice' ); //register notification
1925
+ } else {
1926
+ add_action( 'admin_notices', 'ithemes_plugin_upgrade_notice' ); //register notification
1927
+ }
1928
+
1929
+ }
1930
+
1931
+ //if they've clicked a button hide the notice
1932
+ if ( isset( $_GET['itsec_no_upgrade_nag'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'itsec-nag' ) ) {
1933
+
1934
+ delete_site_option( 'itsec_had_other_version' );
1935
+
1936
+ $options = get_site_option( 'itsec_data' );
1937
+
1938
+ $options['already_supported'] = true;
1939
+
1940
+ update_site_option( 'itsec_data', $options );
1941
+
1942
+ if ( is_multisite() ) {
1943
+ remove_action( 'network_admin_notices', 'ithemes_plugin_upgrade_notice' );
1944
+ } else {
1945
+ remove_action( 'admin_notices', 'ithemes_plugin_upgrade_notice' );
1946
+ }
1947
+
1948
+ if ( sanitize_text_field( $_GET['itsec_no_upgrade_nag'] ) == 'off' && isset( $_SERVER['HTTP_REFERER'] ) ) {
1949
+
1950
+ wp_redirect( $_SERVER['HTTP_REFERER'], '302' );
1951
+
1952
+ } else {
1953
+
1954
+ wp_redirect( 'admin.php?page=itsec', '302' );
1955
+
1956
+ }
1957
+
1958
+ }
1959
+
1960
+ }
1961
+
1962
+ }
1963
+
1964
+ }
core/class-itsec-dashboard-admin.php ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Display the plugin's dashboard information.
4
+ *
5
+ * Sets up and displays the dashboard status, file permissions and other system
6
+ * information on the plugin's dashboard.
7
+ *
8
+ * @since 4.0.0
9
+ *
10
+ * @package iThemes_Security
11
+ */
12
+ class ITSEC_Dashboard_Admin {
13
+
14
+ /**
15
+ * Initialize the plugin dashboard
16
+ *
17
+ * Initialize areas of the plugin dashboard.
18
+ *
19
+ * @since 4.0.0
20
+ *
21
+ * @return ITSEC_Dashboard_Admin
22
+ */
23
+ function __construct() {
24
+
25
+ if ( is_admin() ) {
26
+
27
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'itsec_add_admin_meta_boxes' ) );
28
+
29
+ }
30
+
31
+ }
32
+
33
+ /**
34
+ * Add meta boxes to the plugin dashboard.
35
+ *
36
+ * Adds plugin's metaboxes including status, system information and file
37
+ * permissions to the plugin dashboard.
38
+ *
39
+ * @since 4.0.0
40
+ *
41
+ * @return void
42
+ */
43
+ public function itsec_add_admin_meta_boxes() {
44
+
45
+ //System status shows which plugin features have been activated
46
+ add_meta_box(
47
+ 'itsec_status',
48
+ __( 'Security Status', 'better-wp-security' ),
49
+ array( $this, 'metabox_normal_status' ),
50
+ 'toplevel_page_itsec',
51
+ 'advanced',
52
+ 'core'
53
+ );
54
+
55
+ add_meta_box(
56
+ 'itsec_file_permissions',
57
+ __( 'WordPress File Permissions', 'better-wp-security' ),
58
+ array( $this, 'metabox_normal_file_permissions' ),
59
+ 'toplevel_page_itsec',
60
+ 'advanced',
61
+ 'core'
62
+ );
63
+
64
+ add_meta_box(
65
+ 'itsec_system_info',
66
+ __( 'System Information', 'better-wp-security' ),
67
+ array( $this, 'metabox_normal_system_info' ),
68
+ 'toplevel_page_itsec',
69
+ 'advanced',
70
+ 'core'
71
+ );
72
+
73
+ }
74
+
75
+ /**
76
+ * Display the file permissions metabox.
77
+ *
78
+ * Builds and displays the table that shows WordPress file permissions as marked up
79
+ * in the system.php file.
80
+ *
81
+ * @since 4.0.0
82
+ *
83
+ * @return void
84
+ */
85
+ public function metabox_normal_file_permissions() {
86
+
87
+ require_once( 'content/perms.php' );
88
+
89
+ }
90
+
91
+ /**
92
+ * Display security status.
93
+ *
94
+ * Builds and displays the table showing the security status as determined
95
+ * by which features have been configured.
96
+ *
97
+ * @since 4.0.0
98
+ *
99
+ * @return void
100
+ */
101
+ public function metabox_normal_status() {
102
+
103
+ require_once( 'content/status.php' );
104
+
105
+ }
106
+
107
+ /**
108
+ * Display the system information metabox.
109
+ *
110
+ * Builds and displays the table that shows system infmormation as marked up
111
+ * in the system.php file.
112
+ *
113
+ * @since 4.0.0
114
+ *
115
+ * @return void
116
+ */
117
+ public function metabox_normal_system_info() {
118
+
119
+ require_once( 'content/system.php' );
120
+
121
+ }
122
+
123
+ /**
124
+ * Displays required status array.
125
+ *
126
+ * Loops through the filterable status array to build the table items for the
127
+ * security status metabox.
128
+ *
129
+ * @since 4.0.0
130
+ *
131
+ * @param array $status_array array of statuses
132
+ * @param string $button_text string for button
133
+ * @param string $button_class string for button
134
+ *
135
+ * @return void
136
+ */
137
+ private function status_loop( $status_array, $button_text, $button_class ) {
138
+
139
+ foreach ( $status_array as $status ) {
140
+
141
+ if ( isset( $status['advanced'] ) && true === $status['advanced'] ) {
142
+
143
+ $page = 'advanced';
144
+
145
+ } elseif ( isset( $status['pro'] ) && true === $status['pro'] ) {
146
+
147
+ $page = 'pro';
148
+
149
+ } else {
150
+
151
+ $page = 'settings';
152
+
153
+ }
154
+
155
+ if ( false === strpos( $status['link'], 'http:' ) && false === strpos( $status['link'], '?page=' ) ) {
156
+
157
+ $setting_link = '?page=toplevel_page_itsec_' . $page . $status['link'];
158
+
159
+ } else {
160
+
161
+ $setting_link = $status['link'];
162
+
163
+ }
164
+
165
+ printf( '<li><p>%s</p><div class="itsec_status_action"><a class="button-%s" href="%s">%s</a></div></li>', $status['text'], $button_class, $setting_link, $button_text );
166
+
167
+ }
168
+
169
+ }
170
+
171
+ }
core/class-itsec-files.php ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * iThemes file handler.
5
+ *
6
+ * Writes to core files including wp-config.php, htaccess and nginx.conf.
7
+ *
8
+ * @package iThemes_Security
9
+ *
10
+ * @since 4.0.0
11
+ */
12
+ final class ITSEC_Files {
13
+
14
+ /**
15
+ * The module's that have registered with the file writer
16
+ *
17
+ * @since 4.0.0
18
+ * @access private
19
+ * @var array
20
+ */
21
+ private $file_modules;
22
+
23
+ /**
24
+ * The current rewrite rules
25
+ *
26
+ * @since 4.0.0
27
+ * @access private
28
+ * @var array
29
+ */
30
+ private $rewrite_rules;
31
+
32
+ /**
33
+ * The current wp-config.php rules
34
+ *
35
+ * @since 4.0.0
36
+ * @access private
37
+ * @var array
38
+ */
39
+ private $wpconfig_rules;
40
+
41
+ /**
42
+ * Whether or not rewrite rules have been modified externally
43
+ *
44
+ * @since 4.0.0
45
+ * @access private
46
+ * @var bool
47
+ */
48
+ private $rewrites_changed;
49
+
50
+ /**
51
+ * Whether or not wp-config.php rules have been modified externally
52
+ *
53
+ * @since 4.0.0
54
+ * @access private
55
+ * @var bool
56
+ */
57
+ private $config_changed;
58
+
59
+ /**
60
+ * Create and manage wp_config.php or .htaccess/nginx rewrites.
61
+ *
62
+ * Executes primary file actions at plugins_loaded.
63
+ *
64
+ * @since 4.0.0
65
+ *
66
+ * @return ITSEC_Files
67
+ */
68
+ public function __construct() {
69
+
70
+ $this->rewrites_changed = false;
71
+ $this->config_changed = false;
72
+ $this->rewrite_rules = array();
73
+ $this->wpconfig_rules = array();
74
+
75
+ //Add the metabox
76
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) );
77
+ add_action( 'plugins_loaded', array( $this, 'file_writer_init' ) );
78
+ add_action( 'admin_init', array( $this, 'admin_init' ) );
79
+
80
+
81
+ add_filter( 'itsec_filter_can_write_to_files', array( $this, 'can_write_to_files' ) );
82
+ }
83
+
84
+ /**
85
+ * Check the setting that allows writing files.
86
+ *
87
+ * @since 1.15.0
88
+ *
89
+ * @return bool True if files can be written to, false otherwise.
90
+ */
91
+ public function can_write_to_files() {
92
+ global $itsec_globals;
93
+
94
+ if ( isset( $itsec_globals ) && isset( $itsec_globals['settings'] ) && isset( $itsec_globals['settings']['write_files'] ) && ( true === $itsec_globals['settings']['write_files'] ) ) {
95
+ return true;
96
+ }
97
+
98
+ return false;
99
+ }
100
+
101
+ /**
102
+ * Add meta boxes to primary options pages.
103
+ *
104
+ * Adds the meta boxes containing rewrite rules that appears on the iThemes Security
105
+ * Dashboard.
106
+ *
107
+ * @since 4.0.0
108
+ *
109
+ * @return void
110
+ */
111
+ function add_admin_meta_boxes() {
112
+
113
+ add_meta_box(
114
+ 'itsec_rewrite',
115
+ __( 'Rewrite Rules', 'better-wp-security' ),
116
+ array( $this, 'rewrite_metabox' ),
117
+ 'toplevel_page_itsec',
118
+ 'bottom',
119
+ 'core'
120
+ );
121
+
122
+ add_meta_box(
123
+ 'itsec_wpconfig',
124
+ __( 'wp-config.php Rules', 'better-wp-security' ),
125
+ array( $this, 'config_metabox' ),
126
+ 'toplevel_page_itsec',
127
+ 'bottom',
128
+ 'core'
129
+ );
130
+
131
+ }
132
+
133
+ /**
134
+ * Processes file writing after saving options.
135
+ *
136
+ * Looks to see if rewrites_changed is true and starts file writing process as appropriate
137
+ *
138
+ * @since 4.0.0
139
+ *
140
+ * @return void
141
+ */
142
+ public function admin_init() {
143
+ global $itsec_globals;
144
+
145
+ if ( true === $this->rewrites_changed ) {
146
+ if ( isset( $itsec_globals['settings']['write_files'] ) && true === $itsec_globals['settings']['write_files'] ) {
147
+ do_action( 'itsec_pre_save_rewrites' );
148
+
149
+ $rewrites = $this->save_rewrites();
150
+
151
+ if ( is_array( $rewrites ) ) {
152
+ if ( false === $rewrites['success'] ) {
153
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $rewrites['text'], 'error' );
154
+
155
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
156
+ $file = ITSEC_Lib_Config_File::get_server_config_file_path();
157
+
158
+ $message = sprintf( __( 'Unable to update the <code>%1$s</code> file. You may need to manually remove the existing iThemes Security modifications and replace them with the rules found at <a href="%2$s">Security > Dashboard</a> under the "Rewrite Rules" section.', 'better-wp-security' ), $file, admin_url( 'admin.php?page=itsec#itsec_rewrite' ) );
159
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, 'error' );
160
+ } else if ( true !== $rewrites['text'] ) {
161
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), __( 'Settings Updated', 'better-wp-security' ) . '<br />' . $rewrites['text'], 'updated' );
162
+ }
163
+ } else {
164
+ add_site_option( 'itsec_manual_update', true );
165
+ }
166
+ } else {
167
+ add_site_option( 'itsec_manual_update', true );
168
+ }
169
+ }
170
+
171
+ if ( true === $this->config_changed ) {
172
+ if ( isset( $itsec_globals['settings']['write_files'] ) && true === $itsec_globals['settings']['write_files'] ) {
173
+ do_action( 'itsec_pre_save_configs' );
174
+
175
+ $configs = $this->save_wpconfig();
176
+
177
+ if ( is_array( $configs ) ) {
178
+ if ( false === $configs['success'] ) {
179
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $configs['text'], 'error' );
180
+
181
+ $message = sprintf( __( 'Unable to update the <code>%1$s</code> file. You may need to manually remove the existing iThemes Security modifications and replace them with the rules found at <a href="%2$s">Security > Dashboard</a> under the "wp-config.php Rules" section.', 'better-wp-security' ), ABSPATH . 'wp-config.php', admin_url( 'admin.php?page=itsec#itsec_wpconfig' ) );
182
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, 'error' );
183
+ }
184
+
185
+ if ( 1 == get_site_option( 'itsec_clear_login' ) ) {
186
+ delete_site_option( 'itsec_clear_login' );
187
+
188
+ wp_clear_auth_cookie();
189
+
190
+ $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ITSEC_Lib::get_home_root() . 'wp-login.php?loggedout=true';
191
+ wp_safe_redirect( $redirect_to );
192
+ exit();
193
+ }
194
+ } else {
195
+ add_site_option( 'itsec_manual_update', true );
196
+ }
197
+ } else {
198
+ add_site_option( 'itsec_manual_update', true );
199
+ }
200
+ }
201
+ }
202
+
203
+ /**
204
+ * Calls config metabox action.
205
+ *
206
+ * Allows a hook to add to the metabox containing the wp-config.php rules.
207
+ *
208
+ * @since 4.0.0
209
+ *
210
+ * @return void
211
+ */
212
+ public function config_metabox() {
213
+
214
+ do_action( 'itsec_wpconfig_metabox' );
215
+
216
+ }
217
+
218
+ /**
219
+ * Echos content metabox contents.
220
+ *
221
+ * Echos the contents of the wp-config.php metabox
222
+ *
223
+ * @since 4.0.0
224
+ *
225
+ * @return void
226
+ */
227
+ public function config_metabox_contents() {
228
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
229
+
230
+ $config = ITSEC_Lib_Config_File::get_wp_config();
231
+
232
+ if ( empty( $config ) ) {
233
+ _e( 'There are no rules to write.', 'better-wp-security' );
234
+ } else {
235
+ echo '<div class="itsec_rewrite_rules">' . highlight_string( $config, true ) . '</div>';
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Execute activation functions.
241
+ *
242
+ * Writes necessary information to wp-config and .htaccess upon plugin activation.
243
+ *
244
+ * @since 4.0.0
245
+ *
246
+ * @return void
247
+ */
248
+ public function do_activate() {
249
+ $this->save_wpconfig();
250
+ $this->save_rewrites();
251
+ }
252
+
253
+ /**
254
+ * Execute deactivation functions.
255
+ *
256
+ * Writes necessary information to wp-config and .htaccess upon plugin deactivation.
257
+ *
258
+ * @since 4.0.0
259
+ *
260
+ * @return void
261
+ */
262
+ public function do_deactivate() {
263
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
264
+
265
+ ITSEC_Lib_Config_File::reset_wp_config();
266
+ ITSEC_Lib_Config_File::reset_server_config();
267
+ }
268
+
269
+ /**
270
+ * Initialize file writer and rules arrays.
271
+ *
272
+ * Sets up initial information such as file locations and more to make
273
+ * calling quicker.
274
+ *
275
+ * @since 4.0.0
276
+ *
277
+ * @return void
278
+ */
279
+ public function file_writer_init() {
280
+
281
+ $this->file_modules = apply_filters( 'itsec_file_modules', $this->file_modules );
282
+
283
+ if ( '1' == get_site_option( 'itsec_config_changed' ) || '1' == get_site_option( 'itsec_rewrites_changed' ) ) {
284
+
285
+ $this->rewrites_changed = get_site_option( 'itsec_rewrites_changed' ) == '1' ? true : false;
286
+ $this->config_changed = get_site_option( 'itsec_config_changed' ) == '1' ? true : false;
287
+
288
+ delete_site_option( 'itsec_rewrites_changed' );
289
+ delete_site_option( 'itsec_config_changed' );
290
+
291
+ }
292
+
293
+ }
294
+
295
+ /**
296
+ * Attempt to get a lock for atomic operations.
297
+ *
298
+ * Tries to get a more robust lock on the file in question. Useful in situations where automatic
299
+ * file locking doesn't work.
300
+ *
301
+ * @since 4.0.0
302
+ *
303
+ * @param string $lock_file file name of lock
304
+ * @param int $exp seconds until lock expires
305
+ *
306
+ * @return bool true if lock was achieved, else false
307
+ */
308
+ public function get_file_lock( $lock_file, $exp = 180 ) {
309
+
310
+ global $itsec_globals;
311
+
312
+ clearstatcache();
313
+
314
+ if ( isset( $itsec_globals['settings']['lock_file'] ) && true === $itsec_globals['settings']['lock_file'] ) {
315
+ return true;
316
+ }
317
+
318
+ //Make sure the iThemes directory is actually there
319
+ if ( ! @is_dir( $itsec_globals['ithemes_dir'] ) ) {
320
+
321
+ @mkdir( $itsec_globals['ithemes_dir'] );
322
+ $handle = @fopen( $itsec_globals['ithemes_dir'] . '/.htaccess', 'w+' );
323
+ @fwrite( $handle, 'Deny from all' );
324
+ @fclose( $handle );
325
+
326
+ }
327
+
328
+ $lock_file = $itsec_globals['ithemes_dir'] . '/' . sanitize_text_field( $lock_file ) . '.lock';
329
+ $dir_age = @filectime( $lock_file );
330
+
331
+ if ( false === @mkdir( $lock_file ) ) {
332
+
333
+ if ( false !== $dir_age ) {
334
+
335
+ if ( ( time() - $dir_age ) > intval( $exp ) ) { //see if the lock has expired
336
+
337
+ @rmdir( $lock_file );
338
+ @mkdir( $lock_file );
339
+
340
+ } else { //couldn't get the lock
341
+
342
+ return false;
343
+
344
+ }
345
+
346
+ } else {
347
+
348
+ return false;
349
+
350
+ }
351
+
352
+ }
353
+
354
+ return true; //file lock was achieved
355
+
356
+ }
357
+
358
+ /**
359
+ * Sorts given arrays py priority key
360
+ *
361
+ * Allows for sorting of the rules array by a specified priority deeper in the array
362
+ *
363
+ * @since 4.0.0
364
+ *
365
+ * @access private
366
+ *
367
+ * @param string $a value a
368
+ * @param string $b value b
369
+ *
370
+ * @return int -1 if a less than b, 0 if they're equal or 1 if a is greater
371
+ */
372
+ private function priority_sort( $a, $b ) {
373
+
374
+ if ( isset( $a['priority'] ) && isset( $b['priority'] ) ) {
375
+
376
+ if ( $a['priority'] == $b['priority'] ) {
377
+ return 0;
378
+ }
379
+
380
+ return $a['priority'] > $b['priority'] ? 1 : - 1;
381
+
382
+ } else {
383
+
384
+ return 1;
385
+
386
+ }
387
+
388
+ }
389
+
390
+ /**
391
+ * Process quick ban of host.
392
+ *
393
+ * Immediately adds the supplied host to the .htaccess file for banning.
394
+ *
395
+ * @since 4.0.0
396
+ *
397
+ * @param string $host the host to ban
398
+ *
399
+ * @return bool true on success or false on failure
400
+ */
401
+ public static function quick_ban( $host ) {
402
+ $host = trim( $host );
403
+
404
+ if ( ! ITSEC_Lib::validates_ip_address( $host ) ) {
405
+ return false;
406
+ }
407
+
408
+
409
+ $host_rule = '# ' . __( 'Quick ban IP. Will be updated on next formal rules save.', 'better-wp-security' ) . "\n";
410
+
411
+ if ( 'nginx' === ITSEC_Lib::get_server() ) {
412
+ $host_rule .= "\tdeny $host;\n";
413
+ } else if ( 'apache' === ITSEC_Lib::get_server() ) {
414
+ $dhost = str_replace( '.', '\\.', $host ); //re-define $dhost to match required output for SetEnvIf-RegEX
415
+
416
+ $host_rule .= "SetEnvIF REMOTE_ADDR \"^$dhost$\" DenyAccess\n"; //Ban IP
417
+ $host_rule .= "SetEnvIF X-FORWARDED-FOR \"^$dhost$\" DenyAccess\n"; //Ban IP from Proxy-User
418
+ $host_rule .= "SetEnvIF X-CLUSTER-CLIENT-IP \"^$dhost$\" DenyAccess\n"; //Ban IP for Cluster/Cloud-hosted WP-Installs
419
+ $host_rule .= "<IfModule mod_authz_core.c>\n";
420
+ $host_rule .= "\t<RequireAll>\n";
421
+ $host_rule .= "\t\tRequire all granted\n";
422
+ $host_rule .= "\t\tRequire not env DenyAccess\n";
423
+ $host_rule .= "\t\tRequire not ip $host\n";
424
+ $host_rule .= "\t</RequireAll>\n";
425
+ $host_rule .= "</IfModule>\n";
426
+ $host_rule .= "<IfModule !mod_authz_core.c>\n";
427
+ $host_rule .= "\tOrder allow,deny\n";
428
+ $host_rule .= "\tDeny from env=DenyAccess\n";
429
+ $host_rule .= "\tDeny from $host\n";
430
+ $host_rule .= "\tAllow from all\n";
431
+ $host_rule .= "</IfModule>\n";
432
+ }
433
+
434
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
435
+ $result = ITSEC_Lib_Config_File::append_server_config( $host_rule );
436
+
437
+ if ( is_wp_error( $result ) ) {
438
+ return false;
439
+ }
440
+
441
+ return true;
442
+ }
443
+
444
+ /**
445
+ * Release the lock.
446
+ *
447
+ * Releases a file lock to allow others to use it.
448
+ *
449
+ * @since 4.0.0
450
+ *
451
+ * @param string $lock_file file name of lock
452
+ *
453
+ * @return bool true if released, false otherwise
454
+ */
455
+ public function release_file_lock( $lock_file ) {
456
+
457
+ global $itsec_globals;
458
+
459
+ if ( isset( $itsec_globals['settings']['lock_file'] ) && true === $itsec_globals['settings']['lock_file'] ) {
460
+ return true;
461
+ }
462
+
463
+ $lock_file = $itsec_globals['ithemes_dir'] . '/' . sanitize_text_field( $lock_file ) . '.lock';
464
+
465
+ if ( ! is_dir( $lock_file ) ) {
466
+
467
+ return true;
468
+
469
+ } else {
470
+
471
+ if ( ! @rmdir( $lock_file ) ) {
472
+
473
+ @chmod( $itsec_globals['ithemes_dir'], 0775 );
474
+
475
+ if ( file_exists( $lock_file . '/Thumbs.db' ) ) {
476
+ unlink( $lock_file . '/Thumbs.db' );
477
+ }
478
+
479
+ return @rmdir( $lock_file );
480
+
481
+ } else {
482
+
483
+ return true;
484
+
485
+ }
486
+
487
+ }
488
+
489
+ }
490
+
491
+ /**
492
+ * Calls rewrite metabox action.
493
+ *
494
+ * Executes the action to draw the htaccess rewrite rules metabox
495
+ *
496
+ * @since 4.0.0
497
+ *
498
+ * @return void
499
+ */
500
+ public function rewrite_metabox() {
501
+
502
+ do_action( 'itsec_rewrite_metabox' );
503
+
504
+ }
505
+
506
+ /**
507
+ * Echos rewrite metabox content.
508
+ *
509
+ * Echos the rewrite rules in the dashboard.
510
+ *
511
+ * @since 4.0.0
512
+ *
513
+ * @return void
514
+ */
515
+ public function rewrite_metabox_contents() {
516
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
517
+
518
+ $config = ITSEC_Lib_Config_File::get_server_config();
519
+
520
+ if ( empty( $config ) ) {
521
+ _e( 'There are no rules to write.', 'better-wp-security' );
522
+ } else {
523
+ echo '<div class="itsec_rewrite_rules">' . highlight_string( $config, true ) . '</div>';
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Saves all rewrite rules to htaccess or similar file.
529
+ *
530
+ * Gets a file lock for .htaccess and calls the writing function if successful.
531
+ *
532
+ * @since 4.0.0
533
+ *
534
+ * @return mixed array or false if writing disabled or error message
535
+ */
536
+ public function save_rewrites() {
537
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
538
+
539
+ $result = ITSEC_Lib_Config_File::update_server_config();
540
+
541
+ if ( is_wp_error( $result ) ) {
542
+ $retval = array(
543
+ 'success' => false,
544
+ 'text' => $result->get_error_message(),
545
+ );
546
+ } else {
547
+ $server = ITSEC_Lib_Utility::get_web_server();
548
+
549
+ if ( 'nginx' === $server ) {
550
+ $retval = array(
551
+ 'success' => true,
552
+ 'text' => __( 'You must restart your NGINX server for the settings to take effect', 'better-wp-security' ),
553
+ );
554
+ } else {
555
+ $retval = array(
556
+ 'success' => true,
557
+ 'text' => true,
558
+ );
559
+ }
560
+ }
561
+
562
+ return $retval;
563
+ }
564
+
565
+ /**
566
+ * Saves all wpconfig rules to wp-config.php.
567
+ *
568
+ * Gets a file lock for wp-config.php and calls the writing function if successful.
569
+ *
570
+ * @since 4.0.0
571
+ *
572
+ * @return mixed array or false if writing disabled or error message
573
+ */
574
+ public function save_wpconfig() {
575
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
576
+
577
+ $result = ITSEC_Lib_Config_File::update_wp_config();
578
+
579
+ if ( is_wp_error( $result ) ) {
580
+ $retval = array(
581
+ 'success' => false,
582
+ 'text' => $result->get_error_message(),
583
+ );
584
+ } else {
585
+ $retval = array(
586
+ 'success' => true,
587
+ 'text' => true,
588
+ );
589
+ }
590
+
591
+ return $retval;
592
+ }
593
+ }
core/class-itsec-global-settings.php ADDED
@@ -0,0 +1,1417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Plugin-wide settings for logs, email and more
5
+ *
6
+ * @package iThemes_Security
7
+ *
8
+ * @since 4.0
9
+ */
10
+ class ITSEC_Global_Settings {
11
+
12
+ private
13
+ $settings,
14
+ $core,
15
+ $allowed_tags;
16
+
17
+ function __construct( $core ) {
18
+
19
+ if ( is_admin() ) {
20
+
21
+ $this->core = $core;
22
+ $this->settings = get_site_option( 'itsec_global' );
23
+ $this->allowed_tags = array(
24
+ 'a' => array(
25
+ 'href' => array(),
26
+ 'title' => array(),
27
+ ),
28
+ 'br' => array(),
29
+ 'em' => array(),
30
+ 'strong' => array(),
31
+ 'h1' => array(),
32
+ 'h2' => array(),
33
+ 'h3' => array(),
34
+ 'h4' => array(),
35
+ 'h5' => array(),
36
+ 'h6' => array(),
37
+ 'div' => array(
38
+ 'style' => array(),
39
+ ),
40
+ );
41
+
42
+ add_action( 'itsec_add_admin_meta_boxes', array(
43
+ $this,
44
+ 'itsec_add_admin_meta_boxes'
45
+ ) ); //add meta boxes to admin page
46
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init' ) ); //initialize admin area
47
+ add_filter( 'itsec_tooltip_modules', array( $this, 'itsec_tooltip_modules' ) ); //register tooltip action
48
+ add_filter( 'itsec_tracking_vars', array( $this, 'itsec_tracking_vars' ) ); //register vars to track
49
+
50
+ //manually save options on multisite
51
+ if ( is_multisite() ) {
52
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init_multisite' ) ); //save multisite options
53
+ }
54
+
55
+ }
56
+
57
+ }
58
+
59
+ /**
60
+ * Build and echo the advanced page description.
61
+ *
62
+ * @since 4.0
63
+ *
64
+ * @return void
65
+ */
66
+ public function add_module_advanced_intro() {
67
+
68
+ global $itsec_globals;
69
+
70
+ if ( array_key_exists( 'backup', $itsec_globals['free_modules'] ) ) {
71
+
72
+ $backup_link_open = '<strong><a href="?page=toplevel_page_itsec_backups">';
73
+ $backup_link_close = '</a></strong>';
74
+
75
+ } else {
76
+
77
+ $backup_link_open = '';
78
+ $backup_link_close = '';
79
+
80
+ }
81
+
82
+ printf(
83
+ '<p>%s %s%s%s %s</p>',
84
+ __( 'The settings below are more advanced settings that should be done with caution on an existing site.', 'better-wp-security' ),
85
+ $backup_link_open,
86
+ __( 'Make sure you have a good backup before changing any setting on this page.', 'better-wp-security' ),
87
+ $backup_link_close,
88
+ __( 'In addition, these settings will not be reversed if you remove this plugin. That said, all settings on this page use methods recommended by WordPress.org itself and will help in improving the security of your site.', 'better-wp-security' )
89
+ );
90
+
91
+ }
92
+
93
+ /**
94
+ * Build and echo the away mode description
95
+ *
96
+ * @since 4.0
97
+ *
98
+ * @return void
99
+ */
100
+ public function add_module_intro() {
101
+
102
+ ?>
103
+
104
+ <label class="itsec_toc_label" for="itsec_toc_select"><?php _e( 'Go to', 'better-wp-security' ); ?></label>
105
+ <select class="itsec_toc" onchange="itsec_toc_select(this.value)">
106
+ <option value="#global_table_of_contents"><?php _e( 'Choose a section...', 'better-wp-security' ); ?></option>
107
+ <?php foreach ( $this->core->get_toc_items() as $box ) { ?>
108
+ <?php echo '<option value="#' . $box['id'] . '" >' . $box['title'] . '</option>'; ?>
109
+ <?php } ?>
110
+ </select>
111
+
112
+ <?php
113
+
114
+ }
115
+
116
+ /**
117
+ * Add meta boxes to primary options pages
118
+ *
119
+ * @since 4.0
120
+ *
121
+ * @param array $available_pages array of available page_hooks
122
+ */
123
+ public function itsec_add_admin_meta_boxes() {
124
+
125
+ add_meta_box(
126
+ 'global_table_of_contents',
127
+ __( 'Quick Links', 'better-wp-security' ),
128
+ array( $this, 'add_module_intro' ),
129
+ 'security_page_toplevel_page_itsec_settings',
130
+ 'normal',
131
+ 'core'
132
+ );
133
+
134
+ add_meta_box(
135
+ 'advanced_intro',
136
+ __( 'Welcome', 'better-wp-security' ),
137
+ array( $this, 'add_module_advanced_intro' ),
138
+ 'security_page_toplevel_page_itsec_advanced',
139
+ 'normal',
140
+ 'core'
141
+ );
142
+
143
+ add_meta_box(
144
+ 'global_options',
145
+ __( 'Global Settings', 'better-wp-security' ),
146
+ array( $this, 'metabox_advanced_settings' ),
147
+ 'security_page_toplevel_page_itsec_settings',
148
+ 'advanced',
149
+ 'core'
150
+ );
151
+
152
+ }
153
+
154
+ /**
155
+ * Execute admin initializations
156
+ *
157
+ * @since 4.0
158
+ *
159
+ * @return void
160
+ */
161
+ public function itsec_admin_init() {
162
+
163
+ //Add Settings sections
164
+ add_settings_section(
165
+ 'global',
166
+ __( 'Global Settings', 'better-wp-security' ),
167
+ '__return_empty_string',
168
+ 'security_page_toplevel_page_itsec_settings'
169
+ );
170
+
171
+ //Settings Fields
172
+ add_settings_field(
173
+ 'itsec_global[write_files]',
174
+ __( 'Write to Files', 'better-wp-security' ),
175
+ array( $this, 'settings_field_write_files' ),
176
+ 'security_page_toplevel_page_itsec_settings',
177
+ 'global'
178
+ );
179
+
180
+ add_settings_field(
181
+ 'itsec_global[notification_email]',
182
+ __( 'Notification Email', 'better-wp-security' ),
183
+ array( $this, 'settings_field_notification_email' ),
184
+ 'security_page_toplevel_page_itsec_settings',
185
+ 'global',
186
+ array( 'label_for' => 'itsec_global_notification_email' )
187
+ );
188
+
189
+ add_settings_field(
190
+ 'itsec_global[digest_email]',
191
+ __( 'Send Digest Email', 'better-wp-security' ),
192
+ array( $this, 'settings_field_digest_email' ),
193
+ 'security_page_toplevel_page_itsec_settings',
194
+ 'global'
195
+ );
196
+
197
+ add_settings_field(
198
+ 'itsec_global[backup_email]',
199
+ __( 'Backup Delivery Email', 'better-wp-security' ),
200
+ array( $this, 'settings_field_backup_email' ),
201
+ 'security_page_toplevel_page_itsec_settings',
202
+ 'global',
203
+ array( 'label_for' => 'itsec_global_backup_email' )
204
+ );
205
+
206
+ add_settings_field(
207
+ 'itsec_global[lockout_message]',
208
+ __( 'Host Lockout Message', 'better-wp-security' ),
209
+ array( $this, 'settings_field_lockout_message' ),
210
+ 'security_page_toplevel_page_itsec_settings',
211
+ 'global',
212
+ array( 'label_for' => 'itsec_global_lockout_message' )
213
+ );
214
+
215
+ add_settings_field(
216
+ 'itsec_global[user_lockout_message]',
217
+ __( 'User Lockout Message', 'better-wp-security' ),
218
+ array( $this, 'settings_field_user_lockout_message' ),
219
+ 'security_page_toplevel_page_itsec_settings',
220
+ 'global',
221
+ array( 'label_for' => 'itsec_global_user_lockout_message' )
222
+ );
223
+
224
+ add_settings_field(
225
+ 'itsec_global[community_lockout_message]',
226
+ __( 'Community Lockout Message', 'better-wp-security' ),
227
+ array( $this, 'settings_field_community_lockout_message' ),
228
+ 'security_page_toplevel_page_itsec_settings',
229
+ 'global',
230
+ array( 'label_for' => 'itsec_global_community_lockout_message' )
231
+ );
232
+
233
+ add_settings_field(
234
+ 'itsec_global[blacklist]',
235
+ __( 'Blacklist Repeat Offender', 'better-wp-security' ),
236
+ array( $this, 'settings_field_blacklist' ),
237
+ 'security_page_toplevel_page_itsec_settings',
238
+ 'global'
239
+ );
240
+
241
+ add_settings_field(
242
+ 'itsec_global[blacklist_count]',
243
+ __( 'Blacklist Threshold', 'better-wp-security' ),
244
+ array( $this, 'settings_field_blacklist_count' ),
245
+ 'security_page_toplevel_page_itsec_settings',
246
+ 'global'
247
+ );
248
+
249
+ add_settings_field(
250
+ 'itsec_global[blacklist_period]',
251
+ __( 'Blacklist Lookback Period', 'better-wp-security' ),
252
+ array( $this, 'settings_field_blacklist_period' ),
253
+ 'security_page_toplevel_page_itsec_settings',
254
+ 'global'
255
+ );
256
+
257
+ add_settings_field(
258
+ 'itsec_global[lockout_period]',
259
+ __( 'Lockout Period', 'better-wp-security' ),
260
+ array( $this, 'settings_field_lockout_period' ),
261
+ 'security_page_toplevel_page_itsec_settings',
262
+ 'global'
263
+ );
264
+
265
+ add_settings_field(
266
+ 'itsec_global[lockout_white_list]',
267
+ __( 'Lockout White List', 'better-wp-security' ),
268
+ array( $this, 'settings_field_lockout_white_list' ),
269
+ 'security_page_toplevel_page_itsec_settings',
270
+ 'global',
271
+ array( 'label_for' => 'itsec_global_lockout_white_list' )
272
+ );
273
+
274
+ add_settings_field(
275
+ 'itsec_global[email_notifications]',
276
+ __( 'Email Lockout Notifications', 'better-wp-security' ),
277
+ array( $this, 'settings_field_email_notifications' ),
278
+ 'security_page_toplevel_page_itsec_settings',
279
+ 'global'
280
+ );
281
+
282
+ add_settings_field(
283
+ 'itsec_global[log_type]',
284
+ __( 'Log Type', 'better-wp-security' ),
285
+ array( $this, 'settings_field_log_type' ),
286
+ 'security_page_toplevel_page_itsec_settings',
287
+ 'global'
288
+ );
289
+
290
+ add_settings_field(
291
+ 'itsec_global[log_rotation]',
292
+ __( 'Days to Keep Database Logs', 'better-wp-security' ),
293
+ array( $this, 'settings_field_log_rotation' ),
294
+ 'security_page_toplevel_page_itsec_settings',
295
+ 'global'
296
+ );
297
+
298
+ add_settings_field(
299
+ 'itsec_global[log_location]',
300
+ __( 'Path to Log Files', 'better-wp-security' ),
301
+ array( $this, 'settings_field_log_location' ),
302
+ 'security_page_toplevel_page_itsec_settings',
303
+ 'global'
304
+ );
305
+
306
+ if ( is_dir( WP_PLUGIN_DIR . '/iwp-client' ) ) {
307
+
308
+ add_settings_field(
309
+ 'itsec_global[infinitewp_compatibility]',
310
+ __( 'Add InfiniteWP Compatibility', 'better-wp-security' ),
311
+ array( $this, 'settings_field_infinitewp_compatibility' ),
312
+ 'security_page_toplevel_page_itsec_settings',
313
+ 'global'
314
+ );
315
+
316
+ }
317
+
318
+ add_settings_field(
319
+ 'itsec_global[allow_tracking]',
320
+ __( 'Allow Data Tracking', 'better-wp-security' ),
321
+ array( $this, 'settings_field_allow_tracking' ),
322
+ 'security_page_toplevel_page_itsec_settings',
323
+ 'global'
324
+ );
325
+
326
+ if ( ITSEC_Lib::get_server() == 'nginx' ) {
327
+
328
+ add_settings_field(
329
+ 'itsec_global[nginx_file]',
330
+ __( 'NGINX Conf File', 'better-wp-security' ),
331
+ array( $this, 'settings_field_nginx_file' ),
332
+ 'security_page_toplevel_page_itsec_settings',
333
+ 'global'
334
+ );
335
+
336
+ }
337
+
338
+ add_settings_field(
339
+ 'itsec_global[lock_file]',
340
+ __( 'Disable File Locking', 'better-wp-security' ),
341
+ array( $this, 'settings_field_lock_file' ),
342
+ 'security_page_toplevel_page_itsec_settings',
343
+ 'global'
344
+ );
345
+
346
+ add_settings_field(
347
+ 'itsec_global[proxy_override]',
348
+ __( 'Override Proxy Detection', 'better-wp-security' ),
349
+ array( $this, 'settings_field_proxy_override' ),
350
+ 'security_page_toplevel_page_itsec_settings',
351
+ 'global'
352
+ );
353
+
354
+ add_settings_field(
355
+ 'itsec_global[hide_admin_bar]',
356
+ __( 'Hide Security Menu in Admin Bar', 'better-wp-security' ),
357
+ array( $this, 'settings_field_hide_admin_bar' ),
358
+ 'security_page_toplevel_page_itsec_settings',
359
+ 'global'
360
+ );
361
+
362
+ //Register the settings field for the entire module
363
+ register_setting(
364
+ 'security_page_toplevel_page_itsec_settings',
365
+ 'itsec_global',
366
+ array( $this, 'sanitize_module_input' )
367
+ );
368
+
369
+ }
370
+
371
+ /**
372
+ * Prepare and save options in network settings
373
+ *
374
+ * @since 4.0
375
+ *
376
+ * @return void
377
+ */
378
+ public function itsec_admin_init_multisite() {
379
+
380
+ if ( isset( $_POST['itsec_global'] ) ) {
381
+
382
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
383
+ die( __( 'Security error!', 'better-wp-security' ) );
384
+ }
385
+
386
+ update_site_option( 'itsec_global', $_POST['itsec_global'] ); //we must manually save network options
387
+
388
+ }
389
+
390
+ }
391
+
392
+ /**
393
+ * Register backups for tooltips
394
+ *
395
+ * @since 4.0
396
+ *
397
+ * @param array $tooltip_modules array of tooltip modules
398
+ *
399
+ * @return array array of tooltip modules
400
+ */
401
+ public function itsec_tooltip_modules( $tooltip_modules ) {
402
+
403
+ global $itsec_globals;
404
+
405
+ if ( get_site_transient( 'ITSEC_SHOW_WRITE_FILES_TOOLTIP' ) || ( ! isset ( $this->settings['write_files'] ) || $this->settings['write_files'] === false ) ) {
406
+
407
+ $tooltip_modules['writing'] = array(
408
+ 'priority' => 5,
409
+ 'class' => 'itsec_tooltip_writing',
410
+ 'heading' => __( 'Allow File Updates', 'better-wp-security' ),
411
+ 'text' => __( 'Many of the functions of this plugin require editing your wp-config.php or .htaccess files. Would you like to allow us to safely update these files for you automatically?', 'better-wp-security' ),
412
+ 'link_text' => __( 'Allow File Updates', 'better-wp-security' ),
413
+ 'callback' => array( $this, 'tooltip_ajax_writing' ),
414
+ 'success' => __( 'Setting Saved. File updates allowed.', 'better-wp-security' ),
415
+ 'failure' => __( 'Whoops. Something went wrong. Check the "Global Settings" section on the settings page (it is the first setting) to make sure your option was saved or contact support.', 'better-wp-security' ),
416
+ );
417
+
418
+ }
419
+
420
+ if ( ! isset ( $this->settings['allow_tracking'] ) || $this->settings['allow_tracking'] === false ) {
421
+
422
+ $tooltip_modules['tracking'] = array(
423
+ 'priority' => 1,
424
+ 'class' => 'itsec_tooltip_tracking',
425
+ 'heading' => __( 'Help Us Improve', 'better-wp-security' ),
426
+ 'text' => sprintf(
427
+ '%s %s', $itsec_globals['plugin_name'],
428
+ __( 'would like to collect anonymous data about features you use to help improve this plugin. Absolutely no information that can identify you will be collected.', 'better-wp-security' )
429
+ ),
430
+ 'link_text' => __( 'Yes, I\'d like to help', 'better-wp-security' ),
431
+ 'callback' => array( $this, 'tooltip_ajax_tracking' ),
432
+ 'success' => __( 'Setting Saved. Thanks for helping us make this plugin better.', 'better-wp-security' ),
433
+ 'failure' => __( 'Whoops. Something went wrong. Check the global settings page or contact support.', 'better-wp-security' ),
434
+ );
435
+
436
+ }
437
+
438
+ return $tooltip_modules;
439
+
440
+ }
441
+
442
+ /**
443
+ * Adds fields that will be tracked for Google Analytics
444
+ *
445
+ * @since 4.4
446
+ *
447
+ * @param array $vars tracking vars
448
+ *
449
+ * @return array tracking vars
450
+ */
451
+ public function itsec_tracking_vars( $vars ) {
452
+
453
+ $vars['itsec_global'] = array(
454
+ 'blacklist' => '1:b',
455
+ 'email_notifications' => '1:b',
456
+ 'write_files' => '0:b',
457
+ 'nginx_file' => '0:b',
458
+ 'infinitewp_compatibility' => '0:b',
459
+ 'lock_file' => '0:b',
460
+ 'digest_email' => '0:b',
461
+ 'proxy_override' => '0:b',
462
+ 'hide_admin_bar' => '0:b',
463
+ );
464
+
465
+ return $vars;
466
+
467
+ }
468
+
469
+ /**
470
+ * Render the settings metabox
471
+ *
472
+ * @since 4.0
473
+ *
474
+ * @return void
475
+ */
476
+ public function metabox_advanced_settings() {
477
+
478
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'global', false );
479
+
480
+ echo '<p>' . PHP_EOL;
481
+
482
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
483
+
484
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
485
+
486
+ echo '</p>' . PHP_EOL;
487
+
488
+ }
489
+
490
+ /**
491
+ * Sanitize and validate input
492
+ *
493
+ * @since 4.0
494
+ *
495
+ * @param Array $input array of input fields
496
+ *
497
+ * @return Array Sanitized array
498
+ */
499
+ public function sanitize_module_input( $input ) {
500
+
501
+ global $itsec_globals;
502
+
503
+ $input['did_upgrade'] = isset( $this->settings['did_upgrade'] ) ? $this->settings['did_upgrade'] : false;
504
+
505
+ if ( isset( $input['backup_email'] ) ) {
506
+
507
+ $bad_emails = array();
508
+ $emails_to_save = array();
509
+
510
+ if ( isset( $input['backup_email'] ) && ! is_array( $input['backup_email'] ) ) {
511
+
512
+ $emails = explode( PHP_EOL, $input['backup_email'] );
513
+
514
+ } elseif ( isset( $input['backup_email'] ) ) {
515
+
516
+ $emails = $input['backup_email'];
517
+
518
+ }
519
+
520
+ foreach ( $emails as $email ) {
521
+
522
+ $email = sanitize_text_field( trim( $email ) );
523
+
524
+ if ( strlen( $email ) > 0 ) {
525
+
526
+ if ( is_email( $email ) === false ) {
527
+ $bad_emails[] = $email;
528
+ }
529
+
530
+ $emails_to_save[] = $email;
531
+
532
+ }
533
+
534
+ }
535
+
536
+ if ( sizeof( $bad_emails ) > 0 ) {
537
+
538
+ $bad_addresses = implode( ', ', $bad_emails );
539
+ $type = 'error';
540
+ $message = __( 'The following backup email address(es) do not appear to be valid: ', 'better-wp-security' ) . $bad_addresses;
541
+
542
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
543
+
544
+ }
545
+
546
+ $input['backup_email'] = $emails_to_save;
547
+ }
548
+
549
+ if ( isset( $input['notification_email'] ) ) {
550
+
551
+ $bad_emails = array();
552
+ $emails_to_save = array();
553
+
554
+ if ( isset( $input['notification_email'] ) && ! is_array( $input['notification_email'] ) ) {
555
+
556
+ $emails = explode( PHP_EOL, $input['notification_email'] );
557
+
558
+ } else {
559
+
560
+ $emails = $input['notification_email'];
561
+
562
+ }
563
+
564
+ foreach ( $emails as $email ) {
565
+
566
+ $email = sanitize_text_field( trim( $email ) );
567
+
568
+ if ( strlen( $email ) > 0 ) {
569
+
570
+ if ( is_email( $email ) === false ) {
571
+ $bad_emails[] = $email;
572
+ }
573
+
574
+ $emails_to_save[] = $email;
575
+
576
+ }
577
+
578
+ }
579
+
580
+ if ( sizeof( $bad_emails ) > 0 ) {
581
+
582
+ $bad_addresses = implode( ', ', $bad_emails );
583
+ $type = 'error';
584
+ $message = __( 'The following notification email address(es) do not appear to be valid: ',
585
+ 'better-wp-security' ) . $bad_addresses;
586
+
587
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
588
+
589
+ }
590
+
591
+ $input['notification_email'] = $emails_to_save;
592
+ }
593
+
594
+ $input['lockout_message'] = isset( $input['lockout_message'] ) ? trim( wp_kses( $input['lockout_message'], $this->allowed_tags ) ) : '';
595
+ $input['user_lockout_message'] = isset( $input['user_lockout_message'] ) ? trim( wp_kses( $input['user_lockout_message'], $this->allowed_tags ) ) : '';
596
+ $input['community_lockout_message'] = isset( $input['community_lockout_message'] ) ? trim( wp_kses( $input['community_lockout_message'], $this->allowed_tags ) ) : '';
597
+ $input['blacklist'] = ( isset( $input['blacklist'] ) && intval( $input['blacklist'] == 1 ) ? true : false );
598
+ $input['blacklist_count'] = isset( $input['blacklist_count'] ) ? absint( $input['blacklist_count'] ) : 3;
599
+ $input['blacklist_period'] = isset( $input['blacklist_period'] ) ? absint( $input['blacklist_period'] ) : 7;
600
+ $input['email_notifications'] = ( isset( $input['email_notifications'] ) && intval( $input['email_notifications'] == 1 ) ? true : false );
601
+ $input['lockout_period'] = isset( $input['lockout_period'] ) ? absint( $input['lockout_period'] ) : 15;
602
+ $input['log_rotation'] = isset( $input['log_rotation'] ) ? absint( $input['log_rotation'] ) : 14;
603
+ $input['allow_tracking'] = ( isset( $input['allow_tracking'] ) && intval( $input['allow_tracking'] == 1 ) ? true : false );
604
+ $input['write_files'] = ( isset( $input['write_files'] ) && intval( $input['write_files'] == 1 ) ? true : false );
605
+ $input['nginx_file'] = isset( $input['nginx_file'] ) ? sanitize_text_field( $input['nginx_file'] ) : ABSPATH . 'nginx.conf';
606
+ $input['infinitewp_compatibility'] = ( isset( $input['infinitewp_compatibility'] ) && intval( $input['infinitewp_compatibility'] == 1 ) ? true : false );
607
+ $input['log_info'] = $itsec_globals['settings']['log_info'];
608
+ $input['lock_file'] = ( isset( $input['lock_file'] ) && intval( $input['lock_file'] == 1 ) ? true : false );
609
+ $input['digest_email'] = ( isset( $input['digest_email'] ) && intval( $input['digest_email'] == 1 ) ? true : false );
610
+ $input['proxy_override'] = ( isset( $input['proxy_override'] ) && intval( $input['proxy_override'] == 1 ) ? true : false );
611
+ $input['hide_admin_bar'] = ( isset( $input['hide_admin_bar'] ) && intval( $input['hide_admin_bar'] == 1 ) ? true : false );
612
+
613
+ //Set a fresh message queue if we're just turning on the digest.
614
+ if ( $input['digest_email'] === true && ( ! isset( $this->settings['digest_email'] ) || $this->settings['digest_email'] === false ) ) {
615
+
616
+ $digest_queue = array(
617
+ 'last_sent' => $itsec_globals['current_time_gmt'],
618
+ 'messages' => array(),
619
+ );
620
+
621
+ update_site_option( 'itsec_message_queue', $digest_queue );
622
+
623
+ }
624
+
625
+ $input['log_location'] = isset( $input['log_location'] ) ? sanitize_text_field( $input['log_location'] ) : $itsec_globals['ithemes_log_dir'];
626
+
627
+ //Process white list
628
+ if ( isset( $input['lockout_white_list'] ) && ! is_array( $input['lockout_white_list'] ) ) {
629
+
630
+ $white_listed_addresses = explode( PHP_EOL, $input['lockout_white_list'] );
631
+
632
+ } elseif ( isset( $input['lockout_white_list'] ) ) {
633
+
634
+ $white_listed_addresses = $input['lockout_white_list'];
635
+
636
+ } else {
637
+
638
+ $white_listed_addresses = array();
639
+
640
+ }
641
+
642
+ $bad_white_listed_ips = array();
643
+ $raw_white_listed_ips = array();
644
+
645
+ foreach ( $white_listed_addresses as $index => $address ) {
646
+
647
+ $address = trim( $address );
648
+
649
+ if ( strlen( trim( $address ) ) > 0 ) {
650
+
651
+ if ( ITSEC_Lib::validates_ip_address( $address ) === false ) {
652
+
653
+ $bad_white_listed_ips[] = filter_var( $address, FILTER_SANITIZE_STRING );
654
+
655
+ }
656
+
657
+ $raw_white_listed_ips[] = filter_var( $address, FILTER_SANITIZE_STRING );
658
+
659
+ } else {
660
+
661
+ unset( $white_listed_addresses[ $index ] );
662
+
663
+ }
664
+
665
+ }
666
+
667
+ $raw_white_listed_ips = array_unique( $raw_white_listed_ips );
668
+
669
+ if ( sizeof( $bad_white_listed_ips ) > 0 ) {
670
+
671
+ $type = 'error';
672
+ $message = '';
673
+
674
+ $message .= sprintf(
675
+ '%s<br /><br />',
676
+ __( 'There is a problem with an IP address in the white list:', 'better-wp-security' )
677
+ );
678
+
679
+ foreach ( $bad_white_listed_ips as $bad_ip ) {
680
+
681
+ $message .= sprintf(
682
+ '%s %s<br />',
683
+ $bad_ip,
684
+ __( 'is not a valid address in the white list users box.', 'better-wp-security' )
685
+ );
686
+
687
+ }
688
+
689
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
690
+
691
+ }
692
+
693
+ $input['lockout_white_list'] = $raw_white_listed_ips;
694
+
695
+ if ( $input['log_location'] != $itsec_globals['ithemes_log_dir'] ) {
696
+
697
+ $good_path = ITSEC_Lib::validate_path( $input['log_location'] );
698
+
699
+ } else {
700
+
701
+ $good_path = true;
702
+
703
+ }
704
+
705
+ if ( $good_path !== true ) {
706
+
707
+ $input['log_location'] = $itsec_globals['ithemes_log_dir'];
708
+
709
+ $type = 'error';
710
+ $message = __( 'The file path entered for the log location does not appear to be valid. it has been reset to: ' . $itsec_globals['ithemes_log_dir'], 'better-wp-security' );
711
+
712
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
713
+
714
+ }
715
+
716
+ $input['log_type'] = isset( $input['log_type'] ) ? intval( $input['log_type'] ) : 0;
717
+
718
+ if ( ! isset( $type ) && $input['write_files'] === true && $this->settings['write_files'] === false ) {
719
+
720
+ add_site_option( 'itsec_rewrites_changed', true );
721
+
722
+ }
723
+
724
+ if ( is_multisite() ) {
725
+
726
+ if ( isset( $type ) ) {
727
+
728
+ $error_handler = new WP_Error();
729
+
730
+ $error_handler->add( $type, $message );
731
+
732
+ $this->core->show_network_admin_notice( $error_handler );
733
+
734
+ } else {
735
+
736
+ $this->core->show_network_admin_notice( false );
737
+
738
+ }
739
+
740
+ $this->settings = $input;
741
+
742
+ }
743
+
744
+ return $input;
745
+
746
+ }
747
+
748
+ /**
749
+ * echos allow tracking Field
750
+ *
751
+ * @since 4.0
752
+ *
753
+ * @return void
754
+ */
755
+ public function settings_field_allow_tracking() {
756
+
757
+ if ( isset( $this->settings['allow_tracking'] ) && $this->settings['allow_tracking'] === true ) {
758
+
759
+ $allow_tracking = 1;
760
+
761
+ } else {
762
+
763
+ $allow_tracking = 0;
764
+
765
+ }
766
+
767
+ echo '<input type="checkbox" id="itsec_global_allow_tracking" name="itsec_global[allow_tracking]" value="1" ' . checked( 1, $allow_tracking, false ) . '/>';
768
+ echo '<label for="itsec_global_allow_tracking">' . __( 'Allow iThemes to track plugin usage via anonymous data. ', 'better-wp-security' ) . '</label>';
769
+
770
+ }
771
+
772
+ /**
773
+ * echos Backup email Field
774
+ *
775
+ * @since 4.0
776
+ *
777
+ * @return void
778
+ */
779
+ public function settings_field_backup_email() {
780
+
781
+ if ( isset( $this->settings['backup_email'] ) && is_array( $this->settings['backup_email'] ) ) {
782
+
783
+ $emails = implode( PHP_EOL, $this->settings['backup_email'] );
784
+
785
+ } else {
786
+
787
+ $emails = get_option( 'admin_email' );
788
+
789
+ }
790
+
791
+ echo '<textarea id="itsec_global_backup_email" name="itsec_global[backup_email]">' . $emails . PHP_EOL . '</textarea>';
792
+ echo '<p class="description">' . __( 'The email address(es) all database backups will be sent to. One address per line.', 'better-wp-security' ) . '</p>';
793
+
794
+ }
795
+
796
+ /**
797
+ * echos Blacklist Field
798
+ *
799
+ * @since 4.0
800
+ *
801
+ * @return void
802
+ */
803
+ public function settings_field_blacklist() {
804
+
805
+ if ( isset( $this->settings['blacklist'] ) && $this->settings['blacklist'] === false ) {
806
+
807
+ $blacklist = 0;
808
+
809
+ } else {
810
+
811
+ $blacklist = 1;
812
+
813
+ }
814
+
815
+ echo '<input type="checkbox" id="itsec_global_blacklist" name="itsec_global[blacklist]" value="1" ' . checked( 1, $blacklist, false ) . '/>';
816
+ echo '<label for="itsec_global_blacklist"> ' . __( 'Enable Blacklist Repeat Offender', 'better-wp-security' ) . '</label>';
817
+ echo '<p class="description"> ' . __( 'If this box is checked the IP address of the offending computer will be added to the "Ban Users" blacklist after reaching the number of lockouts listed below.', 'better-wp-security' ) . '</p>';
818
+
819
+ if ( ITSEC_Lib::get_server() == 'nginx' ) {
820
+ echo '<p class="description"> ' . __( 'Note that as you are on NGINX you will still need to manually restart the server even though the users will be added to the banned users list.', 'better-wp-security' ) . '</p>';
821
+ }
822
+
823
+ }
824
+
825
+ /**
826
+ * echos Blacklist Threshold Field
827
+ *
828
+ * @since 4.0
829
+ *
830
+ * @return void
831
+ */
832
+ public function settings_field_blacklist_count() {
833
+
834
+ if ( isset( $this->settings['blacklist_count'] ) ) {
835
+
836
+ $blacklist_count = absint( $this->settings['blacklist_count'] );
837
+
838
+ } else {
839
+
840
+ $blacklist_count = 3;
841
+
842
+ }
843
+
844
+ echo '<input class="small-text" name="itsec_global[blacklist_count]" id="itsec_global_blacklist_count" value="' . $blacklist_count . '" type="text">';
845
+ echo '<label for="itsec_global_blacklist_count"> ' . __( 'Lockouts', 'better-wp-security' ) . '</label>';
846
+ echo '<p class="description"> ' . __( 'The number of lockouts per IP before the host is banned permanently from this site.', 'better-wp-security' ) . '</p>';
847
+
848
+ }
849
+
850
+ /**
851
+ * echos Blacklist Lookback Period Field
852
+ *
853
+ * @since 4.0
854
+ *
855
+ * @return void
856
+ */
857
+ public function settings_field_blacklist_period() {
858
+
859
+ if ( isset( $this->settings['blacklist_period'] ) ) {
860
+
861
+ $blacklist_period = absint( $this->settings['blacklist_period'] );
862
+
863
+ } else {
864
+
865
+ $blacklist_period = 7;
866
+
867
+ }
868
+
869
+ echo '<input class="small-text" name="itsec_global[blacklist_period]" id="itsec_global_blacklist_period" value="' . $blacklist_period . '" type="text">';
870
+ echo '<label for="itsec_global_blacklist_period"> ' . __( 'Days', 'better-wp-security' ) . '</label>';
871
+ echo '<p class="description"> ' . __( 'How many days should a lockout be remembered to meet the blacklist count above.', 'better-wp-security' ) . '</p>';
872
+
873
+ }
874
+
875
+ /**
876
+ * echos Admin User Username Field
877
+ *
878
+ * @since 4.0
879
+ *
880
+ * @return void
881
+ */
882
+ public function settings_field_community_lockout_message() {
883
+
884
+ if ( isset( $this->settings['community_lockout_message'] ) ) {
885
+
886
+ $community_lockout_message = wp_kses( $this->settings['community_lockout_message'], $this->allowed_tags );
887
+
888
+ } else {
889
+
890
+ $community_lockout_message = __( "Your IP address has been flagged as a threat by the iThemes Security network.", 'better-wp-security' );
891
+
892
+ }
893
+
894
+ echo '<textarea class="widefat" name="itsec_global[community_lockout_message]" id="itsec_global_community_lockout_message" rows="5" >' . $community_lockout_message . PHP_EOL . '</textarea><br />';
895
+ echo '<p class="description">' . __( 'The message to display to a user when their IP has been flagged as bad by the iThemes network.', 'better-wp-security' ) . '</p>';
896
+ echo '<p class="description">' . __( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div', 'better-wp-security' ) . '</p>';
897
+
898
+ }
899
+
900
+ /**
901
+ * echos digest email field
902
+ *
903
+ * @since 4.5
904
+ *
905
+ * @return void
906
+ */
907
+ public function settings_field_digest_email() {
908
+
909
+ if ( isset( $this->settings['digest_email'] ) && $this->settings['digest_email'] === false ) {
910
+
911
+ $digest_email = 0;
912
+
913
+ } else {
914
+
915
+ $digest_email = 1;
916
+
917
+ }
918
+
919
+ echo '<input type="checkbox" id="itsec_global_digest_email" name="itsec_global[digest_email]" value="1" ' . checked( 1, $digest_email, false ) . '/>';
920
+ echo '<label for="itsec_global_digest_email">' . __( 'Send digest email', 'better-wp-security' ) . '</label>';
921
+
922
+ printf(
923
+ '<p class="description">%s</p>',
924
+ __( 'During periods of heavy attack or other times a security plugin can generate a LOT of email just telling you that it is doing its job. Turning this on will reduce the emails from this plugin to no more than one per day for any notification.', 'better-wp-security' )
925
+ );
926
+
927
+ }
928
+
929
+ /**
930
+ * echos Lockout Email Field
931
+ *
932
+ * @since 4.0
933
+ *
934
+ * @return void
935
+ */
936
+ public function settings_field_email_notifications() {
937
+
938
+ if ( isset( $this->settings['email_notifications'] ) && $this->settings['email_notifications'] === false ) {
939
+
940
+ $email_notifications = 0;
941
+
942
+ } else {
943
+
944
+ $email_notifications = 1;
945
+
946
+ }
947
+
948
+ echo '<input type="checkbox" id="itsec_global_email_notifications" name="itsec_global[email_notifications]" value="1" ' . checked( 1, $email_notifications, false ) . '/>';
949
+ echo '<label for="itsec_global_email_notifications">' . __( 'Enable Email Lockout Notifications', 'better-wp-security' ) . '</label>';
950
+
951
+ printf(
952
+ '<p class="description">%s<a href="admin.php?page=toplevel_page_itsec_settings">%s</a>%s</p>',
953
+ __( 'This feature will trigger an email to be sent to the ', 'better-wp-security' ),
954
+ __( 'notifications email address', 'better-wp-security' ),
955
+ __( ' whenever a host or user is locked out of the system.', 'better-wp-security' )
956
+ );
957
+
958
+ }
959
+
960
+ /**
961
+ * echos hide admin bar field
962
+ *
963
+ * @since 4.5
964
+ *
965
+ * @return void
966
+ */
967
+ public function settings_field_hide_admin_bar() {
968
+
969
+ if ( isset( $this->settings['hide_admin_bar'] ) && $this->settings['hide_admin_bar'] === false ) {
970
+
971
+ $hide_admin_bar = 0;
972
+
973
+ } else {
974
+
975
+ $hide_admin_bar = 1;
976
+
977
+ }
978
+
979
+ echo '<input type="checkbox" id="itsec_global_hide_admin_bar" name="itsec_global[hide_admin_bar]" value="1" ' . checked( 1, $hide_admin_bar, false ) . '/>';
980
+ echo '<label for="itsec_global_hide_admin_bar">' . __( 'Hide security menu in admin bar.', 'better-wp-security' ) . '</label>';
981
+
982
+ }
983
+
984
+ /**
985
+ * echos Lockout Email Field
986
+ *
987
+ * @since 4.0
988
+ *
989
+ * @return void
990
+ */
991
+ public function settings_field_infinitewp_compatibility() {
992
+
993
+ if ( isset( $this->settings['infinitewp_compatibility'] ) && $this->settings['infinitewp_compatibility'] === true ) {
994
+
995
+ $infinitewp_compatibility = 1;
996
+
997
+ } else {
998
+
999
+ $infinitewp_compatibility = 0;
1000
+
1001
+ }
1002
+
1003
+ echo '<input type="checkbox" id="itsec_global_infinitewp_compatibility" name="itsec_global[infinitewp_compatibility]" value="1" ' . checked( 1, $infinitewp_compatibility, false ) . '/>';
1004
+ echo '<label for="itsec_global_infinitewp_compatibility">' . __( 'Enable InfiniteWP Compatibility', 'better-wp-security' ) . '</label>';
1005
+
1006
+ printf(
1007
+ '<p class="description">%s <a href="http://infinitewp.com" target="_blank">%s</a> %s</p>',
1008
+ __( 'Turning this feature on will enable compatibility with', 'better-wp-security' ),
1009
+ __( 'InfiniteWP.', 'better-wp-security' ),
1010
+ __( 'Do not turn it on unless you use the InfiniteWP service.', 'better-wp-security' )
1011
+ );
1012
+
1013
+ }
1014
+
1015
+ /**
1016
+ * echos Lock File Field
1017
+ *
1018
+ * @since 4.0.20
1019
+ *
1020
+ * @return void
1021
+ */
1022
+ public function settings_field_lock_file() {
1023
+
1024
+ if ( isset( $this->settings['lock_file'] ) && $this->settings['lock_file'] === true ) {
1025
+
1026
+ $lock_file = 1;
1027
+
1028
+ } else {
1029
+
1030
+ $lock_file = 0;
1031
+
1032
+ }
1033
+
1034
+ echo '<input type="checkbox" id="itsec_global_lock_file" name="itsec_global[lock_file]" value="1" ' . checked( 1, $lock_file, false ) . '/>';
1035
+ echo '<label for="itsec_global_lock_file">' . __( 'Disable File Locking', 'better-wp-security' ) . '</label>';
1036
+
1037
+ printf(
1038
+ '<p class="description">%s</p>',
1039
+ __( 'Turning this option on will prevent errors related to file locking however might result in operations being executed twice. We do not recommend turning this off unless your host prevents the file locking feature from working correctly.', 'better-wp-security' )
1040
+ );
1041
+
1042
+ }
1043
+
1044
+ /**
1045
+ * echos Admin User Username Field
1046
+ *
1047
+ * @since 4.0
1048
+ *
1049
+ * @return void
1050
+ */
1051
+ public function settings_field_lockout_message() {
1052
+
1053
+ if ( isset( $this->settings['lockout_message'] ) ) {
1054
+
1055
+ $lockout_message = wp_kses( $this->settings['lockout_message'], $this->allowed_tags );
1056
+
1057
+ } else {
1058
+
1059
+ $lockout_message = __( 'error', 'better-wp-security' );
1060
+
1061
+ }
1062
+
1063
+ echo '<textarea class="widefat" name="itsec_global[lockout_message]" id="itsec_global_lockout_message" rows="5" >' . $lockout_message . PHP_EOL . '</textarea>';
1064
+ echo '<p class="description">' . __( 'The message to display when a computer (host) has been locked out.', 'better-wp-security' ) . '</p>';
1065
+ echo '<p class="description">' . __( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div', 'better-wp-security' ) . '</p>';
1066
+
1067
+ }
1068
+
1069
+ /**
1070
+ * echos Lockout Period Field
1071
+ *
1072
+ * @since 4.0
1073
+ *
1074
+ * @return void
1075
+ */
1076
+ public function settings_field_lockout_period() {
1077
+
1078
+ if ( isset( $this->settings['lockout_period'] ) ) {
1079
+ $lockout_period = absint( $this->settings['lockout_period'] );
1080
+ } else {
1081
+ $lockout_period = 15;
1082
+ }
1083
+
1084
+ echo '<input class="small-text" name="itsec_global[lockout_period]" id="itsec_global_lockout_period" value="' . $lockout_period . '" type="text">';
1085
+ echo '<label for="itsec_global_lockout_period"> ' . __( 'Minutes', 'better-wp-security' ) . '</label>';
1086
+ echo '<p class="description"> ' . __( 'The length of time a host or user will be banned from this site after hitting the limit of bad logins.', 'better-wp-security' ) . '</p>';
1087
+
1088
+ }
1089
+
1090
+ /**
1091
+ * echos Lockout White List Field
1092
+ *
1093
+ * @since 4.0
1094
+ *
1095
+ * @return void
1096
+ */
1097
+ public function settings_field_lockout_white_list() {
1098
+
1099
+ $white_list = '';
1100
+
1101
+ //Convert and show the agent list
1102
+ if ( isset( $this->settings['lockout_white_list'] ) && is_array( $this->settings['lockout_white_list'] ) && sizeof( $this->settings['lockout_white_list'] ) >= 1 ) {
1103
+
1104
+ $white_list = implode( PHP_EOL, $this->settings['lockout_white_list'] );
1105
+
1106
+ } elseif ( isset( $this->settings['lockout_white_list'] ) && ! is_array( $this->settings['lockout_white_list'] ) && strlen( $this->settings['lockout_white_list'] ) > 1 ) {
1107
+
1108
+ $white_list = $this->settings['lockout_white_list'];
1109
+
1110
+ }
1111
+
1112
+ echo '<textarea id="itsec_global_lockout_white_list" name="itsec_global[lockout_white_list]" rows="10" cols="50">' . $white_list . PHP_EOL . '</textarea>';
1113
+ echo '<p class="submit"><a href="' . PHP_EOL . ITSEC_Lib::get_ip() . '" class="itsec_add_ip_to_whitelist button-primary">' . __( 'Add my current IP to Whitelist', 'better-wp-security' ) . '</a></p>';
1114
+ echo '<p class="description">' . __( 'Use the guidelines below to enter hosts that will not be locked out from your site. This will keep you from locking yourself out of any features if you should trigger a lockout. Please note this does not override away mode and will only prevent a temporary ban. Should a permanent ban be triggered you will still be added to the "Ban Users" list unless the IP address is also white listed in that section.', 'better-wp-security' ) . '</p>';
1115
+ echo '<ul>';
1116
+ echo '<li>' . __( 'You may white list users by individual IP address or IP address range.', 'better-wp-security' ) . '</li>';
1117
+ echo '<li>' . __( 'Individual IP addesses must be in IPV4 standard format (i.e. ###.###.###.### or ###.###.###.###/##). Wildcards (*) or a netmask is allowed to specify a range of ip addresses.', 'better-wp-security' ) . '</li>';
1118
+ echo '<li>' . __( 'If using a wildcard (*) you must start with the right-most number in the ip field. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not.', 'better-wp-security' ) . '</li>';
1119
+ echo '<li><a href="http://ip-lookup.net/domain-lookup.php" target="_blank">' . __( 'Lookup IP Address.', 'better-wp-security' ) . '</a></li>';
1120
+ echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
1121
+ echo '</ul>';
1122
+ echo '<p class="description"><strong>' . __( 'This white list will prevent any ip listed from triggering an automatic lockout. You can still block the IP address manually in the banned users settings.', 'better-wp-security' ) . '</strong></p>';
1123
+
1124
+ }
1125
+
1126
+ /**
1127
+ * echos Log Location Field
1128
+ *
1129
+ * @since 4.0
1130
+ *
1131
+ * @return void
1132
+ */
1133
+ public function settings_field_log_location() {
1134
+
1135
+ global $itsec_globals;
1136
+
1137
+ if ( isset( $this->settings['log_location'] ) ) {
1138
+
1139
+ $log_location = sanitize_text_field( $this->settings['log_location'] );
1140
+
1141
+ } else {
1142
+
1143
+ $log_location = $itsec_globals['ithemes_log_dir'];
1144
+
1145
+ }
1146
+
1147
+ echo '<input class="large-text" name="itsec_global[log_location]" id="itsec_global_log_location" value="' . $log_location . '" type="text">';
1148
+ echo '<label for="itsec_global_log_location"> ' . __( 'The path on your server where log files should be stored.', 'better-wp-security' ) . '</label>';
1149
+ echo '<p class="description"> ' . __( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ) . '</p>';
1150
+ echo '<input id="itsec_reset_log_location" class="button-secondary" name="itsec_reset_log_location" type="button" value="' . __( 'Restore Default Location', 'better-wp-security' ) . '" />' . PHP_EOL;
1151
+
1152
+ }
1153
+
1154
+ /**
1155
+ * echos Log Rotation Field
1156
+ *
1157
+ * @since 4.0
1158
+ *
1159
+ * @return void
1160
+ */
1161
+ public function settings_field_log_rotation() {
1162
+
1163
+ if ( isset( $this->settings['log_rotation'] ) ) {
1164
+
1165
+ $log_rotation = absint( $this->settings['log_rotation'] );
1166
+
1167
+ } else {
1168
+
1169
+ $log_rotation = 14;
1170
+
1171
+ }
1172
+
1173
+ echo '<input class="small-text" name="itsec_global[log_rotation]" id="itsec_global_log_rotation" value="' . $log_rotation . '" type="text">';
1174
+ echo '<label for="itsec_global_log_rotation"> ' . __( 'Days', 'better-wp-security' ) . '</label>';
1175
+ echo '<p class="description"> ' . __( 'The number of days database logs should be kept. File logs will be kept indefinitely but will be rotated once the file hits 10MB.', 'better-wp-security' ) . '</p>';
1176
+
1177
+ }
1178
+
1179
+ /**
1180
+ * echos Log type Field
1181
+ *
1182
+ * @since 4.0
1183
+ *
1184
+ * @return void
1185
+ */
1186
+ public function settings_field_log_type() {
1187
+
1188
+ global $itsec_globals;
1189
+
1190
+ if ( isset( $this->settings['log_type'] ) ) {
1191
+
1192
+ $log_type = $this->settings['log_type'];
1193
+
1194
+ } else {
1195
+
1196
+ $log_type = 0;
1197
+
1198
+ }
1199
+
1200
+ echo '<select id="itsec_global_log_type" name="itsec_global[log_type]">';
1201
+
1202
+ echo '<option value="0" ' . selected( $log_type, '0' ) . '>' . __( 'Database Only', 'better-wp-security' ) . '</option>';
1203
+ echo '<option value="1" ' . selected( $log_type, '1' ) . '>' . __( 'File Only', 'better-wp-security' ) . '</option>';
1204
+ echo '<option value="2" ' . selected( $log_type, '2' ) . '>' . __( 'Both', 'better-wp-security' ) . '</option>';
1205
+ echo '</select>';
1206
+ echo '<label for="itsec_global_log_type"> ' . __( 'How should event logs be kept', 'better-wp-security' ) . '</label>';
1207
+ echo '<p class="description">' . $itsec_globals['plugin_name'] . __( ' can log events in multiple ways, each with advantages and disadvantages. Database Only puts all events in the database with your posts and other WordPress data. This makes it easy to retrieve and process but can be slower if the database table gets very large. File Only is very fast but the plugin does not process the logs itself as that would take far more resources. For most users or smaller sites Database Only should be fine. If you have a very large site or a log processing software then File Only might be a better option.' ) . '</p>';
1208
+
1209
+ }
1210
+
1211
+ /**
1212
+ * echos NGINX conf file Field
1213
+ *
1214
+ * @since 4.0
1215
+ *
1216
+ * @return void
1217
+ */
1218
+ public function settings_field_nginx_file() {
1219
+
1220
+ if ( isset( $this->settings['nginx_file'] ) ) {
1221
+
1222
+ $nginx_file = sanitize_text_field( $this->settings['nginx_file'] );
1223
+
1224
+ } else {
1225
+
1226
+ $nginx_file = ABSPATH . 'nginx.conf';
1227
+
1228
+ }
1229
+
1230
+ echo '<input class="large-text" name="itsec_global[nginx_file]" id="itsec_backup_nginx_file" value="' . $nginx_file . '" type="text">';
1231
+ echo '<label for="itsec_backup_nginx_file"> ' . __( 'The path on your server where backup files should be stored.', 'better-wp-security' ) . '</label>';
1232
+ echo '<p class="description"> ' . __( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ) . '</p>';
1233
+
1234
+ }
1235
+
1236
+ /**
1237
+ * echos Admin User Username Field
1238
+ *
1239
+ * @since 4.0
1240
+ *
1241
+ * @return void
1242
+ */
1243
+ public function settings_field_notification_email() {
1244
+
1245
+ if ( isset( $this->settings['notification_email'] ) && is_array( $this->settings['notification_email'] ) ) {
1246
+
1247
+ $emails = implode( PHP_EOL, $this->settings['notification_email'] );
1248
+
1249
+ } else {
1250
+
1251
+ $emails = get_option( 'admin_email' );
1252
+
1253
+ }
1254
+
1255
+ echo '<textarea id="itsec_global_notification_email" name="itsec_global[notification_email]">' . $emails . PHP_EOL . '</textarea>';
1256
+ echo '<p class="description">' . __( 'The email address(es) all security notifications will be sent to. One address per line.', 'better-wp-security' ) . '</p>';
1257
+
1258
+ }
1259
+
1260
+ /**
1261
+ * echos digest email field
1262
+ *
1263
+ * @since 4.5
1264
+ *
1265
+ * @return void
1266
+ */
1267
+ public function settings_field_proxy_override() {
1268
+
1269
+ if ( isset( $this->settings['proxy_override'] ) && $this->settings['proxy_override'] === true ) {
1270
+
1271
+ $proxy_override = 1;
1272
+
1273
+ } else {
1274
+
1275
+ $proxy_override = 0;
1276
+
1277
+ }
1278
+
1279
+ echo '<input type="checkbox" id="itsec_global_proxy_override" name="itsec_global[proxy_override]" value="1" ' . checked( 1, $proxy_override, false ) . '/>';
1280
+ echo '<label for="itsec_global_proxy_override">' . __( 'Disable Proxy IP Detection', 'better-wp-security' ) . '</label>';
1281
+
1282
+ printf(
1283
+ '<p class="description">%s</p>',
1284
+ __( 'If you\'re not using a proxy service such as Varnish, Cloudflare or others turning this on may result in more accurate IP detection.', 'better-wp-security' )
1285
+ );
1286
+
1287
+ }
1288
+
1289
+ /**
1290
+ * echos Admin User Username Field
1291
+ *
1292
+ * @since 4.0
1293
+ *
1294
+ * @return void
1295
+ */
1296
+ public function settings_field_user_lockout_message() {
1297
+
1298
+ if ( isset( $this->settings['user_lockout_message'] ) ) {
1299
+
1300
+ $user_lockout_message = wp_kses( $this->settings['user_lockout_message'], $this->allowed_tags );
1301
+
1302
+ } else {
1303
+
1304
+ $user_lockout_message = __( 'You have been locked out due to too many login attempts.', 'better-wp-security' );
1305
+
1306
+ }
1307
+
1308
+ echo '<textarea class="widefat" name="itsec_global[user_lockout_message]" id="itsec_global_user_lockout_message" rows="5" >' . $user_lockout_message . PHP_EOL . '</textarea><br />';
1309
+ echo '<p class="description">' . __( 'The message to display to a user when their account has been locked out.', 'better-wp-security' ) . '</p>';
1310
+ echo '<p class="description">' . __( 'You can use HTML in your message. Allowed tags include: a, br, em, strong, h1, h2, h3, h4, h5, h6, div', 'better-wp-security' ) . '</p>';
1311
+
1312
+ }
1313
+
1314
+ /**
1315
+ * echos write files Field
1316
+ *
1317
+ * @since 4.0
1318
+ *
1319
+ * @return void
1320
+ */
1321
+ public function settings_field_write_files() {
1322
+
1323
+ global $itsec_globals;
1324
+
1325
+ if ( isset( $this->settings['write_files'] ) && $this->settings['write_files'] === true ) {
1326
+
1327
+ $write_files = 1;
1328
+
1329
+ } else {
1330
+
1331
+ $write_files = 0;
1332
+
1333
+ }
1334
+
1335
+ if ( ITSEC_Lib::get_server() == 'nginx' ) {
1336
+
1337
+ $server_file = '';
1338
+
1339
+ } else {
1340
+
1341
+ $server_file = ' and .htaccess';
1342
+
1343
+ }
1344
+
1345
+ echo '<input type="checkbox" id="itsec_global_write_files" name="itsec_global[write_files]" value="1" ' . checked( 1, $write_files, false ) . '/>';
1346
+
1347
+ printf(
1348
+ '<label for="itsec_global_write_files">%s %s %s%s.</label>',
1349
+ __( 'Allow', 'better-wp-security' ),
1350
+ $itsec_globals['plugin_name'],
1351
+ __( 'to write to wp-config.php', 'better-wp-security' ),
1352
+ $server_file
1353
+ );
1354
+
1355
+ printf(
1356
+ '<p class="description">%s %s %s%s%s</p>',
1357
+ __( 'Whether or not', 'better-wp-security' ),
1358
+ $itsec_globals['plugin_name'],
1359
+ __( 'should be allowed to write to wp-config.php', 'better-wp-security' ),
1360
+ $server_file,
1361
+ __( ' automatically. If disabled you will need to manually place configuration options in those files.',
1362
+ 'better-wp-security' )
1363
+ );
1364
+
1365
+ }
1366
+
1367
+ /**
1368
+ * Performs actions for tracking tooltip function.
1369
+ *
1370
+ * @since 4.0
1371
+ *
1372
+ * return void
1373
+ */
1374
+ public function tooltip_ajax_tracking() {
1375
+
1376
+ $this->settings['allow_tracking'] = true;
1377
+
1378
+ $result = update_site_option( 'itsec_global', $this->settings );
1379
+
1380
+ if ( $result === true ) {
1381
+
1382
+ echo 'true';
1383
+
1384
+ } else {
1385
+
1386
+ echo 'false';
1387
+
1388
+ }
1389
+
1390
+ }
1391
+
1392
+ /**
1393
+ * Performs actions for writing tooltip function.
1394
+ *
1395
+ * @since 4.0
1396
+ *
1397
+ * return void
1398
+ */
1399
+ public function tooltip_ajax_writing() {
1400
+
1401
+ $this->settings['write_files'] = true;
1402
+
1403
+ $result = update_site_option( 'itsec_global', $this->settings );
1404
+
1405
+ if ( $result === true ) {
1406
+
1407
+ echo 'true';
1408
+
1409
+ } else {
1410
+
1411
+ echo 'false';
1412
+
1413
+ }
1414
+
1415
+ }
1416
+
1417
+ }
core/class-itsec-lib.php ADDED
@@ -0,0 +1,980 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Miscellaneous plugin-wide functions.
5
+ *
6
+ * Various static functions to provide information to modules and other areas throughout the plugin.
7
+ *
8
+ * @package iThemes_Security
9
+ *
10
+ * @since 4.0.0
11
+ */
12
+ final class ITSEC_Lib {
13
+
14
+ /**
15
+ * Converts CIDR to ip range.
16
+ *
17
+ * Modified from function at http://stackoverflow.com/questions/4931721/getting-list-ips-from-cidr-notation-in-php
18
+ * as it was far more elegant than my own solution
19
+ *
20
+ * @since 4.0.0.0
21
+ *
22
+ * @param string $cidr cidr notation to convert
23
+ *
24
+ * @return array range of ips returned
25
+ */
26
+ public static function cidr_to_range( $cidr ) {
27
+
28
+ $range = array();
29
+
30
+ if ( strpos( $cidr, '/' ) ) {
31
+
32
+ $cidr = explode( '/', $cidr );
33
+
34
+ $range[] = long2ip( ( ip2long( $cidr[0] ) ) & ( ( - 1 << ( 32 - (int) $cidr[1] ) ) ) );
35
+ $range[] = long2ip( ( ip2long( $cidr[0] ) ) + pow( 2, ( 32 - (int) $cidr[1] ) ) - 1 );
36
+
37
+ } else { //if not a range just return the original ip
38
+
39
+ $range[] = $cidr;
40
+
41
+ }
42
+
43
+ return $range;
44
+
45
+ }
46
+
47
+ /**
48
+ * Clear caches.
49
+ *
50
+ * Clears popular WordPress caching mechanisms.
51
+ *
52
+ * @since 4.0.0
53
+ *
54
+ * @param bool $page [optional] true to clear page cache
55
+ *
56
+ * @return void
57
+ */
58
+ public static function clear_caches( $page = false ) {
59
+
60
+ //clear APC Cache
61
+ if ( function_exists( 'apc_store' ) ) {
62
+ apc_clear_cache(); //Let's clear APC (if it exists) when big stuff is saved.
63
+ }
64
+
65
+ //clear w3 total cache or wp super cache
66
+ if ( function_exists( 'w3tc_pgcache_flush' ) ) {
67
+
68
+ if ( true == $page ) {
69
+ w3tc_pgcache_flush();
70
+ w3tc_minify_flush();
71
+ }
72
+
73
+ w3tc_dbcache_flush();
74
+ w3tc_objectcache_flush();
75
+
76
+ } else if ( function_exists( 'wp_cache_clear_cache' ) && true == $page ) {
77
+
78
+ wp_cache_clear_cache();
79
+
80
+ }
81
+
82
+ }
83
+
84
+ /**
85
+ * Creates appropriate database tables.
86
+ *
87
+ * Uses dbdelta to create database tables either on activation or in the event that one is missing.
88
+ *
89
+ * @since 4.0.0
90
+ *
91
+ * @return void
92
+ */
93
+ public static function create_database_tables() {
94
+
95
+ global $wpdb;
96
+
97
+ $charset_collate = '';
98
+
99
+ if ( ! empty( $wpdb->charset ) ) {
100
+ $charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
101
+ }
102
+
103
+ if ( ! empty( $wpdb->collate ) ) {
104
+ $charset_collate .= " COLLATE $wpdb->collate";
105
+ }
106
+
107
+ //Set up log table
108
+ $tables = "CREATE TABLE " . $wpdb->prefix . "itsec_log (
109
+ log_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
110
+ log_type varchar(20) NOT NULL DEFAULT '',
111
+ log_function varchar(255) NOT NULL DEFAULT '',
112
+ log_priority int(2) NOT NULL DEFAULT 1,
113
+ log_date datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
114
+ log_date_gmt datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
115
+ log_host varchar(20),
116
+ log_username varchar(20),
117
+ log_user bigint(20) UNSIGNED,
118
+ log_url varchar(255),
119
+ log_referrer varchar(255),
120
+ log_data longtext NOT NULL,
121
+ PRIMARY KEY (log_id),
122
+ KEY log_type (log_type),
123
+ KEY log_date_gmt (log_date_gmt)
124
+ ) " . $charset_collate . ";";
125
+
126
+ //set up lockout table
127
+ $tables .= "CREATE TABLE " . $wpdb->prefix . "itsec_lockouts (
128
+ lockout_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
129
+ lockout_type varchar(20) NOT NULL,
130
+ lockout_start datetime NOT NULL,
131
+ lockout_start_gmt datetime NOT NULL,
132
+ lockout_expire datetime NOT NULL,
133
+ lockout_expire_gmt datetime NOT NULL,
134
+ lockout_host varchar(20),
135
+ lockout_user bigint(20) UNSIGNED,
136
+ lockout_username varchar(20),
137
+ lockout_active int(1) NOT NULL DEFAULT 1,
138
+ PRIMARY KEY (lockout_id),
139
+ KEY lockout_expire_gmt (lockout_expire_gmt),
140
+ KEY lockout_host (lockout_host),
141
+ KEY lockout_user (lockout_user),
142
+ KEY lockout_username (lockout_username),
143
+ KEY lockout_active (lockout_active)
144
+ ) " . $charset_collate . ";";
145
+
146
+ //set up temp table
147
+ $tables .= "CREATE TABLE " . $wpdb->prefix . "itsec_temp (
148
+ temp_id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
149
+ temp_type varchar(20) NOT NULL,
150
+ temp_date datetime NOT NULL,
151
+ temp_date_gmt datetime NOT NULL,
152
+ temp_host varchar(20),
153
+ temp_user bigint(20) UNSIGNED,
154
+ temp_username varchar(20),
155
+ PRIMARY KEY (temp_id),
156
+ KEY temp_date_gmt (temp_date_gmt),
157
+ KEY temp_host (temp_host),
158
+ KEY temp_user (temp_user),
159
+ KEY temp_username (temp_username)
160
+ ) " . $charset_collate . ";";
161
+
162
+ require_once ABSPATH . 'wp-admin/includes/upgrade.php';
163
+ @dbDelta( $tables );
164
+
165
+ }
166
+
167
+ /**
168
+ * Gets location of wp-config.php.
169
+ *
170
+ * Finds and returns path to wp-config.php
171
+ *
172
+ * @since 4.0.0
173
+ *
174
+ * @return string path to wp-config.php
175
+ * */
176
+ public static function get_config() {
177
+
178
+ if ( file_exists( trailingslashit( ABSPATH ) . 'wp-config.php' ) ) {
179
+
180
+ return trailingslashit( ABSPATH ) . 'wp-config.php';
181
+
182
+ } else {
183
+
184
+ return trailingslashit( dirname( ABSPATH ) ) . 'wp-config.php';
185
+
186
+ }
187
+
188
+ }
189
+
190
+ /**
191
+ * Gets current url
192
+ *
193
+ * Finds and returns current url.
194
+ *
195
+ * @since 4.3.0
196
+ *
197
+ * @return string current url
198
+ * */
199
+ public static function get_current_url() {
200
+
201
+ $page_url = 'http';
202
+
203
+ if ( isset( $_SERVER["HTTPS"] ) ) {
204
+
205
+ if ( 'on' == $_SERVER["HTTPS"] ) {
206
+ $page_url .= "s";
207
+ }
208
+
209
+ }
210
+
211
+ $page_url .= "://";
212
+
213
+ if ( '80' != $_SERVER["SERVER_PORT"] ) {
214
+
215
+ $page_url .= $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
216
+
217
+ } else {
218
+
219
+ $page_url .= $_SERVER["SERVER_NAME"] . $_SERVER["REQUEST_URI"];
220
+
221
+ }
222
+
223
+ return esc_url( $page_url );
224
+ }
225
+
226
+ /**
227
+ * Return primary domain from given url.
228
+ *
229
+ * Returns primary domain name (without subdomains) of given URL.
230
+ *
231
+ * @since 4.0.0
232
+ *
233
+ * @param string $url URL to filter
234
+ *
235
+ * @return string domain name or '*' on error or domain mapped multisite
236
+ * */
237
+ public static function get_domain( $url ) {
238
+ if ( is_multisite() && function_exists( 'domain_mapping_warning' ) ) {
239
+ return '*';
240
+ }
241
+
242
+
243
+ $host = parse_url( $url, PHP_URL_HOST );
244
+
245
+ if ( false === $host ) {
246
+ return '*';
247
+ }
248
+ if ( 'www.' == substr( $host, 0, 4 ) ) {
249
+ return substr( $host, 4 );
250
+ }
251
+
252
+ $host_parts = explode( '.', $host );
253
+
254
+ if ( count( $host_parts ) > 2 ) {
255
+ $host_parts = array_slice( $host_parts, -2, 2 );
256
+ }
257
+
258
+ return implode( '.', $host_parts );
259
+ }
260
+
261
+ /**
262
+ * Get path to WordPress install.
263
+ *
264
+ * Get the absolute filesystem path to the root of the WordPress installation.
265
+ *
266
+ * @since 4.3.0
267
+ *
268
+ * @return string Full filesystem path to the root of the WordPress installation
269
+ */
270
+ public static function get_home_path() {
271
+
272
+ $home = set_url_scheme( get_option( 'home' ), 'http' );
273
+ $siteurl = set_url_scheme( get_option( 'siteurl' ), 'http' );
274
+
275
+ if ( ! empty( $home ) && 0 !== strcasecmp( $home, $siteurl ) ) {
276
+
277
+ $wp_path_rel_to_home = str_ireplace( $home, '', $siteurl ); /* $siteurl - $home */
278
+ $pos = strripos( str_replace( '\\', '/', $_SERVER['SCRIPT_FILENAME'] ), trailingslashit( $wp_path_rel_to_home ) );
279
+
280
+ if ( $pos === false ) {
281
+
282
+ $home_path = dirname( $_SERVER['SCRIPT_FILENAME'] );
283
+
284
+ } else {
285
+
286
+ $home_path = substr( $_SERVER['SCRIPT_FILENAME'], 0, $pos );
287
+
288
+ }
289
+
290
+ } else {
291
+
292
+ $home_path = ABSPATH;
293
+
294
+ }
295
+
296
+ return trailingslashit( str_replace( '\\', '/', $home_path ) );
297
+
298
+ }
299
+
300
+ /**
301
+ * Returns the root of the WordPress install.
302
+ *
303
+ * Get's the URI path to the WordPress installation.
304
+ *
305
+ * @since 4.0.6
306
+ *
307
+ * @return string the root folder
308
+ */
309
+ public static function get_home_root() {
310
+
311
+ //homeroot from wp_rewrite
312
+ $home_root = parse_url( site_url() );
313
+
314
+ if ( isset( $home_root['path'] ) ) {
315
+
316
+ $home_root = trailingslashit( $home_root['path'] );
317
+
318
+ } else {
319
+
320
+ $home_root = '/';
321
+
322
+ }
323
+
324
+ return $home_root;
325
+
326
+ }
327
+
328
+ /**
329
+ * Gets location of .htaccess
330
+ *
331
+ * Finds and returns path to .htaccess or nginx.conf if appropriate
332
+ *
333
+ * @since 4.0.0
334
+ *
335
+ * @return string path to .htaccess
336
+ */
337
+ public static function get_htaccess() {
338
+
339
+ global $itsec_globals;
340
+
341
+ if ( 'nginx' === ITSEC_Lib::get_server() ) {
342
+
343
+ return $itsec_globals['settings']['nginx_file'];
344
+
345
+ } else {
346
+
347
+ return ITSEC_Lib::get_home_path() . '.htaccess';
348
+
349
+ }
350
+
351
+ }
352
+
353
+ /**
354
+ * Returns the actual IP address of the user.
355
+ *
356
+ * Determines the user's IP address by returning the forwarded IP address if present or
357
+ * the direct IP address if not.
358
+ *
359
+ * @since 4.0.0
360
+ *
361
+ * @return String The IP address of the user
362
+ */
363
+ public static function get_ip() {
364
+
365
+ global $itsec_globals;
366
+
367
+ if ( isset( $itsec_globals['settings']['proxy_override'] ) && true === $itsec_globals['settings']['proxy_override'] ) {
368
+ return esc_sql( $_SERVER['REMOTE_ADDR'] );
369
+ }
370
+
371
+ //Just get the headers if we can or else use the SERVER global
372
+ if ( function_exists( 'apache_request_headers' ) ) {
373
+
374
+ $headers = apache_request_headers();
375
+
376
+ } else {
377
+
378
+ $headers = $_SERVER;
379
+
380
+ }
381
+
382
+ //Get the forwarded IP if it exists
383
+ if ( array_key_exists( 'X-Forwarded-For', $headers ) &&
384
+ (
385
+ filter_var( $headers['X-Forwarded-For'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ||
386
+ filter_var( $headers['X-Forwarded-For'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 ) )
387
+ ) {
388
+
389
+ $the_ip = $headers['X-Forwarded-For'];
390
+
391
+ } elseif (
392
+ array_key_exists( 'HTTP_X_FORWARDED_FOR', $headers ) &&
393
+ (
394
+ filter_var( $headers['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) ||
395
+ filter_var( $headers['HTTP_X_FORWARDED_FOR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 )
396
+ )
397
+ ) {
398
+
399
+ $the_ip = $headers['HTTP_X_FORWARDED_FOR'];
400
+
401
+ } else if ( isset( $_SERVER['REMOTE_ADDR'] ) ) {
402
+
403
+ $the_ip = $_SERVER['REMOTE_ADDR'];
404
+
405
+ } else {
406
+ $the_ip = '';
407
+ }
408
+
409
+ return esc_sql( $the_ip );
410
+
411
+ }
412
+
413
+ /**
414
+ * Gets PHP Memory Limit.
415
+ *
416
+ * Attempts to get the maximum amount of memory allowed for the application by the server.
417
+ *
418
+ * @since 4.0.0
419
+ *
420
+ * @return int php memory limit in megabytes
421
+ */
422
+ public static function get_memory_limit() {
423
+
424
+ return (int) ini_get( 'memory_limit' );
425
+
426
+ }
427
+
428
+ /**
429
+ * Returns the URL of the current module.
430
+ *
431
+ * Get's the full URL of the current module.
432
+ *
433
+ * @since 4.0.0
434
+ *
435
+ * @param string $file the module file from which to derive the path
436
+ *
437
+ * @return string the path of the current module
438
+ */
439
+ public static function get_module_path( $file ) {
440
+
441
+ global $itsec_globals;
442
+
443
+ $path = str_replace( $itsec_globals['plugin_dir'], '', dirname( $file ) );
444
+ $path = ltrim( str_replace( '\\', '/', $path ), '/' );
445
+
446
+ return trailingslashit( trailingslashit( $itsec_globals['plugin_url'] ) . $path );
447
+
448
+ }
449
+
450
+ /**
451
+ * Returns a psuedo-random string of requested length.
452
+ *
453
+ * Builds a random string similar to the WordPress password functions.
454
+ *
455
+ * @since 4.0.0
456
+ *
457
+ * @param int $length how long the string should be (max 62)
458
+ * @param bool $base32 true if use only base32 characters to generate
459
+ * @param bool $special_chars whether to include special characters in generation
460
+ *
461
+ * @return string
462
+ */
463
+ public static function get_random( $length, $base32 = false, $special_chars = false ) {
464
+
465
+ if ( true === $base32 ) {
466
+
467
+ $string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
468
+
469
+ } else {
470
+
471
+ $string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
472
+
473
+ if ( true === $special_chars ) {
474
+
475
+ $string .= '_)(*&^%$#@!~`:;<>,.?/{}[]|';
476
+
477
+ }
478
+
479
+ }
480
+
481
+ return substr( str_shuffle( $string ), mt_rand( 0, strlen( $string ) - $length ), $length );
482
+
483
+ }
484
+
485
+ /**
486
+ * Returns the server type of the plugin user.
487
+ *
488
+ * Attempts to figure out what http server the visiting user is running.
489
+ *
490
+ * @since 4.0.0
491
+ *
492
+ * @return string|bool server type the user is using of false if undetectable.
493
+ */
494
+ public static function get_server() {
495
+
496
+ //Allows to override server authentication for testing or other reasons.
497
+ if ( defined( 'ITSEC_SERVER_OVERRIDE' ) ) {
498
+ return ITSEC_SERVER_OVERRIDE;
499
+ }
500
+
501
+ $server_raw = strtolower( filter_var( $_SERVER['SERVER_SOFTWARE'], FILTER_SANITIZE_STRING ) );
502
+
503
+ //figure out what server they're using
504
+ if ( false !== strpos( $server_raw, 'apache' ) ) {
505
+
506
+ return 'apache';
507
+
508
+ } elseif ( false !== strpos( $server_raw, 'nginx' ) ) {
509
+
510
+ return 'nginx';
511
+
512
+ } elseif ( false !== strpos( $server_raw, 'litespeed' ) ) {
513
+
514
+ return 'litespeed';
515
+
516
+ } else { //unsupported server
517
+
518
+ return false;
519
+
520
+ }
521
+
522
+ }
523
+
524
+ /**
525
+ * Determine whether the server supports SSL (shared cert not supported.
526
+ *
527
+ * Attempts to retrieve an HTML version of the homepage in an effort to determine if SSL is available.
528
+ *
529
+ * @since 4.0.0
530
+ *
531
+ * @return bool true if ssl is supported or false
532
+ */
533
+ public static function get_ssl() {
534
+
535
+ $url = str_ireplace( 'http://', 'https://', get_bloginfo( 'url' ) );
536
+
537
+ if ( function_exists( 'wp_http_supports' ) && wp_http_supports( array( 'ssl' ), $url ) ) {
538
+
539
+ return true;
540
+
541
+ } elseif ( function_exists( 'curl_init' ) ) {
542
+
543
+ //use a manual CURL request to better account for self-signed certificates
544
+ $timeout = 5; //timeout for the request
545
+ $site_title = trim( get_bloginfo() );
546
+
547
+ $request = curl_init();
548
+
549
+ curl_setopt( $request, CURLOPT_RETURNTRANSFER, true );
550
+ curl_setopt( $request, CURLOPT_VERBOSE, false );
551
+ curl_setopt( $request, CURLOPT_SSL_VERIFYPEER, false );
552
+ curl_setopt( $request, CURLOPT_HEADER, true );
553
+ curl_setopt( $request, CURLOPT_URL, $url );
554
+ curl_setopt( $request, CURLOPT_RETURNTRANSFER, true );
555
+ curl_setopt( $request, CURLOPT_CONNECTTIMEOUT, $timeout );
556
+
557
+ $data = curl_exec( $request );
558
+
559
+ $header_size = curl_getinfo( $request, CURLINFO_HEADER_SIZE );
560
+ $http_code = intval( curl_getinfo( $request, CURLINFO_HTTP_CODE ) );
561
+ $body = substr( $data, $header_size );
562
+
563
+ preg_match( '/<title>(.+)<\/title>/', $body, $matches );
564
+
565
+ if ( 200 == $http_code && isset( $matches[1] ) && false !== strpos( $matches[1], $site_title ) ) {
566
+
567
+ return true;
568
+
569
+ } else {
570
+
571
+ return false;
572
+
573
+ }
574
+
575
+ }
576
+
577
+ return false;
578
+
579
+ }
580
+
581
+ /**
582
+ * Converts IP with a netmask wildcards to one with * instead
583
+ *
584
+ * Allows use of wildcards in IP address by converting them to standard notation.
585
+ *
586
+ * @since 4.0.0
587
+ *
588
+ * @param string $ip ip to convert
589
+ *
590
+ * @return string the converted ip
591
+ */
592
+ public static function ip_mask_to_range( $ip ) {
593
+
594
+ if ( strpos( $ip, '/' ) ) {
595
+
596
+ $parts = explode( '/', trim( $ip ) );
597
+ $octets = array_reverse( explode( '.', trim( $parts[0] ) ) );
598
+
599
+ if ( isset( $parts[1] ) && 0 < intval( $parts[1] ) ) {
600
+
601
+ $wildcards = ( 32 - $parts[1] ) / 8;
602
+
603
+ for ( $count = 0; $count < $wildcards; $count ++ ) {
604
+
605
+ $octets[$count] = '[0-9]+';
606
+
607
+ }
608
+
609
+ return implode( '.', array_reverse( $octets ) );
610
+
611
+ } else {
612
+
613
+ return $ip;
614
+
615
+ }
616
+
617
+ }
618
+
619
+ return $ip;
620
+
621
+ }
622
+
623
+ /**
624
+ * Converts IP with * wildcards to one with a netmask instead
625
+ *
626
+ * Attempts to create a standardized CIDR block from an IP using wildcards.
627
+ *
628
+ * @since 4.0.0
629
+ *
630
+ * @param string $ip ip to convert
631
+ *
632
+ * @return string the converted ip
633
+ */
634
+ public static function ip_wild_to_mask( $ip ) {
635
+
636
+ $host_parts = array_reverse( explode( '.', trim( $ip ) ) );
637
+
638
+ if ( strpos( $ip, '*' ) ) {
639
+
640
+ $mask = 32; //used to calculate netmask with wildcards
641
+ $converted_host = str_replace( '*', '0', $ip );
642
+
643
+ //convert hosts with wildcards to host with netmask and create rule lines
644
+ foreach ( $host_parts as $part ) {
645
+
646
+ if ( '*' === $part ) {
647
+ $mask = $mask - 8;
648
+ }
649
+
650
+ }
651
+
652
+ $converted_host = trim( $converted_host );
653
+
654
+ //Apply a mask if we had to convert
655
+ if ( 0 < $mask ) {
656
+ $converted_host .= '/' . $mask;
657
+ }
658
+
659
+ return $converted_host;
660
+
661
+ }
662
+
663
+ return $ip;
664
+
665
+ }
666
+
667
+ /**
668
+ * Determine whether we're on the login page or not.
669
+ *
670
+ * Attempts to determine whether or not the user is on the WordPress dashboard login page.
671
+ *
672
+ * @since 4.0.0
673
+ *
674
+ * @return bool true if is login page else false
675
+ */
676
+ public static function is_login_page() {
677
+
678
+ return in_array( $GLOBALS['pagenow'], array( 'wp-login.php', 'wp-register.php' ) );
679
+
680
+ }
681
+
682
+ /**
683
+ * Checks jQuery version.
684
+ *
685
+ * Checks if the jquery version saved is vulnerable to http://bugs.jquery.com/ticket/9521
686
+ *
687
+ * @since 4.0.0
688
+ *
689
+ * @return mixed|bool true if known safe false if unsafe or null if untested
690
+ */
691
+ public static function safe_jquery_version() {
692
+
693
+ $jquery_version = get_site_option( 'itsec_jquery_version' );
694
+
695
+ if ( false !== $jquery_version && version_compare( $jquery_version, '1.6.3', '>=' ) ) {
696
+
697
+ return true;
698
+
699
+ } elseif ( false === $jquery_version ) {
700
+
701
+ return null;
702
+
703
+ }
704
+
705
+ return false;
706
+
707
+ }
708
+
709
+ /**
710
+ * Set a 404 error.
711
+ *
712
+ * Forces the given page to a WordPress 404 error.
713
+ *
714
+ * @since 4.0.0
715
+ *
716
+ * @return void
717
+ */
718
+ public static function set_404() {
719
+
720
+ global $wp_query;
721
+
722
+ status_header( 404 );
723
+
724
+ if ( function_exists( 'nocache_headers' ) ) {
725
+ nocache_headers();
726
+ }
727
+
728
+ $wp_query->set_404();
729
+ $page_404 = get_404_template();
730
+
731
+ if ( 1 < strlen( $page_404 ) ) {
732
+
733
+ include( $page_404 );
734
+
735
+ } else {
736
+
737
+ include( get_query_template( 'index' ) );
738
+
739
+ }
740
+
741
+ die();
742
+
743
+ }
744
+
745
+ /**
746
+ * Increases minimum memory limit.
747
+ *
748
+ * This function, adopted from builder, attempts to increase the minimum
749
+ * memory limit before heavy functions.
750
+ *
751
+ * @since 4.0.0
752
+ *
753
+ * @param int $new_memory_limit what the new memory limit should be
754
+ *
755
+ * @return void
756
+ */
757
+ public static function set_minimum_memory_limit( $new_memory_limit ) {
758
+
759
+ $memory_limit = @ini_get( 'memory_limit' );
760
+
761
+ if ( - 1 < $memory_limit ) {
762
+
763
+ $unit = strtolower( substr( $memory_limit, - 1 ) );
764
+
765
+ $new_unit = strtolower( substr( $new_memory_limit, - 1 ) );
766
+
767
+ if ( 'm' == $unit ) {
768
+
769
+ $memory_limit *= 1048576;
770
+
771
+ } else if ( 'g' == $unit ) {
772
+
773
+ $memory_limit *= 1073741824;
774
+
775
+ } else if ( 'k' == $unit ) {
776
+
777
+ $memory_limit *= 1024;
778
+
779
+ }
780
+
781
+ if ( 'm' == $new_unit ) {
782
+
783
+ $new_memory_limit *= 1048576;
784
+
785
+ } else if ( 'g' == $new_unit ) {
786
+
787
+ $new_memory_limit *= 1073741824;
788
+
789
+ } else if ( 'k' == $new_unit ) {
790
+
791
+ $new_memory_limit *= 1024;
792
+
793
+ }
794
+
795
+ if ( (int) $memory_limit < (int) $new_memory_limit ) {
796
+ @ini_set( 'memory_limit', $new_memory_limit );
797
+ }
798
+
799
+ }
800
+
801
+ }
802
+
803
+ /**
804
+ * Checks if user exists.
805
+ *
806
+ * Checks to see if WordPress user with given id exists.
807
+ *
808
+ * @since 4.0.0
809
+ *
810
+ * @param int $user_id user id of user to check
811
+ *
812
+ * @return bool true if user exists otherwise false
813
+ *
814
+ * */
815
+ public static function user_id_exists( $user_id ) {
816
+
817
+ global $wpdb;
818
+
819
+ //return false if username is null
820
+ if ( '' == $user_id ) {
821
+ return false;
822
+ }
823
+
824
+ //queary the user table to see if the user is there
825
+ $saved_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM `" . $wpdb->users . "` WHERE ID='%s';", sanitize_text_field( $user_id ) ) );
826
+
827
+ if ( $saved_id == $user_id ) {
828
+
829
+ return true;
830
+
831
+ } else {
832
+
833
+ return false;
834
+
835
+ }
836
+
837
+ }
838
+
839
+ /**
840
+ * Validates a list of ip addresses.
841
+ *
842
+ * Makes sure that the provided IP addresses are in fact valid IPV4 addresses.
843
+ *
844
+ * @since 4.0.0
845
+ *
846
+ * @param string $ip string of hosts to check
847
+ *
848
+ * @return array array of good hosts or false
849
+ */
850
+ public static function validates_ip_address( $ip ) {
851
+ $ip = trim( filter_var( $ip, FILTER_SANITIZE_STRING ) );
852
+
853
+ if ( substr_count( $ip, '.' ) !== 3 ) {
854
+ return false;
855
+ }
856
+
857
+ $has_cidr = ( false !== strpos( $ip, '/' ) );
858
+ $has_wildcard = ( false !== strpos( $ip, '*' ) );
859
+
860
+ if ( $has_cidr && $has_wildcard ) {
861
+ return false;
862
+ }
863
+
864
+ $ip_digit_regex = '(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';
865
+ $cidr_digit_regex = '(?:3[0-2]|2[0-9]|1[1-9]|[148])';
866
+
867
+ $ip_regex = "(?:$ip_digit_regex\.){3}$ip_digit_regex";
868
+
869
+ if ( $has_cidr ) {
870
+ return (boolean) preg_match( "{^$ip_regex/$cidr_digit_regex$}", $ip );
871
+ }
872
+
873
+ if ( $has_wildcard ) {
874
+ $wildcard_count = substr_count( $ip, '*' );
875
+
876
+ if ( 1 === $wildcard_count ) {
877
+ return (boolean) preg_match( "{^(?:$ip_digit_regex\.){3}\*$}", $ip );
878
+ } else if ( 2 === $wildcard_count ) {
879
+ return (boolean) preg_match( "{^(?:$ip_digit_regex\.){2}\*\.\*$}", $ip );
880
+ } else if ( 3 === $wildcard_count ) {
881
+ return (boolean) preg_match( "{^(?:$ip_digit_regex\.)\*\.\*\.\*$}", $ip );
882
+ }
883
+
884
+ return false;
885
+ }
886
+
887
+ return (boolean) preg_match( "{^$ip_regex$}", $ip );
888
+ }
889
+
890
+ /**
891
+ * Validates a file path
892
+ *
893
+ * Adapted from http://stackoverflow.com/questions/4049856/replace-phps-realpath/4050444#4050444 as a replacement for PHP's realpath
894
+ *
895
+ * @since 4.0.0
896
+ *
897
+ * @param string $path The original path, can be relative etc.
898
+ *
899
+ * @return bool true if the path is valid and writeable else false
900
+ */
901
+ public static function validate_path( $path ) {
902
+
903
+ // whether $path is unix or not
904
+ $unipath = strlen( $path ) == 0 || $path{0} != '/';
905
+
906
+ // attempts to detect if path is relative in which case, add cwd
907
+ if ( false === strpos( $path, ':' ) && $unipath ) {
908
+ $path = getcwd() . DIRECTORY_SEPARATOR . $path;
909
+ }
910
+
911
+ // resolve path parts (single dot, double dot and double delimiters)
912
+ $path = str_replace( array( '/', '\\' ), DIRECTORY_SEPARATOR, $path );
913
+ $parts = array_filter( explode( DIRECTORY_SEPARATOR, $path ), 'strlen' );
914
+ $absolutes = array();
915
+
916
+ foreach ( $parts as $part ) {
917
+
918
+ if ( '.' == $part ) {
919
+ continue;
920
+ }
921
+
922
+ if ( '..' == $part ) {
923
+
924
+ array_pop( $absolutes );
925
+
926
+ } else {
927
+
928
+ $absolutes[] = $part;
929
+
930
+ }
931
+
932
+ }
933
+
934
+ $path = implode( DIRECTORY_SEPARATOR, $absolutes );
935
+
936
+ // resolve any symlinks
937
+ if ( function_exists( 'linkinfo' ) ) { //linkinfo not available on Windows with PHP < 5.3.0
938
+
939
+ if ( file_exists( $path ) && 0 < linkinfo( $path ) ) {
940
+ $path = @readlink( $path );
941
+ }
942
+
943
+ } else {
944
+
945
+ if ( file_exists( $path ) && 0 < linkinfo( $path ) ) {
946
+ $path = @readlink( $path );
947
+ }
948
+
949
+ }
950
+
951
+ // put initial separator that could have been lost
952
+ $path = ! $unipath ? '/' . $path : $path;
953
+
954
+ $test = @touch( $path . '/test.txt' );
955
+ @unlink( $path . '/test.txt' );
956
+
957
+ return $test;
958
+
959
+ }
960
+
961
+ /**
962
+ * Validates a URL
963
+ *
964
+ * Ensures the provided URL is a valid URL.
965
+ *
966
+ * @since 4.3.0
967
+ *
968
+ * @param string $url the url to validate
969
+ *
970
+ * @return bool true if valid url else false
971
+ */
972
+ public static function validate_url( $url ) {
973
+
974
+ $pattern = "/^(http|https|ftp):\/\/([A-Z0-9][A-Z0-9_-]*(?:\.[A-Z0-9][A-Z0-9_-]*)+):?(\d+)?\/?/i";
975
+
976
+ return (bool) preg_match( $pattern, $url );
977
+
978
+ }
979
+
980
+ }
core/class-itsec-lockout.php ADDED
@@ -0,0 +1,1334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handles lockouts for modules and core
5
+ *
6
+ * @package iThemes-Security
7
+ * @since 4.0
8
+ */
9
+ class ITSEC_Lockout {
10
+
11
+ private
12
+ $core,
13
+ $lockout_modules;
14
+
15
+ function __construct( $core ) {
16
+
17
+ $this->core = $core;
18
+ $this->lockout_modules = array(); //array to hold information on modules using this feature
19
+
20
+ //Run database cleanup daily with cron
21
+ if ( ! wp_next_scheduled( 'itsec_purge_lockouts' ) ) {
22
+ wp_schedule_event( time(), 'daily', 'itsec_purge_lockouts' );
23
+ }
24
+
25
+ add_action( 'itsec_purge_lockouts', array( $this, 'purge_lockouts' ) );
26
+
27
+ //Check for host lockouts
28
+ add_action( 'init', array( $this, 'check_lockout' ) );
29
+
30
+ //Register all plugin modules
31
+ add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
32
+
33
+ //Set an error message on improper logout
34
+ add_action( 'login_head', array( $this, 'set_lockout_error' ) );
35
+
36
+ //Add the metabox
37
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) );
38
+
39
+ //Process clear lockout form
40
+ add_action( 'itsec_admin_init', array( $this, 'release_lockout' ) );
41
+
42
+ //Register Logger
43
+ add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
44
+
45
+ //Register Sync
46
+ add_filter( 'itsec_sync_modules', array( $this, 'register_sync' ) );
47
+
48
+ //Add Javascripts script
49
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
50
+
51
+ //Run ajax for temp whitelist
52
+ add_action( 'wp_ajax_itsec_temp_whitelist_ajax', array( $this, 'itsec_temp_whitelist_ajax' ) );
53
+ add_action( 'wp_ajax_itsec_temp_whitelist_release_ajax', array( $this, 'itsec_temp_whitelist_release_ajax' ) );
54
+
55
+ }
56
+
57
+ /**
58
+ * Add meta boxes to primary options pages.
59
+ *
60
+ * @since 4.0
61
+ *
62
+ * @return void
63
+ */
64
+ function add_admin_meta_boxes() {
65
+
66
+ add_meta_box(
67
+ 'itsec_lockouts',
68
+ __( 'Active Lockouts', 'better-wp-security' ),
69
+ array( $this, 'lockout_metabox' ),
70
+ 'toplevel_page_itsec',
71
+ 'bottom',
72
+ 'core'
73
+ );
74
+
75
+ $lockout_pages = array(
76
+ 'toplevel_page_itsec',
77
+ 'security_page_toplevel_page_itsec_settings',
78
+ 'security_page_toplevel_page_itsec_logs'
79
+ );
80
+
81
+ foreach ( $lockout_pages as $page ) {
82
+
83
+ add_meta_box(
84
+ 'itsec_self_protect',
85
+ __( "Don't Lock Yourself Out", 'better-wp-security' ),
86
+ array( $this, 'self_protect_metabox' ),
87
+ $page,
88
+ 'top',
89
+ 'core'
90
+ );
91
+
92
+ }
93
+
94
+ }
95
+
96
+ /**
97
+ * Add Tracking Javascript.
98
+ *
99
+ * Adds javascript for tracking settings to all itsec admin pages
100
+ *
101
+ * @since 4.3
102
+ *
103
+ * @return void
104
+ */
105
+ public function admin_script() {
106
+
107
+ global $itsec_globals;
108
+
109
+ //scripts for all itsec pages
110
+ if ( isset( get_current_screen()->id ) && ( strpos( get_current_screen()->id, 'itsec' ) !== false || strpos( get_current_screen()->id, 'dashboard' ) !== false ) ) {
111
+
112
+ wp_enqueue_script( 'itsec_temp_whitelist', $itsec_globals['plugin_url'] . 'core/js/admin-whitelist.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
113
+ wp_localize_script( 'itsec_temp_whitelist', 'itsec_temp_whitelist', array(
114
+ 'nonce' => wp_create_nonce( 'itsec_temp_whitelist_nonce' ),
115
+ 'success' => __( 'Temporarily Whitelist my IP', 'better-wp-security' ),
116
+ ) );
117
+
118
+ }
119
+
120
+ }
121
+
122
+ /**
123
+ * Checks if the host or user is locked out and executes lockout
124
+ *
125
+ * @since 4.0
126
+ *
127
+ * @param mixed $user WordPress user object or false
128
+ * @param mixed $username the username to check
129
+ *
130
+ * @return void
131
+ */
132
+ public function check_lockout( $user = false, $username = false ) {
133
+
134
+ global $wpdb, $itsec_globals;
135
+
136
+ $wpdb->hide_errors(); //Hide database errors in case the tables aren't there
137
+
138
+ $host = ITSEC_Lib::get_ip();
139
+ $username = sanitize_text_field( trim( $username ) );
140
+ $username_check = false;
141
+ $user_check = false;
142
+ $host_check = false;
143
+
144
+ if ( $user !== false && $user !== '' && $user !== null ) {
145
+
146
+ $user = get_userdata( intval( $user ) );
147
+ $user_id = $user->ID;
148
+
149
+ } else {
150
+
151
+ $user = wp_get_current_user();
152
+ $user_id = $user->ID;
153
+
154
+ if ( $username !== false && $username != '' ) {
155
+ $username_check = $wpdb->get_var( "SELECT `lockout_username` FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . "' AND `lockout_username`='" . $username . "';" );
156
+ }
157
+
158
+ $host_check = $wpdb->get_var( "SELECT `lockout_host` FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . "' AND `lockout_host`='" . $host . "';" );
159
+
160
+ }
161
+
162
+ if ( $user_id !== 0 && $user_id !== null ) {
163
+
164
+ $user_check = $wpdb->get_var( "SELECT `lockout_user` FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_active`=1 AND `lockout_expire_gmt` > '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . "' AND `lockout_user`=" . intval( $user_id ) . ";" );
165
+
166
+ }
167
+
168
+ $error = $wpdb->last_error;
169
+
170
+ if ( strlen( trim( $error ) ) > 0 ) {
171
+ ITSEC_Lib::create_database_tables();
172
+ }
173
+
174
+ if ( $host_check !== null && $host_check !== false ) {
175
+
176
+ $this->execute_lock();
177
+
178
+ } elseif ( ( $user_check !== false && $user_check !== null ) || ( $username_check !== false && $username_check !== null ) ) {
179
+
180
+ $this->execute_lock( true );
181
+
182
+ }
183
+
184
+ }
185
+
186
+ /**
187
+ * Executes lockout and logging for modules
188
+ *
189
+ * @since 4.0
190
+ *
191
+ * @param string $module string name of the calling module
192
+ * @param string $user username of user
193
+ *
194
+ * @return void
195
+ */
196
+ public function do_lockout( $module, $user = null ) {
197
+
198
+ global $wpdb, $itsec_globals;
199
+
200
+ $wpdb->hide_errors(); //Hide database errors in case the tables aren't there
201
+
202
+ $lock_host = null;
203
+ $lock_user = null;
204
+ $lock_username = null;
205
+ $options = $this->lockout_modules[$module];
206
+
207
+ $host = ITSEC_Lib::get_ip();
208
+
209
+ if ( isset( $options['host'] ) && $options['host'] > 0 ) {
210
+
211
+ $wpdb->insert(
212
+ $wpdb->base_prefix . 'itsec_temp',
213
+ array(
214
+ 'temp_type' => $options['type'],
215
+ 'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
216
+ 'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
217
+ 'temp_host' => $host,
218
+ )
219
+ );
220
+
221
+ $host_count = $wpdb->get_var(
222
+ $wpdb->prepare(
223
+ "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_temp` WHERE `temp_date_gmt` > '%s' AND `temp_host`='%s';",
224
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( $options['period'] * 60 ) ),
225
+ $host
226
+ )
227
+ );
228
+
229
+ if ( $host_count >= $options['host'] ) {
230
+
231
+ $lock_host = $host;
232
+
233
+ }
234
+
235
+ }
236
+
237
+ if ( $user !== null && isset( $options['user'] ) && $options['user'] > 0 ) {
238
+
239
+ $user_id = username_exists( sanitize_text_field( $user ) );
240
+
241
+ if ( $user_id !== null ) {
242
+
243
+ $wpdb->insert(
244
+ $wpdb->base_prefix . 'itsec_temp',
245
+ array(
246
+ 'temp_type' => $options['type'],
247
+ 'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
248
+ 'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
249
+ 'temp_user' => intval( $user_id ),
250
+ 'temp_username' => sanitize_text_field( $user ),
251
+ )
252
+ );
253
+
254
+ $user_count = $wpdb->get_var(
255
+ $wpdb->prepare(
256
+ "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_temp` WHERE `temp_date_gmt` > '%s' AND `temp_username`='%s' OR `temp_user`=%s;",
257
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( $options['period'] * 60 ) ),
258
+ sanitize_text_field( $user ),
259
+ intval( $user_id )
260
+ )
261
+ );
262
+
263
+ if ( $user_count >= $options['user'] ) {
264
+
265
+ $lock_user = $user_id;
266
+
267
+ }
268
+
269
+ } else {
270
+
271
+ $user = sanitize_text_field( $user );
272
+
273
+ $wpdb->insert(
274
+ $wpdb->base_prefix . 'itsec_temp',
275
+ array(
276
+ 'temp_type' => $options['type'],
277
+ 'temp_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
278
+ 'temp_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
279
+ 'temp_username' => $user,
280
+ )
281
+ );
282
+
283
+ $user_count = $wpdb->get_var(
284
+ $wpdb->prepare(
285
+ "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_temp` WHERE `temp_date_gmt` > '%s' AND `temp_username`='%s';",
286
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( $options['period'] * 60 ) ),
287
+ $user
288
+ )
289
+ );
290
+
291
+ if ( $user_count >= $options['user'] ) {
292
+
293
+ $lock_username = $user;
294
+
295
+ }
296
+
297
+ }
298
+
299
+ }
300
+
301
+ $error = $wpdb->last_error;
302
+
303
+ if ( strlen( trim( $error ) ) > 0 ) {
304
+ ITSEC_Lib::create_database_tables();
305
+ }
306
+
307
+ if ( ! $this->is_ip_whitelisted( $host ) && ( $lock_host !== null || $lock_user !== null || $lock_username !== null ) ) {
308
+
309
+ $this->lockout( $options['type'], $options['reason'], $lock_host, $lock_user, $lock_username );
310
+
311
+ } elseif ( $lock_host !== null || $lock_user !== null ) {
312
+
313
+ global $itsec_logger;
314
+
315
+ $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array( __( 'A whitelisted host has triggered a lockout condition but was not locked out.', 'better-wp-security' ) ), sanitize_text_field( $host ) );
316
+
317
+ }
318
+
319
+ }
320
+
321
+ /**
322
+ * Executes lockout (locks user out)
323
+ *
324
+ * @param boolean $user if we're locking out a user or not
325
+ *
326
+ * @return void
327
+ */
328
+ protected function execute_lock( $user = false, $network = false ) {
329
+
330
+ if ( $this->is_ip_whitelisted( ITSEC_Lib::get_ip() ) ) {
331
+ return;
332
+ }
333
+
334
+ global $itsec_globals;
335
+
336
+ wp_logout();
337
+ @header( 'HTTP/1.0 403 Forbidden' );
338
+ @header( 'Cache-Control: no-cache, must-revalidate, max-age=0' );
339
+ @header( 'Expires: Thu, 22 Jun 1978 00:28:00 GMT' );
340
+ @header( 'Pragma: no-cache' );
341
+
342
+ if ( $network === true ) { //lockout triggered by iThemes Network
343
+
344
+ if ( isset( $itsec_globals['settings']['community_lockout_message'] ) ) {
345
+
346
+ die( $itsec_globals['settings']['community_lockout_message'] );
347
+
348
+ } else {
349
+
350
+ die( __( "Your IP address has been flagged as a threat by the iThemes Security network.", 'better-wp-security' ) );
351
+
352
+ }
353
+
354
+ } elseif ( $user === true ) { //lockout the user
355
+
356
+ if ( isset( $itsec_globals['settings']['user_lockout_message'] ) ) {
357
+
358
+ die( $itsec_globals['settings']['user_lockout_message'] );
359
+
360
+ } else {
361
+
362
+ die( __( 'You have been locked out due to too many invalid login attempts.', 'better-wp-security' ) );
363
+
364
+ }
365
+
366
+ } else { //just lockout the host
367
+
368
+ if ( isset( $itsec_globals['settings']['lockout_message'] ) ) {
369
+
370
+ die( $itsec_globals['settings']['lockout_message'] );
371
+
372
+ } else {
373
+
374
+ die( __( 'error', 'better-wp-security' ) );
375
+
376
+ }
377
+
378
+ }
379
+
380
+ }
381
+
382
+ /**
383
+ * Provides a description of lockout configuration for use in module settings.
384
+ *
385
+ * @since 4.0
386
+ *
387
+ * @return string the description of settings.
388
+ */
389
+ public function get_lockout_description() {
390
+
391
+ global $itsec_globals;
392
+
393
+ $settings = $itsec_globals['settings'];
394
+
395
+ $description = sprintf(
396
+ '<h4>%s</h4><p>%s <a href="#global_options">%s</a>.<br /> %s</p><ul><li><strong>%s:</strong> %s</li><li><strong>%s:</strong> %s</li><li><strong>%s:</strong> %s</li><li><strong>%s:</strong> %s</li><li><strong>%s:</strong> %s</li><li><strong>%s:</strong> %s</li></ul>',
397
+ __( 'About Lockouts', 'better-wp-security' ),
398
+ __( 'Your lockout settings can be configured in', 'better-wp-security' ),
399
+ __( 'Global Settings', 'better-wp-security' ),
400
+ __( 'Your current settings are configured as follows:', 'better-wp-security' ),
401
+ __( 'Permanently ban', 'better-wp-security' ),
402
+ ( $settings['blacklist'] === true ? __( 'yes', 'better-wp-security' ) : __( 'no', 'better-wp-security' ) ),
403
+ __( 'Number of lockouts before permanent ban', 'better-wp-security' ),
404
+ $settings['blacklist_count'],
405
+ __( 'How long lockouts will be remembered for ban', 'better-wp-security' ),
406
+ $settings['blacklist_period'],
407
+ __( 'Host lockout message', 'better-wp-security' ),
408
+ $settings['lockout_message'],
409
+ __( 'User lockout message', 'better-wp-security' ),
410
+ $settings['user_lockout_message'],
411
+ __( 'Is this computer white-listed', 'better-wp-security' ),
412
+ ( $this->is_ip_whitelisted( ITSEC_Lib::get_ip() ) === true ? __( 'yes', 'better-wp-security' ) : __( 'no', 'better-wp-security' ) )
413
+ );
414
+
415
+ return $description;
416
+
417
+ }
418
+
419
+ /**
420
+ * Shows all lockouts currently in the database.
421
+ *
422
+ * @since 4.0
423
+ *
424
+ * @param string $type 'all', 'host', or 'user'
425
+ * @param bool $current true for all lockouts, false for current lockouts
426
+ * @param int $limit the maximum number of locks to return
427
+ *
428
+ * @return array all lockouts in the system
429
+ */
430
+ public function get_lockouts( $type = 'all', $current = false, $limit = 0 ) {
431
+
432
+ global $wpdb, $itsec_globals;
433
+
434
+ if ( $type !== 'all' || $current === true ) {
435
+ $where = " WHERE ";
436
+ } else {
437
+ $where = '';
438
+ }
439
+
440
+ switch ( $type ) {
441
+
442
+ case 'host':
443
+ $type_statement = "`lockout_host` IS NOT NULL && `lockout_host` != ''";
444
+ break;
445
+ case 'user':
446
+ $type_statement = "`lockout_user` != 0";
447
+ break;
448
+ case 'username':
449
+ $type_statement = "`lockout_username` IS NOT NULL && `lockout_username` != ''";
450
+ break;
451
+ default:
452
+ $type_statement = '';
453
+ break;
454
+
455
+ }
456
+
457
+ if ( $current === true ) {
458
+
459
+ if ( $type_statement !== '' ) {
460
+ $and = ' AND ';
461
+ } else {
462
+ $and = '';
463
+ }
464
+
465
+ $active = $and . " `lockout_active`=1 AND `lockout_expire_gmt` > '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . "'";
466
+
467
+ } else {
468
+
469
+ $active = '';
470
+
471
+ }
472
+
473
+ if ( absint( $limit ) > 0 ) {
474
+
475
+ $limit = " LIMIT " . absint( $limit );
476
+
477
+ } else {
478
+
479
+ $limit = '';
480
+
481
+ }
482
+
483
+ return $wpdb->get_results( "SELECT * FROM `" . $wpdb->base_prefix . "itsec_lockouts`" . $where . $type_statement . $active . $limit . ";", ARRAY_A );
484
+
485
+ }
486
+
487
+ /**
488
+ * Determines whether a given IP address is whitelisted.
489
+ *
490
+ * @since 4.0
491
+ *
492
+ * @access private
493
+ *
494
+ * @param string $ip_to_check ip to check
495
+ *
496
+ * @return boolean true if whitelisted or false
497
+ */
498
+ protected function is_ip_whitelisted( $ip_to_check, $current = false ) {
499
+
500
+ global $itsec_globals;
501
+
502
+ $white_ips = isset( $itsec_globals['settings']['lockout_white_list'] ) ? $itsec_globals['settings']['lockout_white_list'] : array();
503
+
504
+ if ( ! is_array( $white_ips ) ) {
505
+ $white_ips = explode( PHP_EOL, $white_ips );
506
+ }
507
+
508
+ //Add the server IP address
509
+ if ( isset( $_SERVER['LOCAL_ADDR'] ) ) {
510
+
511
+ $white_ips[] = $_SERVER['LOCAL_ADDR'];
512
+
513
+ } elseif ( isset( $_SERVER['SERVER_ADDR'] ) ) {
514
+
515
+ $white_ips[] = $_SERVER['SERVER_ADDR'];
516
+
517
+ }
518
+
519
+ if ( $current === true ) {
520
+ $white_ips[] = ITSEC_Lib::get_ip(); //add current user ip to whitelist to check automatically
521
+ }
522
+
523
+ $temp = get_site_option( 'itsec_temp_whitelist_ip' );
524
+
525
+ if ( $temp !== false ) {
526
+
527
+ if ( $temp['exp'] < $itsec_globals['current_time'] ) {
528
+
529
+ delete_site_option( 'itsec_temp_whitelist_ip' );
530
+
531
+ } else {
532
+
533
+ $white_ips[] = filter_var( $temp['ip'],
534
+ FILTER_VALIDATE_IP,
535
+ FILTER_FLAG_IPV4 );
536
+
537
+ }
538
+
539
+ }
540
+
541
+ if ( is_array( $white_ips ) && sizeof( $white_ips > 0 ) ) {
542
+
543
+ foreach ( $white_ips as $white_ip ) {
544
+
545
+ $converted_white_ip = ITSEC_Lib::ip_wild_to_mask( $white_ip );
546
+
547
+ $check_range = ITSEC_Lib::cidr_to_range( $converted_white_ip );
548
+ $ip_range = ITSEC_Lib::cidr_to_range( $ip_to_check );
549
+
550
+ if ( sizeof( $check_range ) === 2 ) { //range to check
551
+
552
+ $check_min = ip2long( $check_range[0] );
553
+ $check_max = ip2long( $check_range[1] );
554
+
555
+ if ( sizeof( $ip_range ) === 2 ) {
556
+
557
+ $ip_min = ip2long( $ip_range[0] );
558
+ $ip_max = ip2long( $ip_range[1] );
559
+
560
+ if ( ( $check_min < $ip_min && $ip_min < $check_max ) || ( $check_min < $ip_max && $ip_max < $check_max ) ) {
561
+ return true;
562
+ }
563
+
564
+ } else {
565
+
566
+ $ip = ip2long( $ip_range[0] );
567
+
568
+ if ( $check_min < $ip && $ip < $check_max ) {
569
+ return true;
570
+ }
571
+
572
+ }
573
+
574
+ } else { //single ip to check
575
+
576
+ $check = ip2long( $check_range[0] );
577
+
578
+ if ( sizeof( $ip_range ) === 2 ) {
579
+
580
+ $ip_min = ip2long( $ip_range[0] );
581
+ $ip_max = ip2long( $ip_range[1] );
582
+
583
+ if ( $ip_min < $check && $check < $ip_max ) {
584
+ return true;
585
+ }
586
+
587
+ } else {
588
+
589
+ $ip = ip2long( $ip_range[0] );
590
+
591
+ if ( $check == $ip ) {
592
+ return true;
593
+ }
594
+
595
+ }
596
+
597
+ }
598
+
599
+ }
600
+
601
+ }
602
+
603
+ return false;
604
+
605
+ }
606
+
607
+ /**
608
+ * Process ajax request to set temp whitelist
609
+ *
610
+ * @since 4.3
611
+ *
612
+ * @return void
613
+ */
614
+ public function itsec_temp_whitelist_ajax() {
615
+
616
+ global $itsec_globals;
617
+
618
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_temp_whitelist_nonce' ) ) {
619
+ die ();
620
+ }
621
+
622
+ $add_temp = false;
623
+ $current_ip = ITSEC_Lib::get_ip();
624
+ $temp_ip = get_site_option( 'itsec_temp_whitelist_ip' );
625
+
626
+ if ( $temp_ip !== false ) {
627
+
628
+ if ( $temp_ip['exp'] < $itsec_globals['current_time'] ) {
629
+ delete_site_option( 'itsec_temp_whitelist_ip' );
630
+ $add_temp = true;
631
+ }
632
+
633
+ } else {
634
+
635
+ $add_temp = true;
636
+
637
+ }
638
+
639
+ if ( $add_temp === false ) {
640
+
641
+ die( 'error' );
642
+
643
+ } else {
644
+
645
+ $response = array(
646
+ 'ip' => ITSEC_Lib::get_ip(),
647
+ 'exp' => $itsec_globals['current_time'] + 86400,
648
+ );
649
+
650
+ add_site_option( 'itsec_temp_whitelist_ip', $response );
651
+
652
+ $response['exp'] = human_time_diff( $itsec_globals['current_time'], $response['exp'] );
653
+ $response['message1'] = __( 'Your IP Address', 'better-wp-security' );
654
+ $response['message2'] = __( 'is whitelisted for', 'better-wp-security' );
655
+ $response['message3'] = __( 'Remove IP from Whitelist', 'better-wp-security' );
656
+
657
+ die( json_encode( $response ) );
658
+
659
+ }
660
+
661
+ }
662
+
663
+ /**
664
+ * Process ajax request to release temp whitelist
665
+ *
666
+ * @since 4.6
667
+ *
668
+ * @return void
669
+ */
670
+ public function itsec_temp_whitelist_release_ajax() {
671
+
672
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_temp_whitelist_nonce' ) ) {
673
+ die ();
674
+ }
675
+
676
+ delete_site_option( 'itsec_temp_whitelist_ip' );
677
+ die( 'true' );
678
+
679
+ }
680
+
681
+ /**
682
+ * Locks out given user or host
683
+ *
684
+ * @since 4.0
685
+ *
686
+ * @param string $type The type of lockout (for user reference)
687
+ * @param string $reason Reason for lockout, for notifications
688
+ * @param string $host Host to lock out
689
+ * @param int $user user id to lockout
690
+ * @param string $username username to lockout
691
+ *
692
+ * @return void
693
+ */
694
+ private function lockout( $type, $reason, $host = null, $user = null, $username = null ) {
695
+
696
+ global $wpdb, $itsec_logger, $itsec_globals, $itsec_files;
697
+
698
+ $host_expiration = null;
699
+ $user_expiration = null;
700
+ $username = sanitize_text_field( trim( $username ) );
701
+
702
+ if ( $itsec_files->get_file_lock( 'lockout_' . $host . $user . $username ) ) {
703
+
704
+ //Do we have a good host to lock out or not
705
+ if ( $host != null && $this->is_ip_whitelisted( sanitize_text_field( $host ) ) === false && ITSEC_Lib::validates_ip_address( $host ) === true ) {
706
+ $good_host = sanitize_text_field( $host );
707
+ } else {
708
+ $good_host = false;
709
+ }
710
+
711
+ //Do we have a valid user to lockout or not
712
+ if ( $user !== null && ITSEC_Lib::user_id_exists( intval( $user ) ) === true ) {
713
+ $good_user = intval( $user );
714
+ } else {
715
+ $good_user = false;
716
+ }
717
+
718
+ //Do we have a valid username to lockout or not
719
+ if ( $username !== null && $username != '' ) {
720
+ $good_username = $username;
721
+ } else {
722
+ $good_username = false;
723
+ }
724
+
725
+ $blacklist_host = false; //assume we're not permanently blcking the host
726
+
727
+ //Sanitize the data for later
728
+ $type = sanitize_text_field( $type );
729
+ $reason = sanitize_text_field( $reason );
730
+
731
+ //handle a permanent host ban (if needed)
732
+ if ( isset( $itsec_globals['settings']['blacklist'] ) && $itsec_globals['settings']['blacklist'] === true && $good_host !== false ) { //permanent blacklist
733
+
734
+ $blacklist_period = isset( $itsec_globals['settings']['blacklist_period'] ) ? $itsec_globals['settings']['blacklist_period'] * 24 * 60 * 60 : 604800;
735
+
736
+ $host_count = 1 + $wpdb->get_var(
737
+ $wpdb->prepare(
738
+ "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_expire_gmt` > '%s' AND `lockout_host`='%s';",
739
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - $blacklist_period ),
740
+ $host
741
+ )
742
+ );
743
+
744
+ if ( $host_count >= $itsec_globals['settings']['blacklist_count'] && isset( $itsec_globals['settings']['write_files'] ) && $itsec_globals['settings']['write_files'] === true ) {
745
+
746
+ $host_expiration = false;
747
+
748
+ if ( ! class_exists( 'ITSEC_Ban_Users' ) ) {
749
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/modules/ban-users/class-itsec-ban-users.php' );
750
+ }
751
+
752
+ ITSEC_Ban_Users::insert_ip( sanitize_text_field( $host ) ); //Send it to the Ban Users module for banning
753
+
754
+ $blacklist_host = true; //flag it so we don't do a temp ban as well
755
+
756
+ }
757
+
758
+ }
759
+
760
+ //We have temp bans to perform
761
+ if ( $good_host !== false || $good_user !== false || $good_username || $good_username !== false ) {
762
+
763
+ if ( $this->is_ip_whitelisted( sanitize_text_field( $host ) ) ) {
764
+
765
+ $whitelisted = true;
766
+ $expiration = date( 'Y-m-d H:i:s', 1 );
767
+ $expiration_gmt = date( 'Y-m-d H:i:s', 1 );
768
+
769
+ } else {
770
+
771
+ $whitelisted = false;
772
+ $exp_seconds = ( intval( $itsec_globals['settings']['lockout_period'] ) * 60 );
773
+ $expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $exp_seconds );
774
+ $expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $exp_seconds );
775
+
776
+ }
777
+
778
+ if ( $good_host !== false && $blacklist_host === false ) { //temp lockout host
779
+
780
+ $host_expiration = $expiration;
781
+
782
+ $wpdb->insert(
783
+ $wpdb->base_prefix . 'itsec_lockouts',
784
+ array(
785
+ 'lockout_type' => $type,
786
+ 'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
787
+ 'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
788
+ 'lockout_expire' => $expiration,
789
+ 'lockout_expire_gmt' => $expiration_gmt,
790
+ 'lockout_host' => sanitize_text_field( $host ),
791
+ )
792
+ );
793
+
794
+ $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array(
795
+ 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
796
+ ), sanitize_text_field( $host ) );
797
+
798
+ }
799
+
800
+ if ( $good_user !== false ) { //blacklist host and temp lockout user
801
+
802
+ $user_expiration = $expiration;
803
+
804
+ $wpdb->insert(
805
+ $wpdb->base_prefix . 'itsec_lockouts',
806
+ array(
807
+ 'lockout_type' => $type,
808
+ 'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
809
+ 'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
810
+ 'lockout_expire' => $expiration,
811
+ 'lockout_expire_gmt' => $expiration_gmt,
812
+ 'lockout_host' => '',
813
+ 'lockout_user' => intval( $user ),
814
+ )
815
+ );
816
+
817
+ if ( $whitelisted === false ) {
818
+ $itsec_logger->log_event( 'lockout', 10, array(
819
+ 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
820
+ ), '', '', intval( $user ) );
821
+ } else {
822
+ $itsec_logger->log_event( 'lockout', 10, array(
823
+ __( 'White Listed', 'better-wp-security' ), 'type' => $type
824
+ ), '', '', intval( $user ) );
825
+ }
826
+
827
+ }
828
+
829
+ if ( $good_username !== false ) { //blacklist host and temp lockout username
830
+
831
+ $user_expiration = $expiration;
832
+
833
+ $wpdb->insert(
834
+ $wpdb->base_prefix . 'itsec_lockouts',
835
+ array(
836
+ 'lockout_type' => $type,
837
+ 'lockout_start' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
838
+ 'lockout_start_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
839
+ 'lockout_expire' => $expiration,
840
+ 'lockout_expire_gmt' => $expiration_gmt,
841
+ 'lockout_host' => '',
842
+ 'lockout_username' => $username,
843
+ )
844
+ );
845
+
846
+ if ( $whitelisted === false ) {
847
+ $itsec_logger->log_event( 'lockout', 10, array(
848
+ 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => $type
849
+ ), '', '', $username );
850
+ } else {
851
+ $itsec_logger->log_event( 'lockout', 10, array(
852
+ __( 'White Listed', 'better-wp-security' ), 'type' => $type
853
+ ), '', '', $username );
854
+ }
855
+
856
+ }
857
+
858
+ if ( $whitelisted === false ) {
859
+
860
+ if ( $itsec_globals['settings']['email_notifications'] === true ) { //send email notifications
861
+ $this->send_lockout_email( $good_host, $good_user, $good_username, $host_expiration, $user_expiration, $reason );
862
+ }
863
+
864
+ if ( $good_host !== false ) {
865
+
866
+ $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
867
+ $this->execute_lock();
868
+
869
+ } else {
870
+
871
+ $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
872
+ $this->execute_lock( true );
873
+
874
+ }
875
+
876
+ }
877
+
878
+ }
879
+
880
+ $itsec_files->release_file_lock( 'lockout_' . $host . $user . $username );
881
+
882
+ }
883
+
884
+ }
885
+
886
+ /**
887
+ * Active lockouts table and form for dashboard.
888
+ *
889
+ * @Since 4.0
890
+ *
891
+ * @return void
892
+ */
893
+ public function lockout_metabox() {
894
+
895
+ global $itsec_globals;
896
+
897
+ ?>
898
+ <form method="post" action="" id="itsec_release_lockout_form">
899
+ <?php wp_nonce_field( 'itsec_release_lockout', 'wp_nonce' ); ?>
900
+ <input type="hidden" name="itsec_release_lockout" value="true"/>
901
+ <?php //get locked out hosts and users from database
902
+ $host_locks = $this->get_lockouts( 'host', true, 50 );
903
+ $user_locks = $this->get_lockouts( 'user', true, 50 );
904
+ $username_locks = $this->get_lockouts( 'username', true, 50 );
905
+ ?>
906
+ <table class="form-table">
907
+ <tr valign="top">
908
+ <th scope="row" class="settinglabel">
909
+ <?php _e( 'Locked out hosts', 'better-wp-security' ); ?>
910
+ </th>
911
+ <td class="settingfield">
912
+ <?php if ( sizeof( $host_locks ) > 0 ) { ?>
913
+ <ul>
914
+ <?php foreach ( $host_locks as $host ) { ?>
915
+ <li style="list-style: none;"><input type="checkbox"
916
+ name="lo_<?php echo $host['lockout_id']; ?>"
917
+ id="lo_<?php echo $host['lockout_id']; ?>"
918
+ value="<?php echo $host['lockout_id']; ?>"/>
919
+ <label
920
+ for="lo_<?php echo $host['lockout_id']; ?>"><strong><?php echo filter_var( $host['lockout_host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 );; ?></strong>
921
+ - <?php _e( 'Expires in', 'better-wp-security' ); ?>
922
+ <em> <?php echo human_time_diff( $itsec_globals['current_time_gmt'], strtotime( $host['lockout_expire_gmt'] ) ); ?></em></label>
923
+ </li>
924
+ <?php } ?>
925
+ </ul>
926
+ <?php } else { //no host is locked out ?>
927
+ <ul>
928
+ <li style="list-style: none;">
929
+ <p><?php _e( 'Currently no hosts are locked out of this website.', 'better-wp-security' ); ?></p>
930
+ </li>
931
+ </ul>
932
+ <?php } ?>
933
+ </td>
934
+ </tr>
935
+ <tr valign="top">
936
+ <th scope="row" class="settinglabel">
937
+ <?php _e( 'Locked out users', 'better-wp-security' ); ?>
938
+ </th>
939
+ <td class="settingfield">
940
+ <?php if ( sizeof( $user_locks ) > 0 ) { ?>
941
+ <ul>
942
+ <?php foreach ( $user_locks as $user ) { ?>
943
+ <?php $userdata = get_userdata( $user['lockout_user'] ); ?>
944
+ <li style="list-style: none;"><input type="checkbox"
945
+ name="lo_<?php echo $user['lockout_id']; ?>"
946
+ id="lo_<?php echo $user['lockout_id']; ?>"
947
+ value="<?php echo $user['lockout_id']; ?>"/>
948
+ <label
949
+ for="lo_<?php echo $user['lockout_id']; ?>"><strong><?php echo isset( $userdata->lockout ) ? $userdata->user_login : ''; ?></strong>
950
+ - <?php _e( 'Expires in', 'better-wp-security' ); ?>
951
+ <em> <?php echo human_time_diff( $itsec_globals['current_time_gmt'], strtotime( $user['lockout_expire_gmt'] ) ); ?></em></label>
952
+ </li>
953
+ <?php } ?>
954
+ </ul>
955
+ <?php } else { //no user is locked out ?>
956
+ <ul>
957
+ <li style="list-style: none;">
958
+ <p><?php _e( 'Currently no users are locked out of this website.', 'better-wp-security' ); ?></p>
959
+ </li>
960
+ </ul>
961
+ <?php } ?>
962
+ </td>
963
+ </tr>
964
+ <tr valign="top">
965
+ <th scope="row" class="settinglabel">
966
+ <?php _e( 'Locked out usernames (not real users)', 'better-wp-security' ); ?>
967
+ </th>
968
+ <td class="settingfield">
969
+ <?php if ( sizeof( $username_locks ) > 0 ) { ?>
970
+ <ul>
971
+ <?php foreach ( $username_locks as $user ) { ?>
972
+ <li style="list-style: none;"><input type="checkbox"
973
+ name="lo_<?php echo $user['lockout_id']; ?>"
974
+ id="lo_<?php echo $user['lockout_id']; ?>"
975
+ value="<?php echo $user['lockout_id']; ?>"/>
976
+ <label
977
+ for="lo_<?php echo $user['lockout_id']; ?>"><strong><?php echo sanitize_text_field( $user['lockout_username'] ); ?></strong>
978
+ - <?php _e( 'Expires in', 'better-wp-security' ); ?>
979
+ <em> <?php echo human_time_diff( $itsec_globals['current_time_gmt'], strtotime( $user['lockout_expire_gmt'] ) ); ?></em></label>
980
+ </li>
981
+ <?php } ?>
982
+ </ul>
983
+ <?php } else { //no user is locked out ?>
984
+ <ul>
985
+ <li style="list-style: none;">
986
+ <p><?php _e( 'Currently no usernames are locked out of this website.', 'better-wp-security' ); ?></p>
987
+ </li>
988
+ </ul>
989
+ <?php } ?>
990
+ </td>
991
+ </tr>
992
+ </table>
993
+ <p class="submit"><input type="submit" class="button-primary"
994
+ value="<?php _e( 'Release Lockout', 'better-wp-security' ); ?>"/></p>
995
+ </form>
996
+ <?php
997
+ }
998
+
999
+ /**
1000
+ * Purges lockouts more than 7 days old from the database
1001
+ *
1002
+ * @return void
1003
+ */
1004
+ public function purge_lockouts() {
1005
+
1006
+ global $wpdb, $itsec_globals;
1007
+
1008
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE `lockout_expire_gmt` < '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( ( $itsec_globals['settings']['blacklist_period'] + 1 ) * 24 * 60 * 60 ) ) . "';" );
1009
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_temp` WHERE `temp_date_gmt` < '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - 86400 ) . "';" );
1010
+
1011
+ }
1012
+
1013
+ /**
1014
+ * Register 404 and file change detection for logger
1015
+ *
1016
+ * @param array $logger_modules array of logger modules
1017
+ *
1018
+ * @return array array of logger modules
1019
+ */
1020
+ public function register_logger( $logger_modules ) {
1021
+
1022
+ $logger_modules['lockout'] = array(
1023
+ 'type' => 'lockout',
1024
+ 'function' => __( 'Host or User Lockout', 'better-wp-security' ),
1025
+ );
1026
+
1027
+ return $logger_modules;
1028
+
1029
+ }
1030
+
1031
+ /**
1032
+ * Register Lockouts for Sync
1033
+ *
1034
+ * @param array $sync_modules array of logger modules
1035
+ *
1036
+ * @return array array of logger modules
1037
+ */
1038
+ public function register_sync( $sync_modules ) {
1039
+
1040
+ $sync_modules['lockout'] = array(
1041
+ 'verbs' => array(
1042
+ 'itsec-get-lockouts' => 'Ithemes_Sync_Verb_ITSEC_Get_Lockouts',
1043
+ 'itsec-release-lockout' => 'Ithemes_Sync_Verb_ITSEC_Release_Lockout',
1044
+ 'itsec-get-temp-whitelist' => 'Ithemes_Sync_Verb_ITSEC_Get_Temp_Whitelist',
1045
+ 'itsec-set-temp-whitelist' => 'Ithemes_Sync_Verb_ITSEC_Set_Temp_Whitelist',
1046
+ ),
1047
+ 'everything' => array(
1048
+ 'itsec-get-lockouts',
1049
+ 'itsec-get-temp-whitelist',
1050
+ ),
1051
+ 'path' => dirname( __FILE__ ),
1052
+ );
1053
+
1054
+ return $sync_modules;
1055
+
1056
+ }
1057
+
1058
+ /**
1059
+ * Register modules that will use the lockout service
1060
+ *
1061
+ * @return void
1062
+ */
1063
+ public function register_modules() {
1064
+
1065
+ $this->lockout_modules = apply_filters( 'itsec_lockout_modules', $this->lockout_modules );
1066
+
1067
+ }
1068
+
1069
+ /**
1070
+ * Process clearing lockouts on view log page
1071
+ *
1072
+ * @since 4.0
1073
+ *
1074
+ * @return bool true on success or false
1075
+ */
1076
+ public function release_lockout( $id = null ) {
1077
+
1078
+ global $wpdb;
1079
+
1080
+ if ( $id !== null && trim( $id ) !== '' ) {
1081
+
1082
+ $sanitized_id = intval( $id );
1083
+
1084
+ $lockout = $wpdb->get_results( "SELECT * FROM `" . $wpdb->base_prefix . "itsec_lockouts` WHERE lockout_id = " . $sanitized_id . ";", ARRAY_A );
1085
+
1086
+ if ( is_array( $lockout ) && sizeof( $lockout ) >= 1 ) {
1087
+
1088
+ $success = $wpdb->update(
1089
+ $wpdb->base_prefix . 'itsec_lockouts',
1090
+ array(
1091
+ 'lockout_active' => 0,
1092
+ ),
1093
+ array(
1094
+ 'lockout_id' => $sanitized_id,
1095
+ )
1096
+ );
1097
+
1098
+ return $success === false ? false : true;
1099
+
1100
+ } else {
1101
+
1102
+ return false;
1103
+
1104
+ }
1105
+
1106
+ } elseif ( isset( $_POST['itsec_release_lockout'] ) && $_POST['itsec_release_lockout'] == 'true' ) {
1107
+
1108
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_release_lockout' ) ) {
1109
+ die( __( 'Security error!', 'better-wp-security' ) );
1110
+ }
1111
+
1112
+ $type = 'updated';
1113
+ $message = __( 'The selected lockouts have been cleared.', 'better-wp-security' );
1114
+
1115
+ foreach ( $_POST as $key => $value ) {
1116
+
1117
+ if ( strstr( $key, "lo_" ) ) { //see if it's a lockout to avoid processing extra post fields
1118
+
1119
+ $wpdb->update(
1120
+ $wpdb->base_prefix . 'itsec_lockouts',
1121
+ array(
1122
+ 'lockout_active' => 0,
1123
+ ),
1124
+ array(
1125
+ 'lockout_id' => intval( $value ),
1126
+ )
1127
+ );
1128
+
1129
+ }
1130
+
1131
+ }
1132
+
1133
+ ITSEC_Lib::clear_caches();
1134
+
1135
+ if ( is_multisite() ) {
1136
+
1137
+ $error_handler = new WP_Error();
1138
+
1139
+ $error_handler->add( $type, $message );
1140
+
1141
+ $this->core->show_network_admin_notice( $error_handler );
1142
+
1143
+ } else {
1144
+
1145
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
1146
+
1147
+ }
1148
+
1149
+ }
1150
+
1151
+ }
1152
+
1153
+ /**
1154
+ * Active lockouts table and form for dashboard.
1155
+ *
1156
+ * @Since 4.0
1157
+ *
1158
+ * @return void
1159
+ */
1160
+ public function self_protect_metabox() {
1161
+
1162
+ global $itsec_globals;
1163
+
1164
+ echo '<p>' . __( 'Security is a delicate item. It does not care who you are, if it sees that you are trying to do something strange it will lock you out. This can be troublesome on sites with existing errors, particularly missing assets such as images and others.', 'better-wp-security' ) . '</p>';
1165
+ echo '<p>' . __( 'Use the button below to temporarily white list your IP from lockouts for 24 hours. It will still notify you of the situation but it will not lock you out of your site allowing you a chance to fix the issue.', 'better-wp-security' ) . '</p>';
1166
+ echo '<p>' . __( 'Please note that if your IP address changes at any time during the period (such as you switch locations) you could still inadvertently lock yourself out.', 'better-wp-security' ) . '</p>';
1167
+
1168
+ $temp = get_site_option( 'itsec_temp_whitelist_ip' );
1169
+
1170
+ if ( $temp !== false ) {
1171
+
1172
+ echo '<p class="itsec_temp_whitelist submit">';
1173
+ echo __( 'Your IP Address', 'better-wp-security' ) . ', <strong>' . $temp['ip'] . '</strong>, ' . __( 'is whitelisted for', 'better-wp-security' ) . ' <strong>' . human_time_diff( $itsec_globals['current_time'], $temp['exp'] ) . '</strong>.<br />';
1174
+ echo '<a href="#" class="itsec_temp_whitelist_release_ajax button-primary">' . __( 'Remove IP from Whitelist', 'better-wp-security' ) . '</a>';
1175
+ echo '</p>';
1176
+
1177
+ } else {
1178
+
1179
+ echo '<p class="itsec_temp_whitelist submit"><a href="#" class="itsec_temp_whitelist_ajax button-primary">' . __( 'Temporarily Whitelist my IP', 'better-wp-security' ) . '</a></p>';
1180
+
1181
+ }
1182
+
1183
+ }
1184
+
1185
+ /**
1186
+ * Sends an email to notify site admins of lockouts
1187
+ *
1188
+ * @since 4.0
1189
+ *
1190
+ * @param string $host the host to lockout
1191
+ * @param int $user the user id to lockout
1192
+ * @param string $username the username to lockout
1193
+ * @param string $host_expiration when the host login expires
1194
+ * @param string $user_expiration when the user lockout expires
1195
+ * @param string $reason the reason for the lockout to show to the user
1196
+ *
1197
+ * @return void
1198
+ */
1199
+ private function send_lockout_email( $host, $user, $username, $host_expiration, $user_expiration, $reason ) {
1200
+
1201
+ global $itsec_notify, $itsec_globals;
1202
+
1203
+ if ( ! isset( $itsec_globals['settings']['digest_email'] ) || $itsec_globals['settings']['digest_email'] === false ) {
1204
+
1205
+ $plural_text = __( 'has', 'better-wp-security' );
1206
+
1207
+ //Tell which host was locked out
1208
+ if ( $host !== false ) {
1209
+
1210
+ $host_text = sprintf( '%s, <a href="http://ip-adress.com/ip_tracer/%s"><strong>%s</strong></a>, ', __( 'host', 'better-wp-security' ), sanitize_text_field( $host ), sanitize_text_field( $host ) );
1211
+
1212
+ $host_expiration_text = __( 'The host has been locked out ', 'better-wp-security' );
1213
+
1214
+ if ( $host_expiration === false ) {
1215
+
1216
+ $host_expiration_text .= '<strong>' . __( 'permanently', 'better-wp-security' ) . '</strong>';
1217
+ $release_text = sprintf( '%s <a href="%s">%s</a>.', __( 'To release the host lockout you can remove the host from the', 'better-wp-security' ), wp_login_url( get_admin_url( '', 'admin.php?page=toplevel_page_itsec_settings' ) ), __( 'host list', 'better-wp-security' ) );
1218
+
1219
+ } else {
1220
+
1221
+ $host_expiration_text .= sprintf( '<strong>%s %s</strong>', __( 'until', 'better-wp-security' ), sanitize_text_field( $host_expiration ) );
1222
+ $release_text = sprintf( '%s <a href="%s">%s</a>.', __( 'To release the lockout please visit', 'better-wp-security' ), wp_login_url( get_admin_url( '', 'admin.php?page=itsec' ) ), __( 'the admin area', 'better-wp-security' ) );
1223
+
1224
+ }
1225
+
1226
+ } else {
1227
+
1228
+ $host_expiration_text = '';
1229
+ $host_text = '';
1230
+ $release_text = '';
1231
+
1232
+ }
1233
+
1234
+ $user_object = get_userdata( $user ); //try to get and actual user object
1235
+
1236
+ //Tell them which user was locked out and setup the expiration copy
1237
+ if ( $user_object !== false || $username !== false ) {
1238
+
1239
+ if ( $user_object !== false ) {
1240
+ $login = $user_object->user_login;
1241
+ } else {
1242
+ $login = sanitize_text_field( $username );
1243
+ }
1244
+
1245
+ if ( $host_text === '' ) {
1246
+
1247
+ $user_expiration_text = sprintf( '%s <strong>%s %s</strong>.', __( 'The user has been locked out', 'better-wp-security' ), __( 'until', 'better-wp-security' ), sanitize_text_field( $user_expiration ) );
1248
+
1249
+ $user_text = sprintf( '%s, <strong>%s</strong>, ', __( 'user', 'better-wp-security' ), $login );
1250
+
1251
+ $release_text = sprintf( '%s <a href="%s">%s</a>.', __( 'To release the lockout please visit', 'better-wp-security' ), wp_login_url( get_admin_url( '', 'admin.php?page=itsec' ) ), __( 'the lockouts page', 'better-wp-security' ) );
1252
+
1253
+ } else {
1254
+
1255
+ $user_expiration_text = sprintf( '%s <strong>%s %s</strong>.', __( 'and the user has been locked out', 'better-wp-security' ), __( 'until', 'better-wp-security' ), sanitize_text_field( $user_expiration ) );
1256
+ $plural_text = __( 'have', 'better-wp-security' );
1257
+ $user_text = sprintf( '%s, <strong>%s</strong>, ', __( 'and a user', 'better-wp-security' ), $login );
1258
+
1259
+ if ( $host_expiration === false ) {
1260
+
1261
+ $release_text .= sprintf( '%s <a href="%s">%s</a>.', __( 'To release the user lockout please visit', 'better-wp-security' ), wp_login_url( get_admin_url( '', 'admin.php?page=itsec' ) ), __( 'the lockouts page', 'better-wp-security' ) );
1262
+
1263
+ } else {
1264
+
1265
+ $release_text = sprintf( '%s <a href="%s">%s</a>.', __( 'To release the lockouts please visit', 'better-wp-security' ), wp_login_url( get_admin_url( '', 'admin.php?page=itsec' ) ), __( 'the lockouts page', 'better-wp-security' ) );
1266
+
1267
+ }
1268
+
1269
+ }
1270
+
1271
+ } else {
1272
+
1273
+ $user_expiration_text = '.';
1274
+ $user_text = '';
1275
+ $release_text = '';
1276
+
1277
+ }
1278
+
1279
+ //Put the copy all together
1280
+ $body = sprintf(
1281
+ '<p>%s,</p><p>%s %s %s %s %s <a href="%s">%s</a> %s <strong>%s</strong>.</p><p>%s %s</p><p>%s</p><p><em>*%s %s. %s <a href="%s">%s</a>.</em></p>',
1282
+ __( 'Dear Site Admin', 'better-wp-security' ),
1283
+ __( 'A', 'better-wp-security' ),
1284
+ $host_text,
1285
+ $user_text,
1286
+ $plural_text,
1287
+ __( ' been locked out of the WordPress site at', 'better-wp-security' ),
1288
+ get_option( 'siteurl' ),
1289
+ get_option( 'siteurl' ),
1290
+ __( 'due to', 'better-wp-security' ),
1291
+ sanitize_text_field( $reason ),
1292
+ $host_expiration_text,
1293
+ $user_expiration_text,
1294
+ $release_text,
1295
+ __( 'This email was generated automatically by' ),
1296
+ $itsec_globals['plugin_name'],
1297
+ __( 'To change your email preferences please visit', 'better-wp-security' ),
1298
+ wp_login_url( get_admin_url( '', 'admin.php?page=toplevel_page_itsec_settings' ) ),
1299
+ __( 'the plugin settings', 'better-wp-security' ) );
1300
+
1301
+ //Setup the remainder of the email
1302
+ $subject = '[' . get_option( 'siteurl' ) . '] ' . __( 'Site Lockout Notification', 'better-wp-security' );
1303
+ $subject = apply_filters( 'itsec_lockout_email_subject', $subject );
1304
+ $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
1305
+
1306
+ $args = array(
1307
+ 'headers' => $headers,
1308
+ 'message' => $body,
1309
+ 'subject' => $subject,
1310
+ );
1311
+
1312
+ $itsec_notify->notify( $args );
1313
+
1314
+ }
1315
+
1316
+ }
1317
+
1318
+ /**
1319
+ * Sets an error message when a user has been forcibly logged out due to lockout
1320
+ *
1321
+ * @return string
1322
+ */
1323
+ public function set_lockout_error() {
1324
+
1325
+ global $itsec_globals;
1326
+
1327
+ //check to see if it's the logout screen
1328
+ if ( isset( $_GET['itsec'] ) && $_GET['itsec'] == true ) {
1329
+ return '<div id="login_error">' . $itsec_globals['settings']['user_lockout_message'] . '</div>' . PHP_EOL;
1330
+ }
1331
+
1332
+ }
1333
+
1334
+ }
core/class-itsec-logger-all-logs.php ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Log tables for Authentication Module
5
+ *
6
+ * @package iThemes-Security
7
+ * @subpackage Authentication
8
+ * @since 4.0
9
+ */
10
+ final class ITSEC_Logger_All_Logs extends ITSEC_WP_List_Table {
11
+
12
+ function __construct() {
13
+
14
+ parent::__construct(
15
+ array(
16
+ 'singular' => 'itsec_raw_log_item',
17
+ 'plural' => 'itsec_raw_log_items',
18
+ 'ajax' => true
19
+ )
20
+ );
21
+
22
+ }
23
+
24
+ /**
25
+ * Define type column
26
+ *
27
+ * @param array $item array of row data
28
+ *
29
+ * @return string formatted output
30
+ *
31
+ **/
32
+ function column_time( $item ) {
33
+
34
+ return $item['time'];
35
+
36
+ }
37
+
38
+ /**
39
+ * Define function column
40
+ *
41
+ * @param array $item array of row data
42
+ *
43
+ * @return string formatted output
44
+ *
45
+ **/
46
+ function column_function( $item ) {
47
+
48
+ return $item['function'];
49
+
50
+ }
51
+
52
+ /**
53
+ * Define priority column
54
+ *
55
+ * @param array $item array of row data
56
+ *
57
+ * @return string formatted output
58
+ *
59
+ **/
60
+ function column_priority( $item ) {
61
+
62
+ return $item['priority'];
63
+
64
+ }
65
+
66
+ /**
67
+ * Define host column
68
+ *
69
+ * @param array $item array of row data
70
+ *
71
+ * @return string formatted output
72
+ *
73
+ **/
74
+ function column_host( $item ) {
75
+
76
+ $r = array();
77
+ if ( ! is_array( $item['host'] ) ) {
78
+ $item['host'] = array( $item['host'] );
79
+ }
80
+ foreach ( $item['host'] as $host ) {
81
+ $r[] = '<a href="http://ip-adress.com/ip_tracer/' . filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) . '" target="_blank">' . filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) . '</a>';
82
+ }
83
+ $return = implode( '<br />', $r );
84
+
85
+ return $return;
86
+
87
+ }
88
+
89
+ /**
90
+ * Define username column
91
+ *
92
+ * @param array $item array of row data
93
+ *
94
+ * @return string formatted output
95
+ *
96
+ **/
97
+ function column_user( $item ) {
98
+
99
+ if ( $item['user_id'] != 0 ) {
100
+ return '<a href="/wp-admin/user-edit.php?user_id=' . $item['user_id'] . '" target="_blank">' . $item['user'] . '</a>';
101
+ } else {
102
+ return $item['user'];
103
+ }
104
+
105
+ }
106
+
107
+ /**
108
+ * Define url column
109
+ *
110
+ * @param array $item array of row data
111
+ *
112
+ * @return string formatted output
113
+ *
114
+ **/
115
+ function column_url( $item ) {
116
+
117
+ return $item['url'];
118
+
119
+ }
120
+
121
+ /**
122
+ * Define referrer column
123
+ *
124
+ * @param array $item array of row data
125
+ *
126
+ * @return string formatted output
127
+ *
128
+ **/
129
+ function column_referrer( $item ) {
130
+
131
+ return $item['referrer'];
132
+
133
+ }
134
+
135
+ /**
136
+ * Define data column
137
+ *
138
+ * @param array $item array of row data
139
+ *
140
+ * @return string formatted output
141
+ *
142
+ **/
143
+ function column_data( $item ) {
144
+
145
+ global $itsec_logger;
146
+
147
+ $raw_data = maybe_unserialize( $item['data'] );
148
+
149
+ $data = apply_filters( "itsec_logger_filter_{$item['type']}_data_column_details", '', $raw_data );
150
+
151
+ if ( empty( $data ) ) {
152
+ if ( is_array( $raw_data ) && sizeof( $raw_data ) > 0 ) {
153
+
154
+ $data = $itsec_logger->print_array( $raw_data, true );
155
+
156
+ } elseif ( ! is_array( $raw_data ) ) {
157
+
158
+ $data = sanitize_text_field( $raw_data );
159
+
160
+ } else {
161
+
162
+ $data = '';
163
+
164
+ }
165
+ }
166
+
167
+ if ( strlen( $data ) > 1 ) {
168
+
169
+ $content = '<div class="itsec-all-log-dialog" id="itsec-log-all-row-' . $item['id'] . '" style="display:none;">';
170
+ $content .= $data;
171
+ $content .= '</div>';
172
+
173
+ $content .= '<a href="itsec-log-all-row-' . $item['id'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
174
+
175
+ return $content;
176
+
177
+ } else {
178
+
179
+ return '';
180
+
181
+ }
182
+
183
+ }
184
+
185
+ /**
186
+ * Define Columns
187
+ *
188
+ * @return array array of column titles
189
+ */
190
+ public function get_columns() {
191
+
192
+ return array(
193
+ 'function' => __( 'Function', 'better-wp-security' ),
194
+ 'priority' => __( 'Priority', 'better-wp-security' ),
195
+ 'time' => __( 'Time', 'better-wp-security' ),
196
+ 'host' => __( 'Host', 'better-wp-security' ),
197
+ 'user' => __( 'User', 'better-wp-security' ),
198
+ 'url' => __( 'URL', 'better-wp-security' ),
199
+ 'referrer' => __( 'Referrer', 'better-wp-security' ),
200
+ 'data' => __( 'Data', 'better-wp-security' ),
201
+ );
202
+
203
+ }
204
+
205
+ /**
206
+ * Prepare data for table
207
+ *
208
+ * @return void
209
+ */
210
+ public function prepare_items() {
211
+
212
+ global $itsec_logger, $wpdb;
213
+
214
+ $columns = $this->get_columns();
215
+ $hidden = array();
216
+ $this->_column_headers = array( $columns, $hidden, false );
217
+ $per_page = 20; //20 items per page
218
+ $current_page = $this->get_pagenum();
219
+ $total_items = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log`;" );
220
+
221
+ $items = $itsec_logger->get_events( 'all', array(), $per_page, ( ( $current_page - 1 ) * $per_page ), 'log_date' );
222
+
223
+ $table_data = array();
224
+
225
+ $count = 0;
226
+
227
+ foreach ( $items as $item ) { //loop through and group 404s
228
+
229
+ $table_data[ $count ]['id'] = $count;
230
+ $table_data[ $count ]['type'] = sanitize_text_field( $item['log_type'] );
231
+ $table_data[ $count ]['function'] = sanitize_text_field( $item['log_function'] );
232
+ $table_data[ $count ]['priority'] = sanitize_text_field( $item['log_priority'] );
233
+ $table_data[ $count ]['time'] = sanitize_text_field( $item['log_date'] );
234
+ $table_data[ $count ]['host'] = sanitize_text_field( $item['log_host'] );
235
+ $table_data[ $count ]['user'] = sanitize_text_field( $item['log_username'] );
236
+ $table_data[ $count ]['user_id'] = sanitize_text_field( $item['log_user'] );
237
+ $table_data[ $count ]['url'] = sanitize_text_field( $item['log_url'] );
238
+ $table_data[ $count ]['referrer'] = sanitize_text_field( $item['log_referrer'] );
239
+ $table_data[ $count ]['data'] = $item['log_data'];
240
+
241
+ $count ++;
242
+
243
+ }
244
+
245
+ $this->items = $table_data;
246
+
247
+ $this->set_pagination_args(
248
+ array(
249
+ 'total_items' => $total_items,
250
+ 'per_page' => $per_page,
251
+ 'total_pages' => ceil( $total_items / $per_page )
252
+ )
253
+ );
254
+
255
+ }
256
+
257
+ }
core/class-itsec-logger.php ADDED
@@ -0,0 +1,659 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handles the writing, maintenance and display of log files
5
+ *
6
+ * @package iThemes-Security
7
+ * @since 4.0
8
+ */
9
+ final class ITSEC_Logger {
10
+
11
+ private
12
+ $log_file,
13
+ $logger_displays,
14
+ $logger_modules,
15
+ $module_path;
16
+
17
+ function __construct() {
18
+
19
+ global $itsec_globals;
20
+
21
+ //make sure the log file info is there or generate it. This should only affect beta users.
22
+ if ( ! isset( $itsec_globals['settings']['log_info'] ) ) {
23
+
24
+ $itsec_globals['settings']['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . ITSEC_Lib::get_random( mt_rand( 0, 10 ) );
25
+
26
+ update_site_option( 'itsec_global', $itsec_globals['settings'] );
27
+
28
+ }
29
+
30
+ //Make sure the logs directory was created
31
+ if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
32
+ @mkdir( trailingslashit( $itsec_globals['ithemes_dir'] ) . 'logs' );
33
+ }
34
+
35
+ //don't create a log file if we don't need it.
36
+ if ( isset( $itsec_globals['settings']['log_type'] ) && $itsec_globals['settings']['log_type'] !== 0 ) {
37
+
38
+ $this->log_file = $itsec_globals['ithemes_log_dir'] . '/event-log-' . $itsec_globals['settings']['log_info'] . '.log';
39
+ $this->start_log(); //create a log file if we don't have one
40
+
41
+ }
42
+
43
+ $this->logger_modules = array(); //array to hold information on modules using this feature
44
+ $this->logger_displays = array(); //array to hold metabox information
45
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
46
+
47
+ add_action( 'plugins_loaded', array( $this, 'register_modules' ) );
48
+
49
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
50
+
51
+ //Run database cleanup daily with cron
52
+ if ( ! wp_next_scheduled( 'itsec_purge_logs' ) ) {
53
+ wp_schedule_event( time(), 'daily', 'itsec_purge_logs' );
54
+ }
55
+
56
+ add_action( 'itsec_purge_logs', array( $this, 'purge_logs' ) );
57
+
58
+ if ( is_admin() ) {
59
+
60
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/lib/class-itsec-wp-list-table.php' ); //used for generating log tables
61
+
62
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add log meta boxes
63
+
64
+ }
65
+
66
+ if ( isset( $_POST['itsec_clear_logs'] ) && $_POST['itsec_clear_logs'] === 'clear_logs' ) {
67
+
68
+ global $itsec_clear_all_logs;
69
+
70
+ $itsec_clear_all_logs = true;
71
+
72
+ add_action( 'plugins_loaded', array( $this, 'purge_logs' ) );
73
+
74
+ }
75
+
76
+ }
77
+
78
+ /**
79
+ * Adds a log meta box only if logging is active. Overrides WP Core add_meta_box
80
+ *
81
+ * @since 4.0
82
+ *
83
+ * @return void
84
+ */
85
+ public function add_admin_meta_boxes() {
86
+
87
+ global $itsec_globals;
88
+
89
+ if ( isset( $itsec_globals['settings']['log_type'] ) && ( $itsec_globals['settings']['log_type'] === 0 || $itsec_globals['settings']['log_type'] === 2 ) ) {
90
+
91
+ add_meta_box(
92
+ 'itsec_log_header',
93
+ __( 'Security Log Information', 'better-wp-security' ),
94
+ array( $this, 'metabox_logs_header' ),
95
+ 'security_page_toplevel_page_itsec_logs',
96
+ 'top',
97
+ 'core'
98
+ );
99
+
100
+ add_meta_box(
101
+ 'itsec_log_all',
102
+ __( 'Security Log Data', 'better-wp-security' ),
103
+ array( $this, 'metabox_all_logs' ),
104
+ 'security_page_toplevel_page_itsec_logs',
105
+ 'normal',
106
+ 'core'
107
+ );
108
+
109
+ } else {
110
+
111
+ add_meta_box(
112
+ 'itsec_log_header',
113
+ __( 'Security Log Information', 'better-wp-security' ),
114
+ array( $this, 'metabox_logs_header_no_logs' ),
115
+ 'security_page_toplevel_page_itsec_logs',
116
+ 'top',
117
+ 'core'
118
+ );
119
+
120
+ }
121
+
122
+ }
123
+
124
+ /**
125
+ * Add Logger Admin Javascript
126
+ *
127
+ * @since 4.3
128
+ *
129
+ * @return void
130
+ */
131
+ public function admin_script() {
132
+
133
+ global $itsec_globals;
134
+
135
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'toplevel_page_itsec_logs' ) !== false ) {
136
+
137
+ wp_enqueue_script( 'itsec_logger', $itsec_globals['plugin_url'] . 'core/js/admin-logs.js', array( 'jquery' ), $itsec_globals['plugin_build'], true );
138
+ wp_enqueue_script( 'itsec_url_js', $itsec_globals['plugin_url'] . 'core/js/url.js', array(), $itsec_globals['plugin_build'], true );
139
+
140
+ }
141
+
142
+ }
143
+
144
+ /**
145
+ * Displays all logs content
146
+ *
147
+ * @since 4.3
148
+ *
149
+ * @return void
150
+ */
151
+ public function all_logs_content() {
152
+
153
+ global $wpdb;
154
+
155
+ require( dirname( __FILE__ ) . '/class-itsec-logger-all-logs.php' );
156
+
157
+ $log_display = new ITSEC_Logger_All_Logs();
158
+ $log_display->prepare_items();
159
+ $log_display->display();
160
+
161
+ $log_count = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log`;" );
162
+
163
+ ?>
164
+ <form method="post" action="">
165
+ <?php wp_nonce_field( 'itsec_clear_logs', 'wp_nonce' ); ?>
166
+ <input type="hidden" name="itsec_clear_logs" value="clear_logs"/>
167
+ <table class="form-table">
168
+ <tr valign="top">
169
+ <th scope="row" class="settinglabel">
170
+ <?php _e( 'Log Summary', 'better-wp-security' ); ?>
171
+ </th>
172
+ <td class="settingfield">
173
+
174
+ <p><?php _e( 'Your database contains', 'better-wp-security' ); ?>
175
+ <strong><?php echo $log_count; ?></strong> <?php _e( 'log entries.', 'better-wp-security' ); ?>
176
+ </p>
177
+
178
+ <p><?php _e( 'Use the button below to purge the log table in your database. Please note this will purge all log entries in the database including 404s.', 'better-wp-security' ); ?></p>
179
+
180
+ <p class="submit"><input type="submit" class="button-primary"
181
+ value="<?php _e( 'Clear Logs', 'better-wp-security' ); ?>"/></p>
182
+ </td>
183
+ </tr>
184
+ </table>
185
+ </form>
186
+ <?php
187
+
188
+ }
189
+
190
+ /**
191
+ * Gets events from the logs for a specified module
192
+ *
193
+ * @param string $module module or type of events to fetch
194
+ * @param array $params array of extra query parameters
195
+ * @param int $limit the maximum number of rows to retrieve
196
+ * @param int $offset the offset of the data
197
+ * @param string $order order by column
198
+ * @param bool $direction false for descending or true for ascending
199
+ *
200
+ * @return bool|mixed false on error, null if no events or array of events
201
+ */
202
+ public function get_events( $module, $params = array(), $limit = null, $offset = null, $order = null, $direction = false ) {
203
+
204
+ global $wpdb;
205
+
206
+ if ( isset( $module ) !== true || strlen( $module ) < 1 ) {
207
+ return false;
208
+ }
209
+
210
+ if ( sizeof( $params ) > 0 || $module != 'all' ) {
211
+ $where = " WHERE ";
212
+ } else {
213
+ $where = '';
214
+ }
215
+
216
+ $param_search = '';
217
+
218
+ if ( $module == 'all' ) {
219
+
220
+ $module_sql = '';
221
+ $and = '';
222
+
223
+ } else {
224
+
225
+ $module_sql = "`log_type` = '" . esc_sql( $module ) . "'";
226
+ $and = ' AND ';
227
+
228
+ }
229
+
230
+ if ( $direction === false ) {
231
+
232
+ $order_direction = ' DESC';
233
+
234
+ } else {
235
+
236
+ $order_direction = ' ASC';
237
+
238
+ }
239
+
240
+ if ( $order !== null ) {
241
+
242
+ $order_statement = ' ORDER BY `' . esc_sql( $order ) . '`';
243
+
244
+ } else {
245
+
246
+ $order_statement = ' ORDER BY `log_id`';
247
+
248
+ }
249
+
250
+ if ( $limit !== null ) {
251
+
252
+ if ( $offset !== null ) {
253
+
254
+ $result_limit = ' LIMIT ' . absint( $offset ) . ', ' . absint( $limit );
255
+
256
+ } else {
257
+
258
+ $result_limit = ' LIMIT ' . absint( $limit );
259
+
260
+ }
261
+
262
+ } else {
263
+
264
+ $result_limit = '';
265
+
266
+ }
267
+
268
+ if ( sizeof( $params ) > 0 ) {
269
+
270
+ foreach ( $params as $field => $value ) {
271
+
272
+ if ( gettype( $value ) != 'integer' ) {
273
+ $param_search .= $and . "`" . esc_sql( $field ) . "`='" . esc_sql( $value ) . "'";
274
+ } else {
275
+ $param_search .= $and . "`" . esc_sql( $field ) . "`=" . esc_sql( $value ) . "";
276
+ }
277
+
278
+ }
279
+
280
+ }
281
+
282
+ $items = $wpdb->get_results( "SELECT * FROM `" . $wpdb->base_prefix . "itsec_log`" . $where . $module_sql . $param_search . $order_statement . $order_direction . $result_limit . ";", ARRAY_A );
283
+
284
+ return $items;
285
+
286
+ }
287
+
288
+ /**
289
+ * Logs events sent by other modules or systems
290
+ *
291
+ * @param string $module the module requesting the log entry
292
+ * @param int $priority the priority of the log entry (1-10)
293
+ * @param array $data extra data to log (non-indexed data would be good here)
294
+ * @param string $host the remote host triggering the event
295
+ * @param string $username the username triggering the event
296
+ * @param string $user the user id triggering the event
297
+ * @param string $url the url triggering the event
298
+ * @param string $referrer the referrer to the url (if applicable)
299
+ *
300
+ * @return void
301
+ */
302
+ public function log_event( $module, $priority = 5, $data = array(), $host = '', $username = '', $user = '', $url = '', $referrer = '' ) {
303
+ global $wpdb, $itsec_globals;
304
+
305
+ if ( isset( $this->logger_modules[ $module ] ) ) {
306
+ $options = $this->logger_modules[ $module ];
307
+
308
+ if ( ! isset( $itsec_globals['settings']['log_type'] ) || $itsec_globals['settings']['log_type'] === 0 || $itsec_globals['settings']['log_type'] == 2 ) {
309
+ $values = array(
310
+ 'log_type' => $options['type'],
311
+ 'log_priority' => intval( $priority ),
312
+ 'log_function' => $options['function'],
313
+ 'log_date' => date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ),
314
+ 'log_date_gmt' => date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ),
315
+ 'log_host' => sanitize_text_field( $host ),
316
+ 'log_username' => sanitize_text_field( $username ),
317
+ 'log_user' => intval( $user ),
318
+ 'log_url' => $url,
319
+ 'log_referrer' => $referrer,
320
+ 'log_data' => serialize( $data ),
321
+ );
322
+
323
+ $columns = '`' . implode( '`, `', array_keys( $values ) ) . '`';
324
+ $placeholders = '%s, %d, %s, %s, %s, %s, %s, %s, %s, %s, %s';
325
+
326
+ $query_format = "INSERT INTO `{$wpdb->base_prefix}itsec_log` ($columns) VALUES ($placeholders)";
327
+
328
+ $wpdb->hide_errors(); //Don't show error if table isn't present. Instead we'll just try to reconstruct the tables.
329
+ $result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
330
+ $wpdb->show_errors();
331
+
332
+ if ( ! $result ) {
333
+ ITSEC_Lib::create_database_tables();
334
+
335
+ // Attempt the query again. Since errors will now be shown, a remaining issue will be display an error.
336
+ $result = $wpdb->query( $wpdb->prepare( $query_format, $values ) );
337
+ }
338
+ }
339
+
340
+ if ( isset( $itsec_globals['settings']['log_type'] ) && ( $itsec_globals['settings']['log_type'] === 1 || $itsec_globals['settings']['log_type'] == 2 ) ) {
341
+ $file_data = $this->sanitize_array( $data, true );
342
+
343
+ $message =
344
+ $options['type'] . ',' .
345
+ intval( $priority ) . ',' .
346
+ $options['function'] . ',' .
347
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time'] ) . ',' .
348
+ date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] ) . ',' .
349
+ sanitize_text_field( $host ) . ',' .
350
+ sanitize_text_field( $username ) . ',' .
351
+ ( intval( $user ) === 0 ? '' : intval( $user ) ) . ',' .
352
+ esc_sql( $url ) . ',' .
353
+ esc_sql( $referrer ) . ',' .
354
+ maybe_serialize( $file_data );
355
+
356
+ error_log( $message . PHP_EOL, 3, $this->log_file );
357
+
358
+ }
359
+
360
+ }
361
+
362
+ }
363
+
364
+ /**
365
+ * Displays into box for logs page
366
+ *
367
+ * @since 4.0
368
+ *
369
+ * @return void
370
+ */
371
+ public function metabox_logs_header() {
372
+
373
+ global $itsec_globals;
374
+
375
+ printf(
376
+ '<p>%s %s. %s</p>',
377
+ __( 'Below are various logs of information collected by', 'better-wp-security' ),
378
+ $itsec_globals['plugin_name'],
379
+ __( 'This information can help you get a picture of what is happening with your site and the level of success you have achieved in your security efforts.', 'better-wp-security' )
380
+ );
381
+
382
+ }
383
+
384
+ /**
385
+ * Displays into box for logs page when only file logging is enabled
386
+ *
387
+ * @since 4.0
388
+ *
389
+ * @return void
390
+ */
391
+ public function metabox_logs_header_no_logs() {
392
+
393
+ global $itsec_globals;
394
+
395
+ printf(
396
+ '<p>%s</p>',
397
+ __( 'To view logs within the plugin you must enable database logging in the plugin settings. File logging is not available for access within the plugin itself.', 'better-wp-security' )
398
+ );
399
+
400
+ }
401
+
402
+ /**
403
+ * Displays into box for logs page
404
+ *
405
+ * @since 4.0
406
+ *
407
+ * @return void
408
+ */
409
+ public function metabox_all_logs() {
410
+
411
+ $log_filter = isset( $_GET['itsec_log_filter'] ) ? sanitize_text_field( $_GET['itsec_log_filter'] ) : 'all-log-data';
412
+ $callback = null;
413
+
414
+ echo '<p>' . __( 'To adjust logging options visit the global settings page.', 'better-wp-security' ) . '</p>';
415
+
416
+ echo '<label for="itsec_log_filter"><strong>' . __( 'Select Filter: ', 'better-wp-security' ) . '</strong></label>';
417
+ echo '<select id="itsec_log_filter" name="itsec_log_filter">';
418
+ echo '<option value="all-log-data" ' . selected( $log_filter, 'all-log-data' ) . '>' . __( 'All Log Data', 'better-wp-security' ) . '</option>';
419
+
420
+ if ( sizeof( $this->logger_displays ) > 0 ) {
421
+
422
+ foreach ( $this->logger_displays as $display ) {
423
+
424
+ if ( $display['module'] === $log_filter ) {
425
+ $callback = $display['callback'];
426
+ }
427
+
428
+ echo '<option value="' . $display['module'] . '" ' . selected( $log_filter, $display['module'] ) . '>' . $display['title'] . '</option>';
429
+
430
+ }
431
+
432
+ }
433
+
434
+ echo '</select>';
435
+
436
+ if ( $log_filter === 'all-log-data' || $callback === null ) {
437
+
438
+ $this->all_logs_content();
439
+
440
+ } else {
441
+
442
+ call_user_func_array( $callback, array() );
443
+
444
+ }
445
+
446
+ }
447
+
448
+ /**
449
+ * A better print array function to display array data in the logs
450
+ *
451
+ * @since 4.2
452
+ *
453
+ * @param array $array_items array to print or return
454
+ * @param bool $return true to return the data false to echo it
455
+ */
456
+ public function print_array( $array_items, $return = true ) {
457
+
458
+ $items = '';
459
+
460
+ //make sure we're working with an array
461
+ if ( ! is_array( $array_items ) ) {
462
+ return false;
463
+ }
464
+
465
+ if ( sizeof( $array_items ) > 0 ) {
466
+
467
+ $items .= '<ul>';
468
+
469
+ foreach ( $array_items as $key => $item ) {
470
+
471
+ if ( is_array( $item ) ) {
472
+
473
+ $items .= '<li>';
474
+
475
+ if ( ! is_numeric( $key ) ) {
476
+ $items .= '<h3>' . $key . '</h3>';
477
+ }
478
+
479
+ $items .= $this->print_array( $item, true ) . PHP_EOL;
480
+
481
+ $items .= '</li>';
482
+
483
+ } else {
484
+
485
+ if ( strlen( trim( $item ) ) > 0 ) {
486
+ $items .= '<li><h3>' . $key . ' = ' . $item . '</h3></li>' . PHP_EOL;
487
+ }
488
+
489
+ }
490
+
491
+ }
492
+
493
+ $items .= '</ul>';
494
+
495
+ }
496
+
497
+ return $items;
498
+
499
+ }
500
+
501
+ /**
502
+ * Purges database logs and rotates file logs (when needed)
503
+ *
504
+ * @return void
505
+ */
506
+ public function purge_logs() {
507
+
508
+ global $wpdb, $itsec_globals, $itsec_clear_all_logs;
509
+
510
+ if ( isset( $itsec_clear_all_logs ) && $itsec_clear_all_logs === true ) {
511
+
512
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_clear_logs' ) ) {
513
+ return;
514
+ }
515
+
516
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log`;" );
517
+
518
+ } else {
519
+
520
+ //Clean up the database log first
521
+ if ( $itsec_globals['settings']['log_type'] === 0 || $itsec_globals['settings']['log_type'] == 2 ) {
522
+
523
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log` WHERE `log_date_gmt` < '" . date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] - ( $itsec_globals['settings']['log_rotation'] * 24 * 60 * 60 ) ) . "';" );
524
+
525
+ } else {
526
+
527
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "itsec_log`;" );
528
+
529
+ }
530
+
531
+ if ( ( @file_exists( $this->log_file ) && @filesize( $this->log_file ) >= 10485760 ) ) {
532
+ $this->rotate_log();
533
+ }
534
+
535
+ }
536
+
537
+ }
538
+
539
+ /**
540
+ * Register modules that will use the logger service
541
+ *
542
+ * @return void
543
+ */
544
+ public function register_modules() {
545
+
546
+ $this->logger_modules = apply_filters( 'itsec_logger_modules', $this->logger_modules );
547
+ $this->logger_displays = apply_filters( 'itsec_logger_displays', $this->logger_displays );
548
+
549
+ }
550
+
551
+ /**
552
+ * Rotates the event-log.log file when called
553
+ *
554
+ * Adapted from http://www.phpclasses.org/browse/file/49471.html
555
+ *
556
+ * @return void
557
+ */
558
+ private function rotate_log() {
559
+
560
+ // rotate
561
+ $path_info = pathinfo( $this->log_file );
562
+ $base_directory = $path_info['dirname'];
563
+ $base_name = $path_info['basename'];
564
+ $num_map = array();
565
+
566
+ foreach ( new DirectoryIterator( $base_directory ) as $fInfo ) {
567
+
568
+ if ( $fInfo->isDot() || ! $fInfo->isFile() ) {
569
+ continue;
570
+ }
571
+
572
+ if ( preg_match( '/^' . $base_name . '\.?([0-9]*)$/', $fInfo->getFilename(), $matches ) ) {
573
+
574
+ $num = $matches[1];
575
+ $old_file = $fInfo->getFilename();
576
+
577
+ if ( $num == '' ) {
578
+ $num = - 1;
579
+ }
580
+
581
+ $num_map[ $num ] = $old_file;
582
+
583
+ }
584
+
585
+ }
586
+
587
+ krsort( $num_map );
588
+
589
+ foreach ( $num_map as $num => $old_file ) {
590
+
591
+ $new_file = $num + 1;
592
+ @rename( $base_directory . DIRECTORY_SEPARATOR . $old_file, $this->log_file . '.' . $new_file );
593
+
594
+ }
595
+
596
+ $this->start_log();
597
+
598
+ }
599
+
600
+ /**
601
+ * Sanitizes strings in a given array recursively
602
+ *
603
+ * @param array $array array to sanitize
604
+ * @param bool $to_string true if output should be string or false for array output
605
+ *
606
+ * @return mixed sanitized array or string
607
+ */
608
+ private function sanitize_array( $array, $to_string = false ) {
609
+
610
+ $sanitized_array = array();
611
+ $string = '';
612
+
613
+ //Loop to sanitize each piece of data
614
+ foreach ( $array as $key => $value ) {
615
+
616
+ if ( is_array( $value ) ) {
617
+
618
+ if ( $to_string === false ) {
619
+ $sanitized_array[ esc_sql( $key ) ] = $this->sanitize_array( $value );
620
+ } else {
621
+ $string .= esc_sql( $key ) . '=' . $this->sanitize_array( $value, true );
622
+ }
623
+
624
+ } else {
625
+
626
+ $sanitized_array[ esc_sql( $key ) ] = esc_sql( $value );
627
+
628
+ $string .= esc_sql( $key ) . '=' . esc_sql( $value );
629
+
630
+ }
631
+
632
+ }
633
+
634
+ if ( $to_string === false ) {
635
+ return $sanitized_array;
636
+ } else {
637
+ return $string;
638
+ }
639
+
640
+ }
641
+
642
+ /**
643
+ * Creates a new log file and adds header information (if needed)
644
+ *
645
+ * @return void
646
+ */
647
+ private function start_log() {
648
+
649
+ if ( file_exists( $this->log_file ) !== true ) { //only if current log file doesn't exist
650
+
651
+ $header = 'log_type,log_priority,log_function,log_date,log_date_gmt,log_host,log_username,log_user,log_url,log_referrer,log_data' . PHP_EOL;
652
+
653
+ @error_log( $header, 3, $this->log_file );
654
+
655
+ }
656
+
657
+ }
658
+
659
+ }
core/class-itsec-notify.php ADDED
@@ -0,0 +1,317 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Handles sending notifications to users
5
+ *
6
+ * @package iThemes-Security
7
+ * @since 4.5
8
+ */
9
+ class ITSEC_Notify {
10
+
11
+ private
12
+ $queue;
13
+
14
+ function __construct() {
15
+
16
+ global $itsec_globals;
17
+
18
+ $this->queue = get_site_option( 'itsec_message_queue' );
19
+
20
+ if ( isset( $itsec_globals['settings']['digest_email'] ) && $itsec_globals['settings']['digest_email'] === true ) {
21
+
22
+ if ( defined( 'ITSEC_NOTIFY_USE_CRON' ) && true === ITSEC_NOTIFY_USE_CRON ) {
23
+
24
+ add_action( 'itsec_digest_email', array( $this, 'init' ) ); //Action to execute during a cron run.
25
+
26
+ //schedule digest email
27
+ if ( false === wp_next_scheduled( 'itsec_digest_email' ) ) {
28
+ wp_schedule_event( time(), 'daily', 'itsec_digest_email' );
29
+ }
30
+
31
+ } else {
32
+
33
+ //Send digest if it has been 24 hours
34
+ if (
35
+ get_site_transient( 'itsec_notification_running' ) === false && (
36
+ $this->queue === false ||
37
+ (
38
+ is_array( $this->queue ) &&
39
+ isset( $this->queue['last_sent'] ) &&
40
+ $this->queue['last_sent'] < ( $itsec_globals['current_time_gmt'] - 86400 )
41
+ )
42
+ )
43
+ ) {
44
+ add_action( 'init', array( $this, 'init' ) );
45
+ }
46
+
47
+ }
48
+
49
+ }
50
+
51
+ }
52
+
53
+ /**
54
+ * Processes and sends daily digest message
55
+ *
56
+ * @since 4.5
57
+ *
58
+ * @return void
59
+ */
60
+ public function init() {
61
+
62
+ global $itsec_globals, $itsec_lockout;
63
+
64
+ if ( is_404() || ( ( ! defined( 'ITSEC_NOTIFY_USE_CRON' ) || false === ITSEC_NOTIFY_USE_CRON ) && get_site_transient( 'itsec_notification_running' ) !== false ) ) {
65
+ return;
66
+ }
67
+
68
+ if ( ( ! defined( 'ITSEC_NOTIFY_USE_CRON' ) || false === ITSEC_NOTIFY_USE_CRON ) ) {
69
+ set_site_transient( 'itsec_notification_running', true, 3600 );
70
+ }
71
+
72
+ $messages = false;
73
+ $has_lockouts = true; //assume a lockout has occured by default
74
+
75
+ if ( isset( $this->queue['messages'] ) && sizeof( $this->queue['messages'] ) > 0 ) {
76
+ $messages = $this->queue['messages'];
77
+ }
78
+
79
+ $host_count = sizeof( $itsec_lockout->get_lockouts( 'host', true ) );
80
+ $user_count = sizeof( $itsec_lockout->get_lockouts( 'user', true ) );
81
+
82
+ if ( $host_count == 0 && $user_count == 0 ) {
83
+
84
+ $has_lockouts = false;
85
+ $lockout_message = __( 'There have been no lockouts since the last email check.', 'better-wp-security' );
86
+
87
+ } elseif ( $host_count === 0 && $user_count > 1 ) {
88
+
89
+ $lockout_message = sprintf(
90
+ '%s %s %s',
91
+ __( 'There have been', 'better-wp-security' ),
92
+ $user_count,
93
+ __( 'users or usernames locked out for attempting to log in with incorrect credentials.', 'better-wp-security' )
94
+ );
95
+
96
+ } elseif ( $host_count === 0 && $user_count == 1 ) {
97
+
98
+ $lockout_message = sprintf(
99
+ '%s %s %s',
100
+ __( 'There has been', 'better-wp-security' ),
101
+ $user_count,
102
+ __( 'user or username locked out for attempting to log in with incorrect credentials.', 'better-wp-security' )
103
+ );
104
+
105
+ } elseif ( $host_count == 1 && $user_count === 0 ) {
106
+
107
+ $lockout_message = sprintf(
108
+ '%s %s %s',
109
+ __( 'There has been', 'better-wp-security' ),
110
+ $host_count,
111
+ __( 'host locked out.', 'better-wp-security' )
112
+ );
113
+
114
+ } elseif ( $host_count > 1 && $user_count === 0 ) {
115
+
116
+ $lockout_message = sprintf(
117
+ '%s %s %s',
118
+ __( 'There have been', 'better-wp-security' ),
119
+ $host_count,
120
+ __( 'hosts locked out.', 'better-wp-security' )
121
+ );
122
+
123
+ } else {
124
+
125
+ $lockout_message = sprintf(
126
+ '%s %s %s %s %s %s %s',
127
+ __( 'There have been', 'better-wp-security' ),
128
+ $user_count + $host_count,
129
+ __( 'lockout(s) including', 'better-wp-security' ),
130
+ $user_count,
131
+ __( 'user(s) and', 'better-wp-security' ),
132
+ $host_count,
133
+ __( 'host(s) locked out of your site.', 'better-wp-security' )
134
+ );
135
+
136
+ }
137
+
138
+ if ( $has_lockouts !== false || $messages !== false ) {
139
+
140
+ $module_message = '';
141
+
142
+ if ( is_array( $messages ) ) {
143
+
144
+ foreach ( $messages as $message ) {
145
+
146
+ if ( is_string( $message ) ) {
147
+ $module_message .= '<p>' . $message . '</p>';
148
+ }
149
+
150
+ }
151
+
152
+ }
153
+
154
+ $body = sprintf(
155
+ '<p>%s,</p><p>%s <a href="%s">%s</a></p><p><strong>%s: </strong>%s</p>%s<p>%s %s</p><p>%s <a href="%s">%s</a>.</p>',
156
+ __( 'Dear Site Admin', 'better-wp-security' ),
157
+ __( 'The following is a summary of security related activity on your site. For details please visit', 'better-wp-security' ),
158
+ wp_login_url( get_admin_url( '', 'admin.php?page=toplevel_page_itsec_logs' ) ),
159
+ __( 'the security logs', 'better-wp-security' ),
160
+ __( 'Lockouts', 'better-wp-security' ),
161
+ $lockout_message,
162
+ $module_message,
163
+ __( 'This email was generated automatically by' ),
164
+ $itsec_globals['plugin_name'],
165
+ __( 'To change your email preferences please visit', 'better-wp-security' ),
166
+ wp_login_url( get_admin_url( '', 'admin.php?page=toplevel_page_itsec_settings' ) ),
167
+ __( 'the plugin settings', 'better-wp-security' )
168
+ );
169
+
170
+ //Setup the remainder of the email
171
+ $subject = '[' . get_option( 'siteurl' ) . '] ' . __( 'Daily Security Digest', 'better-wp-security' );
172
+ $subject = apply_filters( 'itsec_lockout_email_subject', $subject );
173
+ $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
174
+
175
+ $this->send_mail( $subject, $body, $headers );
176
+
177
+ }
178
+
179
+ $this->queue = array(
180
+ 'last_sent' => $itsec_globals['current_time_gmt'],
181
+ 'messages' => array(),
182
+ );
183
+
184
+ update_site_option( 'itsec_message_queue', $this->queue );
185
+
186
+ }
187
+
188
+ /**
189
+ * Enqueue or send notification accordingly
190
+ *
191
+ * @since 4.5
192
+ *
193
+ * @param int $type 1 for lockout or 2 for custom message
194
+ * @param null|array $body Custom message information to send
195
+ *
196
+ * @return bool whether the message was successfully enqueue or sent
197
+ */
198
+ public function notify( $body = null ) {
199
+
200
+ global $itsec_globals;
201
+
202
+ $allowed_tags = array(
203
+ 'a' => array(
204
+ 'href' => array(),
205
+ ),
206
+ 'em' => array(),
207
+ 'p' => array(),
208
+ 'strong' => array(),
209
+ 'table' => array(
210
+ 'border' => array(),
211
+ 'style' => array(),
212
+ ),
213
+ 'tr' => array(),
214
+ 'td' => array(
215
+ 'colspan' => array(),
216
+ ),
217
+ 'th' => array(),
218
+ 'br' => array(),
219
+ 'h4' => array(),
220
+ );
221
+
222
+ if ( isset( $itsec_globals['settings']['digest_email'] ) && $itsec_globals['settings']['digest_email'] === true ) {
223
+
224
+ if ( ! in_array( wp_kses( $body, $allowed_tags ), $this->queue['messages'] ) ) {
225
+
226
+ $this->queue['messages'][] = wp_kses( $body, $allowed_tags );
227
+
228
+ update_site_option( 'itsec_message_queue', $this->queue );
229
+
230
+ }
231
+
232
+ return true;
233
+
234
+ } elseif ( isset( $itsec_globals['settings']['email_notifications'] ) && $itsec_globals['settings']['email_notifications'] === true ) {
235
+
236
+ $subject = trim( sanitize_text_field( $body['subject'] ) );
237
+ $message = wp_kses( $body['message'], $allowed_tags );
238
+
239
+ if ( isset( $body['headers'] ) ) {
240
+
241
+ $headers = $body['headers'];
242
+
243
+ } else {
244
+
245
+ $headers = '';
246
+
247
+ }
248
+
249
+ $attachments = isset( $body['attachments'] ) && is_array( $body['attachments'] ) ? $body['attachments'] : array();
250
+
251
+ return $this->send_mail( $subject, $message, $headers, $attachments );
252
+
253
+ }
254
+
255
+ return true;
256
+
257
+ }
258
+
259
+ /**
260
+ * Sends email to recipient
261
+ *
262
+ * @since 4.5
263
+ *
264
+ * @param string $subject Email subject
265
+ * @param string $message Message contents
266
+ * @param string|array $headers Optional. Additional headers.
267
+ * @param string|array $attachments Optional. Files to attach.
268
+ *
269
+ * @return bool Whether the email contents were sent successfully.
270
+ */
271
+ private function send_mail( $subject, $message, $headers = '', $attachments = array() ) {
272
+
273
+ global $itsec_globals;
274
+
275
+ $recipients = $itsec_globals['settings']['notification_email'];
276
+ $all_success = true;
277
+
278
+ add_filter( 'wp_mail_content_type', array( $this, 'wp_mail_content_type' ) );
279
+
280
+ foreach ( $recipients as $recipient ) {
281
+
282
+ if ( is_email( trim( $recipient ) ) ) {
283
+
284
+ if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG === true ) {
285
+ $message .= '<p>' . __( 'Debug info (source page): ' . esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) . '</p>';
286
+ }
287
+
288
+ $success = wp_mail( trim( $recipient ), $subject, '<html>' . $message . '</html>', $headers );
289
+
290
+ if ( $all_success === true && $success === false ) {
291
+ $all_success = false;
292
+ }
293
+
294
+ }
295
+
296
+ }
297
+
298
+ remove_filter( 'wp_mail_content_type', array( $this, 'wp_mail_content_type' ) );
299
+
300
+ return $all_success;
301
+
302
+ }
303
+
304
+ /**
305
+ * Set HTML content type for email
306
+ *
307
+ * @since 4.5
308
+ *
309
+ * @return string html content type
310
+ */
311
+ public function wp_mail_content_type() {
312
+
313
+ return 'text/html';
314
+
315
+ }
316
+
317
+ }
core/class-itsec-setup.php ADDED
@@ -0,0 +1,538 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Plugin activation, upgrade, deactivation and uninstall
5
+ *
6
+ * @package iThemes-Security
7
+ * @since 4.0
8
+ */
9
+ class ITSEC_Setup {
10
+
11
+ private
12
+ $defaults;
13
+
14
+ /**
15
+ * Establish setup object
16
+ *
17
+ * Establishes set object and calls appropriate execution function
18
+ *
19
+ * @param bool $case [optional] Appropriate execution module to call
20
+ *
21
+ * */
22
+ function __construct( $case = false, $upgrading = false ) {
23
+
24
+ global $itsec_globals;
25
+
26
+ $this->defaults = array(
27
+ 'notification_email' => array( get_option( 'admin_email' ) ),
28
+ 'backup_email' => array( get_option( 'admin_email' ) ),
29
+ 'lockout_message' => __( 'error', 'better-wp-security' ),
30
+ 'user_lockout_message' => __( 'You have been locked out due to too many invalid login attempts.', 'better-wp-security' ),
31
+ 'community_lockout_message' => __( "Your IP address has been flagged as a threat by the iThemes Security network.", 'better-wp-security' ),
32
+ 'blacklist' => true,
33
+ 'blacklist_count' => 3,
34
+ 'blacklist_period' => 7,
35
+ 'email_notifications' => true,
36
+ 'lockout_period' => 15,
37
+ 'lockout_white_list' => array(),
38
+ 'log_rotation' => 14,
39
+ 'log_type' => 0,
40
+ 'log_location' => $itsec_globals['ithemes_log_dir'],
41
+ 'allow_tracking' => false,
42
+ 'write_files' => false,
43
+ 'nginx_file' => ABSPATH . 'nginx.conf',
44
+ 'infinitewp_compatibility' => false,
45
+ 'did_upgrade' => false,
46
+ 'lock_file' => false,
47
+ 'digest_email' => false,
48
+ 'proxy_override' => false,
49
+ 'hide_admin_bar' => false,
50
+ );
51
+
52
+ if ( ! $case ) {
53
+ die( 'error' );
54
+ }
55
+
56
+ switch ( $case ) {
57
+
58
+ case 'activate': //active plugin
59
+ $this->activate_execute();
60
+ break;
61
+
62
+ case 'upgrade': //upgrade plugin
63
+ $this->upgrade_execute( $upgrading );
64
+ break;
65
+
66
+ case 'deactivate': //deactivate plugin
67
+ $this->deactivate_execute();
68
+ break;
69
+
70
+ case 'uninstall': //uninstall plugin
71
+
72
+ //Don't run the uninstall script if another version of the plugin is active
73
+ $free_base = 'better-wp-security/better-wp-security.php';
74
+ $pro_base = 'ithemes-security-pro/ithemes-security-pro.php';
75
+
76
+ if ( $pro_base == $itsec_globals['plugin_base'] ) {
77
+ $plugin = $free_base;
78
+ } else {
79
+ $plugin = $pro_base;
80
+ }
81
+
82
+ if ( is_multisite() ) {
83
+
84
+ $active = is_plugin_active_for_network( $plugin );
85
+
86
+ } else {
87
+
88
+ $active = is_plugin_active( $plugin );
89
+
90
+ }
91
+
92
+ if ( $active === false ) {
93
+ $this->uninstall_execute();
94
+ }
95
+
96
+ break;
97
+
98
+ }
99
+
100
+ }
101
+
102
+ /**
103
+ * Execute setup script for each module installed
104
+ *
105
+ * @return void
106
+ */
107
+ function do_modules() {
108
+
109
+ global $itsec_globals;
110
+
111
+ $free_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/modules';
112
+ $pro_modules_folder = trailingslashit( $itsec_globals['plugin_dir'] ) . 'pro';
113
+
114
+ $has_pro = is_dir( $pro_modules_folder );
115
+
116
+ if ( $has_pro ) {
117
+
118
+ foreach ( $itsec_globals['pro_modules'] as $module => $info ) {
119
+
120
+ if ( file_exists( $pro_modules_folder . '/' . $module . '/setup.php' ) ) {
121
+ require( $pro_modules_folder . '/' . $module . '/setup.php' );
122
+ }
123
+
124
+ }
125
+
126
+ }
127
+
128
+ foreach ( $itsec_globals['free_modules'] as $module => $info ) {
129
+
130
+ if ( ( $has_pro === false || ! in_array( $module, $itsec_globals['pro_modules'] ) ) && file_exists( $free_modules_folder . '/' . $module . '/setup.php' ) ) {
131
+ require( $free_modules_folder . '/' . $module . '/setup.php' );
132
+ }
133
+
134
+ }
135
+
136
+ }
137
+
138
+ /**
139
+ * Public function to activate
140
+ *
141
+ * */
142
+ static function on_activate() {
143
+
144
+ global $itsec_setup_action;
145
+
146
+ $itsec_setup_action = 'activate';
147
+
148
+ define( 'ITSEC_DO_ACTIVATION', true );
149
+
150
+ new ITSEC_Setup( 'activate' );
151
+
152
+ }
153
+
154
+ /**
155
+ * Public function to deactivate
156
+ *
157
+ * */
158
+ static function on_deactivate() {
159
+
160
+ global $itsec_setup_action;
161
+
162
+ if ( defined( 'ITSEC_DEVELOPMENT' ) && ITSEC_DEVELOPMENT == true ) { //set ITSEC_DEVELOPMENT to true to reset settings on deactivation for development
163
+
164
+ $itsec_setup_action = 'uninstall';
165
+
166
+ } else {
167
+
168
+ $itsec_setup_action = 'deactivate';
169
+
170
+ }
171
+
172
+ new ITSEC_Setup( $itsec_setup_action );
173
+ }
174
+
175
+ /**
176
+ * Public function to uninstall
177
+ *
178
+ * */
179
+ static function on_uninstall() {
180
+
181
+ global $itsec_setup_action;
182
+
183
+ $itsec_setup_action = 'uninstall';
184
+
185
+ new ITSEC_Setup( 'uninstall' );
186
+
187
+ }
188
+
189
+ /**
190
+ * Execute activation.
191
+ *
192
+ * @since 4.0
193
+ *
194
+ * @param boolean $upgrade true if the plugin is updating
195
+ *
196
+ * @return void
197
+ */
198
+ private function activate_execute() {
199
+
200
+ global $itsec_globals, $itsec_files;
201
+
202
+ //if this is multisite make sure they're network activating or die
203
+ if ( defined( 'ITSEC_DO_ACTIVATION' ) && ITSEC_DO_ACTIVATION == true && is_multisite() && ! strpos( $_SERVER['REQUEST_URI'], 'wp-admin/network/plugins.php' ) ) {
204
+
205
+ die ( __( '<strong>ERROR</strong>: You must activate this plugin from the network dashboard.', 'better-wp-security' ) );
206
+
207
+ }
208
+
209
+ //make sure directories are present and they are not remotely accessible
210
+ if ( ! is_dir( $itsec_globals['ithemes_dir'] ) ) {
211
+
212
+ @mkdir( $itsec_globals['ithemes_dir'] );
213
+ $handle = @fopen( $itsec_globals['ithemes_dir'] . '/.htaccess', 'w+' );
214
+ @fwrite( $handle, 'Deny from all' );
215
+ @fclose( $handle );
216
+
217
+ }
218
+
219
+ if ( ! is_dir( $itsec_globals['ithemes_log_dir'] ) ) {
220
+
221
+ @mkdir( $itsec_globals['ithemes_log_dir'] );
222
+ $handle = @fopen( $itsec_globals['ithemes_log_dir'] . '/.htaccess', 'w+' );
223
+ @fwrite( $handle, 'Deny from all' );
224
+ @fclose( $handle );
225
+
226
+ }
227
+
228
+ if ( ! is_dir( $itsec_globals['ithemes_backup_dir'] ) ) {
229
+
230
+ @mkdir( $itsec_globals['ithemes_backup_dir'] );
231
+ $handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/.htaccess', 'w+' );
232
+ @fwrite( $handle, 'Deny from all' );
233
+ @fclose( $handle );
234
+
235
+ }
236
+
237
+ if ( ( $site_data = get_site_option( 'itsec_data' ) ) === false ) {
238
+ add_site_option( 'itsec_data', array(), false );
239
+ }
240
+
241
+ if ( get_site_option( 'itsec_initials' ) === false ) {
242
+ add_site_option( 'itsec_initials', array(), false );
243
+ }
244
+
245
+ if ( get_site_option( 'itsec_api_nag' ) === false ) { //show the nag to activate an API key
246
+ add_site_option( 'itsec_api_nag', true, false );
247
+ }
248
+
249
+ $options = get_site_option( 'itsec_global' );
250
+
251
+ if ( $options === false || ( isset( $options['log_info'] ) && sizeof( $options ) <= 2 ) ) {
252
+
253
+ $this->defaults['log_info'] = substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . ITSEC_Lib::get_random( mt_rand( 0, 10 ) );
254
+
255
+ $itsec_globals['settings'] = $this->defaults;
256
+
257
+ update_site_option( 'itsec_global', $this->defaults );
258
+
259
+ }
260
+
261
+ //load utility functions
262
+ if ( ! class_exists( 'ITSEC_Lib' ) ) {
263
+ require( trailingslashit( $itsec_globals['plugin_dir'] ) . 'core/class-itsec-lib.php' );
264
+ }
265
+
266
+ ITSEC_Lib::create_database_tables();
267
+
268
+ $this->do_modules();
269
+
270
+ }
271
+
272
+ /**
273
+ * Update Execution
274
+ *
275
+ * @since 4.0
276
+ *
277
+ * @param string $old_version Old version number
278
+ *
279
+ * @return void
280
+ */
281
+ private function upgrade_execute( $upgrade = false ) {
282
+
283
+ global $itsec_old_version, $itsec_globals, $wpdb, $itsec_setup_action;
284
+
285
+ $itsec_setup_action = 'upgrade';
286
+ $itsec_old_version = $upgrade;
287
+
288
+ if ( $itsec_old_version < 4000 ) {
289
+
290
+ global $itsec_bwps_options;
291
+
292
+ if ( wp_next_scheduled( 'bwps_backup' ) ) {
293
+ wp_clear_scheduled_hook( 'bwps_backup' );
294
+ }
295
+
296
+ if ( is_multisite() ) {
297
+
298
+ switch_to_blog( 1 );
299
+
300
+ $itsec_bwps_options = get_option( 'bit51_bwps' );
301
+ delete_option( 'bit51_bwps' );
302
+ delete_option( 'bwps_intrusion_warning' );
303
+ delete_option( 'bit51_bwps_data' );
304
+ delete_site_transient( 'bit51_bwps_backup' );
305
+ delete_site_transient( 'bwps_away' );
306
+
307
+ restore_current_blog();
308
+
309
+ } else {
310
+
311
+ $itsec_bwps_options = get_option( 'bit51_bwps' );
312
+ delete_option( 'bit51_bwps' );
313
+ delete_option( 'bwps_intrusion_warning' );
314
+ delete_option( 'bit51_bwps_data' );
315
+ delete_site_transient( 'bit51_bwps_backup' );
316
+ delete_site_transient( 'bwps_away' );
317
+
318
+ }
319
+
320
+ if ( $itsec_bwps_options !== false ) {
321
+
322
+ $current_options = get_site_option( 'itsec_global' );
323
+
324
+ if ( $current_options === false ) {
325
+ $current_options = $this->defaults;
326
+ }
327
+
328
+ $current_options['notification_email'] = array( isset( $itsec_bwps_options['ll_emailaddress'] ) && strlen( $itsec_bwps_options['ll_emailaddress'] ) ? $itsec_bwps_options['ll_emailaddress'] : get_option( 'admin_email' ) );
329
+ $current_options['backup_email'] = array( isset( $itsec_bwps_options['backup_emailaddress'] ) && strlen( $itsec_bwps_options['backup_emailaddress'] ) ? $itsec_bwps_options['backup_emailaddress'] : get_option( 'admin_email' ) );
330
+ $current_options['blacklist'] = isset( $itsec_bwps_options['ll_blacklistip'] ) && $itsec_bwps_options['ll_blacklistip'] == 0 ? false : true;
331
+ $current_options['blacklist_count'] = isset( $itsec_bwps_options['ll_blacklistipthreshold'] ) && intval( $itsec_bwps_options['ll_blacklistipthreshold'] ) > 0 ? intval( $itsec_bwps_options['ll_blacklistipthreshold'] ) : 3;
332
+ $current_options['write_files'] = isset( $itsec_bwps_options['st_writefiles'] ) && $itsec_bwps_options['st_writefiles'] == 1 ? true : false;
333
+ $itsec_globals['settings']['write_files'] = $current_options['write_files'];
334
+ $current_options['did_upgrade'] = true;
335
+
336
+ if ( isset( $itsec_bwps_options['id_whitelist'] ) && ! is_array( $itsec_bwps_options['id_whitelist'] ) && strlen( $itsec_bwps_options['id_whitelist'] ) > 1 ) {
337
+
338
+ $raw_hosts = explode( PHP_EOL, $itsec_bwps_options['id_whitelist'] );
339
+
340
+ foreach ( $raw_hosts as $host ) {
341
+
342
+ if ( strlen( $host ) > 1 ) {
343
+ $current_options['lockout_white_list'][] = $host;
344
+ }
345
+
346
+ }
347
+
348
+ }
349
+
350
+ if ( $current_options['write_files'] === false ) {
351
+ set_site_transient( 'ITSEC_SHOW_WRITE_FILES_TOOLTIP', true, 600 );
352
+ }
353
+
354
+ update_site_option( 'itsec_global', $current_options );
355
+
356
+ }
357
+
358
+ $wpdb->query( "DROP TABLE IF EXISTS `" . $wpdb->base_prefix . "bwps_lockouts`;" );
359
+ $wpdb->query( "DROP TABLE IF EXISTS `" . $wpdb->base_prefix . "bwps_log`;" );
360
+ $wpdb->query( "DROP TABLE IF EXISTS `" . $wpdb->base_prefix . "BWPS_d404`;" );
361
+ $wpdb->query( "DROP TABLE IF EXISTS `" . $wpdb->base_prefix . "BWPS_ll`;" );
362
+ $wpdb->query( "DROP TABLE IF EXISTS `" . $wpdb->base_prefix . "BWPS_lockouts`;" );
363
+
364
+ delete_option( 'bwps_file_log' );
365
+ delete_option( 'bwps_awaymode' );
366
+ delete_option( 'bwps_filecheck' );
367
+ delete_option( 'BWPS_Login_Slug' );
368
+ delete_option( 'BWPS_options' );
369
+ delete_option( 'BWPS_versions' );
370
+ delete_option( 'bit51_bwps_data' );
371
+
372
+ }
373
+
374
+ $this->do_modules();
375
+
376
+ $itsec_globals['data']['build'] = $itsec_globals['plugin_build'];
377
+
378
+ update_site_option( 'itsec_data', $itsec_globals['data'] );
379
+
380
+ if ( $itsec_old_version < 4030 ) {
381
+
382
+ ITSEC_Lib::create_database_tables(); //adds username field to lockouts and temp
383
+ add_site_option( 'itsec_rewrites_changed', true );
384
+
385
+ }
386
+
387
+ if ( $itsec_old_version < 4031 ) {
388
+
389
+ $banned_option = get_site_option( 'itsec_ban_users' );
390
+
391
+ if ( isset( $banned_option['white_list'] ) ) {
392
+
393
+ $banned_white_list = $banned_option['white_list'];
394
+ $options = get_site_option( 'itsec_global' );
395
+ $white_list = isset( $options['lockout_white_list'] ) ? $options['lockout_white_list'] : array();
396
+
397
+ if ( ! is_array( $white_list ) ) {
398
+ $white_list = explode( PHP_EOL, $white_list );
399
+ }
400
+
401
+ if ( ! is_array( $banned_white_list ) ) {
402
+ $banned_white_list = explode( PHP_EOL, $banned_white_list );
403
+ }
404
+
405
+ $new_white_list = array_merge( $white_list, $banned_white_list );
406
+
407
+ $options['lockout_white_list'] = $new_white_list;
408
+
409
+ update_site_option( 'itsec_global', $options );
410
+
411
+ }
412
+
413
+ }
414
+
415
+ if ( $itsec_old_version < 4033 ) {
416
+
417
+ if ( get_site_option( 'itsec_api_nag' ) === false ) { //show the nag to activate an API key
418
+ add_site_option( 'itsec_api_nag', true, false );
419
+ }
420
+
421
+ }
422
+
423
+ }
424
+
425
+ /**
426
+ * Deactivate execution
427
+ *
428
+ * @since 4.0
429
+ *
430
+ * @return void
431
+ * */
432
+ private function deactivate_execute() {
433
+
434
+ global $itsec_files, $wpdb;
435
+
436
+ wp_clear_scheduled_hook( 'itsec_purge_lockouts' );
437
+
438
+ $this->do_modules();
439
+
440
+ $itsec_files->do_deactivate();
441
+
442
+ delete_site_option( 'itsec_flush_old_rewrites' );
443
+ delete_site_option( 'itsec_manual_update' );
444
+ delete_site_option( 'itsec_rewrites_changed' );
445
+ delete_site_option( 'itsec_config_changed' );
446
+ delete_site_option( 'itsec_had_other_version' );
447
+ delete_site_option( 'itsec_no_file_lock_release' );
448
+ delete_site_option( 'itsec_clear_login' );
449
+ delete_site_option( 'itsec_temp_whitelist_ip' );
450
+ delete_site_option( 'itsec_api_nag' );
451
+ delete_site_transient( 'ITSEC_SHOW_WRITE_FILES_TOOLTIP' );
452
+ delete_site_transient( 'itsec_upload_dir' );
453
+ delete_site_transient( 'itsec_notification_running' );
454
+ wp_clear_scheduled_hook( 'itsec_digest_email' );
455
+
456
+ $htaccess = ITSEC_Lib::get_htaccess();
457
+
458
+ //Make sure we can write to the file
459
+ $perms = substr( sprintf( '%o', @fileperms( $htaccess ) ), - 4 );
460
+
461
+ if ( $perms == '0444' ) {
462
+ @chmod( $htaccess, 0664 );
463
+ }
464
+
465
+ flush_rewrite_rules();
466
+
467
+ //reset file permissions if we changed them
468
+ if ( $perms == '0444' ) {
469
+ @chmod( $htaccess, 0444 );
470
+ }
471
+
472
+ ITSEC_Lib::clear_caches();
473
+
474
+ }
475
+
476
+ /**
477
+ * Uninstall execution
478
+ *
479
+ * @since 4.0
480
+ *
481
+ * @return void
482
+ * */
483
+ private function uninstall_execute() {
484
+
485
+ global $itsec_globals, $itsec_files, $wpdb;
486
+
487
+ $this->deactivate_execute();
488
+
489
+ $itsec_files->do_deactivate();
490
+
491
+ delete_site_option( 'itsec_global' );
492
+ delete_site_option( 'itsec_data' );
493
+ delete_site_option( 'itsec_initials' );
494
+ delete_site_option( 'itsec_jquery_version' );
495
+ delete_site_option( 'itsec_message_queue' );
496
+
497
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->base_prefix . "itsec_log;" );
498
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->base_prefix . "itsec_lockouts;" );
499
+ $wpdb->query( "DROP TABLE IF EXISTS " . $wpdb->base_prefix . "itsec_temp;" );
500
+
501
+ if ( is_dir( $itsec_globals['ithemes_dir'] ) ) {
502
+ $this->recursive_delete( $itsec_globals['ithemes_dir'] );
503
+ }
504
+
505
+ ITSEC_Lib::clear_caches();
506
+ }
507
+
508
+ /**
509
+ * Deletes all iThemes Security files.
510
+ *
511
+ * @access private
512
+ *
513
+ * @since 4.0
514
+ *
515
+ * @param string $path path of plugin files
516
+ *
517
+ * @return void
518
+ */
519
+ private function recursive_delete( $path ) {
520
+
521
+ foreach ( scandir( $path ) as $item ) {
522
+
523
+ if ( $item != '.' && $item != '..' ) {
524
+
525
+ if ( is_dir( $path . '/' . $item ) ) {
526
+ $this->recursive_delete( $path . '/' . $item );
527
+ }
528
+
529
+ }
530
+
531
+ @unlink( $path . '/' . $item );
532
+ }
533
+
534
+ @rmdir( $path );
535
+
536
+ }
537
+
538
+ }
core/class-itsec-sync.php ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Handles the abstraction of sync integration.
4
+ *
5
+ * Registers modules with sync verbs and loads appropriate verb classes where applicable.
6
+ *
7
+ * @package iThemes_Security
8
+ *
9
+ * @since 4.0.0
10
+ */
11
+ final class ITSEC_Sync {
12
+
13
+ /**
14
+ * The module's that have registered with sync
15
+ *
16
+ * @since 4.1.0
17
+ * @access private
18
+ * @var array
19
+ */
20
+ private $sync_modules;
21
+
22
+ /**
23
+ * Loads sync modules
24
+ *
25
+ * Executes primary file actions at plugins_loaded.
26
+ *
27
+ * @since 4.1.0
28
+ *
29
+ * @return ITSEC_Sync
30
+ */
31
+ public function __construct() {
32
+
33
+ $this->sync_modules = array(); //array to hold information on modules using this feature
34
+
35
+ add_action( 'plugins_loaded', array( $this, 'register_modules' ), 20 );
36
+ add_action( 'ithemes_sync_register_verbs', array( $this, 'ithemes_sync_register_verbs' ) );
37
+
38
+ }
39
+
40
+ /**
41
+ * Returns all modules registered with Sync.
42
+ *
43
+ * Returns an array of all modules containing sync verbs.
44
+ *
45
+ * @since 4.1.0
46
+ *
47
+ * @return array sync module registrations
48
+ */
49
+ public function get_modules() {
50
+
51
+ return $this->sync_modules;
52
+
53
+ }
54
+
55
+ /**
56
+ * Register verbs for iThemes Sync.
57
+ *
58
+ * Registers all verbs for a given module.
59
+ *
60
+ * @since 4.1.0
61
+ *
62
+ * @param object $api iThemes Sync Object
63
+ *
64
+ * @return void
65
+ */
66
+ public function ithemes_sync_register_verbs( $api ) {
67
+
68
+ foreach ( $this->sync_modules as $module ) {
69
+
70
+ if ( isset( $module['verbs'] ) && isset( $module['path'] ) ) {
71
+
72
+ foreach ( $module['verbs'] as $name => $class ) {
73
+
74
+ $api->register( $name, $class, trailingslashit( $module['path'] ) . 'class-ithemes-sync-verb-' . $name . '.php' );
75
+
76
+ }
77
+
78
+ }
79
+
80
+ }
81
+
82
+ $api->register( 'itsec-get-everything', 'Ithemes_Sync_Verb_ITSEC_Get_Everything', dirname( __FILE__ ) . '/class-ithemes-sync-verb-itsec-get-everything.php' );
83
+
84
+ }
85
+
86
+ /**
87
+ * Register modules that will use the sync service.
88
+ *
89
+ * Executes a filter that allows modules to register themselves for iThemes Sync integration.
90
+ *
91
+ * @since 4.1.0
92
+ *
93
+ * @return void
94
+ */
95
+ public function register_modules() {
96
+
97
+ $this->sync_modules = apply_filters( 'itsec_sync_modules', $this->sync_modules );
98
+
99
+ }
100
+
101
+ }
core/content/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/content/perms.php ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WordPress Permissions check code from Serverbuddy by PluginBuddy written by Dustin Bolton of iThemes
4
+ */
5
+ global $itsec_globals;
6
+
7
+ $tests = array();
8
+
9
+ //BEGIN FOLDERS
10
+ $this_test = array(
11
+ 'title' => '/',
12
+ 'suggestion' => '= 755',
13
+ 'value' => substr( sprintf( '%o', fileperms( ABSPATH . '/' ) ), - 4 ),
14
+ );
15
+
16
+ if ( ! fileperms( ABSPATH . '/' ) || 755 != substr( sprintf( '%o', fileperms( ABSPATH . '/' ) ), - 4 ) ) {
17
+
18
+ $this_test['status'] = 'WARNING';
19
+
20
+ } else {
21
+
22
+ $this_test['status'] = 'OK';
23
+
24
+ }
25
+
26
+ array_push( $tests, $this_test );
27
+
28
+ $this_test = array(
29
+ 'title' => '/wp-includes/',
30
+ 'suggestion' => '= 755',
31
+ 'value' => substr( sprintf( '%o', fileperms( ABSPATH . '/wp-includes/' ) ), - 4 ),
32
+ );
33
+
34
+ if ( ! fileperms( ABSPATH . '/wp-includes/' ) || 755 != substr( sprintf( '%o', fileperms( ABSPATH . '/wp-includes/' ) ), - 4 ) ) {
35
+
36
+ $this_test['status'] = 'WARNING';
37
+
38
+ } else {
39
+
40
+ $this_test['status'] = 'OK';
41
+
42
+ }
43
+
44
+ array_push( $tests, $this_test );
45
+
46
+
47
+ $this_test = array(
48
+ 'title' => '/wp-admin/',
49
+ 'suggestion' => '= 755',
50
+ 'value' => substr( sprintf( '%o', fileperms( ABSPATH . '/wp-admin/' ) ), - 4 ),
51
+ );
52
+
53
+ if ( ! fileperms( ABSPATH . '/wp-admin/' ) || 755 != substr( sprintf( '%o', fileperms( ABSPATH . '/wp-admin/' ) ), - 4 ) ) {
54
+
55
+ $this_test['status'] = 'WARNING';
56
+
57
+ } else {
58
+
59
+ $this_test['status'] = 'OK';
60
+
61
+ }
62
+
63
+ array_push( $tests, $this_test );
64
+
65
+
66
+ $this_test = array(
67
+ 'title' => '/wp-admin/js/',
68
+ 'suggestion' => '= 755',
69
+ 'value' => substr( sprintf( '%o', fileperms( ABSPATH . '/wp-admin/js/' ) ), - 4 ),
70
+ );
71
+
72
+ if ( ! fileperms( ABSPATH . '/wp-admin/js/' ) || 755 != substr( sprintf( '%o', fileperms( ABSPATH . '/wp-admin/js/' ) ), - 4 ) ) {
73
+
74
+ $this_test['status'] = 'WARNING';
75
+
76
+ } else {
77
+
78
+ $this_test['status'] = 'OK';
79
+
80
+ }
81
+
82
+ array_push( $tests, $this_test );
83
+
84
+
85
+ $this_test = array(
86
+ 'title' => get_theme_root(),
87
+ 'suggestion' => '= 755',
88
+ 'value' => substr( sprintf( '%o', fileperms( get_theme_root() ) ), - 4 ),
89
+ );
90
+
91
+ if ( ! fileperms( get_theme_root() ) || 755 != substr( sprintf( '%o', fileperms( get_theme_root() ) ), - 4 ) ) {
92
+
93
+ $this_test['status'] = 'WARNING';
94
+
95
+ } else {
96
+
97
+ $this_test['status'] = 'OK';
98
+
99
+ }
100
+
101
+ array_push( $tests, $this_test );
102
+
103
+ $this_test = array(
104
+ 'title' => str_replace( ABSPATH, '', dirname( plugin_dir_path( $itsec_globals['plugin_file'] ) ) ),
105
+ 'suggestion' => '= 755',
106
+ 'value' => substr( sprintf( '%o', fileperms( dirname( plugin_dir_path( $itsec_globals['plugin_file'] ) ) ) ), - 4 ),
107
+ );
108
+
109
+ if ( ! dirname( plugin_dir_path( $itsec_globals['plugin_file'] ) ) || 755 != substr( sprintf( '%o', fileperms( dirname( plugin_dir_path( $itsec_globals['plugin_file'] ) ) ) ), - 4 ) ) {
110
+
111
+ $this_test['status'] = 'WARNING';
112
+
113
+ } else {
114
+
115
+ $this_test['status'] = 'OK';
116
+
117
+ }
118
+
119
+ array_push( $tests, $this_test );
120
+
121
+ if ( defined( 'WP_CONTENT_DIR' ) ) {
122
+
123
+ $wp_content_dir = WP_CONTENT_DIR;
124
+
125
+ } else {
126
+
127
+ $wp_content_dir = ABSPATH . '/wp-content/';
128
+
129
+ }
130
+
131
+ $this_test = array(
132
+ 'title' => str_replace( ABSPATH, '', $wp_content_dir ),
133
+ 'suggestion' => '= 755',
134
+ 'value' => substr( sprintf( '%o', fileperms( $wp_content_dir ) ), - 4 ),
135
+ );
136
+
137
+ if ( ! fileperms( $wp_content_dir ) || 755 != substr( sprintf( '%o', fileperms( $wp_content_dir ) ), - 4 ) ) {
138
+
139
+ $this_test['status'] = 'WARNING';
140
+
141
+ } else {
142
+
143
+ $this_test['status'] = 'OK';
144
+
145
+ }
146
+
147
+ array_push( $tests, $this_test );
148
+
149
+ $wp_upload_dir = wp_upload_dir();
150
+
151
+ $this_test = array(
152
+ 'title' => str_replace( ABSPATH, '', $wp_upload_dir['basedir'] ),
153
+ 'suggestion' => '= 755',
154
+ 'value' => substr( sprintf( '%o', fileperms( $wp_upload_dir['basedir'] ) ), - 4 ),
155
+ );
156
+
157
+ if ( ! fileperms( $wp_upload_dir['basedir'] ) || 755 != substr( sprintf( '%o', fileperms( $wp_upload_dir['basedir'] ) ), - 4 ) ) {
158
+
159
+ $this_test['status'] = 'WARNING';
160
+
161
+ } else {
162
+
163
+ $this_test['status'] = 'OK';
164
+
165
+ }
166
+
167
+ array_push( $tests, $this_test );
168
+ //END FOLDERS
169
+
170
+ //BEGIN FILES
171
+ $this_test = array(
172
+ 'title' => 'wp-config.php',
173
+ 'suggestion' => '= 444',
174
+ 'value' => substr( sprintf( '%o', fileperms( ITSEC_Lib::get_config() ) ), - 4 ),
175
+ );
176
+
177
+ if ( ! fileperms( ITSEC_Lib::get_config() ) || 444 != substr( sprintf( '%o', fileperms( ITSEC_Lib::get_config() ) ), - 4 ) ) {
178
+
179
+ $this_test['status'] = 'WARNING';
180
+
181
+ } else {
182
+
183
+ $this_test['status'] = 'OK';
184
+
185
+ }
186
+
187
+ array_push( $tests, $this_test );
188
+
189
+ $this_test = array(
190
+ 'title' => '.htaccess',
191
+ 'suggestion' => '= 444',
192
+ 'value' => substr( sprintf( '%o', fileperms( ITSEC_Lib::get_htaccess() ) ), - 4 ),
193
+ );
194
+
195
+ if ( ! fileperms( ITSEC_Lib::get_htaccess() ) || 444 != substr( sprintf( '%o', fileperms( ITSEC_Lib::get_htaccess() ) ), - 4 ) ) {
196
+
197
+ $this_test['status'] = 'WARNING';
198
+
199
+ } else {
200
+
201
+ $this_test['status'] = 'OK';
202
+
203
+ }
204
+
205
+ array_push( $tests, $this_test );
206
+ //END FILES
207
+
208
+ ?>
209
+
210
+ <table class="widefat">
211
+ <thead>
212
+ <tr class="thead">
213
+ <th><?php _e('Relative Path', 'better-wp-security' ); ?></th>
214
+ <th><?php _e('Suggestion', 'better-wp-security' ); ?></th>
215
+ <th<?php _e('>Value', 'better-wp-security' ); ?></th>
216
+ <th><?php _e('Result', 'better-wp-security' ); ?></th>
217
+ <th style="width: 60px;"><?php _e('Status', 'better-wp-security' ); ?></th>
218
+ </tr>
219
+ </thead>
220
+ <tfoot>
221
+ <tr class="thead">
222
+ <th><?php _e('Relative Path', 'better-wp-security' ); ?></th>
223
+ <th><?php _e('Suggestion', 'better-wp-security' ); ?></th>
224
+ <th><?php _e('Value', 'better-wp-security' ); ?></th>
225
+ <th><?php _e('Result', 'better-wp-security' ); ?></th>
226
+ <th style="width: 60px;"><?php _e('Status', 'better-wp-security' ); ?></th>
227
+ </tr>
228
+ </tfoot>
229
+ <tbody>
230
+
231
+ <?php
232
+ foreach ( $tests as $this_test ) {
233
+
234
+ echo '<tr class="entry-row alternate">';
235
+ echo ' <td>' . $this_test['title'] . '</td>';
236
+ echo ' <td>' . $this_test['suggestion'] . '</td>';
237
+ echo ' <td>' . $this_test['value'] . '</td>';
238
+ echo ' <td>' . $this_test['status'] . '</td>';
239
+ echo ' <td>';
240
+
241
+ if ( 'OK' == $this_test['status'] ) {
242
+
243
+ echo '<div style="background-color: #22EE5B; border: 1px solid #E2E2E2;">&nbsp;&nbsp;&nbsp;</div>';
244
+
245
+ } elseif ( 'FAIL' == $this_test['status'] ) {
246
+
247
+ echo '<div style="background-color: #CF3333; border: 1px solid #E2E2E2;">&nbsp;&nbsp;&nbsp;</div>';
248
+
249
+ } elseif ( 'WARNING' == $this_test['status'] ) {
250
+
251
+ echo '<div style="background-color: #FEFF7F; border: 1px solid #E2E2E2;">&nbsp;&nbsp;&nbsp;</div>';
252
+
253
+ }
254
+
255
+ echo ' </td>';
256
+ echo '</tr>';
257
+ }
258
+ ?>
259
+ </tbody>
260
+ </table>
core/content/status.php ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ $statuses = array(
3
+ 'safe-high' => array(),
4
+ 'high' => array(),
5
+ 'safe-medium' => array(),
6
+ 'medium' => array(),
7
+ 'safe-low' => array(),
8
+ 'low' => array(),
9
+ );
10
+
11
+ $statuses = apply_filters( 'itsec_add_dashboard_status', $statuses );
12
+
13
+ echo '<div id="itsec_tabbed_dashboard_content">';
14
+ echo '<ul class="itsec-tabs">';
15
+ echo '<li><a href="#itsec_showall">' . __( 'All', 'better-wp-security' ) . '</a></li>';
16
+ echo '<li><a href="#itsec_high">' . __( 'High', 'better-wp-security' ) . '</a></li>';
17
+ echo '<li><a href="#itsec_medium">' . __( 'Medium', 'better-wp-security' ) . '</a></li>';
18
+ echo '<li><a href="#itsec_low">' . __( 'Low', 'better-wp-security' ) . '</a></li>';
19
+ echo '<li><a href="#itsec_completed">' . __( 'Completed', 'better-wp-security' ) . '</a></li>';
20
+ echo '</ul>';
21
+
22
+ // Begin High Priority Tab
23
+ echo '<div id="itsec_high">';
24
+ if ( isset ( $statuses['high'][0] ) ) {
25
+
26
+ printf( '<h2>%s</h2>', __( 'High Priority', 'better-wp-security' ) );
27
+ _e( 'These are items that should be secured immediately.', 'better-wp-security' );
28
+
29
+ echo '<ul class="statuslist high-priority">';
30
+
31
+ if ( isset ( $statuses['high'] ) ) {
32
+
33
+ $this->status_loop( $statuses['high'], __( 'Fix it', 'better-wp-security' ), 'primary' );
34
+
35
+ }
36
+
37
+ echo '</ul>';
38
+
39
+ } else {
40
+ echo '<div class="itsec-priority-items-completed">';
41
+ printf( '<h2>%s</h2>', __( 'High Priority', 'better-wp-security' ) );
42
+ printf( '<p>%s</p>', __( 'You have secured all High Priority items.', 'better-wp-security' ) );
43
+ echo '</div>';
44
+ }
45
+
46
+ echo '</div>';
47
+
48
+ // Begin Medium Priority Tab
49
+ echo '<div id="itsec_medium">';
50
+
51
+ if ( isset ( $statuses['medium'][0] ) ) {
52
+
53
+ printf( '<h2>%s</h2>', __( 'Medium Priority', 'better-wp-security' ) );
54
+ _e( 'These are medium priority items that should be fixed if no conflicts are present, but they are not critical to the overall security of your site.', 'better-wp-security' );
55
+
56
+ echo '<ul class="statuslist medium-priority">';
57
+
58
+ if ( isset ( $statuses['medium'] ) ) {
59
+
60
+ $this->status_loop( $statuses['medium'], __( 'Fix it', 'better-wp-security' ), 'primary' );
61
+
62
+ }
63
+
64
+ echo '</ul>';
65
+
66
+ } else {
67
+ echo '<div class="itsec-priority-items-completed">';
68
+ printf( '<h2>%s</h2>', __( 'Medium Priority', 'better-wp-security' ) );
69
+ printf( '<p>%s</p>', __( 'You have secured all Medium Priority items.', 'better-wp-security' ) );
70
+ echo '</div>';
71
+ }
72
+
73
+ echo '</div>';
74
+
75
+ // Begin Low Priority Tab
76
+ echo '<div id="itsec_low">';
77
+
78
+ if ( isset ( $statuses['low'][0] ) ) {
79
+
80
+ printf( '<h2>%s</h2>', __( 'Low Priority', 'better-wp-security' ) );
81
+ _e( 'These are low priority items that should be secured if, and only if, your plugins or theme do not conflict with their use.', 'better-wp-security' );
82
+
83
+ echo '<ul class="statuslist low-priority">';
84
+
85
+ if ( isset ( $statuses['low'] ) ) {
86
+
87
+ $this->status_loop( $statuses['low'], __( 'Fix it', 'better-wp-security' ), 'primary' );
88
+
89
+ }
90
+
91
+ echo '</ul>';
92
+
93
+ } else {
94
+ echo '<div class="itsec-priority-items-completed">';
95
+ printf( '<h2>%s</h2>', __( 'Low Priority', 'better-wp-security' ) );
96
+ printf( '<p>%s</p>', __( 'You have secured all Low Priority items.', 'better-wp-security' ) );
97
+ echo '</div>';
98
+ }
99
+
100
+ echo '</div>';
101
+
102
+ // Begin Completed Tab
103
+ echo '<div id="itsec_completed">';
104
+
105
+ if ( isset ( $statuses['safe-high'] ) || isset ( $statuses['safe-medium'] ) || isset ( $statuses['safe-low'] ) ) {
106
+
107
+ printf( '<h2>%s</h2>', __( 'Completed', 'better-wp-security' ) );
108
+ _e( 'These are items that you have successfully secured.', 'better-wp-security' );
109
+
110
+ echo '<ul class="statuslist completed">';
111
+
112
+ if ( isset ( $statuses['safe-high'] ) ) {
113
+
114
+ $this->status_loop( $statuses['safe-high'], __( 'Edit', 'better-wp-security' ), 'secondary' );
115
+
116
+ }
117
+
118
+ if ( isset ( $statuses['safe-medium'] ) ) {
119
+
120
+ $this->status_loop( $statuses['safe-medium'], __( 'Edit', 'better-wp-security' ), 'secondary' );
121
+
122
+ }
123
+
124
+ if ( isset ( $statuses['safe-low'] ) ) {
125
+
126
+ $this->status_loop( $statuses['safe-low'], __( 'Edit', 'better-wp-security' ), 'secondary' );
127
+
128
+ }
129
+
130
+ echo '</ul>';
131
+
132
+ }
133
+
134
+ echo '</div>';
135
+
136
+ // Begin Show All Tab
137
+ echo '<div id="itsec_showall">';
138
+
139
+ if ( isset ( $statuses['high'][0] ) ) {
140
+
141
+ printf( '<h2>%s</h2>', __( 'High Priority', 'better-wp-security' ) );
142
+ _e( 'These are items that should be secured immediately.', 'better-wp-security' );
143
+
144
+ echo '<ul class="statuslist high-priority">';
145
+
146
+ if ( isset ( $statuses['high'] ) ) {
147
+
148
+ $this->status_loop( $statuses['high'], __( 'Fix it', 'better-wp-security' ), 'primary' );
149
+
150
+ }
151
+
152
+ echo '</ul>';
153
+
154
+ } else {
155
+ echo '<div class="itsec-priority-items-completed">';
156
+ printf( '<h2>%s</h2>', __( 'High Priority', 'better-wp-security' ) );
157
+ printf( '<p>%s</p>', __( 'You have secured all High Priority items.', 'better-wp-security' ) );
158
+ echo '</div>';
159
+ }
160
+
161
+ if ( isset ( $statuses['medium'][0] ) ) {
162
+
163
+ printf( '<h2>%s</h2>', __( 'Medium Priority', 'better-wp-security' ) );
164
+ _e( 'These are items that should be secured if possible however they are not critical to the overall security of your site.', 'better-wp-security' );
165
+
166
+ echo '<ul class="statuslist medium-priority">';
167
+
168
+ if ( isset ( $statuses['medium'] ) ) {
169
+
170
+ $this->status_loop( $statuses['medium'], __( 'Fix it', 'better-wp-security' ), 'primary' );
171
+
172
+ }
173
+
174
+ echo '</ul>';
175
+
176
+ } else {
177
+ echo '<div class="itsec-priority-items-completed">';
178
+ printf( '<h2>%s</h2>', __( 'Medium Priority', 'better-wp-security' ) );
179
+ printf( '<p>%s</p>', __( 'You have secured all Medium Priority items.', 'better-wp-security' ) );
180
+ echo '</div>';
181
+ }
182
+
183
+ if ( isset ( $statuses['low'][0] ) ) {
184
+
185
+ printf( '<h2>%s</h2>', __( 'Low Priority', 'better-wp-security' ) );
186
+ _e( 'These are items that should be secured if, and only if, your plugins or theme do not conflict with their use.', 'better-wp-security' );
187
+
188
+ echo '<ul class="statuslist low-priority">';
189
+
190
+ if ( isset ( $statuses['low'] ) ) {
191
+
192
+ $this->status_loop( $statuses['low'], __( 'Fix it', 'better-wp-security' ), 'primary' );
193
+
194
+ }
195
+
196
+ echo '</ul>';
197
+
198
+ } else {
199
+ echo '<div class="itsec-priority-items-completed">';
200
+ printf( '<h2>%s</h2>', __( 'Low Priority', 'better-wp-security' ) );
201
+ printf( '<p>%s</p>', __( 'You have secured all Low Priority items.', 'better-wp-security' ) );
202
+ echo '</div>';
203
+ }
204
+
205
+ if ( isset ( $statuses['safe-high'] ) || isset ( $statuses['safe-medium'] ) || isset ( $statuses['safe-low'] ) ) {
206
+
207
+ printf( '<h2>%s</h2>', __( 'Completed', 'better-wp-security' ) );
208
+ _e( 'These are items that you have successfuly secured.', 'better-wp-security' );
209
+
210
+ echo '<ul class="statuslist completed">';
211
+
212
+ if ( isset ( $statuses['safe-high'] ) ) {
213
+
214
+ $this->status_loop( $statuses['safe-high'], __( 'Edit', 'better-wp-security' ), 'secondary' );
215
+
216
+ }
217
+
218
+ if ( isset ( $statuses['safe-medium'] ) ) {
219
+
220
+ $this->status_loop( $statuses['safe-medium'], __( 'Edit', 'better-wp-security' ), 'secondary' );
221
+
222
+ }
223
+
224
+ if ( isset ( $statuses['safe-low'] ) ) {
225
+
226
+ $this->status_loop( $statuses['safe-low'], __( 'Edit', 'better-wp-security' ), 'secondary' );
227
+
228
+ }
229
+
230
+ echo '</ul>';
231
+
232
+ }
233
+
234
+ echo '</div>';
235
+ echo '</div>';
core/content/system.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ global $wpdb, $itsec_globals;
3
+ $config_file = ITSEC_Lib::get_config();
4
+ $htaccess = ITSEC_Lib::get_htaccess();
5
+ ?>
6
+
7
+ <ul class="itsec-support">
8
+ <li>
9
+ <h4><?php _e( 'User Information', 'better-wp-security' ); ?></h4>
10
+ <ul>
11
+ <li><?php _e( 'Public IP Address', 'better-wp-security' ); ?>: <strong><a target="_blank"
12
+ title="<?php _e( 'Get more information on this address', 'better-wp-security' ); ?>"
13
+ href="http://whois.domaintools.com/<?php echo ITSEC_Lib::get_ip(); ?>"><?php echo ITSEC_Lib::get_ip(); ?></a></strong>
14
+ </li>
15
+ <li><?php _e( 'User Agent', 'better-wp-security' ); ?>:
16
+ <strong><?php echo filter_var( $_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_STRING ); ?></strong></li>
17
+ </ul>
18
+ </li>
19
+
20
+ <li>
21
+ <h4><?php _e( 'File System Information', 'better-wp-security' ); ?></h4>
22
+ <ul>
23
+ <li><?php _e( 'Website Root Folder', 'better-wp-security' ); ?>: <strong><?php echo get_site_url(); ?></strong>
24
+ </li>
25
+ <li><?php _e( 'Document Root Path', 'better-wp-security' ); ?>:
26
+ <strong><?php echo filter_var( $_SERVER['DOCUMENT_ROOT'], FILTER_SANITIZE_STRING ); ?></strong></li>
27
+ <?php
28
+ if ( @is_writable( $htaccess ) ) {
29
+
30
+ $copen = '<font color="red">';
31
+ $cclose = '</font>';
32
+ $htaw = __( 'Yes', 'better-wp-security' );
33
+
34
+ } else {
35
+
36
+ $copen = '';
37
+ $cclose = '';
38
+ $htaw = __( 'No.', 'better-wp-security' );
39
+
40
+ }
41
+ ?>
42
+ <li><?php _e( '.htaccess File is Writable', 'better-wp-security' ); ?>:
43
+ <strong><?php echo $copen . $htaw . $cclose; ?></strong></li>
44
+ <?php
45
+ if ( @is_writable( $config_file ) ) {
46
+
47
+ $copen = '<font color="red">';
48
+ $cclose = '</font>';
49
+ $wconf = __( 'Yes', 'better-wp-security' );
50
+
51
+ } else {
52
+
53
+ $copen = '';
54
+ $cclose = '';
55
+ $wconf = __( 'No.', 'better-wp-security' );
56
+
57
+ }
58
+ ?>
59
+ <li><?php _e( 'wp-config.php File is Writable', 'better-wp-security' ); ?>:
60
+ <strong><?php echo $copen . $wconf . $cclose; ?></strong></li>
61
+ </ul>
62
+ </li>
63
+
64
+ <li>
65
+ <h4><?php _e( 'Database Information', 'better-wp-security' ); ?></h4>
66
+ <ul>
67
+ <li><?php _e( 'MySQL Database Version', 'better-wp-security' ); ?>
68
+ : <?php $sqlversion = $wpdb->get_var( "SELECT VERSION() AS version" ); ?>
69
+ <strong><?php echo $sqlversion; ?></strong></li>
70
+ <li><?php _e( 'MySQL Client Version', 'better-wp-security' ); ?>: <strong><?php echo mysql_get_client_info(); ?></strong></li>
71
+ <li><?php _e( 'Database Host', 'better-wp-security' ); ?>: <strong><?php echo DB_HOST; ?></strong></li>
72
+ <li><?php _e( 'Database Name', 'better-wp-security' ); ?>: <strong><?php echo DB_NAME; ?></strong></li>
73
+ <li><?php _e( 'Database User', 'better-wp-security' ); ?>: <strong><?php echo DB_USER; ?></strong></li>
74
+ <?php $mysqlinfo = $wpdb->get_results( "SHOW VARIABLES LIKE 'sql_mode'" );
75
+ if ( is_array( $mysqlinfo ) ) {
76
+ $sql_mode = $mysqlinfo[0]->Value;
77
+ }
78
+ if ( empty( $sql_mode ) ) {
79
+ $sql_mode = __( 'Not Set', 'better-wp-security' );
80
+ } else {
81
+ $sql_mode = __( 'Off', 'better-wp-security' );
82
+ }
83
+ ?>
84
+ <li><?php _e( 'SQL Mode', 'better-wp-security' ); ?>: <strong><?php echo $sql_mode; ?></strong></li>
85
+ </ul>
86
+ </li>
87
+
88
+ <li>
89
+ <h4><?php _e( 'Server Information', 'better-wp-security' ); ?></h4>
90
+ <?php $server_addr = array_key_exists( 'SERVER_ADDR', $_SERVER ) ? $_SERVER['SERVER_ADDR'] : $_SERVER['LOCAL_ADDR']; ?>
91
+ <ul>
92
+ <li><?php _e( 'Server / Website IP Address', 'better-wp-security' ); ?>: <strong><a target="_blank"
93
+ title="<?php _e( 'Get more information on this address', 'better-wp-security' ); ?>"
94
+ href="http://whois.domaintools.com/<?php echo $server_addr; ?>"><?php echo $server_addr; ?></a></strong>
95
+ </li>
96
+ <li><?php _e( 'Server Type', 'better-wp-security' ); ?>:
97
+ <strong><?php echo filter_var( filter_var( $_SERVER['SERVER_SOFTWARE'], FILTER_SANITIZE_STRING ), FILTER_SANITIZE_STRING ); ?></strong>
98
+ </li>
99
+ <li><?php _e( 'Operating System', 'better-wp-security' ); ?>: <strong><?php echo PHP_OS; ?></strong></li>
100
+ <li><?php _e( 'Browser Compression Supported', 'better-wp-security' ); ?>:
101
+ <strong><?php echo filter_var( $_SERVER['HTTP_ACCEPT_ENCODING'], FILTER_SANITIZE_STRING ); ?></strong></li>
102
+ <?php
103
+ // from backupbuddy
104
+
105
+ $disabled_functions = @ini_get( 'disable_functions' );
106
+
107
+ if ( $disabled_functions == '' || $disabled_functions === false ) {
108
+ $disabled_functions = '<i>(' . __( 'none', 'better-wp-security' ) . ')</i>';
109
+ }
110
+
111
+ $disabled_functions = str_replace( ', ', ',', $disabled_functions ); // Normalize spaces or lack of spaces between disabled functions.
112
+ $disabled_functions_array = explode( ',', $disabled_functions );
113
+
114
+ $php_uid = __( 'unavailable', 'better-wp-security' );
115
+ $php_user = __( 'unavailable', 'better-wp-security' );
116
+
117
+ if ( is_callable( 'posix_geteuid' ) && ( false === in_array( 'posix_geteuid', $disabled_functions_array ) ) ) {
118
+
119
+ $php_uid = @posix_geteuid();
120
+
121
+ if ( is_callable( 'posix_getpwuid' ) && ( false === in_array( 'posix_getpwuid', $disabled_functions_array ) ) ) {
122
+
123
+ $php_user = @posix_getpwuid( $php_uid );
124
+ $php_user = $php_user['name'];
125
+
126
+ }
127
+ }
128
+
129
+ $php_gid = __( 'undefined', 'better-wp-security' );
130
+
131
+ if ( is_callable( 'posix_getegid' ) && ( false === in_array( 'posix_getegid', $disabled_functions_array ) ) ) {
132
+ $php_gid = @posix_getegid();
133
+ }
134
+
135
+ ?>
136
+ <li><?php _e( 'PHP Process User (UID:GID)', 'better-wp-security' ); ?>:
137
+ <strong><?php echo $php_user . ' (' . $php_uid . ':' . $php_gid . ')'; ?></strong></li>
138
+ </ul>
139
+ </li>
140
+
141
+ <li>
142
+ <h4><?php _e( 'PHP Information', 'better-wp-security' ); ?></h4>
143
+ <ul>
144
+ <li><?php _e( 'PHP Version', 'better-wp-security' ); ?>: <strong><?php echo PHP_VERSION; ?></strong></li>
145
+ <li><?php _e( 'PHP Memory Usage', 'better-wp-security' ); ?>:
146
+ <strong><?php echo round( memory_get_usage() / 1024 / 1024, 2 ) . __( ' MB', 'better-wp-security' ); ?></strong>
147
+ </li>
148
+ <?php
149
+ if ( ini_get( 'memory_limit' ) ) {
150
+ $memory_limit = filter_var( ini_get( 'memory_limit' ), FILTER_SANITIZE_STRING );
151
+ } else {
152
+ $memory_limit = __( 'N/A', 'better-wp-security' );
153
+ }
154
+ ?>
155
+ <li><?php _e( 'PHP Memory Limit', 'better-wp-security' ); ?>: <strong><?php echo $memory_limit; ?></strong></li>
156
+ <?php
157
+ if ( ini_get( 'upload_max_filesize' ) ) {
158
+ $upload_max = filter_var( ini_get( 'upload_max_filesize' ), FILTER_SANITIZE_STRING );
159
+ } else {
160
+ $upload_max = __( 'N/A', 'better-wp-security' );
161
+ }
162
+ ?>
163
+ <li><?php _e( 'PHP Max Upload Size', 'better-wp-security' ); ?>: <strong><?php echo $upload_max; ?></strong></li>
164
+ <?php
165
+ if ( ini_get( 'post_max_size' ) ) {
166
+ $post_max = filter_var( ini_get( 'post_max_size' ), FILTER_SANITIZE_STRING );
167
+ } else {
168
+ $post_max = __( 'N/A', 'better-wp-security' );
169
+ }
170
+ ?>
171
+ <li><?php _e( 'PHP Max Post Size', 'better-wp-security' ); ?>: <strong><?php echo $post_max; ?></strong></li>
172
+ <?php
173
+ if ( ini_get( 'safe_mode' ) ) {
174
+ $safe_mode = __( 'On', 'better-wp-security' );
175
+ } else {
176
+ $safe_mode = __( 'Off', 'better-wp-security' );
177
+ }
178
+ ?>
179
+ <li><?php _e( 'PHP Safe Mode', 'better-wp-security' ); ?>: <strong><?php echo $safe_mode; ?></strong></li>
180
+ <?php
181
+ if ( ini_get( 'allow_url_fopen' ) ) {
182
+ $allow_url_fopen = __( 'On', 'better-wp-security' );
183
+ } else {
184
+ $allow_url_fopen = __( 'Off', 'better-wp-security' );
185
+ }
186
+ ?>
187
+ <li><?php _e( 'PHP Allow URL fopen', 'better-wp-security' ); ?>: <strong><?php echo $allow_url_fopen; ?></strong>
188
+ </li>
189
+ <?php
190
+ if ( ini_get( 'allow_url_include' ) ) {
191
+ $allow_url_include = __( 'On', 'better-wp-security' );
192
+ } else {
193
+ $allow_url_include = __( 'Off', 'better-wp-security' );
194
+ }
195
+ ?>
196
+ <li><?php _e( 'PHP Allow URL Include' ); ?>: <strong><?php echo $allow_url_include; ?></strong></li>
197
+ <?php
198
+ if ( ini_get( 'display_errors' ) ) {
199
+ $display_errors = __( 'On', 'better-wp-security' );
200
+ } else {
201
+ $display_errors = __( 'Off', 'better-wp-security' );
202
+ }
203
+ ?>
204
+ <li><?php _e( 'PHP Display Errors', 'better-wp-security' ); ?>: <strong><?php echo $display_errors; ?></strong>
205
+ </li>
206
+ <?php
207
+ if ( ini_get( 'display_startup_errors' ) ) {
208
+ $display_startup_errors = __( 'On', 'better-wp-security' );
209
+ } else {
210
+ $display_startup_errors = __( 'Off', 'better-wp-security' );
211
+ }
212
+ ?>
213
+ <li><?php _e( 'PHP Display Startup Errors', 'better-wp-security' ); ?>:
214
+ <strong><?php echo $display_startup_errors; ?></strong></li>
215
+ <?php
216
+ if ( ini_get( 'expose_php' ) ) {
217
+ $expose_php = __( 'On', 'better-wp-security' );
218
+ } else {
219
+ $expose_php = __( 'Off', 'better-wp-security' );
220
+ }
221
+ ?>
222
+ <li><?php _e( 'PHP Expose PHP', 'better-wp-security' ); ?>: <strong><?php echo $expose_php; ?></strong></li>
223
+ <?php
224
+ if ( ini_get( 'register_globals' ) ) {
225
+ $register_globals = __( 'On', 'better-wp-security' );
226
+ } else {
227
+ $register_globals = __( 'Off', 'better-wp-security' );
228
+ }
229
+ ?>
230
+ <li><?php _e( 'PHP Register Globals', 'better-wp-security' ); ?>: <strong><?php echo $register_globals; ?></strong></li>
231
+ <?php
232
+ if ( ini_get( 'max_execution_time' ) ) {
233
+ $max_execute = filter_var( ini_get( 'max_execution_time' ) );
234
+ } else {
235
+ $max_execute = __( 'N/A', 'better-wp-security' );
236
+ }
237
+ ?>
238
+ <li><?php _e( 'PHP Max Script Execution Time' ); ?>:
239
+ <strong><?php echo $max_execute; ?> <?php _e( 'Seconds' ); ?></strong></li>
240
+ <?php
241
+ if ( ini_get( 'magic_quotes_gpc' ) ) {
242
+ $magic_quotes_gpc = __( 'On', 'better-wp-security' );
243
+ } else {
244
+ $magic_quotes_gpc = __( 'Off', 'better-wp-security' );
245
+ }
246
+ ?>
247
+ <li><?php _e( 'PHP Magic Quotes GPC', 'better-wp-security' ); ?>: <strong><?php echo $magic_quotes_gpc; ?></strong></li>
248
+ <?php
249
+ if ( ini_get( 'open_basedir' ) ) {
250
+ $open_basedir = __( 'On', 'better-wp-security' );
251
+ } else {
252
+ $open_basedir = __( 'Off', 'better-wp-security' );
253
+ }
254
+ ?>
255
+ <li><?php _e( 'PHP open_basedir', 'better-wp-security' ); ?>: <strong><?php echo $open_basedir; ?></strong></li>
256
+ <?php
257
+ if ( is_callable( 'xml_parser_create' ) ) {
258
+ $xml = __( 'Yes', 'better-wp-security' );
259
+ } else {
260
+ $xml = __( 'No', 'better-wp-security' );
261
+ }
262
+ ?>
263
+ <li><?php _e( 'PHP XML Support', 'better-wp-security' ); ?>: <strong><?php echo $xml; ?></strong></li>
264
+ <?php
265
+ if ( is_callable( 'iptcparse' ) ) {
266
+ $iptc = __( 'Yes', 'better-wp-security' );
267
+ } else {
268
+ $iptc = __( 'No', 'better-wp-security' );
269
+ }
270
+ ?>
271
+ <li><?php _e( 'PHP IPTC Support', 'better-wp-security' ); ?>: <strong><?php echo $iptc; ?></strong></li>
272
+ <?php
273
+ if ( is_callable( 'exif_read_data' ) ) {
274
+ $exif = __( 'Yes', 'better-wp-security' ) . " ( V" . substr( phpversion( 'exif' ), 0, 4 ) . ")";
275
+ } else {
276
+ $exif = __( 'No', 'better-wp-security' );
277
+ }
278
+ ?>
279
+ <li><?php _e( 'PHP Exif Support', 'better-wp-security' ); ?>: <strong><?php echo $exif; ?></strong></li>
280
+ <?php $disabled_functions = str_replace( ',', ', ', $disabled_functions ); // Normalize spaces or lack of spaces between disabled functions. ?>
281
+ <li><?php _e( 'Disabled PHP Functions', 'better-wp-security' ); ?>: <strong><?php echo $disabled_functions; ?></strong></li>
282
+ </ul>
283
+ </li>
284
+
285
+ <li>
286
+ <h4><?php _e( 'WordPress Configuration', 'better-wp-security' ); ?></h4>
287
+ <ul>
288
+ <?php
289
+ if ( is_multisite() ) {
290
+ $multSite = __( 'Multisite is enabled', 'better-wp-security' );
291
+ } else {
292
+ $multSite = __( 'Multisite is NOT enabled', 'better-wp-security' );
293
+ }
294
+ ?>
295
+ <li><?php _e( ' Multisite', 'better-wp-security' ); ?>: <strong><?php echo $multSite; ?></strong></li>
296
+ <?php
297
+ if ( get_option( 'permalink_structure' ) != '' ) {
298
+ $copen = '';
299
+ $cclose = '';
300
+ $permalink_structure = __( 'Enabled', 'better-wp-security' );
301
+ } else {
302
+ $copen = '<font color="red">';
303
+ $cclose = '</font>';
304
+ $permalink_structure = __( 'WARNING! Permalinks are NOT Enabled. Permalinks MUST be enabled for this plugin to function correctly', 'better-wp-security' );
305
+ }
306
+ ?>
307
+ <li><?php _e( 'WP Permalink Structure', 'better-wp-security' ); ?>:
308
+ <strong> <?php echo $copen . $permalink_structure . $cclose; ?></strong></li>
309
+ <li><?php _e( 'Wp-config Location', 'better-wp-security' ); ?>: <strong><?php echo $config_file ?></strong></li>
310
+ <?php $active_plugins = implode( ',', get_option( 'active_plugins' ) ); ?>
311
+ <li><?php _e( 'Active Plugins', 'better-wp-security' ); ?>: <strong><?php echo $active_plugins ?></strong></li>
312
+ <li><?php _e( 'Content Directory', 'better-wp-security' ); ?>: <strong><?php echo WP_CONTENT_DIR ?></strong></li>
313
+ </ul>
314
+ </li>
315
+ <li>
316
+ <h4><?php echo $itsec_globals['plugin_name'] . __( ' variables', 'better-wp-security' ); ?></h4>
317
+ <ul>
318
+ <li><?php _e( 'Build Version', 'better-wp-security' ); ?>: <strong><?php echo $itsec_globals['plugin_build']; ?></strong><br/>
319
+ <em><?php _e( 'Note: this is NOT the same as the version number on the plugin page or WordPress.org page and is instead used for support.', 'better-wp-security' ); ?></em>
320
+ </li>
321
+ </ul>
322
+ </li>
323
+ </ul>
core/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/css/ithemes.css ADDED
@@ -0,0 +1,633 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #screen-meta-links #itsec-meta-link-wrap a::after {
2
+ content : '';
3
+ margin-right : 5px;
4
+ }
5
+
6
+ #itsec-meta-link-wrap a.show-settings {
7
+ float : right;
8
+ margin : 0 0 0 6px;
9
+ }
10
+
11
+ div.inside ul {
12
+ margin-left : 20px;
13
+ }
14
+
15
+ div.inside ul li {
16
+ line-height : 16px;
17
+ list-style : disc;
18
+ }
19
+
20
+ .form-table th, .form-table td label {
21
+ font-weight : 600;
22
+ }
23
+
24
+ #poststuff h2.settings-section-header {
25
+ padding : 1em 0 0;
26
+ }
27
+
28
+ .current-time-date {
29
+ display : inline-block;
30
+ font-size : 1.25em;
31
+ background : #efefef;
32
+ padding : .75em 1em;
33
+ }
34
+
35
+ .ui-datepicker {
36
+ display : none;
37
+ }
38
+
39
+ a.itsec_return_to_top {
40
+ position : fixed;
41
+ bottom : 10px;
42
+ right : 10px;
43
+ z-index : 200;
44
+ width : 41px;
45
+ height : 41px;
46
+ background : url(../img/return-to-top.png) center center no-repeat;
47
+ display : block;
48
+ text-indent : -9999em;
49
+ opacity : .8;
50
+ }
51
+
52
+ a.itsec_return_to_top:hover {
53
+ opacity : 1;
54
+ }
55
+
56
+ #global_table_of_contents div.inside {
57
+ padding-top : 6px;
58
+ }
59
+
60
+ #global_table_of_contents .hndle,
61
+ #global_table_of_contents .handlediv {
62
+ display : none;
63
+ }
64
+
65
+ #global_table_of_contents.fixed {
66
+ position : fixed;
67
+ right : 0;
68
+ bottom : 45%;
69
+ z-index : 2;
70
+ }
71
+
72
+ div.inside .itsec_toc_label {
73
+ font-weight : 600;
74
+ font-size : 14px;
75
+ }
76
+
77
+ div.inside .itsec_toc {
78
+ margin : 0;
79
+ }
80
+
81
+ div.inside .itsec_toc li {
82
+ list-style : none;
83
+ }
84
+
85
+ #side-sortables.empty-container {
86
+ border : none;
87
+ }
88
+
89
+ .ui-dialog {
90
+ position : fixed !important;
91
+ }
92
+
93
+ /***************************************
94
+ Warnings/Notices
95
+ ****************************************/
96
+ .itsec-warning-message {
97
+ display : block;
98
+ clear : both;
99
+ font-size : 1.125em;
100
+ margin : 1.5em 0;
101
+ padding : 1em;
102
+ border-left : 4px solid #ca8383;
103
+ background : #ffd4d4;
104
+ color : #9c3e3e;
105
+ }
106
+
107
+ .itsec-notice-message {
108
+ display : block;
109
+ clear : both;
110
+ font-size : 1.125em;
111
+ margin : 1.5em 0;
112
+ padding : 1em;
113
+ border-left : 4px solid #e8e4a7;
114
+ background : #fff9ec;
115
+ }
116
+
117
+ .itsec-notice-message span,
118
+ .itsec-warning-message span {
119
+ font-weight : 700;
120
+ }
121
+
122
+ /***************************************
123
+ Support Page List
124
+ ****************************************/
125
+ .inside .itsec-support {
126
+ margin : 0;
127
+ }
128
+
129
+ .inside .itsec-support li {
130
+ list-style : none;
131
+ }
132
+
133
+ .inside .itsec-support li h4 {
134
+ background : #ebebeb;
135
+ padding : 1em;
136
+ margin-bottom : 0;
137
+ }
138
+
139
+ .inside .itsec-support li ul {
140
+ border : 1px solid #ebebeb;
141
+ margin : 0;
142
+ }
143
+
144
+ .inside .itsec-support li li {
145
+ padding : .5em 1em .5em 2em;
146
+ margin : 0;
147
+ }
148
+
149
+ .inside .itsec-support li li:nth-child(2n+1) {
150
+ background : #fafafa;
151
+ }
152
+
153
+ /***************************************
154
+ Dashboard information
155
+ ****************************************/
156
+ #itsec_get_started .inside {
157
+ padding : 0;
158
+ margin : 0;
159
+ }
160
+
161
+ #itsec_get_started .itsec-video {
162
+ position : relative;
163
+ padding-bottom : 48.5%;
164
+ padding-top : 25px;
165
+ height : 0;
166
+ width : 85%;
167
+ margin : 1em auto 0 auto;
168
+ }
169
+
170
+ #itsec_get_started .itsec-video iframe {
171
+ position : absolute;
172
+ top : 0;
173
+ left : 0;
174
+ width : 100%;
175
+ height : 100%;
176
+ }
177
+
178
+ .itsec_getting_started {
179
+ padding : 0 2em;
180
+ overflow : hidden;
181
+ }
182
+
183
+ .itsec_getting_started .column {
184
+ width : 100%;
185
+ max-width : 45%;
186
+ float : left;
187
+ padding-right : 2.5%;
188
+ border-right : 1px solid #ebebeb;
189
+ padding-bottom : 2em;
190
+ }
191
+
192
+ .itsec_getting_started .column.two {
193
+ margin-right : 0;
194
+ padding-right : 0;
195
+ padding-left : 2.5%;
196
+ border : 0;
197
+ max-width : 49%;
198
+ }
199
+
200
+ .itsec_getting_started .itsec-video-link {
201
+ width : 100%;
202
+ max-width : 200px;
203
+ float : left;
204
+ margin : 0 1.5em 1.5em 0;
205
+ }
206
+
207
+ .itsec_getting_started .itsec-video-link img {
208
+ max-width : 100%;
209
+ height : auto;
210
+ }
211
+
212
+ .itsec_getting_started .itsec-video-description {
213
+
214
+ }
215
+
216
+ .itsec_getting_started .itsec_video {
217
+ display : none;
218
+ }
219
+
220
+ .itsec-video-dialog .itsec_video {
221
+ padding : 0;
222
+ margin : 0 0 -5px 0;
223
+ }
224
+
225
+ .itsec-video-dialog .ui-dialog-titlebar {
226
+ line-height : 1;
227
+ font-size : 1em;
228
+ height : 35px;
229
+ background : #222;
230
+ border-bottom : none;
231
+ }
232
+
233
+ .itsec-video-dialog .ui-dialog-titlebar button {
234
+ height : 30px;
235
+ width : 30px;
236
+ padding : 0;
237
+ }
238
+
239
+ #itsec_tabbed_dashboard_content {
240
+ padding : 0 1em;
241
+ }
242
+
243
+ #itsec_tabbed_dashboard_content h2 {
244
+ margin-bottom : 0;
245
+ }
246
+
247
+ #itsec_tabbed_dashboard_content .itsec-tabs {
248
+ overflow : hidden;
249
+ padding : 0;
250
+ margin-top : 2em;
251
+ margin-bottom : 2em;
252
+ }
253
+
254
+ #itsec_tabbed_dashboard_content .itsec-tabs li {
255
+ display : block;
256
+ float : left;
257
+ padding : 0;
258
+ border : 0;
259
+ border-right : 1px solid #ebebeb;
260
+ }
261
+
262
+ #itsec_tabbed_dashboard_content .itsec-tabs li a {
263
+ display : block;
264
+ padding : 1em 2em;
265
+ text-decoration : none;
266
+ background : #fff;
267
+ border-bottom : 3px solid transparent;
268
+ }
269
+
270
+ #itsec_tabbed_dashboard_content .itsec-tabs li.ui-state-active a,
271
+ #itsec_tabbed_dashboard_content .itsec-tabs li.ui-state-hover a {
272
+ background : #ebebeb;
273
+ border-bottom : 3px solid #025680;
274
+ }
275
+
276
+ #itsec_tabbed_dashboard_content .statuslist {
277
+ margin-bottom : 4em;
278
+ }
279
+
280
+ #itsec_tabbed_dashboard_content .statuslist li {
281
+ padding : .75em 1em;
282
+ }
283
+
284
+ #itsec_tabbed_dashboard_content .statuslist.high-priority {
285
+ border : 1px solid #ffd4d4;
286
+ }
287
+
288
+ #itsec_tabbed_dashboard_content .statuslist.high-priority li {
289
+ background : #ffeaea url(../img/flag16-red.png) 12px center no-repeat;
290
+ padding-left : 40px;
291
+ border-bottom : 1px solid #ffd4d4;
292
+ }
293
+
294
+ #itsec_tabbed_dashboard_content .statuslist.medium-priority {
295
+ border : 1px solid #dadaa4;
296
+ }
297
+
298
+ #itsec_tabbed_dashboard_content .statuslist.medium-priority li {
299
+ background : #ffffcb url(../img/flag16-yellow.png) 12px center no-repeat;
300
+ padding-left : 40px;
301
+ border-left : 4px solid #dadaa4;
302
+ border-bottom : 1px solid #dadaa4;
303
+ }
304
+
305
+ #itsec_tabbed_dashboard_content .statuslist.low-priority {
306
+ border : 1px solid #a6cbdb;
307
+ }
308
+
309
+ #itsec_tabbed_dashboard_content .statuslist.low-priority li {
310
+ background : #daf4ff url(../img/flag16-blue.png) 12px center no-repeat;
311
+ padding-left : 40px;
312
+ border-left : 4px solid #a6cbdb;
313
+ border-bottom : 1px solid #a6cbdb;
314
+ }
315
+
316
+ #itsec_tabbed_dashboard_content .statuslist.completed li {
317
+ background : #fafafa url(../img/check16.png) 12px center no-repeat;
318
+ padding-left : 40px;
319
+ }
320
+
321
+ #itsec_tabbed_dashboard_content .statuslist.high-priority li:last-child,
322
+ #itsec_tabbed_dashboard_content .statuslist.medium-priority li:last-child,
323
+ #itsec_tabbed_dashboard_content .statuslist.low-priority li:last-child {
324
+ border-bottom : 0;
325
+ }
326
+
327
+ div.itsec_rewrite_rules {
328
+ height : 500px;
329
+ overflow : auto;
330
+ width : 100%;
331
+ line-height : 1em;
332
+ }
333
+
334
+ div.itsec_rewrite_rules code {
335
+ overflow-x : auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */
336
+ overflow-y : hidden;
337
+ background-color : transparent;
338
+ white-space : pre-wrap; /* css-3 */
339
+ white-space : -moz-pre-wrap !important; /* Mozilla, since 1999 */
340
+ white-space : -pre-wrap; /* Opera 4-6 */
341
+ white-space : -o-pre-wrap; /* Opera 7 */
342
+ /* width: 99%; */
343
+ word-wrap : break-word; /* Internet Explorer 5.5+ */
344
+ }
345
+
346
+ /***************************************
347
+ Dashboard Security Status Lists
348
+ ****************************************/
349
+ #itsec_status ul {
350
+ list-style-position : inside;
351
+ margin : 1em 0;
352
+ border : 1px solid #ebebeb;
353
+ padding : 0;
354
+ }
355
+
356
+ #itsec_status ul li {
357
+ padding : .5em 1em;
358
+ margin : 0;
359
+ border-bottom : 1px solid #ebebeb;
360
+ overflow : hidden;
361
+ }
362
+
363
+ #itsec_status ul li:last-child {
364
+ border-bottom : none;
365
+ }
366
+
367
+ #itsec_status ul.completed li {
368
+ border-left : 5px solid #c9c9c9;
369
+ }
370
+
371
+ #itsec_status ul.high-priority li {
372
+ border-left : 5px solid #ffd4d4;
373
+ }
374
+
375
+ #itsec_status ul.medium-priority li {
376
+ border-left : 5px solid #d9f4ff;
377
+ }
378
+
379
+ #itsec_status ul.low-priority li {
380
+ border-left : 5px solid #d9f4ff;
381
+ }
382
+
383
+ #itsec_status ul li p {
384
+ float : left;
385
+ width : 75%;
386
+ margin : 0;
387
+ padding : .3em 0;
388
+ }
389
+
390
+ #itsec_status ul li .itsec_status_action {
391
+ float : right;
392
+ width : 20%;
393
+ margin-left : 5%;
394
+ text-align : right;
395
+ }
396
+
397
+ #normal-sortables #itsec_release_lockout_form p.submit {
398
+ float : none;
399
+ padding-left : 0;
400
+ margin : 1.5em 0 0;
401
+ padding-top : 1.5em;
402
+ }
403
+
404
+ /***************************************
405
+ iThemes Security Dialogs
406
+ ****************************************/
407
+
408
+ /* Logs Modal */
409
+ .itsec-dialog-logs .ui-dialog-content {
410
+ padding : 1em 2em 2em 2em;
411
+ }
412
+
413
+ .itsec-dialog-logs .ui-dialog-titlebar span {
414
+ padding : .25em 1em;
415
+ text-align : left;
416
+ }
417
+
418
+ /* All Logs Modal */
419
+ .itsec-all-log-dialog ul li h3 {
420
+ border-bottom : 1px solid #ebebeb;
421
+ text-transform : capitalize;
422
+ }
423
+
424
+ .itsec-all-log-dialog ul ul li h3 {
425
+ border : none;
426
+ text-transform : none;
427
+ font-size : 1.2em;
428
+ margin : 0;
429
+ }
430
+
431
+ .itsec-all-log-dialog ul ul li {
432
+ padding : .5em 1em;
433
+ }
434
+
435
+ .itsec-all-log-dialog ul ul ul li,
436
+ .itsec-all-log-dialog ul ul ul ul li {
437
+ padding : 0;
438
+ }
439
+
440
+ .itsec-all-log-dialog ul ul li:nth-child(2n) {
441
+ background : #ebebeb;
442
+ }
443
+
444
+ .itsec-all-log-dialog ul ul ul li:nth-child(2n),
445
+ .itsec-all-log-dialog ul ul ul ul li:nth-child(2n) {
446
+ background : transparent;
447
+ }
448
+
449
+ /* Initial setup Modal */
450
+ .itsec-setup-dialog .ui-dialog-titlebar {
451
+ background : #2ea2cc;
452
+ color : #fff;
453
+ font-size : 1.5em;
454
+ font-weight : normal;
455
+ height : auto;
456
+ line-height : 2.5;
457
+ padding : 0 1em;
458
+ }
459
+
460
+ .itsec-setup-dialog .ui-dialog-titlebar span {
461
+ text-align : left;
462
+ }
463
+
464
+ .itsec-setup-dialog .ui-dialog-titlebar button {
465
+ width : 30px;
466
+ height : 30px;
467
+ color : #5bbdd5;
468
+ top : 8px;
469
+ right : 15px;
470
+ margin : 0;
471
+ padding : 0;
472
+ }
473
+
474
+ .itsec-setup-dialog .ui-dialog-titlebar button:before {
475
+ color : #5bbdd5;
476
+ }
477
+
478
+ .itsec-setup-dialog .ui-dialog-titlebar button:hover:before {
479
+ color : #fff;
480
+ }
481
+
482
+ .itsec-setup-dialog .ui-icon,
483
+ .itsec-setup-dialog .ui-icon:hover {
484
+ background : none !important;
485
+ }
486
+
487
+ #itsec_intro_modal {
488
+ padding : 0;
489
+ margin : 0;
490
+ }
491
+
492
+ #itsec_intro_modal li {
493
+ border-bottom : 1px solid #ebebeb;
494
+ padding : 1em;
495
+ overflow : hidden;
496
+ list-style : none;
497
+ list-style-position : inside;
498
+ }
499
+
500
+ #itsec_intro_modal li p {
501
+ margin : 0;
502
+ }
503
+
504
+ #itsec_intro_modal li h4 {
505
+ margin : 0;
506
+ }
507
+
508
+ #itsec_intro_modal .itsec-intro-close {
509
+ position : relative;
510
+ display : block;
511
+ width : 98%;
512
+ line-height : 3;
513
+ text-align : right;
514
+ color : #2ea2cc;
515
+ }
516
+
517
+ #itsec_intro_modal .itsec_tooltip_success {
518
+ display : block;
519
+ padding : .45em 1em;
520
+ margin-top : .25em;
521
+ border : 1px solid #c9c9c9;
522
+ background : #fafafa url(../img/check16.png) 12px center no-repeat;
523
+ padding-left : 40px;
524
+ }
525
+
526
+ #itsec_intro_modal .itsec_tooltip_failure {
527
+ display : block;
528
+ padding : .45em 1em;
529
+ margin-top : .25em;
530
+ border : 1px solid #dadaa4;
531
+ background : #ffffcb url(../img/flag16-yellow.png) 12px center no-repeat;
532
+ padding-left : 40px;
533
+ }
534
+
535
+ /* Malware & File Change status message */
536
+ #itsec_malware_scan_status,
537
+ #itsec_file_change_status {
538
+ display : none;
539
+ }
540
+
541
+ #itsec_malware_scan_status p,
542
+ #itsec_file_change_status p {
543
+ padding : 5px 10px 5px 30px;
544
+ border : 1px solid #a8d657;
545
+ background : #e2edce url(../img/green-check16.png) 10px center no-repeat;
546
+ color : #435523;
547
+ }
548
+
549
+ /***************************************
550
+ Media Queries
551
+ ****************************************/
552
+
553
+ @media only screen and (max-width : 1280px) {
554
+ #itsec_get_started .itsec-video {
555
+ width : 100%;
556
+ padding-bottom : 56.25%;
557
+ }
558
+
559
+ }
560
+
561
+ @media only screen and (max-width : 850px) {
562
+ #post-body.columns-2 #postbox-container-1 {
563
+ margin-left : 0;
564
+ max-width : 100%;
565
+ }
566
+
567
+ #global_table_of_contents.fixed {
568
+ bottom : 0;
569
+ margin : 0;
570
+ width : 100%;
571
+ }
572
+
573
+ }
574
+
575
+ @media only screen and (max-width : 600px) {
576
+ .itsec_getting_started {
577
+ padding : 0;
578
+ }
579
+
580
+ .itsec_getting_started .button-primary {
581
+ margin-bottom : 1em;
582
+ }
583
+
584
+ .itsec_getting_started .column {
585
+ max-width : 100%;
586
+ float : none;
587
+ padding-right : 2em;
588
+ padding-left : 2em;
589
+ padding-bottom : 2em;
590
+ padding-left : 2em;
591
+ border-bottom : 1px solid #ebebeb;
592
+ border-right : 0;
593
+ box-sizing : border-box;
594
+ overflow : hidden;
595
+ }
596
+
597
+ .itsec_getting_started .column.two {
598
+ padding-right : 2em;
599
+ padding-left : 2em;
600
+ border : 0;
601
+ max-width : 100%;
602
+ }
603
+
604
+ h2.nav-tab-wrapper {
605
+ border-bottom : 0;
606
+ padding : 10px 0 3px 0;
607
+ }
608
+
609
+ h2.nav-tab-wrapper .nav-tab.nav-tab-active,
610
+ h2.nav-tab-wrapper .nav-tab {
611
+ border-bottom : 1px solid #ccc;
612
+ margin : 2px;
613
+ }
614
+ }
615
+
616
+ @media only screen and (max-width : 400px) {
617
+ #itsec_tabbed_dashboard_content .itsec-tabs li {
618
+ float : none;
619
+ width : 100%;
620
+ }
621
+
622
+ #itsec_tabbed_dashboard_content .itsec-tabs li a {
623
+ border-left : 3px solid transparent;
624
+ border-bottom : 0;
625
+ }
626
+
627
+ #itsec_tabbed_dashboard_content .itsec-tabs li.ui-state-active a,
628
+ #itsec_tabbed_dashboard_content .itsec-tabs li.ui-state-hover a {
629
+ border-left : 3px solid #025680;
630
+ border-bottom : 0;
631
+ }
632
+ }
633
+
core/css/itsec_notice.css ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #itsec_setup_notice,
2
+ #itsec_support_notice,
3
+ #itsec_upgrade_notice,
4
+ #itsec_api_notice {
5
+ margin : 1em 0;
6
+ position : relative;
7
+ font-size : 14px;
8
+ line-height : 1.6;
9
+ -webkit-border-radius : 3px;
10
+ -moz-border-radius : 3px;
11
+ border-radius : 3px;
12
+ color : #3e85b5;
13
+
14
+ border : 1px solid #9fd0f1;
15
+ border-bottom-width : 3px;
16
+ background : #e3eef6;
17
+ -webkit-box-shadow : none;
18
+ -moz-box-shadow : none;
19
+ box-shadow : none;
20
+ }
21
+
22
+ #itsec_support_notice span {
23
+ display : block;
24
+ margin-bottom : 1.5em;
25
+ padding-right : 3em;
26
+ }
27
+
28
+ #itsec_setup_notice span.it-icon-itsec,
29
+ #itsec_support_notice span.it-icon-itsec,
30
+ #itsec_api_notice span.it-icon-itsec {
31
+ font-size : 2em;
32
+ line-height : .1;
33
+ vertical-align : middle;
34
+ margin : 0;
35
+ margin-right : .5em;
36
+ padding : 0;
37
+ display : inline;
38
+ }
39
+
40
+ #itsec_support_notice {
41
+ padding-bottom : 2em;
42
+ }
43
+
44
+ #itsec_setup_notice .itsec-notice-button,
45
+ #itsec_support_notice .itsec-notice-button,
46
+ #itsec_api_notice .itsec-notice-button {
47
+ background : #9fd0f1;
48
+ border : 1px solid #79afd3;
49
+ border-bottom-width : 2px;
50
+ border-radius : 3px;
51
+ display : inline-block;
52
+ margin : 10px 0 10px 10px;
53
+ padding : 10px 18px;
54
+ -webkit-transition : all .1s linear;
55
+ -moz-transition : all .1s linear;
56
+ transition : all .1s linear;
57
+
58
+ color : #0071bc;
59
+ font-weight : bold;
60
+ text-decoration : none;
61
+ }
62
+
63
+ .itsec_notice_text {
64
+ display : block;
65
+ margin : 10px 0 10px 0;
66
+ }
67
+
68
+ #itsec_support_notice .itsec-notice-button {
69
+ margin-left : 0;
70
+ margin-right : 10px;
71
+ }
72
+
73
+ #itsec_setup_notice .itsec-notice-button:hover,
74
+ #itsec_support_notice .itsec-notice-button:hover,
75
+ #itsec_api_notice .itsec-notice-button:hover {
76
+ background : #eff7fd;
77
+ cursor : pointer;
78
+ }
79
+
80
+ #itsec_setup_notice .itsec-notice-hide,
81
+ #itsec_support_notice .itsec-notice-hide,
82
+ #itsec_upgrade_notice .itsec-notice-hide,
83
+ #itsec_api_notice .itsec-notice-hide {
84
+ border : 1px solid transparent;
85
+ border-radius : 3px;
86
+ display : inline-block;
87
+ float : right;
88
+ margin : 8px 0;
89
+ padding : 7px 14px;
90
+ -webkit-transition : all .1s linear;
91
+ -moz-transition : all .1s linear;
92
+ transition : all .1s linear;
93
+ background : none;
94
+ font-weight : bold;
95
+ text-decoration : none;
96
+ }
97
+
98
+ #itsec_support_notice .itsec-notice-hide {
99
+ float : none;
100
+ position : absolute;
101
+ top : 14px;
102
+ right : 8px;
103
+ }
104
+
105
+ #itsec_setup_notice .itsec-notice-hide:hover,
106
+ #itsec_support_notice .itsec-notice-hide:hover,
107
+ #itsec_upgrade_notice .itsec-notice-hide:hover,
108
+ #itsec_api_notice .itsec-notice-hide:hover {
109
+ background : #9fd0f1;
110
+ border : 1px solid #79afd3;
111
+ border-bottom-width : 2px;
112
+ border-radius : 3px;
113
+ cursor : pointer;
114
+ }
115
+
116
+ #itsec_setup_notice a.itsec-notice-button,
117
+ #itsec_api_notice a.itsec-notice-button {
118
+ line-height : 15px;
119
+ font-size : 14px;
120
+ }
core/history.txt ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 1.0.0 - 2014-03-25 - Chris Wiegman
2
+ Initial Release
3
+ 1.0.1 - 2014-03-25 - Chris Wiegman
4
+ Better conversion of ip to cidr
5
+ 1.0.2 - 2014-03-27 - Chris Wiegman
6
+ Don't show security menu on multisite for non network admins
7
+ Fix for module path of windows servers
8
+ Module path working correctly on Windows servers
9
+ 404 white list should transfer to global white list
10
+ White list implementation working across all lockouts
11
+ Add extra dismiss box to close welcome modal (fix for smaller screens)
12
+ 1.0.3 - 2014-04-01 - Chris Wiegman
13
+ Fixed history.txt (for iThemes customers)
14
+ Moved upgrade to separate function for more seamless update
15
+ Upgrade system rewritten for better functionality
16
+ Make sure 404 doesn't fail if there is not a 404.php in the theme
17
+ Make sure WordPress root URLs render correctly
18
+ Filewrite now only builds rules on demand.
19
+ Fixed dismiss button on intro modal for small screens
20
+ General cleanup and typo fixing
21
+ 1.0.4 - 2014-04-02 - Chris Wiegman
22
+ Added ability to manually purge log table
23
+ 1.0.5 - 2014-04-03 - Chris Wiegman
24
+ Added "Show intro" button next to screen options to bring the intro modal back
25
+ Added ability to use HTML in error messages
26
+ Minor copy and other tweaks
27
+ 1.0.6 - 2014-05-03 - Chris Wiegman
28
+ Execute permanent ban on the correct lockout count, not the next one
29
+ Updated quick ban rules to match standard ban rules (will work with proxy)
30
+ 1.0.7 - 2014-05-03 - Chris Wiegman
31
+ Update plugin build
32
+ 1.0.8 - 2014-04-08 - Chris Wiegman
33
+ Make sure global settings save button matches others
34
+ Fixed link in locout email
35
+ Email address settings retain end of line
36
+ Sanitize email addresses on save and not just use
37
+ Make sure whitelist is actually an array before trying to process
38
+ Make sure rewrite rules show on dashboard when file writing isnt allowed
39
+ Added extra information to dashboard server information to help troubleshooting
40
+ 1.0.9 - 2014-04-10 - Chris Wiegman
41
+ Minor typo fixes
42
+ Update nginx rewrite rule on comment spam when domain mapping is active
43
+ Added the ability to disable file locking (old behavior)
44
+ Better file lock release (try more than 1 method) before failing
45
+ Don't automatically show file lock error on first attempt
46
+ 1.0.10 - 2014-04-14 - Chris Wiegman
47
+ When activating SSL Log out the user to prevent cookie conflicts
48
+ Use LOCK_EX as a second file locking method on wp-config.php and .htaccess
49
+ Minor code cleanup
50
+ Make sure all wp_enqueue_script dependencies are in proper format
51
+ 1.0.11 - 2013-04-17 - Chris Wiegman
52
+ Make sure logs directory is present before trying to use it
53
+ Log a message when witelisted host triggers a lockout
54
+ Don't create log files if they're not going to be used
55
+ Miscellaneous typos and orther bugfixes
56
+ Add pro tab if pro modules need it
57
+ Upgrade module loader to only load what is needed
58
+ 1.0.12 - 2014-04-18 - Chris Wiegman
59
+ Make sure uploads directory is only working in blog 1 in multisite
60
+ Better checks for run method in module loader
61
+ 1.1.0 - 2014-04-21 - Chris Wiegman
62
+ Make sure "remove write permissions" works
63
+ Better descriptions on white list
64
+ Add pro table of contents if needed
65
+ Make sure security admin bar item works
66
+ Make sure lockout message only happens when needed
67
+ Suppress errors on readlink calls
68
+ Make sure class is present for permanent ban
69
+ Make sure white list is an array
70
+ Fix white listed IPs not working
71
+ 1.1.1 - 2014-04-24 - Chris Wiegman
72
+ Miscellaneous typos and other fixes
73
+ Remove extra file lock on saving .htaccess, nginx.conf and wp-config.php. Only flock will be used in these operations
74
+ 1.2.0 - 2014-05-07 - Chris Wiegman
75
+ Better cache clearing and formatting updates
76
+ Make sure rewrite rules are updated on this update
77
+ Remove extra (settings) items from admin bar menu (leave logs and important information)
78
+ Add WP_CONTENT_DIR to system information on dashboard
79
+ Move support nag to free version only and make sure it properly redirects
80
+ Fix check for presence of BackupBuddy to work with BackupBuddy >=4.2.16.0
81
+ Clean up details views on log pages
82
+ Add username column to temp and lockouts tables
83
+ Lockout usernames whether they exist or not
84
+ Don't duplicate lockouts
85
+ Fixed malformed lockout error on lockout message
86
+ Don't display a host lockout when none exists
87
+ Add Sync integration to release lockouts
88
+ Improved reliability of brute force user lockouts
89
+ 1.2.1 - 2014-05-19 - Chris Wiegman
90
+ Fixed links in lockout emails
91
+ Fixed IP mask calculations
92
+ Add call to pro user-logging module
93
+ Add ability to temporarily whitelist an IP address
94
+ 1.3.0 - 2014-05-28 - Chris Wiegman
95
+ Added call to two-factor module
96
+ 1.4.0 - 2014-06-11 - Chris Wiegman
97
+ Added call to settings import/export module (pro)
98
+ Added button to restore default log location
99
+ Don't automatically load front-end classes in dashboard pages
100
+ Avoid errors on save if htaccess is completely empty
101
+ Only register activation/deactivation/install hooks in admin
102
+ Make sure temporary white-list is always available
103
+ Improved check for white-listed IP during lockout
104
+ Added ability to use constant to override server detection
105
+ Don't remove extra line spaces in .htaccess
106
+ Minor reformating and typo fixes
107
+ 1.4.1 - 2014-06-12 - Chris Wiegman
108
+ Fixed get_module_path to prevent 404 errors on plugin assets
109
+ Fixed misplaced parenthesis forcing computer to always display it isn't whitelisted
110
+ 1.4.2 - 2014-07-02 - Chris Wiegman
111
+ Fixed an issue that was preventing an IP from being permanently banned due to too many lockouts
112
+ Updated .htaccess rules for an IP that has been banned from too many lockouts to be more effective in more hosting environments
113
+ Fixed responsive issues in iThemes notifications that prevented notifications from being easily read on small screens.
114
+ 1.5.0 - 2014-07-28 - Chris Wiegman
115
+ Added malware and malware scheduling modules
116
+ Added better URL validation to ITSEC_LIB
117
+ Added exception for 127.0.0.1 to prevent a local server from being locked out of a site during wp-cron or other calls
118
+ Added button to quickly add current IP address to permanent whitelist
119
+ Added appropriate message for logs page when logs are not available due to "file only" logging being selected
120
+ 1.5.1 - 2014-07-29 - Chris Wiegman
121
+ Make sure pro core module loads to remove upsell when pro has already been purchased.
122
+ 1.5.2 - 2014-07-30 - Chris Wiegman
123
+ Clean up notifications for file change detection and malware scanning
124
+ 1.5.3 - 2014-08-11 - Chris Wiegman
125
+ Ensure that individual module updates fire when updating the plugin
126
+ Added function to retrieve current URL from the front-end
127
+ 1.5.4 - 2014-08-20 - Chris Wiegman
128
+ Low Severity Security Fix - Lack of access control patched - Sucuri (reported 19Aug2014)
129
+ 1.6.0 - 2014-09-09 - Chris Wiegman
130
+ New Feature: Add IPCheck Brute Force API integration
131
+ New Feature: Add ability to receive a daily digest email instead of individual emails per event.
132
+ Enhancement: Added "Go Pro" menu item to admin menus.
133
+ Enhancement: Added button to release IP address from temporary whitelist.
134
+ Fixed: introduction screen should now display completely on computers with low-resolution screens.
135
+ Fixed: multisite bug that still showed BackupBuddy (if present) even though BackupBuddy is not multisite compatible.
136
+ Fixed: Scrolling table of contents should not cover side-bar items on pro.
137
+ Fixed: When changing admin user login form will no show the correct path when WordPress is not installed in the same directory as the website address.
138
+ Fixed: File locking will try to create the iThemes Directory if it isn't already present rather than just saying a lock could not be attained.
139
+ 1.6.1 - 2014-09-09 - Chris Wiegman
140
+ Fixed: Fixed typos in digest email.
141
+ Fixed: Fixed typos in default network lockout message.
142
+ Fixed: Force stylesheet reload for new nags and other items by pushing plugin build number to stylesheet registrations
143
+ 1.7.0 - 2014-09-15 - Chris Wiegman
144
+ New Feature: Automatically generate strong passwords
145
+ New Feature: Password expiration
146
+ Fixed: When an invalid log directory is detected it will not fail but will instead reset it to the original.
147
+ Fixed: No more duplicate digest emails
148
+ Fixed: No more "Array" message appearing in digest emails from user lockouts
149
+ Fixed: HTML in traditional file log emails will display correctly.
150
+ Fixed: From address in notification emails will now display correctly.
151
+ Fixed: MySQL errors will no longer appear for missing iThemes Security tables. Instead it will attempt to recreate them.
152
+ 1.7.1 - 2014-09-16 - Chris Wiegman
153
+ Fixed: Version bump to break cache.
154
+ 1.7.2 - 2014-09-17 - Chris Wiegman
155
+ Enhancement: Default log rotation changed from 30 days to 14 days
156
+ Fixed: All logs page will properly display even with 50,000+ entries in the log
157
+ 1.7.3 - 2014-10-09 - Chris Wiegman
158
+ Fixed: fixed duplicate ID issue from user_id_exists calls.
159
+ Fixed: Fixed an error in the lockout module that results in an error for users of multisite
160
+ Fixed: Notification emails will no longer send if not turned on
161
+ Fixed: Duplicate messages will not be allowed in digest emails
162
+ Fixed: Duplicate digest emails will have a far lesser chance of sending
163
+ Fixed: User lockout count in email notifications will now be correct
164
+ 1.7.4 - 2014-10-09 - Chris Wiegman
165
+ Fixed: Error on line 1312 when iThemes API is actived with version 4.4.15
166
+ 1.8.0 - 2014-10-13 - Chris Wiegman
167
+ New Pro Feature: Dashboard widget. Get important information and handle user blocking right from the WordPress Dashboard.
168
+ 1.9.0 - 2014-10-21 - Chris Wiegman
169
+ New Pro Feature: File change scanning will now compare WordPress core files to the WordPress.org repository.
170
+ Fixed: Make sure php_gid is always defined to prevent error message if the function is not usable.
171
+ Fixed: Link to BackupBuddy in admin bar will now work correctly.
172
+ 1.10.0 - 2014-11-04 - Chris Wiegman
173
+ New Pro Feature: Temporary privilege escalation
174
+ 1.10.1 - 2014-11-05 - Chris Wiegman
175
+ Security Fix: Fixed possible XSS vulnerability in ITSEC_Lib. - Low priority - Thanks to http://planetzuda.com
176
+ 1.11.0 - 2014-12-04 - Chris Wiegman
177
+ New Pro Feature: wp-cli integration
178
+ New Feature: Temporarily whitelist your IP address via iThemes Sync
179
+ New Feature: Override proxy IP detection
180
+ New feature: Hide admin bar (if desired)
181
+ Enhancement: Added filter to allow for custom log pages
182
+ Enhancement: Added debug constant to help troubleshoot multiple emails
183
+ Enhancement: Added constant to force digest emails via wp-cron instead of custom timing
184
+ Fixed: Various missing variable fixes were added
185
+ Fixed: MySQL errors on MySQL 5.6 during activation were fixed.
186
+ Fixed: HTML emails now contain HTML tag
187
+ Fixed: Lockout count in emails should now be more accurate
188
+ 1.12.0 - 2014-12-16 - Chris Wiegman
189
+ New Pro Feature: Google reCAPTCHA
190
+ 1.12.1 - 2015-01-05 - Chris Wiegman
191
+ New Feature: Add file/folder permissions check to Dashboard
192
+ Fix/Enhancement: Minor refactoring of various core components
193
+ 1.12.2 - 2015-01-12 - Chris Wiegman
194
+ Fix: Fixed duplicate module listsing on log page dropdown
195
+ Fix: Fixed missing lockouts on iThemes Sync dashboard
196
+ 1.13.0 - 2015-01-21 - Chris Wiegman
197
+ New Feature: Change WordPress Salts
198
+ Enhancement: Refactored ITSEC_Lib and ITSEC_Files for better usability and new functions to make changing salts possible
199
+ 1.13.1 - 2015-01-27 - Chris Jean
200
+ Bug Fix: Generating wp-config.php file updates no longer produces warnings.
201
+ 1.13.2 - 2015-01-27 - Chris Jean
202
+ Bug Fix: Fixed .htaccess file modifications failing.
203
+ 1.13.3 - 2015-02-05 - Chris Wiegman
204
+ Fix: Quick banning IPs will now work correctly if existing htaccess rules are in place
205
+ Fix: minor bug fixes and typo corrections.
206
+ 1.13.4 - 2015-02-20 - Chris Wiegman
207
+ Enhancement: Limit the number of lockouts that can be displayed at any given time in the dashboard.
208
+ Fix: Make sure header error messages are suppressed when performing a lockout.
209
+ Fix: Fix error message from missing login information when displaying lockouts.
210
+ 1.13.5 - 2015-02-26 - Chris Jean
211
+ Bug Fix: Fixed regression that prevented adding wildcard IP's in the form of 'XXX.XXX.XXX.*' to Ban Hosts.
212
+ 1.13.6 - 2015-03-20 - Chris Jean
213
+ Enhancement: Translation files can now be stored in WP_LANG_DIR/plugins/ithemes-security-pro for
214
+ iThemes Security Pro and WP_LANG_DIR/plugins/better-wp-security for iThemes Security free version.
215
+ Bug Fix: The file permissions check will no longer list a warning if the plugins directory has permissions of 755.
216
+ 1.13.7 - 2015-04-22 - Chris Jean
217
+ Enhancement: Improved domain name generation given the host name.
218
+ 1.14.0 - 2015-06-04 - Chris Jean
219
+ Enhancement: Added new library classes for managing files, directories, and config files.
220
+ 1.14.1 - 2015-06-08 - Chris Jean
221
+ Bug Fix: Fixed "Fatal error: Call to undefined method ITSEC_Lib_File::get_full_file_permissions()" which could occur when saving settings.
222
+ 1.14.2 - 2015-06-09 - Chris Jean
223
+ Bug Fix: Warnings when file writes fail are now hidden.
224
+ Bug Fix: Fixed a situation where creation of a zipped export file would
225
+ >fail, but an email would still be sent as if the zip was created
226
+ >successfully.
227
+ Enhancement: Improved error messages for when file writes fail.
228
+ Enhancement: Improved error messages for when export file creation fails.
229
+ Enhancement: Improved error messages for situations when the .htaccess,
230
+ >nginx.conf, or wp-config.php files may need to be manually updated.
231
+ 1.14.2 - 2015-06-09 - Chris Jean
232
+ Bug Fix: Warnings when file writes fail are now hidden.
233
+ Bug Fix: Fixed a situation where creation of a zipped export file would
234
+ >fail, but an email would still be sent as if the zip was created
235
+ >successfully.
236
+ Enhancement: Improved error messages for when file writes fail.
237
+ Enhancement: Improved error messages for when export file creation fails.
238
+ Enhancement: Improved error messages for situations when the .htaccess,
239
+ >nginx.conf, or wp-config.php files may need to be manually updated.
240
+ 1.14.2 - 2015-06-09 - Chris Jean
241
+ Bug Fix: Warnings when file writes fail are now hidden.
242
+ Bug Fix: Fixed a situation where creation of a zipped export file would
243
+ >fail, but an email would still be sent as if the zip was created
244
+ >successfully.
245
+ Enhancement: Improved error messages for when file writes fail.
246
+ Enhancement: Improved error messages for when export file creation fails.
247
+ Enhancement: Improved error messages for situations when the .htaccess,
248
+ >nginx.conf, or wp-config.php files may need to be manually updated.
249
+ 1.14.2 - 2015-06-09 - Chris Jean
250
+ Bug Fix: Warnings when file writes fail are now hidden.
251
+ Enhancement: Improved error messages for when file writes fail.
252
+ Enhancement: Improved error messages for situations when the .htaccess, nginx.conf, or wp-config.php files may need to be manually updated.
253
+ 1.14.3 - 2015-06-16 - Chris Jean
254
+ Bug Fix: Fixed support for wp-config.php files placed one directory above the ABSPATH.
255
+ 1.14.4 - 2015-06-18 - Chris Jean
256
+ Bug Fix: Manual backups now work as expected after changing the content directory.
257
+ Bug Fix: Readded support for Litespeed .htaccess file modifications.
258
+ 1.15.0 - 2015-07-02 - Chris Jean
259
+ Feature Removal: Removed the malware scanning feature as VirusTotal no longer supports scanning from WordPress sites. A replacement is in the works.
260
+ Bug Fix: The close button on the "Thank you for activating iThemes Security" message now appears in the correct location.
261
+ Bug Fix: Removed the site's URL being displayed in the "Replace jQuery With a Safe Version" setting details.
262
+ Bug Fix: Updated .htaccess rules to be compatible with Apache 2.4 without the auth compat module.
263
+ Bug Fix: Enabling and disabling the "Remove File Writing Permissions" setting now updates the file permissions properly.
264
+ Bug Fix: Web servers that cannot be recognized now default to Apache.
265
+ Enhancement: Updated the hackrepair lists.
266
+ 1.16.0 - 2015-08-03 - Chris Jean
267
+ Feature Removal: Removed the "Remove WordPress Generator Meta Tag" feature as it is not recommended due to limited security benefit and creating compatibility issues.
268
+ Enhancement: Added the ability to undo the Content Directory change.
269
+ Bug Fix: No longer tries to load a non-existent JavaScript file for the salts module.
270
+ Bug Fix: Fixed an issue with one-time database backups on multi-site installs.
271
+ Bug Fix: Fixed issues related to locating .htaccess or nginx.conf files on sites with WordPress installed in a separate directory.
272
+ Bug Fix: Fixed issues with PHP blocking in uploads directory not working with certain non-standard setups.
273
+ Bug Fix: Minor change to fix a warning that can appear after changing the Content Directory.
274
+ Bug Fix: Fixed a PHP fatal error that could occur on some servers when adding a ban to the site's .htaccess or nginx.conf file.
275
+ Bug Fix: Fixed some issues with profile pages on multisite setups that affected both two factor authentication and the password generator.
276
+ 1.16.1 - 2015-08-14 - Chris Jean
277
+ Bug Fix: Fixed "Call to undefined function get_home_path()" error.
278
+ 1.17.0 - 2015-09-14 - Chris Jean
279
+ New Feature: Added malware scanning provided by Sucuri SiteCheck.
280
+ 1.17.1 - 2015-09-14 - Chris Jean
281
+ Enhancement: Updated link to Sucuri SiteCheck.
282
+ 1.17.2 - 2015-09-15 - Chris Jean
283
+ Enhancement: Updated better-wp-security's translation domain from it-l10n-better-wp-security to better-wp-security.
284
+ 1.17.3 - 2015-09-15 - Chris Jean
285
+ Compatibility Fix: Added support for ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY. Setting it to true can bypass "SSL peer certificate or SSH remote key was not OK" errors on servers with bad SSL configurations.
core/img/alert16.png ADDED
Binary file
core/img/bb-ad.jpg ADDED
Binary file
core/img/check16.png ADDED
Binary file
core/img/flag16-blue.png ADDED
Binary file
core/img/flag16-red.png ADDED
Binary file
core/img/flag16-yellow.png ADDED
Binary file
core/img/green-check16.png ADDED
Binary file
core/img/index.php ADDED
File without changes
core/img/return-to-top.png ADDED
Binary file
core/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/js/admin-dashboard-footer.js ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ if ( typeof postboxes !== 'undefined' ) {
2
+ postboxes.add_postbox_toggles( pagenow );
3
+ }
core/js/admin-dashboard.js ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( '#screen-meta-links' ).append(
4
+ '<div id="itsec-meta-link-wrap" class="hide-if-no-js screen-meta-toggle">' +
5
+ '<a href="' + document.location + '&show_admin_modal=true" class="show-settings">' + itsec_dashboard.text + '</a>' +
6
+ '</div>'
7
+ );
8
+
9
+ jQuery( '.itsec_toc_item_link' ).click( function ( event ) {
10
+
11
+ event.preventDefault();
12
+
13
+ var goto = jQuery( this ).attr( 'href' );
14
+
15
+ console.log( goto );
16
+
17
+ jQuery( 'html, body' ).animate(
18
+ {
19
+ scrollTop: jQuery( goto ).offset().top
20
+ },
21
+ 1000
22
+ );
23
+
24
+ } );
25
+
26
+ jQuery( '.dialog' ).click( function ( event ) {
27
+
28
+ event.preventDefault();
29
+
30
+ var target = jQuery( this ).attr( 'href' );
31
+ var title = jQuery( this ).parents( '.inside' ).siblings( 'h3.hndle' ).children( 'span' ).text();
32
+
33
+ jQuery( '#' + target ).dialog( {
34
+ dialogClass : 'wp-dialog itsec-dialog itsec-dialog-logs',
35
+ modal : true,
36
+ closeOnEscape: true,
37
+ title : title,
38
+ height : ( jQuery( window ).height() * 0.8 ),
39
+ width : ( jQuery( window ).width() * 0.8 ),
40
+ open : function ( event, ui ) {
41
+
42
+ jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
43
+ jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
44
+ } );
45
+
46
+ }
47
+
48
+ } );
49
+
50
+ jQuery( '.ui-dialog :button' ).blur();
51
+
52
+ } );
53
+
54
+ jQuery( '.itsec-video-link' ).click( function ( event ) {
55
+
56
+ event.preventDefault();
57
+
58
+ var target = jQuery( this ).data( 'video-id' );
59
+
60
+ jQuery( '.' + target ).dialog( {
61
+ dialogClass : 'wp-dialog itsec-dialog itsec-video-dialog',
62
+ modal : true,
63
+ closeOnEscape: true,
64
+ width : 'auto',
65
+ resizable : false,
66
+ draggable : false,
67
+ create : function ( event, ui ) {
68
+ jQuery( this ).css( "maxWidth", "853px" );
69
+ },
70
+ open : function ( event, ui ) {
71
+
72
+ jQuery( '.ui-widget-overlay' ).bind( 'click', function () {
73
+ jQuery( this ).siblings( '.ui-dialog' ).find( '.ui-dialog-content' ).dialog( 'close' );
74
+ } );
75
+
76
+ }
77
+
78
+ } );
79
+
80
+ jQuery( '.ui-dialog :button' ).blur();
81
+
82
+ } );
83
+
84
+ jQuery( '.itsec_return_to_top' ).click( function ( event ) {
85
+
86
+ event.preventDefault();
87
+
88
+ jQuery( 'html, body' ).animate( {
89
+ scrollTop: jQuery( 'html, body' ).offset().top
90
+ },
91
+ 500
92
+ );
93
+
94
+ } );
95
+
96
+ jQuery( function () {
97
+ jQuery( "#itsec_tabbed_dashboard_content" ).tabs();
98
+ } );
99
+
100
+ var toc_fixed = false;
101
+
102
+ jQuery( window ).scroll( function () {
103
+
104
+ if ( jQuery( this ).scrollTop() >= 550 ) {
105
+
106
+ if ( ! toc_fixed ) {
107
+
108
+ toc_fixed = true;
109
+ jQuery( '#global_table_of_contents' ).addClass( 'fixed' );
110
+
111
+ }
112
+
113
+ } else {
114
+
115
+ if ( toc_fixed ) {
116
+
117
+ toc_fixed = false;
118
+ jQuery( '#global_table_of_contents' ).removeClass( 'fixed' );
119
+
120
+ }
121
+
122
+ }
123
+
124
+ } );
125
+
126
+ } );
127
+
128
+ function itsec_toc_select( value ) {
129
+
130
+ if ( value ) {
131
+
132
+ if ( jQuery( value ).hasClass( 'closed' ) ) {
133
+ jQuery( value ).removeClass( 'closed' );
134
+ }
135
+
136
+ jQuery( 'html, body' ).animate(
137
+ {
138
+ scrollTop: jQuery( value ).offset().top - 50
139
+ },
140
+ 500
141
+ );
142
+ }
143
+
144
+ }
145
+
146
+ if ( window.location.hash ) {
147
+
148
+ var id = window.location.hash.substring( 1 );
149
+
150
+ jQuery( window ).load( function () {
151
+
152
+ var target_offset = jQuery( "#" + id ).offset();
153
+ var target_top = target_offset.top;
154
+ var scroll_target = jQuery( '#' + id ).parent().parent();
155
+ var toggle_target = scroll_target.parents( '.postbox' );
156
+
157
+ // open metabox if needed
158
+ if ( toggle_target.hasClass( 'closed' ) ) {
159
+ toggle_target.removeClass( 'closed' );
160
+ }
161
+
162
+ //scroll to setting and highlight it
163
+ scroll_to_setting( scroll_target );
164
+
165
+ } );
166
+
167
+ function scroll_to_setting( scroll_target ) {
168
+
169
+ var id = window.location.hash.substring( 1 );
170
+ var target_offset = jQuery( "#" + id ).offset();
171
+ var target_top = target_offset.top;
172
+
173
+ jQuery( 'html, body' ).animate( { scrollTop: target_top - 100 }, 500 );
174
+
175
+ jQuery( scroll_target ).animate( {
176
+ backgroundColor: '#ffffcb'
177
+ }, 1000 );
178
+
179
+ setTimeout( function () {
180
+
181
+ jQuery( scroll_target ).animate( {
182
+ backgroundColor: '#fff'
183
+ }, 1000 )
184
+
185
+ }, 6000 );
186
+
187
+ }
188
+
189
+ }
core/js/admin-global-settings.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( '#itsec_reset_log_location' ).click( function ( event ) {
4
+
5
+ event.preventDefault();
6
+
7
+ jQuery( '#itsec_global_log_location' ).val( itsec_global_settings.location );
8
+
9
+ } );
10
+
11
+ jQuery( '.itsec_add_ip_to_whitelist' ).click( function ( event ) {
12
+
13
+ event.preventDefault();
14
+
15
+ jQuery( '#itsec_global_lockout_white_list' ).val( jQuery( '#itsec_global_lockout_white_list' ).val() + jQuery( '.itsec_add_ip_to_whitelist' ).attr( 'href' ) );
16
+
17
+ } );
18
+
19
+ } );
core/js/admin-logs.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ var $_GET = {};
4
+
5
+ document.location.search.replace( /\??(?:([^=]+)=([^&]*)&?)/g, function () {
6
+ function decode( s ) {
7
+ return decodeURIComponent( s.split( "+" ).join( " " ) );
8
+ }
9
+
10
+ $_GET[decode( arguments[1] )] = decode( arguments[2] );
11
+ } );
12
+
13
+ var uri = URI( window.location.href )
14
+
15
+ jQuery( '#itsec_log_filter' ).on( 'change', function () {
16
+
17
+ uri.removeSearch( 'itsec_log_filter' ).removeSearch( 'orderby' ).removeSearch( 'order' ).removeSearch( 'paged' ).addSearch( { itsec_log_filter: [this.value] } );
18
+ window.location.replace( uri );
19
+
20
+ } );
21
+
22
+ } );
core/js/admin-modal.js ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ //setup the tooltip
4
+ jQuery( '#itsec_intro_modal' ).dialog(
5
+ {
6
+ dialogClass : 'wp-dialog itsec-setup-dialog',
7
+ modal : true,
8
+ closeOnEscape: false,
9
+ title : itsec_tooltip_text.title,
10
+ width : '75%',
11
+ resizable : false,
12
+ draggable : false,
13
+ close : function ( event, ui ) {
14
+
15
+ var data = {
16
+ action: 'itsec_tooltip_ajax',
17
+ module: 'close',
18
+ nonce : itsec_tooltip_text.nonce
19
+ };
20
+
21
+ //call the ajax
22
+ jQuery.post( ajaxurl, data, function () {
23
+
24
+ var url = window.location.href;
25
+ console.log( url );
26
+ url = url.substring( 0, url.lastIndexOf( "&" ) );
27
+
28
+ window.location.replace( url );
29
+
30
+ } );
31
+
32
+ }
33
+
34
+ }
35
+ );
36
+
37
+ jQuery( '.ui-dialog a' ).blur();
38
+
39
+ jQuery( '.itsec-intro-close' ).click( function ( event ) {
40
+ jQuery( '#itsec_intro_modal' ).dialog( 'close' );
41
+ } );
42
+
43
+ //process tooltip actions
44
+ jQuery( '.itsec_tooltip_ajax' ).click( function ( event ) {
45
+
46
+ event.preventDefault();
47
+
48
+ var module = jQuery( this ).attr( 'href' );
49
+ var caller = this;
50
+
51
+ var data = {
52
+ action: 'itsec_tooltip_ajax',
53
+ module: module,
54
+ nonce : itsec_tooltip_text.nonce
55
+ };
56
+
57
+ //let user know we're working
58
+ jQuery( caller ).removeClass( 'itsec_tooltip_ajax button-primary' ).addClass( 'button-secondary' ).html( 'Working...' );
59
+
60
+ //call the ajax
61
+ jQuery.post( ajaxurl, data, function ( response ) {
62
+
63
+ if ( response == 'true' ) {
64
+
65
+ jQuery( caller ).replaceWith( '<span class="itsec_tooltip_success">' + itsec_tooltip_text.messages[module].success + '</span>' );
66
+
67
+ } else {
68
+
69
+ jQuery( caller ).replaceWith( '<span class="itsec_tooltip_failure">' + itsec_tooltip_text.messages[module].failure + '</span>' );
70
+ }
71
+
72
+ } );
73
+
74
+ } );
75
+
76
+ } );
core/js/admin-whitelist.js ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ set_temp();
4
+ release_temp();
5
+
6
+ } );
7
+
8
+ var set_temp = function () {
9
+
10
+ //process add to whitelist
11
+ jQuery( '.itsec_temp_whitelist_ajax' ).bind( 'click', function ( event ) {
12
+
13
+ event.preventDefault();
14
+
15
+ var caller = this;
16
+
17
+ var data = {
18
+ action: 'itsec_temp_whitelist_ajax',
19
+ nonce : itsec_temp_whitelist.nonce
20
+ };
21
+
22
+ //let user know we're working
23
+ jQuery( caller ).removeClass( 'itsec_tooltip_ajax button-primary' ).addClass( 'button-secondary' ).html( 'Working...' );
24
+
25
+ //call the ajax
26
+ jQuery.post( ajaxurl, data, function ( response ) {
27
+
28
+ if ( response !== 'error' ) {
29
+
30
+ data = jQuery.parseJSON( response );
31
+
32
+ if ( jQuery( caller ).hasClass( 'dashboard-whitelist' ) ) {
33
+
34
+ jQuery( '.itsec_temp_whitelist' ).replaceWith( '<p class="itsec_temp_whitelist submit"><a href="#" class="itsec_temp_whitelist_release_ajax button-primary dashboard-whitelist">' + data.message3 + '</a><span class="itsec_temp_whitelist_ip">' + data.message1 + ' <strong>' + data.ip + '</strong>, ' + data.message2 + ' <strong>' + data.exp + '</strong>.</span></p>' );
35
+
36
+ } else {
37
+
38
+ jQuery( '.itsec_temp_whitelist' ).replaceWith( '<p class="itsec_temp_whitelist submit">' + data.message1 + ', <strong>' + data.ip + '</strong>, ' + data.message2 + ' <strong>' + data.exp + '</strong>.<br /><a href="#" class="itsec_temp_whitelist_release_ajax button-primary">' + data.message3 + '</a></p>' );
39
+
40
+ }
41
+
42
+ release_temp();
43
+
44
+ }
45
+ else {
46
+
47
+ jQuery( caller ).replaceWith( '<span class="itsec_temp_whitelist_ajax">fail</span>' );
48
+ }
49
+
50
+ } );
51
+
52
+ } );
53
+
54
+ }
55
+
56
+ var release_temp = function () {
57
+
58
+ //process reset whitelist actions
59
+ jQuery( '.itsec_temp_whitelist_release_ajax' ).bind( 'click', function ( event ) {
60
+
61
+ event.preventDefault();
62
+
63
+ var caller = this;
64
+
65
+ var data = {
66
+ action: 'itsec_temp_whitelist_release_ajax',
67
+ nonce : itsec_temp_whitelist.nonce
68
+ };
69
+
70
+ //let user know we're working
71
+ jQuery( caller ).removeClass( 'itsec_tooltip_ajax button-primary' ).addClass( 'button-secondary' ).html( 'Working...' );
72
+
73
+ //call the ajax
74
+ jQuery.post( ajaxurl, data, function ( response ) {
75
+
76
+ if ( response !== 'error' ) {
77
+
78
+ var d_class = '';
79
+
80
+ if ( jQuery( caller ).hasClass( 'dashboard-whitelist' ) ) {
81
+
82
+ d_class = ' dashboard-whitelist';
83
+
84
+ }
85
+
86
+ jQuery( '.itsec_temp_whitelist' ).replaceWith( '<p class="itsec_temp_whitelist submit"><a href="#" class="itsec_temp_whitelist_ajax' + d_class + ' button-primary">' + itsec_temp_whitelist.success + '</a></p>' );
87
+ set_temp();
88
+
89
+ }
90
+ else {
91
+
92
+ jQuery( caller ).replaceWith( '<span class="itsec_temp_whitelist_ajax">fail</span>' );
93
+ }
94
+
95
+ } );
96
+
97
+ } );
98
+
99
+ }
core/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/js/tracking.js ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ if ( typeof itsec_tracking_vars != 'undefined' ) {
2
+
3
+ href = location.href;
4
+
5
+ (function () {
6
+ var ga = document.createElement( 'script' );
7
+ ga.type = 'text/javascript';
8
+ ga.async = true;
9
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
10
+ var s = document.getElementsByTagName( 'script' )[0];
11
+ s.parentNode.insertBefore( ga, s );
12
+ })();
13
+
14
+ var _gaq = _gaq || [];
15
+ _gaq.push( ['_setAccount', 'UA-47645120-1'] );
16
+
17
+ function itsec_get_vars( type, values ) {
18
+
19
+ var data = {
20
+ action: 'itsec_tracking_ajax',
21
+ type : type,
22
+ nonce : itsec_tracking_vars.nonce
23
+ };
24
+
25
+ if ( type != 'receive' ) {
26
+ data.values = values;
27
+ }
28
+
29
+ jQuery.post( ajaxurl, data, function ( response ) {
30
+
31
+ if ( response == 'false' ) {
32
+
33
+ return false;
34
+
35
+ } else {
36
+
37
+ return true;
38
+
39
+ }
40
+
41
+ } );
42
+
43
+ }
44
+
45
+ jQuery( document ).ready( function () {
46
+
47
+ var tracking_settings = itsec_tracking_vars.vars;
48
+ var track_it = new Array();
49
+ var timestamp = new Date().getTime();
50
+
51
+ jQuery( '.itsec-settings-form' ).submit( function ( event ) {
52
+
53
+ var values = jQuery( this ).serializeArray();
54
+
55
+ jQuery.each( values, function ( name, value ) {
56
+
57
+ var section = value.name.substring( 0, value.name.indexOf( '[' ) );
58
+ var setting = value.name.substring( value.name.indexOf( '[' ) + 1, value.name.indexOf( ']' ) );
59
+
60
+ if ( typeof tracking_settings[section] != 'undefined' && typeof tracking_settings[section][setting] != 'undefined' ) {
61
+
62
+ var setting_value = tracking_settings[section][setting];
63
+
64
+ var value_array = setting_value.split( ':' );
65
+ var default_type = value_array[1];
66
+
67
+ if ( default_type == 'b' && value.value == 1 ) {
68
+
69
+ var saved_value = 'true';
70
+
71
+ }
72
+ else {
73
+
74
+ if ( default_type == 'b' ) {
75
+ var saved_value = 'false';
76
+ }
77
+ else {
78
+ var saved_value = value.value;
79
+ }
80
+
81
+ }
82
+
83
+ delete tracking_settings[section][setting];
84
+
85
+ var item = new Object();
86
+
87
+ item.section = section;
88
+ item.setting = setting;
89
+ item.value = saved_value;
90
+
91
+ track_it.push( item );
92
+
93
+ }
94
+
95
+ } );
96
+
97
+ jQuery.each( tracking_settings, function ( section, settings ) {
98
+
99
+ var section = section;
100
+
101
+ jQuery.each( tracking_settings[section], function ( setting, value ) {
102
+
103
+ var value_array = value.split( ':' );
104
+ var default_type = value_array[1];
105
+ var default_value = value_array[0];
106
+
107
+ if ( default_type == 'b' && default_value == 0 ) {
108
+ var saved_value = 'false';
109
+ } else if ( default_type == 'b' ) {
110
+ var saved_value = 'true';
111
+ } else {
112
+ var saved_value = default_value;
113
+ }
114
+
115
+ var item = new Object();
116
+
117
+ item.section = section;
118
+ item.setting = setting;
119
+ item.value = saved_value;
120
+
121
+ track_it.push( item );
122
+
123
+ } );
124
+
125
+ } );
126
+
127
+ var group_size = Math.ceil( track_it.length / 9 );
128
+ var group_count = 1;
129
+ var count = 0;
130
+ var saved_values = '';
131
+
132
+ jQuery.each( track_it, function ( setting, value ) {
133
+
134
+ saved_value = ( value.section + '[' + value.setting + '] = ' + value.value + '; ')
135
+ saved_values = saved_values.concat( saved_value );
136
+
137
+ if ( count === group_size - 1 ) {
138
+
139
+ _gaq.push( ['_trackEvent', 'ITSEC', 'Group ' + ( group_count ), saved_values, timestamp, true] );
140
+ saved_values = '';
141
+ count = 0;
142
+ group_count ++;
143
+
144
+ } else {
145
+ count ++;
146
+ }
147
+
148
+ } );
149
+
150
+ } );
151
+
152
+ } );
153
+
154
+ }
core/js/url.js ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! URI.js v1.13.1 http://medialize.github.io/URI.js/ */
2
+ /* build contains: IPv6.js, punycode.js, SecondLevelDomains.js, URI.js, URITemplate.js, jquery.URI.js, URI.fragmentQuery.js */
3
+ (function(f,g){"object"===typeof exports?module.exports=g():"function"===typeof define&&define.amd?define(g):f.IPv6=g(f)})(this,function(f){var g=f&&f.IPv6;return{best:function(f){f=f.toLowerCase().split(":");var k=f.length,b=8;""===f[0]&&""===f[1]&&""===f[2]?(f.shift(),f.shift()):""===f[0]&&""===f[1]?f.shift():""===f[k-1]&&""===f[k-2]&&f.pop();k=f.length;-1!==f[k-1].indexOf(".")&&(b=7);var g;for(g=0;g<k&&""!==f[g];g++);if(g<b)for(f.splice(g,1,"0000");f.length<b;)f.splice(g,0,"0000");for(g=0;g<b;g++){for(var k=
4
+ f[g].split(""),r=0;3>r;r++)if("0"===k[0]&&1<k.length)k.splice(0,1);else break;f[g]=k.join("")}var k=-1,q=r=0,h=-1,w=!1;for(g=0;g<b;g++)w?"0"===f[g]?q+=1:(w=!1,q>r&&(k=h,r=q)):"0"===f[g]&&(w=!0,h=g,q=1);q>r&&(k=h,r=q);1<r&&f.splice(k,r,"");k=f.length;b="";""===f[0]&&(b=":");for(g=0;g<k;g++){b+=f[g];if(g===k-1)break;b+=":"}""===f[k-1]&&(b+=":");return b},noConflict:function(){f.IPv6===this&&(f.IPv6=g);return this}}});
5
+ (function(f){function g(a){throw RangeError(z[a]);}function s(a,c){for(var d=a.length;d--;)a[d]=c(a[d]);return a}function k(a,c){return s(a.split(p),c).join(".")}function b(a){for(var c=[],d=0,b=a.length,p,e;d<b;)p=a.charCodeAt(d++),55296<=p&&56319>=p&&d<b?(e=a.charCodeAt(d++),56320==(e&64512)?c.push(((p&1023)<<10)+(e&1023)+65536):(c.push(p),d--)):c.push(p);return c}function u(a){return s(a,function(a){var c="";65535<a&&(a-=65536,c+=D(a>>>10&1023|55296),a=56320|a&1023);return c+=D(a)}).join("")}function r(a,
6
+ c){return a+22+75*(26>a)-((0!=c)<<5)}function q(a,c,d){var b=0;a=d?B(a/H):a>>1;for(a+=B(a/c);a>v*y>>1;b+=l)a=B(a/v);return B(b+(v+1)*a/(a+E))}function h(c){var d=[],b=c.length,p,e=0,f=F,z=C,h,m,v,n,k;h=c.lastIndexOf(a);0>h&&(h=0);for(m=0;m<h;++m)128<=c.charCodeAt(m)&&g("not-basic"),d.push(c.charCodeAt(m));for(h=0<h?h+1:0;h<b;){m=e;p=1;for(v=l;;v+=l){h>=b&&g("invalid-input");n=c.charCodeAt(h++);n=10>n-48?n-22:26>n-65?n-65:26>n-97?n-97:l;(n>=l||n>B((t-e)/p))&&g("overflow");e+=n*p;k=v<=z?x:v>=z+y?y:
7
+ v-z;if(n<k)break;n=l-k;p>B(t/n)&&g("overflow");p*=n}p=d.length+1;z=q(e-m,p,0==m);B(e/p)>t-f&&g("overflow");f+=B(e/p);e%=p;d.splice(e++,0,f)}return u(d)}function w(c){var d,p,e,f,z,h,m,n,v,k=[],w,s,A;c=b(c);w=c.length;d=F;p=0;z=C;for(h=0;h<w;++h)v=c[h],128>v&&k.push(D(v));for((e=f=k.length)&&k.push(a);e<w;){m=t;for(h=0;h<w;++h)v=c[h],v>=d&&v<m&&(m=v);s=e+1;m-d>B((t-p)/s)&&g("overflow");p+=(m-d)*s;d=m;for(h=0;h<w;++h)if(v=c[h],v<d&&++p>t&&g("overflow"),v==d){n=p;for(m=l;;m+=l){v=m<=z?x:m>=z+y?y:m-z;
8
+ if(n<v)break;A=n-v;n=l-v;k.push(D(r(v+A%n,0)));n=B(A/n)}k.push(D(r(n,0)));z=q(p,s,e==f);p=0;++e}++p;++d}return k.join("")}var A="object"==typeof exports&&exports,m="object"==typeof module&&module&&module.exports==A&&module,n="object"==typeof global&&global;if(n.global===n||n.window===n)f=n;var e,t=2147483647,l=36,x=1,y=26,E=38,H=700,C=72,F=128,a="-",c=/^xn--/,d=/[^ -~]/,p=/\x2E|\u3002|\uFF0E|\uFF61/g,z={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)",
9
+ "invalid-input":"Invalid input"},v=l-x,B=Math.floor,D=String.fromCharCode,G;e={version:"1.2.3",ucs2:{decode:b,encode:u},decode:h,encode:w,toASCII:function(a){return k(a,function(a){return d.test(a)?"xn--"+w(a):a})},toUnicode:function(a){return k(a,function(a){return c.test(a)?h(a.slice(4).toLowerCase()):a})}};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return e});else if(A&&!A.nodeType)if(m)m.exports=e;else for(G in e)e.hasOwnProperty(G)&&(A[G]=e[G]);else f.punycode=
10
+ e})(this);
11
+ (function(f,g){"object"===typeof exports?module.exports=g():"function"===typeof define&&define.amd?define(g):f.SecondLevelDomains=g(f)})(this,function(f){var g=f&&f.SecondLevelDomains,s={list:{ac:" com gov mil net org ",ae:" ac co gov mil name net org pro sch ",af:" com edu gov net org ",al:" com edu gov mil net org ",ao:" co ed gv it og pb ",ar:" com edu gob gov int mil net org tur ",at:" ac co gv or ",au:" asn com csiro edu gov id net org ",ba:" co com edu gov mil net org rs unbi unmo unsa untz unze ",bb:" biz co com edu gov info net org store tv ",
12
+ bh:" biz cc com edu gov info net org ",bn:" com edu gov net org ",bo:" com edu gob gov int mil net org tv ",br:" adm adv agr am arq art ato b bio blog bmd cim cng cnt com coop ecn edu eng esp etc eti far flog fm fnd fot fst g12 ggf gov imb ind inf jor jus lel mat med mil mus net nom not ntr odo org ppg pro psc psi qsl rec slg srv tmp trd tur tv vet vlog wiki zlg ",bs:" com edu gov net org ",bz:" du et om ov rg ",ca:" ab bc mb nb nf nl ns nt nu on pe qc sk yk ",ck:" biz co edu gen gov info net org ",
13
+ cn:" ac ah bj com cq edu fj gd gov gs gx gz ha hb he hi hl hn jl js jx ln mil net nm nx org qh sc sd sh sn sx tj tw xj xz yn zj ",co:" com edu gov mil net nom org ",cr:" ac c co ed fi go or sa ",cy:" ac biz com ekloges gov ltd name net org parliament press pro tm ","do":" art com edu gob gov mil net org sld web ",dz:" art asso com edu gov net org pol ",ec:" com edu fin gov info med mil net org pro ",eg:" com edu eun gov mil name net org sci ",er:" com edu gov ind mil net org rochest w ",es:" com edu gob nom org ",
14
+ et:" biz com edu gov info name net org ",fj:" ac biz com info mil name net org pro ",fk:" ac co gov net nom org ",fr:" asso com f gouv nom prd presse tm ",gg:" co net org ",gh:" com edu gov mil org ",gn:" ac com gov net org ",gr:" com edu gov mil net org ",gt:" com edu gob ind mil net org ",gu:" com edu gov net org ",hk:" com edu gov idv net org ",id:" ac co go mil net or sch web ",il:" ac co gov idf k12 muni net org ","in":" ac co edu ernet firm gen gov i ind mil net nic org res ",iq:" com edu gov i mil net org ",
15
+ ir:" ac co dnssec gov i id net org sch ",it:" edu gov ",je:" co net org ",jo:" com edu gov mil name net org sch ",jp:" ac ad co ed go gr lg ne or ",ke:" ac co go info me mobi ne or sc ",kh:" com edu gov mil net org per ",ki:" biz com de edu gov info mob net org tel ",km:" asso com coop edu gouv k medecin mil nom notaires pharmaciens presse tm veterinaire ",kn:" edu gov net org ",kr:" ac busan chungbuk chungnam co daegu daejeon es gangwon go gwangju gyeongbuk gyeonggi gyeongnam hs incheon jeju jeonbuk jeonnam k kg mil ms ne or pe re sc seoul ulsan ",
16
+ kw:" com edu gov net org ",ky:" com edu gov net org ",kz:" com edu gov mil net org ",lb:" com edu gov net org ",lk:" assn com edu gov grp hotel int ltd net ngo org sch soc web ",lr:" com edu gov net org ",lv:" asn com conf edu gov id mil net org ",ly:" com edu gov id med net org plc sch ",ma:" ac co gov m net org press ",mc:" asso tm ",me:" ac co edu gov its net org priv ",mg:" com edu gov mil nom org prd tm ",mk:" com edu gov inf name net org pro ",ml:" com edu gov net org presse ",mn:" edu gov org ",
17
+ mo:" com edu gov net org ",mt:" com edu gov net org ",mv:" aero biz com coop edu gov info int mil museum name net org pro ",mw:" ac co com coop edu gov int museum net org ",mx:" com edu gob net org ",my:" com edu gov mil name net org sch ",nf:" arts com firm info net other per rec store web ",ng:" biz com edu gov mil mobi name net org sch ",ni:" ac co com edu gob mil net nom org ",np:" com edu gov mil net org ",nr:" biz com edu gov info net org ",om:" ac biz co com edu gov med mil museum net org pro sch ",
18
+ pe:" com edu gob mil net nom org sld ",ph:" com edu gov i mil net ngo org ",pk:" biz com edu fam gob gok gon gop gos gov net org web ",pl:" art bialystok biz com edu gda gdansk gorzow gov info katowice krakow lodz lublin mil net ngo olsztyn org poznan pwr radom slupsk szczecin torun warszawa waw wroc wroclaw zgora ",pr:" ac biz com edu est gov info isla name net org pro prof ",ps:" com edu gov net org plo sec ",pw:" belau co ed go ne or ",ro:" arts com firm info nom nt org rec store tm www ",rs:" ac co edu gov in org ",
19
+ sb:" com edu gov net org ",sc:" com edu gov net org ",sh:" co com edu gov net nom org ",sl:" com edu gov net org ",st:" co com consulado edu embaixada gov mil net org principe saotome store ",sv:" com edu gob org red ",sz:" ac co org ",tr:" av bbs bel biz com dr edu gen gov info k12 name net org pol tel tsk tv web ",tt:" aero biz cat co com coop edu gov info int jobs mil mobi museum name net org pro tel travel ",tw:" club com ebiz edu game gov idv mil net org ",mu:" ac co com gov net or org ",mz:" ac co edu gov org ",
20
+ na:" co com ",nz:" ac co cri geek gen govt health iwi maori mil net org parliament school ",pa:" abo ac com edu gob ing med net nom org sld ",pt:" com edu gov int net nome org publ ",py:" com edu gov mil net org ",qa:" com edu gov mil net org ",re:" asso com nom ",ru:" ac adygeya altai amur arkhangelsk astrakhan bashkiria belgorod bir bryansk buryatia cbg chel chelyabinsk chita chukotka chuvashia com dagestan e-burg edu gov grozny int irkutsk ivanovo izhevsk jar joshkar-ola kalmykia kaluga kamchatka karelia kazan kchr kemerovo khabarovsk khakassia khv kirov koenig komi kostroma kranoyarsk kuban kurgan kursk lipetsk magadan mari mari-el marine mil mordovia mosreg msk murmansk nalchik net nnov nov novosibirsk nsk omsk orenburg org oryol penza perm pp pskov ptz rnd ryazan sakhalin samara saratov simbirsk smolensk spb stavropol stv surgut tambov tatarstan tom tomsk tsaritsyn tsk tula tuva tver tyumen udm udmurtia ulan-ude vladikavkaz vladimir vladivostok volgograd vologda voronezh vrn vyatka yakutia yamal yekaterinburg yuzhno-sakhalinsk ",
21
+ rw:" ac co com edu gouv gov int mil net ",sa:" com edu gov med net org pub sch ",sd:" com edu gov info med net org tv ",se:" a ac b bd c d e f g h i k l m n o org p parti pp press r s t tm u w x y z ",sg:" com edu gov idn net org per ",sn:" art com edu gouv org perso univ ",sy:" com edu gov mil net news org ",th:" ac co go in mi net or ",tj:" ac biz co com edu go gov info int mil name net nic org test web ",tn:" agrinet com defense edunet ens fin gov ind info intl mincom nat net org perso rnrt rns rnu tourism ",
22
+ tz:" ac co go ne or ",ua:" biz cherkassy chernigov chernovtsy ck cn co com crimea cv dn dnepropetrovsk donetsk dp edu gov if in ivano-frankivsk kh kharkov kherson khmelnitskiy kiev kirovograd km kr ks kv lg lugansk lutsk lviv me mk net nikolaev od odessa org pl poltava pp rovno rv sebastopol sumy te ternopil uzhgorod vinnica vn zaporizhzhe zhitomir zp zt ",ug:" ac co go ne or org sc ",uk:" ac bl british-library co cym gov govt icnet jet lea ltd me mil mod national-library-scotland nel net nhs nic nls org orgn parliament plc police sch scot soc ",
23
+ us:" dni fed isa kids nsn ",uy:" com edu gub mil net org ",ve:" co com edu gob info mil net org web ",vi:" co com k12 net org ",vn:" ac biz com edu gov health info int name net org pro ",ye:" co com gov ltd me net org plc ",yu:" ac co edu gov org ",za:" ac agric alt bourse city co cybernet db edu gov grondar iaccess imt inca landesign law mil net ngo nis nom olivetti org pix school tm web ",zm:" ac co com edu gov net org sch "},has:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1)return!1;
24
+ var g=f.lastIndexOf(".",b-1);if(0>=g||g>=b-1)return!1;var r=s.list[f.slice(b+1)];return r?0<=r.indexOf(" "+f.slice(g+1,b)+" "):!1},is:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1||0<=f.lastIndexOf(".",b-1))return!1;var g=s.list[f.slice(b+1)];return g?0<=g.indexOf(" "+f.slice(0,b)+" "):!1},get:function(f){var b=f.lastIndexOf(".");if(0>=b||b>=f.length-1)return null;var g=f.lastIndexOf(".",b-1);if(0>=g||g>=b-1)return null;var r=s.list[f.slice(b+1)];return!r||0>r.indexOf(" "+f.slice(g+
25
+ 1,b)+" ")?null:f.slice(g+1)},noConflict:function(){f.SecondLevelDomains===this&&(f.SecondLevelDomains=g);return this}};return s});
26
+ (function(f,g){"object"===typeof exports?module.exports=g(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"===typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],g):f.URI=g(f.punycode,f.IPv6,f.SecondLevelDomains,f)})(this,function(f,g,s,k){function b(a,c){if(!(this instanceof b))return new b(a,c);void 0===a&&(a="undefined"!==typeof location?location.href+"":"");this.href(a);return void 0!==c?this.absoluteTo(c):this}function u(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g,
27
+ "\\$1")}function r(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function q(a){return"Array"===r(a)}function h(a,c){var d,b;if(q(c)){d=0;for(b=c.length;d<b;d++)if(!h(a,c[d]))return!1;return!0}var e=r(c);d=0;for(b=a.length;d<b;d++)if("RegExp"===e){if("string"===typeof a[d]&&a[d].match(c))return!0}else if(a[d]===c)return!0;return!1}function w(a,c){if(!q(a)||!q(c)||a.length!==c.length)return!1;a.sort();c.sort();for(var d=0,b=a.length;d<b;d++)if(a[d]!==c[d])return!1;
28
+ return!0}function A(a){return escape(a)}function m(a){return encodeURIComponent(a).replace(/[!'()*]/g,A).replace(/\*/g,"%2A")}var n=k&&k.URI;b.version="1.13.1";var e=b.prototype,t=Object.prototype.hasOwnProperty;b._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:b.duplicateQueryParameters,escapeQuerySpace:b.escapeQuerySpace}};b.duplicateQueryParameters=!1;b.escapeQuerySpace=!0;b.protocol_expression=
29
+ /^[a-z][a-z0-9.+-]*$/i;b.idn_expression=/[^a-z0-9\.-]/i;b.punycode_expression=/(xn--)/i;b.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;b.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
30
+ b.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u2018\u2019]))/ig;b.findUri={start:/\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,end:/[\s\r\n]|$/,trim:/[`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u201e\u2018\u2019]+$/};b.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"};b.invalid_hostname_characters=
31
+ /[^a-zA-Z0-9\.-]/;b.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",iframe:"src",embed:"src",source:"src",track:"src",input:"src"};b.getDomAttribute=function(a){if(a&&a.nodeName){var c=a.nodeName.toLowerCase();return"input"===c&&"image"!==a.type?void 0:b.domAttributes[c]}};b.encode=m;b.decode=decodeURIComponent;b.iso8859=function(){b.encode=escape;b.decode=unescape};b.unicode=function(){b.encode=m;b.decode=decodeURIComponent};b.characters=
32
+ {pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%3B":";","%3D":"="}}}};b.encodeQuery=
33
+ function(a,c){var d=b.encode(a+"");void 0===c&&(c=b.escapeQuerySpace);return c?d.replace(/%20/g,"+"):d};b.decodeQuery=function(a,c){a+="";void 0===c&&(c=b.escapeQuerySpace);try{return b.decode(c?a.replace(/\+/g,"%20"):a)}catch(d){return a}};b.recodePath=function(a){a=(a+"").split("/");for(var c=0,d=a.length;c<d;c++)a[c]=b.encodePathSegment(b.decode(a[c]));return a.join("/")};b.decodePath=function(a){a=(a+"").split("/");for(var c=0,d=a.length;c<d;c++)a[c]=b.decodePathSegment(a[c]);return a.join("/")};
34
+ var l={encode:"encode",decode:"decode"},x,y=function(a,c){return function(d){return b[c](d+"").replace(b.characters[a][c].expression,function(d){return b.characters[a][c].map[d]})}};for(x in l)b[x+"PathSegment"]=y("pathname",l[x]);b.encodeReserved=y("reserved","encode");b.parse=function(a,c){var d;c||(c={});d=a.indexOf("#");-1<d&&(c.fragment=a.substring(d+1)||null,a=a.substring(0,d));d=a.indexOf("?");-1<d&&(c.query=a.substring(d+1)||null,a=a.substring(0,d));"//"===a.substring(0,2)?(c.protocol=null,
35
+ a=a.substring(2),a=b.parseAuthority(a,c)):(d=a.indexOf(":"),-1<d&&(c.protocol=a.substring(0,d)||null,c.protocol&&!c.protocol.match(b.protocol_expression)?c.protocol=void 0:"file"===c.protocol?a=a.substring(d+3):"//"===a.substring(d+1,d+3)?(a=a.substring(d+3),a=b.parseAuthority(a,c)):(a=a.substring(d+1),c.urn=!0)));c.path=a;return c};b.parseHost=function(a,c){var d=a.indexOf("/"),b;-1===d&&(d=a.length);"["===a.charAt(0)?(b=a.indexOf("]"),c.hostname=a.substring(1,b)||null,c.port=a.substring(b+2,d)||
36
+ null,"/"===c.port&&(c.port=null)):a.indexOf(":")!==a.lastIndexOf(":")?(c.hostname=a.substring(0,d)||null,c.port=null):(b=a.substring(0,d).split(":"),c.hostname=b[0]||null,c.port=b[1]||null);c.hostname&&"/"!==a.substring(d).charAt(0)&&(d++,a="/"+a);return a.substring(d)||"/"};b.parseAuthority=function(a,c){a=b.parseUserinfo(a,c);return b.parseHost(a,c)};b.parseUserinfo=function(a,c){var d=a.indexOf("/"),p=-1<d?a.lastIndexOf("@",d):a.indexOf("@");-1<p&&(-1===d||p<d)?(d=a.substring(0,p).split(":"),c.username=
37
+ d[0]?b.decode(d[0]):null,d.shift(),c.password=d[0]?b.decode(d.join(":")):null,a=a.substring(p+1)):(c.username=null,c.password=null);return a};b.parseQuery=function(a,c){if(!a)return{};a=a.replace(/&+/g,"&").replace(/^\?*&*|&+$/g,"");if(!a)return{};for(var d={},p=a.split("&"),e=p.length,f,h,m=0;m<e;m++)f=p[m].split("="),h=b.decodeQuery(f.shift(),c),f=f.length?b.decodeQuery(f.join("="),c):null,d[h]?("string"===typeof d[h]&&(d[h]=[d[h]]),d[h].push(f)):d[h]=f;return d};b.build=function(a){var c="";a.protocol&&
38
+ (c+=a.protocol+":");a.urn||!c&&!a.hostname||(c+="//");c+=b.buildAuthority(a)||"";"string"===typeof a.path&&("/"!==a.path.charAt(0)&&"string"===typeof a.hostname&&(c+="/"),c+=a.path);"string"===typeof a.query&&a.query&&(c+="?"+a.query);"string"===typeof a.fragment&&a.fragment&&(c+="#"+a.fragment);return c};b.buildHost=function(a){var c="";if(a.hostname)c=b.ip6_expression.test(a.hostname)?c+("["+a.hostname+"]"):c+a.hostname;else return"";a.port&&(c+=":"+a.port);return c};b.buildAuthority=function(a){return b.buildUserinfo(a)+
39
+ b.buildHost(a)};b.buildUserinfo=function(a){var c="";a.username&&(c+=b.encode(a.username),a.password&&(c+=":"+b.encode(a.password)),c+="@");return c};b.buildQuery=function(a,c,d){var p="",f,e,h,m;for(e in a)if(t.call(a,e)&&e)if(q(a[e]))for(f={},h=0,m=a[e].length;h<m;h++)void 0!==a[e][h]&&void 0===f[a[e][h]+""]&&(p+="&"+b.buildQueryParameter(e,a[e][h],d),!0!==c&&(f[a[e][h]+""]=!0));else void 0!==a[e]&&(p+="&"+b.buildQueryParameter(e,a[e],d));return p.substring(1)};b.buildQueryParameter=function(a,
40
+ c,d){return b.encodeQuery(a,d)+(null!==c?"="+b.encodeQuery(c,d):"")};b.addQuery=function(a,c,d){if("object"===typeof c)for(var p in c)t.call(c,p)&&b.addQuery(a,p,c[p]);else if("string"===typeof c)void 0===a[c]?a[c]=d:("string"===typeof a[c]&&(a[c]=[a[c]]),q(d)||(d=[d]),a[c]=a[c].concat(d));else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");};b.removeQuery=function(a,c,d){var p;if(q(c))for(d=0,p=c.length;d<p;d++)a[c[d]]=void 0;else if("object"===typeof c)for(p in c)t.call(c,
41
+ p)&&b.removeQuery(a,p,c[p]);else if("string"===typeof c)if(void 0!==d)if(a[c]===d)a[c]=void 0;else{if(q(a[c])){p=a[c];var e={},f,h;if(q(d))for(f=0,h=d.length;f<h;f++)e[d[f]]=!0;else e[d]=!0;f=0;for(h=p.length;f<h;f++)void 0!==e[p[f]]&&(p.splice(f,1),h--,f--);a[c]=p}}else a[c]=void 0;else throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");};b.hasQuery=function(a,c,d,e){if("object"===typeof c){for(var f in c)if(t.call(c,f)&&!b.hasQuery(a,f,c[f]))return!1;return!0}if("string"!==
42
+ typeof c)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter");switch(r(d)){case "Undefined":return c in a;case "Boolean":return a=Boolean(q(a[c])?a[c].length:a[c]),d===a;case "Function":return!!d(a[c],c,a);case "Array":return q(a[c])?(e?h:w)(a[c],d):!1;case "RegExp":return q(a[c])?e?h(a[c],d):!1:Boolean(a[c]&&a[c].match(d));case "Number":d=String(d);case "String":return q(a[c])?e?h(a[c],d):!1:a[c]===d;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");
43
+ }};b.commonPath=function(a,c){var d=Math.min(a.length,c.length),b;for(b=0;b<d;b++)if(a.charAt(b)!==c.charAt(b)){b--;break}if(1>b)return a.charAt(0)===c.charAt(0)&&"/"===a.charAt(0)?"/":"";if("/"!==a.charAt(b)||"/"!==c.charAt(b))b=a.substring(0,b).lastIndexOf("/");return a.substring(0,b+1)};b.withinString=function(a,c,d){d||(d={});var e=d.start||b.findUri.start,f=d.end||b.findUri.end,h=d.trim||b.findUri.trim,m=/[a-z0-9-]=["']?$/i;for(e.lastIndex=0;;){var g=e.exec(a);if(!g)break;g=g.index;if(d.ignoreHtml){var n=
44
+ a.slice(Math.max(g-3,0),g);if(n&&m.test(n))continue}var n=g+a.slice(g).search(f),l=a.slice(g,n).replace(h,"");d.ignore&&d.ignore.test(l)||(n=g+l.length,l=c(l,g,n,a),a=a.slice(0,g)+l+a.slice(n),e.lastIndex=g+l.length)}e.lastIndex=0;return a};b.ensureValidHostname=function(a){if(a.match(b.invalid_hostname_characters)){if(!f)throw new TypeError('Hostname "'+a+'" contains characters other than [A-Z0-9.-] and Punycode.js is not available');if(f.toASCII(a).match(b.invalid_hostname_characters))throw new TypeError('Hostname "'+
45
+ a+'" contains characters other than [A-Z0-9.-]');}};b.noConflict=function(a){if(a)return a={URI:this.noConflict()},k.URITemplate&&"function"===typeof k.URITemplate.noConflict&&(a.URITemplate=k.URITemplate.noConflict()),k.IPv6&&"function"===typeof k.IPv6.noConflict&&(a.IPv6=k.IPv6.noConflict()),k.SecondLevelDomains&&"function"===typeof k.SecondLevelDomains.noConflict&&(a.SecondLevelDomains=k.SecondLevelDomains.noConflict()),a;k.URI===this&&(k.URI=n);return this};e.build=function(a){if(!0===a)this._deferred_build=
46
+ !0;else if(void 0===a||this._deferred_build)this._string=b.build(this._parts),this._deferred_build=!1;return this};e.clone=function(){return new b(this)};e.valueOf=e.toString=function(){return this.build(!1)._string};l={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"};y=function(a){return function(c,d){if(void 0===c)return this._parts[a]||"";this._parts[a]=c||null;this.build(!d);return this}};for(x in l)e[x]=y(l[x]);l={query:"?",fragment:"#"};y=function(a,
47
+ c){return function(d,b){if(void 0===d)return this._parts[a]||"";null!==d&&(d+="",d.charAt(0)===c&&(d=d.substring(1)));this._parts[a]=d;this.build(!b);return this}};for(x in l)e[x]=y(x,l[x]);l={search:["?","query"],hash:["#","fragment"]};y=function(a,c){return function(d,b){var e=this[a](d,b);return"string"===typeof e&&e.length?c+e:e}};for(x in l)e[x]=y(l[x][1],l[x][0]);e.pathname=function(a,c){if(void 0===a||!0===a){var d=this._parts.path||(this._parts.hostname?"/":"");return a?b.decodePath(d):d}this._parts.path=
48
+ a?b.recodePath(a):"/";this.build(!c);return this};e.path=e.pathname;e.href=function(a,c){var d;if(void 0===a)return this.toString();this._string="";this._parts=b._parts();var e=a instanceof b,f="object"===typeof a&&(a.hostname||a.path||a.pathname);a.nodeName&&(f=b.getDomAttribute(a),a=a[f]||"",f=!1);!e&&f&&void 0!==a.pathname&&(a=a.toString());if("string"===typeof a)this._parts=b.parse(a,this._parts);else if(e||f)for(d in e=e?a._parts:a,e)t.call(this._parts,d)&&(this._parts[d]=e[d]);else throw new TypeError("invalid input");
49
+ this.build(!c);return this};e.is=function(a){var c=!1,d=!1,e=!1,f=!1,h=!1,m=!1,g=!1,n=!this._parts.urn;this._parts.hostname&&(n=!1,d=b.ip4_expression.test(this._parts.hostname),e=b.ip6_expression.test(this._parts.hostname),c=d||e,h=(f=!c)&&s&&s.has(this._parts.hostname),m=f&&b.idn_expression.test(this._parts.hostname),g=f&&b.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return n;case "absolute":return!n;case "domain":case "name":return f;case "sld":return h;
50
+ case "ip":return c;case "ip4":case "ipv4":case "inet4":return d;case "ip6":case "ipv6":case "inet6":return e;case "idn":return m;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return g}return null};var E=e.protocol,H=e.port,C=e.hostname;e.protocol=function(a,c){if(void 0!==a&&a&&(a=a.replace(/:(\/\/)?$/,""),!a.match(b.protocol_expression)))throw new TypeError('Protocol "'+a+"\" contains characters other than [A-Z0-9.+-] or doesn't start with [A-Z]");return E.call(this,
51
+ a,c)};e.scheme=e.protocol;e.port=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError('Port "'+a+'" contains characters other than [0-9]');return H.call(this,a,c)};e.hostname=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var d={};b.parseHost(a,d);a=d.hostname}return C.call(this,a,c)};e.host=function(a,c){if(this._parts.urn)return void 0===a?"":this;
52
+ if(void 0===a)return this._parts.hostname?b.buildHost(this._parts):"";b.parseHost(a,this._parts);this.build(!c);return this};e.authority=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?b.buildAuthority(this._parts):"";b.parseAuthority(a,this._parts);this.build(!c);return this};e.userinfo=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var d=b.buildUserinfo(this._parts);return d.substring(0,
53
+ d.length-1)}"@"!==a[a.length-1]&&(a+="@");b.parseUserinfo(a,this._parts);this.build(!c);return this};e.resource=function(a,c){var d;if(void 0===a)return this.path()+this.search()+this.hash();d=b.parse(a);this._parts.path=d.path;this._parts.query=d.query;this._parts.fragment=d.fragment;this.build(!c);return this};e.subdomain=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.length-this.domain().length-
54
+ 1;return this._parts.hostname.substring(0,d)||""}d=this._parts.hostname.length-this.domain().length;d=this._parts.hostname.substring(0,d);d=new RegExp("^"+u(d));a&&"."!==a.charAt(a.length-1)&&(a+=".");a&&b.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(d,a);this.build(!c);return this};e.domain=function(a,c){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(c=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.match(/\./g);
55
+ if(d&&2>d.length)return this._parts.hostname;d=this._parts.hostname.length-this.tld(c).length-1;d=this._parts.hostname.lastIndexOf(".",d-1)+1;return this._parts.hostname.substring(d)||""}if(!a)throw new TypeError("cannot set domain empty");b.ensureValidHostname(a);!this._parts.hostname||this.is("IP")?this._parts.hostname=a:(d=new RegExp(u(this.domain())+"$"),this._parts.hostname=this._parts.hostname.replace(d,a));this.build(!c);return this};e.tld=function(a,c){if(this._parts.urn)return void 0===a?
56
+ "":this;"boolean"===typeof a&&(c=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var d=this._parts.hostname.lastIndexOf("."),d=this._parts.hostname.substring(d+1);return!0!==c&&s&&s.list[d.toLowerCase()]?s.get(this._parts.hostname)||d:d}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(s&&s.is(a))d=new RegExp(u(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(d,a);else throw new TypeError('TLD "'+a+'" contains characters other than [A-Z0-9]');else{if(!this._parts.hostname||
57
+ this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");d=new RegExp(u(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(d,a)}else throw new TypeError("cannot set TLD empty");this.build(!c);return this};e.directory=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var d=this._parts.path.length-this.filename().length-1,d=this._parts.path.substring(0,
58
+ d)||(this._parts.hostname?"/":"");return a?b.decodePath(d):d}d=this._parts.path.length-this.filename().length;d=this._parts.path.substring(0,d);d=new RegExp("^"+u(d));this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a));a&&"/"!==a.charAt(a.length-1)&&(a+="/");a=b.recodePath(a);this._parts.path=this._parts.path.replace(d,a);this.build(!c);return this};e.filename=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";
59
+ var d=this._parts.path.lastIndexOf("/"),d=this._parts.path.substring(d+1);return a?b.decodePathSegment(d):d}d=!1;"/"===a.charAt(0)&&(a=a.substring(1));a.match(/\.?\//)&&(d=!0);var e=new RegExp(u(this.filename())+"$");a=b.recodePath(a);this._parts.path=this._parts.path.replace(e,a);d?this.normalizePath(c):this.build(!c);return this};e.suffix=function(a,c){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var d=this.filename(),
60
+ e=d.lastIndexOf(".");if(-1===e)return"";d=d.substring(e+1);d=/^[a-z0-9%]+$/i.test(d)?d:"";return a?b.decodePathSegment(d):d}"."===a.charAt(0)&&(a=a.substring(1));if(d=this.suffix())e=a?new RegExp(u(d)+"$"):new RegExp(u("."+d)+"$");else{if(!a)return this;this._parts.path+="."+b.recodePath(a)}e&&(a=b.recodePath(a),this._parts.path=this._parts.path.replace(e,a));this.build(!c);return this};e.segment=function(a,c,d){var b=this._parts.urn?":":"/",e=this.path(),f="/"===e.substring(0,1),e=e.split(b);void 0!==
61
+ a&&"number"!==typeof a&&(d=c,c=a,a=void 0);if(void 0!==a&&"number"!==typeof a)throw Error('Bad segment "'+a+'", must be 0-based integer');f&&e.shift();0>a&&(a=Math.max(e.length+a,0));if(void 0===c)return void 0===a?e:e[a];if(null===a||void 0===e[a])if(q(c)){e=[];a=0;for(var h=c.length;a<h;a++)if(c[a].length||e.length&&e[e.length-1].length)e.length&&!e[e.length-1].length&&e.pop(),e.push(c[a])}else{if(c||"string"===typeof c)""===e[e.length-1]?e[e.length-1]=c:e.push(c)}else c||"string"===typeof c&&c.length?
62
+ e[a]=c:e.splice(a,1);f&&e.unshift("");return this.path(e.join(b),d)};e.segmentCoded=function(a,c,d){var e,f;"number"!==typeof a&&(d=c,c=a,a=void 0);if(void 0===c){a=this.segment(a,c,d);if(q(a))for(e=0,f=a.length;e<f;e++)a[e]=b.decode(a[e]);else a=void 0!==a?b.decode(a):void 0;return a}if(q(c))for(e=0,f=c.length;e<f;e++)c[e]=b.decode(c[e]);else c="string"===typeof c?b.encode(c):c;return this.segment(a,c,d)};var F=e.query;e.query=function(a,c){if(!0===a)return b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);
63
+ if("function"===typeof a){var d=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace),e=a.call(this,d);this._parts.query=b.buildQuery(e||d,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);this.build(!c);return this}return void 0!==a&&"string"!==typeof a?(this._parts.query=b.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!c),this):F.call(this,a,c)};e.setQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);
64
+ if("object"===typeof a)for(var f in a)t.call(a,f)&&(e[f]=a[f]);else if("string"===typeof a)e[a]=void 0!==c?c:null;else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");this._parts.query=b.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.addQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);b.addQuery(e,a,void 0===c?null:c);this._parts.query=
65
+ b.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.removeQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);b.removeQuery(e,a,c);this._parts.query=b.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(d=c);this.build(!d);return this};e.hasQuery=function(a,c,d){var e=b.parseQuery(this._parts.query,this._parts.escapeQuerySpace);
66
+ return b.hasQuery(e,a,c,d)};e.setSearch=e.setQuery;e.addSearch=e.addQuery;e.removeSearch=e.removeQuery;e.hasSearch=e.hasQuery;e.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};e.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(),
67
+ this.build(!a));return this};e.normalizeHostname=function(a){this._parts.hostname&&(this.is("IDN")&&f?this._parts.hostname=f.toASCII(this._parts.hostname):this.is("IPv6")&&g&&(this._parts.hostname=g.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!a));return this};e.normalizePort=function(a){"string"===typeof this._parts.protocol&&this._parts.port===b.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};e.normalizePath=
68
+ function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;var c,d=this._parts.path,e="",f,h;"/"!==d.charAt(0)&&(c=!0,d="/"+d);d=d.replace(/(\/(\.\/)+)|(\/\.$)/g,"/").replace(/\/{2,}/g,"/");c&&(e=d.substring(1).match(/^(\.\.\/)+/)||"")&&(e=e[0]);for(;;){f=d.indexOf("/..");if(-1===f)break;else if(0===f){d=d.substring(3);continue}h=d.substring(0,f).lastIndexOf("/");-1===h&&(h=f);d=d.substring(0,h)+d.substring(f+3)}c&&this.is("relative")&&(d=e+d.substring(1));d=b.recodePath(d);
69
+ this._parts.path=d;this.build(!a);return this};e.normalizePathname=e.normalizePath;e.normalizeQuery=function(a){"string"===typeof this._parts.query&&(this._parts.query.length?this.query(b.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a));return this};e.normalizeFragment=function(a){this._parts.fragment||(this._parts.fragment=null,this.build(!a));return this};e.normalizeSearch=e.normalizeQuery;e.normalizeHash=e.normalizeFragment;e.iso8859=function(){var a=
70
+ b.encode,c=b.decode;b.encode=escape;b.decode=decodeURIComponent;this.normalize();b.encode=a;b.decode=c;return this};e.unicode=function(){var a=b.encode,c=b.decode;b.encode=m;b.decode=unescape;this.normalize();b.encode=a;b.decode=c;return this};e.readable=function(){var a=this.clone();a.username("").password("").normalize();var c="";a._parts.protocol&&(c+=a._parts.protocol+"://");a._parts.hostname&&(a.is("punycode")&&f?(c+=f.toUnicode(a._parts.hostname),a._parts.port&&(c+=":"+a._parts.port)):c+=a.host());
71
+ a._parts.hostname&&a._parts.path&&"/"!==a._parts.path.charAt(0)&&(c+="/");c+=a.path(!0);if(a._parts.query){for(var d="",e=0,h=a._parts.query.split("&"),m=h.length;e<m;e++){var g=(h[e]||"").split("="),d=d+("&"+b.decodeQuery(g[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"));void 0!==g[1]&&(d+="="+b.decodeQuery(g[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}c+="?"+d.substring(1)}return c+=b.decodeQuery(a.hash(),!0)};e.absoluteTo=function(a){var c=this.clone(),d=["protocol","username",
72
+ "password","hostname","port"],e,f;if(this._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a instanceof b||(a=new b(a));c._parts.protocol||(c._parts.protocol=a._parts.protocol);if(this._parts.hostname)return c;for(e=0;f=d[e];e++)c._parts[f]=a._parts[f];c._parts.path?".."===c._parts.path.substring(-2)&&(c._parts.path+="/"):(c._parts.path=a._parts.path,c._parts.query||(c._parts.query=a._parts.query));"/"!==c.path().charAt(0)&&(a=a.directory(),c._parts.path=(a?
73
+ a+"/":"")+c._parts.path,c.normalizePath());c.build();return c};e.relativeTo=function(a){var c=this.clone().normalize(),d,e,f,h;if(c._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a=(new b(a)).normalize();d=c._parts;e=a._parts;f=c.path();h=a.path();if("/"!==f.charAt(0))throw Error("URI is already relative");if("/"!==h.charAt(0))throw Error("Cannot calculate a URI relative to another relative URI");d.protocol===e.protocol&&(d.protocol=null);if(d.username===
74
+ e.username&&d.password===e.password&&null===d.protocol&&null===d.username&&null===d.password&&d.hostname===e.hostname&&d.port===e.port)d.hostname=null,d.port=null;else return c.build();if(f===h)return d.path="",c.build();a=b.commonPath(c.path(),a.path());if(!a)return c.build();e=e.path.substring(a.length).replace(/[^\/]*$/,"").replace(/.*?\//g,"../");d.path=e+d.path.substring(a.length);return c.build()};e.equals=function(a){var c=this.clone();a=new b(a);var d={},e={},f={},h;c.normalize();a.normalize();
75
+ if(c.toString()===a.toString())return!0;d=c.query();e=a.query();c.query("");a.query("");if(c.toString()!==a.toString()||d.length!==e.length)return!1;d=b.parseQuery(d,this._parts.escapeQuerySpace);e=b.parseQuery(e,this._parts.escapeQuerySpace);for(h in d)if(t.call(d,h)){if(!q(d[h])){if(d[h]!==e[h])return!1}else if(!w(d[h],e[h]))return!1;f[h]=!0}for(h in e)if(t.call(e,h)&&!f[h])return!1;return!0};e.duplicateQueryParameters=function(a){this._parts.duplicateQueryParameters=!!a;return this};e.escapeQuerySpace=
76
+ function(a){this._parts.escapeQuerySpace=!!a;return this};return b});
77
+ (function(f,g){"object"===typeof exports?module.exports=g(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],g):f.URITemplate=g(f.URI,f)})(this,function(f,g){function s(b){if(s._cache[b])return s._cache[b];if(!(this instanceof s))return new s(b);this.expression=b;s._cache[b]=this;return this}function k(b){this.data=b;this.cache={}}var b=g&&g.URITemplate,u=Object.prototype.hasOwnProperty,r=s.prototype,q={"":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encode"},
78
+ "+":{prefix:"",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},"#":{prefix:"#",separator:",",named:!1,empty_name_separator:!1,encode:"encodeReserved"},".":{prefix:".",separator:".",named:!1,empty_name_separator:!1,encode:"encode"},"/":{prefix:"/",separator:"/",named:!1,empty_name_separator:!1,encode:"encode"},";":{prefix:";",separator:";",named:!0,empty_name_separator:!1,encode:"encode"},"?":{prefix:"?",separator:"&",named:!0,empty_name_separator:!0,encode:"encode"},"&":{prefix:"&",
79
+ separator:"&",named:!0,empty_name_separator:!0,encode:"encode"}};s._cache={};s.EXPRESSION_PATTERN=/\{([^a-zA-Z0-9%_]?)([^\}]+)(\}|$)/g;s.VARIABLE_PATTERN=/^([^*:]+)((\*)|:(\d+))?$/;s.VARIABLE_NAME_PATTERN=/[^a-zA-Z0-9%_]/;s.expand=function(b,f){var g=q[b.operator],m=g.named?"Named":"Unnamed",n=b.variables,e=[],t,l,k;for(k=0;l=n[k];k++)t=f.get(l.name),t.val.length?e.push(s["expand"+m](t,g,l.explode,l.explode&&g.separator||",",l.maxlength,l.name)):t.type&&e.push("");return e.length?g.prefix+e.join(g.separator):
80
+ ""};s.expandNamed=function(b,g,k,m,n,e){var t="",l=g.encode;g=g.empty_name_separator;var s=!b[l].length,q=2===b.type?"":f[l](e),r,u,C;u=0;for(C=b.val.length;u<C;u++)n?(r=f[l](b.val[u][1].substring(0,n)),2===b.type&&(q=f[l](b.val[u][0].substring(0,n)))):s?(r=f[l](b.val[u][1]),2===b.type?(q=f[l](b.val[u][0]),b[l].push([q,r])):b[l].push([void 0,r])):(r=b[l][u][1],2===b.type&&(q=b[l][u][0])),t&&(t+=m),k?t+=q+(g||r?"=":"")+r:(u||(t+=f[l](e)+(g||r?"=":"")),2===b.type&&(t+=q+","),t+=r);return t};s.expandUnnamed=
81
+ function(b,g,k,m,n){var e="",t=g.encode;g=g.empty_name_separator;var l=!b[t].length,s,q,r,u;r=0;for(u=b.val.length;r<u;r++)n?q=f[t](b.val[r][1].substring(0,n)):l?(q=f[t](b.val[r][1]),b[t].push([2===b.type?f[t](b.val[r][0]):void 0,q])):q=b[t][r][1],e&&(e+=m),2===b.type&&(s=n?f[t](b.val[r][0].substring(0,n)):b[t][r][0],e+=s,e=k?e+(g||q?"=":""):e+","),e+=q;return e};s.noConflict=function(){g.URITemplate===s&&(g.URITemplate=b);return s};r.expand=function(b){var f="";this.parts&&this.parts.length||this.parse();
82
+ b instanceof k||(b=new k(b));for(var g=0,m=this.parts.length;g<m;g++)f+="string"===typeof this.parts[g]?this.parts[g]:s.expand(this.parts[g],b);return f};r.parse=function(){var b=this.expression,f=s.EXPRESSION_PATTERN,g=s.VARIABLE_PATTERN,m=s.VARIABLE_NAME_PATTERN,n=[],e=0,k,l,r;for(f.lastIndex=0;;){l=f.exec(b);if(null===l){n.push(b.substring(e));break}else n.push(b.substring(e,l.index)),e=l.index+l[0].length;if(!q[l[1]])throw Error('Unknown Operator "'+l[1]+'" in "'+l[0]+'"');if(!l[3])throw Error('Unclosed Expression "'+
83
+ l[0]+'"');k=l[2].split(",");for(var u=0,E=k.length;u<E;u++){r=k[u].match(g);if(null===r)throw Error('Invalid Variable "'+k[u]+'" in "'+l[0]+'"');if(r[1].match(m))throw Error('Invalid Variable Name "'+r[1]+'" in "'+l[0]+'"');k[u]={name:r[1],explode:!!r[3],maxlength:r[4]&&parseInt(r[4],10)}}if(!k.length)throw Error('Expression Missing Variable(s) "'+l[0]+'"');n.push({expression:l[0],operator:l[1],variables:k})}n.length||n.push(b);this.parts=n;return this};k.prototype.get=function(b){var f=this.data,
84
+ g={type:0,val:[],encode:[],encodeReserved:[]},m;if(void 0!==this.cache[b])return this.cache[b];this.cache[b]=g;f="[object Function]"===String(Object.prototype.toString.call(f))?f(b):"[object Function]"===String(Object.prototype.toString.call(f[b]))?f[b](b):f[b];if(void 0!==f&&null!==f)if("[object Array]"===String(Object.prototype.toString.call(f))){m=0;for(b=f.length;m<b;m++)void 0!==f[m]&&null!==f[m]&&g.val.push([void 0,String(f[m])]);g.val.length&&(g.type=3)}else if("[object Object]"===String(Object.prototype.toString.call(f))){for(m in f)u.call(f,
85
+ m)&&void 0!==f[m]&&null!==f[m]&&g.val.push([m,String(f[m])]);g.val.length&&(g.type=2)}else g.type=1,g.val.push([void 0,String(f)]);return g};f.expand=function(b,g){var k=(new s(b)).expand(g);return new f(k)};return s});
86
+ (function(f,g){"object"===typeof exports?module.exports=g(require("jquery","./URI")):"function"===typeof define&&define.amd?define(["jquery","./URI"],g):g(f.jQuery,f.URI)})(this,function(f,g){function s(b){return b.replace(/([.*+?^=!:${}()|[\]\/\\])/g,"\\$1")}function k(b){var f=b.nodeName.toLowerCase();return"input"===f&&"image"!==b.type?void 0:g.domAttributes[f]}function b(b){return{get:function(g){return f(g).uri()[b]()},set:function(g,e){f(g).uri()[b](e);return e}}}function u(b,g){var e,h,l;if(!k(b)||
87
+ !g)return!1;e=g.match(A);if(!e||!e[5]&&":"!==e[2]&&!q[e[2]])return!1;l=f(b).uri();if(e[5])return l.is(e[5]);if(":"===e[2])return h=e[1].toLowerCase()+":",q[h]?q[h](l,e[4]):!1;h=e[1].toLowerCase();return r[h]?q[e[2]](l[h](),e[4],h):!1}var r={},q={"=":function(b,f){return b===f},"^=":function(b,f){return!!(b+"").match(new RegExp("^"+s(f),"i"))},"$=":function(b,f){return!!(b+"").match(new RegExp(s(f)+"$","i"))},"*=":function(b,f,e){"directory"===e&&(b+="/");return!!(b+"").match(new RegExp(s(f),"i"))},
88
+ "equals:":function(b,f){return b.equals(f)},"is:":function(b,f){return b.is(f)}};f.each("authority directory domain filename fragment hash host hostname href password path pathname port protocol query resource scheme search subdomain suffix tld username".split(" "),function(g,h){r[h]=!0;f.attrHooks["uri:"+h]=b(h)});var h=function(b,g){return f(b).uri().href(g).toString()};f.each(["src","href","action","uri","cite"],function(b,g){f.attrHooks[g]={set:h}});f.attrHooks.uri.get=function(b){return f(b).uri()};
89
+ f.fn.uri=function(b){var f=this.first(),e=f.get(0),h=k(e);if(!h)throw Error('Element "'+e.nodeName+'" does not have either property: href, src, action, cite');if(void 0!==b){var l=f.data("uri");if(l)return l.href(b);b instanceof g||(b=g(b||""))}else{if(b=f.data("uri"))return b;b=g(f.attr(h)||"")}b._dom_element=e;b._dom_attribute=h;b.normalize();f.data("uri",b);return b};g.prototype.build=function(b){if(this._dom_element)this._string=g.build(this._parts),this._deferred_build=!1,this._dom_element.setAttribute(this._dom_attribute,
90
+ this._string),this._dom_element[this._dom_attribute]=this._string;else if(!0===b)this._deferred_build=!0;else if(void 0===b||this._deferred_build)this._string=g.build(this._parts),this._deferred_build=!1;return this};var w,A=/^([a-zA-Z]+)\s*([\^\$*]?=|:)\s*(['"]?)(.+)\3|^\s*([a-zA-Z0-9]+)\s*$/;w=f.expr.createPseudo?f.expr.createPseudo(function(b){return function(f){return u(f,b)}}):function(b,f,e){return u(b,e[3])};f.expr[":"].uri=w;return f});
91
+ (function(f,g){"object"===typeof exports?module.exports=g(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],g):g(f.URI)})(this,function(f){var g=f.prototype,s=g.fragment;f.fragmentPrefix="?";var k=f._parts;f._parts=function(){var b=k();b.fragmentPrefix=f.fragmentPrefix;return b};g.fragmentPrefix=function(b){this._parts.fragmentPrefix=b;return this};g.fragment=function(b,g){var k=this._parts.fragmentPrefix,q=this._parts.fragment||"";return!0===b?q.substring(0,k.length)!==k?
92
+ {}:f.parseQuery(q.substring(k.length)):void 0!==b&&"string"!==typeof b?(this._parts.fragment=k+f.buildQuery(b),this.build(!g),this):s.call(this,b,g)};g.addFragment=function(b,g,k){var q=this._parts.fragmentPrefix,h=f.parseQuery((this._parts.fragment||"").substring(q.length));f.addQuery(h,b,g);this._parts.fragment=q+f.buildQuery(h);"string"!==typeof b&&(k=g);this.build(!k);return this};g.removeFragment=function(b,g,k){var q=this._parts.fragmentPrefix,h=f.parseQuery((this._parts.fragment||"").substring(q.length));
93
+ f.removeQuery(h,b,g);this._parts.fragment=q+f.buildQuery(h);"string"!==typeof b&&(k=g);this.build(!k);return this};g.addHash=g.addFragment;g.removeHash=g.removeFragment;return f});
core/lib/class-itsec-lib-config-file.php ADDED
@@ -0,0 +1,753 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iThemes Security config file library.
4
+ *
5
+ * Contains the ITSEC_Lib_Config_File class.
6
+ *
7
+ * @package iThemes_Security
8
+ */
9
+
10
+ /**
11
+ * iThemes Security Config File Library class.
12
+ *
13
+ * Utility class for adding, updating, and removing iThemes Security modifications from existing config files.
14
+ *
15
+ * @package iThemes_Security
16
+ * @since 1.15.0
17
+ */
18
+ class ITSEC_Lib_Config_File {
19
+ /**
20
+ * The current version of the config file format.
21
+ *
22
+ * @since 1.15.0
23
+ * @var int
24
+ */
25
+ const FORMAT_VERSION = 2;
26
+
27
+
28
+ /**
29
+ * Get the server config to be written to the config file.
30
+ *
31
+ * The server config is retrieved by the itsec_filter_SERVER_server_config_modification filter where SERVER is a
32
+ * valid server name.
33
+ *
34
+ * @since 1.15.0
35
+ *
36
+ * @return string The server config.
37
+ */
38
+ public static function get_server_config() {
39
+ $server = ITSEC_Lib_Utility::get_web_server();
40
+ $modification = apply_filters( "itsec_filter_{$server}_server_config_modification", '' );
41
+ $comment_delimiter = self::get_comment_delimiter( $server );
42
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
43
+
44
+ return $modification;
45
+ }
46
+
47
+ /**
48
+ * Get the minimal server config to be written to the config file.
49
+ *
50
+ * The server config is retrieved by the itsec_filter_SERVER_minimal_server_config_modification filter where SERVER
51
+ * is a valid server name.
52
+ *
53
+ * @since 1.15.0
54
+ *
55
+ * @return string The server config.
56
+ */
57
+ public static function get_minimal_server_config() {
58
+ $server = ITSEC_Lib_Utility::get_web_server();
59
+ $modification = apply_filters( "itsec_filter_{$server}_minimal_server_config_modification", '' );
60
+
61
+ if ( empty( $modification ) ) {
62
+ return '';
63
+ }
64
+
65
+ $comment_delimiter = self::get_comment_delimiter( $server );
66
+ $modification = "$comment_delimiter " . __( 'iThemes Security preserved the following settings as removing them could prevent the site from functioning correctly.', 'better-wp-security' ) . "\n$modification";
67
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
68
+
69
+ return $modification;
70
+ }
71
+
72
+ /**
73
+ * Update the server config file.
74
+ *
75
+ * @since 1.15.0
76
+ *
77
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
78
+ */
79
+ public static function update_server_config() {
80
+ $server = ITSEC_Lib_Utility::get_web_server();
81
+ $modification = self::get_server_config();
82
+
83
+ return self::write_server_config( $server, $modification );
84
+ }
85
+
86
+ /**
87
+ * Remove all removable settings from the server config file.
88
+ *
89
+ * @since 1.15.0
90
+ *
91
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
92
+ */
93
+ public static function reset_server_config() {
94
+ $server = ITSEC_Lib_Utility::get_web_server();
95
+ $modification = self::get_minimal_server_config();
96
+
97
+ return self::write_server_config( $server, $modification );
98
+ }
99
+
100
+ /**
101
+ * Add a modification to the server config file without having to rebuild all iThemes Security modifications.
102
+ *
103
+ * By default, this modification is wrapped in comments that allow for removal of the modification when future
104
+ * updates occur. Thus, this function should only be used if a future update will include the same changes. If the
105
+ * optional $permanent attribute is set to true, the comment wrapper will not be added to the modification.
106
+ *
107
+ * @since 1.15.0
108
+ *
109
+ * @param string $modification The modification to add to the server config file.
110
+ * @param bool $permanent Optional. Set to true to prevent iThemes Security from removing the change in the
111
+ * future. Defaults to false.
112
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
113
+ */
114
+ public static function append_server_config( $modification, $permanent = false ) {
115
+ $server = ITSEC_Lib_Utility::get_web_server();
116
+
117
+ if ( true !== $permanent ) {
118
+ $comment_delimiter = self::get_comment_delimiter( $server );
119
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
120
+ }
121
+
122
+ return self::write_server_config( $server, $modification, false );
123
+ }
124
+
125
+ /**
126
+ * Write the supplied modification to the appropriate server config file.
127
+ *
128
+ * @since 1.15.0
129
+ * @access protected
130
+ *
131
+ * @param string $server The server type of the server config file.
132
+ * @param string $modification The modification to add to the server config file.
133
+ * @param bool $clear_existing_modifications Optional. Whether or not existing modifications should be removed
134
+ * first. Defaults to true.
135
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
136
+ */
137
+ protected static function write_server_config( $server, $modification, $clear_existing_modifications = true ) {
138
+ $file_path = self::get_server_config_file_path();
139
+
140
+ if ( empty( $file_path ) ) {
141
+ return true;
142
+ // return new WP_Error( 'itsec-lib-config-file-server-config-file-updates-disabled', __( 'Updates to the server config file are disabled via a filter.', 'better-wp-security' ) );
143
+ }
144
+
145
+ return self::update( $file_path, $server, $modification, $clear_existing_modifications );
146
+ }
147
+
148
+ /**
149
+ * Get the config to be written to the wp-config.php file.
150
+ *
151
+ * The config is retrieved by the itsec_filter_wp_config_modification filter.
152
+ *
153
+ * @since 1.15.0
154
+ *
155
+ * @return string The wp-config.php config.
156
+ */
157
+ public static function get_wp_config() {
158
+ $modification = apply_filters( 'itsec_filter_wp_config_modification', '' );
159
+ $comment_delimiter = self::get_comment_delimiter( 'wp-config' );
160
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
161
+
162
+ return $modification;
163
+ }
164
+
165
+ /**
166
+ * Get the minimal config to be written to the wp-config.php file.
167
+ *
168
+ * The config is retrieved by the itsec_filter_minimal_wp_config_modification filter.
169
+ *
170
+ * @since 1.15.0
171
+ *
172
+ * @return string The wp-config.php config.
173
+ */
174
+ public static function get_minimal_wp_config() {
175
+ $modification = apply_filters( 'itsec_filter_minimal_wp_config_modification', '' );
176
+
177
+ if ( empty( $modification ) ) {
178
+ return '';
179
+ }
180
+
181
+ $comment_delimiter = self::get_comment_delimiter( 'wp-config' );
182
+ $modification = "$comment_delimiter " . __( 'iThemes Security preserved the following settings as removing them could prevent the site from functioning correctly.', 'better-wp-security' ) . "\n$modification";
183
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
184
+
185
+ return $modification;
186
+ }
187
+
188
+ /**
189
+ * Update the wp-config.php file.
190
+ *
191
+ * The modification to be written to the file is retrieved by the itsec_filter_wp_config_modification filter.
192
+ *
193
+ * @since 1.15.0
194
+ *
195
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
196
+ */
197
+ public static function update_wp_config() {
198
+ $modification = self::get_wp_config();
199
+
200
+ return self::write_wp_config( $modification );
201
+ }
202
+
203
+ /**
204
+ * Remove all removable settings from the wp-config.php file.
205
+ *
206
+ * It is possible that some settings must remain when the plugin is disabled and removed. This function retrieves
207
+ * those necessary settings via the itsec_filter_minimal_wp_config_modification filter and ensures that they are
208
+ * written to the server config file.
209
+ *
210
+ * @since 1.15.0
211
+ *
212
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
213
+ */
214
+ public static function reset_wp_config() {
215
+ $modification = self::get_minimal_wp_config();
216
+
217
+ return self::write_wp_config( $modification );
218
+ }
219
+
220
+ /**
221
+ * Add a modification to the wp-config.php file without having to rebuild all iThemes Security modifications.
222
+ *
223
+ * By default, this modification is wrapped in comments that allow for removal of the modification when future
224
+ * updates occur. Thus, this function should only be used if a future update will include the same changes. If the
225
+ * optional $permanent attribute is set to true, the comment wrapper will not be added to the modification. This
226
+ * results in iThemes Security being unable to manage the modification in the future and should only be used for
227
+ * changes that are one-time in nature and should not be undone, such as changing the Content Directory.
228
+ *
229
+ * @since 1.15.0
230
+ *
231
+ * @param string $modification The modification to add to the wp-config.php file.
232
+ * @param bool $permanent Optional. Set to true to prevent iThemes Security from removing the change in the
233
+ * future. Defaults to false.
234
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
235
+ */
236
+ public static function append_wp_config( $modification, $permanent = false ) {
237
+ if ( true !== $permanent ) {
238
+ $comment_delimiter = self::get_comment_delimiter( 'wp-config' );
239
+ $modification = self::get_prepared_modification( $modification, $comment_delimiter );
240
+ }
241
+
242
+ return self::write_wp_config( $modification, false );
243
+ }
244
+
245
+ /**
246
+ * Remove matched content from the wp-config.php file.
247
+ *
248
+ * @since 1.17.0
249
+ *
250
+ * @param array|string $patterns An array of regular expression strings or a string for a regular expression to
251
+ * match in the file.
252
+ * @return int|WP_Error Number of matches removed or a WP_Error object on error.
253
+ */
254
+ public static function remove_from_wp_config( $patterns ) {
255
+ $file_path = self::get_wp_config_file_path();
256
+
257
+ if ( empty( $file_path ) ) {
258
+ return new WP_Error( 'itsec-lib-config-file-wp-config-file-updates-disabled', __( 'Updates to <code>wp-config.php</code> are disabled via a filter.', 'better-wp-security' ) );
259
+ }
260
+
261
+ return self::remove( $file_path, $patterns );
262
+ }
263
+
264
+ /**
265
+ * Write the supplied modification to the wp-config.php file.
266
+ *
267
+ * @since 1.15.0
268
+ * @access protected
269
+ *
270
+ * @param string $modification The modification to add to the wp-config.php file.
271
+ * @param bool $clear_existing_modifications Optional. Whether or not existing modifications should be removed
272
+ * first. Defaults to true.
273
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
274
+ */
275
+ protected static function write_wp_config( $modification, $clear_existing_modifications = true ) {
276
+ $file_path = self::get_wp_config_file_path();
277
+
278
+ if ( empty( $file_path ) ) {
279
+ return new WP_Error( 'itsec-lib-config-file-wp-config-file-updates-disabled', __( 'Updates to <code>wp-config.php</code> are disabled via a filter.', 'better-wp-security' ) );
280
+ }
281
+
282
+ return self::update( $file_path, 'wp-config', $modification, $clear_existing_modifications );
283
+ }
284
+
285
+ /**
286
+ * Returns the contents of the file.
287
+ *
288
+ * @since 1.15.0
289
+ * @access protected
290
+ *
291
+ * @param string $file Config file to read.
292
+ * @return string|WP_Error The contents of the file, an empty string if the file does not exist, or a WP_Error object on error.
293
+ */
294
+ protected static function get_file_contents( $file ) {
295
+ if ( ! ITSEC_Lib_File::exists( $file ) ) {
296
+ return '';
297
+ }
298
+
299
+ $contents = ITSEC_Lib_File::read( $file );
300
+
301
+ if ( is_wp_error( $contents ) ) {
302
+ return new WP_Error( 'itsec-lib-config-file-cannot-read-file', sprintf( __( 'Unable to read %1$s due to the following error: %2$s', 'better-wp-security' ), $file, $contents->get_error_message() ) );
303
+ }
304
+
305
+ return $contents;
306
+ }
307
+
308
+ /**
309
+ * Returns the contents of the file with the iThemes Security modifications removed.
310
+ *
311
+ * @since 1.15.0
312
+ * @access protected
313
+ *
314
+ * @param string $file Config file to read.
315
+ * @param string $type The type of config file. Valid options are apache, nginx, and wp-config.
316
+ * @return string|WP_Error The contents of the file with modifications removed, an empty string if the file does not exist, or a WP_Error object on error.
317
+ */
318
+ protected static function get_file_contents_without_modification( $file, $type ) {
319
+ $contents = self::get_file_contents( $file );
320
+
321
+ if ( is_wp_error( $contents ) ) {
322
+ return $contents;
323
+ }
324
+
325
+
326
+ // Contents of just whitespace are treated as empty.
327
+ if ( preg_match( '/^\s+$/', $contents ) ) {
328
+ return '';
329
+ }
330
+
331
+
332
+ $format_version = 0;
333
+
334
+ // Attempt to retrieve config file details from the contents.
335
+ if ( preg_match( '/iThemes\s+Security\s+Config\s+Details:\s+([^\s]+)/', $contents, $match ) ) {
336
+ $details = explode( ':', $match[1] );
337
+
338
+ if ( isset( $details[0] ) && ( (string) intval( $details[0] ) === $details[0] ) ) {
339
+ $format_version = intval( $details[0] );
340
+ }
341
+ }
342
+
343
+
344
+ $placeholder = self::get_placeholder();
345
+
346
+ // Ensure that the generated placeholder can be uniquely identified in the contents.
347
+ while ( false !== strpos( $contents, $placeholder ) ) {
348
+ $placeholder = self::get_placeholder();
349
+ }
350
+
351
+
352
+ // Create a set of regex patterns to identify existing iThemes Security modifications.
353
+ $comment_delimiter = self::get_comment_delimiter( $type );
354
+ $quoted_comment_delimiter = preg_quote( $comment_delimiter, '/' );
355
+ $line_ending = self::get_line_ending( $contents );
356
+
357
+ $patterns = array(
358
+ "$quoted_comment_delimiter+\s*BEGIN\s+iThemes\s+Security.*?$quoted_comment_delimiter+\s*END\s+iThemes\s+Security",
359
+ "$quoted_comment_delimiter+\s*BEGIN\s+Better\s+WP\s+Security.*?$quoted_comment_delimiter+\s*END\s+Better\s+WP\s+Security",
360
+ );
361
+
362
+ // Remove matched content.
363
+ foreach ( $patterns as $pattern ) {
364
+ $contents = preg_replace( "/\s*{$pattern}[^\r\n]*\s*/is", "$line_ending$placeholder", $contents );
365
+ }
366
+
367
+
368
+ if ( 'wp-config' === $type ) {
369
+ // Special treatment for wp-config.php config data.
370
+
371
+ // If the format is old or could not be detected, assume that cleanup of old modifications is required.
372
+ if ( version_compare( $format_version, self::FORMAT_VERSION, '<' ) ) {
373
+ // This code is clumsy, but it's the only way to remove the modifications given that the start and end
374
+ // were not indicated in older versions.
375
+
376
+ $contents = preg_replace( '/(<\?(?:php)?)?.+BWPS_FILECHECK.+/', "$1$line_ending$placeholder", $contents );
377
+ $contents = preg_replace( '/(<\?(?:php)?)?.+BWPS_AWAY_MODE.+/', "$1$line_ending$placeholder", $contents );
378
+
379
+ if ( preg_match( '|//\s*The entry below were created by iThemes Security to disable the file editor|', $contents ) ) {
380
+ $contents = preg_replace( '%(<\?(?:php)?)?.*//\s*The entry below were created by iThemes Security to disable the file editor.*(?:\r\n|\r|\n)%', "$1$line_ending$placeholder", $contents );
381
+ $contents = preg_replace( '/(<\?(?:php)?)?.+DISALLOW_FILE_EDIT.+(?:\r\r\n|\r\n|\r|\n)/', "$1$line_ending$placeholder", $contents );
382
+ }
383
+
384
+ if ( preg_match( '|//\s*The entries below were created by iThemes Security to enforce SSL|', $contents ) ) {
385
+ $contents = preg_replace( '%(<\?(?:php)?)?.*//\s*The entries below were created by iThemes Security to enforce SSL.*(?:\r\n|\r|\n)%', "$1$line_ending$placeholder", $contents );
386
+ $contents = preg_replace( '/(<\?(?:php)?)?.+FORCE_SSL_LOGIN.+(?:\r\r\n|\r\n|\r|\n)/', "$1$line_ending$placeholder", $contents );
387
+ $contents = preg_replace( '/(<\?(?:php)?)?.+FORCE_SSL_ADMIN.+(?:\r\r\n|\r\n|\r|\n)/', "$1$line_ending$placeholder", $contents );
388
+ }
389
+ }
390
+ }
391
+
392
+
393
+ // Remove adjacent placeholders.
394
+ $contents = preg_replace( "/$placeholder(?:\s*$placeholder)+/", $placeholder, $contents );
395
+
396
+ // Remove whitespace from around the placeholders.
397
+ $contents = preg_replace( "/\s*$placeholder\s*/", $placeholder, $contents );
398
+
399
+ // Placeholders at the beginning or end of the contents do not need to have newlines added.
400
+ $contents = preg_replace( "/^$placeholder|$placeholder$/", '', $contents );
401
+
402
+ // Remaining placeholders are replaced with two newlines to leave a gap between sections of remaining contents.
403
+ $contents = preg_replace( "/$placeholder/", "$line_ending$line_ending", $contents );
404
+
405
+
406
+ return $contents;
407
+ }
408
+
409
+ /**
410
+ * Update modifications in the supplied configuration file.
411
+ *
412
+ * If a blank $contents argument is supplied, all modifications will be removed.
413
+ *
414
+ * @since 1.15.0
415
+ * @access protected
416
+ *
417
+ * @param string $file Config file to update.
418
+ * @param string $type The type of config file. Valid options are apache, nginx, and
419
+ * wp-config.
420
+ * @param string $modification The contents to add or update the file with. If an empty string is
421
+ * supplied, all iThemes Security modifications will be removed.
422
+ * @param bool $clear_existing_modifications Optional. Whether or not existing modifications should be removed
423
+ * first. Defaults to true.
424
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
425
+ */
426
+ protected static function update( $file, $type, $modification, $clear_existing_modifications = true ) {
427
+ // Check to make sure that the settings give permission to write files.
428
+ if ( false === apply_filters( 'itsec_filter_can_write_to_files', false ) ) {
429
+ $display_file = str_replace( '\\', '/', $file );
430
+ $abspath = str_replace( '\\', '/', ABSPATH );
431
+ $display_file = preg_replace( '/^' . preg_quote( $abspath, '/' ) . '/', '', $display_file );
432
+ $display_file = ltrim( $display_file, '/' );
433
+
434
+ return new WP_Error( 'itsec-file-writes-are-disabled', sprintf( __( 'The "Write to Files" setting is disabled. Manual configuration for the <code>%s</code> file can be found on the Security > Dashboard page.', 'better-wp-security' ), $display_file ) );
435
+ }
436
+
437
+
438
+ if ( $clear_existing_modifications ) {
439
+ $contents = self::get_file_contents_without_modification( $file, $type );
440
+ } else {
441
+ $contents = self::get_file_contents( $file );
442
+ }
443
+
444
+ if ( is_wp_error( $contents ) ) {
445
+ return $contents;
446
+ }
447
+
448
+
449
+ $modification = ltrim( $modification, "\x0B\r\n\0" );
450
+ $modification = rtrim( $modification, " \t\x0B\r\n\0" );
451
+
452
+ if ( empty( $modification ) ) {
453
+ // If there isn't a new modification, write the content without any modification and return the result.
454
+
455
+ if ( empty( $contents ) ) {
456
+ $contents = PHP_EOL;
457
+ }
458
+
459
+ return ITSEC_Lib_File::write( $file, $contents );
460
+ }
461
+
462
+
463
+ $placeholder = self::get_placeholder();
464
+
465
+ // Ensure that the generated placeholder can be uniquely identified in the contents.
466
+ while ( false !== strpos( $contents, $placeholder ) ) {
467
+ $placeholder = self::get_placeholder();
468
+ }
469
+
470
+
471
+ if ( 'wp-config' === $type ) {
472
+ // Put the placeholder at the beginning of the file, after the <?php tag.
473
+ $contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(?:\r\r\n|\r\n|\r|\n)/', "\${1}$placeholder", $contents, 1 );
474
+
475
+ if ( false === strpos( $contents, $placeholder ) ) {
476
+ $contents = preg_replace( '/^(.*?<\?(?:php)?)\s*(.+(?:\r\r\n|\r\n|\r|\n))/', "\${1}$placeholder$2", $contents, 1 );
477
+ }
478
+
479
+ if ( false === strpos( $contents, $placeholder ) ) {
480
+ $contents = "<?php$placeholder?" . ">$contents";
481
+ }
482
+ } else {
483
+ // Apache and nginx server config files.
484
+ $contents = "$placeholder$contents";
485
+ }
486
+
487
+
488
+ // Pad away from existing sections when adding iThemes Security modifications.
489
+ $line_ending = self::get_line_ending( $contents );
490
+
491
+ while ( ! preg_match( "/(?:^|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))$placeholder/", $contents ) ) {
492
+ $contents = preg_replace( "/$placeholder/", "$line_ending$placeholder", $contents );
493
+ }
494
+ while ( ! preg_match( "/$placeholder(?:$|(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n)(?:(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n))/", $contents ) ) {
495
+ $contents = preg_replace( "/$placeholder/", "$placeholder$line_ending", $contents );
496
+ }
497
+
498
+
499
+ // Ensure that the file ends in a newline if the placeholder is at the end.
500
+ $contents = preg_replace( "/$placeholder$/", "$placeholder$line_ending", $contents );
501
+
502
+ if ( ! empty( $modification ) ) {
503
+ // Normalize line endings of the modification to match the file's line endings.
504
+ $modification = ITSEC_Lib_Utility::normalize_line_endings( $modification, $line_ending );
505
+
506
+ // Exchange the placeholder with the modification.
507
+ $contents = preg_replace( "/$placeholder/", $modification, $contents );
508
+ }
509
+
510
+ // Write the new contents to the file and return the results.
511
+ return ITSEC_Lib_File::write( $file, $contents );
512
+ }
513
+
514
+ /**
515
+ * Add the identifying comments to the modification to identify them as coming from iThemes Security.
516
+ *
517
+ * @since 1.15.0
518
+ * @access protected
519
+ *
520
+ * @param string $file Config file to update.
521
+ * @param string $type The type of config file. Valid options are apache, nginx, and wp-config.
522
+ * @param string $modification The contents to add or update the file with. If an empty string is supplied, all
523
+ * iThemes Security modifications will be removed.
524
+ * @return bool|WP_Error Boolean true on success or a WP_Error object otherwise.
525
+ */
526
+ protected static function get_prepared_modification( $modification, $comment_delimiter ) {
527
+ // Trim off unwanted whitespace around modification.
528
+ $modification = ltrim( $modification, "\x0B\r\n\0" );
529
+ $modification = rtrim( $modification, " \t\x0B\r\n\0" );
530
+
531
+ if ( empty( $modification ) ) {
532
+ // Do not wrap an empty modification.
533
+ return '';
534
+ }
535
+
536
+
537
+ // Update the modification to have the beginning and ending comments in order to identify the section as being
538
+ // added by iThemes Security.
539
+ $supplied_modification = $modification;
540
+ $modification = "$comment_delimiter BEGIN iThemes Security - " . __( 'Do not modify or remove this line', 'better-wp-security' ) . "\n";
541
+ $modification .= "$comment_delimiter iThemes Security Config Details: " . self::FORMAT_VERSION . "\n";
542
+ $modification .= "$supplied_modification\n";
543
+ $modification .= "$comment_delimiter END iThemes Security - " . __( 'Do not modify or remove this line', 'better-wp-security' );
544
+
545
+ return $modification;
546
+ }
547
+
548
+ /**
549
+ * Remove matched content from the supplied file.
550
+ *
551
+ * @since 1.17.0
552
+ *
553
+ * @param string $file Config file to update.
554
+ * @param array|string $patterns An array of regular expression strings or a string for a regular expression to
555
+ * match in the file.
556
+ * @return int|WP_Error Number of matches removed or a WP_Error object on error.
557
+ */
558
+ protected static function remove( $file, $patterns ) {
559
+ $replacements = array();
560
+
561
+ foreach ( (array) $patterns as $pattern ) {
562
+ $replacements[$pattern] = '';
563
+ }
564
+
565
+
566
+ return self::replace( $file, $replacements );
567
+ }
568
+
569
+ /**
570
+ * Replace matched content in a file.
571
+ *
572
+ * @since 1.17.0
573
+ *
574
+ * @param string $file Config file to update.
575
+ * @param array|string $replacements An array of regular expression string indexes with replacement string values.
576
+ * @return int|WP_Error Number of replacements made or a WP_Error object on error.
577
+ */
578
+ protected static function replace( $file, $replacements ) {
579
+ $contents = self::get_file_contents( $file );
580
+
581
+ if ( is_wp_error( $contents ) ) {
582
+ return $contents;
583
+ }
584
+
585
+
586
+ $total = 0;
587
+
588
+ foreach ( (array) $replacements as $pattern => $replacement ) {
589
+ $contents = preg_replace( $pattern, $replacement, $contents, -1, $count );
590
+ $total += $count;
591
+ }
592
+
593
+ // Write the new contents to the file and return the results.
594
+ return ITSEC_Lib_File::write( $file, $contents );
595
+ }
596
+
597
+ /**
598
+ * Get the appropriate comment delimiter for a specific type of config.
599
+ *
600
+ * @since 1.15.0
601
+ * @access protected
602
+ *
603
+ * @param string $type The type of config that the comment will be used for.
604
+ * @return string The comment delimiter.
605
+ */
606
+ protected static function get_comment_delimiter( $type ) {
607
+ if ( 'wp-config' === $type ) {
608
+ $delimiter = '//';
609
+ } else {
610
+ $delimiter = '#';
611
+ }
612
+
613
+ $delimiter = apply_filters( 'itsec_filter_server_config_file_comment_delimiter', $delimiter, $type );
614
+
615
+ return $delimiter;
616
+ }
617
+
618
+ /**
619
+ * Internal class function to get the primary line ending found in the contents.
620
+ *
621
+ * @since 1.15.0
622
+ * @access protected
623
+ *
624
+ * @param string $contents The contents to count line endings in.
625
+ * @return string The line ending.
626
+ */
627
+ protected static function get_line_ending( $contents ) {
628
+ if ( empty( $contents ) ) {
629
+ return PHP_EOL;
630
+ }
631
+
632
+ $count["\n"] = preg_match_all( "/(?<!\r)\n/", $contents, $matches );
633
+ $count["\r"] = preg_match_all( "/\r(?!\n)/", $contents, $matches );
634
+ $count["\r\n"] = preg_match_all( "/(?<!\r)\r\n/", $contents, $matches );
635
+ $count["\r\r\n"] = preg_match_all( "/\r\r\n/", $contents, $matches );
636
+
637
+ if ( 0 == array_sum( $count ) ) {
638
+ return PHP_EOL;
639
+ }
640
+
641
+ $maxes = array_keys( $count, max( $count ) );
642
+
643
+ if ( in_array( "\r\r\n", $maxes ) ) {
644
+ return "\r\r\n";
645
+ }
646
+
647
+ return $maxes[0];
648
+ }
649
+
650
+ /**
651
+ * Internal class function to get a random string to use as a placeholder.
652
+ *
653
+ * @since 1.15.0
654
+ * @access protected
655
+ *
656
+ * @return string Returns a random string of characters.
657
+ */
658
+ protected static function get_placeholder() {
659
+ $characters = str_split( 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' );
660
+
661
+ $string = '';
662
+
663
+ for ( $x = 0; $x < 100; $x++ ) {
664
+ $string .= array_rand( $characters );
665
+ }
666
+
667
+ return $string;
668
+ }
669
+
670
+ /**
671
+ * Get full file path to the server's config file for the site.
672
+ *
673
+ * Customize the returned value with the itsec_filter_server_config_file_path filter. Filter the value to a blank
674
+ * string ("") in order to disable modifications to this file.
675
+ *
676
+ * @since 1.15.0
677
+ *
678
+ * @return string Full path to the server config file or a blank string if modifications for the file are disabled.
679
+ */
680
+ public static function get_server_config_file_path() {
681
+ $file = self::get_default_server_config_file_name();
682
+
683
+ if ( empty( $file ) ) {
684
+ return '';
685
+ }
686
+
687
+ require_once( ABSPATH . 'wp-admin/includes/file.php' );
688
+
689
+ $home_path = get_home_path();
690
+ $file_path = $home_path . $file;
691
+ $file_path = apply_filters( 'itsec_filter_server_config_file_path', $file_path, $file );
692
+
693
+ return $file_path;
694
+ }
695
+
696
+ /**
697
+ * Get the default name for server config files based upon the web server.
698
+ *
699
+ * Customize the returned value with the itsec_filter_default_server_config_file_name filter. This filter can be
700
+ * used to change the name of the config file used for this server, add support for additional server types (Apache
701
+ * and nginx are supported by default), or to disable modifications for the active server type by returning a blank
702
+ * string ("").
703
+ *
704
+ * @since 1.15.0
705
+ * @access protected
706
+ *
707
+ * @return string|bool File name of the config file used for the server, a blank string if modifications for the
708
+ * server config file are disabled, or a boolean false if the server is not recognized.
709
+ */
710
+ protected static function get_default_server_config_file_name() {
711
+ $server = ITSEC_Lib_Utility::get_web_server();
712
+
713
+ $defaults = array(
714
+ 'apache' => '.htaccess',
715
+ 'litespeed' => '.htaccess',
716
+ 'nginx' => 'nginx.conf',
717
+ );
718
+
719
+ if ( isset( $defaults[$server] ) ) {
720
+ $name = $defaults[$server];
721
+ } else {
722
+ $name = false;
723
+ }
724
+
725
+ return apply_filters( 'itsec_filter_default_server_config_file_name', $name, $server );
726
+ }
727
+
728
+ /**
729
+ * Get full file path to the site's wp-config.php file.
730
+ *
731
+ * Customize the returned value with the itsec_filter_wp_config_file_path filter. Filter the value to a blank string
732
+ * ("") in order to disable modifications to this file.
733
+ *
734
+ * @since 1.15.0
735
+ *
736
+ * @return string Full path to the wp-config.php file or a blank string if modifications for the file are disabled.
737
+ */
738
+ public static function get_wp_config_file_path() {
739
+ if ( file_exists( ABSPATH . 'wp-config.php') ) {
740
+ $path = ABSPATH . 'wp-config.php';
741
+ } else if ( file_exists( dirname( ABSPATH ) . '/wp-config.php' ) && ! file_exists( dirname( ABSPATH ) . '/wp-settings.php' ) ) {
742
+ $path = dirname( ABSPATH ) . '/wp-config.php';
743
+ } else {
744
+ $path = ABSPATH . 'wp-config.php';
745
+ }
746
+
747
+ return apply_filters( 'itsec_filter_wp_config_file_path', $path );
748
+ }
749
+ }
750
+
751
+
752
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-utility.php' );
753
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-file.php' );
core/lib/class-itsec-lib-directory.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iThemes Security directory library.
4
+ *
5
+ * Contains the ITSEC_Lib_Directory class.
6
+ *
7
+ * @package iThemes_Security
8
+ */
9
+
10
+ /**
11
+ * iThemes Security Directory Library class.
12
+ *
13
+ * Utility class for managing directories.
14
+ *
15
+ * @package iThemes_Security
16
+ * @since 1.15.0
17
+ */
18
+ class ITSEC_Lib_Directory {
19
+ /**
20
+ * Get a listing of files and subdirectories contained in the supplied directory.
21
+ *
22
+ * @since 1.15.0
23
+ *
24
+ * @return array|WP_Error An array of the files and directories on success or a WP_Error object if an error occurs.
25
+ */
26
+ public static function read( $dir ) {
27
+ $callable = array();
28
+
29
+ if ( ITSEC_Lib_Utility::is_callable_function( 'opendir' ) && ITSEC_Lib_Utility::is_callable_function( 'readdir' ) && ITSEC_Lib_Utility::is_callable_function( 'closedir' ) ) {
30
+ $callable[] = 'opendir';
31
+ }
32
+ if ( ITSEC_Lib_Utility::is_callable_function( 'glob' ) ) {
33
+ $callable[] = 'glob';
34
+ }
35
+
36
+ if ( empty( $callable ) ) {
37
+ return new WP_Error( 'itsec-lib-directory-read-no-callable-functions', sprintf( __( '%s could not be read. Both the opendir/readdir/closedir and glob functions are disabled on the server.', 'better-wp-security' ), $dir ) );
38
+ }
39
+
40
+
41
+ if ( in_array( 'opendir', $callable ) ) {
42
+ if ( false !== ( $dh = opendir( $dir ) ) ) {
43
+ $files = array();
44
+
45
+ while ( false !== ( $file = readdir( $dh ) ) ) {
46
+ if ( in_array( basename( $file ), array( '.', '..' ) ) ) {
47
+ continue;
48
+ }
49
+
50
+ $files[] = "$dir/$file";
51
+ }
52
+
53
+ closedir( $dh );
54
+
55
+ sort( $files );
56
+
57
+ return $files;
58
+ }
59
+ }
60
+
61
+ if ( ITSEC_Lib_Utility::is_callable_function( 'glob' ) ) {
62
+ $visible = glob( "$dir/*" );
63
+ $hidden = glob( "$dir/.*" );
64
+
65
+ if ( false !== $visible || false !== $hidden ) {
66
+ if ( false === $visible ) {
67
+ $visible = array();
68
+ }
69
+ if ( false === $hidden ) {
70
+ $hidden = array();
71
+ }
72
+
73
+ $files = array_merge( $visible, $hidden );
74
+
75
+ foreach ( $files as $index => $file ) {
76
+ if ( in_array( basename( $file ), array( '.', '..' ) ) ) {
77
+ unset( $files[$index] );
78
+ }
79
+ }
80
+
81
+ sort( $files );
82
+
83
+ return $files;
84
+ }
85
+ }
86
+
87
+
88
+ return new WP_Error( 'itsec-lib-directory-read-cannot-read', sprintf( __( '%s could not be read due to an unknown error.', 'better-wp-security' ), $dir ) );
89
+ }
90
+
91
+ /**
92
+ * Determine if the supplied directory exists.
93
+ *
94
+ * @since 1.15.0
95
+ *
96
+ * @param string $dir Full path to the directory to test.
97
+ * @return bool|WP_Error Boolean true if it exists, false if it does not
98
+ */
99
+ public static function is_dir( $dir ) {
100
+ $dir = rtrim( $dir, '/' );
101
+ @clearstatcache( true, $dir );
102
+
103
+ return @is_dir( $dir );
104
+ }
105
+
106
+ /**
107
+ * Create the supplied directory.
108
+ *
109
+ * @since 1.15.0
110
+ *
111
+ * @param string $dir Full path to the directory to create.
112
+ * @return bool|WP_Error Boolean true if it is created successfully, WP_Error object otherwise.
113
+ */
114
+ public static function create( $dir ) {
115
+ $dir = rtrim( $dir, '/' );
116
+
117
+ if ( self::is_dir( $dir ) ) {
118
+ return true;
119
+ }
120
+
121
+ if ( ITSEC_Lib_File::exists( $dir ) ) {
122
+ return new WP_Error( 'itsec-lib-directory-create-file-exists', sprintf( __( 'The directory %s could not be created as a file with that name already exists.', 'better-wp-security' ), $dir ) );
123
+ }
124
+
125
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'mkdir' ) ) {
126
+ return new WP_Error( 'itsec-lib-directory-create-mkdir-is-disabled', sprintf( __( 'The directory %s could not be created as the mkdir() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $dir ) );
127
+ }
128
+
129
+
130
+ $parent = dirname( $dir );
131
+
132
+ while ( ! empty( $parent ) && ( ! self::is_dir( $parent ) ) ) {
133
+ $parent = dirname( $parent );
134
+ }
135
+
136
+ if ( empty( $parent ) ) {
137
+ return new WP_Error( 'itsec-lib-directory-create-unable-to-find-parent', sprintf( __( 'The directory %s could not be created as an existing parent directory could not be found.', 'better-wp-security' ), $dir ) );
138
+ }
139
+
140
+
141
+ $perms = self::get_permissions( $parent );
142
+
143
+ if ( ! is_int( $perms ) ) {
144
+ $perms = 0755;
145
+ }
146
+
147
+ $cached_umask = umask( 0 );
148
+ $result = @mkdir( $dir, $perms, true );
149
+ umask( $cached_umask );
150
+
151
+ if ( $result ) {
152
+ return true;
153
+ }
154
+
155
+ return new WP_Error( 'itsec-lib-directory-create-failed', sprintf( __( 'The directory %s could not be created due to an unknown error. This could be due to a permissions issue.', 'better-wp-security' ), $dir ) );
156
+ }
157
+
158
+ /**
159
+ * Recursively remove the supplied directory.
160
+ *
161
+ * @since 1.15.0
162
+ *
163
+ * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
164
+ */
165
+ public static function remove( $dir ) {
166
+ if ( ! ITSEC_Lib_File::exists( $dir ) ) {
167
+ return true;
168
+ }
169
+
170
+
171
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'rmdir' ) ) {
172
+ return new WP_Error( 'itsec-lib-directory-remove-rmdir-is-disabled', sprintf( __( 'The directory %s could not be removed as the rmdir() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $dir ) );
173
+ }
174
+
175
+
176
+ $files = self::read( $dir );
177
+
178
+ if ( is_wp_error( $files ) ) {
179
+ return new WP_Error( 'itsec-lib-directory-remove-read-error', sprintf( __( 'Unable to remove %1$s due to the following error: %2$s', 'better-wp-security' ), $dir, $files->get_error_message() ) );
180
+ }
181
+
182
+ foreach ( $files as $file ) {
183
+ if ( ITSEC_Lib_File::is_file( $file ) ) {
184
+ ITSEC_Lib_File::remove( $file );
185
+ } else if ( self::is_dir( $file ) ) {
186
+ self::remove( $file );
187
+ }
188
+ }
189
+
190
+ $result = rmdir( $dir );
191
+ @clearstatcache( true, $dir );
192
+
193
+ if ( $result ) {
194
+ return true;
195
+ }
196
+
197
+ return new WP_Error( 'itsec-lib-directory-remove-unknown-error', sprintf( __( 'Unable to remove %s due to an unknown error.', 'better-wp-security' ), $dir ) );
198
+ }
199
+
200
+ /**
201
+ * Get file permissions from the requested directory.
202
+ *
203
+ * If the directory permissions cannot be read, a default value of 0644 will be returned.
204
+ *
205
+ * @since 1.15.0
206
+ *
207
+ * @param string $dir Full path to the file to retrieve permissions from.
208
+ * @return int|WP_Error The permissions as an int or a WP_Error object if an error occurs.
209
+ */
210
+ public static function get_permissions( $dir ) {
211
+ if ( ! self::is_dir( $dir ) ) {
212
+ return new WP_Error( 'itsec-lib-dir-get-permissions-missing-dir', sprintf( __( 'Permissions for the directory %s could not be read as the directory could not be found.', 'better-wp-security' ), $dir ) );
213
+ }
214
+
215
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'fileperms' ) ) {
216
+ return new WP_Error( 'itsec-lib-directory-get-permissions-fileperms-is-disabled', sprintf( __( 'Permissions for the directory %s could not be read as the fileperms() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $dir ) );
217
+ }
218
+
219
+
220
+ $dir = rtrim( $dir, '/' );
221
+ @clearstatcache( true, $dir );
222
+
223
+ return fileperms( $dir ) & 0777;
224
+ }
225
+
226
+ /**
227
+ * Get default file permissions to use for new files.
228
+ *
229
+ * @since 1.15.0
230
+ * @uses FS_CHMOD_DIR Define that sets default file permissions.
231
+ *
232
+ * @return int|WP_Error The default permissions as an int or a WP_Error object if an error occurs.
233
+ */
234
+ public static function get_default_permissions() {
235
+ if ( defined( 'FS_CHMOD_DIR' ) ) {
236
+ return FS_CHMOD_DIR;
237
+ }
238
+
239
+ $perms = self::get_permissions( ABSPATH );
240
+
241
+ if ( ! is_wp_error( $perms ) ) {
242
+ return $perms;
243
+ }
244
+
245
+ return 0755;
246
+ }
247
+
248
+ /**
249
+ * Change directory permissions.
250
+ *
251
+ * @since 1.15.0
252
+ *
253
+ * @param string $dir Full path to the directory to change permissions for.
254
+ * @param int $perms New permissions to set.
255
+ * @return bool|WP_Error Boolean true if successful, false if not successful, or WP_Error if the chmod() function
256
+ * is unavailable.
257
+ */
258
+ public static function chmod( $dir, $perms ) {
259
+ return ITSEC_Lib_File::chmod( $dir, $perms );
260
+ }
261
+ }
262
+
263
+
264
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-utility.php' );
265
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-file.php' );
core/lib/class-itsec-lib-file.php ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iThemes Security file library.
4
+ *
5
+ * Contains the ITSEC_Lib_File class.
6
+ *
7
+ * @package iThemes_Security
8
+ */
9
+
10
+ /**
11
+ * iThemes Security File Library class.
12
+ *
13
+ * Utility class for managing files.
14
+ *
15
+ * @package iThemes_Security
16
+ * @since 1.15.0
17
+ */
18
+ class ITSEC_Lib_File {
19
+ /**
20
+ * Read requested file and return the contents.
21
+ *
22
+ * @since 1.15.0
23
+ *
24
+ * @param string $file Full path to config file to update.
25
+ * @return string|WP_Error String of the file contents or a WP_Error object otherwise.
26
+ */
27
+ public static function read( $file ) {
28
+ if ( ! self::is_file( $file ) ) {
29
+ return new WP_Error( 'itsec-lib-file-read-non-file', sprintf( __( '%s could not be read. It does not appear to be a file.', 'better-wp-security' ), $file ) );
30
+ }
31
+
32
+
33
+ $callable = array();
34
+
35
+ if ( ITSEC_Lib_Utility::is_callable_function( 'file_get_contents' ) ) {
36
+ $callable[] = 'file_get_contents';
37
+ }
38
+ if ( ITSEC_Lib_Utility::is_callable_function( 'fopen' ) && ITSEC_Lib_Utility::is_callable_function( 'feof' ) && ITSEC_Lib_Utility::is_callable_function( 'fread' ) && ITSEC_Lib_Utility::is_callable_function( 'flock' ) ) {
39
+ $callable[] = 'fopen';
40
+ }
41
+
42
+ if ( empty( $callable ) ) {
43
+ return new WP_Error( 'itsec-lib-file-read-no-callable-functions', sprintf( __( '%s could not be read. Both the fopen/feof/fread/flock and file_get_contents functions are disabled on the server.', 'better-wp-security' ), $file ) );
44
+ }
45
+
46
+
47
+ $contents = false;
48
+
49
+ // Different permissions to try in case the starting set of permissions are prohibiting read.
50
+ $trial_perms = array(
51
+ false,
52
+ 0644,
53
+ 0664,
54
+ 0666,
55
+ );
56
+
57
+
58
+ foreach ( $trial_perms as $perms ) {
59
+ if ( false !== $perms ) {
60
+ if ( ! isset( $original_file_perms ) ) {
61
+ $original_file_perms = self::get_permissions( $file );
62
+ }
63
+
64
+ self::chmod( $file, $perms );
65
+ }
66
+
67
+
68
+
69
+
70
+ if ( in_array( 'fopen', $callable ) ) {
71
+ if ( false !== ( $fh = fopen( $file, 'rb' ) ) ) {
72
+ flock( $fh, LOCK_SH );
73
+
74
+ $contents = '';
75
+
76
+ while ( ! feof( $fh ) ) {
77
+ $contents .= fread( $fh, 1024 );
78
+ }
79
+
80
+ flock( $fh, LOCK_UN );
81
+ fclose( $fh );
82
+ }
83
+ }
84
+
85
+ if ( ( false === $contents ) && in_array( 'file_get_contents', $callable ) ) {
86
+ $contents = file_get_contents( $file );
87
+ }
88
+
89
+
90
+ if ( false !== $contents ) {
91
+ if ( isset( $original_file_perms ) && is_int( $original_file_perms ) ) {
92
+ // Reset the original file permissions if they were modified.
93
+ self::chmod( $file, $original_file_perms );
94
+ }
95
+
96
+ return $contents;
97
+ }
98
+ }
99
+
100
+
101
+ return new WP_Error( 'itsec-lib-file-read-cannot-read', sprintf( __( '%s could not be read due to an unknown error.', 'better-wp-security' ), $file ) );
102
+ }
103
+
104
+ /**
105
+ * Update or append the requested file with the supplied contents.
106
+ *
107
+ * @since 1.15.0
108
+ *
109
+ * @param string $file Full path to config file to update.
110
+ * @param string $contents Contents to write to the file.
111
+ * @param bool $append Optional. Set to true to append contents to the file. Defaults to false.
112
+ * @return bool|WP_Error Boolean true on success, WP_Error object otherwise.
113
+ */
114
+ public static function write( $file, $contents, $append = false ) {
115
+ $callable = array();
116
+
117
+ if ( ITSEC_Lib_Utility::is_callable_function( 'fopen' ) && ITSEC_Lib_Utility::is_callable_function( 'fwrite' ) && ITSEC_Lib_Utility::is_callable_function( 'flock' ) ) {
118
+ $callable[] = 'fopen';
119
+ }
120
+ if ( ITSEC_Lib_Utility::is_callable_function( 'file_put_contents' ) ) {
121
+ $callable[] = 'file_put_contents';
122
+ }
123
+
124
+ if ( empty( $callable ) ) {
125
+ return new WP_Error( 'itsec-lib-file-write-no-callable-functions', sprintf( __( '%s could not be written. Both the fopen/fwrite/flock and file_put_contents functions are disabled on the server. This is a server configuration issue that must be resolved before iThemes Security can write files.', 'better-wp-security' ), $file ) );
126
+ }
127
+
128
+
129
+ if ( ITSEC_Lib_Directory::is_dir( $file ) ) {
130
+ return new WP_Error( 'itsec-lib-file-write-path-exists-as-directory', sprintf( __( '%s could not be written as a file. The requested path already exists as a directory. The directory must be removed or a new file name must be chosen before the file can be written.', 'better-wp-security' ), $file ) );
131
+ }
132
+
133
+ if ( ! ITSEC_Lib_Directory::is_dir( dirname( $file ) ) ) {
134
+ $result = ITSEC_Lib_Directory::create( dirname( $file ) );
135
+
136
+ if ( is_wp_error( $result ) ) {
137
+ return $result;
138
+ }
139
+ }
140
+
141
+
142
+ $file_existed = self::is_file( $file );
143
+ $success = false;
144
+
145
+ // Different permissions to try in case the starting set of permissions are prohibiting write.
146
+ $trial_perms = array(
147
+ false,
148
+ 0644,
149
+ 0664,
150
+ 0666,
151
+ );
152
+
153
+
154
+ foreach ( $trial_perms as $perms ) {
155
+ if ( false !== $perms ) {
156
+ if ( ! isset( $original_file_perms ) ) {
157
+ $original_file_perms = self::get_permissions( $file );
158
+ }
159
+
160
+ self::chmod( $file, $perms );
161
+ }
162
+
163
+ if ( in_array( 'fopen', $callable ) ) {
164
+ if ( $append ) {
165
+ $mode = 'ab';
166
+ } else {
167
+ $mode = 'wb';
168
+ }
169
+
170
+ if ( false !== ( $fh = @fopen( $file, $mode ) ) ) {
171
+ flock( $fh, LOCK_EX );
172
+
173
+ mbstring_binary_safe_encoding();
174
+
175
+ $data_length = strlen( $contents );
176
+ $bytes_written = @fwrite( $fh, $contents );
177
+
178
+ reset_mbstring_encoding();
179
+
180
+ @flock( $fh, LOCK_UN );
181
+ @fclose( $fh );
182
+
183
+ if ( $data_length === $bytes_written ) {
184
+ $success = true;
185
+ }
186
+ }
187
+ }
188
+
189
+ if ( ! $success && in_array( 'file_put_contents', $callable ) ) {
190
+ if ( $append ) {
191
+ $flags = FILE_APPEND;
192
+ } else {
193
+ $flags = 0;
194
+ }
195
+
196
+ mbstring_binary_safe_encoding();
197
+
198
+ $data_length = strlen( $contents );
199
+ $bytes_written = @file_put_contents( $file, $contents, $flags );
200
+
201
+ reset_mbstring_encoding();
202
+
203
+ if ( $data_length === $bytes_written ) {
204
+ $success = true;
205
+ }
206
+ }
207
+
208
+ if ( $success ) {
209
+ if ( ! $file_existed ) {
210
+ // Set default file permissions for the new file.
211
+ self::chmod( $file, self::get_default_permissions() );
212
+ } else if ( isset( $original_file_perms ) && ! is_wp_error( $original_file_perms ) ) {
213
+ // Reset the original file permissions if they were modified.
214
+ self::chmod( $file, $original_file_perms );
215
+ }
216
+
217
+ return true;
218
+ }
219
+
220
+ if ( ! $file_existed ) {
221
+ // If the file is new, there is no point attempting different permissions.
222
+ break;
223
+ }
224
+ }
225
+
226
+
227
+ return new WP_Error( 'itsec-lib-file-write-file-put-contents-failed', sprintf( __( '%s could not be written. This could be due to a permissions issue. Ensure that PHP runs as a user that has permission to write to this location.', 'better-wp-security' ), $file ) );
228
+ }
229
+
230
+ /**
231
+ * Create or append the requested file with the supplied contents.
232
+ *
233
+ * @since 1.15.0
234
+ *
235
+ * @param string $file Full path to config file to update.
236
+ * @param string $contents Contents to append to the file.
237
+ * @return bool|WP_Error Boolean true on success, WP_Error object otherwise.
238
+ */
239
+ public static function append( $file, $contents ) {
240
+ return self::write( $file, $contents, true );
241
+ }
242
+
243
+ /**
244
+ * Remove the supplied file.
245
+ *
246
+ * @since 1.15.0
247
+ *
248
+ * @return bool|WP_Error Boolean true on success or a WP_Error object if an error occurs.
249
+ */
250
+ public static function remove( $file ) {
251
+ if ( ! self::exists( $file ) ) {
252
+ return true;
253
+ }
254
+
255
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'unlink' ) ) {
256
+ return new WP_Error( 'itsec-lib-file-remove-unlink-is-disabled', sprintf( __( 'The file %s could not be removed as the unlink() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
257
+ }
258
+
259
+
260
+ $result = @unlink( $file );
261
+ @clearstatcache( true, $file );
262
+
263
+ if ( $result ) {
264
+ return true;
265
+ }
266
+
267
+ return new WP_Error( 'itsec-lib-file-remove-unknown-error', sprintf( __( 'Unable to remove %s due to an unknown error.', 'better-wp-security' ), $file ) );
268
+ }
269
+
270
+ /**
271
+ * Change file permissions.
272
+ *
273
+ * @since 1.15.0
274
+ *
275
+ * @param string $file Full path to the file to change permissions for.
276
+ * @param int $perms New permissions to set.
277
+ * @return bool|WP_Error Boolean true if successful, false if not successful, or WP_Error if the chmod() function
278
+ * is unavailable.
279
+ */
280
+ public static function chmod( $file, $perms ) {
281
+ if ( ! is_int( $perms ) ) {
282
+ return new WP_Error( 'itsec-lib-file-chmod-invalid-perms', sprintf( __( 'The file %1$s could not have its permissions updated as non-integer permissions were sent: (%2$s) %3$s', 'better-wp-security' ), $file, gettype( $perms ), $perms ) );
283
+ }
284
+
285
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'chmod' ) ) {
286
+ return new WP_Error( 'itsec-lib-file-chmod-chmod-is-disabled', sprintf( __( 'The file %s could not have its permissions updated as the chmod() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
287
+ }
288
+
289
+ return @chmod( $file, $perms );
290
+ }
291
+
292
+ /**
293
+ * Determine if a file or directory exists.
294
+ *
295
+ * @since 1.15.0
296
+ *
297
+ * @param string $file Full path to test for existence.
298
+ * @return bool|WP_Error Boolean true if it exists, false if it does not.
299
+ */
300
+ public static function exists( $file ) {
301
+ @clearstatcache( true, $file );
302
+
303
+ return @file_exists( $file );
304
+ }
305
+
306
+ /**
307
+ * Determine if a file exists.
308
+ *
309
+ * @since 1.15.0
310
+ *
311
+ * @param string $file Full path to file to test for existence.
312
+ * @return bool|WP_Error Boolean true if it exists, false if it does not.
313
+ */
314
+ public static function is_file( $file ) {
315
+ @clearstatcache( true, $file );
316
+
317
+ return @is_file( $file );
318
+ }
319
+
320
+ /**
321
+ * Get file permissions from the requested file.
322
+ *
323
+ * @since 1.15.0
324
+ *
325
+ * @param string $file Full path to the file to retrieve permissions from.
326
+ * @return int|WP_Error The permissions as an int or a WP_Error object if an error occurs.
327
+ */
328
+ public static function get_permissions( $file ) {
329
+ if ( ! self::is_file( $file ) ) {
330
+ return new WP_Error( 'itsec-lib-file-get-permissions-missing-file', sprintf( __( 'Permissions for the file %s could not be read as the file could not be found.', 'better-wp-security' ), $file ) );
331
+ }
332
+
333
+ if ( ! ITSEC_Lib_Utility::is_callable_function( 'fileperms' ) ) {
334
+ return new WP_Error( 'itsec-lib-file-get-permissions-fileperms-is-disabled', sprintf( __( 'Permissions for the file %s could not be read as the fileperms() function is disabled. This is a system configuration issue.', 'better-wp-security' ), $file ) );
335
+ }
336
+
337
+
338
+ @clearstatcache( true, $file );
339
+
340
+ return fileperms( $file ) & 0777;
341
+ }
342
+
343
+ /**
344
+ * Get default file permissions to use for new files.
345
+ *
346
+ * @since 1.15.0
347
+ * @uses FS_CHMOD_FILE Define that sets default file permissions.
348
+ *
349
+ * @return int|WP_Error The default permissions as an int or a WP_Error object if an error occurs.
350
+ */
351
+ public static function get_default_permissions() {
352
+ if ( defined( 'FS_CHMOD_FILE' ) ) {
353
+ return FS_CHMOD_FILE;
354
+ }
355
+
356
+ $perms = self::get_permissions( ABSPATH . 'index.php' );
357
+
358
+ if ( ! is_wp_error( $perms ) ) {
359
+ return $perms;
360
+ }
361
+
362
+ return 0644;
363
+ }
364
+ }
365
+
366
+
367
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-utility.php' );
368
+ require_once( dirname( __FILE__ ) . '/class-itsec-lib-directory.php' );
core/lib/class-itsec-lib-utility.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * iThemes Security utility function library.
4
+ *
5
+ * Contains the ITSEC_Lib_Utility class.
6
+ *
7
+ * @package iThemes_Security
8
+ */
9
+
10
+ /**
11
+ * iThemes Security Utility Library class.
12
+ *
13
+ * Various utility functions.
14
+ *
15
+ * @package iThemes_Security
16
+ * @since 1.15.0
17
+ */
18
+ class ITSEC_Lib_Utility {
19
+ /**
20
+ * Determines if a function is callable.
21
+ *
22
+ * @since 1.15.0
23
+ *
24
+ * @param string $function Name of function.
25
+ * @return bool Boolean true if the function is callable, false otherwise.
26
+ */
27
+ public static function is_callable_function( $function ) {
28
+ if ( ! is_callable( $function ) ) {
29
+ return false;
30
+ }
31
+
32
+ if ( ! isset( $GLOBALS['itsec_lib_cached_values'] ) ) {
33
+ $GLOBALS['itsec_lib_cached_values'] = array();
34
+ }
35
+
36
+ if ( ! isset( $GLOBALS['itsec_lib_cached_values']['ini_get:disable_functions'] ) ) {
37
+ $GLOBALS['itsec_lib_cached_values']['ini_get:disable_functions'] = preg_split( '/\s*,\s*/', (string) ini_get( 'disable_functions' ) );
38
+ }
39
+
40
+ if ( in_array( $function, $GLOBALS['itsec_lib_cached_values']['ini_get:disable_functions'] ) ) {
41
+ return false;
42
+ }
43
+
44
+ if ( ! isset( $GLOBALS['itsec_lib_cached_values']['ini_get:suhosin.executor.func.blacklist'] ) ) {
45
+ $GLOBALS['itsec_lib_cached_values']['ini_get:suhosin.executor.func.blacklist'] = preg_split( '/\s*,\s*/', (string) ini_get( 'suhosin.executor.func.blacklist' ) );
46
+ }
47
+
48
+ if ( in_array( $function, $GLOBALS['itsec_lib_cached_values']['ini_get:suhosin.executor.func.blacklist'] ) ) {
49
+ return false;
50
+ }
51
+
52
+ return true;
53
+ }
54
+
55
+ /**
56
+ * Returns the type of web server.
57
+ *
58
+ * This code makes a best effort attempt of identifying the active web server. If the ITSEC_SERVER_OVERRIDE define
59
+ * is defined, this value is returned.
60
+ *
61
+ * @since 1.15.0
62
+ *
63
+ * @return string Returns apache, nginx, litespeed, or iis. Defaults to apache when the server cannot be identified.
64
+ */
65
+ public static function get_web_server() {
66
+ if ( defined( 'ITSEC_SERVER_OVERRIDE' ) ) {
67
+ return ITSEC_SERVER_OVERRIDE;
68
+ }
69
+
70
+
71
+ if ( isset( $_SERVER['SERVER_SOFTWARE'] ) ) {
72
+ $server_software = strtolower( $_SERVER['SERVER_SOFTWARE'] );
73
+ } else {
74
+ $server_software = '';
75
+ }
76
+
77
+ if ( false !== strpos( $server_software, 'apache' ) ) {
78
+ $server = 'apache';
79
+ } else if ( false !== strpos( $server_software, 'nginx' ) ) {
80
+ $server = 'nginx';
81
+ } else if ( false !== strpos( $server_software, 'litespeed' ) ) {
82
+ $server = 'litespeed';
83
+ } else if ( false !== strpos( $server_software, 'thttpd' ) ) {
84
+ $server = 'thttpd';
85
+ } else if ( false !== strpos( $server_software, 'microsoft-iis' ) ) {
86
+ $server = 'iis';
87
+ } else {
88
+ $server = 'apache';
89
+ }
90
+
91
+ return apply_filters( 'itsec_filter_web_server', $server );
92
+ }
93
+
94
+ /**
95
+ * Updates the supplied content to use the same line endings.
96
+ *
97
+ * @since 1.15.0
98
+ *
99
+ * @param string $content The content to update.
100
+ * @param string $line_ending Optional. The line ending to use. Defaults to "\n".
101
+ * @return string The content with normalized line endings.
102
+ */
103
+ public static function normalize_line_endings( $content, $line_ending = "\n" ) {
104
+ return preg_replace( '/(?<!\r)\n|\r(?!\n)|(?<!\r)\r\n|\r\r\n/', $line_ending, $content );
105
+ }
106
+
107
+ /**
108
+ * Returns the directory path to the uploads directory relative to the site root.
109
+ *
110
+ * @since 1.16.1
111
+ *
112
+ * @return string|bool The upload directory relative path or false if the path could not be determined.
113
+ */
114
+ public static function get_relative_upload_url_path() {
115
+ $upload_dir_details = wp_upload_dir();
116
+ $upload_baseurl = parse_url( $upload_dir_details['baseurl'], PHP_URL_PATH );
117
+ $home_url = parse_url( home_url(), PHP_URL_PATH );
118
+
119
+ $upload_path = preg_replace( '/^' . preg_quote( $home_url, '/' ) . '/', '', $upload_baseurl, 1, $count );
120
+
121
+ if ( 1 === $count ) {
122
+ return trim( $upload_path, '/' );
123
+ }
124
+
125
+ return false;
126
+ }
127
+
128
+ /**
129
+ * Remove comments from a string containing PHP code.
130
+ *
131
+ * @since 1.15.0
132
+ *
133
+ * @param string $contents String containing the code to strip of comments.
134
+ * @return string|WP_Error Returns a string containing the stripped source or a WP_Error object on an error.
135
+ */
136
+ public static function strip_php_comments( $contents ) {
137
+ if ( ! self::is_callable_function( 'token_get_all' ) ) {
138
+ return new WP_Error( 'itsec-lib-utility-strip-php-comments-token-get-all-is-disabled', __( 'Unable to strip comments from the source code as the token_get_all() function is disabled. This is a system configuration issue.', 'better-wp-security' ) );
139
+ }
140
+
141
+
142
+ $tokens = token_get_all( $contents );
143
+
144
+ if ( ! is_array( $tokens ) ) {
145
+ return new WP_Error( 'itsec-lib-utility-strip-php-comments-token-get-all-invalid-response', sprintf( __( 'Unable to strip comments from the source code as the token_get_all() function returned an unrecognized value (type: %s)', 'better-wp-security' ), gettype( $tokens ) ) );
146
+ }
147
+
148
+
149
+ if ( ! defined( 'T_ML_COMMENT' ) ) {
150
+ define( 'T_ML_COMMENT', T_COMMENT );
151
+ }
152
+ if ( ! defined( 'T_DOC_COMMENT' ) ) {
153
+ define( 'T_DOC_COMMENT', T_ML_COMMENT );
154
+ }
155
+
156
+ $contents = '';
157
+
158
+ foreach ( $tokens as $token ) {
159
+ if ( is_string( $token ) ) {
160
+ $contents .= $token;
161
+ } else {
162
+ list( $id, $text ) = $token;
163
+
164
+ switch ($id) {
165
+ case T_COMMENT:
166
+ case T_ML_COMMENT:
167
+ case T_DOC_COMMENT:
168
+ break;
169
+ default:
170
+ $contents .= $text;
171
+ break;
172
+ }
173
+ }
174
+ }
175
+
176
+ return $contents;
177
+ }
178
+ }
core/lib/class-itsec-wp-list-table.php ADDED
@@ -0,0 +1,1055 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copy of class WP_List_Table in case of change
4
+ *
5
+ * @package iThemes Security
6
+ * @since 4.0
7
+ */
8
+
9
+ /**
10
+ * Base class for displaying a list of items in an ajaxified HTML table.
11
+ *
12
+ * @package WordPress
13
+ * @subpackage List_Table
14
+ * @since 3.1.0
15
+ * @access private
16
+ */
17
+ class ITSEC_WP_List_Table {
18
+
19
+ /**
20
+ * The current list of items
21
+ *
22
+ * @since 3.1.0
23
+ * @var array
24
+ * @access protected
25
+ */
26
+ var $items;
27
+
28
+ /**
29
+ * Various information about the current table
30
+ *
31
+ * @since 3.1.0
32
+ * @var array
33
+ * @access private
34
+ */
35
+ var $_args;
36
+
37
+ /**
38
+ * Various information needed for displaying the pagination
39
+ *
40
+ * @since 3.1.0
41
+ * @var array
42
+ * @access private
43
+ */
44
+ var $_pagination_args = array();
45
+
46
+ /**
47
+ * The current screen
48
+ *
49
+ * @since 3.1.0
50
+ * @var object
51
+ * @access protected
52
+ */
53
+ var $screen;
54
+
55
+ /**
56
+ * Cached bulk actions
57
+ *
58
+ * @since 3.1.0
59
+ * @var array
60
+ * @access private
61
+ */
62
+ var $_actions;
63
+
64
+ /**
65
+ * Cached pagination output
66
+ *
67
+ * @since 3.1.0
68
+ * @var string
69
+ * @access private
70
+ */
71
+ var $_pagination;
72
+
73
+ /**
74
+ * Constructor. The child class should call this constructor from its own constructor
75
+ *
76
+ * @param array $args An associative array with information about the current table
77
+ *
78
+ * @access protected
79
+ */
80
+ function __construct( $args = array() ) {
81
+
82
+ $args = wp_parse_args( $args, array(
83
+ 'plural' => '',
84
+ 'singular' => '',
85
+ 'ajax' => false,
86
+ 'screen' => null,
87
+ ) );
88
+
89
+ $this->screen = convert_to_screen( $args['screen'] );
90
+
91
+ add_filter( "manage_{$this->screen->id}_columns", array( $this, 'get_columns' ), 0 );
92
+
93
+ if ( ! $args['plural'] ) {
94
+ $args['plural'] = $this->screen->base;
95
+ }
96
+
97
+ $args['plural'] = sanitize_key( $args['plural'] );
98
+ $args['singular'] = sanitize_key( $args['singular'] );
99
+
100
+ $this->_args = $args;
101
+
102
+ if ( $args['ajax'] ) {
103
+ // wp_enqueue_script( 'list-table' );
104
+ add_action( 'admin_footer', array( $this, '_js_vars' ) );
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Checks the current user's permissions
110
+ *
111
+ * @uses wp_die()
112
+ *
113
+ * @since 3.1.0
114
+ * @access public
115
+ * @abstract
116
+ */
117
+ function ajax_user_can() {
118
+
119
+ die( 'function WP_List_Table::ajax_user_can() must be over-ridden in a sub-class.' );
120
+ }
121
+
122
+ /**
123
+ * Prepares the list of items for displaying.
124
+ *
125
+ * @uses WP_List_Table::set_pagination_args()
126
+ *
127
+ * @since 3.1.0
128
+ * @access public
129
+ * @abstract
130
+ */
131
+ function prepare_items() {
132
+
133
+ die( 'function WP_List_Table::prepare_items() must be over-ridden in a sub-class.' );
134
+ }
135
+
136
+ /**
137
+ * An internal method that sets all the necessary pagination arguments
138
+ *
139
+ * @param array $args An associative array with information about the pagination
140
+ *
141
+ * @access protected
142
+ */
143
+ function set_pagination_args( $args ) {
144
+
145
+ $args = wp_parse_args( $args, array(
146
+ 'total_items' => 0,
147
+ 'total_pages' => 0,
148
+ 'per_page' => 0,
149
+ ) );
150
+
151
+ if ( ! $args['total_pages'] && $args['per_page'] > 0 ) {
152
+ $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
153
+ }
154
+
155
+ // redirect if page number is invalid and headers are not already sent
156
+ if ( ! headers_sent() && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) && $args['total_pages'] > 0 && $this->get_pagenum() > $args['total_pages'] ) {
157
+ wp_redirect( add_query_arg( 'paged', $args['total_pages'] ) );
158
+ exit;
159
+ }
160
+
161
+ $this->_pagination_args = $args;
162
+ }
163
+
164
+ /**
165
+ * Access the pagination args
166
+ *
167
+ * @since 3.1.0
168
+ * @access public
169
+ *
170
+ * @param string $key
171
+ *
172
+ * @return array
173
+ */
174
+ function get_pagination_arg( $key ) {
175
+
176
+ if ( 'page' == $key ) {
177
+ return $this->get_pagenum();
178
+ }
179
+
180
+ if ( isset( $this->_pagination_args[$key] ) ) {
181
+ return $this->_pagination_args[$key];
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Whether the table has items to display or not
187
+ *
188
+ * @since 3.1.0
189
+ * @access public
190
+ *
191
+ * @return bool
192
+ */
193
+ function has_items() {
194
+
195
+ return ! empty( $this->items );
196
+ }
197
+
198
+ /**
199
+ * Message to be displayed when there are no items
200
+ *
201
+ * @since 3.1.0
202
+ * @access public
203
+ */
204
+ function no_items() {
205
+
206
+ _e( 'No items found.' );
207
+ }
208
+
209
+ /**
210
+ * Display the search box.
211
+ *
212
+ * @since 3.1.0
213
+ * @access public
214
+ *
215
+ * @param string $text The search button text
216
+ * @param string $input_id The search input id
217
+ */
218
+ function search_box( $text, $input_id ) {
219
+
220
+ if ( empty( $_REQUEST['s'] ) && ! $this->has_items() ) {
221
+ return;
222
+ }
223
+
224
+ $input_id = $input_id . '-search-input';
225
+
226
+ if ( ! empty( $_REQUEST['orderby'] ) ) {
227
+ echo '<input type="hidden" name="orderby" value="' . esc_attr( $_REQUEST['orderby'] ) . '" />';
228
+ }
229
+ if ( ! empty( $_REQUEST['order'] ) ) {
230
+ echo '<input type="hidden" name="order" value="' . esc_attr( $_REQUEST['order'] ) . '" />';
231
+ }
232
+ if ( ! empty( $_REQUEST['post_mime_type'] ) ) {
233
+ echo '<input type="hidden" name="post_mime_type" value="' . esc_attr( $_REQUEST['post_mime_type'] ) . '" />';
234
+ }
235
+ if ( ! empty( $_REQUEST['detached'] ) ) {
236
+ echo '<input type="hidden" name="detached" value="' . esc_attr( $_REQUEST['detached'] ) . '" />';
237
+ }
238
+ ?>
239
+ <p class="search-box">
240
+ <label class="screen-reader-text" for="<?php echo $input_id ?>"><?php echo $text; ?>:</label> <input
241
+ type="search" id="<?php echo $input_id ?>" name="s" value="<?php _admin_search_query(); ?>"/>
242
+ <?php submit_button( $text, 'button', false, false, array( 'id' => 'search-submit' ) ); ?>
243
+ </p>
244
+ <?php
245
+ }
246
+
247
+ /**
248
+ * Get an associative array ( id => link ) with the list
249
+ * of views available on this table.
250
+ *
251
+ * @since 3.1.0
252
+ * @access protected
253
+ *
254
+ * @return array
255
+ */
256
+ function get_views() {
257
+
258
+ return array();
259
+ }
260
+
261
+ /**
262
+ * Display the list of views available on this table.
263
+ *
264
+ * @since 3.1.0
265
+ * @access public
266
+ */
267
+ function views() {
268
+
269
+ $views = $this->get_views();
270
+ /**
271
+ * Filter the list of available list table views.
272
+ *
273
+ * The dynamic portion of the hook name, $this->screen->id, refers
274
+ * to the ID of the current screen, usually a string.
275
+ *
276
+ * @since 3.5.0
277
+ *
278
+ * @param array $views An array of available list table views.
279
+ */
280
+ $views = apply_filters( "views_{$this->screen->id}", $views );
281
+
282
+ if ( empty( $views ) ) {
283
+ return;
284
+ }
285
+
286
+ echo "<ul class='subsubsub'>\n";
287
+ foreach ( $views as $class => $view ) {
288
+ $views[$class] = "\t<li class='$class'>$view";
289
+ }
290
+ echo implode( " |</li>\n", $views ) . "</li>\n";
291
+ echo "</ul>";
292
+ }
293
+
294
+ /**
295
+ * Get an associative array ( option_name => option_title ) with the list
296
+ * of bulk actions available on this table.
297
+ *
298
+ * @since 3.1.0
299
+ * @access protected
300
+ *
301
+ * @return array
302
+ */
303
+ function get_bulk_actions() {
304
+
305
+ return array();
306
+ }
307
+
308
+ /**
309
+ * Display the bulk actions dropdown.
310
+ *
311
+ * @since 3.1.0
312
+ * @access public
313
+ */
314
+ function bulk_actions() {
315
+
316
+ if ( is_null( $this->_actions ) ) {
317
+ $no_new_actions = $this->_actions = $this->get_bulk_actions();
318
+ /**
319
+ * Filter the list table Bulk Actions drop-down.
320
+ *
321
+ * The dynamic portion of the hook name, $this->screen->id, refers
322
+ * to the ID of the current screen, usually a string.
323
+ *
324
+ * This filter can currently only be used to remove bulk actions.
325
+ *
326
+ * @since 3.5.0
327
+ *
328
+ * @param array $actions An array of the available bulk actions.
329
+ */
330
+ $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
331
+ $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
332
+ $two = '';
333
+ } else {
334
+ $two = '2';
335
+ }
336
+
337
+ if ( empty( $this->_actions ) ) {
338
+ return;
339
+ }
340
+
341
+ echo "<select name='action$two'>\n";
342
+ echo "<option value='-1' selected='selected'>" . __( 'Bulk Actions' ) . "</option>\n";
343
+
344
+ foreach ( $this->_actions as $name => $title ) {
345
+ $class = 'edit' == $name ? ' class="hide-if-no-js"' : '';
346
+
347
+ echo "\t<option value='$name'$class>$title</option>\n";
348
+ }
349
+
350
+ echo "</select>\n";
351
+
352
+ submit_button( __( 'Apply' ), 'action', false, false, array( 'id' => "doaction$two" ) );
353
+ echo "\n";
354
+ }
355
+
356
+ /**
357
+ * Get the current action selected from the bulk actions dropdown.
358
+ *
359
+ * @since 3.1.0
360
+ * @access public
361
+ *
362
+ * @return string|bool The action name or False if no action was selected
363
+ */
364
+ function current_action() {
365
+
366
+ if ( isset( $_REQUEST['action'] ) && - 1 != $_REQUEST['action'] ) {
367
+ return $_REQUEST['action'];
368
+ }
369
+
370
+ if ( isset( $_REQUEST['action2'] ) && - 1 != $_REQUEST['action2'] ) {
371
+ return $_REQUEST['action2'];
372
+ }
373
+
374
+ return false;
375
+ }
376
+
377
+ /**
378
+ * Generate row actions div
379
+ *
380
+ * @since 3.1.0
381
+ * @access protected
382
+ *
383
+ * @param array $actions The list of actions
384
+ * @param bool $always_visible Whether the actions should be always visible
385
+ *
386
+ * @return string
387
+ */
388
+ function row_actions( $actions, $always_visible = false ) {
389
+
390
+ $action_count = count( $actions );
391
+ $i = 0;
392
+
393
+ if ( ! $action_count ) {
394
+ return '';
395
+ }
396
+
397
+ $out = '<div class="' . ( $always_visible ? 'row-actions visible' : 'row-actions' ) . '">';
398
+ foreach ( $actions as $action => $link ) {
399
+ ++$i;
400
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
401
+ $out .= "<span class='$action'>$link$sep</span>";
402
+ }
403
+ $out .= '</div>';
404
+
405
+ return $out;
406
+ }
407
+
408
+ /**
409
+ * Display a monthly dropdown for filtering items
410
+ *
411
+ * @since 3.1.0
412
+ * @access protected
413
+ */
414
+ function months_dropdown( $post_type ) {
415
+
416
+ global $wpdb, $wp_locale;
417
+
418
+ $months = $wpdb->get_results( $wpdb->prepare( "
419
+ SELECT DISTINCT YEAR( post_date ) AS year, MONTH( post_date ) AS month
420
+ FROM $wpdb->posts
421
+ WHERE post_type = %s
422
+ ORDER BY post_date DESC
423
+ ", $post_type ) );
424
+
425
+ /**
426
+ * Filter the 'Months' drop-down results.
427
+ *
428
+ * @since 3.7.0
429
+ *
430
+ * @param object $months The months drop-down query results.
431
+ * @param string $post_type The post type.
432
+ */
433
+ $months = apply_filters( 'months_dropdown_results', $months, $post_type );
434
+
435
+ $month_count = count( $months );
436
+
437
+ if ( ! $month_count || ( 1 == $month_count && 0 == $months[0]->month ) ) {
438
+ return;
439
+ }
440
+
441
+ $m = isset( $_GET['m'] ) ? (int) $_GET['m'] : 0;
442
+ ?>
443
+ <select name='m'>
444
+ <option<?php selected( $m, 0 ); ?> value='0'><?php _e( 'Show all dates' ); ?></option>
445
+ <?php
446
+ foreach ( $months as $arc_row ) {
447
+ if ( 0 == $arc_row->year ) {
448
+ continue;
449
+ }
450
+
451
+ $month = zeroise( $arc_row->month, 2 );
452
+ $year = $arc_row->year;
453
+
454
+ printf( "<option %s value='%s'>%s</option>\n",
455
+ selected( $m, $year . $month, false ),
456
+ esc_attr( $arc_row->year . $month ),
457
+ /* translators: 1: month name, 2: 4-digit year */
458
+ sprintf( __( '%1$s %2$d' ), $wp_locale->get_month( $month ), $year )
459
+ );
460
+ }
461
+ ?>
462
+ </select>
463
+ <?php
464
+ }
465
+
466
+ /**
467
+ * Display a view switcher
468
+ *
469
+ * @since 3.1.0
470
+ * @access protected
471
+ */
472
+ function view_switcher( $current_mode ) {
473
+
474
+ $modes = array(
475
+ 'list' => __( 'List View' ),
476
+ 'excerpt' => __( 'Excerpt View' )
477
+ );
478
+
479
+ ?>
480
+ <input type="hidden" name="mode" value="<?php echo esc_attr( $current_mode ); ?>"/>
481
+ <div class="view-switch">
482
+ <?php
483
+ foreach ( $modes as $mode => $title ) {
484
+ $class = ( $current_mode == $mode ) ? 'class="current"' : '';
485
+ echo "<a href='" . esc_url( add_query_arg( 'mode', $mode, $_SERVER['REQUEST_URI'] ) ) . "' $class><img id='view-switch-$mode' src='" . esc_url( includes_url( 'images/blank.gif' ) ) . "' width='20' height='20' title='$title' alt='$title' /></a>\n";
486
+ }
487
+ ?>
488
+ </div>
489
+ <?php
490
+ }
491
+
492
+ /**
493
+ * Display a comment count bubble
494
+ *
495
+ * @since 3.1.0
496
+ * @access protected
497
+ *
498
+ * @param int $post_id
499
+ * @param int $pending_comments
500
+ */
501
+ function comments_bubble( $post_id, $pending_comments ) {
502
+
503
+ $pending_phrase = sprintf( __( '%s pending' ), number_format( $pending_comments ) );
504
+
505
+ if ( $pending_comments ) {
506
+ echo '<strong>';
507
+ }
508
+
509
+ echo "<a href='" . esc_url( add_query_arg( 'p', $post_id, admin_url( 'edit-comments.php' ) ) ) . "' title='" . esc_attr( $pending_phrase ) . "' class='post-com-count'><span class='comment-count'>" . number_format_i18n( get_comments_number() ) . "</span></a>";
510
+
511
+ if ( $pending_comments ) {
512
+ echo '</strong>';
513
+ }
514
+ }
515
+
516
+ /**
517
+ * Get the current page number
518
+ *
519
+ * @since 3.1.0
520
+ * @access protected
521
+ *
522
+ * @return int
523
+ */
524
+ function get_pagenum() {
525
+
526
+ $pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
527
+
528
+ if ( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
529
+ $pagenum = $this->_pagination_args['total_pages'];
530
+ }
531
+
532
+ return max( 1, $pagenum );
533
+ }
534
+
535
+ /**
536
+ * Get number of items to display on a single page
537
+ *
538
+ * @since 3.1.0
539
+ * @access protected
540
+ *
541
+ * @return int
542
+ */
543
+ function get_items_per_page( $option, $default = 20 ) {
544
+
545
+ $per_page = (int) get_user_option( $option );
546
+ if ( empty( $per_page ) || $per_page < 1 ) {
547
+ $per_page = $default;
548
+ }
549
+
550
+ /**
551
+ * Filter the number of items to be displayed on each page of the list table.
552
+ *
553
+ * The dynamic hook name, $option, refers to the per page option depending
554
+ * on the type of list table in use. Possible values may include:
555
+ * 'edit_comments_per_page', 'sites_network_per_page', 'site_themes_network_per_page',
556
+ * 'themes_netework_per_page', 'users_network_per_page', 'edit_{$post_type}', etc.
557
+ *
558
+ * @since 2.9.0
559
+ *
560
+ * @param int $per_page Number of items to be displayed. Default 20.
561
+ */
562
+
563
+ return (int) apply_filters( $option, $per_page );
564
+ }
565
+
566
+ /**
567
+ * Display the pagination.
568
+ *
569
+ * @since 3.1.0
570
+ * @access protected
571
+ */
572
+ function pagination( $which ) {
573
+
574
+ if ( empty( $this->_pagination_args ) ) {
575
+ return;
576
+ }
577
+
578
+ extract( $this->_pagination_args, EXTR_SKIP );
579
+
580
+ $output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
581
+
582
+ $current = $this->get_pagenum();
583
+
584
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
585
+
586
+ $current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
587
+
588
+ $page_links = array();
589
+
590
+ $disable_first = $disable_last = '';
591
+ if ( $current == 1 ) {
592
+ $disable_first = ' disabled';
593
+ }
594
+ if ( $current == $total_pages ) {
595
+ $disable_last = ' disabled';
596
+ }
597
+
598
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
599
+ 'first-page' . $disable_first,
600
+ esc_attr__( 'Go to the first page' ),
601
+ esc_url( remove_query_arg( 'paged', $current_url ) ),
602
+ '&laquo;'
603
+ );
604
+
605
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
606
+ 'prev-page' . $disable_first,
607
+ esc_attr__( 'Go to the previous page' ),
608
+ esc_url( add_query_arg( 'paged', max( 1, $current - 1 ), $current_url ) ),
609
+ '&lsaquo;'
610
+ );
611
+
612
+ if ( 'bottom' == $which ) {
613
+ $html_current_page = $current;
614
+ } else {
615
+ $html_current_page = sprintf( "<input class='current-page' title='%s' type='text' name='paged' value='%s' size='%d' />",
616
+ esc_attr__( 'Current page' ),
617
+ $current,
618
+ strlen( $total_pages )
619
+ );
620
+ }
621
+
622
+ $html_total_pages = sprintf( "<span class='total-pages'>%s</span>", number_format_i18n( $total_pages ) );
623
+ $page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
624
+
625
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
626
+ 'next-page' . $disable_last,
627
+ esc_attr__( 'Go to the next page' ),
628
+ esc_url( add_query_arg( 'paged', min( $total_pages, $current + 1 ), $current_url ) ),
629
+ '&rsaquo;'
630
+ );
631
+
632
+ $page_links[] = sprintf( "<a class='%s' title='%s' href='%s'>%s</a>",
633
+ 'last-page' . $disable_last,
634
+ esc_attr__( 'Go to the last page' ),
635
+ esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
636
+ '&raquo;'
637
+ );
638
+
639
+ $pagination_links_class = 'pagination-links';
640
+ if ( ! empty( $infinite_scroll ) ) {
641
+ $pagination_links_class = ' hide-if-js';
642
+ }
643
+ $output .= "\n<span class='$pagination_links_class'>" . join( "\n", $page_links ) . '</span>';
644
+
645
+ if ( $total_pages ) {
646
+ $page_class = $total_pages < 2 ? ' one-page' : '';
647
+ } else {
648
+ $page_class = ' no-pages';
649
+ }
650
+
651
+ $this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
652
+
653
+ echo $this->_pagination;
654
+ }
655
+
656
+ /**
657
+ * Get a list of columns. The format is:
658
+ * 'internal-name' => 'Title'
659
+ *
660
+ * @since 3.1.0
661
+ * @access protected
662
+ * @abstract
663
+ *
664
+ * @return array
665
+ */
666
+ function get_columns() {
667
+
668
+ die( 'function WP_List_Table::get_columns() must be over-ridden in a sub-class.' );
669
+ }
670
+
671
+ /**
672
+ * Get a list of sortable columns. The format is:
673
+ * 'internal-name' => 'orderby'
674
+ * or
675
+ * 'internal-name' => array( 'orderby', true )
676
+ *
677
+ * The second format will make the initial sorting order be descending
678
+ *
679
+ * @since 3.1.0
680
+ * @access protected
681
+ *
682
+ * @return array
683
+ */
684
+ function get_sortable_columns() {
685
+
686
+ return array();
687
+ }
688
+
689
+ /**
690
+ * Get a list of all, hidden and sortable columns, with filter applied
691
+ *
692
+ * @since 3.1.0
693
+ * @access protected
694
+ *
695
+ * @return array
696
+ */
697
+ function get_column_info() {
698
+
699
+ if ( isset( $this->_column_headers ) ) {
700
+ return $this->_column_headers;
701
+ }
702
+
703
+ $columns = get_column_headers( $this->screen );
704
+ $hidden = get_hidden_columns( $this->screen );
705
+
706
+ $sortable_columns = $this->get_sortable_columns();
707
+ /**
708
+ * Filter the list table sortable columns for a specific screen.
709
+ *
710
+ * The dynamic portion of the hook name, $this->screen->id, refers
711
+ * to the ID of the current screen, usually a string.
712
+ *
713
+ * @since 3.5.0
714
+ *
715
+ * @param array $sortable_columns An array of sortable columns.
716
+ */
717
+ $_sortable = apply_filters( "manage_{$this->screen->id}_sortable_columns", $sortable_columns );
718
+
719
+ $sortable = array();
720
+ foreach ( $_sortable as $id => $data ) {
721
+ if ( empty( $data ) ) {
722
+ continue;
723
+ }
724
+
725
+ $data = (array) $data;
726
+ if ( ! isset( $data[1] ) ) {
727
+ $data[1] = false;
728
+ }
729
+
730
+ $sortable[$id] = $data;
731
+ }
732
+
733
+ $this->_column_headers = array( $columns, $hidden, $sortable );
734
+
735
+ return $this->_column_headers;
736
+ }
737
+
738
+ /**
739
+ * Return number of visible columns
740
+ *
741
+ * @since 3.1.0
742
+ * @access public
743
+ *
744
+ * @return int
745
+ */
746
+ function get_column_count() {
747
+
748
+ list ( $columns, $hidden ) = $this->get_column_info();
749
+ $hidden = array_intersect( array_keys( $columns ), array_filter( $hidden ) );
750
+
751
+ return count( $columns ) - count( $hidden );
752
+ }
753
+
754
+ /**
755
+ * Print column headers, accounting for hidden and sortable columns.
756
+ *
757
+ * @since 3.1.0
758
+ * @access protected
759
+ *
760
+ * @param bool $with_id Whether to set the id attribute or not
761
+ */
762
+ function print_column_headers( $with_id = true ) {
763
+
764
+ list( $columns, $hidden, $sortable ) = $this->get_column_info();
765
+
766
+ $current_url = set_url_scheme( 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] );
767
+ $current_url = remove_query_arg( 'paged', $current_url );
768
+
769
+ if ( isset( $_GET['orderby'] ) ) {
770
+ $current_orderby = $_GET['orderby'];
771
+ } else {
772
+ $current_orderby = '';
773
+ }
774
+
775
+ if ( isset( $_GET['order'] ) && 'desc' == $_GET['order'] ) {
776
+ $current_order = 'desc';
777
+ } else {
778
+ $current_order = 'asc';
779
+ }
780
+
781
+ if ( ! empty( $columns['cb'] ) ) {
782
+ static $cb_counter = 1;
783
+ $columns['cb'] = '<label class="screen-reader-text" for="cb-select-all-' . $cb_counter . '">' . __( 'Select All' ) . '</label>'
784
+ . '<input id="cb-select-all-' . $cb_counter . '" type="checkbox" />';
785
+ $cb_counter ++;
786
+ }
787
+
788
+ foreach ( $columns as $column_key => $column_display_name ) {
789
+ $class = array( 'manage-column', "column-$column_key" );
790
+
791
+ $style = '';
792
+ if ( in_array( $column_key, $hidden ) ) {
793
+ $style = 'display:none;';
794
+ }
795
+
796
+ $style = ' style="' . $style . '"';
797
+
798
+ if ( 'cb' == $column_key ) {
799
+ $class[] = 'check-column';
800
+ } elseif ( in_array( $column_key, array( 'posts', 'comments', 'links' ) ) ) {
801
+ $class[] = 'num';
802
+ }
803
+
804
+ if ( isset( $sortable[$column_key] ) ) {
805
+ list( $orderby, $desc_first ) = $sortable[$column_key];
806
+
807
+ if ( $current_orderby == $orderby ) {
808
+ $order = 'asc' == $current_order ? 'desc' : 'asc';
809
+ $class[] = 'sorted';
810
+ $class[] = $current_order;
811
+ } else {
812
+ $order = $desc_first ? 'desc' : 'asc';
813
+ $class[] = 'sortable';
814
+ $class[] = $desc_first ? 'asc' : 'desc';
815
+ }
816
+
817
+ $column_display_name = '<a href="' . esc_url( add_query_arg( compact( 'orderby', 'order' ), $current_url ) ) . '"><span>' . $column_display_name . '</span><span class="sorting-indicator"></span></a>';
818
+ }
819
+
820
+ $id = $with_id ? "id='$column_key'" : '';
821
+
822
+ if ( ! empty( $class ) ) {
823
+ $class = "class='" . join( ' ', $class ) . "'";
824
+ }
825
+
826
+ echo "<th scope='col' $id $class $style>$column_display_name</th>";
827
+ }
828
+ }
829
+
830
+ /**
831
+ * Display the table
832
+ *
833
+ * @since 3.1.0
834
+ * @access public
835
+ */
836
+ function display() {
837
+
838
+ extract( $this->_args );
839
+
840
+ $this->display_tablenav( 'top' );
841
+
842
+ ?>
843
+ <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>" cellspacing="0">
844
+ <thead>
845
+ <tr>
846
+ <?php $this->print_column_headers(); ?>
847
+ </tr>
848
+ </thead>
849
+
850
+ <tfoot>
851
+ <tr>
852
+ <?php $this->print_column_headers( false ); ?>
853
+ </tr>
854
+ </tfoot>
855
+
856
+ <tbody id="the-list"<?php if ( $singular ) {
857
+ echo " data-wp-lists='list:$singular'";
858
+ } ?>>
859
+ <?php $this->display_rows_or_placeholder(); ?>
860
+ </tbody>
861
+ </table>
862
+ <?php
863
+ $this->display_tablenav( 'bottom' );
864
+ }
865
+
866
+ /**
867
+ * Get a list of CSS classes for the <table> tag
868
+ *
869
+ * @since 3.1.0
870
+ * @access protected
871
+ *
872
+ * @return array
873
+ */
874
+ function get_table_classes() {
875
+
876
+ return array( 'widefat', 'fixed', $this->_args['plural'] );
877
+ }
878
+
879
+ /**
880
+ * Generate the table navigation above or below the table
881
+ *
882
+ * @since 3.1.0
883
+ * @access protected
884
+ */
885
+ function display_tablenav( $which ) {
886
+
887
+ if ( 'top' == $which ) {
888
+ wp_nonce_field( 'bulk-' . $this->_args['plural'] );
889
+ }
890
+ ?>
891
+ <div class="tablenav <?php echo esc_attr( $which ); ?>">
892
+
893
+ <div class="alignleft actions bulkactions">
894
+ <?php $this->bulk_actions(); ?>
895
+ </div>
896
+ <?php
897
+ $this->extra_tablenav( $which );
898
+ $this->pagination( $which );
899
+ ?>
900
+
901
+ <br class="clear"/>
902
+ </div>
903
+ <?php
904
+ }
905
+
906
+ /**
907
+ * Extra controls to be displayed between bulk actions and pagination
908
+ *
909
+ * @since 3.1.0
910
+ * @access protected
911
+ */
912
+ function extra_tablenav( $which ) {
913
+ }
914
+
915
+ /**
916
+ * Generate the <tbody> part of the table
917
+ *
918
+ * @since 3.1.0
919
+ * @access protected
920
+ */
921
+ function display_rows_or_placeholder() {
922
+
923
+ if ( $this->has_items() ) {
924
+ $this->display_rows();
925
+ } else {
926
+ list( $columns, $hidden ) = $this->get_column_info();
927
+ echo '<tr class="no-items"><td class="colspanchange" colspan="' . $this->get_column_count() . '">';
928
+ $this->no_items();
929
+ echo '</td></tr>';
930
+ }
931
+ }
932
+
933
+ /**
934
+ * Generate the table rows
935
+ *
936
+ * @since 3.1.0
937
+ * @access protected
938
+ */
939
+ function display_rows() {
940
+
941
+ foreach ( $this->items as $item ) {
942
+ $this->single_row( $item );
943
+ }
944
+ }
945
+
946
+ /**
947
+ * Generates content for a single row of the table
948
+ *
949
+ * @since 3.1.0
950
+ * @access protected
951
+ *
952
+ * @param object $item The current item
953
+ */
954
+ function single_row( $item ) {
955
+
956
+ static $row_class = '';
957
+ $row_class = ( $row_class == '' ? ' class="alternate"' : '' );
958
+
959
+ echo '<tr' . $row_class . '>';
960
+ $this->single_row_columns( $item );
961
+ echo '</tr>';
962
+ }
963
+
964
+ /**
965
+ * Generates the columns for a single row of the table
966
+ *
967
+ * @since 3.1.0
968
+ * @access protected
969
+ *
970
+ * @param object $item The current item
971
+ */
972
+ function single_row_columns( $item ) {
973
+
974
+ list( $columns, $hidden ) = $this->get_column_info();
975
+
976
+ foreach ( $columns as $column_name => $column_display_name ) {
977
+ $class = "class='$column_name column-$column_name'";
978
+
979
+ $style = '';
980
+ if ( in_array( $column_name, $hidden ) ) {
981
+ $style = ' style="display:none;"';
982
+ }
983
+
984
+ $attributes = "$class$style";
985
+
986
+ if ( 'cb' == $column_name ) {
987
+ echo '<th scope="row" class="check-column">';
988
+ echo $this->column_cb( $item );
989
+ echo '</th>';
990
+ } elseif ( method_exists( $this, 'column_' . $column_name ) ) {
991
+ echo "<td $attributes>";
992
+ echo call_user_func( array( $this, 'column_' . $column_name ), $item );
993
+ echo "</td>";
994
+ } else {
995
+ echo "<td $attributes>";
996
+ echo $this->column_default( $item, $column_name );
997
+ echo "</td>";
998
+ }
999
+ }
1000
+ }
1001
+
1002
+ /**
1003
+ * Handle an incoming ajax request (called from admin-ajax.php)
1004
+ *
1005
+ * @since 3.1.0
1006
+ * @access public
1007
+ */
1008
+ function ajax_response() {
1009
+
1010
+ $this->prepare_items();
1011
+
1012
+ extract( $this->_args );
1013
+ extract( $this->_pagination_args, EXTR_SKIP );
1014
+
1015
+ ob_start();
1016
+ if ( ! empty( $_REQUEST['no_placeholder'] ) ) {
1017
+ $this->display_rows();
1018
+ } else {
1019
+ $this->display_rows_or_placeholder();
1020
+ }
1021
+
1022
+ $rows = ob_get_clean();
1023
+
1024
+ $response = array( 'rows' => $rows );
1025
+
1026
+ if ( isset( $total_items ) ) {
1027
+ $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
1028
+ }
1029
+
1030
+ if ( isset( $total_pages ) ) {
1031
+ $response['total_pages'] = $total_pages;
1032
+ $response['total_pages_i18n'] = number_format_i18n( $total_pages );
1033
+ }
1034
+
1035
+ die( json_encode( $response ) );
1036
+ }
1037
+
1038
+ /**
1039
+ * Send required variables to JavaScript land
1040
+ *
1041
+ * @access private
1042
+ */
1043
+ function _js_vars() {
1044
+
1045
+ $args = array(
1046
+ 'class' => get_class( $this ),
1047
+ 'screen' => array(
1048
+ 'id' => $this->screen->id,
1049
+ 'base' => $this->screen->base,
1050
+ )
1051
+ );
1052
+
1053
+ printf( "<script type='text/javascript'>list_args = %s;</script>\n", json_encode( $args ) );
1054
+ }
1055
+ }
core/lib/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/admin-user/class-itsec-admin-user-admin.php ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Admin_User_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $module_path;
9
+
10
+ function run( $core ) {
11
+
12
+ $this->core = $core;
13
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
14
+
15
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
16
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
17
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'dashboard_status' ) ); //add information for plugin status
18
+
19
+ if ( ! empty( $_POST ) ) {
20
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
21
+ }
22
+
23
+ }
24
+
25
+ /**
26
+ * Add meta boxes to primary options pages
27
+ *
28
+ * @return void
29
+ */
30
+ public function add_admin_meta_boxes() {
31
+
32
+ $id = 'admin_user_options';
33
+ $title = __( 'Admin User', 'better-wp-security' );
34
+
35
+ add_meta_box(
36
+ $id,
37
+ $title,
38
+ array( $this, 'metabox_admin_user_settings' ),
39
+ 'security_page_toplevel_page_itsec_advanced',
40
+ 'advanced',
41
+ 'core'
42
+ );
43
+
44
+ }
45
+
46
+ /**
47
+ * Add Away mode Javascript
48
+ *
49
+ * @return void
50
+ */
51
+ public function admin_script() {
52
+
53
+ global $itsec_globals;
54
+
55
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_advanced' ) !== false ) {
56
+
57
+ wp_enqueue_script( 'itsec_admin_user_js', $this->module_path . 'js/admin-admin-user.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
58
+
59
+ }
60
+
61
+ }
62
+
63
+ /**
64
+ * Changes Admin User
65
+ *
66
+ * Changes the username and id of the 1st user
67
+ *
68
+ * @param string $username the username to change if changing at the same time
69
+ * @param bool $id whether to change the id as well
70
+ *
71
+ * @return bool success or failure
72
+ *
73
+ **/
74
+ private function change_admin_user( $username = null, $id = false ) {
75
+
76
+ global $itsec_files, $wpdb;
77
+
78
+ if ( $itsec_files->get_file_lock( 'admin_user' ) ) { //make sure it isn't already running
79
+
80
+ //sanitize the username
81
+ $new_user = sanitize_text_field( $username );
82
+
83
+ //Get the full user object
84
+ $user_object = get_user_by( 'id', '1' );
85
+
86
+ if ( ! is_null( $username ) && validate_username( $new_user ) && false === username_exists( $new_user ) ) { //there is a valid username to change
87
+
88
+ if ( $id === true ) { //we're changing the id too so we'll set the username
89
+
90
+ $user_login = $new_user;
91
+
92
+ } else { // we're only changing the username
93
+
94
+ //query main user table
95
+ $wpdb->query( "UPDATE `" . $wpdb->users . "` SET user_login = '" . esc_sql( $new_user ) . "' WHERE user_login='admin';" );
96
+
97
+ if ( is_multisite() ) { //process sitemeta if we're in a multi-site situation
98
+
99
+ $oldAdmins = $wpdb->get_var( "SELECT meta_value FROM `" . $wpdb->sitemeta . "` WHERE meta_key = 'site_admins'" );
100
+ $newAdmins = str_replace( '5:"admin"', strlen( $new_user ) . ':"' . esc_sql( $new_user ) . '"', $oldAdmins );
101
+ $wpdb->query( "UPDATE `" . $wpdb->sitemeta . "` SET meta_value = '" . esc_sql( $newAdmins ) . "' WHERE meta_key = 'site_admins'" );
102
+
103
+ }
104
+
105
+ wp_clear_auth_cookie();
106
+ $itsec_files->release_file_lock( 'admin_user' );
107
+
108
+ return true;
109
+
110
+ }
111
+
112
+ } elseif ( $username !== null ) { //username didn't validate
113
+ $itsec_files->release_file_lock( 'admin_user' );
114
+
115
+ return false;
116
+
117
+ } else { //only changing the id
118
+
119
+ $user_login = $user_object->user_login;
120
+
121
+ }
122
+
123
+ if ( $id === true ) { //change the user id
124
+
125
+ $wpdb->query( "DELETE FROM `" . $wpdb->users . "` WHERE ID = 1;" );
126
+
127
+ $wpdb->insert( $wpdb->users, array(
128
+ 'user_login' => $user_login, 'user_pass' => $user_object->user_pass,
129
+ 'user_nicename' => $user_object->user_nicename, 'user_email' => $user_object->user_email,
130
+ 'user_url' => $user_object->user_url, 'user_registered' => $user_object->user_registered,
131
+ 'user_activation_key' => $user_object->user_activation_key,
132
+ 'user_status' => $user_object->user_status, 'display_name' => $user_object->display_name
133
+ ) );
134
+
135
+ if ( is_multisite() && $username !== null && validate_username( $new_user ) ) { //process sitemeta if we're in a multi-site situation
136
+
137
+ $oldAdmins = $wpdb->get_var( "SELECT meta_value FROM `" . $wpdb->sitemeta . "` WHERE meta_key = 'site_admins'" );
138
+ $newAdmins = str_replace( '5:"admin"', strlen( $new_user ) . ':"' . esc_sql( $new_user ) . '"', $oldAdmins );
139
+ $wpdb->query( "UPDATE `" . $wpdb->sitemeta . "` SET meta_value = '" . esc_sql( $newAdmins ) . "' WHERE meta_key = 'site_admins'" );
140
+
141
+ }
142
+
143
+ $new_user = $wpdb->insert_id;
144
+
145
+ $wpdb->query( "UPDATE `" . $wpdb->posts . "` SET post_author = '" . $new_user . "' WHERE post_author = 1;" );
146
+ $wpdb->query( "UPDATE `" . $wpdb->usermeta . "` SET user_id = '" . $new_user . "' WHERE user_id = 1;" );
147
+ $wpdb->query( "UPDATE `" . $wpdb->comments . "` SET user_id = '" . $new_user . "' WHERE user_id = 1;" );
148
+ $wpdb->query( "UPDATE `" . $wpdb->links . "` SET link_owner = '" . $new_user . "' WHERE link_owner = 1;" );
149
+
150
+ wp_clear_auth_cookie();
151
+ $itsec_files->release_file_lock( 'admin_user' );
152
+
153
+ return true;
154
+
155
+ }
156
+
157
+ }
158
+
159
+ return false;
160
+
161
+ }
162
+
163
+ /**
164
+ * Sets the status in the plugin dashboard
165
+ *
166
+ * @since 4.0
167
+ *
168
+ * @return array array of statuses
169
+ */
170
+ public function dashboard_status( $statuses ) {
171
+
172
+ if ( ! username_exists( 'admin' ) ) {
173
+
174
+ $status_array = 'safe-high';
175
+ $status = array(
176
+ 'text' => __( 'The <em>admin</em> user has been removed or renamed.', 'better-wp-security' ),
177
+ 'link' => '#itsec_authentication_admin_user_username', 'advanced' => true,
178
+ );
179
+
180
+ } else {
181
+
182
+ $status_array = 'high';
183
+ $status = array(
184
+ 'text' => __( 'The <em>admin</em> user still exists.', 'better-wp-security' ),
185
+ 'link' => '#itsec_authentication_admin_user_username', 'advanced' => true,
186
+ );
187
+
188
+ }
189
+
190
+ array_push( $statuses[$status_array], $status );
191
+
192
+ if ( ! ITSEC_Lib::user_id_exists( 1 ) ) {
193
+
194
+ $status_array = 'safe-medium';
195
+ $status = array(
196
+ 'text' => __( 'The user with id 1 has been removed.', 'better-wp-security' ),
197
+ 'link' => '#itsec_authentication_admin_user_userid', 'advanced' => true,
198
+ );
199
+
200
+ } else {
201
+
202
+ $status_array = 'medium';
203
+ $status = array(
204
+ 'text' => __( 'A user with id 1 still exists.', 'better-wp-security' ),
205
+ 'link' => '#itsec_authentication_admin_user_userid', 'advanced' => true,
206
+ );
207
+
208
+ }
209
+
210
+ array_push( $statuses[$status_array], $status );
211
+
212
+ return $statuses;
213
+
214
+ }
215
+
216
+ /**
217
+ * Execute admin initializations
218
+ *
219
+ * @return void
220
+ */
221
+ public function initialize_admin() {
222
+
223
+ $this->settings = ( username_exists( 'admin' ) || ITSEC_Lib::user_id_exists( 1 ) ) ? false : true;
224
+
225
+ if ( ! $this->settings === true && isset( $_POST['itsec_enable_admin_user'] ) && $_POST['itsec_enable_admin_user'] == 'true' ) {
226
+
227
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'ITSEC_admin_save' ) ) {
228
+ die( __( 'Security check', 'better-wp-security' ) );
229
+ }
230
+
231
+ //Process admin user
232
+ $username = isset( $_POST['itsec_admin_user_username'] ) ? trim( sanitize_text_field( $_POST['itsec_admin_user_username'] ) ) : null;
233
+ $change_id_1 = ( isset( $_POST['itsec_admin_user_id'] ) && intval( $_POST['itsec_admin_user_id'] == 1 ) ? true : false );
234
+
235
+ $admin_success = true;
236
+
237
+ if ( strlen( $username ) >= 1 ) {
238
+
239
+ $admin_success = $this->change_admin_user( $username, $change_id_1 );
240
+
241
+ } elseif ( $change_id_1 === true ) {
242
+
243
+ $admin_success = $this->change_admin_user( null, $change_id_1 );
244
+
245
+ }
246
+
247
+ if ( $admin_success === false ) {
248
+
249
+ $type = 'error';
250
+ $message = __( 'The new admin username you entered is invalid or WordPress could not change the user id or username. Please check the name and try again.', 'better-wp-security' );
251
+
252
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
253
+
254
+ }
255
+
256
+ if ( is_multisite() ) {
257
+
258
+ if ( isset( $type ) ) {
259
+
260
+ $error_handler = new WP_Error();
261
+
262
+ $error_handler->add( $type, $message );
263
+
264
+ $this->core->show_network_admin_notice( $error_handler );
265
+
266
+ } else {
267
+
268
+ $this->core->show_network_admin_notice( false );
269
+
270
+ }
271
+
272
+ $this->settings = true;
273
+
274
+ }
275
+
276
+ if ( $admin_success === true ) {
277
+
278
+ $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ITSEC_Lib::get_home_root() . 'wp-login.php?loggedout=true';
279
+ wp_safe_redirect( $redirect_to );
280
+
281
+ }
282
+
283
+ }
284
+
285
+ }
286
+
287
+ /**
288
+ * Render the settings metabox
289
+ *
290
+ * @since 4.0
291
+ *
292
+ * @return void
293
+ */
294
+ public function metabox_admin_user_settings() {
295
+
296
+ $this->settings = ( username_exists( 'admin' ) || ITSEC_Lib::user_id_exists( 1 ) ) ? false : true;
297
+
298
+ if ( $this->settings === true ) {
299
+
300
+ echo '<p>' . __( 'It looks like you have already removed the admin user. No further action is necessary.', 'better-wp-security' ) . '</p>';
301
+
302
+ } else {
303
+
304
+ echo '<p>' . __( 'This feature will improve the security of your WordPress installation by removing common user attributes that can be used to target your site.', 'better-wp-security' ) . '</p>';
305
+ echo sprintf( '<div class="itsec-warning-message"><span>%s: </span><a href="?page=toplevel_page_itsec_backups">%s</a> %s</div>', __( 'WARNING', 'better-wp-security' ), __( 'Backup your database', 'better-wp-security' ), __( 'before changing the admin user.', 'better-wp-security' ) );
306
+ echo sprintf( '<div class="itsec-notice-message"><span>%s: </span> %s </div>', __( 'Notice', 'better-wp-security' ), __( 'Changing the admin username or id of user 1 will log you out of your site requiring you to log back in again.', 'better-wp-security' ) );
307
+
308
+ ?>
309
+
310
+ <form method="post" action="?page=toplevel_page_itsec_advanced&settings-updated=true"
311
+ class="itsec-form">
312
+
313
+ <?php wp_nonce_field( 'ITSEC_admin_save', 'wp_nonce' ); ?>
314
+
315
+ <table class="form-table">
316
+ <tr valign="top">
317
+ <th scope="row" class="settinglabel">
318
+ <label
319
+ for="itsec_enable_admin_user"><?php _e( 'Enable Change Admin User', 'better-wp-security' ); ?></label>
320
+ </th>
321
+ <td class="settingfield">
322
+ <?php //username field ?>
323
+ <input type="checkbox" id="itsec_enable_admin_user" name="itsec_enable_admin_user"
324
+ value="true"/>
325
+
326
+ <p class="description"><?php _e( 'Check this box to enable admin user renaming.', 'better-wp-security' ); ?></p>
327
+ </td>
328
+ </tr>
329
+
330
+ <?php if ( username_exists( 'admin' ) ) { ?>
331
+ <tr valign="top" id="admin_user_username_field">
332
+ <th scope="row" class="settinglabel">
333
+ <label
334
+ for="itsec_admin_user_username"><?php _e( 'New Admin Username', 'better-wp-security' ); ?></label>
335
+ </th>
336
+ <td class="settingfield">
337
+ <?php //username field ?>
338
+ <input name="itsec_admin_user_username" id="itsec_admin_user_username" value=""
339
+ type="text"><br/>
340
+
341
+ <p class="description"><?php _e( 'Enter a new username to replace "admin." Please note that if you are logged in as admin you will have to log in again.', 'better-wp-security' ); ?></p>
342
+ </td>
343
+ </tr>
344
+ <?php } ?>
345
+ <?php if ( ITSEC_Lib::user_id_exists( 1 ) ) { ?>
346
+ <tr valign="top" id="admin_user_id_field">
347
+ <th scope="row" class="settinglabel">
348
+ <label
349
+ for="itsec_admin_user_id"><?php _e( 'Change User ID 1', 'better-wp-security' ); ?></label>
350
+ </th>
351
+ <td class="settingfield">
352
+ <?php //username field ?>
353
+ <input type="checkbox" id="itsec_admin_user_id" name="itsec_admin_user_id" value="1"/>
354
+
355
+ <p class="description"><?php _e( 'Change the ID of the user with ID 1.', 'better-wp-security' ); ?></p>
356
+ </td>
357
+ </tr>
358
+ <?php } ?>
359
+ </table>
360
+ <p class="submit">
361
+ <input type="submit" class="button-primary"
362
+ value="<?php _e( 'Save Admin User', 'better-wp-security' ); ?>"/>
363
+ </p>
364
+ </form>
365
+
366
+ <?php
367
+
368
+ }
369
+
370
+ }
371
+
372
+ }
core/modules/admin-user/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/admin-user/js/admin-admin-user.js ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_enable_admin_user" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_enable_admin_user" ).is( ':checked' ) ) {
6
+ jQuery( "#admin_user_username_field, #admin_user_id_field" ).show();
7
+
8
+ } else {
9
+ jQuery( "#admin_user_username_field, #admin_user_id_field" ).hide();
10
+
11
+ }
12
+
13
+ } ).change();
14
+
15
+ } );
core/modules/admin-user/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/class-ithemes-sync-verb-itsec-get-away-mode.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Get_Away_Mode extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-get-away-mode';
6
+ public static $description = 'Retrieve current away mode status.';
7
+
8
+ public $default_arguments = array();
9
+
10
+ public function run( $arguments ) {
11
+
12
+ $away = ITSEC_Away_Mode::check_away( null, true );
13
+
14
+ if ( $away !== false && $away > 0 ) {
15
+
16
+ $away_enabled = true;
17
+ $next = $away;
18
+
19
+ } elseif ( $away !== false ) {
20
+
21
+ $away_enabled = false;
22
+ $next = absint( $away );
23
+
24
+ }
25
+
26
+ return array(
27
+ 'api' => '0',
28
+ 'enabled' => $away_enabled,
29
+ 'next' => $next,
30
+ );
31
+
32
+ }
33
+
34
+ }
core/modules/away-mode/class-ithemes-sync-verb-itsec-override-away-mode.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Override_Away_Mode extends Ithemes_Sync_Verb {
4
+
5
+ public static $name = 'itsec-override-away-mode';
6
+ public static $description = 'Override current away mode status.';
7
+
8
+ public $default_arguments = array(
9
+ 'id' => '', //lockout id to release
10
+ );
11
+
12
+ public function run( $arguments ) {
13
+
14
+ global $itsec_globals;
15
+
16
+ $current_status = ITSEC_Away_Mode::check_away( null, true );
17
+ $intention = sanitize_text_field( $arguments['intention'] );
18
+ $saved_options = get_site_option( 'itsec_away_mode_sync_override' );
19
+ $saved_intention = isset( $saved_options['intention'] ) ? $saved_options['intention'] : false;
20
+
21
+ switch ( $intention ) {
22
+
23
+ case 'activate': //process activation
24
+
25
+ if ( $saved_intention === false && $current_status < 0 ) { //option doesn't already exist
26
+
27
+ $success = add_site_option( 'itsec_away_mode_sync_override', array(
28
+ 'intention' => $intention,
29
+ 'expires' => ( $itsec_globals['current_time'] + absint( $current_status ) )
30
+ ) );
31
+
32
+ } elseif ( $saved_intention == 'deactivate' ) { //allready tried to override
33
+
34
+ $success = delete_site_option( 'itsec_away_mode_sync_override' );
35
+
36
+ }
37
+
38
+ break;
39
+
40
+ case 'deactivate': //process deactivation
41
+
42
+ if ( $saved_intention === false && $current_status > 0 ) {
43
+
44
+ $success = add_site_option( 'itsec_away_mode_sync_override', array(
45
+ 'intention' => $intention,
46
+ 'expires' => ( $itsec_globals['current_time'] + absint( $current_status ) )
47
+ ) );
48
+
49
+ } elseif ( $saved_intention == 'activate' ) {
50
+
51
+ $success = delete_site_option( 'itsec_away_mode_sync_override' );
52
+
53
+ }
54
+
55
+ break;
56
+
57
+ default: //invalid intention
58
+
59
+ $success = false;
60
+
61
+ break;
62
+
63
+ }
64
+
65
+ if ( $success === false ) {
66
+ $status = 'error';
67
+ } else {
68
+ $status = $intention . 'd';
69
+ }
70
+
71
+ return array(
72
+ 'api' => '0',
73
+ 'status' => $status
74
+ );
75
+
76
+ }
77
+
78
+ }
core/modules/away-mode/class-itsec-away-mode-admin.php ADDED
@@ -0,0 +1,603 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Away_Mode_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $away_file,
9
+ $module_path;
10
+
11
+ function run( $core ) {
12
+
13
+ global $itsec_globals;
14
+
15
+ $this->core = $core;
16
+ $this->settings = get_site_option( 'itsec_away_mode' );
17
+ $this->away_file = $itsec_globals['ithemes_dir'] . '/itsec_away.confg'; //override file
18
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
19
+
20
+ add_action( 'itsec_add_admin_meta_boxes', array(
21
+ $this, 'add_admin_meta_boxes'
22
+ ) ); //add meta boxes to admin page
23
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
24
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
25
+ add_filter( 'itsec_add_dashboard_status', array(
26
+ $this, 'dashboard_status'
27
+ ) ); //add information for plugin status
28
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
29
+
30
+ //manually save options on multisite
31
+ if ( is_multisite() ) {
32
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
33
+ }
34
+
35
+ }
36
+
37
+ /**
38
+ * Add meta boxes to primary options pages
39
+ *
40
+ * @return void
41
+ */
42
+ public function add_admin_meta_boxes() {
43
+
44
+ $id = 'away_mode_options';
45
+ $title = __( 'Away Mode', 'better-wp-security' );
46
+
47
+ add_meta_box(
48
+ $id,
49
+ $title,
50
+ array( $this, 'metabox_away_mode_settings' ),
51
+ 'security_page_toplevel_page_itsec_settings',
52
+ 'advanced',
53
+ 'core'
54
+ );
55
+
56
+ $this->core->add_toc_item(
57
+ array(
58
+ 'id' => $id,
59
+ 'title' => $title,
60
+ )
61
+ );
62
+
63
+ }
64
+
65
+ /**
66
+ * Add Away mode Javascript
67
+ *
68
+ * @return void
69
+ */
70
+ public function admin_script() {
71
+
72
+ global $itsec_globals;
73
+
74
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
75
+
76
+ wp_enqueue_script( 'itsec_away_mode_js', $this->module_path . 'js/admin-away-mode.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
77
+ wp_enqueue_script( 'jquery-ui-datepicker' );
78
+ wp_enqueue_style( 'jquery-datepicker-style', $this->module_path . 'css/smoothness/jquery-ui-1.10.4.custom.css', array(), $itsec_globals['plugin_build'] );
79
+
80
+ }
81
+
82
+ }
83
+
84
+ /**
85
+ * echos Enable Away Mode Field
86
+ *
87
+ * @since 4.0
88
+ *
89
+ * @return void
90
+ */
91
+ public function away_mode_enabled() {
92
+
93
+ //disable the option if away mode is in the past
94
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true && ( $this->settings['type'] == 1 || ( $this->settings['end'] > current_time( 'timestamp' ) || $this->settings['type'] === 2 ) ) ) {
95
+ $enabled = 1;
96
+ } else {
97
+ $enabled = 0;
98
+ }
99
+
100
+ $content = '<input type="checkbox" id="itsec_away_mode_enabled" name="itsec_away_mode[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
101
+ $content .= '<label for="itsec_away_mode_enabled"> ' . __( 'Enable away mode', 'better-wp-security' ) . '</label>';
102
+
103
+ echo $content;
104
+
105
+ }
106
+
107
+ /**
108
+ * echos End date field
109
+ *
110
+ * @since 4.0
111
+ *
112
+ * @return void
113
+ */
114
+ public function away_mode_end_date() {
115
+
116
+ $current = current_time( 'timestamp' ); //The current time
117
+
118
+ //if saved date is in the past update it to something in the future
119
+ if ( isset( $this->settings['end'] ) && isset( $this->settings['enabled'] ) && $current < $this->settings['end'] ) {
120
+ $end = $this->settings['end'];
121
+ } else {
122
+ $end = strtotime( date( 'n/j/y 12:00 \a\m', ( current_time( 'timestamp' ) + ( 86400 * 2 ) ) ) );
123
+ }
124
+
125
+ //Date Field
126
+ $content = '<input class="end_date_field" type="text" id="itsec_away_mode_end_date" name="itsec_away_mode[away_end][date]" value="' . date( 'm/d/y', $end ) . '"/><br>';
127
+ $content .= '<label class="end_date_field" for="itsec_away_mode_end_date"> ' . __( 'Set the date at which the admin dashboard should become available', 'better-wp-security' ) . '</label>';
128
+
129
+ echo $content;
130
+
131
+ }
132
+
133
+ /**
134
+ * echos End time field
135
+ *
136
+ * @since 4.0
137
+ *
138
+ * @return void
139
+ */
140
+ public function away_mode_end_time() {
141
+
142
+ $current = current_time( 'timestamp' ); //The current time
143
+
144
+ //if saved date is in the past update it to something in the future
145
+ if ( isset( $this->settings['end'] ) && isset( $this->settings['enabled'] ) && $current < $this->settings['end'] ) {
146
+ $end = $this->settings['end'];
147
+ } else {
148
+ $end = strtotime( date( 'n/j/y 6:00 \a\m', ( current_time( 'timestamp' ) + ( 86400 * 2 ) ) ) );
149
+ }
150
+
151
+ //Hour Field
152
+ $content = '<select name="itsec_away_mode[away_end][hour]" id="itsec_away_mode_away_mod_end_time">';
153
+
154
+ for ( $i = 1; $i <= 12; $i ++ ) {
155
+ $content .= '<option value="' . sprintf( '%02d', $i ) . '" ' . selected( date( 'g', $end ), $i, false ) . '>' . $i . '</option>';
156
+ }
157
+
158
+ $content .= '</select>';
159
+
160
+ //Minute Field
161
+ $content .= '<select name="itsec_away_mode[away_end][minute]" id="itsec_away_mode_away_mod_end_time">';
162
+
163
+ for ( $i = 0; $i <= 59; $i ++ ) {
164
+
165
+ $content .= '<option value="' . sprintf( '%02d', $i ) . '" ' . selected( date( 'i', $end ), sprintf( '%02d', $i ), false ) . '>' . sprintf( '%02d', $i ) . '</option>';
166
+ }
167
+
168
+ $content .= '</select>';
169
+
170
+ //AM/PM Field
171
+ $content .= '<select name="itsec_away_mode[away_end][sel]" id="itsec_away_mode">';
172
+ $content .= '<option value="am" ' . selected( date( 'a', $end ), 'am', false ) . '>' . __( 'am', 'better-wp-security' ) . '</option>';
173
+ $content .= '<option value="pm" ' . selected( date( 'a', $end ), 'pm', false ) . '>' . __( 'pm', 'better-wp-security' ) . '</option>';
174
+ $content .= '</select><br>';
175
+ $content .= '<label for="itsec_away_mode_away_mod_end_time"> ' . __( 'Set the time at which the admin dashboard should become available again.', 'better-wp-security' ) . '</label>';
176
+
177
+ echo $content;
178
+
179
+ }
180
+
181
+ /**
182
+ * echos Start date field
183
+ *
184
+ * @since 4.0
185
+ *
186
+ * @return void
187
+ */
188
+ public function away_mode_start_date() {
189
+
190
+ $current = current_time( 'timestamp' ); //The current time
191
+
192
+ //if saved date is in the past update it to something in the future
193
+ if ( isset( $this->settings['start'] ) && isset( $this->settings['enabled'] ) && $current < $this->settings['end'] ) {
194
+ $start = $this->settings['start'];
195
+ } else {
196
+ $start = strtotime( date( 'n/j/y 12:00 \a\m', ( current_time( 'timestamp' ) + ( 86400 ) ) ) );
197
+ }
198
+
199
+ //Date Field
200
+ $content = '<input class="start_date_field" type="text" id="itsec_away_mode_start_date" name="itsec_away_mode[away_start][date]" value="' . date( 'm/d/y', $start ) . '"/><br>';
201
+ $content .= '<label class="start_date_field" for="itsec_away_mode_start_date"> ' . __( 'Set the date at which the admin dashboard should become unavailable', 'better-wp-security' ) . '</label>';
202
+
203
+ echo $content;
204
+
205
+ }
206
+
207
+ /**
208
+ * echos Start time field
209
+ *
210
+ * @since 4.0
211
+ *
212
+ * @return void
213
+ */
214
+ public function away_mode_start_time() {
215
+
216
+ $current = current_time( 'timestamp' ); //The current time
217
+
218
+ //if saved date is in the past update it to something in the future
219
+ if ( isset( $this->settings['start'] ) && isset( $this->settings['enabled'] ) && $current < $this->settings['end'] ) {
220
+ $start = $this->settings['start'];
221
+ } else {
222
+ $start = strtotime( date( 'n/j/y 12:00 \a\m', ( current_time( 'timestamp' ) + ( 86400 ) ) ) );
223
+ }
224
+
225
+ //Hour Field
226
+ $content = '<select name="itsec_away_mode[away_start][hour]" id="itsec_away_mode_away_mod_start_time">';
227
+
228
+ for ( $i = 1; $i <= 12; $i ++ ) {
229
+ $content .= '<option value="' . sprintf( '%02d', $i ) . '" ' . selected( date( 'g', $start ), $i, false ) . '>' . $i . '</option>';
230
+ }
231
+
232
+ $content .= '</select>';
233
+
234
+ //Minute Field
235
+ $content .= '<select name="itsec_away_mode[away_start][minute]" id="itsec_away_mode_away_mod_start_time">';
236
+
237
+ for ( $i = 0; $i <= 59; $i ++ ) {
238
+
239
+ $content .= '<option value="' . sprintf( '%02d', $i ) . '" ' . selected( date( 'i', $start ), sprintf( '%02d', $i ), false ) . '>' . sprintf( '%02d', $i ) . '</option>';
240
+ }
241
+
242
+ $content .= '</select>';
243
+
244
+ //AM/PM Field
245
+ $content .= '<select name="itsec_away_mode[away_start][sel]" id="itsec_away_mode_away_mod_start_time">';
246
+ $content .= '<option value="am" ' . selected( date( 'a', $start ), 'am', false ) . '>' . __( 'am', 'better-wp-security' ) . '</option>';
247
+ $content .= '<option value="pm" ' . selected( date( 'a', $start ), 'pm', false ) . '>' . __( 'pm', 'better-wp-security' ) . '</option>';
248
+ $content .= '</select><br>';
249
+ $content .= '<label for="itsec_away_mode_away_mod_start_time"> ' . __( 'Set the time at which the admin dashboard should become unavailable.', 'better-wp-security' ) . '</label>';
250
+
251
+ echo $content;
252
+
253
+ }
254
+
255
+ /**
256
+ * echos type Field
257
+ *
258
+ * @since 4.0
259
+ *
260
+ * @return void
261
+ */
262
+ public function away_mode_type() {
263
+
264
+ $content = '<select name="itsec_away_mode[type]" id="itsec_away_mode_type">';
265
+ $content .= '<option value="1" ' . selected( $this->settings['type'], 1, false ) . '>' . __( 'Daily', 'better-wp-security' ) . '</option>';
266
+ $content .= '<option value="2" ' . selected( $this->settings['type'], 2, false ) . '>' . __( 'One Time', 'better-wp-security' ) . '</option>';
267
+ $content .= '</select><br>';
268
+ $content .= '<label for="itsec_away_mode_type"> ' . __( 'Select the type of restriction you would like to enable', 'better-wp-security' ) . '</label>';
269
+
270
+ echo $content;
271
+
272
+ }
273
+
274
+ /**
275
+ * Sets the status in the plugin dashboard
276
+ *
277
+ * @since 4.0
278
+ *
279
+ * @return array array of statuses
280
+ */
281
+ public function dashboard_status( $statuses ) {
282
+
283
+ if ( $this->settings['enabled'] === true ) {
284
+
285
+ $status_array = 'safe-medium';
286
+ $status = array(
287
+ 'text' => __( 'Away Mode is enabled and your WordPress Dashboard is not available when you will not be needing it.', 'better-wp-security' ),
288
+ 'link' => '#itsec_away_mode_enabled',
289
+ );
290
+
291
+ } else {
292
+
293
+ $status_array = 'medium';
294
+ $status = array(
295
+ 'text' => __( 'Your WordPress Dashboard is available 24/7. Do you really update 24 hours a day? Consider using Away Mode.', 'better-wp-security' ),
296
+ 'link' => '#itsec_away_mode_enabled',
297
+ );
298
+
299
+ }
300
+
301
+ array_push( $statuses[$status_array], $status );
302
+
303
+ return $statuses;
304
+
305
+ }
306
+
307
+ /**
308
+ * Execute admin initializations
309
+ *
310
+ * @return void
311
+ */
312
+ public function initialize_admin() {
313
+
314
+ //Add Settings sections
315
+ add_settings_section(
316
+ 'away_mode-enabled',
317
+ __( 'Away Mode', 'better-wp-security' ),
318
+ '__return_empty_string',
319
+ 'security_page_toplevel_page_itsec_settings'
320
+ );
321
+
322
+ add_settings_section(
323
+ 'away_mode-settings',
324
+ __( 'Away Mode', 'better-wp-security' ),
325
+ '__return_empty_string',
326
+ 'security_page_toplevel_page_itsec_settings'
327
+ );
328
+
329
+ //Away Mode Fields
330
+ add_settings_field(
331
+ 'itsec_away_mode[enabled]',
332
+ __( 'Away Mode', 'better-wp-security' ),
333
+ array( $this, 'away_mode_enabled' ),
334
+ 'security_page_toplevel_page_itsec_settings',
335
+ 'away_mode-enabled'
336
+ );
337
+
338
+ add_settings_field(
339
+ 'itsec_away_mode[type]',
340
+ __( 'Type of Restriction', 'better-wp-security' ),
341
+ array( $this, 'away_mode_type' ),
342
+ 'security_page_toplevel_page_itsec_settings',
343
+ 'away_mode-settings'
344
+ );
345
+
346
+ add_settings_field(
347
+ 'itsec_away_mode[start_date]', __( 'Start Date', 'better-wp-security' ),
348
+ array( $this, 'away_mode_start_date' ),
349
+ 'security_page_toplevel_page_itsec_settings',
350
+ 'away_mode-settings'
351
+ );
352
+
353
+ add_settings_field(
354
+ 'itsec_away_mode[start_time]', __( 'Start Time', 'better-wp-security' ),
355
+ array( $this, 'away_mode_start_time' ),
356
+ 'security_page_toplevel_page_itsec_settings',
357
+ 'away_mode-settings'
358
+ );
359
+
360
+ add_settings_field(
361
+ 'itsec_away_mode[end_date]',
362
+ __( 'End Date', 'better-wp-security' ),
363
+ array( $this, 'away_mode_end_date' ),
364
+ 'security_page_toplevel_page_itsec_settings',
365
+ 'away_mode-settings'
366
+ );
367
+
368
+ add_settings_field(
369
+ 'itsec_away_mode[end_time]',
370
+ __( 'End Time', 'better-wp-security' ),
371
+ array( $this, 'away_mode_end_time' ),
372
+ 'security_page_toplevel_page_itsec_settings',
373
+ 'away_mode-settings'
374
+ );
375
+
376
+ //Register the settings field for the entire module
377
+ register_setting(
378
+ 'security_page_toplevel_page_itsec_settings',
379
+ 'itsec_away_mode',
380
+ array( $this, 'sanitize_module_input' )
381
+ );
382
+
383
+ }
384
+
385
+ /**
386
+ * Render the settings metabox
387
+ *
388
+ * @since 4.0
389
+ *
390
+ * @return void
391
+ */
392
+ public function metabox_away_mode_settings() {
393
+
394
+ $content = '<p>' . __( 'As most sites are only updated at certain times of the day it is not always necessary to provide access to the WordPress dashboard 24 hours a day, 7 days a week. The options below will allow you to disable access to the WordPress Dashboard for the specified period. In addition to limiting exposure to attackers this could also be useful to disable site access based on a schedule for classroom or other reasons.', 'better-wp-security' ) . '</p>';
395
+
396
+ if ( preg_match( "/^(G|H)(:| \\h)/", get_option( 'time_format' ) ) ) {
397
+ $currdate = date_i18n( 'l, d F Y' . ' ' . get_option( 'time_format' ), current_time( 'timestamp' ) );
398
+ } else {
399
+ $currdate = date( 'g:i a \o\n l F jS, Y', current_time( 'timestamp' ) );
400
+ }
401
+
402
+ $content .= '<p>' . sprintf( __( 'Please note that according to your %sWordPress timezone settings%s your current time is:', 'better-wp-security' ), '<a href="' . admin_url( 'options-general.php#timezone_string' ) . '">', '</a>' );
403
+ $content .= '<div class="current-time-date">' . $currdate . '</div>';
404
+ $content .= '<p>' . sprintf( __( 'If this is incorrect please correct it on the %sWordPress general settings page%s by setting the appropriate time zone. Failure to set the correct timezone may result in unintended lockouts.', 'better-wp-security' ), '<a href="' . admin_url( 'options-general.php#timezone_string' ) . '">', '</a>' ) . '</p>';
405
+
406
+ echo $content;
407
+
408
+ //set information explaining away mode is enabled
409
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === 1 && ( $this->settings['type'] === 1 || ( $this->settings['end'] > current_time( 'timestamp' ) ) ) ) {
410
+
411
+ $content = '<hr />';
412
+
413
+ $content .= sprintf( '<p><strong>%s</strong></p>', __( 'Away mode is currently enabled.', 'better-wp-security' ) );
414
+
415
+ //Create the appropriate notification based on daily or one time use
416
+ if ( $this->settings['type'] === 1 ) {
417
+
418
+ $content .= sprintf( '<p>' . __( 'The dashboard of this website will become unavailable %s%s%s from %s%s%s until %s%s%s.', 'better-wp-security' ) . '</p>', '<strong>', __( 'every day', 'better-wp-security' ), '</strong>', '<strong>', date_i18n( get_option( 'time_format' ), $this->settings['start'] ), '</strong>', '<strong>', date_i18n( get_option( 'time_format' ), $this->settings['end'] ), '</strong>' );
419
+
420
+ } else {
421
+
422
+ $content .= sprintf( '<p>' . __( 'The dashboard of this website will become unavailable from %s%s%s on %s%s%s until %s%s%s on %s%s%s.', 'better-wp-security' ) . '</p>', '<strong>', date_i18n( get_option( 'time_format' ), $this->settings['start'] ), '</strong>', '<strong>', date_i18n( get_option( 'date_format' ), $this->settings['start'] ), '</strong>', '<strong>', date_i18n( get_option( 'time_format' ), $this->settings['end'] ), '</strong>', '<strong>', date_i18n( get_option( 'date_format' ), $this->settings['end'] ), '</strong>' );
423
+
424
+ }
425
+
426
+ $content .= '<p>' . __( 'You will not be able to log into this website when the site is unavailable.', 'better-wp-security' ) . '</p>';
427
+
428
+ echo $content;
429
+ }
430
+
431
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'away_mode-enabled', false );
432
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'away_mode-settings', false );
433
+
434
+ echo '<p>' . PHP_EOL;
435
+
436
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
437
+
438
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
439
+
440
+ echo '</p>' . PHP_EOL;
441
+
442
+ }
443
+
444
+ /**
445
+ * Sanitize and validate input
446
+ *
447
+ * @param Array $input array of input fields
448
+ *
449
+ * @return Array Sanitized array
450
+ */
451
+ public function sanitize_module_input( $input ) {
452
+
453
+ //process away mode settings
454
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
455
+ $input['type'] = ( isset( $input['type'] ) && intval( $input['type'] == 1 ) ? 1 : 2 );
456
+
457
+ if ( ! isset( $input['away_start'] ) && ! isset( $input['start'] ) ) {
458
+
459
+ $input['start'] = $this->settings['start'];
460
+
461
+ } elseif ( ! isset( $input['away_start'] ) && isset( $input['start'] ) ) {
462
+
463
+ $input['start'] = intval( $input['start'] );
464
+
465
+ } else {
466
+
467
+ $input['start'] = strtotime( $input['away_start']['date'] . ' ' . $input['away_start']['hour'] . ':' . $input['away_start']['minute'] . ' ' . $input['away_start']['sel'] );
468
+ unset( $input['away_start'] );
469
+
470
+ }
471
+
472
+ if ( ! isset( $input['away_end'] ) && ! isset( $input['end'] ) ) {
473
+
474
+ $input['end'] = $this->settings['end'];
475
+
476
+ } elseif ( ! isset( $input['away_end'] ) && isset( $input['end'] ) ) {
477
+
478
+ $input['end'] = intval( $input['end'] );
479
+
480
+ } else {
481
+
482
+ $input['end'] = strtotime( $input['away_end']['date'] . ' ' . $input['away_end']['hour'] . ':' . $input['away_end']['minute'] . ' ' . $input['away_end']['sel'] );
483
+ unset( $input['away_end'] );
484
+
485
+ }
486
+
487
+ if ( $input['enabled'] === true && $input['start'] === 1 && $input['end'] === 1 ) {
488
+ $input['enabled'] = false;
489
+ }
490
+
491
+ if ( ! class_exists( 'ITSEC_Away_Mode' ) ) {
492
+ require( dirname( __FILE__ ) . '/class-itsec-away-mode.php' );
493
+ }
494
+
495
+ if ( $input['enabled'] === true && ITSEC_Away_Mode::check_away( $input ) === true ) {
496
+
497
+ $input['enabled'] = false; //disable away mode
498
+
499
+ $type = 'error';
500
+ $message = __( 'Invalid away mode time listed. The time entered would lock you out of your site now. Please try again.', 'better-wp-security' );
501
+
502
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
503
+
504
+ }
505
+
506
+ if ( $input['enabled'] === true && $input['type'] === 2 && $input['end'] < $input['start'] ) {
507
+
508
+ $input['enabled'] = false; //disable away mode
509
+
510
+ $type = 'error';
511
+ $message = __( 'Invalid away mode time listed. The start time selected is after the end time selected.', 'better-wp-security' );
512
+
513
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
514
+
515
+ }
516
+
517
+ if ( $input['enabled'] === true && $input['type'] === 2 && $input['end'] < current_time( 'timestamp' ) ) {
518
+
519
+ $input['enabled'] = false; //disable away mode
520
+
521
+ $type = 'error';
522
+ $message = __( 'Invalid away mode time listed. The period selected already ended.', 'better-wp-security' );
523
+
524
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
525
+
526
+ }
527
+
528
+ if ( $input['enabled'] === true && ! file_exists( $this->away_file ) ) {
529
+
530
+ @file_put_contents( $this->away_file, 'true' );
531
+
532
+ } elseif ( $input['enabled'] === false ) {
533
+
534
+ @unlink( $this->away_file );
535
+
536
+ }
537
+
538
+ //process other settings
539
+
540
+ if ( is_multisite() ) {
541
+
542
+ if ( isset( $type ) ) {
543
+
544
+ $error_handler = new WP_Error();
545
+
546
+ $error_handler->add( $type, $message );
547
+
548
+ $this->core->show_network_admin_notice( $error_handler );
549
+
550
+ } else {
551
+
552
+ $this->core->show_network_admin_notice( false );
553
+
554
+ }
555
+
556
+ $this->settings = $input;
557
+
558
+ }
559
+
560
+ return $input;
561
+
562
+ }
563
+
564
+ /**
565
+ * Prepare and save options in network settings
566
+ *
567
+ * @return void
568
+ */
569
+ public function save_network_options() {
570
+
571
+ if ( isset( $_POST['itsec_away_mode'] ) ) {
572
+
573
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
574
+ die( __( 'Security error!', 'better-wp-security' ) );
575
+ }
576
+
577
+ update_site_option( 'itsec_away_mode', $_POST['itsec_away_mode'] ); //we must manually save network options
578
+
579
+ }
580
+
581
+ }
582
+
583
+ /**
584
+ * Adds fields that will be tracked for Google Analytics
585
+ *
586
+ * @since 4.0
587
+ *
588
+ * @param array $vars tracking vars
589
+ *
590
+ * @return array tracking vars
591
+ */
592
+ public function tracking_vars( $vars ) {
593
+
594
+ $vars['itsec_away_mode'] = array(
595
+ 'enabled' => '0:b',
596
+ 'type' => '0:b',
597
+ );
598
+
599
+ return $vars;
600
+
601
+ }
602
+
603
+ }
core/modules/away-mode/class-itsec-away-mode.php ADDED
@@ -0,0 +1,268 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Away_Mode {
4
+
5
+ function run() {
6
+
7
+ //Execute away mode functions on admin init
8
+ add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
9
+ add_action( 'itsec_admin_init', array( $this, 'execute_away_mode' ) );
10
+ add_action( 'login_init', array( $this, 'execute_away_mode' ) );
11
+
12
+ //Register Sync
13
+ add_filter( 'itsec_sync_modules', array( $this, 'register_sync' ) );
14
+
15
+ }
16
+
17
+ /**
18
+ * Check if away mode is active
19
+ *
20
+ * @since 4.4
21
+ *
22
+ * @param array $input [NULL] Input of options to check if calling from form
23
+ * @param bool $remaining will return the number of seconds remaining
24
+ * @param bool $override Whether or not we're calculating override values
25
+ *
26
+ * @return mixed true if locked out else false or times until next condition (negative until lockout, positive until release)
27
+ */
28
+ public static function check_away( $input = null, $remaining = false, $override = false ) {
29
+
30
+ global $itsec_globals;
31
+
32
+ ITSEC_Lib::clear_caches(); //lets try to make sure nothing is storing a bad time
33
+
34
+ $form = true;
35
+ $has_away_file = @file_exists( $itsec_globals['ithemes_dir'] . '/itsec_away.confg' );
36
+ $status = false; //assume they're not locked out to start
37
+
38
+ //Normal usage check
39
+ if ( $input === null ) { //if we didn't provide input to check we need to get it
40
+
41
+ $form = false;
42
+ $input = get_site_option( 'itsec_away_mode' );
43
+
44
+ }
45
+
46
+ if ( ( $form === false && ! isset( $input['enabled'] ) ) || ! isset( $input['type'] ) || ! isset( $input['start'] ) || ! isset( $input['end'] ) || ! $has_away_file ) {
47
+ return false; //if we don't have complete settings don't lock them out
48
+ }
49
+
50
+ $current_time = $itsec_globals['current_time']; //use current time
51
+ $enabled = isset( $input['enabled'] ) ? $input['enabled'] : $form;
52
+ $test_type = $input['type'];
53
+ $test_start = $input['start'];
54
+ $test_end = $input['end'];
55
+
56
+ if ( $test_type === 1 ) { //daily
57
+
58
+ $test_start -= strtotime( date( 'Y-m-d', $test_start ) );
59
+ $test_end -= strtotime( date( 'Y-m-d', $test_end ) );
60
+ $day_seconds = $current_time - strtotime( date( 'Y-m-d', $current_time ) );
61
+
62
+ if ( $test_start === $test_end ) {
63
+ $status = false;
64
+ }
65
+
66
+ if ( $test_start < $test_end ) { //same day
67
+
68
+ if ( $test_start <= $day_seconds && $test_end >= $day_seconds && $enabled === true ) {
69
+ $status = $test_end - $day_seconds;
70
+ }
71
+
72
+ } else { //overnight
73
+
74
+ if ( ( $test_start < $day_seconds || $test_end > $day_seconds ) && $enabled === true ) {
75
+
76
+ if ( $day_seconds >= $test_start ) {
77
+
78
+ $status = ( 86400 - $day_seconds ) + $test_end;
79
+
80
+ } else {
81
+
82
+ $status = $test_end - $day_seconds;
83
+
84
+ }
85
+
86
+ }
87
+
88
+ }
89
+
90
+ } else if ( $test_start !== $test_end && $test_start <= $current_time && $test_end >= $current_time && $enabled === true ) { //one time
91
+
92
+ $status = $test_end - $current_time;
93
+
94
+ }
95
+
96
+ //they are allowed to log in
97
+ if ( $status === false ) {
98
+
99
+ if ( $test_type === 1 ) {
100
+
101
+ if ( $day_seconds > $test_start ) { //actually starts tomorrow
102
+
103
+ $status = - ( ( 86400 + $test_start ) - $day_seconds );
104
+
105
+ } else { //starts today
106
+
107
+ $status = - ( $test_start - $day_seconds );
108
+
109
+ }
110
+
111
+ } else {
112
+
113
+ $status = - ( $test_start - $current_time );
114
+
115
+ if ( $status > 0 ) {
116
+
117
+ if ( $form === false && isset( $input['enabled'] ) && $input['enabled'] === true ) { //disable away mode if one-time is in the past
118
+
119
+ $input['enabled'] = false;
120
+ update_site_option( 'itsec_away_mode', $input );
121
+
122
+ }
123
+
124
+ $status = 0;
125
+
126
+ }
127
+
128
+ }
129
+
130
+ }
131
+
132
+ if ( $override === false ) {
133
+
134
+ //work in an override from sync
135
+ $override_option = get_site_option( 'itsec_away_mode_sync_override' );
136
+ $override = $override_option['intention'];
137
+ $expires = $override_option['expires'];
138
+
139
+ if ( $expires < $itsec_globals['current_time'] ) {
140
+
141
+ delete_site_option( 'itsec_away_mode_sync_override' );
142
+
143
+ } else {
144
+
145
+ if ( $override === 'activate' ) {
146
+
147
+ if ( $status <= 0 ) { //not currently locked out
148
+
149
+ $input['start'] = $current_time - 1;
150
+
151
+ $status = ITSEC_Away_Mode::check_away( $input, true, true );
152
+
153
+ } else {
154
+
155
+ delete_site_option( 'itsec_away_mode_sync_override' );
156
+
157
+ }
158
+
159
+ } elseif ( $override === 'deactivate' ) {
160
+
161
+ if ( $status > 0 ) { //currently locked out
162
+
163
+ $input['end'] = $current_time - 1;
164
+
165
+ $status = ITSEC_Away_Mode::check_away( $input, true, true );
166
+
167
+ } else {
168
+
169
+ delete_site_option( 'itsec_away_mode_sync_override' );
170
+
171
+ }
172
+
173
+ }
174
+
175
+ }
176
+
177
+ }
178
+
179
+ if ( $remaining === true ) {
180
+
181
+ return $status;
182
+
183
+ } else {
184
+
185
+ if ( $status > 0 && $status !== false ) {
186
+ return true;
187
+ }
188
+
189
+ }
190
+
191
+ return false; //always default to NOT locking folks out
192
+
193
+ }
194
+
195
+ /**
196
+ * Execute away mode functionality
197
+ *
198
+ * @return void
199
+ */
200
+ public function execute_away_mode() {
201
+
202
+ global $itsec_logger;
203
+
204
+ //execute lockout if applicable
205
+ if ( $this->check_away() ) {
206
+
207
+ $itsec_logger->log_event(
208
+ 'away_mode',
209
+ 5,
210
+ array(
211
+ __( 'A host was prevented from accessing the dashboard due to away-mode restrictions being in effect',
212
+ 'better-wp-security' ),
213
+ ),
214
+ ITSEC_Lib::get_ip(),
215
+ '',
216
+ '',
217
+ '',
218
+ ''
219
+ );
220
+
221
+ wp_redirect( get_option( 'siteurl' ) );
222
+ wp_clear_auth_cookie();
223
+
224
+ }
225
+
226
+ }
227
+
228
+ /**
229
+ * Register 404 and file change detection for logger
230
+ *
231
+ * @param array $logger_modules array of logger modules
232
+ *
233
+ * @return array array of logger modules
234
+ */
235
+ public function register_logger( $logger_modules ) {
236
+
237
+ $logger_modules['away_mode'] = array(
238
+ 'type' => 'away_mode',
239
+ 'function' => __( 'Away Mode Triggered', 'better-wp-security' ),
240
+ );
241
+
242
+ return $logger_modules;
243
+
244
+ }
245
+
246
+ /**
247
+ * Register Lockouts for Sync
248
+ *
249
+ * @param array $sync_modules array of logger modules
250
+ *
251
+ * @return array array of logger modules
252
+ */
253
+ public function register_sync( $sync_modules ) {
254
+
255
+ $sync_modules['away_mode'] = array(
256
+ 'verbs' => array(
257
+ 'itsec-get-away-mode' => 'Ithemes_Sync_Verb_ITSEC_Get_Away_Mode',
258
+ 'itsec-override-away-mode' => 'Ithemes_Sync_Verb_ITSEC_Override_Away_Mode'
259
+ ),
260
+ 'everything' => 'itsec-get-away-mode',
261
+ 'path' => dirname( __FILE__ ),
262
+ );
263
+
264
+ return $sync_modules;
265
+
266
+ }
267
+
268
+ }
core/modules/away-mode/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/css/smoothness/images/animated-overlay.gif ADDED
Binary file
core/modules/away-mode/css/smoothness/images/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-icons_222222_256x240.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-icons_2e83ff_256x240.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-icons_454545_256x240.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-icons_888888_256x240.png ADDED
Binary file
core/modules/away-mode/css/smoothness/images/ui-icons_cd0a0a_256x240.png ADDED
Binary file
core/modules/away-mode/css/smoothness/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/css/smoothness/jquery-ui-1.10.4.custom.css ADDED
@@ -0,0 +1,550 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! jQuery UI - v1.10.4 - 2014-03-24
2
+ * http://jqueryui.com
3
+ * Includes: jquery.ui.core.css, jquery.ui.datepicker.css, jquery.ui.theme.css
4
+ * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
5
+ * Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
6
+
7
+ /* Layout helpers
8
+ ----------------------------------*/
9
+ .ui-helper-hidden {
10
+ display: none;
11
+ }
12
+ .ui-helper-hidden-accessible {
13
+ border: 0;
14
+ clip: rect(0 0 0 0);
15
+ height: 1px;
16
+ margin: -1px;
17
+ overflow: hidden;
18
+ padding: 0;
19
+ position: absolute;
20
+ width: 1px;
21
+ }
22
+ .ui-helper-reset {
23
+ margin: 0;
24
+ padding: 0;
25
+ border: 0;
26
+ outline: 0;
27
+ line-height: 1.3;
28
+ text-decoration: none;
29
+ font-size: 100%;
30
+ list-style: none;
31
+ }
32
+ .ui-helper-clearfix:before,
33
+ .ui-helper-clearfix:after {
34
+ content: "";
35
+ display: table;
36
+ border-collapse: collapse;
37
+ }
38
+ .ui-helper-clearfix:after {
39
+ clear: both;
40
+ }
41
+ .ui-helper-clearfix {
42
+ min-height: 0; /* support: IE7 */
43
+ }
44
+ .ui-helper-zfix {
45
+ width: 100%;
46
+ height: 100%;
47
+ top: 0;
48
+ left: 0;
49
+ position: absolute;
50
+ opacity: 0;
51
+ filter:Alpha(Opacity=0);
52
+ }
53
+
54
+ .ui-front {
55
+ z-index: 100;
56
+ }
57
+
58
+
59
+ /* Interaction Cues
60
+ ----------------------------------*/
61
+ .ui-state-disabled {
62
+ cursor: default !important;
63
+ }
64
+
65
+
66
+ /* Icons
67
+ ----------------------------------*/
68
+
69
+ /* states and images */
70
+ .ui-icon {
71
+ display: block;
72
+ text-indent: -99999px;
73
+ overflow: hidden;
74
+ background-repeat: no-repeat;
75
+ }
76
+
77
+
78
+ /* Misc visuals
79
+ ----------------------------------*/
80
+
81
+ /* Overlays */
82
+ .ui-widget-overlay {
83
+ position: fixed;
84
+ top: 0;
85
+ left: 0;
86
+ width: 100%;
87
+ height: 100%;
88
+ }
89
+ .ui-datepicker {
90
+ width: 17em;
91
+ padding: .2em .2em 0;
92
+ display: none;
93
+ }
94
+ .ui-datepicker .ui-datepicker-header {
95
+ position: relative;
96
+ padding: .2em 0;
97
+ }
98
+ .ui-datepicker .ui-datepicker-prev,
99
+ .ui-datepicker .ui-datepicker-next {
100
+ position: absolute;
101
+ top: 2px;
102
+ width: 1.8em;
103
+ height: 1.8em;
104
+ }
105
+ .ui-datepicker .ui-datepicker-prev-hover,
106
+ .ui-datepicker .ui-datepicker-next-hover {
107
+ top: 1px;
108
+ }
109
+ .ui-datepicker .ui-datepicker-prev {
110
+ left: 2px;
111
+ }
112
+ .ui-datepicker .ui-datepicker-next {
113
+ right: 2px;
114
+ }
115
+ .ui-datepicker .ui-datepicker-prev-hover {
116
+ left: 1px;
117
+ }
118
+ .ui-datepicker .ui-datepicker-next-hover {
119
+ right: 1px;
120
+ }
121
+ .ui-datepicker .ui-datepicker-prev span,
122
+ .ui-datepicker .ui-datepicker-next span {
123
+ display: block;
124
+ position: absolute;
125
+ left: 50%;
126
+ margin-left: -8px;
127
+ top: 50%;
128
+ margin-top: -8px;
129
+ }
130
+ .ui-datepicker .ui-datepicker-title {
131
+ margin: 0 2.3em;
132
+ line-height: 1.8em;
133
+ text-align: center;
134
+ }
135
+ .ui-datepicker .ui-datepicker-title select {
136
+ font-size: 1em;
137
+ margin: 1px 0;
138
+ }
139
+ .ui-datepicker select.ui-datepicker-month,
140
+ .ui-datepicker select.ui-datepicker-year {
141
+ width: 49%;
142
+ }
143
+ .ui-datepicker table {
144
+ width: 100%;
145
+ font-size: .9em;
146
+ border-collapse: collapse;
147
+ margin: 0 0 .4em;
148
+ }
149
+ .ui-datepicker th {
150
+ padding: .7em .3em;
151
+ text-align: center;
152
+ font-weight: bold;
153
+ border: 0;
154
+ }
155
+ .ui-datepicker td {
156
+ border: 0;
157
+ padding: 1px;
158
+ }
159
+ .ui-datepicker td span,
160
+ .ui-datepicker td a {
161
+ display: block;
162
+ padding: .2em;
163
+ text-align: right;
164
+ text-decoration: none;
165
+ }
166
+ .ui-datepicker .ui-datepicker-buttonpane {
167
+ background-image: none;
168
+ margin: .7em 0 0 0;
169
+ padding: 0 .2em;
170
+ border-left: 0;
171
+ border-right: 0;
172
+ border-bottom: 0;
173
+ }
174
+ .ui-datepicker .ui-datepicker-buttonpane button {
175
+ float: right;
176
+ margin: .5em .2em .4em;
177
+ cursor: pointer;
178
+ padding: .2em .6em .3em .6em;
179
+ width: auto;
180
+ overflow: visible;
181
+ }
182
+ .ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
183
+ float: left;
184
+ }
185
+
186
+ /* with multiple calendars */
187
+ .ui-datepicker.ui-datepicker-multi {
188
+ width: auto;
189
+ }
190
+ .ui-datepicker-multi .ui-datepicker-group {
191
+ float: left;
192
+ }
193
+ .ui-datepicker-multi .ui-datepicker-group table {
194
+ width: 95%;
195
+ margin: 0 auto .4em;
196
+ }
197
+ .ui-datepicker-multi-2 .ui-datepicker-group {
198
+ width: 50%;
199
+ }
200
+ .ui-datepicker-multi-3 .ui-datepicker-group {
201
+ width: 33.3%;
202
+ }
203
+ .ui-datepicker-multi-4 .ui-datepicker-group {
204
+ width: 25%;
205
+ }
206
+ .ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
207
+ .ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
208
+ border-left-width: 0;
209
+ }
210
+ .ui-datepicker-multi .ui-datepicker-buttonpane {
211
+ clear: left;
212
+ }
213
+ .ui-datepicker-row-break {
214
+ clear: both;
215
+ width: 100%;
216
+ font-size: 0;
217
+ }
218
+
219
+ /* RTL support */
220
+ .ui-datepicker-rtl {
221
+ direction: rtl;
222
+ }
223
+ .ui-datepicker-rtl .ui-datepicker-prev {
224
+ right: 2px;
225
+ left: auto;
226
+ }
227
+ .ui-datepicker-rtl .ui-datepicker-next {
228
+ left: 2px;
229
+ right: auto;
230
+ }
231
+ .ui-datepicker-rtl .ui-datepicker-prev:hover {
232
+ right: 1px;
233
+ left: auto;
234
+ }
235
+ .ui-datepicker-rtl .ui-datepicker-next:hover {
236
+ left: 1px;
237
+ right: auto;
238
+ }
239
+ .ui-datepicker-rtl .ui-datepicker-buttonpane {
240
+ clear: right;
241
+ }
242
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button {
243
+ float: left;
244
+ }
245
+ .ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
246
+ .ui-datepicker-rtl .ui-datepicker-group {
247
+ float: right;
248
+ }
249
+ .ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
250
+ .ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
251
+ border-right-width: 0;
252
+ border-left-width: 1px;
253
+ }
254
+
255
+ /* Component containers
256
+ ----------------------------------*/
257
+ .ui-widget {
258
+ font-size: 1.1em;
259
+ }
260
+ .ui-widget .ui-widget {
261
+ font-size: 1em;
262
+ }
263
+ .ui-widget input,
264
+ .ui-widget select,
265
+ .ui-widget textarea,
266
+ .ui-widget button {
267
+ font-size: 1em;
268
+ }
269
+ .ui-widget-content {
270
+ background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;
271
+ color: #222222;
272
+ }
273
+ .ui-widget-content a {
274
+ color: #222222;
275
+ }
276
+ .ui-widget-header {
277
+ background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
278
+ color: #222222;
279
+ font-weight: bold;
280
+ }
281
+ .ui-widget-header a {
282
+ color: #222222;
283
+ }
284
+
285
+ /* Interaction Cues
286
+ ----------------------------------*/
287
+ .ui-state-highlight,
288
+ .ui-widget-content .ui-state-highlight,
289
+ .ui-widget-header .ui-state-highlight {
290
+ border: 1px solid #fcefa1;
291
+ background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;
292
+ color: #363636;
293
+ }
294
+ .ui-state-highlight a,
295
+ .ui-widget-content .ui-state-highlight a,
296
+ .ui-widget-header .ui-state-highlight a {
297
+ color: #363636;
298
+ }
299
+ .ui-state-error,
300
+ .ui-widget-content .ui-state-error,
301
+ .ui-widget-header .ui-state-error {
302
+ border: 1px solid #cd0a0a;
303
+ background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
304
+ color: #cd0a0a;
305
+ }
306
+ .ui-state-error a,
307
+ .ui-widget-content .ui-state-error a,
308
+ .ui-widget-header .ui-state-error a {
309
+ color: #cd0a0a;
310
+ }
311
+ .ui-state-error-text,
312
+ .ui-widget-content .ui-state-error-text,
313
+ .ui-widget-header .ui-state-error-text {
314
+ color: #cd0a0a;
315
+ }
316
+ .ui-priority-primary,
317
+ .ui-widget-content .ui-priority-primary,
318
+ .ui-widget-header .ui-priority-primary {
319
+ font-weight: bold;
320
+ }
321
+ .ui-priority-secondary,
322
+ .ui-widget-content .ui-priority-secondary,
323
+ .ui-widget-header .ui-priority-secondary {
324
+ opacity: .7;
325
+ filter:Alpha(Opacity=70);
326
+ font-weight: normal;
327
+ }
328
+ .ui-state-disabled,
329
+ .ui-widget-content .ui-state-disabled,
330
+ .ui-widget-header .ui-state-disabled {
331
+ opacity: .35;
332
+ filter:Alpha(Opacity=35);
333
+ background-image: none;
334
+ }
335
+ .ui-state-disabled .ui-icon {
336
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
337
+ }
338
+
339
+ /* Icons
340
+ ----------------------------------*/
341
+
342
+ /* states and images */
343
+ .ui-icon {
344
+ width: 16px;
345
+ height: 16px;
346
+ }
347
+ .ui-icon,
348
+ .ui-widget-content .ui-icon {
349
+ background-image: url(images/ui-icons_222222_256x240.png);
350
+ }
351
+ .ui-widget-header .ui-icon {
352
+ background-image: url(images/ui-icons_222222_256x240.png);
353
+ }
354
+ .ui-state-default .ui-icon {
355
+ background-image: url(images/ui-icons_888888_256x240.png);
356
+ }
357
+ .ui-state-hover .ui-icon,
358
+ .ui-state-focus .ui-icon {
359
+ background-image: url(images/ui-icons_454545_256x240.png);
360
+ }
361
+ .ui-state-active .ui-icon {
362
+ background-image: url(images/ui-icons_454545_256x240.png);
363
+ }
364
+ .ui-state-highlight .ui-icon {
365
+ background-image: url(images/ui-icons_2e83ff_256x240.png);
366
+ }
367
+ .ui-state-error .ui-icon,
368
+ .ui-state-error-text .ui-icon {
369
+ background-image: url(images/ui-icons_cd0a0a_256x240.png);
370
+ }
371
+
372
+ /* positioning */
373
+ .ui-icon-blank { background-position: 16px 16px; }
374
+ .ui-icon-carat-1-n { background-position: 0 0; }
375
+ .ui-icon-carat-1-ne { background-position: -16px 0; }
376
+ .ui-icon-carat-1-e { background-position: -32px 0; }
377
+ .ui-icon-carat-1-se { background-position: -48px 0; }
378
+ .ui-icon-carat-1-s { background-position: -64px 0; }
379
+ .ui-icon-carat-1-sw { background-position: -80px 0; }
380
+ .ui-icon-carat-1-w { background-position: -96px 0; }
381
+ .ui-icon-carat-1-nw { background-position: -112px 0; }
382
+ .ui-icon-carat-2-n-s { background-position: -128px 0; }
383
+ .ui-icon-carat-2-e-w { background-position: -144px 0; }
384
+ .ui-icon-triangle-1-n { background-position: 0 -16px; }
385
+ .ui-icon-triangle-1-ne { background-position: -16px -16px; }
386
+ .ui-icon-triangle-1-e { background-position: -32px -16px; }
387
+ .ui-icon-triangle-1-se { background-position: -48px -16px; }
388
+ .ui-icon-triangle-1-s { background-position: -64px -16px; }
389
+ .ui-icon-triangle-1-sw { background-position: -80px -16px; }
390
+ .ui-icon-triangle-1-w { background-position: -96px -16px; }
391
+ .ui-icon-triangle-1-nw { background-position: -112px -16px; }
392
+ .ui-icon-triangle-2-n-s { background-position: -128px -16px; }
393
+ .ui-icon-triangle-2-e-w { background-position: -144px -16px; }
394
+ .ui-icon-arrow-1-n { background-position: 0 -32px; }
395
+ .ui-icon-arrow-1-ne { background-position: -16px -32px; }
396
+ .ui-icon-arrow-1-e { background-position: -32px -32px; }
397
+ .ui-icon-arrow-1-se { background-position: -48px -32px; }
398
+ .ui-icon-arrow-1-s { background-position: -64px -32px; }
399
+ .ui-icon-arrow-1-sw { background-position: -80px -32px; }
400
+ .ui-icon-arrow-1-w { background-position: -96px -32px; }
401
+ .ui-icon-arrow-1-nw { background-position: -112px -32px; }
402
+ .ui-icon-arrow-2-n-s { background-position: -128px -32px; }
403
+ .ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
404
+ .ui-icon-arrow-2-e-w { background-position: -160px -32px; }
405
+ .ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
406
+ .ui-icon-arrowstop-1-n { background-position: -192px -32px; }
407
+ .ui-icon-arrowstop-1-e { background-position: -208px -32px; }
408
+ .ui-icon-arrowstop-1-s { background-position: -224px -32px; }
409
+ .ui-icon-arrowstop-1-w { background-position: -240px -32px; }
410
+ .ui-icon-arrowthick-1-n { background-position: 0 -48px; }
411
+ .ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
412
+ .ui-icon-arrowthick-1-e { background-position: -32px -48px; }
413
+ .ui-icon-arrowthick-1-se { background-position: -48px -48px; }
414
+ .ui-icon-arrowthick-1-s { background-position: -64px -48px; }
415
+ .ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
416
+ .ui-icon-arrowthick-1-w { background-position: -96px -48px; }
417
+ .ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
418
+ .ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
419
+ .ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
420
+ .ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
421
+ .ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
422
+ .ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
423
+ .ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
424
+ .ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
425
+ .ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
426
+ .ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
427
+ .ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
428
+ .ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
429
+ .ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
430
+ .ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
431
+ .ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
432
+ .ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
433
+ .ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
434
+ .ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
435
+ .ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
436
+ .ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
437
+ .ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
438
+ .ui-icon-arrow-4 { background-position: 0 -80px; }
439
+ .ui-icon-arrow-4-diag { background-position: -16px -80px; }
440
+ .ui-icon-extlink { background-position: -32px -80px; }
441
+ .ui-icon-newwin { background-position: -48px -80px; }
442
+ .ui-icon-refresh { background-position: -64px -80px; }
443
+ .ui-icon-shuffle { background-position: -80px -80px; }
444
+ .ui-icon-transfer-e-w { background-position: -96px -80px; }
445
+ .ui-icon-transferthick-e-w { background-position: -112px -80px; }
446
+ .ui-icon-folder-collapsed { background-position: 0 -96px; }
447
+ .ui-icon-folder-open { background-position: -16px -96px; }
448
+ .ui-icon-document { background-position: -32px -96px; }
449
+ .ui-icon-document-b { background-position: -48px -96px; }
450
+ .ui-icon-note { background-position: -64px -96px; }
451
+ .ui-icon-mail-closed { background-position: -80px -96px; }
452
+ .ui-icon-mail-open { background-position: -96px -96px; }
453
+ .ui-icon-suitcase { background-position: -112px -96px; }
454
+ .ui-icon-comment { background-position: -128px -96px; }
455
+ .ui-icon-person { background-position: -144px -96px; }
456
+ .ui-icon-print { background-position: -160px -96px; }
457
+ .ui-icon-trash { background-position: -176px -96px; }
458
+ .ui-icon-locked { background-position: -192px -96px; }
459
+ .ui-icon-unlocked { background-position: -208px -96px; }
460
+ .ui-icon-bookmark { background-position: -224px -96px; }
461
+ .ui-icon-tag { background-position: -240px -96px; }
462
+ .ui-icon-home { background-position: 0 -112px; }
463
+ .ui-icon-flag { background-position: -16px -112px; }
464
+ .ui-icon-calendar { background-position: -32px -112px; }
465
+ .ui-icon-cart { background-position: -48px -112px; }
466
+ .ui-icon-pencil { background-position: -64px -112px; }
467
+ .ui-icon-clock { background-position: -80px -112px; }
468
+ .ui-icon-disk { background-position: -96px -112px; }
469
+ .ui-icon-calculator { background-position: -112px -112px; }
470
+ .ui-icon-zoomin { background-position: -128px -112px; }
471
+ .ui-icon-zoomout { background-position: -144px -112px; }
472
+ .ui-icon-search { background-position: -160px -112px; }
473
+ .ui-icon-wrench { background-position: -176px -112px; }
474
+ .ui-icon-gear { background-position: -192px -112px; }
475
+ .ui-icon-heart { background-position: -208px -112px; }
476
+ .ui-icon-star { background-position: -224px -112px; }
477
+ .ui-icon-link { background-position: -240px -112px; }
478
+ .ui-icon-cancel { background-position: 0 -128px; }
479
+ .ui-icon-plus { background-position: -16px -128px; }
480
+ .ui-icon-plusthick { background-position: -32px -128px; }
481
+ .ui-icon-minus { background-position: -48px -128px; }
482
+ .ui-icon-minusthick { background-position: -64px -128px; }
483
+ .ui-icon-close { background-position: -80px -128px; }
484
+ .ui-icon-closethick { background-position: -96px -128px; }
485
+ .ui-icon-key { background-position: -112px -128px; }
486
+ .ui-icon-lightbulb { background-position: -128px -128px; }
487
+ .ui-icon-scissors { background-position: -144px -128px; }
488
+ .ui-icon-clipboard { background-position: -160px -128px; }
489
+ .ui-icon-copy { background-position: -176px -128px; }
490
+ .ui-icon-contact { background-position: -192px -128px; }
491
+ .ui-icon-image { background-position: -208px -128px; }
492
+ .ui-icon-video { background-position: -224px -128px; }
493
+ .ui-icon-script { background-position: -240px -128px; }
494
+ .ui-icon-alert { background-position: 0 -144px; }
495
+ .ui-icon-info { background-position: -16px -144px; }
496
+ .ui-icon-notice { background-position: -32px -144px; }
497
+ .ui-icon-help { background-position: -48px -144px; }
498
+ .ui-icon-check { background-position: -64px -144px; }
499
+ .ui-icon-bullet { background-position: -80px -144px; }
500
+ .ui-icon-radio-on { background-position: -96px -144px; }
501
+ .ui-icon-radio-off { background-position: -112px -144px; }
502
+ .ui-icon-pin-w { background-position: -128px -144px; }
503
+ .ui-icon-pin-s { background-position: -144px -144px; }
504
+ .ui-icon-play { background-position: 0 -160px; }
505
+ .ui-icon-pause { background-position: -16px -160px; }
506
+ .ui-icon-seek-next { background-position: -32px -160px; }
507
+ .ui-icon-seek-prev { background-position: -48px -160px; }
508
+ .ui-icon-seek-end { background-position: -64px -160px; }
509
+ .ui-icon-seek-start { background-position: -80px -160px; }
510
+ /* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
511
+ .ui-icon-seek-first { background-position: -80px -160px; }
512
+ .ui-icon-stop { background-position: -96px -160px; }
513
+ .ui-icon-eject { background-position: -112px -160px; }
514
+ .ui-icon-volume-off { background-position: -128px -160px; }
515
+ .ui-icon-volume-on { background-position: -144px -160px; }
516
+ .ui-icon-power { background-position: 0 -176px; }
517
+ .ui-icon-signal-diag { background-position: -16px -176px; }
518
+ .ui-icon-signal { background-position: -32px -176px; }
519
+ .ui-icon-battery-0 { background-position: -48px -176px; }
520
+ .ui-icon-battery-1 { background-position: -64px -176px; }
521
+ .ui-icon-battery-2 { background-position: -80px -176px; }
522
+ .ui-icon-battery-3 { background-position: -96px -176px; }
523
+ .ui-icon-circle-plus { background-position: 0 -192px; }
524
+ .ui-icon-circle-minus { background-position: -16px -192px; }
525
+ .ui-icon-circle-close { background-position: -32px -192px; }
526
+ .ui-icon-circle-triangle-e { background-position: -48px -192px; }
527
+ .ui-icon-circle-triangle-s { background-position: -64px -192px; }
528
+ .ui-icon-circle-triangle-w { background-position: -80px -192px; }
529
+ .ui-icon-circle-triangle-n { background-position: -96px -192px; }
530
+ .ui-icon-circle-arrow-e { background-position: -112px -192px; }
531
+ .ui-icon-circle-arrow-s { background-position: -128px -192px; }
532
+ .ui-icon-circle-arrow-w { background-position: -144px -192px; }
533
+ .ui-icon-circle-arrow-n { background-position: -160px -192px; }
534
+ .ui-icon-circle-zoomin { background-position: -176px -192px; }
535
+ .ui-icon-circle-zoomout { background-position: -192px -192px; }
536
+ .ui-icon-circle-check { background-position: -208px -192px; }
537
+ .ui-icon-circlesmall-plus { background-position: 0 -208px; }
538
+ .ui-icon-circlesmall-minus { background-position: -16px -208px; }
539
+ .ui-icon-circlesmall-close { background-position: -32px -208px; }
540
+ .ui-icon-squaresmall-plus { background-position: -48px -208px; }
541
+ .ui-icon-squaresmall-minus { background-position: -64px -208px; }
542
+ .ui-icon-squaresmall-close { background-position: -80px -208px; }
543
+ .ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
544
+ .ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
545
+ .ui-icon-grip-solid-vertical { background-position: -32px -224px; }
546
+ .ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
547
+ .ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
548
+ .ui-icon-grip-diagonal-se { background-position: -80px -224px; }
549
+
550
+
core/modules/away-mode/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/js/admin-away-mode.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_away_mode_end_date, #itsec_away_mode_start_date" ).datepicker();
4
+
5
+ jQuery( "#itsec_away_mode_enabled" ).change(function () {
6
+
7
+ if ( jQuery( "#itsec_away_mode_enabled" ).is( ':checked' ) ) {
8
+
9
+ jQuery( "#away_mode-settings" ).show();
10
+
11
+ } else {
12
+
13
+ jQuery( "#away_mode-settings" ).hide();
14
+
15
+ }
16
+
17
+ } ).change();
18
+
19
+ jQuery( "#itsec_away_mode_type" ).change(function () {
20
+
21
+ if ( jQuery( "#itsec_away_mode_type" ).val() == "2" ) {
22
+
23
+ jQuery( ".end_date_field, .start_date_field" ).closest( "tr" ).show();
24
+
25
+ } else {
26
+
27
+ jQuery( ".end_date_field, .start_date_field" ).closest( "tr" ).hide();
28
+
29
+ }
30
+
31
+ } ).change();
32
+
33
+ } );
core/modules/away-mode/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/away-mode/setup.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Away_Mode_Setup' ) ) {
4
+
5
+ class ITSEC_Away_Mode_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'type' => false,
17
+ 'start' => 1,
18
+ 'end' => 1,
19
+ );
20
+
21
+ if ( isset( $itsec_setup_action ) ) {
22
+
23
+ switch ( $itsec_setup_action ) {
24
+
25
+ case 'activate':
26
+ $this->execute_activate();
27
+ break;
28
+ case 'upgrade':
29
+ $this->execute_upgrade();
30
+ break;
31
+ case 'deactivate':
32
+ $this->execute_deactivate();
33
+ break;
34
+ case 'uninstall':
35
+ $this->execute_uninstall();
36
+ break;
37
+
38
+ }
39
+
40
+ } else {
41
+ wp_die( 'error' );
42
+ }
43
+
44
+ }
45
+
46
+ /**
47
+ * Execute module activation.
48
+ *
49
+ * @since 4.0
50
+ *
51
+ * @return void
52
+ */
53
+ public function execute_activate() {
54
+
55
+ $options = get_site_option( 'itsec_away_mode' );
56
+
57
+ if ( $options === false ) {
58
+
59
+ add_site_option( 'itsec_away_mode', $this->defaults );
60
+
61
+ }
62
+
63
+ }
64
+
65
+ /**
66
+ * Execute module deactivation
67
+ *
68
+ * @return void
69
+ */
70
+ public function execute_deactivate() {
71
+
72
+ delete_site_option( 'itsec_away_mode_sync_override' );
73
+ delete_site_transient( 'itsec_away' );
74
+ delete_site_transient( 'itsec_away_mode' );
75
+
76
+ }
77
+
78
+ /**
79
+ * Execute module uninstall
80
+ *
81
+ * @return void
82
+ */
83
+ public function execute_uninstall() {
84
+
85
+ $this->execute_deactivate();
86
+
87
+ delete_site_option( 'itsec_away_mode' );
88
+
89
+ }
90
+
91
+ /**
92
+ * Execute module upgrade
93
+ *
94
+ * @return void
95
+ */
96
+ public function execute_upgrade() {
97
+
98
+ global $itsec_old_version;
99
+
100
+ if ( $itsec_old_version < 4000 ) {
101
+
102
+ global $itsec_bwps_options, $itsec_globals;
103
+
104
+ $current_options = get_site_option( 'itsec_away_mode' );
105
+ $current_time = $itsec_globals['current_time'];
106
+
107
+ if ( $current_options === false ) {
108
+ $current_options = $this->defaults;
109
+ }
110
+
111
+ $current_options['enabled'] = isset( $itsec_bwps_options['am_enabled'] ) && $itsec_bwps_options['am_enabled'] == 1 ? true : false;
112
+ $current_options['type'] = isset( $itsec_bwps_options['am_type'] ) && $itsec_bwps_options['am_type'] == 1 ? 1 : 2;
113
+
114
+ if ( isset( $itsec_bwps_options['am_startdate'] ) && isset( $itsec_bwps_options['am_starttime'] ) ) {
115
+
116
+ $current_options['start'] = strtotime( date( 'Y-m-d', $itsec_bwps_options['am_startdate'] ) ) + intval( $itsec_bwps_options['am_starttime'] );
117
+
118
+ } elseif ( isset( $current_options['am_starttime'] ) && $current_options['type'] == 1 ) {
119
+
120
+ $current_options['start'] = strtotime( date( 'Y-m-d', $current_time ) ) + intval( $itsec_bwps_options['am_starttime'] );
121
+
122
+ } else {
123
+
124
+ $current_options['enabled'] = false; //didn't have the whole start picture so disable
125
+
126
+ }
127
+
128
+ if ( isset( $itsec_bwps_options['am_enddate'] ) && isset( $itsec_bwps_options['am_endtime'] ) ) {
129
+
130
+ $current_options['end'] = strtotime( date( 'Y-m-d', $itsec_bwps_options['am_enddate'] ) ) + intval( $itsec_bwps_options['am_endtime'] );
131
+
132
+ } elseif ( isset( $itsec_bwps_options['am_endtime'] ) && $itsec_bwps_options['type'] == 1 ) {
133
+
134
+ $current_options['end'] = strtotime( date( 'Y-m-d', $current_time ) ) + intval( $itsec_bwps_options['am_endtime'] );
135
+
136
+ } else {
137
+
138
+ $current_options['enabled'] = false; //didn't have the whole start picture so disable
139
+
140
+ }
141
+
142
+ update_site_option( 'itsec_away_mode', $current_options );
143
+
144
+ $away_file = $itsec_globals['ithemes_dir'] . '/itsec_away.confg'; //override file
145
+
146
+ if ( $current_options['enabled'] === true && ! file_exists( $away_file ) ) {
147
+
148
+ @file_put_contents( $away_file, 'true' );
149
+
150
+ } else {
151
+
152
+ @unlink( $away_file );
153
+
154
+ }
155
+
156
+ }
157
+
158
+ }
159
+
160
+ }
161
+
162
+ }
163
+
164
+ new ITSEC_Away_Mode_Setup();
core/modules/backup/class-itsec-backup-admin.php ADDED
@@ -0,0 +1,1026 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Database backup Administrative Screens
5
+ *
6
+ * Sets up all administrative functions for the database backup feature
7
+ * including fields, sanitation and all other privileged functions.
8
+ *
9
+ * @since 4.0.0
10
+ *
11
+ * @package iThemes_Security
12
+ */
13
+ class ITSEC_Backup_Admin {
14
+
15
+ /**
16
+ * The module's saved options
17
+ *
18
+ * @since 4.0.0
19
+ * @access private
20
+ * @var array
21
+ */
22
+ private $settings;
23
+
24
+ /**
25
+ * The core plugin class utilized in order to set up admin and other screens
26
+ *
27
+ * @since 4.0.0
28
+ * @access private
29
+ * @var ITSEC_Core
30
+ */
31
+ private $core;
32
+
33
+ /**
34
+ * The absolute web patch to the module's files
35
+ *
36
+ * @since 4.0.0
37
+ * @access private
38
+ * @var string
39
+ */
40
+ private $module_path;
41
+
42
+ /**
43
+ * Setup the module's administrative functionality
44
+ *
45
+ * Loads the database backup module's privileged functionality including
46
+ * settings fields.
47
+ *
48
+ * @since 4.0.0
49
+ *
50
+ * @param ITSEC_Core $core The core plugin instance
51
+ *
52
+ * @return void
53
+ */
54
+ function run( $core ) {
55
+
56
+ $this->core = $core;
57
+ $this->settings = get_site_option( 'itsec_backup' );
58
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
59
+
60
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); //enqueue scripts for admin page
61
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'itsec_add_admin_meta_boxes' ) ); //add meta boxes to admin page
62
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init' ) ); //initialize admin area
63
+
64
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'itsec_add_dashboard_status' ) ); //add information for plugin status
65
+ add_filter( 'itsec_tooltip_modules', array( $this, 'itsec_tooltip_modules' ) ); //register tooltip action
66
+ add_filter( 'itsec_tracking_vars', array( $this, 'itsec_tracking_vars' ) );
67
+
68
+ if ( isset( $_POST['itsec_backup'] ) && $_POST['itsec_backup'] == 'one_time_backup' ) {
69
+ add_action( 'itsec_admin_init', array( $this, 'one_time_backup' ) );
70
+ } elseif ( is_multisite() ) {
71
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init_multisite' ) ); //save multisite options
72
+ }
73
+
74
+ }
75
+
76
+ /**
77
+ * Build and echo the database backup description.
78
+ *
79
+ * Echos the module description for database backups.
80
+ *
81
+ * @since 4.0.0
82
+ *
83
+ * @return void
84
+ */
85
+ public function add_module_intro() {
86
+
87
+ echo '<p>' . __( 'One of the best ways to protect yourself from an attack is to have access to a database backup of your site. If something goes wrong, you can get your site back by restoring the database from a backup and replacing the files with fresh ones. Use the button below to create a backup of your database for this purpose. You can also schedule automated backups and download or delete previous backups.', 'better-wp-security' ) . '</p>';
88
+
89
+ }
90
+
91
+ /**
92
+ * Add Files Admin Javascript
93
+ *
94
+ * Enqueues files used in the admin area for the database backup module
95
+ *
96
+ * @since 4.0.0
97
+ *
98
+ * @return void
99
+ */
100
+ public function admin_enqueue_scripts() {
101
+
102
+ global $itsec_globals;
103
+
104
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
105
+
106
+ wp_register_script( 'itsec_backup_js', $this->module_path . 'js/admin-backup.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
107
+ wp_enqueue_script( 'itsec_backup_js' );
108
+ wp_localize_script( 'itsec_backup_js', 'exclude_text', array(
109
+ 'available' => __( 'Tables for Backup', 'better-wp-security' ),
110
+ 'excluded' => __( 'Excluded Tables', 'better-wp-security' ),
111
+ 'location' => $itsec_globals['ithemes_backup_dir'],
112
+ ) );
113
+
114
+ wp_register_script( 'jquery_multiselect', $this->module_path . 'js/jquery.multi-select.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
115
+ wp_enqueue_script( 'jquery_multiselect' );
116
+
117
+ wp_register_style( 'itsec_ms_styles', $this->module_path . 'css/multi-select.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
118
+ wp_enqueue_style( 'itsec_ms_styles' );
119
+
120
+ wp_register_style( 'itsec_backup_styles', $this->module_path . 'css/admin-backup.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
121
+ wp_enqueue_style( 'itsec_backup_styles' );
122
+
123
+ }
124
+
125
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_backups' ) !== false ) {
126
+
127
+ wp_register_style( 'itsec_backup_styles', $this->module_path . 'css/admin-backup.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
128
+ wp_enqueue_style( 'itsec_backup_styles' );
129
+
130
+ }
131
+
132
+ }
133
+
134
+ /**
135
+ * Link to external backup plugin's settings page.
136
+ *
137
+ * Allows another backup plugin to set the backup links in iThemes Security to the correct location.
138
+ *
139
+ * @since 4.0.0
140
+ *
141
+ * @return string Link information for external backup plugin
142
+ */
143
+ public function external_backup_link() {
144
+
145
+ $backup_link = '#itsec_backup_enabled';
146
+
147
+ /**
148
+ * Link to external backup plugin's settings page.
149
+ *
150
+ * Filterable variable to link backup locations in this plugin to the correct external backup page.
151
+ *
152
+ * @since 4.0.0
153
+ *
154
+ * @param string $backup_link Link information for external backup plugin
155
+ */
156
+
157
+ return apply_filters( 'itsec_external_backup_link', $backup_link );
158
+
159
+ }
160
+
161
+ /**
162
+ * Is another backup function present
163
+ *
164
+ * Allows another backup plugin to register itself thereby preventing duplicate backups.
165
+ *
166
+ * @since 4.0.0
167
+ *
168
+ * @return bool true if another backup is present or false.
169
+ */
170
+ public function has_backup() {
171
+
172
+ $has_backup = false;
173
+
174
+ /**
175
+ * Is another backup plugin present.
176
+ *
177
+ * Filterable variable to let this plugin know that another backup solution is present.
178
+ *
179
+ * @since 4.0.0
180
+ *
181
+ * @param bool $has_backup Whether or not another backup plugin is present.
182
+ */
183
+
184
+ return apply_filters( 'itsec_has_external_backup', $has_backup );
185
+
186
+ }
187
+
188
+ /**
189
+ * Add meta boxes to primary options pages
190
+ *
191
+ * Adds the module's meta settings box to the settings page and
192
+ * registers the added box in the page's table of contents.
193
+ *
194
+ * @since 4.0.0
195
+ *
196
+ * @return void
197
+ */
198
+ public function itsec_add_admin_meta_boxes() {
199
+
200
+ if ( ! class_exists( 'backupbuddy_api' ) || is_multisite() ) {
201
+
202
+ add_meta_box(
203
+ 'backup_description',
204
+ __( 'Description', 'better-wp-security' ),
205
+ array( $this, 'add_module_intro' ),
206
+ 'security_page_toplevel_page_itsec_backups',
207
+ 'normal',
208
+ 'core'
209
+ );
210
+
211
+ add_meta_box(
212
+ 'backup_one_time',
213
+ __( 'Make a Database Backup', 'better-wp-security' ),
214
+ array( $this, 'metabox_one_time' ),
215
+ 'security_page_toplevel_page_itsec_backups',
216
+ 'advanced',
217
+ 'core'
218
+ );
219
+
220
+ $id = 'backup_options';
221
+ $title = __( 'Database Backups', 'better-wp-security' );
222
+
223
+ add_meta_box(
224
+ $id,
225
+ $title,
226
+ array( $this, 'metabox_advanced_settings' ),
227
+ 'security_page_toplevel_page_itsec_settings',
228
+ 'advanced',
229
+ 'core'
230
+ );
231
+
232
+ add_meta_box(
233
+ 'backupbuddy_info',
234
+ __( 'Take the Next Steps in Security with BackupBuddy', 'better-wp-security' ),
235
+ array( $this, 'metabox_backupbuddy' ),
236
+ 'security_page_toplevel_page_itsec_backups',
237
+ 'advanced',
238
+ 'core'
239
+ );
240
+
241
+ $this->core->add_toc_item(
242
+ array(
243
+ 'id' => $id,
244
+ 'title' => $title,
245
+ )
246
+ );
247
+
248
+ }
249
+
250
+ }
251
+
252
+ /**
253
+ * Sets the status in the plugin dashboard
254
+ *
255
+ * Sets a medium priority item for the module's functionality in the plugin
256
+ * dashboard.
257
+ *
258
+ * @since 4.0.0
259
+ *
260
+ * @param array $statuses array of existing plugin dashboard statuses
261
+ *
262
+ * @return array statuses
263
+ */
264
+ public function itsec_add_dashboard_status( $statuses ) {
265
+
266
+ if ( ! is_multisite() && class_exists( 'backupbuddy_api' ) && 1 <= sizeof( backupbuddy_api::getSchedules() ) ) {
267
+
268
+ if ( true === $this->settings['enabled'] ) { //disable our backups if we have to
269
+
270
+ $this->settings['enabled'] = false;
271
+ update_site_option( 'itsec_backup', $this->settings );
272
+
273
+ }
274
+
275
+ $status_array = 'safe-medium';
276
+ $status = array(
277
+ 'text' => __( 'Your site is performing scheduled database and file backups.', 'better-wp-security' ),
278
+ 'link' => '?page=pb_backupbuddy_scheduling',
279
+ );
280
+
281
+ } elseif ( ! is_multisite() && class_exists( 'backupbuddy_api' ) ) {
282
+
283
+ if ( $this->settings['enabled'] === true ) { //disable our backups if we have to
284
+
285
+ $this->settings['enabled'] = false;
286
+ update_site_option( 'itsec_backup', $this->settings );
287
+
288
+ }
289
+
290
+ $status_array = 'medium';
291
+ $status = array(
292
+ 'text' => __( 'BackupBuddy is installed but backups do not appear to have been scheduled. Please schedule backups.', 'better-wp-security' ),
293
+ 'link' => '?page=pb_backupbuddy_scheduling',
294
+ );
295
+
296
+ } elseif ( true === $this->has_backup() && true === $this->scheduled_backup() ) {
297
+
298
+ if ( true === $this->settings['enabled'] ) { //disable our backups if we have to
299
+
300
+ $this->settings['enabled'] = false;
301
+ update_site_option( 'itsec_backup', $this->settings );
302
+
303
+ }
304
+
305
+ $status_array = 'safe-medium';
306
+ $status = array(
307
+ 'text' => __( 'You are using a 3rd party backup solution.', 'better-wp-security' ),
308
+ 'link' => $this->external_backup_link(),
309
+ );
310
+
311
+ } elseif ( true === $this->has_backup() ) {
312
+
313
+ if ( true === $this->settings['enabled'] ) { //disable our backups if we have to
314
+
315
+ $this->settings['enabled'] = false;
316
+ update_site_option( 'itsec_backup', $this->settings );
317
+
318
+ }
319
+
320
+ $status_array = 'medium';
321
+ $status = array(
322
+ 'text' => __( 'It looks like you have a 3rd-party backup solution in place but are not using it. Please turn on scheduled backups.', 'better-wp-security' ),
323
+ 'link' => $this->external_backup_link(),
324
+ );
325
+
326
+ } elseif ( true === $this->settings['enabled'] ) {
327
+
328
+ $status_array = 'medium';
329
+ $status = array(
330
+ 'text' => __( 'Your site is performing scheduled database backups but is not backing up files. Consider purchasing or scheduling BackupBuddy to protect your investment.', 'better-wp-security' ),
331
+ 'link' => 'http://ithemes.com/better-backups',
332
+ );
333
+
334
+ } else {
335
+
336
+ $status_array = 'high';
337
+ $status = array(
338
+ 'text' => __( 'Your site is not performing any scheduled database backups.', 'better-wp-security' ),
339
+ 'link' => '#itsec_backup_enabled',
340
+ );
341
+
342
+ }
343
+
344
+ array_push( $statuses[$status_array], $status );
345
+
346
+ return $statuses;
347
+
348
+ }
349
+
350
+ /**
351
+ * Execute admin initializations
352
+ *
353
+ * Sets up all module settings fields and sections.
354
+ *
355
+ * @since 4.0.0
356
+ *
357
+ * @return void
358
+ */
359
+ public function itsec_admin_init() {
360
+
361
+ //Add Settings sections
362
+ add_settings_section(
363
+ 'backup-settings-2',
364
+ __( 'Configure Database Backups', 'better-wp-security' ),
365
+ '__return_empty_string',
366
+ 'security_page_toplevel_page_itsec_settings'
367
+ );
368
+
369
+ add_settings_section(
370
+ 'backup-enabled',
371
+ __( 'Enable Database Backups', 'better-wp-security' ),
372
+ '__return_empty_string',
373
+ 'security_page_toplevel_page_itsec_settings'
374
+ );
375
+
376
+ add_settings_section(
377
+ 'backup-settings',
378
+ __( 'Backup Schedule Settings', 'better-wp-security' ),
379
+ '__return_empty_string',
380
+ 'security_page_toplevel_page_itsec_settings'
381
+ );
382
+
383
+ add_settings_field(
384
+ 'itsec_backup[enabled]',
385
+ __( 'Schedule Database Backups', 'better-wp-security' ),
386
+ array( $this, 'settings_field_enabled' ),
387
+ 'security_page_toplevel_page_itsec_settings',
388
+ 'backup-enabled'
389
+ );
390
+
391
+ if ( ! defined( 'ITSEC_BACKUP_CRON' ) || false === ITSEC_BACKUP_CRON ) { //Use cron if needed
392
+
393
+ add_settings_field(
394
+ 'itsec_backup[interval]',
395
+ __( 'Backup Interval', 'better-wp-security' ),
396
+ array( $this, 'settings_field_interval' ),
397
+ 'security_page_toplevel_page_itsec_settings',
398
+ 'backup-settings'
399
+ );
400
+
401
+ }
402
+
403
+ add_settings_field(
404
+ 'itsec_backup[all_sites]',
405
+ __( 'Backup Full Database', 'better-wp-security' ),
406
+ array( $this, 'settings_field_all_sites' ),
407
+ 'security_page_toplevel_page_itsec_settings',
408
+ 'backup-settings-2'
409
+ );
410
+
411
+ add_settings_field(
412
+ 'itsec_backup[method]',
413
+ __( 'Backup Method', 'better-wp-security' ),
414
+ array( $this, 'settings_field_method' ),
415
+ 'security_page_toplevel_page_itsec_settings',
416
+ 'backup-settings-2'
417
+ );
418
+
419
+ add_settings_field(
420
+ 'itsec_backup[location]',
421
+ __( 'Backup Location', 'better-wp-security' ),
422
+ array( $this, 'settings_field_location' ),
423
+ 'security_page_toplevel_page_itsec_settings',
424
+ 'backup-settings-2'
425
+ );
426
+
427
+ add_settings_field(
428
+ 'itsec_backup[retain]',
429
+ __( 'Backups to Retain', 'better-wp-security' ),
430
+ array( $this, 'settings_field_retain' ),
431
+ 'security_page_toplevel_page_itsec_settings',
432
+ 'backup-settings-2'
433
+ );
434
+
435
+ add_settings_field(
436
+ 'itsec_backup[zip]',
437
+ __( 'Compress Backup Files', 'better-wp-security' ),
438
+ array( $this, 'settings_field_zip' ),
439
+ 'security_page_toplevel_page_itsec_settings',
440
+ 'backup-settings-2'
441
+ );
442
+
443
+ add_settings_field(
444
+ 'itsec_backup[exclude]',
445
+ __( 'Exclude Tables', 'better-wp-security' ),
446
+ array( $this, 'settings_field_exclude' ),
447
+ 'security_page_toplevel_page_itsec_settings',
448
+ 'backup-settings-2'
449
+ );
450
+
451
+ //Register the settings field for the entire module
452
+ register_setting(
453
+ 'security_page_toplevel_page_itsec_settings',
454
+ 'itsec_backup',
455
+ array( $this, 'sanitize_module_input' )
456
+ );
457
+
458
+ }
459
+
460
+ /**
461
+ * Prepare and save options in network settings
462
+ *
463
+ * Saves the options in a multi-site network where data sensitization and processing is not
464
+ * called automatically on form submission.
465
+ *
466
+ * @since 4.0.0
467
+ *
468
+ * @return void
469
+ */
470
+ public function itsec_admin_init_multisite() {
471
+
472
+ if ( isset( $_POST['itsec_backup'] ) ) {
473
+
474
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
475
+ die( __( 'Security error!', 'better-wp-security' ) );
476
+ }
477
+
478
+ update_site_option( 'itsec_backup', $_POST['itsec_backup'] ); //we must manually save network options
479
+
480
+ }
481
+
482
+ }
483
+
484
+ /**
485
+ * Register backups for tooltips.
486
+ *
487
+ * Registers the backup module for the tooltips that are displayed with a new activation.
488
+ *
489
+ * @since 4.0.0
490
+ *
491
+ * @param array $tooltip_modules array of tooltip modules
492
+ *
493
+ * @return array array of tooltip modules
494
+ */
495
+ public function itsec_tooltip_modules( $tooltip_modules ) {
496
+
497
+ $tooltip_modules['backup'] = array(
498
+ 'priority' => 10,
499
+ 'class' => 'itsec_tooltip_backup',
500
+ 'heading' => __( 'Back up your site', 'better-wp-security' ),
501
+ 'text' => __( 'We recommend making a database backup before you get started securing your site.', 'better-wp-security' ),
502
+ 'link_text' => __( 'Make a backup', 'better-wp-security' ),
503
+ 'callback' => array( $this, 'tooltip_ajax' ),
504
+ 'success' => __( 'Backup completed. Please check your email or uploads folder.', 'better-wp-security' ),
505
+ 'failure' => __( 'Whoops. Something went wrong. Check the backup page or contact support.', 'better-wp-security' ),
506
+ );
507
+
508
+ return $tooltip_modules;
509
+
510
+ }
511
+
512
+ /**
513
+ * Adds fields that will be tracked for Google Analytics.
514
+ *
515
+ * Registers all settings in the module that will be tracked on change by
516
+ * Google Analytics if "allow tracking" is enabled.
517
+ *
518
+ * @since 4.0.0
519
+ *
520
+ * @param array $vars tracking vars
521
+ *
522
+ * @return array tracking vars
523
+ */
524
+ public function itsec_tracking_vars( $vars ) {
525
+
526
+ $vars['itsec_backup'] = array(
527
+ 'enabled' => '0:b',
528
+ 'method' => '3:s',
529
+ 'zip' => '1:b',
530
+ );
531
+
532
+ return $vars;
533
+
534
+ }
535
+
536
+ /**
537
+ * Render the settings metabox
538
+ *
539
+ * Displays the contents of the module's settings metabox on the "Settings"
540
+ * page with all module options.
541
+ *
542
+ * @since 4.0.0
543
+ *
544
+ * @return void
545
+ */
546
+ public function metabox_advanced_settings() {
547
+
548
+ echo '<p>' . __( 'One of the best ways to protect yourself from an attack is to have access to a database backup of your site. If something goes wrong, you can get your site back by restoring the database from a backup and replacing the files with fresh ones. Use the button below to create a backup of your database for this purpose. You can also schedule automated backups and download or delete previous backups.', 'better-wp-security' ) . '</p>';
549
+
550
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'backup-settings-2', false );
551
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'backup-enabled', false );
552
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'backup-settings', false );
553
+
554
+ echo '<p>' . PHP_EOL;
555
+
556
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
557
+
558
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
559
+
560
+ echo '</p>' . PHP_EOL;
561
+
562
+ }
563
+
564
+ /**
565
+ * Render the BackupBuddy metabox.
566
+ *
567
+ * Display the BackupBuddy information metabox.
568
+ *
569
+ * @since 4.0.0
570
+ *
571
+ * @return void
572
+ */
573
+ public function metabox_backupbuddy() {
574
+
575
+ echo '<p>' . __( 'A database backup is just a simple start. BackupBuddy goes one step further to provide complete backups of all your site files (including image and media files, themes, plugins, widgets and settings) - which aren\'t included in a database backup. With BackupBuddy you can customize backup schedules, send your backup files safely off-site to remote storage destinations, restore your site quickly & easily and even move your whole site to a new host or domain.', 'better-wp-security' ) . '</p>';
576
+ echo '<h4>' . __( '5 Reasons You Need a Complete Backup Strategy', 'better-wp-security' ) . '</h4>';
577
+ echo '<ol>';
578
+ echo '<li><strong>' . __( 'Database backups aren\'t enough.', 'better-wp-security' ) . '</strong> ' . __( 'You need complete backups of your entire site (including images and media files, themes, plugins, widgets and settings).', 'better-wp-security' ) . '</li>';
579
+ echo '<li><strong>' . __( 'Backup files should be protected.', 'better-wp-security' ) . '</strong> ' . __( 'Send and store them safely off-site to a secure remote destination (like email, Dropbox, Amazon S3, etc.)', 'better-wp-security' ) . '</li>';
580
+ echo '<li><strong>' . __( 'Backups should be automated and scheduled so you don\'t forget.', 'better-wp-security' ) . '</strong> ' . __( 'Set daily, weekly or monthly backups that automatically send backups off-site.', 'better-wp-security' ) . '</li>';
581
+ echo '<li><strong>' . __( 'Restoring your site should be quick and easy.', 'better-wp-security' ) . '</strong> ' . __( 'If you get hacked or your server crashes, you shouldn\'t have to worry about restoring your site. Reliable backups mean nothing gets corrupted or broken during the restore process.', 'better-wp-security' ) . '</li>';
582
+ echo '<li><strong>' . __( 'You should own your backup files.', 'better-wp-security' ) . '</strong> ' . __( 'Don\'t just rely on a host or service. It\'s your site, so you should own everything on it.', 'better-wp-security' ) . '</li>';
583
+ echo '</ol>';
584
+
585
+ echo '<p class="bub-cta"><a href="http://ithemes.com/better-backups" target="_blank" class="button-primary" >' . __( 'Learn more about BackupBuddy', 'better-wp-security' ) . '</a></p>';
586
+
587
+ }
588
+
589
+ /**
590
+ * Render the one-time backup metabox.
591
+ *
592
+ * Display the form for one-time database backups.
593
+ *
594
+ * @since 4.0.0
595
+ *
596
+ * @return void
597
+ */
598
+ public function metabox_one_time() {
599
+
600
+ echo '<form method="post" action="">';
601
+ echo wp_nonce_field( 'itsec_do_backup', 'wp_nonce' );
602
+ echo '<input type="hidden" name="itsec_backup" value="one_time_backup" />';
603
+ echo '<p>' . __( 'Press the button below to create a backup of your WordPress database. If you have "Send Backups By Email" selected in automated backups you will receive an email containing the backup file.', 'better-wp-security' ) . '</p>';
604
+ echo '<p class="submit"><input type="submit" class="button-primary" value="' . __( 'Create Database Backup', 'better-wp-security' ) . '" /></p>';
605
+ echo '<p><a href="?page=toplevel_page_itsec_settings#itsec_backup_all_sites">' . __( 'Adjust Backup Settings', 'better-wp-security' ) . '</a>';
606
+ echo '</form>';
607
+
608
+ }
609
+
610
+ /**
611
+ * Executes one-time backup.
612
+ *
613
+ * Performs execution of one-time backups which are typically called by the user.
614
+ *
615
+ * @since 4.0.0
616
+ *
617
+ * @return void
618
+ */
619
+ public function one_time_backup() {
620
+
621
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec_do_backup' ) ) {
622
+ die( __( 'Security error!', 'better-wp-security' ) );
623
+ }
624
+
625
+ if ( ! class_exists( 'ITSEC_Backup' ) ) {
626
+ require( dirname( __FILE__ ) . '/class-itsec-backup.php' );
627
+ }
628
+
629
+ $module = new ITSEC_Backup();
630
+ $module->run( $this->core );
631
+ $module->do_backup( true );
632
+
633
+ }
634
+
635
+ /**
636
+ * Sanitize and validate input
637
+ *
638
+ * Sanitizes and validates module options saved on the settings page or via multisite.
639
+ *
640
+ * @since 4.0.0
641
+ *
642
+ * @param Array $input array of input fields
643
+ *
644
+ * @return Array Sanitized array
645
+ */
646
+ public function sanitize_module_input( $input ) {
647
+
648
+ global $itsec_globals;
649
+
650
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
651
+ $input['all_sites'] = ( isset( $input['all_sites'] ) && intval( $input['all_sites'] == 1 ) ? true : false );
652
+ $input['interval'] = isset( $input['interval'] ) ? absint( $input['interval'] ) : 3;
653
+ $input['method'] = isset( $input['method'] ) ? intval( $input['method'] ) : 0;
654
+ $input['location'] = isset( $input['location'] ) ? sanitize_text_field( $input['location'] ) : $itsec_globals['ithemes_backup_dir'];
655
+ $input['last_run'] = isset( $this->settings['last_run'] ) ? $this->settings['last_run'] : 0;
656
+ $input['retain'] = isset( $input['retain'] ) ? absint( $input['retain'] ) : 0;
657
+
658
+ if ( isset( $input['location'] ) && $input['location'] != $itsec_globals['ithemes_backup_dir'] ) {
659
+
660
+ $good_path = ITSEC_Lib::validate_path( $input['location'] );
661
+
662
+ } else {
663
+
664
+ $good_path = true;
665
+
666
+ }
667
+
668
+ if ( true !== $good_path ) {
669
+
670
+ $input['location'] = $itsec_globals['ithemes_backup_dir'];
671
+
672
+ $type = 'error';
673
+ $message = __( 'The file path entered for the backup file location does not appear to be valid. it has been reset to: ' . $itsec_globals['ithemes_backup_dir'], 'better-wp-security' );
674
+
675
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
676
+
677
+ }
678
+
679
+ $input['exclude'] = ( isset( $input['exclude'] ) ? $input['exclude'] : array() );
680
+
681
+ $input['zip'] = ( isset( $input['zip'] ) && intval( $input['zip'] == 1 ) ? true : false );
682
+
683
+ if ( is_multisite() ) {
684
+
685
+ if ( isset( $type ) ) {
686
+
687
+ $error_handler = new WP_Error();
688
+
689
+ $error_handler->add( $type, $message );
690
+
691
+ $this->core->show_network_admin_notice( $error_handler );
692
+
693
+ } else {
694
+
695
+ $this->core->show_network_admin_notice( false );
696
+
697
+ }
698
+
699
+ $this->settings = $input;
700
+
701
+ }
702
+
703
+ return $input;
704
+
705
+ }
706
+
707
+ /**
708
+ * Is another backup plugin scheduling regular backups.
709
+ *
710
+ * Allows another backup plugin to let the dashboard status know if it is scheduling regular backups.
711
+ *
712
+ * @since 4.0.0
713
+ *
714
+ * @return bool true if another backup is scheduling backups or false.
715
+ */
716
+ public function scheduled_backup() {
717
+
718
+ $scheduled_backup = false;
719
+
720
+ /**
721
+ * Is another backup plugin scheduling regular backups.
722
+ *
723
+ * Filterable variable to let this plugin know that another backup solution is scheduling regular backups.
724
+ *
725
+ * @since 4.0.0
726
+ *
727
+ * @param bool $scheduled_backup Whether or not another backup plugin is scheduling regular backups.
728
+ */
729
+
730
+ return apply_filters( 'itsec_scheduled_external_backup', $scheduled_backup );
731
+
732
+ }
733
+
734
+ /**
735
+ * Echos all sites Field.
736
+ *
737
+ * Echos the settings field for backing up all sites vs a single site in multisite.
738
+ *
739
+ * @since 4.0.0
740
+ *
741
+ * @return void
742
+ */
743
+ public function settings_field_all_sites() {
744
+
745
+ if ( isset( $this->settings['all_sites'] ) && true === $this->settings['all_sites'] ) {
746
+
747
+ $all_sites = 1;
748
+
749
+ } else {
750
+
751
+ $all_sites = 0;
752
+
753
+ }
754
+
755
+ echo '<input type="checkbox" id="itsec_backup_all_sites" name="itsec_backup[all_sites]" value="1" ' . checked( 1, $all_sites, false ) . '/>';
756
+ echo '<label for="itsec_backup_all_sites"> ' . __( 'Checking this box will have the backup script backup all tables in your database, even if they are not part of this WordPress site.', 'better-wp-security' ) . '</label>';
757
+
758
+ }
759
+
760
+ /**
761
+ * echos Enable database backup Field
762
+ *
763
+ * Echo's the settings field that determines whether or not the database backup module is enabled.
764
+ *
765
+ * @since 4.0.0
766
+ *
767
+ * @return void
768
+ */
769
+ public function settings_field_enabled() {
770
+
771
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
772
+
773
+ $enabled = 1;
774
+
775
+ } else {
776
+
777
+ $enabled = 0;
778
+
779
+ }
780
+
781
+ echo '<input type="checkbox" id="itsec_backup_enabled" name="itsec_backup[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
782
+ echo '<label for="itsec_backup_enabled"> ' . __( 'Enable Scheduled Database Backups', 'better-wp-security' ) . '</label>';
783
+
784
+ }
785
+
786
+ /**
787
+ * echos exclude tables Field.
788
+ *
789
+ * Echo's the settings field that determines which tables will be excluded or included from the backup.
790
+ *
791
+ * @since 4.0.0
792
+ *
793
+ * @return void
794
+ */
795
+ public function settings_field_exclude() {
796
+
797
+ global $wpdb;
798
+
799
+ $ignored_tables = array(
800
+ 'commentmeta',
801
+ 'comments',
802
+ 'links',
803
+ 'options',
804
+ 'postmeta',
805
+ 'posts',
806
+ 'term_relationships',
807
+ 'term_taxonomy',
808
+ 'terms',
809
+ 'usermeta',
810
+ 'users'
811
+ );
812
+
813
+ //get all of the tables
814
+ if ( isset( $this->settings['all_sites'] ) && true === $this->settings['all_sites'] ) {
815
+
816
+ $tables = $wpdb->get_results( 'SHOW TABLES', ARRAY_N ); //retrieve a list of all tables in the DB
817
+
818
+ } else {
819
+
820
+ $tables = $wpdb->get_results( 'SHOW TABLES LIKE "' . $wpdb->base_prefix . '%"', ARRAY_N ); //retrieve a list of all tables for this WordPress installation
821
+
822
+ }
823
+
824
+ echo '<label for="itsec_backup_exclude"> ' . __( 'Tables with data that does not need to be backed up', 'better-wp-security' ) . '</label>';
825
+ echo '<select multiple="multiple" name="itsec_backup[exclude][]" id="itsec_backup_exclude">';
826
+
827
+ foreach ( $tables as $table ) {
828
+
829
+ $short_table = substr( $table[0], strlen( $wpdb->prefix ) );
830
+
831
+ if ( in_array( $short_table, $ignored_tables ) === false ) {
832
+
833
+ if ( isset( $this->settings['exclude'] ) && in_array( $short_table, $this->settings['exclude'] ) ) {
834
+ $selected = ' selected';
835
+ } else {
836
+ $selected = '';
837
+ }
838
+
839
+ echo '<option value="' . $short_table . '"' . $selected . '>' . $table[0] . '</option>';
840
+
841
+ }
842
+
843
+ }
844
+
845
+ echo '</select>';
846
+ echo '<p class="description"> ' . __( 'Some plugins can create log files in your database. While these logs might be handy for some functions, they can also take up a lot of space and, in some cases, even make backing up your database almost impossible. Select log tables above to exclude their data from the backup. Note: The table itself will be backed up, but not the data in the table.', 'better-wp-security' ) . '</p>';
847
+
848
+ }
849
+
850
+ /**
851
+ * echos Backup Interval Field.
852
+ *
853
+ * Echos the field that lets the user choose the numeric value applying to how often backups should occur.
854
+ *
855
+ * @since 4.0.0
856
+ *
857
+ * @return void
858
+ */
859
+ public function settings_field_interval() {
860
+
861
+ if ( isset( $this->settings['interval'] ) ) {
862
+
863
+ $interval = absint( $this->settings['interval'] );
864
+
865
+ } else {
866
+
867
+ $interval = 3;
868
+
869
+ }
870
+
871
+ echo '<input class="small-text" name="itsec_backup[interval]" id="itsec_backup_interval" value="' . $interval . '" type="text"> ';
872
+ echo '<label for="itsec_backup_interval"> ' . __( 'Days', 'better-wp-security' ) . '</label>';
873
+ echo '<p class="description"> ' . __( 'The number of days between database backups.', 'better-wp-security' ) . '</p>';
874
+
875
+ }
876
+
877
+ /**
878
+ * echos Backup Location Field.
879
+ *
880
+ * Echos the field that lets the user set where the backup should be stored.
881
+ *
882
+ * @since 4.0.0
883
+ *
884
+ * @return void
885
+ */
886
+ public function settings_field_location() {
887
+
888
+ global $itsec_globals;
889
+
890
+ if ( isset( $this->settings['location'] ) ) {
891
+
892
+ $location = sanitize_text_field( $this->settings['location'] );
893
+
894
+ } else {
895
+
896
+ $location = $itsec_globals['ithemes_backup_dir'];
897
+
898
+ }
899
+
900
+ echo '<input class="large-text" name="itsec_backup[location]" id="itsec_backup_location" value="' . $location . '" type="text">';
901
+ echo '<label for="itsec_backup_location"> ' . __( 'The path on your machine where backup files should be stored.', 'better-wp-security' ) . '</label>';
902
+ echo '<p class="description"> ' . __( 'This path must be writable by your website. For added security, it is recommended you do not include it in your website root folder.', 'better-wp-security' ) . '</p>';
903
+ echo '<input id="itsec_reset_backup_location" class="button-secondary" name="itsec_reset_backup_location" type="button" value="' . __( 'Restore Default Location', 'better-wp-security' ) . '" />' . PHP_EOL;
904
+
905
+ }
906
+
907
+ /**
908
+ * echos method Field.
909
+ *
910
+ * Echos the field that determines if backups will be saved locally, emailed or both.
911
+ *
912
+ * @since 4.0.0
913
+ *
914
+ * @param array $args field arguements
915
+ *
916
+ * @return void
917
+ */
918
+ public function settings_field_method() {
919
+
920
+ if ( isset( $this->settings['method'] ) ) {
921
+
922
+ $method = $this->settings['method'];
923
+
924
+ } else {
925
+
926
+ $method = 0;
927
+
928
+ }
929
+
930
+ echo '<select id="itsec_backup_method" name="itsec_backup[method]">';
931
+
932
+ echo '<option value="0" ' . selected( $method, '0' ) . '>' . __( 'Save Locally and Email', 'better-wp-security' ) . '</option>';
933
+ echo '<option value="1" ' . selected( $method, '1' ) . '>' . __( 'Email Only', 'better-wp-security' ) . '</option>';
934
+ echo '<option value="2" ' . selected( $method, '2' ) . '>' . __( 'Save Locally Only', 'better-wp-security' ) . '</option>';
935
+ echo '</select><br />';
936
+ echo '<label for="itsec_backup_method"> ' . __( 'Backup Save Method', 'better-wp-security' ) . '</label>';
937
+ echo '<p class="description">' . __( 'Select what we should do with your backup file. You can have it emailed to you, saved locally or both.' ) . '</p>';
938
+
939
+ }
940
+
941
+ /**
942
+ * echos Files to Retain Field
943
+ *
944
+ * Allows the user to set the number of files that are saved to disk.
945
+ *
946
+ * @since 4.0.27
947
+ *
948
+ * @return void
949
+ */
950
+ public function settings_field_retain() {
951
+
952
+ if ( isset( $this->settings['retain'] ) ) {
953
+
954
+ $retain = absint( $this->settings['retain'] );
955
+
956
+ } else {
957
+
958
+ $retain = 0;
959
+
960
+ }
961
+
962
+ echo '<input class="small-text" name="itsec_backup[retain]" id="itsec_backup_retain" value="' . $retain . '" type="text">';
963
+ echo '<label for="itsec_backup_retain"> ' . __( 'Backups', 'better-wp-security' ) . '</label>';
964
+ echo '<p class="description"> ' . __( 'Limit the number of backups stored locally (on this server). Any older backups beyond this number will be removed. Setting to "0" will retain all backups.', 'better-wp-security' ) . '</p>';
965
+
966
+ }
967
+
968
+ /**
969
+ * echos Zip Backups Field.
970
+ *
971
+ * Allows the user to choose whether or not database backups should be zipped before saving.
972
+ *
973
+ * @since 4.0.0
974
+ *
975
+ * @return void
976
+ */
977
+ public function settings_field_zip() {
978
+
979
+ if ( isset( $this->settings['zip'] ) && false === $this->settings['zip'] ) {
980
+
981
+ $zip = 0;
982
+
983
+ } else {
984
+
985
+ $zip = 1;
986
+
987
+ }
988
+
989
+ echo '<input type="checkbox" id="itsec_backup_zip" name="itsec_backup[zip]" value="1" ' . checked( 1, $zip, false ) . '/>';
990
+ echo '<label for="itsec_backup_zip"> ' . __( 'Zip Database Backups', 'better-wp-security' ) . '</label>';
991
+ echo '<p class="description">' . __( 'You may need to turn this off if you are having problems with backups.', 'better-wp-security' ) . '</p>';
992
+
993
+ }
994
+
995
+ /**
996
+ * Performs actions for tooltip function.
997
+ *
998
+ * When the backup button on the new activation tooltip is clicked this will execute the one time backup.
999
+ *
1000
+ * @since 4.0.0
1001
+ *
1002
+ * return void
1003
+ */
1004
+ public function tooltip_ajax() {
1005
+
1006
+ if ( ! class_exists( 'ITSEC_Backup' ) ) {
1007
+ require( dirname( __FILE__ ) . '/class-itsec-backup.php' );
1008
+ }
1009
+
1010
+ $module = new ITSEC_Backup();
1011
+ $module->run( $this->core );
1012
+ $result = $module->do_backup( true );
1013
+
1014
+ if ( true === $result ) {
1015
+
1016
+ die( 'true' );
1017
+
1018
+ } else {
1019
+
1020
+ die( 'false' );
1021
+
1022
+ }
1023
+
1024
+ }
1025
+
1026
+ }
core/modules/backup/class-itsec-backup.php ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Backup execution.
5
+ *
6
+ * Handles database backups at scheduled interval.
7
+ *
8
+ * @since 4.0.0
9
+ *
10
+ * @package iThemes_Security
11
+ */
12
+ class ITSEC_Backup {
13
+
14
+ /**
15
+ * An instance of ITSEC_Core for attaining various items
16
+ *
17
+ * @since 4.0.0
18
+ * @access private
19
+ * @var ITSEC_Core
20
+ */
21
+ private $core;
22
+
23
+ /**
24
+ * The module's saved options
25
+ *
26
+ * @since 4.0.0
27
+ * @access private
28
+ * @var array
29
+ */
30
+ private $settings;
31
+
32
+ /**
33
+ * Setup the module's functionality.
34
+ *
35
+ * Loads the backup detection module's unpriviledged functionality including
36
+ * performing the scans themselves.
37
+ *
38
+ * @since 4.0.0
39
+ *
40
+ * @param ITSEC_Core $core instance of the iThemes Security Core object.
41
+ *
42
+ * @return void
43
+ */
44
+ function run( $core ) {
45
+
46
+ global $itsec_globals;
47
+
48
+ $this->core = $core;
49
+ $this->settings = get_site_option( 'itsec_backup' );
50
+
51
+ add_action( 'itsec_execute_backup_cron', array( $this, 'do_backup' ) ); //Action to execute during a cron run.
52
+
53
+ add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
54
+
55
+ if (
56
+ (
57
+ ! defined( 'DOING_AJAX' ) ||
58
+ false === DOING_AJAX
59
+ ) &&
60
+ (
61
+ ! defined( 'ITSEC_BACKUP_CRON' ) ||
62
+ false === ITSEC_BACKUP_CRON
63
+ ) &&
64
+ true === $this->settings['enabled'] &&
65
+ ! class_exists( 'pb_backupbuddy' ) &&
66
+ ( $itsec_globals['current_time_gmt'] - $this->settings['interval'] * 24 * 60 * 60 ) > $this->settings['last_run']
67
+ ) {
68
+
69
+ add_action( 'init', array( $this, 'do_backup' ), 10, 0 );
70
+
71
+ } elseif ( defined( 'ITSEC_BACKUP_CRON' ) && true === ITSEC_BACKUP_CRON && ! wp_next_scheduled( 'itsec_execute_backup_cron' ) ) { //Use cron if needed
72
+
73
+ wp_schedule_event( time(), 'daily', 'itsec_execute_backup_cron' );
74
+
75
+ }
76
+
77
+ }
78
+
79
+ /**
80
+ * Public function to get lock and call backup.
81
+ *
82
+ * Attempts to get a lock to prevent concurrant backups and calls the backup function itself.
83
+ *
84
+ * @since 4.0.0
85
+ *
86
+ * @param boolean $one_time whether this is a one time backup
87
+ *
88
+ * @return mixed false on error or nothing
89
+ */
90
+ public function do_backup( $one_time = false ) {
91
+
92
+ global $itsec_files;
93
+
94
+ ITSEC_Lib::set_minimum_memory_limit( '128M' );
95
+
96
+ if ( $itsec_files->get_file_lock( 'backup' ) ) {
97
+
98
+ $this->execute_backup( $one_time );
99
+
100
+ $itsec_files->release_file_lock( 'backup' );
101
+
102
+ if ( true === $one_time ) {
103
+
104
+ switch ( $this->settings['method'] ) {
105
+
106
+ case 0:
107
+ $details = __( 'emailed to backup recipients and saved locally.', 'better-wp-security' );
108
+ break;
109
+ case 1:
110
+ $details = __( 'emailed to backup recipients.', 'better-wp-security' );
111
+ break;
112
+ default:
113
+ $details = __( 'saved locally.', 'better-wp-security' );
114
+ break;
115
+
116
+ }
117
+
118
+ $type = 'updated';
119
+ $message = __( 'Backup Completed and ' . $details, 'better-wp-security' );
120
+
121
+ }
122
+
123
+ $success = true;
124
+
125
+ } else {
126
+
127
+ if ( true === $one_time ) {
128
+
129
+ $type = 'error';
130
+ $message = __( 'Something went wrong with your backup. It looks like another process might already be trying to backup your database. Please try again in a few minutes. If the problem persists please contact support.', 'better-wp-security' );
131
+
132
+ }
133
+
134
+ $success = false;
135
+
136
+ }
137
+
138
+ if ( true === $one_time ) {
139
+
140
+ if ( is_multisite() ) {
141
+
142
+ $error_handler = new WP_Error();
143
+
144
+ $error_handler->add( $type, $message );
145
+
146
+ $this->core->show_network_admin_notice( $error_handler );
147
+
148
+ } else {
149
+
150
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
151
+
152
+ }
153
+
154
+ }
155
+
156
+ return $success;
157
+
158
+ }
159
+
160
+ /**
161
+ * Executes backup function.
162
+ *
163
+ * Handles the execution of database backups.
164
+ *
165
+ * @since 4.0.0
166
+ *
167
+ * @param bool $one_time whether this is a one-time backup
168
+ *
169
+ * @return void
170
+ */
171
+ private function execute_backup( $one_time = false ) {
172
+
173
+ global $wpdb, $itsec_globals, $itsec_logger;
174
+
175
+ //get all of the tables
176
+ if ( isset( $this->settings['all_sites'] ) && true === $this->settings['all_sites'] ) {
177
+
178
+ $tables = $wpdb->get_results( 'SHOW TABLES', ARRAY_N ); //retrieve a list of all tables in the DB
179
+
180
+ } else {
181
+
182
+ $tables = $wpdb->get_results( 'SHOW TABLES LIKE "' . $wpdb->base_prefix . '%"', ARRAY_N ); //retrieve a list of all tables for this WordPress installation
183
+
184
+ }
185
+
186
+ $return = '';
187
+
188
+ //cycle through each table
189
+ foreach ( $tables as $table ) {
190
+
191
+ $num_fields = sizeof( $wpdb->get_results( 'DESCRIBE `' . $table[0] . '`;' ) );
192
+
193
+ $return .= 'DROP TABLE IF EXISTS `' . $table[0] . '`;';
194
+
195
+ $row2 = $wpdb->get_row( 'SHOW CREATE TABLE `' . $table[0] . '`;', ARRAY_N );
196
+
197
+ $return .= PHP_EOL . PHP_EOL . $row2[1] . ";" . PHP_EOL . PHP_EOL;
198
+
199
+ if ( ! in_array( substr( $table[0], strlen( $wpdb->prefix ) ), $this->settings['exclude'] ) ) {
200
+
201
+ $result = $wpdb->get_results( 'SELECT * FROM `' . $table[0] . '`;', ARRAY_N );
202
+
203
+ foreach ( $result as $row ) {
204
+
205
+ $return .= 'INSERT INTO `' . $table[0] . '` VALUES(';
206
+
207
+ for ( $j = 0; $j < $num_fields; $j ++ ) {
208
+
209
+ $row[$j] = addslashes( $row[$j] );
210
+ $row[$j] = preg_replace( '#' . PHP_EOL . '#', "\n", $row[$j] );
211
+
212
+ if ( isset( $row[$j] ) ) {
213
+
214
+ $return .= '"' . $row[$j] . '"';
215
+
216
+ } else {
217
+
218
+ $return .= '""';
219
+
220
+ }
221
+
222
+ if ( $j < ( $num_fields - 1 ) ) {
223
+ $return .= ',';
224
+ }
225
+
226
+ }
227
+
228
+ $return .= ");" . PHP_EOL;
229
+
230
+ }
231
+
232
+ }
233
+
234
+ $return .= PHP_EOL . PHP_EOL;
235
+
236
+ }
237
+
238
+ $return .= PHP_EOL . PHP_EOL;
239
+
240
+ $current_time = current_time( 'timestamp' );
241
+
242
+ //save file
243
+ $file = 'backup-' . substr( sanitize_title( get_bloginfo( 'name' ) ), 0, 20 ) . '-' . $current_time . '-' . ITSEC_Lib::get_random( mt_rand( 5, 10 ) );
244
+
245
+ if ( ! is_dir( $itsec_globals['ithemes_backup_dir'] ) ) {
246
+ @mkdir( trailingslashit( $itsec_globals['ithemes_dir'] ) . 'backups' );
247
+ }
248
+
249
+ $handle = @fopen( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql', 'w+' );
250
+
251
+ @fwrite( $handle, $return );
252
+ @fclose( $handle );
253
+
254
+ //zip the file
255
+ if ( true === $this->settings['zip'] ) {
256
+
257
+ if ( ! class_exists( 'PclZip' ) ) {
258
+ require( ABSPATH . 'wp-admin/includes/class-pclzip.php' );
259
+ }
260
+
261
+ $zip = new PclZip( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.zip' );
262
+
263
+ if ( 0 != $zip->create( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql' ) ) {
264
+
265
+ //delete .sql and keep zip
266
+ @unlink( $itsec_globals['ithemes_backup_dir'] . '/' . $file . '.sql' );
267
+
268
+ $fileext = '.zip';
269
+
270
+ }
271
+
272
+ } else {
273
+
274
+ $fileext = '.sql';
275
+
276
+ }
277
+
278
+ if ( 2 !== $this->settings['method'] || true === $one_time ) {
279
+
280
+ $option = get_site_option( 'itsec_global' );
281
+
282
+ $attachment = array( $itsec_globals['ithemes_backup_dir'] . '/' . $file . $fileext );
283
+ $body = __( 'Attached is the backup file for the database powering', 'better-wp-security' ) . ' ' . get_option( 'siteurl' ) . __( ' taken', 'better-wp-security' ) . ' ' . date( 'l, F jS, Y \a\\t g:i a', $itsec_globals['current_time'] );
284
+
285
+ //Setup the remainder of the email
286
+ $recipients = $option['backup_email'];
287
+ $subject = __( 'Site Database Backup', 'better-wp-security' ) . ' ' . date( 'l, F jS, Y \a\\t g:i a', $itsec_globals['current_time'] );
288
+ $subject = apply_filters( 'itsec_backup_email_subject', $subject );
289
+ $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
290
+ $mail_success = false;
291
+
292
+ //Use HTML Content type
293
+ add_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) );
294
+
295
+ //Send emails to all recipients
296
+ foreach ( $recipients as $recipient ) {
297
+
298
+ if ( is_email( trim( $recipient ) ) ) {
299
+
300
+ if ( defined( 'ITSEC_DEBUG' ) && true === ITSEC_DEBUG ) {
301
+ $body .= '<p>' . __( 'Debug info (source page): ' . esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) . '</p>';
302
+ }
303
+
304
+ $mail_success = wp_mail( trim( $recipient ), $subject, '<html>' . $body . '</html>', $headers, $attachment );
305
+
306
+ }
307
+
308
+ }
309
+
310
+ //Remove HTML Content type
311
+ remove_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) );
312
+
313
+ }
314
+
315
+ if ( 1 === $this->settings['method'] ) {
316
+
317
+ @unlink( $itsec_globals['ithemes_backup_dir'] . '/' . $file . $fileext );
318
+
319
+ } else {
320
+
321
+ $retain = isset( $this->settings['retain'] ) ? absint( $this->settings['retain'] ) : 0;
322
+
323
+ //delete extra files
324
+ if ( 0 < $retain ) {
325
+
326
+ $files = scandir( $itsec_globals['ithemes_backup_dir'], 1 );
327
+
328
+ $count = 0;
329
+
330
+ if ( is_array( $files ) && 0 < count( $files ) ) {
331
+
332
+ foreach ( $files as $file ) {
333
+
334
+ if ( strstr( $file, 'backup' ) ) {
335
+
336
+ if ( $count >= $retain ) {
337
+ @unlink( trailingslashit( $itsec_globals['ithemes_backup_dir'] ) . $file );
338
+ }
339
+
340
+ $count ++;
341
+ }
342
+
343
+ }
344
+
345
+ }
346
+
347
+ }
348
+
349
+ }
350
+
351
+ if ( false === $one_time ) {
352
+
353
+ $this->settings['last_run'] = $itsec_globals['current_time_gmt'];
354
+
355
+ update_site_option( 'itsec_backup', $this->settings );
356
+
357
+ }
358
+
359
+ switch ( $this->settings['method'] ) {
360
+
361
+ case 0:
362
+
363
+ if ( false === $mail_success ) {
364
+
365
+ $status = array(
366
+ 'status' => __( 'Error', 'better-wp-security' ),
367
+ 'details' => __( 'saved locally but email to backup recipients could not be sent.', 'better-wp-security' ),
368
+ );
369
+
370
+ } else {
371
+
372
+ $status = array(
373
+ 'status' => __( 'Success', 'better-wp-security' ),
374
+ 'details' => __( 'emailed to backup recipients and saved locally', 'better-wp-security' ),
375
+ );
376
+
377
+ }
378
+
379
+ break;
380
+ case 1:
381
+
382
+ if ( false === $mail_success ) {
383
+
384
+ $status = array(
385
+ 'status' => __( 'Error', 'better-wp-security' ),
386
+ 'details' => __( 'email to backup recipients could not be sent.', 'better-wp-security' ),
387
+ );
388
+
389
+ } else {
390
+
391
+ $status = array(
392
+ 'status' => __( 'Success', 'better-wp-security' ),
393
+ 'details' => __( 'emailed to backup recipients', 'better-wp-security' ),
394
+ );
395
+
396
+ }
397
+
398
+ break;
399
+ default:
400
+ $status = array(
401
+ 'status' => __( 'Success', 'better-wp-security' ),
402
+ 'details' => __( 'saved locally', 'better-wp-security' ),
403
+ );
404
+ break;
405
+
406
+ }
407
+
408
+ $itsec_logger->log_event( 'backup', 3, array( $status ) );
409
+
410
+ }
411
+
412
+ /**
413
+ * Register backups for logger.
414
+ *
415
+ * Adds the backup module to ITSEC_Logger.
416
+ *
417
+ * @since 4.0.0
418
+ *
419
+ * @param array $logger_modules array of logger modules
420
+ *
421
+ * @return array array of logger modules
422
+ */
423
+ public function register_logger( $logger_modules ) {
424
+
425
+ $logger_modules['backup'] = array(
426
+ 'type' => 'backup',
427
+ 'function' => __( 'Database Backup Executed', 'better-wp-security' ),
428
+ );
429
+
430
+ return $logger_modules;
431
+
432
+ }
433
+
434
+ /**
435
+ * Set HTML content type for email.
436
+ *
437
+ * Sets the content type on outgoing emails to HTML.
438
+ *
439
+ * @since 4.0.0
440
+ *
441
+ * @return string html content type
442
+ */
443
+ public function set_html_content_type() {
444
+
445
+ return 'text/html';
446
+
447
+ }
448
+
449
+ }
core/modules/backup/css/admin-backup.css ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ div.inside ul.ms-list {
2
+ margin-left : 0;
3
+ }
4
+
5
+ .ms-container {
6
+ margin-top : 10px;
7
+ }
8
+
9
+ .ms-container .custom-header {
10
+ font-weight : bold;
11
+ text-align : center;
12
+ }
13
+
14
+ #backupbuddy_info .bub-cta {
15
+ margin : 2em .5em;
16
+ }
core/modules/backup/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/backup/css/multi-select.css ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .ms-container {
2
+ background : transparent url('../img/switch.png') no-repeat 50% 50%;
3
+ width : 370px;
4
+ }
5
+
6
+ .ms-container:after {
7
+ content : ".";
8
+ display : block;
9
+ height : 0;
10
+ line-height : 0;
11
+ font-size : 0;
12
+ clear : both;
13
+ min-height : 0;
14
+ visibility : hidden;
15
+ }
16
+
17
+ .ms-container .ms-selectable, .ms-container .ms-selection {
18
+ background : #fff;
19
+ color : #555;
20
+ float : left;
21
+ width : 45%;
22
+ }
23
+
24
+ .ms-container .ms-selection {
25
+ float : right;
26
+ }
27
+
28
+ .ms-container .ms-list {
29
+ -webkit-box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075);
30
+ -moz-box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075);
31
+ box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075);
32
+ -webkit-transition : border linear 0.2s, box-shadow linear 0.2s;
33
+ -moz-transition : border linear 0.2s, box-shadow linear 0.2s;
34
+ -ms-transition : border linear 0.2s, box-shadow linear 0.2s;
35
+ -o-transition : border linear 0.2s, box-shadow linear 0.2s;
36
+ transition : border linear 0.2s, box-shadow linear 0.2s;
37
+ border : 1px solid #ccc;
38
+ -webkit-border-radius : 3px;
39
+ -moz-border-radius : 3px;
40
+ border-radius : 3px;
41
+ position : relative;
42
+ height : 200px;
43
+ padding : 0;
44
+ overflow-y : auto;
45
+ }
46
+
47
+ .ms-container .ms-list.ms-focus {
48
+ border-color : rgba(82, 168, 236, 0.8);
49
+ -webkit-box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
50
+ -moz-box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
51
+ box-shadow : inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
52
+ outline : 0;
53
+ outline : thin dotted \9;
54
+ }
55
+
56
+ .ms-container ul {
57
+ margin : 0;
58
+ list-style-type : none;
59
+ padding : 0;
60
+ }
61
+
62
+ .ms-container .ms-optgroup-container {
63
+ width : 100%;
64
+ }
65
+
66
+ .ms-container .ms-optgroup-label {
67
+ margin : 0;
68
+ padding : 5px 0px 0px 5px;
69
+ cursor : pointer;
70
+ color : #999;
71
+ }
72
+
73
+ .ms-container .ms-selectable li.ms-elem-selectable,
74
+ .ms-container .ms-selection li.ms-elem-selection {
75
+ border-bottom : 1px #eee solid;
76
+ padding : 2px 10px;
77
+ color : #555;
78
+ font-size : 14px;
79
+ }
80
+
81
+ .ms-container .ms-selectable li.ms-hover,
82
+ .ms-container .ms-selection li.ms-hover {
83
+ cursor : pointer;
84
+ color : #fff;
85
+ text-decoration : none;
86
+ background-color : #08c;
87
+ }
88
+
89
+ .ms-container .ms-selectable li.disabled,
90
+ .ms-container .ms-selection li.disabled {
91
+ background-color : #eee;
92
+ color : #aaa;
93
+ cursor : text;
94
+ }
core/modules/backup/img/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/backup/img/switch.png ADDED
Binary file
core/modules/backup/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/backup/js/admin-backup.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_backup_enabled" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_backup_enabled" ).is( ':checked' ) ) {
6
+
7
+ jQuery( "#backup-settings" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#backup-settings" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ jQuery( '#itsec_backup_exclude' ).multiSelect(
18
+ {
19
+ selectableHeader : '<div class="custom-header">' + exclude_text.available + '</div>',
20
+ selectionHeader : '<div class="custom-header">' + exclude_text.excluded + '</div>',
21
+ keepOrder : true
22
+ }
23
+ );
24
+
25
+ jQuery( '#itsec_reset_backup_location' ).click( function ( event ) {
26
+
27
+ event.preventDefault();
28
+
29
+ jQuery( '#itsec_backup_location' ).val( exclude_text.location );
30
+
31
+ } );
32
+
33
+ } );
core/modules/backup/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/backup/js/jquery.multi-select.js ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * MultiSelect v0.9.10
3
+ * Copyright (c) 2012 Louis Cuny
4
+ *
5
+ * This program is free software. It comes without any warranty, to
6
+ * the extent permitted by applicable law. You can redistribute it
7
+ * and/or modify it under the terms of the Do What The Fuck You Want
8
+ * To Public License, Version 2, as published by Sam Hocevar. See
9
+ * http://sam.zoy.org/wtfpl/COPYING for more details.
10
+ */
11
+
12
+ ! function ( $ ) {
13
+
14
+ "use strict";
15
+
16
+ /* MULTISELECT CLASS DEFINITION
17
+ * ====================== */
18
+
19
+ var MultiSelect = function ( element, options ) {
20
+ this.options = options;
21
+ this.$element = $( element );
22
+ this.$container = $( '<div/>', { 'class' : "ms-container" } );
23
+ this.$selectableContainer = $( '<div/>', { 'class' : 'ms-selectable' } );
24
+ this.$selectionContainer = $( '<div/>', { 'class' : 'ms-selection' } );
25
+ this.$selectableUl = $( '<ul/>', { 'class' : "ms-list", 'tabindex' : '-1' } );
26
+ this.$selectionUl = $( '<ul/>', { 'class' : "ms-list", 'tabindex' : '-1' } );
27
+ this.scrollTo = 0;
28
+ this.elemsSelector = 'li:visible:not(.ms-optgroup-label,.ms-optgroup-container,.' + options.disabledClass + ')';
29
+ };
30
+
31
+ MultiSelect.prototype = {
32
+ constructor : MultiSelect,
33
+
34
+ init : function () {
35
+ var that = this,
36
+ ms = this.$element;
37
+
38
+ if ( ms.next( '.ms-container' ).length === 0 ) {
39
+ ms.css( { position : 'absolute', left : '-9999px' } );
40
+ ms.attr( 'id', ms.attr( 'id' ) ? ms.attr( 'id' ) : Math.ceil( Math.random() * 1000 ) + 'multiselect' );
41
+ this.$container.attr( 'id', 'ms-' + ms.attr( 'id' ) );
42
+ this.$container.addClass( that.options.cssClass );
43
+ ms.find( 'option' ).each( function () {
44
+ that.generateLisFromOption( this );
45
+ } );
46
+
47
+ this.$selectionUl.find( '.ms-optgroup-label' ).hide();
48
+
49
+ if ( that.options.selectableHeader ) {
50
+ that.$selectableContainer.append( that.options.selectableHeader );
51
+ }
52
+ that.$selectableContainer.append( that.$selectableUl );
53
+ if ( that.options.selectableFooter ) {
54
+ that.$selectableContainer.append( that.options.selectableFooter );
55
+ }
56
+
57
+ if ( that.options.selectionHeader ) {
58
+ that.$selectionContainer.append( that.options.selectionHeader );
59
+ }
60
+ that.$selectionContainer.append( that.$selectionUl );
61
+ if ( that.options.selectionFooter ) {
62
+ that.$selectionContainer.append( that.options.selectionFooter );
63
+ }
64
+
65
+ that.$container.append( that.$selectableContainer );
66
+ that.$container.append( that.$selectionContainer );
67
+ ms.after( that.$container );
68
+
69
+ that.activeMouse( that.$selectableUl );
70
+ that.activeKeyboard( that.$selectableUl );
71
+
72
+ var action = that.options.dblClick ? 'dblclick' : 'click';
73
+
74
+ that.$selectableUl.on( action, '.ms-elem-selectable', function () {
75
+ that.select( $( this ).data( 'ms-value' ) );
76
+ } );
77
+ that.$selectionUl.on( action, '.ms-elem-selection', function () {
78
+ that.deselect( $( this ).data( 'ms-value' ) );
79
+ } );
80
+
81
+ that.activeMouse( that.$selectionUl );
82
+ that.activeKeyboard( that.$selectionUl );
83
+
84
+ ms.on( 'focus', function () {
85
+ that.$selectableUl.focus();
86
+ } )
87
+ }
88
+
89
+ var selectedValues = ms.find( 'option:selected' ).map(function () {
90
+ return $( this ).val();
91
+ } ).get();
92
+ that.select( selectedValues, 'init' );
93
+
94
+ if ( typeof that.options.afterInit === 'function' ) {
95
+ that.options.afterInit.call( this, this.$container );
96
+ }
97
+ },
98
+
99
+ 'generateLisFromOption' : function ( option, index, $container ) {
100
+ var that = this,
101
+ ms = that.$element,
102
+ attributes = "",
103
+ $option = $( option );
104
+
105
+ for ( var cpt = 0; cpt < option.attributes.length; cpt ++ ) {
106
+ var attr = option.attributes[cpt];
107
+
108
+ if ( attr.name !== 'value' && attr.name !== 'disabled' ) {
109
+ attributes += attr.name + '="' + attr.value + '" ';
110
+ }
111
+ }
112
+ var selectableLi = $( '<li ' + attributes + '><span>' + that.escapeHTML( $option.text() ) + '</span></li>' ),
113
+ selectedLi = selectableLi.clone(),
114
+ value = $option.val(),
115
+ elementId = that.sanitize( value );
116
+
117
+ selectableLi
118
+ .data( 'ms-value', value )
119
+ .addClass( 'ms-elem-selectable' )
120
+ .attr( 'id', elementId + '-selectable' );
121
+
122
+ selectedLi
123
+ .data( 'ms-value', value )
124
+ .addClass( 'ms-elem-selection' )
125
+ .attr( 'id', elementId + '-selection' )
126
+ .hide();
127
+
128
+ if ( $option.prop( 'disabled' ) || ms.prop( 'disabled' ) ) {
129
+ selectedLi.addClass( that.options.disabledClass );
130
+ selectableLi.addClass( that.options.disabledClass );
131
+ }
132
+
133
+ var $optgroup = $option.parent( 'optgroup' );
134
+
135
+ if ( $optgroup.length > 0 ) {
136
+ var optgroupLabel = $optgroup.attr( 'label' ),
137
+ optgroupId = that.sanitize( optgroupLabel ),
138
+ $selectableOptgroup = that.$selectableUl.find( '#optgroup-selectable-' + optgroupId ),
139
+ $selectionOptgroup = that.$selectionUl.find( '#optgroup-selection-' + optgroupId );
140
+
141
+ if ( $selectableOptgroup.length === 0 ) {
142
+ var optgroupContainerTpl = '<li class="ms-optgroup-container"></li>',
143
+ optgroupTpl = '<ul class="ms-optgroup"><li class="ms-optgroup-label"><span>' + optgroupLabel + '</span></li></ul>';
144
+
145
+ $selectableOptgroup = $( optgroupContainerTpl );
146
+ $selectionOptgroup = $( optgroupContainerTpl );
147
+ $selectableOptgroup.attr( 'id', 'optgroup-selectable-' + optgroupId );
148
+ $selectionOptgroup.attr( 'id', 'optgroup-selection-' + optgroupId );
149
+ $selectableOptgroup.append( $( optgroupTpl ) );
150
+ $selectionOptgroup.append( $( optgroupTpl ) );
151
+ if ( that.options.selectableOptgroup ) {
152
+ $selectableOptgroup.find( '.ms-optgroup-label' ).on( 'click', function () {
153
+ var values = $optgroup.children( ':not(:selected)' ).map(function () {
154
+ return $( this ).val()
155
+ } ).get();
156
+ that.select( values );
157
+ } );
158
+ $selectionOptgroup.find( '.ms-optgroup-label' ).on( 'click', function () {
159
+ var values = $optgroup.children( ':selected' ).map(function () {
160
+ return $( this ).val()
161
+ } ).get();
162
+ that.deselect( values );
163
+ } );
164
+ }
165
+ that.$selectableUl.append( $selectableOptgroup );
166
+ that.$selectionUl.append( $selectionOptgroup );
167
+ }
168
+ index = index == undefined ? $selectableOptgroup.children().length : index + 1;
169
+ selectableLi.insertAt( index, $selectableOptgroup.children() );
170
+ selectedLi.insertAt( index, $selectionOptgroup.children() );
171
+ } else {
172
+ index = index == undefined ? that.$selectableUl.children().length : index;
173
+
174
+ selectableLi.insertAt( index, that.$selectableUl );
175
+ selectedLi.insertAt( index, that.$selectionUl );
176
+ }
177
+ },
178
+
179
+ 'addOption' : function ( options ) {
180
+ var that = this;
181
+
182
+ if ( options.value ) {
183
+ options = [options];
184
+ }
185
+ $.each( options, function ( index, option ) {
186
+ if ( option.value && that.$element.find( "option[value='" + option.value + "']" ).length === 0 ) {
187
+ var $option = $( '<option value="' + option.value + '">' + option.text + '</option>' ),
188
+ index = parseInt( (typeof option.index === 'undefined' ? that.$element.children().length : option.index) ),
189
+ $container = option.nested == undefined ? that.$element : $( "optgroup[label='" + option.nested + "']" )
190
+
191
+ $option.insertAt( index, $container );
192
+ that.generateLisFromOption( $option.get( 0 ), index, option.nested );
193
+ }
194
+ } )
195
+ },
196
+
197
+ 'escapeHTML' : function ( text ) {
198
+ return $( "<div>" ).text( text ).html();
199
+ },
200
+
201
+ 'activeKeyboard' : function ( $list ) {
202
+ var that = this;
203
+
204
+ $list.on( 'focus', function () {
205
+ $( this ).addClass( 'ms-focus' );
206
+ } )
207
+ .on( 'blur', function () {
208
+ $( this ).removeClass( 'ms-focus' );
209
+ } )
210
+ .on( 'keydown', function ( e ) {
211
+ switch ( e.which ) {
212
+ case 40:
213
+ case 38:
214
+ e.preventDefault();
215
+ e.stopPropagation();
216
+ that.moveHighlight( $( this ), (e.which === 38) ? - 1 : 1 );
217
+ return;
218
+ case 37:
219
+ case 39:
220
+ e.preventDefault();
221
+ e.stopPropagation();
222
+ that.switchList( $list );
223
+ return;
224
+ case 9:
225
+ if ( that.$element.is( '[tabindex]' ) ) {
226
+ e.preventDefault();
227
+ var tabindex = parseInt( that.$element.attr( 'tabindex' ), 10 );
228
+ tabindex = (e.shiftKey) ? tabindex - 1 : tabindex + 1;
229
+ $( '[tabindex="' + (tabindex) + '"]' ).focus();
230
+ return;
231
+ } else {
232
+ if ( e.shiftKey ) {
233
+ that.$element.trigger( 'focus' );
234
+ }
235
+ }
236
+ }
237
+ if ( $.inArray( e.which, that.options.keySelect ) > - 1 ) {
238
+ e.preventDefault();
239
+ e.stopPropagation();
240
+ that.selectHighlighted( $list );
241
+ return;
242
+ }
243
+ } );
244
+ },
245
+
246
+ 'moveHighlight' : function ( $list, direction ) {
247
+ var $elems = $list.find( this.elemsSelector ),
248
+ $currElem = $elems.filter( '.ms-hover' ),
249
+ $nextElem = null,
250
+ elemHeight = $elems.first().outerHeight(),
251
+ containerHeight = $list.height(),
252
+ containerSelector = '#' + this.$container.prop( 'id' );
253
+
254
+ // Deactive mouseenter event when move is active
255
+ // It fixes a bug when mouse is over the list
256
+ $elems.off( 'mouseenter' );
257
+
258
+ $elems.removeClass( 'ms-hover' );
259
+ if ( direction === 1 ) { // DOWN
260
+
261
+ $nextElem = $currElem.nextAll( this.elemsSelector ).first();
262
+ if ( $nextElem.length === 0 ) {
263
+ var $optgroupUl = $currElem.parent();
264
+
265
+ if ( $optgroupUl.hasClass( 'ms-optgroup' ) ) {
266
+ var $optgroupLi = $optgroupUl.parent(),
267
+ $nextOptgroupLi = $optgroupLi.next( ':visible' );
268
+
269
+ if ( $nextOptgroupLi.length > 0 ) {
270
+ $nextElem = $nextOptgroupLi.find( this.elemsSelector ).first();
271
+ } else {
272
+ $nextElem = $elems.first();
273
+ }
274
+ } else {
275
+ $nextElem = $elems.first();
276
+ }
277
+ }
278
+ } else if ( direction === - 1 ) { // UP
279
+
280
+ $nextElem = $currElem.prevAll( this.elemsSelector ).first();
281
+ if ( $nextElem.length === 0 ) {
282
+ var $optgroupUl = $currElem.parent();
283
+
284
+ if ( $optgroupUl.hasClass( 'ms-optgroup' ) ) {
285
+ var $optgroupLi = $optgroupUl.parent(),
286
+ $prevOptgroupLi = $optgroupLi.prev( ':visible' );
287
+
288
+ if ( $prevOptgroupLi.length > 0 ) {
289
+ $nextElem = $prevOptgroupLi.find( this.elemsSelector ).last();
290
+ } else {
291
+ $nextElem = $elems.last();
292
+ }
293
+ } else {
294
+ $nextElem = $elems.last();
295
+ }
296
+ }
297
+ }
298
+ if ( $nextElem.length > 0 ) {
299
+ $nextElem.addClass( 'ms-hover' );
300
+ var scrollTo = $list.scrollTop() + $nextElem.position().top -
301
+ containerHeight / 2 + elemHeight / 2;
302
+
303
+ $list.scrollTop( scrollTo );
304
+ }
305
+ },
306
+
307
+ 'selectHighlighted' : function ( $list ) {
308
+ var $elems = $list.find( this.elemsSelector ),
309
+ $highlightedElem = $elems.filter( '.ms-hover' ).first();
310
+
311
+ if ( $highlightedElem.length > 0 ) {
312
+ if ( $list.parent().hasClass( 'ms-selectable' ) ) {
313
+ this.select( $highlightedElem.data( 'ms-value' ) );
314
+ } else {
315
+ this.deselect( $highlightedElem.data( 'ms-value' ) );
316
+ }
317
+ $elems.removeClass( 'ms-hover' );
318
+ }
319
+ },
320
+
321
+ 'switchList' : function ( $list ) {
322
+ $list.blur();
323
+ if ( $list.parent().hasClass( 'ms-selectable' ) ) {
324
+ this.$selectionUl.focus();
325
+ } else {
326
+ this.$selectableUl.focus();
327
+ }
328
+ },
329
+
330
+ 'activeMouse' : function ( $list ) {
331
+ var that = this;
332
+
333
+ $list.on( 'mousemove', function () {
334
+ var elems = $list.find( that.elemsSelector );
335
+
336
+ elems.on( 'mouseenter', function () {
337
+ elems.removeClass( 'ms-hover' );
338
+ $( this ).addClass( 'ms-hover' );
339
+ } );
340
+ } );
341
+ },
342
+
343
+ 'refresh' : function () {
344
+ this.destroy();
345
+ this.$element.multiSelect( this.options );
346
+ },
347
+
348
+ 'destroy' : function () {
349
+ $( "#ms-" + this.$element.attr( "id" ) ).remove();
350
+ this.$element.removeData( 'multiselect' );
351
+ },
352
+
353
+ 'select' : function ( value, method ) {
354
+ if ( typeof value === 'string' ) {
355
+ value = [value];
356
+ }
357
+
358
+ var that = this,
359
+ ms = this.$element,
360
+ msIds = $.map( value, function ( val ) {
361
+ return(that.sanitize( val ));
362
+ } ),
363
+ selectables = this.$selectableUl.find( '#' + msIds.join( '-selectable, #' ) + '-selectable' ).filter( ':not(.' + that.options.disabledClass + ')' ),
364
+ selections = this.$selectionUl.find( '#' + msIds.join( '-selection, #' ) + '-selection' ).filter( ':not(.' + that.options.disabledClass + ')' ),
365
+ options = ms.find( 'option:not(:disabled)' ).filter( function () {
366
+ return($.inArray( this.value, value ) > - 1);
367
+ } );
368
+
369
+ if ( method === 'init' ) {
370
+ selectables = this.$selectableUl.find( '#' + msIds.join( '-selectable, #' ) + '-selectable' ),
371
+ selections = this.$selectionUl.find( '#' + msIds.join( '-selection, #' ) + '-selection' );
372
+ }
373
+
374
+ if ( selectables.length > 0 ) {
375
+ selectables.addClass( 'ms-selected' ).hide();
376
+ selections.addClass( 'ms-selected' ).show();
377
+ options.prop( 'selected', true );
378
+
379
+ var selectableOptgroups = that.$selectableUl.children( '.ms-optgroup-container' );
380
+ if ( selectableOptgroups.length > 0 ) {
381
+ selectableOptgroups.each( function () {
382
+ var selectablesLi = $( this ).find( '.ms-elem-selectable' );
383
+ if ( selectablesLi.length === selectablesLi.filter( '.ms-selected' ).length ) {
384
+ $( this ).find( '.ms-optgroup-label' ).hide();
385
+ }
386
+ } );
387
+
388
+ var selectionOptgroups = that.$selectionUl.children( '.ms-optgroup-container' );
389
+ selectionOptgroups.each( function () {
390
+ var selectionsLi = $( this ).find( '.ms-elem-selection' );
391
+ if ( selectionsLi.filter( '.ms-selected' ).length > 0 ) {
392
+ $( this ).find( '.ms-optgroup-label' ).show();
393
+ }
394
+ } );
395
+ } else {
396
+ if ( that.options.keepOrder ) {
397
+ var selectionLiLast = that.$selectionUl.find( '.ms-selected' );
398
+ if ( (selectionLiLast.length > 1) && (selectionLiLast.last().get( 0 ) != selections.get( 0 )) ) {
399
+ selections.insertAfter( selectionLiLast.last() );
400
+ }
401
+ }
402
+ }
403
+ if ( method !== 'init' ) {
404
+ ms.trigger( 'change' );
405
+ if ( typeof that.options.afterSelect === 'function' ) {
406
+ that.options.afterSelect.call( this, value );
407
+ }
408
+ }
409
+ }
410
+ },
411
+
412
+ 'deselect' : function ( value ) {
413
+ if ( typeof value === 'string' ) {
414
+ value = [value];
415
+ }
416
+
417
+ var that = this,
418
+ ms = this.$element,
419
+ msIds = $.map( value, function ( val ) {
420
+ return(that.sanitize( val ));
421
+ } ),
422
+ selectables = this.$selectableUl.find( '#' + msIds.join( '-selectable, #' ) + '-selectable' ),
423
+ selections = this.$selectionUl.find( '#' + msIds.join( '-selection, #' ) + '-selection' ).filter( '.ms-selected' ).filter( ':not(.' + that.options.disabledClass + ')' ),
424
+ options = ms.find( 'option' ).filter( function () {
425
+ return($.inArray( this.value, value ) > - 1);
426
+ } );
427
+
428
+ if ( selections.length > 0 ) {
429
+ selectables.removeClass( 'ms-selected' ).show();
430
+ selections.removeClass( 'ms-selected' ).hide();
431
+ options.prop( 'selected', false );
432
+
433
+ var selectableOptgroups = that.$selectableUl.children( '.ms-optgroup-container' );
434
+ if ( selectableOptgroups.length > 0 ) {
435
+ selectableOptgroups.each( function () {
436
+ var selectablesLi = $( this ).find( '.ms-elem-selectable' );
437
+ if ( selectablesLi.filter( ':not(.ms-selected)' ).length > 0 ) {
438
+ $( this ).find( '.ms-optgroup-label' ).show();
439
+ }
440
+ } );
441
+
442
+ var selectionOptgroups = that.$selectionUl.children( '.ms-optgroup-container' );
443
+ selectionOptgroups.each( function () {
444
+ var selectionsLi = $( this ).find( '.ms-elem-selection' );
445
+ if ( selectionsLi.filter( '.ms-selected' ).length === 0 ) {
446
+ $( this ).find( '.ms-optgroup-label' ).hide();
447
+ }
448
+ } );
449
+ }
450
+ ms.trigger( 'change' );
451
+ if ( typeof that.options.afterDeselect === 'function' ) {
452
+ that.options.afterDeselect.call( this, value );
453
+ }
454
+ }
455
+ },
456
+
457
+ 'select_all' : function () {
458
+ var ms = this.$element,
459
+ values = ms.val();
460
+
461
+ ms.find( 'option:not(":disabled")' ).prop( 'selected', true );
462
+ this.$selectableUl.find( '.ms-elem-selectable' ).filter( ':not(.' + this.options.disabledClass + ')' ).addClass( 'ms-selected' ).hide();
463
+ this.$selectionUl.find( '.ms-optgroup-label' ).show();
464
+ this.$selectableUl.find( '.ms-optgroup-label' ).hide();
465
+ this.$selectionUl.find( '.ms-elem-selection' ).filter( ':not(.' + this.options.disabledClass + ')' ).addClass( 'ms-selected' ).show();
466
+ this.$selectionUl.focus();
467
+ ms.trigger( 'change' );
468
+ if ( typeof this.options.afterSelect === 'function' ) {
469
+ var selectedValues = $.grep( ms.val(), function ( item ) {
470
+ return $.inArray( item, values ) < 0;
471
+ } );
472
+ this.options.afterSelect.call( this, selectedValues );
473
+ }
474
+ },
475
+
476
+ 'deselect_all' : function () {
477
+ var ms = this.$element,
478
+ values = ms.val();
479
+
480
+ ms.find( 'option' ).prop( 'selected', false );
481
+ this.$selectableUl.find( '.ms-elem-selectable' ).removeClass( 'ms-selected' ).show();
482
+ this.$selectionUl.find( '.ms-optgroup-label' ).hide();
483
+ this.$selectableUl.find( '.ms-optgroup-label' ).show();
484
+ this.$selectionUl.find( '.ms-elem-selection' ).removeClass( 'ms-selected' ).hide();
485
+ this.$selectableUl.focus();
486
+ ms.trigger( 'change' );
487
+ if ( typeof this.options.afterDeselect === 'function' ) {
488
+ this.options.afterDeselect.call( this, values );
489
+ }
490
+ },
491
+
492
+ sanitize : function ( value ) {
493
+ var hash = 0, i, char;
494
+ if ( value.length == 0 ) {
495
+ return hash;
496
+ }
497
+ var ls = 0;
498
+ for ( i = 0, ls = value.length; i < ls; i ++ ) {
499
+ char = value.charCodeAt( i );
500
+ hash = ((hash << 5) - hash) + char;
501
+ hash |= 0; // Convert to 32bit integer
502
+ }
503
+ return hash;
504
+ }
505
+ };
506
+
507
+ /* MULTISELECT PLUGIN DEFINITION
508
+ * ======================= */
509
+
510
+ $.fn.multiSelect = function () {
511
+ var option = arguments[0],
512
+ args = arguments;
513
+
514
+ return this.each( function () {
515
+ var $this = $( this ),
516
+ data = $this.data( 'multiselect' ),
517
+ options = $.extend( {}, $.fn.multiSelect.defaults, $this.data(), typeof option === 'object' && option );
518
+
519
+ if ( ! data ) {
520
+ $this.data( 'multiselect', (data = new MultiSelect( this, options )) );
521
+ }
522
+
523
+ if ( typeof option === 'string' ) {
524
+ data[option]( args[1] );
525
+ } else {
526
+ data.init();
527
+ }
528
+ } );
529
+ };
530
+
531
+ $.fn.multiSelect.defaults = {
532
+ keySelect : [32],
533
+ selectableOptgroup : false,
534
+ disabledClass : 'disabled',
535
+ dblClick : false,
536
+ keepOrder : false,
537
+ cssClass : ''
538
+ };
539
+
540
+ $.fn.multiSelect.Constructor = MultiSelect;
541
+
542
+ $.fn.insertAt = function ( index, $parent ) {
543
+ return this.each( function () {
544
+ if ( index === 0 ) {
545
+ $parent.prepend( this );
546
+ } else {
547
+ $parent.children().eq( index - 1 ).after( this );
548
+ }
549
+ } );
550
+ }
551
+
552
+ }( window.jQuery );
core/modules/backup/setup.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Backup_Setup' ) ) {
4
+
5
+ class ITSEC_Backup_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action, $itsec_globals;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'interval' => 3,
17
+ 'all_sites' => false,
18
+ 'method' => 1,
19
+ 'location' => $itsec_globals['ithemes_backup_dir'],
20
+ 'last_run' => 0,
21
+ 'zip' => true,
22
+ 'exclude' => array(
23
+ 'itsec_log',
24
+ 'itsec_temp',
25
+ 'itsec_lockouts',
26
+ ),
27
+ 'retain' => 0,
28
+ );
29
+
30
+ if ( isset( $itsec_setup_action ) ) {
31
+
32
+ switch ( $itsec_setup_action ) {
33
+
34
+ case 'activate':
35
+ $this->execute_activate();
36
+ break;
37
+ case 'upgrade':
38
+ $this->execute_upgrade();
39
+ break;
40
+ case 'deactivate':
41
+ $this->execute_deactivate();
42
+ break;
43
+ case 'uninstall':
44
+ $this->execute_uninstall();
45
+ break;
46
+
47
+ }
48
+
49
+ } else {
50
+ wp_die( 'error' );
51
+ }
52
+
53
+ }
54
+
55
+ /**
56
+ * Execute module activation.
57
+ *
58
+ * @since 4.0
59
+ *
60
+ * @return void
61
+ */
62
+ public function execute_activate() {
63
+
64
+ $options = get_site_option( 'itsec_backup' );
65
+
66
+ if ( $options === false ) {
67
+
68
+ add_site_option( 'itsec_backup', $this->defaults );
69
+
70
+ }
71
+
72
+ }
73
+
74
+ /**
75
+ * Execute module deactivation
76
+ *
77
+ * @return void
78
+ */
79
+ public function execute_deactivate() {
80
+ }
81
+
82
+ /**
83
+ * Execute module uninstall
84
+ *
85
+ * @return void
86
+ */
87
+ public function execute_uninstall() {
88
+
89
+ $this->execute_deactivate();
90
+
91
+ delete_site_option( 'itsec_backup' );
92
+
93
+ }
94
+
95
+ /**
96
+ * Execute module upgrade
97
+ *
98
+ * @return void
99
+ */
100
+ public function execute_upgrade() {
101
+
102
+ global $itsec_old_version;
103
+
104
+ if ( $itsec_old_version < 4000 ) {
105
+
106
+ global $itsec_bwps_options;
107
+
108
+ $current_options = get_site_option( 'itsec_backup' );
109
+
110
+ if ( $current_options === false ) {
111
+ $current_options = $this->defaults;
112
+ }
113
+
114
+ $current_options['enabled'] = isset( $itsec_bwps_options['backup_enabled'] ) && $itsec_bwps_options['backup_enabled'] == 1 ? true : false;
115
+ $current_options['interval'] = isset( $itsec_bwps_options['backup_interval'] ) ? intval( $itsec_bwps_options['backup_interval'] ) : 1;
116
+
117
+ update_site_option( 'itsec_backup', $current_options );
118
+
119
+ }
120
+
121
+ }
122
+
123
+ }
124
+
125
+ }
126
+
127
+ new ITSEC_Backup_Setup();
core/modules/ban-users/class-itsec-ban-users-admin.php ADDED
@@ -0,0 +1,771 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Ban_Users_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $module_path;
9
+
10
+ function run( $core ) {
11
+
12
+ $this->core = $core;
13
+ $this->settings = get_site_option( 'itsec_ban_users' );
14
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
15
+
16
+ add_filter( 'itsec_file_modules', array( $this, 'register_file' ) ); //register tooltip action
17
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
18
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
19
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
20
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'dashboard_status' ) ); //add information for plugin status
21
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
22
+
23
+ //manually save options on multisite
24
+ if ( is_multisite() ) {
25
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
26
+ }
27
+
28
+
29
+ add_filter( 'itsec_filter_apache_server_config_modification', array( $this, 'filter_apache_server_config_modification' ) );
30
+ add_filter( 'itsec_filter_nginx_server_config_modification', array( $this, 'filter_nginx_server_config_modification' ) );
31
+ add_filter( 'itsec_filter_litespeed_server_config_modification', array( $this, 'filter_litespeed_server_config_modification' ) );
32
+ }
33
+
34
+ /**
35
+ * Add meta boxes to primary options pages
36
+ *
37
+ * @since 4.0
38
+ *
39
+ * @return void
40
+ */
41
+ public function add_admin_meta_boxes() {
42
+
43
+ $id = 'ban_users_options';
44
+ $title = __( 'Banned Users', 'better-wp-security' );
45
+
46
+ add_meta_box(
47
+ $id,
48
+ $title,
49
+ array( $this, 'metabox_advanced_settings' ),
50
+ 'security_page_toplevel_page_itsec_settings',
51
+ 'advanced',
52
+ 'core'
53
+ );
54
+
55
+ $this->core->add_toc_item(
56
+ array(
57
+ 'id' => $id,
58
+ 'title' => $title,
59
+ )
60
+ );
61
+
62
+ }
63
+
64
+ /**
65
+ * Add Away mode Javascript
66
+ *
67
+ * @return void
68
+ */
69
+ public function admin_script() {
70
+
71
+ global $itsec_globals;
72
+
73
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
74
+
75
+ wp_enqueue_script( 'itsec_ban_users_js', $this->module_path . 'js/admin-ban_users.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
76
+
77
+ }
78
+
79
+ }
80
+
81
+ /**
82
+ * echos Banned Agents field
83
+ *
84
+ * @since 4.0
85
+ *
86
+ * @return void
87
+ */
88
+ public function ban_users_agent_list() {
89
+
90
+ $agent_list = '';
91
+
92
+ //Convert and show the agent list
93
+ if ( isset( $this->settings['agent_list'] ) && is_array( $this->settings['agent_list'] ) && sizeof( $this->settings['agent_list'] ) >= 1 ) {
94
+
95
+ $agent_list = implode( PHP_EOL, $this->settings['agent_list'] );
96
+
97
+ } elseif ( isset( $this->settings['agent_list'] ) && ! is_array( $this->settings['agent_list'] ) && strlen( $this->settings['agent_list'] ) > 1 ) {
98
+
99
+ $agent_list = $this->settings['agent_list'];
100
+
101
+ }
102
+
103
+ $content = '<textarea id="itsec_ban_users_agent_list" name="itsec_ban_users[agent_list]" rows="10" cols="50">' . $agent_list . PHP_EOL . '</textarea>';
104
+ $content .= '<p>' . __( 'Use the guidelines below to enter user agents that will not be allowed access to your site.', 'better-wp-security' ) . '</p>';
105
+ $content .= '<ul>';
106
+ $content .= '<li>' . __( 'Enter only 1 user agent per line.', 'better-wp-security' ) . '</li>';
107
+ $content .= '</ul>';
108
+
109
+ echo $content;
110
+
111
+ }
112
+
113
+ /**
114
+ * echos hackrepair list Field
115
+ *
116
+ * @since 4.0
117
+ *
118
+ * @return void
119
+ */
120
+ public function ban_users_default() {
121
+
122
+ if ( isset( $this->settings['default'] ) && $this->settings['default'] === true ) {
123
+ $default = 1;
124
+ } else {
125
+ $default = 0;
126
+ }
127
+
128
+ $content = '<input type="checkbox" id="itsec_ban_users_default" name="itsec_ban_users[default]" value="1" ' . checked( 1, $default, false ) . '/>';
129
+ $content .= '<label for="itsec_ban_users_default"> ' . __( 'Enable HackRepair.com\'s blacklist feature', 'better-wp-security' ) . '</label>';
130
+ $content .= '<p class="description">' . __( 'As a getting-started point you can include the excellent blacklist developed by Jim Walker of <a href="http://hackrepair.com/blog/how-to-block-bots-from-seeing-your-website-bad-bots-and-drive-by-hacks-explained" target="_blank">HackRepair.com</a>.', 'better-wp-security' ) . '</p>';
131
+
132
+ echo $content;
133
+
134
+ }
135
+
136
+ /**
137
+ * echos Enabled Field
138
+ *
139
+ * @since 4.0
140
+ *
141
+ * @return void
142
+ */
143
+ public function ban_users_enabled() {
144
+
145
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
146
+ $enabled = 1;
147
+ } else {
148
+ $enabled = 0;
149
+ }
150
+
151
+ $content = '<input type="checkbox" id="itsec_ban_users_enabled" name="itsec_ban_users[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
152
+ $content .= '<label for="itsec_ban_users_enabled"> ' . __( 'Enable ban users', 'better-wp-security' ) . '</label>';
153
+
154
+ echo $content;
155
+
156
+ }
157
+
158
+ /**
159
+ * echos Banned Hosts field
160
+ *
161
+ * @since 4.0
162
+ *
163
+ * @return void
164
+ */
165
+ public function ban_users_host_list() {
166
+
167
+ $host_list = '';
168
+
169
+ //Convert and show the host list
170
+ if ( isset( $this->settings['host_list'] ) && is_array( $this->settings['host_list'] ) && sizeof( $this->settings['host_list'] ) >= 1 ) {
171
+
172
+ $host_list = implode( PHP_EOL, $this->settings['host_list'] );
173
+
174
+ } elseif ( isset( $this->settings['host_list'] ) && ! is_array( $this->settings['host_list'] ) && strlen( $this->settings['host_list'] ) > 1 ) {
175
+
176
+ $host_list = $this->settings['host_list'];
177
+
178
+ }
179
+
180
+ echo '<textarea id="itsec_ban_users_host_list" name="itsec_ban_users[host_list]" rows="10" cols="50">' . $host_list . PHP_EOL . '</textarea>';
181
+ echo '<p>' . __( 'Use the guidelines below to enter hosts that will not be allowed access to your site. Note you cannot ban yourself.', 'better-wp-security' ) . '</p>';
182
+ echo '<ul>';
183
+ echo '<li>' . __( 'You may ban users by individual IP address or IP address range.', 'better-wp-security' ) . '</li>';
184
+ echo '<li>' . __( 'Individual IP addesses must be in IPV4 standard format (i.e. ###.###.###.### or ###.###.###.###/##). Wildcards (*) or a netmask is allowed to specify a range of ip addresses.', 'better-wp-security' ) . '</li>';
185
+ echo '<li>' . __( 'If using a wildcard (*) you must start with the right-most number in the ip field. For example ###.###.###.* and ###.###.*.* are permitted but ###.###.*.### is not.', 'better-wp-security' ) . '</li>';
186
+ echo '<li><a href="http://ip-lookup.net/domain-lookup.php" target="_blank">' . __( 'Lookup IP Address.', 'better-wp-security' ) . '</a></li>';
187
+ echo '<li>' . __( 'Enter only 1 IP address or 1 IP address range per line.', 'better-wp-security' ) . '</li>';
188
+ echo '</ul>';
189
+
190
+ }
191
+
192
+ public function filter_apache_server_config_modification( $modification ) {
193
+ $modification .= $this->get_server_config_default_blacklist_rules( 'apache' );
194
+ $modification .= $this->get_server_config_ban_hosts_rules( 'apache' );
195
+ $modification .= $this->get_server_config_ban_user_agents_rules( 'apache' );
196
+
197
+ return $modification;
198
+ }
199
+
200
+ public function filter_nginx_server_config_modification( $modification ) {
201
+ $modification .= $this->get_server_config_default_blacklist_rules( 'nginx' );
202
+ $modification .= $this->get_server_config_ban_hosts_rules( 'nginx' );
203
+ $modification .= $this->get_server_config_ban_user_agents_rules( 'nginx' );
204
+
205
+ return $modification;
206
+ }
207
+
208
+ public function filter_litespeed_server_config_modification( $modification ) {
209
+ $modification .= $this->get_server_config_default_blacklist_rules( 'litespeed' );
210
+ $modification .= $this->get_server_config_ban_hosts_rules( 'litespeed' );
211
+ $modification .= $this->get_server_config_ban_user_agents_rules( 'litespeed' );
212
+
213
+ return $modification;
214
+ }
215
+
216
+ protected function get_server_config_default_blacklist_rules( $server_type ) {
217
+ if ( true !== $this->settings['default'] ) {
218
+ return '';
219
+ }
220
+
221
+
222
+ $rules = '';
223
+
224
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' );
225
+
226
+ $file = plugin_dir_path( __FILE__ ) . "lists/hackrepair-$server_type.inc";
227
+
228
+ if ( ITSEC_Lib_File::is_file( $file ) ) {
229
+ $default_list = ITSEC_Lib_File::read( $file );
230
+
231
+ if ( ! empty( $default_list ) ) {
232
+ $default_list = preg_replace( '/^/m', "\t", $default_list );
233
+
234
+ $rules .= "\n";
235
+ $rules .= "\t# " . __( 'Enable HackRepair.com\'s blacklist feature - Security > Settings > Banned Users > Default Blacklist', 'better-wp-security' ) . "\n";
236
+ $rules .= $default_list;
237
+ }
238
+ }
239
+
240
+ return $rules;
241
+ }
242
+
243
+ protected function get_server_config_ban_hosts_rules( $server_type ) {
244
+ if ( true !== $this->settings['enabled'] ) {
245
+ return '';
246
+ }
247
+ if ( ! is_array( $this->settings['host_list'] ) || empty( $this->settings['host_list'] ) ) {
248
+ return '';
249
+ }
250
+
251
+
252
+
253
+ if ( ! class_exists( 'ITSEC_Ban_Users' ) ) {
254
+ require( dirname( __FILE__ ) . '/class-itsec-ban-users.php' );
255
+ }
256
+
257
+
258
+ $host_rules = '';
259
+ $set_env_rules = '';
260
+ $deny_rules = '';
261
+ $require_rules = '';
262
+
263
+ // process hosts list
264
+ foreach ( $this->settings['host_list'] as $host ) {
265
+ $host = ITSEC_Lib::ip_wild_to_mask( $host );
266
+ $host = trim( $host );
267
+
268
+ if ( empty( $host ) ) {
269
+ continue;
270
+ }
271
+
272
+ if ( ITSEC_Ban_Users::is_ip_whitelisted( $host ) ) {
273
+ /**
274
+ * @todo warn the user the ip to be banned is whitelisted
275
+ */
276
+ continue;
277
+ }
278
+
279
+
280
+ if ( in_array( $server_type, array( 'apache', 'litespeed' ) ) ) {
281
+ $converted_host = ITSEC_Lib::ip_mask_to_range( $host );
282
+ $converted_host = trim( $converted_host );
283
+
284
+ if ( empty( $converted_host ) ) {
285
+ continue;
286
+ }
287
+
288
+
289
+ $set_env_host = str_replace( '.', '\\.', $converted_host );
290
+
291
+ $set_env_rules .= "\tSetEnvIF REMOTE_ADDR \"^$set_env_host$\" DenyAccess\n"; // Ban IP
292
+ $set_env_rules .= "\tSetEnvIF X-FORWARDED-FOR \"^$set_env_host$\" DenyAccess\n"; // Ban IP from a proxy
293
+ $set_env_rules .= "\tSetEnvIF X-CLUSTER-CLIENT-IP \"^$set_env_host$\" DenyAccess\n"; // Ban IP from a load balancer
294
+ $set_env_rules .= "\n";
295
+
296
+
297
+ $require_host = str_replace( '.[0-9]+', '', $converted_host );
298
+
299
+ $require_rules .= "\t\t\tRequire not ip $require_host\n";
300
+ $deny_rules .= "\t\tDeny from $require_host\n";
301
+ } else if ( 'nginx' === $server_type ) {
302
+ $host_rules .= "\tdeny $host;\n";
303
+ }
304
+ }
305
+
306
+
307
+ $rules = '';
308
+
309
+ if ( 'apache' === $server_type ) {
310
+ if ( ! empty( $set_env_rules ) ) {
311
+ $rules .= "\n";
312
+ $rules .= "\t# " . __( 'Ban Hosts - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
313
+ $rules .= $set_env_rules;
314
+ $rules .= "\t<IfModule mod_authz_core.c>\n";
315
+ $rules .= "\t\t<RequireAll>\n";
316
+ $rules .= "\t\t\tRequire all granted\n";
317
+ $rules .= "\t\t\tRequire not env DenyAccess\n";
318
+ $rules .= $require_rules;
319
+ $rules .= "\t\t</RequireAll>\n";
320
+ $rules .= "\t</IfModule>\n";
321
+ $rules .= "\t<IfModule !mod_authz_core.c>\n";
322
+ $rules .= "\t\tOrder allow,deny\n";
323
+ $rules .= "\t\tAllow from all\n";
324
+ $rules .= "\t\tDeny from env=DenyAccess\n";
325
+ $rules .= $deny_rules;
326
+ $rules .= "\t</IfModule>\n";
327
+ }
328
+ } else if ( 'litespeed' === $server_type ) {
329
+ if ( ! empty( $set_env_rules ) ) {
330
+ $rules .= "\n";
331
+ $rules .= "\t# " . __( 'Ban Hosts - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
332
+ $rules .= $set_env_rules;
333
+ $rules .= "\t<IfModule mod_litespeed.c>\n";
334
+ $rules .= "\t\tOrder allow,deny\n";
335
+ $rules .= "\t\tAllow from all\n";
336
+ $rules .= "\t\tDeny from env=DenyAccess\n";
337
+ $rules .= $deny_rules;
338
+ $rules .= "\t</IfModule>\n";
339
+ }
340
+ } else if ( 'nginx' === $server_type ) {
341
+ if ( ! empty( $host_rules ) ) {
342
+ $rules .= "\n";
343
+ $rules .= "# " . __( 'Ban Hosts - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
344
+ $rules .= $host_rules;
345
+ }
346
+ }
347
+
348
+ return $rules;
349
+ }
350
+
351
+ protected function get_server_config_ban_user_agents_rules( $server_type ) {
352
+ if ( true !== $this->settings['enabled'] ) {
353
+ return '';
354
+ }
355
+ if ( ! is_array( $this->settings['agent_list'] ) || empty( $this->settings['agent_list'] ) ) {
356
+ return '';
357
+ }
358
+
359
+
360
+ $agent_rules = '';
361
+ $rewrite_rules = '';
362
+
363
+ foreach ( $this->settings['agent_list'] as $index => $agent ) {
364
+ $agent = trim( $agent );
365
+
366
+ if ( empty( $agent ) ) {
367
+ continue;
368
+ }
369
+
370
+
371
+ $agent = preg_quote( $agent );
372
+
373
+ if ( in_array( $server_type, array( 'apache', 'litespeed' ) ) ) {
374
+ $agent = str_replace( ' ', '\\ ', $agent );
375
+ $rewrite_rules .= "\t\tRewriteCond %{HTTP_USER_AGENT} ^$agent [NC,OR]\n";
376
+ } else if ( 'nginx' === $server_type ) {
377
+ $agent = str_replace( '"', '\\"', $agent );
378
+ $agent_rules .= "if (\$http_user_agent ~* \"^$agent\") { return 403; }\n";
379
+ }
380
+ }
381
+
382
+ if ( in_array( $server_type, array( 'apache', 'litespeed' ) ) && ! empty( $rewrite_rules ) ) {
383
+ $rewrite_rules = preg_replace( "/\[NC,OR\]\n$/", "[NC]\n", $rewrite_rules );
384
+
385
+ $agent_rules .= "\t<IfModule mod_rewrite.c>\n";
386
+ $agent_rules .= "\t\tRewriteEngine On\n";
387
+ $agent_rules .= $rewrite_rules;
388
+ $agent_rules .= "\t\tRewriteRule ^.* - [F]\n";
389
+ $agent_rules .= "\t</IfModule>\n";
390
+ }
391
+
392
+
393
+ $rules = '';
394
+
395
+ if ( ! empty( $agent_rules ) ) {
396
+ $rules .= "\n";
397
+ $rules .= "\t# " . __( 'Ban User Agents - Security > Settings > Banned Users', 'better-wp-security' ) . "\n";
398
+ $rules .= $agent_rules;
399
+ }
400
+
401
+ return $rules;
402
+ }
403
+
404
+ /**
405
+ * Sets the status in the plugin dashboard
406
+ *
407
+ * @since 4.0
408
+ *
409
+ * @return array statuses
410
+ */
411
+ public function dashboard_status( $statuses ) {
412
+
413
+ if ( $this->settings['enabled'] === true ) {
414
+
415
+ $status_array = 'safe-low';
416
+ $status = array(
417
+ 'text' => __( 'You are blocking known bad hosts and agents with the ban users tool.', 'better-wp-security' ),
418
+ 'link' => '#itsec_ban_users_enabled',
419
+ );
420
+
421
+ } else {
422
+
423
+ $status_array = 'low';
424
+ $status = array(
425
+ 'text' => __( 'You are not blocking any users that are known to be a problem. Consider turning on the Ban Users feature.', 'better-wp-security' ),
426
+ 'link' => '#itsec_ban_users_enabled',
427
+ );
428
+
429
+ }
430
+
431
+ array_push( $statuses[$status_array], $status );
432
+
433
+ return $statuses;
434
+
435
+ }
436
+
437
+ /**
438
+ * Execute admin initializations
439
+ *
440
+ * @return void
441
+ */
442
+ public function initialize_admin() {
443
+
444
+ //default blacklist section
445
+ add_settings_section(
446
+ 'ban_users_default',
447
+ __( 'Default Blacklist', 'better-wp-security' ),
448
+ '__return_empty_string',
449
+ 'security_page_toplevel_page_itsec_settings'
450
+ );
451
+
452
+ //Enabled section
453
+ add_settings_section(
454
+ 'ban_users_enabled',
455
+ __( 'Configure Ban Users', 'better-wp-security' ),
456
+ '__return_empty_string',
457
+ 'security_page_toplevel_page_itsec_settings'
458
+ );
459
+
460
+ //primary settings section
461
+ add_settings_section(
462
+ 'ban_users_settings',
463
+ __( 'Configure Ban Users', 'better-wp-security' ),
464
+ '__return_empty_string',
465
+ 'security_page_toplevel_page_itsec_settings'
466
+ );
467
+
468
+ //default list field
469
+ add_settings_field(
470
+ 'itsec_ban_users[default]',
471
+ __( 'Default Blacklist', 'better-wp-security' ),
472
+ array( $this, 'ban_users_default' ),
473
+ 'security_page_toplevel_page_itsec_settings',
474
+ 'ban_users_default'
475
+ );
476
+
477
+ //enabled field
478
+ add_settings_field(
479
+ 'itsec_ban_users[enabled]',
480
+ __( 'Ban Users', 'better-wp-security' ),
481
+ array( $this, 'ban_users_enabled' ),
482
+ 'security_page_toplevel_page_itsec_settings',
483
+ 'ban_users_enabled'
484
+ );
485
+
486
+ //host list field
487
+ add_settings_field(
488
+ 'itsec_ban_users[host_list]',
489
+ __( 'Ban Hosts', 'better-wp-security' ),
490
+ array( $this, 'ban_users_host_list' ),
491
+ 'security_page_toplevel_page_itsec_settings',
492
+ 'ban_users_settings'
493
+ );
494
+
495
+ //agent _list field
496
+ add_settings_field(
497
+ 'itsec_ban_users[agent_list]',
498
+ __( 'Ban User Agents', 'better-wp-security' ),
499
+ array( $this, 'ban_users_agent_list' ),
500
+ 'security_page_toplevel_page_itsec_settings',
501
+ 'ban_users_settings'
502
+ );
503
+
504
+ //Register the settings field for the entire module
505
+ register_setting(
506
+ 'security_page_toplevel_page_itsec_settings',
507
+ 'itsec_ban_users',
508
+ array( $this, 'sanitize_module_input' )
509
+ );
510
+
511
+ }
512
+
513
+ /**
514
+ * Render the settings metabox
515
+ *
516
+ * @return void
517
+ */
518
+ public function metabox_advanced_settings() {
519
+
520
+ echo '<p>' . __( 'This feature allows you to completely ban hosts and user agents from your site without having to manage any configuration of your server. Any IP addresses or user agents found in the lists below will not be allowed any access to your site.', 'better-wp-security' ) . '</p>';
521
+
522
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'ban_users_default', false );
523
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'ban_users_enabled', false );
524
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'ban_users_settings', false );
525
+
526
+ echo '<p>' . PHP_EOL;
527
+
528
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
529
+
530
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
531
+
532
+ echo '</p>' . PHP_EOL;
533
+
534
+ echo '</form>';
535
+
536
+ }
537
+
538
+ /**
539
+ * Register ban users for file writer
540
+ *
541
+ * @param array $file_modules array of file writer modules
542
+ *
543
+ * @return array array of file writer modules
544
+ */
545
+ public function register_file( $file_modules ) {
546
+
547
+ $file_modules['ban-users'] = array(
548
+ 'rewrite' => array( $this, 'save_rewrite_rules' ),
549
+ );
550
+
551
+ return $file_modules;
552
+
553
+ }
554
+
555
+ /**
556
+ * Sanitize and validate input
557
+ *
558
+ * @param Array $input array of input fields
559
+ *
560
+ * @return Array Sanitized array
561
+ */
562
+ public function sanitize_module_input( $input ) {
563
+
564
+ global $itsec_globals;
565
+
566
+ $no_errors = false; //start out assuming they entered a bad IP somewhere
567
+
568
+ //Sanitize checkbox features
569
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
570
+ $input['default'] = ( isset( $input['default'] ) && intval( $input['default'] == 1 ) ? true : false );
571
+
572
+ //process agent list
573
+ if ( isset( $input['agent_list'] ) && ! is_array( $input['agent_list'] ) ) {
574
+
575
+ $agents = explode( PHP_EOL, $input['agent_list'] );
576
+
577
+ } elseif ( isset( $input['agent_list'] ) ) {
578
+
579
+ $agents = $input['agent_list'];
580
+
581
+ } else {
582
+
583
+ $agents = array();
584
+
585
+ }
586
+
587
+ $good_agents = array();
588
+
589
+ foreach ( $agents as $agent ) {
590
+ $good_agents[] = trim( sanitize_text_field( $agent ) );
591
+ }
592
+
593
+ $input['agent_list'] = $good_agents;
594
+
595
+ //Process hosts list
596
+ if ( isset( $input['host_list'] ) && ! is_array( $input['host_list'] ) ) {
597
+
598
+ $addresses = explode( PHP_EOL, $input['host_list'] );
599
+
600
+ } elseif ( isset( $input['host_list'] ) ) {
601
+
602
+ $addresses = $input['host_list'];
603
+
604
+ } else {
605
+
606
+ $addresses = array();
607
+
608
+ }
609
+
610
+ $bad_ips = array();
611
+ $white_ips = array();
612
+ $raw_ips = array();
613
+
614
+ foreach ( $addresses as $index => $address ) {
615
+
616
+ if ( strlen( trim( $address ) ) > 0 ) {
617
+
618
+ if ( ITSEC_Lib::validates_ip_address( $address ) === false ) {
619
+
620
+ $bad_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
621
+
622
+ }
623
+
624
+ if ( ! class_exists( 'ITSEC_Ban_Users' ) ) {
625
+ require( dirname( __FILE__ ) . '/class-itsec-ban-users.php' );
626
+ }
627
+
628
+ if ( ITSEC_Ban_Users::is_ip_whitelisted( $address, null, true ) ) {
629
+
630
+ $white_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
631
+
632
+ }
633
+
634
+ $raw_ips[] = trim( filter_var( $address, FILTER_SANITIZE_STRING ) );
635
+
636
+ } else {
637
+ unset( $addresses[$index] );
638
+ }
639
+
640
+ }
641
+
642
+ $raw_ips = array_unique( $raw_ips );
643
+
644
+ if ( sizeof( $bad_ips ) > 0 ) {
645
+
646
+ $input['enabled'] = false; //disable ban users list
647
+
648
+ $type = 'error';
649
+
650
+ if ( $no_errors === true ) {
651
+ $message = sprintf( '%s<br /><br />', __( 'Note that the ban users feature has been disabled until the following errors are corrected:', 'better-wp-security' ) );
652
+ }
653
+
654
+ foreach ( $bad_ips as $bad_ip ) {
655
+ $message .= sprintf( '%s %s<br />', $bad_ip, __( 'is not a valid address in the ban users box.', 'better-wp-security' ) );
656
+ }
657
+
658
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
659
+
660
+ } else {
661
+
662
+ $no_errors = true;
663
+
664
+ }
665
+
666
+ if ( sizeof( $white_ips ) > 0 ) {
667
+
668
+ $input['enabled'] = false; //disable ban users list
669
+
670
+ $type = 'error';
671
+
672
+ if ( $no_errors === true ) {
673
+ $message = sprintf( '%s<br /><br />', __( 'Note that the ban users feature has been disabled until the following errors are corrected:', 'better-wp-security' ) );
674
+ }
675
+
676
+ foreach ( $white_ips as $white_ip ) {
677
+ $message .= sprintf( '%s %s<br />', $white_ip, __( 'is not a valid address as it has been white listed.', 'better-wp-security' ) );
678
+ }
679
+
680
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
681
+
682
+ } else {
683
+
684
+ $no_errors = true;
685
+
686
+ }
687
+
688
+ $input['host_list'] = $raw_ips;
689
+
690
+ if ( $no_errors === true ) {
691
+
692
+ if (
693
+ ! isset( $type ) &&
694
+ (
695
+ $input['host_list'] !== $this->settings['host_list'] ||
696
+ $input['enabled'] !== $this->settings['enabled'] ||
697
+ $input['default'] !== $this->settings['default'] ||
698
+ $input['agent_list'] !== $this->settings['agent_list']
699
+ ) ||
700
+ isset( $itsec_globals['settings']['write_files'] ) && $itsec_globals['settings']['write_files'] === true
701
+ ) {
702
+
703
+ add_site_option( 'itsec_rewrites_changed', true );
704
+
705
+ }
706
+
707
+ }
708
+
709
+ if ( is_multisite() ) {
710
+
711
+ if ( isset( $type ) ) {
712
+
713
+ $error_handler = new WP_Error();
714
+
715
+ $error_handler->add( $type, $message );
716
+
717
+ $this->core->show_network_admin_notice( $error_handler );
718
+
719
+ } else {
720
+
721
+ $this->core->show_network_admin_notice( false );
722
+
723
+ }
724
+
725
+ $this->settings = $input;
726
+
727
+ }
728
+
729
+ return $input;
730
+
731
+ }
732
+
733
+ /**
734
+ * Prepare and save options in network settings
735
+ *
736
+ * @return void
737
+ */
738
+ public function save_network_options() {
739
+
740
+ if ( isset( $_POST['itsec_ban_users'] ) ) {
741
+
742
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
743
+ die( __( 'Security error!', 'better-wp-security' ) );
744
+ }
745
+
746
+ update_site_option( 'itsec_ban_users', $_POST['itsec_ban_users'] ); //we must manually save network options
747
+
748
+ }
749
+
750
+ }
751
+
752
+ /**
753
+ * Adds fields that will be tracked for Google Analytics
754
+ *
755
+ * @since 4.0
756
+ *
757
+ * @param array $vars tracking vars
758
+ *
759
+ * @return array tracking vars
760
+ */
761
+ public function tracking_vars( $vars ) {
762
+
763
+ $vars['itsec_ban_users'] = array(
764
+ 'enabled' => '0:b',
765
+ 'default' => '0:b',
766
+ );
767
+
768
+ return $vars;
769
+
770
+ }
771
+ }
core/modules/ban-users/class-itsec-ban-users.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Ban_Users {
4
+
5
+ function run() {
6
+
7
+ return null;
8
+ }
9
+
10
+ /**
11
+ * Inserts an IP address into the htaccess ban list.
12
+ *
13
+ * @since 4.0
14
+ *
15
+ * @param $ip
16
+ * @param null $ban_list
17
+ * @param null $white_list
18
+ *
19
+ * @return void
20
+ */
21
+ public static function insert_ip( $ip, $ban_list = null, $white_list = null ) {
22
+
23
+ $settings = get_site_option( 'itsec_ban_users' );
24
+
25
+ $host = sanitize_text_field( $ip );
26
+
27
+ if ( $ban_list === null ) {
28
+
29
+ $ban_list = isset( $settings['host_list'] ) ? $settings['host_list'] : array();
30
+
31
+ }
32
+
33
+ if ( $white_list === null ) {
34
+
35
+ $global_settings = get_site_option( 'itsec_global' );
36
+
37
+ $white_list = isset( $global_settings['lockout_white_list'] ) ? $global_settings['lockout_white_list'] : array();
38
+
39
+ }
40
+
41
+ if ( ! in_array( $host, $ban_list ) && ! ITSEC_Ban_Users::is_ip_whitelisted( $host, $white_list ) ) {
42
+
43
+ $ban_list[] = $host;
44
+ $settings['host_list'] = $ban_list;
45
+ ITSEC_Files::quick_ban( $host );
46
+ update_site_option( 'itsec_ban_users', $settings );
47
+ add_site_option( 'itsec_rewrites_changed', true );
48
+
49
+ }
50
+
51
+ }
52
+
53
+ /**
54
+ * Determines whether a given IP address is whitelisted
55
+ *
56
+ * @param string $ip_to_check ip to check
57
+ * @param array $white_ips ip list to compare to if not yet saved to options
58
+ * @param boolean $current whether to whitelist the current ip or not (due to saving, etc)
59
+ *
60
+ * @return boolean true if whitelisted or false
61
+ */
62
+ public static function is_ip_whitelisted( $ip_to_check, $white_ips = null, $current = false ) {
63
+
64
+ $ip_to_check = trim( $ip_to_check );
65
+
66
+ if ( $white_ips === null ) {
67
+
68
+ $global_settings = get_site_option( 'itsec_global' );
69
+
70
+ $white_ips = ( isset( $global_settings['lockout_white_list'] ) ? $global_settings['lockout_white_list'] : array() );
71
+
72
+ }
73
+
74
+ if ( $current === true ) {
75
+ $white_ips[] = ITSEC_Lib::get_ip(); //add current user ip to whitelist to check automatically
76
+ }
77
+
78
+ foreach ( $white_ips as $white_ip ) {
79
+
80
+ $converted_white_ip = ITSEC_Lib::ip_wild_to_mask( $white_ip );
81
+
82
+ $check_range = ITSEC_Lib::cidr_to_range( $converted_white_ip );
83
+ $ip_range = ITSEC_Lib::cidr_to_range( $ip_to_check );
84
+
85
+ if ( sizeof( $check_range ) === 2 ) { //range to check
86
+
87
+ $check_min = ip2long( $check_range[0] );
88
+ $check_max = ip2long( $check_range[1] );
89
+
90
+ if ( sizeof( $ip_range ) === 2 ) {
91
+
92
+ $ip_min = ip2long( $ip_range[0] );
93
+ $ip_max = ip2long( $ip_range[1] );
94
+
95
+ if ( ( $check_min < $ip_min && $ip_min < $check_max ) || ( $check_min < $ip_max && $ip_max < $check_max ) ) {
96
+ return true;
97
+ }
98
+
99
+ } else {
100
+
101
+ $ip = ip2long( $ip_range[0] );
102
+
103
+ if ( $check_min < $ip && $ip < $check_max ) {
104
+ return true;
105
+ }
106
+
107
+ }
108
+
109
+ } else { //single ip to check
110
+
111
+ $check = ip2long( $check_range[0] );
112
+
113
+ if ( sizeof( $ip_range ) === 2 ) {
114
+
115
+ $ip_min = ip2long( $ip_range[0] );
116
+ $ip_max = ip2long( $ip_range[1] );
117
+
118
+ if ( $ip_min < $check && $check < $ip_max ) {
119
+ return true;
120
+ }
121
+
122
+ } else {
123
+
124
+ $ip = ip2long( $ip_range[0] );
125
+
126
+ if ( $check == $ip ) {
127
+ return true;
128
+ }
129
+
130
+ }
131
+
132
+ }
133
+
134
+ }
135
+
136
+ return false;
137
+
138
+ }
139
+
140
+ }
core/modules/ban-users/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ban-users/js/admin-ban_users.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_ban_users_enabled" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_ban_users_enabled" ).is( ':checked' ) || ( jQuery( "#setting-error-settings_updated" ).length > 0 && jQuery( "#setting-error-settings_updated" ).hasClass( "error" ) ) ) {
6
+
7
+ jQuery( "#ban_users_settings" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#ban_users_settings" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ } );
core/modules/ban-users/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ban-users/lists/hackrepair-apache.inc ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Start HackRepair.com Blacklist
2
+ RewriteEngine on
3
+ # Start Abuse Agent Blocking
4
+ RewriteCond %{HTTP_USER_AGENT} "^Mozilla.*Indy" [NC,OR]
5
+ RewriteCond %{HTTP_USER_AGENT} "^Mozilla.*NEWT" [NC,OR]
6
+ RewriteCond %{HTTP_USER_AGENT} "^$" [NC,OR]
7
+ RewriteCond %{HTTP_USER_AGENT} "^Maxthon$" [NC,OR]
8
+ RewriteCond %{HTTP_USER_AGENT} "^SeaMonkey$" [NC,OR]
9
+ RewriteCond %{HTTP_USER_AGENT} "^Acunetix" [NC,OR]
10
+ RewriteCond %{HTTP_USER_AGENT} "^binlar" [NC,OR]
11
+ RewriteCond %{HTTP_USER_AGENT} "^BlackWidow" [NC,OR]
12
+ RewriteCond %{HTTP_USER_AGENT} "^Bolt 0" [NC,OR]
13
+ RewriteCond %{HTTP_USER_AGENT} "^BOT for JCE" [NC,OR]
14
+ RewriteCond %{HTTP_USER_AGENT} "^Bot mailto\:craftbot@yahoo\.com" [NC,OR]
15
+ RewriteCond %{HTTP_USER_AGENT} "^casper" [NC,OR]
16
+ RewriteCond %{HTTP_USER_AGENT} "^checkprivacy" [NC,OR]
17
+ RewriteCond %{HTTP_USER_AGENT} "^ChinaClaw" [NC,OR]
18
+ RewriteCond %{HTTP_USER_AGENT} "^clshttp" [NC,OR]
19
+ RewriteCond %{HTTP_USER_AGENT} "^cmsworldmap" [NC,OR]
20
+ RewriteCond %{HTTP_USER_AGENT} "^comodo" [NC,OR]
21
+ RewriteCond %{HTTP_USER_AGENT} "^Custo" [NC,OR]
22
+ RewriteCond %{HTTP_USER_AGENT} "^Default Browser 0" [NC,OR]
23
+ RewriteCond %{HTTP_USER_AGENT} "^diavol" [NC,OR]
24
+ RewriteCond %{HTTP_USER_AGENT} "^DIIbot" [NC,OR]
25
+ RewriteCond %{HTTP_USER_AGENT} "^DISCo" [NC,OR]
26
+ RewriteCond %{HTTP_USER_AGENT} "^dotbot" [NC,OR]
27
+ RewriteCond %{HTTP_USER_AGENT} "^Download Demon" [NC,OR]
28
+ RewriteCond %{HTTP_USER_AGENT} "^eCatch" [NC,OR]
29
+ RewriteCond %{HTTP_USER_AGENT} "^EirGrabber" [NC,OR]
30
+ RewriteCond %{HTTP_USER_AGENT} "^EmailCollector" [NC,OR]
31
+ RewriteCond %{HTTP_USER_AGENT} "^EmailSiphon" [NC,OR]
32
+ RewriteCond %{HTTP_USER_AGENT} "^EmailWolf" [NC,OR]
33
+ RewriteCond %{HTTP_USER_AGENT} "^Express WebPictures" [NC,OR]
34
+ RewriteCond %{HTTP_USER_AGENT} "^extract" [NC,OR]
35
+ RewriteCond %{HTTP_USER_AGENT} "^ExtractorPro" [NC,OR]
36
+ RewriteCond %{HTTP_USER_AGENT} "^EyeNetIE" [NC,OR]
37
+ RewriteCond %{HTTP_USER_AGENT} "^feedfinder" [NC,OR]
38
+ RewriteCond %{HTTP_USER_AGENT} "^FHscan" [NC,OR]
39
+ RewriteCond %{HTTP_USER_AGENT} "^FlashGet" [NC,OR]
40
+ RewriteCond %{HTTP_USER_AGENT} "^flicky" [NC,OR]
41
+ RewriteCond %{HTTP_USER_AGENT} "^g00g1e" [NC,OR]
42
+ RewriteCond %{HTTP_USER_AGENT} "^GetRight" [NC,OR]
43
+ RewriteCond %{HTTP_USER_AGENT} "^GetWeb\!" [NC,OR]
44
+ RewriteCond %{HTTP_USER_AGENT} "^Go\!Zilla" [NC,OR]
45
+ RewriteCond %{HTTP_USER_AGENT} "^Go\-Ahead\-Got\-It" [NC,OR]
46
+ RewriteCond %{HTTP_USER_AGENT} "^grab" [NC,OR]
47
+ RewriteCond %{HTTP_USER_AGENT} "^GrabNet" [NC,OR]
48
+ RewriteCond %{HTTP_USER_AGENT} "^Grafula" [NC,OR]
49
+ RewriteCond %{HTTP_USER_AGENT} "^harvest" [NC,OR]
50
+ RewriteCond %{HTTP_USER_AGENT} "^HMView" [NC,OR]
51
+ RewriteCond %{HTTP_USER_AGENT} "^ia_archiver" [NC,OR]
52
+ RewriteCond %{HTTP_USER_AGENT} "^Image Stripper" [NC,OR]
53
+ RewriteCond %{HTTP_USER_AGENT} "^Image Sucker" [NC,OR]
54
+ RewriteCond %{HTTP_USER_AGENT} "^InterGET" [NC,OR]
55
+ RewriteCond %{HTTP_USER_AGENT} "^Internet Ninja" [NC,OR]
56
+ RewriteCond %{HTTP_USER_AGENT} "^InternetSeer\.com" [NC,OR]
57
+ RewriteCond %{HTTP_USER_AGENT} "^jakarta" [NC,OR]
58
+ RewriteCond %{HTTP_USER_AGENT} "^Java" [NC,OR]
59
+ RewriteCond %{HTTP_USER_AGENT} "^JetCar" [NC,OR]
60
+ RewriteCond %{HTTP_USER_AGENT} "^JOC Web Spider" [NC,OR]
61
+ RewriteCond %{HTTP_USER_AGENT} "^kanagawa" [NC,OR]
62
+ RewriteCond %{HTTP_USER_AGENT} "^kmccrew" [NC,OR]
63
+ RewriteCond %{HTTP_USER_AGENT} "^larbin" [NC,OR]
64
+ RewriteCond %{HTTP_USER_AGENT} "^LeechFTP" [NC,OR]
65
+ RewriteCond %{HTTP_USER_AGENT} "^libwww" [NC,OR]
66
+ RewriteCond %{HTTP_USER_AGENT} "^Mass Downloader" [NC,OR]
67
+ RewriteCond %{HTTP_USER_AGENT} "^microsoft\.url" [NC,OR]
68
+ RewriteCond %{HTTP_USER_AGENT} "^MIDown tool" [NC,OR]
69
+ RewriteCond %{HTTP_USER_AGENT} "^miner" [NC,OR]
70
+ RewriteCond %{HTTP_USER_AGENT} "^Mister PiX" [NC,OR]
71
+ RewriteCond %{HTTP_USER_AGENT} "^MSFrontPage" [NC,OR]
72
+ RewriteCond %{HTTP_USER_AGENT} "^Navroad" [NC,OR]
73
+ RewriteCond %{HTTP_USER_AGENT} "^NearSite" [NC,OR]
74
+ RewriteCond %{HTTP_USER_AGENT} "^Net Vampire" [NC,OR]
75
+ RewriteCond %{HTTP_USER_AGENT} "^NetAnts" [NC,OR]
76
+ RewriteCond %{HTTP_USER_AGENT} "^NetSpider" [NC,OR]
77
+ RewriteCond %{HTTP_USER_AGENT} "^NetZIP" [NC,OR]
78
+ RewriteCond %{HTTP_USER_AGENT} "^nutch" [NC,OR]
79
+ RewriteCond %{HTTP_USER_AGENT} "^Octopus" [NC,OR]
80
+ RewriteCond %{HTTP_USER_AGENT} "^Offline Explorer" [NC,OR]
81
+ RewriteCond %{HTTP_USER_AGENT} "^Offline Navigator" [NC,OR]
82
+ RewriteCond %{HTTP_USER_AGENT} "^PageGrabber" [NC,OR]
83
+ RewriteCond %{HTTP_USER_AGENT} "^Papa Foto" [NC,OR]
84
+ RewriteCond %{HTTP_USER_AGENT} "^pavuk" [NC,OR]
85
+ RewriteCond %{HTTP_USER_AGENT} "^pcBrowser" [NC,OR]
86
+ RewriteCond %{HTTP_USER_AGENT} "^PeoplePal" [NC,OR]
87
+ RewriteCond %{HTTP_USER_AGENT} "^planetwork" [NC,OR]
88
+ RewriteCond %{HTTP_USER_AGENT} "^psbot" [NC,OR]
89
+ RewriteCond %{HTTP_USER_AGENT} "^purebot" [NC,OR]
90
+ RewriteCond %{HTTP_USER_AGENT} "^pycurl" [NC,OR]
91
+ RewriteCond %{HTTP_USER_AGENT} "^RealDownload" [NC,OR]
92
+ RewriteCond %{HTTP_USER_AGENT} "^ReGet" [NC,OR]
93
+ RewriteCond %{HTTP_USER_AGENT} "^Rippers 0" [NC,OR]
94
+ RewriteCond %{HTTP_USER_AGENT} "^sitecheck\.internetseer\.com" [NC,OR]
95
+ RewriteCond %{HTTP_USER_AGENT} "^SiteSnagger" [NC,OR]
96
+ RewriteCond %{HTTP_USER_AGENT} "^skygrid" [NC,OR]
97
+ RewriteCond %{HTTP_USER_AGENT} "^SmartDownload" [NC,OR]
98
+ RewriteCond %{HTTP_USER_AGENT} "^sucker" [NC,OR]
99
+ RewriteCond %{HTTP_USER_AGENT} "^SuperBot" [NC,OR]
100
+ RewriteCond %{HTTP_USER_AGENT} "^SuperHTTP" [NC,OR]
101
+ RewriteCond %{HTTP_USER_AGENT} "^Surfbot" [NC,OR]
102
+ RewriteCond %{HTTP_USER_AGENT} "^tAkeOut" [NC,OR]
103
+ RewriteCond %{HTTP_USER_AGENT} "^Teleport Pro" [NC,OR]
104
+ RewriteCond %{HTTP_USER_AGENT} "^Toata dragostea mea pentru diavola" [NC,OR]
105
+ RewriteCond %{HTTP_USER_AGENT} "^turnit" [NC,OR]
106
+ RewriteCond %{HTTP_USER_AGENT} "^vikspider" [NC,OR]
107
+ RewriteCond %{HTTP_USER_AGENT} "^VoidEYE" [NC,OR]
108
+ RewriteCond %{HTTP_USER_AGENT} "^Web Image Collector" [NC,OR]
109
+ RewriteCond %{HTTP_USER_AGENT} "^Web Sucker" [NC,OR]
110
+ RewriteCond %{HTTP_USER_AGENT} "^WebAuto" [NC,OR]
111
+ RewriteCond %{HTTP_USER_AGENT} "^WebBandit" [NC,OR]
112
+ RewriteCond %{HTTP_USER_AGENT} "^WebCopier" [NC,OR]
113
+ RewriteCond %{HTTP_USER_AGENT} "^WebFetch" [NC,OR]
114
+ RewriteCond %{HTTP_USER_AGENT} "^WebGo IS" [NC,OR]
115
+ RewriteCond %{HTTP_USER_AGENT} "^WebLeacher" [NC,OR]
116
+ RewriteCond %{HTTP_USER_AGENT} "^WebReaper" [NC,OR]
117
+ RewriteCond %{HTTP_USER_AGENT} "^WebSauger" [NC,OR]
118
+ RewriteCond %{HTTP_USER_AGENT} "^Website eXtractor" [NC,OR]
119
+ RewriteCond %{HTTP_USER_AGENT} "^Website Quester" [NC,OR]
120
+ RewriteCond %{HTTP_USER_AGENT} "^WebStripper" [NC,OR]
121
+ RewriteCond %{HTTP_USER_AGENT} "^WebWhacker" [NC,OR]
122
+ RewriteCond %{HTTP_USER_AGENT} "^WebZIP" [NC,OR]
123
+ RewriteCond %{HTTP_USER_AGENT} "^Wget" [NC,OR]
124
+ RewriteCond %{HTTP_USER_AGENT} "^Widow" [NC,OR]
125
+ RewriteCond %{HTTP_USER_AGENT} "^WPScan" [NC,OR]
126
+ RewriteCond %{HTTP_USER_AGENT} "^WWW\-Mechanize" [NC,OR]
127
+ RewriteCond %{HTTP_USER_AGENT} "^WWWOFFLE" [NC,OR]
128
+ RewriteCond %{HTTP_USER_AGENT} "^Xaldon WebSpider" [NC,OR]
129
+ RewriteCond %{HTTP_USER_AGENT} "^Yandex" [NC,OR]
130
+ RewriteCond %{HTTP_USER_AGENT} "^Zeus" [NC,OR]
131
+ RewriteCond %{HTTP_USER_AGENT} "^zmeu" [NC,OR]
132
+ RewriteCond %{HTTP_USER_AGENT} "360Spider" [NC,OR]
133
+ RewriteCond %{HTTP_USER_AGENT} "AhrefsBot" [NC,OR]
134
+ RewriteCond %{HTTP_USER_AGENT} "CazoodleBot" [NC,OR]
135
+ RewriteCond %{HTTP_USER_AGENT} "discobot" [NC,OR]
136
+ RewriteCond %{HTTP_USER_AGENT} "EasouSpider" [NC,OR]
137
+ RewriteCond %{HTTP_USER_AGENT} "ecxi" [NC,OR]
138
+ RewriteCond %{HTTP_USER_AGENT} "GT\:\:WWW" [NC,OR]
139
+ RewriteCond %{HTTP_USER_AGENT} "heritrix" [NC,OR]
140
+ RewriteCond %{HTTP_USER_AGENT} "HTTP\:\:Lite" [NC,OR]
141
+ RewriteCond %{HTTP_USER_AGENT} "HTTrack" [NC,OR]
142
+ RewriteCond %{HTTP_USER_AGENT} "ia_archiver" [NC,OR]
143
+ RewriteCond %{HTTP_USER_AGENT} "id\-search" [NC,OR]
144
+ RewriteCond %{HTTP_USER_AGENT} "IDBot" [NC,OR]
145
+ RewriteCond %{HTTP_USER_AGENT} "Indy Library" [NC,OR]
146
+ RewriteCond %{HTTP_USER_AGENT} "IRLbot" [NC,OR]
147
+ RewriteCond %{HTTP_USER_AGENT} "ISC Systems iRc Search 2\.1" [NC,OR]
148
+ RewriteCond %{HTTP_USER_AGENT} "LinksCrawler" [NC,OR]
149
+ RewriteCond %{HTTP_USER_AGENT} "LinksManager\.com_bot" [NC,OR]
150
+ RewriteCond %{HTTP_USER_AGENT} "linkwalker" [NC,OR]
151
+ RewriteCond %{HTTP_USER_AGENT} "lwp\-trivial" [NC,OR]
152
+ RewriteCond %{HTTP_USER_AGENT} "MFC_Tear_Sample" [NC,OR]
153
+ RewriteCond %{HTTP_USER_AGENT} "Microsoft URL Control" [NC,OR]
154
+ RewriteCond %{HTTP_USER_AGENT} "Missigua Locator" [NC,OR]
155
+ RewriteCond %{HTTP_USER_AGENT} "MJ12bot" [NC,OR]
156
+ RewriteCond %{HTTP_USER_AGENT} "panscient\.com" [NC,OR]
157
+ RewriteCond %{HTTP_USER_AGENT} "PECL\:\:HTTP" [NC,OR]
158
+ RewriteCond %{HTTP_USER_AGENT} "PHPCrawl" [NC,OR]
159
+ RewriteCond %{HTTP_USER_AGENT} "PleaseCrawl" [NC,OR]
160
+ RewriteCond %{HTTP_USER_AGENT} "SBIder" [NC,OR]
161
+ RewriteCond %{HTTP_USER_AGENT} "SearchmetricsBot" [NC,OR]
162
+ RewriteCond %{HTTP_USER_AGENT} "SeznamBot" [NC,OR]
163
+ RewriteCond %{HTTP_USER_AGENT} "Snoopy" [NC,OR]
164
+ RewriteCond %{HTTP_USER_AGENT} "Sogou" [NC,OR]
165
+ RewriteCond %{HTTP_USER_AGENT} "Steeler" [NC,OR]
166
+ RewriteCond %{HTTP_USER_AGENT} "URI\:\:Fetch" [NC,OR]
167
+ RewriteCond %{HTTP_USER_AGENT} "urllib" [NC,OR]
168
+ RewriteCond %{HTTP_USER_AGENT} "Web Sucker" [NC,OR]
169
+ RewriteCond %{HTTP_USER_AGENT} "webalta" [NC,OR]
170
+ RewriteCond %{HTTP_USER_AGENT} "WebCollage" [NC,OR]
171
+ RewriteCond %{HTTP_USER_AGENT} "Wells Search II" [NC,OR]
172
+ RewriteCond %{HTTP_USER_AGENT} "WEP Search" [NC,OR]
173
+ RewriteCond %{HTTP_USER_AGENT} "XoviBot" [NC,OR]
174
+ RewriteCond %{HTTP_USER_AGENT} "YisouSpider" [NC,OR]
175
+ RewriteCond %{HTTP_USER_AGENT} "zermelo" [NC,OR]
176
+ RewriteCond %{HTTP_USER_AGENT} "ZyBorg" [NC,OR]
177
+ # End Abuse Agent Blocking
178
+ # Start Abuse HTTP Referrer Blocking
179
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?semalt\.com" [NC,OR]
180
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?kambasoft\.com" [NC,OR]
181
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?savetubevideo\.com" [NC]
182
+ # End Abuse HTTP Referrer Blocking
183
+ RewriteRule ^.* - [F,L]
184
+ # End HackRepair.com Blacklist, http://pastebin.com/u/hackrepair
core/modules/ban-users/lists/hackrepair-litespeed.inc ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Start HackRepair.com Blacklist
2
+ RewriteEngine on
3
+ # Start Abuse Agent Blocking
4
+ RewriteCond %{HTTP_USER_AGENT} "^Mozilla.*Indy" [NC,OR]
5
+ RewriteCond %{HTTP_USER_AGENT} "^Mozilla.*NEWT" [NC,OR]
6
+ RewriteCond %{HTTP_USER_AGENT} "^$" [NC,OR]
7
+ RewriteCond %{HTTP_USER_AGENT} "^Maxthon$" [NC,OR]
8
+ RewriteCond %{HTTP_USER_AGENT} "^SeaMonkey$" [NC,OR]
9
+ RewriteCond %{HTTP_USER_AGENT} "^Acunetix" [NC,OR]
10
+ RewriteCond %{HTTP_USER_AGENT} "^binlar" [NC,OR]
11
+ RewriteCond %{HTTP_USER_AGENT} "^BlackWidow" [NC,OR]
12
+ RewriteCond %{HTTP_USER_AGENT} "^Bolt 0" [NC,OR]
13
+ RewriteCond %{HTTP_USER_AGENT} "^BOT for JCE" [NC,OR]
14
+ RewriteCond %{HTTP_USER_AGENT} "^Bot mailto\:craftbot@yahoo\.com" [NC,OR]
15
+ RewriteCond %{HTTP_USER_AGENT} "^casper" [NC,OR]
16
+ RewriteCond %{HTTP_USER_AGENT} "^checkprivacy" [NC,OR]
17
+ RewriteCond %{HTTP_USER_AGENT} "^ChinaClaw" [NC,OR]
18
+ RewriteCond %{HTTP_USER_AGENT} "^clshttp" [NC,OR]
19
+ RewriteCond %{HTTP_USER_AGENT} "^cmsworldmap" [NC,OR]
20
+ RewriteCond %{HTTP_USER_AGENT} "^comodo" [NC,OR]
21
+ RewriteCond %{HTTP_USER_AGENT} "^Custo" [NC,OR]
22
+ RewriteCond %{HTTP_USER_AGENT} "^Default Browser 0" [NC,OR]
23
+ RewriteCond %{HTTP_USER_AGENT} "^diavol" [NC,OR]
24
+ RewriteCond %{HTTP_USER_AGENT} "^DIIbot" [NC,OR]
25
+ RewriteCond %{HTTP_USER_AGENT} "^DISCo" [NC,OR]
26
+ RewriteCond %{HTTP_USER_AGENT} "^dotbot" [NC,OR]
27
+ RewriteCond %{HTTP_USER_AGENT} "^Download Demon" [NC,OR]
28
+ RewriteCond %{HTTP_USER_AGENT} "^eCatch" [NC,OR]
29
+ RewriteCond %{HTTP_USER_AGENT} "^EirGrabber" [NC,OR]
30
+ RewriteCond %{HTTP_USER_AGENT} "^EmailCollector" [NC,OR]
31
+ RewriteCond %{HTTP_USER_AGENT} "^EmailSiphon" [NC,OR]
32
+ RewriteCond %{HTTP_USER_AGENT} "^EmailWolf" [NC,OR]
33
+ RewriteCond %{HTTP_USER_AGENT} "^Express WebPictures" [NC,OR]
34
+ RewriteCond %{HTTP_USER_AGENT} "^extract" [NC,OR]
35
+ RewriteCond %{HTTP_USER_AGENT} "^ExtractorPro" [NC,OR]
36
+ RewriteCond %{HTTP_USER_AGENT} "^EyeNetIE" [NC,OR]
37
+ RewriteCond %{HTTP_USER_AGENT} "^feedfinder" [NC,OR]
38
+ RewriteCond %{HTTP_USER_AGENT} "^FHscan" [NC,OR]
39
+ RewriteCond %{HTTP_USER_AGENT} "^FlashGet" [NC,OR]
40
+ RewriteCond %{HTTP_USER_AGENT} "^flicky" [NC,OR]
41
+ RewriteCond %{HTTP_USER_AGENT} "^g00g1e" [NC,OR]
42
+ RewriteCond %{HTTP_USER_AGENT} "^GetRight" [NC,OR]
43
+ RewriteCond %{HTTP_USER_AGENT} "^GetWeb\!" [NC,OR]
44
+ RewriteCond %{HTTP_USER_AGENT} "^Go\!Zilla" [NC,OR]
45
+ RewriteCond %{HTTP_USER_AGENT} "^Go\-Ahead\-Got\-It" [NC,OR]
46
+ RewriteCond %{HTTP_USER_AGENT} "^grab" [NC,OR]
47
+ RewriteCond %{HTTP_USER_AGENT} "^GrabNet" [NC,OR]
48
+ RewriteCond %{HTTP_USER_AGENT} "^Grafula" [NC,OR]
49
+ RewriteCond %{HTTP_USER_AGENT} "^harvest" [NC,OR]
50
+ RewriteCond %{HTTP_USER_AGENT} "^HMView" [NC,OR]
51
+ RewriteCond %{HTTP_USER_AGENT} "^ia_archiver" [NC,OR]
52
+ RewriteCond %{HTTP_USER_AGENT} "^Image Stripper" [NC,OR]
53
+ RewriteCond %{HTTP_USER_AGENT} "^Image Sucker" [NC,OR]
54
+ RewriteCond %{HTTP_USER_AGENT} "^InterGET" [NC,OR]
55
+ RewriteCond %{HTTP_USER_AGENT} "^Internet Ninja" [NC,OR]
56
+ RewriteCond %{HTTP_USER_AGENT} "^InternetSeer\.com" [NC,OR]
57
+ RewriteCond %{HTTP_USER_AGENT} "^jakarta" [NC,OR]
58
+ RewriteCond %{HTTP_USER_AGENT} "^Java" [NC,OR]
59
+ RewriteCond %{HTTP_USER_AGENT} "^JetCar" [NC,OR]
60
+ RewriteCond %{HTTP_USER_AGENT} "^JOC Web Spider" [NC,OR]
61
+ RewriteCond %{HTTP_USER_AGENT} "^kanagawa" [NC,OR]
62
+ RewriteCond %{HTTP_USER_AGENT} "^kmccrew" [NC,OR]
63
+ RewriteCond %{HTTP_USER_AGENT} "^larbin" [NC,OR]
64
+ RewriteCond %{HTTP_USER_AGENT} "^LeechFTP" [NC,OR]
65
+ RewriteCond %{HTTP_USER_AGENT} "^libwww" [NC,OR]
66
+ RewriteCond %{HTTP_USER_AGENT} "^Mass Downloader" [NC,OR]
67
+ RewriteCond %{HTTP_USER_AGENT} "^microsoft\.url" [NC,OR]
68
+ RewriteCond %{HTTP_USER_AGENT} "^MIDown tool" [NC,OR]
69
+ RewriteCond %{HTTP_USER_AGENT} "^miner" [NC,OR]
70
+ RewriteCond %{HTTP_USER_AGENT} "^Mister PiX" [NC,OR]
71
+ RewriteCond %{HTTP_USER_AGENT} "^MSFrontPage" [NC,OR]
72
+ RewriteCond %{HTTP_USER_AGENT} "^Navroad" [NC,OR]
73
+ RewriteCond %{HTTP_USER_AGENT} "^NearSite" [NC,OR]
74
+ RewriteCond %{HTTP_USER_AGENT} "^Net Vampire" [NC,OR]
75
+ RewriteCond %{HTTP_USER_AGENT} "^NetAnts" [NC,OR]
76
+ RewriteCond %{HTTP_USER_AGENT} "^NetSpider" [NC,OR]
77
+ RewriteCond %{HTTP_USER_AGENT} "^NetZIP" [NC,OR]
78
+ RewriteCond %{HTTP_USER_AGENT} "^nutch" [NC,OR]
79
+ RewriteCond %{HTTP_USER_AGENT} "^Octopus" [NC,OR]
80
+ RewriteCond %{HTTP_USER_AGENT} "^Offline Explorer" [NC,OR]
81
+ RewriteCond %{HTTP_USER_AGENT} "^Offline Navigator" [NC,OR]
82
+ RewriteCond %{HTTP_USER_AGENT} "^PageGrabber" [NC,OR]
83
+ RewriteCond %{HTTP_USER_AGENT} "^Papa Foto" [NC,OR]
84
+ RewriteCond %{HTTP_USER_AGENT} "^pavuk" [NC,OR]
85
+ RewriteCond %{HTTP_USER_AGENT} "^pcBrowser" [NC,OR]
86
+ RewriteCond %{HTTP_USER_AGENT} "^PeoplePal" [NC,OR]
87
+ RewriteCond %{HTTP_USER_AGENT} "^planetwork" [NC,OR]
88
+ RewriteCond %{HTTP_USER_AGENT} "^psbot" [NC,OR]
89
+ RewriteCond %{HTTP_USER_AGENT} "^purebot" [NC,OR]
90
+ RewriteCond %{HTTP_USER_AGENT} "^pycurl" [NC,OR]
91
+ RewriteCond %{HTTP_USER_AGENT} "^RealDownload" [NC,OR]
92
+ RewriteCond %{HTTP_USER_AGENT} "^ReGet" [NC,OR]
93
+ RewriteCond %{HTTP_USER_AGENT} "^Rippers 0" [NC,OR]
94
+ RewriteCond %{HTTP_USER_AGENT} "^sitecheck\.internetseer\.com" [NC,OR]
95
+ RewriteCond %{HTTP_USER_AGENT} "^SiteSnagger" [NC,OR]
96
+ RewriteCond %{HTTP_USER_AGENT} "^skygrid" [NC,OR]
97
+ RewriteCond %{HTTP_USER_AGENT} "^SmartDownload" [NC,OR]
98
+ RewriteCond %{HTTP_USER_AGENT} "^sucker" [NC,OR]
99
+ RewriteCond %{HTTP_USER_AGENT} "^SuperBot" [NC,OR]
100
+ RewriteCond %{HTTP_USER_AGENT} "^SuperHTTP" [NC,OR]
101
+ RewriteCond %{HTTP_USER_AGENT} "^Surfbot" [NC,OR]
102
+ RewriteCond %{HTTP_USER_AGENT} "^tAkeOut" [NC,OR]
103
+ RewriteCond %{HTTP_USER_AGENT} "^Teleport Pro" [NC,OR]
104
+ RewriteCond %{HTTP_USER_AGENT} "^Toata dragostea mea pentru diavola" [NC,OR]
105
+ RewriteCond %{HTTP_USER_AGENT} "^turnit" [NC,OR]
106
+ RewriteCond %{HTTP_USER_AGENT} "^vikspider" [NC,OR]
107
+ RewriteCond %{HTTP_USER_AGENT} "^VoidEYE" [NC,OR]
108
+ RewriteCond %{HTTP_USER_AGENT} "^Web Image Collector" [NC,OR]
109
+ RewriteCond %{HTTP_USER_AGENT} "^Web Sucker" [NC,OR]
110
+ RewriteCond %{HTTP_USER_AGENT} "^WebAuto" [NC,OR]
111
+ RewriteCond %{HTTP_USER_AGENT} "^WebBandit" [NC,OR]
112
+ RewriteCond %{HTTP_USER_AGENT} "^WebCopier" [NC,OR]
113
+ RewriteCond %{HTTP_USER_AGENT} "^WebFetch" [NC,OR]
114
+ RewriteCond %{HTTP_USER_AGENT} "^WebGo IS" [NC,OR]
115
+ RewriteCond %{HTTP_USER_AGENT} "^WebLeacher" [NC,OR]
116
+ RewriteCond %{HTTP_USER_AGENT} "^WebReaper" [NC,OR]
117
+ RewriteCond %{HTTP_USER_AGENT} "^WebSauger" [NC,OR]
118
+ RewriteCond %{HTTP_USER_AGENT} "^Website eXtractor" [NC,OR]
119
+ RewriteCond %{HTTP_USER_AGENT} "^Website Quester" [NC,OR]
120
+ RewriteCond %{HTTP_USER_AGENT} "^WebStripper" [NC,OR]
121
+ RewriteCond %{HTTP_USER_AGENT} "^WebWhacker" [NC,OR]
122
+ RewriteCond %{HTTP_USER_AGENT} "^WebZIP" [NC,OR]
123
+ RewriteCond %{HTTP_USER_AGENT} "^Wget" [NC,OR]
124
+ RewriteCond %{HTTP_USER_AGENT} "^Widow" [NC,OR]
125
+ RewriteCond %{HTTP_USER_AGENT} "^WPScan" [NC,OR]
126
+ RewriteCond %{HTTP_USER_AGENT} "^WWW\-Mechanize" [NC,OR]
127
+ RewriteCond %{HTTP_USER_AGENT} "^WWWOFFLE" [NC,OR]
128
+ RewriteCond %{HTTP_USER_AGENT} "^Xaldon WebSpider" [NC,OR]
129
+ RewriteCond %{HTTP_USER_AGENT} "^Yandex" [NC,OR]
130
+ RewriteCond %{HTTP_USER_AGENT} "^Zeus" [NC,OR]
131
+ RewriteCond %{HTTP_USER_AGENT} "^zmeu" [NC,OR]
132
+ RewriteCond %{HTTP_USER_AGENT} "360Spider" [NC,OR]
133
+ RewriteCond %{HTTP_USER_AGENT} "AhrefsBot" [NC,OR]
134
+ RewriteCond %{HTTP_USER_AGENT} "CazoodleBot" [NC,OR]
135
+ RewriteCond %{HTTP_USER_AGENT} "discobot" [NC,OR]
136
+ RewriteCond %{HTTP_USER_AGENT} "EasouSpider" [NC,OR]
137
+ RewriteCond %{HTTP_USER_AGENT} "ecxi" [NC,OR]
138
+ RewriteCond %{HTTP_USER_AGENT} "GT\:\:WWW" [NC,OR]
139
+ RewriteCond %{HTTP_USER_AGENT} "heritrix" [NC,OR]
140
+ RewriteCond %{HTTP_USER_AGENT} "HTTP\:\:Lite" [NC,OR]
141
+ RewriteCond %{HTTP_USER_AGENT} "HTTrack" [NC,OR]
142
+ RewriteCond %{HTTP_USER_AGENT} "ia_archiver" [NC,OR]
143
+ RewriteCond %{HTTP_USER_AGENT} "id\-search" [NC,OR]
144
+ RewriteCond %{HTTP_USER_AGENT} "IDBot" [NC,OR]
145
+ RewriteCond %{HTTP_USER_AGENT} "Indy Library" [NC,OR]
146
+ RewriteCond %{HTTP_USER_AGENT} "IRLbot" [NC,OR]
147
+ RewriteCond %{HTTP_USER_AGENT} "ISC Systems iRc Search 2\.1" [NC,OR]
148
+ RewriteCond %{HTTP_USER_AGENT} "LinksCrawler" [NC,OR]
149
+ RewriteCond %{HTTP_USER_AGENT} "LinksManager\.com_bot" [NC,OR]
150
+ RewriteCond %{HTTP_USER_AGENT} "linkwalker" [NC,OR]
151
+ RewriteCond %{HTTP_USER_AGENT} "lwp\-trivial" [NC,OR]
152
+ RewriteCond %{HTTP_USER_AGENT} "MFC_Tear_Sample" [NC,OR]
153
+ RewriteCond %{HTTP_USER_AGENT} "Microsoft URL Control" [NC,OR]
154
+ RewriteCond %{HTTP_USER_AGENT} "Missigua Locator" [NC,OR]
155
+ RewriteCond %{HTTP_USER_AGENT} "MJ12bot" [NC,OR]
156
+ RewriteCond %{HTTP_USER_AGENT} "panscient\.com" [NC,OR]
157
+ RewriteCond %{HTTP_USER_AGENT} "PECL\:\:HTTP" [NC,OR]
158
+ RewriteCond %{HTTP_USER_AGENT} "PHPCrawl" [NC,OR]
159
+ RewriteCond %{HTTP_USER_AGENT} "PleaseCrawl" [NC,OR]
160
+ RewriteCond %{HTTP_USER_AGENT} "SBIder" [NC,OR]
161
+ RewriteCond %{HTTP_USER_AGENT} "SearchmetricsBot" [NC,OR]
162
+ RewriteCond %{HTTP_USER_AGENT} "SeznamBot" [NC,OR]
163
+ RewriteCond %{HTTP_USER_AGENT} "Snoopy" [NC,OR]
164
+ RewriteCond %{HTTP_USER_AGENT} "Sogou" [NC,OR]
165
+ RewriteCond %{HTTP_USER_AGENT} "Steeler" [NC,OR]
166
+ RewriteCond %{HTTP_USER_AGENT} "URI\:\:Fetch" [NC,OR]
167
+ RewriteCond %{HTTP_USER_AGENT} "urllib" [NC,OR]
168
+ RewriteCond %{HTTP_USER_AGENT} "Web Sucker" [NC,OR]
169
+ RewriteCond %{HTTP_USER_AGENT} "webalta" [NC,OR]
170
+ RewriteCond %{HTTP_USER_AGENT} "WebCollage" [NC,OR]
171
+ RewriteCond %{HTTP_USER_AGENT} "Wells Search II" [NC,OR]
172
+ RewriteCond %{HTTP_USER_AGENT} "WEP Search" [NC,OR]
173
+ RewriteCond %{HTTP_USER_AGENT} "XoviBot" [NC,OR]
174
+ RewriteCond %{HTTP_USER_AGENT} "YisouSpider" [NC,OR]
175
+ RewriteCond %{HTTP_USER_AGENT} "zermelo" [NC,OR]
176
+ RewriteCond %{HTTP_USER_AGENT} "ZyBorg" [NC,OR]
177
+ # End Abuse Agent Blocking
178
+ # Start Abuse HTTP Referrer Blocking
179
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?semalt\.com" [NC,OR]
180
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?kambasoft\.com" [NC,OR]
181
+ RewriteCond %{HTTP_REFERER} "^https?://(?:[^/]+\.)?savetubevideo\.com" [NC]
182
+ # End Abuse HTTP Referrer Blocking
183
+ RewriteRule ^.* - [F,L]
184
+ # End HackRepair.com Blacklist, http://pastebin.com/u/hackrepair
core/modules/ban-users/lists/hackrepair-nginx.inc ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Start HackRepair.com Blacklist
2
+ # Start Abuse Agent Blocking
3
+ if ($http_user_agent ~* "^Mozilla.*Indy"){return 403;}
4
+ if ($http_user_agent ~* "^Mozilla.*NEWT"){return 403;}
5
+ if ($http_user_agent ~* "^$"){return 403;}
6
+ if ($http_user_agent ~* "^Maxthon$"){return 403;}
7
+ if ($http_user_agent ~* "^SeaMonkey$"){return 403;}
8
+ if ($http_user_agent ~* "^Acunetix"){return 403;}
9
+ if ($http_user_agent ~* "^binlar"){return 403;}
10
+ if ($http_user_agent ~* "^BlackWidow"){return 403;}
11
+ if ($http_user_agent ~* "^Bolt 0"){return 403;}
12
+ if ($http_user_agent ~* "^BOT for JCE"){return 403;}
13
+ if ($http_user_agent ~* "^Bot mailto\:craftbot@yahoo\.com"){return 403;}
14
+ if ($http_user_agent ~* "^casper"){return 403;}
15
+ if ($http_user_agent ~* "^checkprivacy"){return 403;}
16
+ if ($http_user_agent ~* "^ChinaClaw"){return 403;}
17
+ if ($http_user_agent ~* "^clshttp"){return 403;}
18
+ if ($http_user_agent ~* "^cmsworldmap"){return 403;}
19
+ if ($http_user_agent ~* "^comodo"){return 403;}
20
+ if ($http_user_agent ~* "^Custo"){return 403;}
21
+ if ($http_user_agent ~* "^Default Browser 0"){return 403;}
22
+ if ($http_user_agent ~* "^diavol"){return 403;}
23
+ if ($http_user_agent ~* "^DIIbot"){return 403;}
24
+ if ($http_user_agent ~* "^DISCo"){return 403;}
25
+ if ($http_user_agent ~* "^dotbot"){return 403;}
26
+ if ($http_user_agent ~* "^Download Demon"){return 403;}
27
+ if ($http_user_agent ~* "^eCatch"){return 403;}
28
+ if ($http_user_agent ~* "^EirGrabber"){return 403;}
29
+ if ($http_user_agent ~* "^EmailCollector"){return 403;}
30
+ if ($http_user_agent ~* "^EmailSiphon"){return 403;}
31
+ if ($http_user_agent ~* "^EmailWolf"){return 403;}
32
+ if ($http_user_agent ~* "^Express WebPictures"){return 403;}
33
+ if ($http_user_agent ~* "^extract"){return 403;}
34
+ if ($http_user_agent ~* "^ExtractorPro"){return 403;}
35
+ if ($http_user_agent ~* "^EyeNetIE"){return 403;}
36
+ if ($http_user_agent ~* "^feedfinder"){return 403;}
37
+ if ($http_user_agent ~* "^FHscan"){return 403;}
38
+ if ($http_user_agent ~* "^FlashGet"){return 403;}
39
+ if ($http_user_agent ~* "^flicky"){return 403;}
40
+ if ($http_user_agent ~* "^g00g1e"){return 403;}
41
+ if ($http_user_agent ~* "^GetRight"){return 403;}
42
+ if ($http_user_agent ~* "^GetWeb\!"){return 403;}
43
+ if ($http_user_agent ~* "^Go\!Zilla"){return 403;}
44
+ if ($http_user_agent ~* "^Go\-Ahead\-Got\-It"){return 403;}
45
+ if ($http_user_agent ~* "^grab"){return 403;}
46
+ if ($http_user_agent ~* "^GrabNet"){return 403;}
47
+ if ($http_user_agent ~* "^Grafula"){return 403;}
48
+ if ($http_user_agent ~* "^harvest"){return 403;}
49
+ if ($http_user_agent ~* "^HMView"){return 403;}
50
+ if ($http_user_agent ~* "^ia_archiver"){return 403;}
51
+ if ($http_user_agent ~* "^Image Stripper"){return 403;}
52
+ if ($http_user_agent ~* "^Image Sucker"){return 403;}
53
+ if ($http_user_agent ~* "^InterGET"){return 403;}
54
+ if ($http_user_agent ~* "^Internet Ninja"){return 403;}
55
+ if ($http_user_agent ~* "^InternetSeer\.com"){return 403;}
56
+ if ($http_user_agent ~* "^jakarta"){return 403;}
57
+ if ($http_user_agent ~* "^Java"){return 403;}
58
+ if ($http_user_agent ~* "^JetCar"){return 403;}
59
+ if ($http_user_agent ~* "^JOC Web Spider"){return 403;}
60
+ if ($http_user_agent ~* "^kanagawa"){return 403;}
61
+ if ($http_user_agent ~* "^kmccrew"){return 403;}
62
+ if ($http_user_agent ~* "^larbin"){return 403;}
63
+ if ($http_user_agent ~* "^LeechFTP"){return 403;}
64
+ if ($http_user_agent ~* "^libwww"){return 403;}
65
+ if ($http_user_agent ~* "^Mass Downloader"){return 403;}
66
+ if ($http_user_agent ~* "^microsoft\.url"){return 403;}
67
+ if ($http_user_agent ~* "^MIDown tool"){return 403;}
68
+ if ($http_user_agent ~* "^miner"){return 403;}
69
+ if ($http_user_agent ~* "^Mister PiX"){return 403;}
70
+ if ($http_user_agent ~* "^MSFrontPage"){return 403;}
71
+ if ($http_user_agent ~* "^Navroad"){return 403;}
72
+ if ($http_user_agent ~* "^NearSite"){return 403;}
73
+ if ($http_user_agent ~* "^Net Vampire"){return 403;}
74
+ if ($http_user_agent ~* "^NetAnts"){return 403;}
75
+ if ($http_user_agent ~* "^NetSpider"){return 403;}
76
+ if ($http_user_agent ~* "^NetZIP"){return 403;}
77
+ if ($http_user_agent ~* "^nutch"){return 403;}
78
+ if ($http_user_agent ~* "^Octopus"){return 403;}
79
+ if ($http_user_agent ~* "^Offline Explorer"){return 403;}
80
+ if ($http_user_agent ~* "^Offline Navigator"){return 403;}
81
+ if ($http_user_agent ~* "^PageGrabber"){return 403;}
82
+ if ($http_user_agent ~* "^Papa Foto"){return 403;}
83
+ if ($http_user_agent ~* "^pavuk"){return 403;}
84
+ if ($http_user_agent ~* "^pcBrowser"){return 403;}
85
+ if ($http_user_agent ~* "^PeoplePal"){return 403;}
86
+ if ($http_user_agent ~* "^planetwork"){return 403;}
87
+ if ($http_user_agent ~* "^psbot"){return 403;}
88
+ if ($http_user_agent ~* "^purebot"){return 403;}
89
+ if ($http_user_agent ~* "^pycurl"){return 403;}
90
+ if ($http_user_agent ~* "^RealDownload"){return 403;}
91
+ if ($http_user_agent ~* "^ReGet"){return 403;}
92
+ if ($http_user_agent ~* "^Rippers 0"){return 403;}
93
+ if ($http_user_agent ~* "^sitecheck\.internetseer\.com"){return 403;}
94
+ if ($http_user_agent ~* "^SiteSnagger"){return 403;}
95
+ if ($http_user_agent ~* "^skygrid"){return 403;}
96
+ if ($http_user_agent ~* "^SmartDownload"){return 403;}
97
+ if ($http_user_agent ~* "^sucker"){return 403;}
98
+ if ($http_user_agent ~* "^SuperBot"){return 403;}
99
+ if ($http_user_agent ~* "^SuperHTTP"){return 403;}
100
+ if ($http_user_agent ~* "^Surfbot"){return 403;}
101
+ if ($http_user_agent ~* "^tAkeOut"){return 403;}
102
+ if ($http_user_agent ~* "^Teleport Pro"){return 403;}
103
+ if ($http_user_agent ~* "^Toata dragostea mea pentru diavola"){return 403;}
104
+ if ($http_user_agent ~* "^turnit"){return 403;}
105
+ if ($http_user_agent ~* "^vikspider"){return 403;}
106
+ if ($http_user_agent ~* "^VoidEYE"){return 403;}
107
+ if ($http_user_agent ~* "^Web Image Collector"){return 403;}
108
+ if ($http_user_agent ~* "^Web Sucker"){return 403;}
109
+ if ($http_user_agent ~* "^WebAuto"){return 403;}
110
+ if ($http_user_agent ~* "^WebBandit"){return 403;}
111
+ if ($http_user_agent ~* "^WebCopier"){return 403;}
112
+ if ($http_user_agent ~* "^WebFetch"){return 403;}
113
+ if ($http_user_agent ~* "^WebGo IS"){return 403;}
114
+ if ($http_user_agent ~* "^WebLeacher"){return 403;}
115
+ if ($http_user_agent ~* "^WebReaper"){return 403;}
116
+ if ($http_user_agent ~* "^WebSauger"){return 403;}
117
+ if ($http_user_agent ~* "^Website eXtractor"){return 403;}
118
+ if ($http_user_agent ~* "^Website Quester"){return 403;}
119
+ if ($http_user_agent ~* "^WebStripper"){return 403;}
120
+ if ($http_user_agent ~* "^WebWhacker"){return 403;}
121
+ if ($http_user_agent ~* "^WebZIP"){return 403;}
122
+ if ($http_user_agent ~* "^Wget"){return 403;}
123
+ if ($http_user_agent ~* "^Widow"){return 403;}
124
+ if ($http_user_agent ~* "^WPScan"){return 403;}
125
+ if ($http_user_agent ~* "^WWW\-Mechanize"){return 403;}
126
+ if ($http_user_agent ~* "^WWWOFFLE"){return 403;}
127
+ if ($http_user_agent ~* "^Xaldon WebSpider"){return 403;}
128
+ if ($http_user_agent ~* "^Yandex"){return 403;}
129
+ if ($http_user_agent ~* "^Zeus"){return 403;}
130
+ if ($http_user_agent ~* "^zmeu"){return 403;}
131
+ if ($http_user_agent ~* "360Spider"){return 403;}
132
+ if ($http_user_agent ~* "AhrefsBot"){return 403;}
133
+ if ($http_user_agent ~* "CazoodleBot"){return 403;}
134
+ if ($http_user_agent ~* "discobot"){return 403;}
135
+ if ($http_user_agent ~* "EasouSpider"){return 403;}
136
+ if ($http_user_agent ~* "ecxi"){return 403;}
137
+ if ($http_user_agent ~* "GT\:\:WWW"){return 403;}
138
+ if ($http_user_agent ~* "heritrix"){return 403;}
139
+ if ($http_user_agent ~* "HTTP\:\:Lite"){return 403;}
140
+ if ($http_user_agent ~* "HTTrack"){return 403;}
141
+ if ($http_user_agent ~* "ia_archiver"){return 403;}
142
+ if ($http_user_agent ~* "id\-search"){return 403;}
143
+ if ($http_user_agent ~* "IDBot"){return 403;}
144
+ if ($http_user_agent ~* "Indy Library"){return 403;}
145
+ if ($http_user_agent ~* "IRLbot"){return 403;}
146
+ if ($http_user_agent ~* "ISC Systems iRc Search 2\.1"){return 403;}
147
+ if ($http_user_agent ~* "LinksCrawler"){return 403;}
148
+ if ($http_user_agent ~* "LinksManager\.com_bot"){return 403;}
149
+ if ($http_user_agent ~* "linkwalker"){return 403;}
150
+ if ($http_user_agent ~* "lwp\-trivial"){return 403;}
151
+ if ($http_user_agent ~* "MFC_Tear_Sample"){return 403;}
152
+ if ($http_user_agent ~* "Microsoft URL Control"){return 403;}
153
+ if ($http_user_agent ~* "Missigua Locator"){return 403;}
154
+ if ($http_user_agent ~* "MJ12bot"){return 403;}
155
+ if ($http_user_agent ~* "panscient\.com"){return 403;}
156
+ if ($http_user_agent ~* "PECL\:\:HTTP"){return 403;}
157
+ if ($http_user_agent ~* "PHPCrawl"){return 403;}
158
+ if ($http_user_agent ~* "PleaseCrawl"){return 403;}
159
+ if ($http_user_agent ~* "SBIder"){return 403;}
160
+ if ($http_user_agent ~* "SearchmetricsBot"){return 403;}
161
+ if ($http_user_agent ~* "SeznamBot"){return 403;}
162
+ if ($http_user_agent ~* "Snoopy"){return 403;}
163
+ if ($http_user_agent ~* "Sogou"){return 403;}
164
+ if ($http_user_agent ~* "Steeler"){return 403;}
165
+ if ($http_user_agent ~* "URI\:\:Fetch"){return 403;}
166
+ if ($http_user_agent ~* "urllib"){return 403;}
167
+ if ($http_user_agent ~* "Web Sucker"){return 403;}
168
+ if ($http_user_agent ~* "webalta"){return 403;}
169
+ if ($http_user_agent ~* "WebCollage"){return 403;}
170
+ if ($http_user_agent ~* "Wells Search II"){return 403;}
171
+ if ($http_user_agent ~* "WEP Search"){return 403;}
172
+ if ($http_user_agent ~* "XoviBot"){return 403;}
173
+ if ($http_user_agent ~* "YisouSpider"){return 403;}
174
+ if ($http_user_agent ~* "zermelo"){return 403;}
175
+ if ($http_user_agent ~* "ZyBorg"){return 403;}
176
+ # End Abuse Agent Blocking
177
+ # Start Abuse HTTP Referrer Blocking
178
+ if ($http_referer ~* "^https?://(?:[^/]+\.)?semalt\.com"){return 403;}
179
+ if ($http_referer ~* "^https?://(?:[^/]+\.)?kambasoft\.com"){return 403;}
180
+ if ($http_referer ~* "^https?://(?:[^/]+\.)?savetubevideo\.com"){return 403;}
181
+ # End Abuse HTTP Referrer Blocking
182
+ # End HackRepair.com Blacklist, http://pastebin.com/u/hackrepair
core/modules/ban-users/lists/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ban-users/setup.php ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Ban_Users_Setup' ) ) {
4
+
5
+ class ITSEC_Ban_Users_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'default' => false,
17
+ 'host_list' => array(),
18
+ 'agent_list' => array(),
19
+ );
20
+
21
+ if ( isset( $itsec_setup_action ) ) {
22
+
23
+ switch ( $itsec_setup_action ) {
24
+
25
+ case 'activate':
26
+ $this->execute_activate();
27
+ break;
28
+ case 'upgrade':
29
+ $this->execute_upgrade();
30
+ break;
31
+ case 'deactivate':
32
+ $this->execute_deactivate();
33
+ break;
34
+ case 'uninstall':
35
+ $this->execute_uninstall();
36
+ break;
37
+
38
+ }
39
+
40
+ } else {
41
+ wp_die( 'error' );
42
+ }
43
+
44
+ }
45
+
46
+ /**
47
+ * Execute module activation.
48
+ *
49
+ * @since 4.0
50
+ *
51
+ * @return void
52
+ */
53
+ public function execute_activate() {
54
+
55
+ $options = get_site_option( 'itsec_ban_users' );
56
+
57
+ if ( $options === false ) {
58
+
59
+ add_site_option( 'itsec_ban_users', $this->defaults );
60
+
61
+ }
62
+
63
+ add_site_option( 'itsec_rewrites_changed', true );
64
+
65
+ }
66
+
67
+ /**
68
+ * Execute module deactivation
69
+ *
70
+ * @return void
71
+ */
72
+ public function execute_deactivate() {
73
+
74
+ }
75
+
76
+ /**
77
+ * Execute module uninstall
78
+ *
79
+ * @return void
80
+ */
81
+ public function execute_uninstall() {
82
+
83
+ $this->execute_deactivate();
84
+
85
+ delete_site_option( 'itsec_ban_users' );
86
+
87
+ }
88
+
89
+ /**
90
+ * Execute module upgrade
91
+ *
92
+ * @return void
93
+ */
94
+ public function execute_upgrade() {
95
+
96
+ global $itsec_old_version;
97
+
98
+ if ( $itsec_old_version < 4000 ) {
99
+
100
+ global $itsec_bwps_options;
101
+
102
+ $current_options = get_site_option( 'itsec_ban_users' );
103
+
104
+ if ( $current_options === false ) {
105
+ $current_options = $this->defaults;
106
+ }
107
+
108
+ $current_options['enabled'] = isset( $itsec_bwps_options['bu_enabled'] ) && $itsec_bwps_options['bu_enabled'] == 1 ? true : false;
109
+ $current_options['default'] = isset( $itsec_bwps_options['bu_blacklist'] ) && $itsec_bwps_options['bu_blacklist'] == 1 ? true : false;
110
+
111
+ if ( isset( $itsec_bwps_options['bu_banlist'] ) && ! is_array( $itsec_bwps_options['bu_banlist'] ) && strlen( $itsec_bwps_options['bu_banlist'] ) > 1 ) {
112
+
113
+ $raw_hosts = explode( PHP_EOL, $itsec_bwps_options['bu_banlist'] );
114
+
115
+ foreach ( $raw_hosts as $host ) {
116
+
117
+ if ( strlen( $host ) > 1 ) {
118
+ $current_options['host_list'][] = $host;
119
+ }
120
+
121
+ }
122
+
123
+ }
124
+
125
+ if ( isset( $itsec_bwps_options['bu_banagent'] ) && ! is_array( $itsec_bwps_options['bu_banagent'] ) && strlen( $itsec_bwps_options['bu_banagent'] ) > 1 ) {
126
+
127
+ $current_options['agent_list'] = explode( PHP_EOL, $itsec_bwps_options['bu_banagent'] );
128
+
129
+ $raw_agents = explode( PHP_EOL, $itsec_bwps_options['bu_banagent'] );
130
+
131
+ foreach ( $raw_agents as $agent ) {
132
+
133
+ if ( strlen( $agent ) > 1 ) {
134
+ $current_options['agent_list'][] = $agent;
135
+ }
136
+
137
+ }
138
+
139
+ }
140
+
141
+ update_site_option( 'itsec_ban_users', $current_options );
142
+ add_site_option( 'itsec_rewrites_changed', true );
143
+
144
+ }
145
+
146
+ if ( $itsec_old_version < 4027 ) {
147
+
148
+ add_site_option( 'itsec_rewrites_changed', true );
149
+
150
+ }
151
+
152
+ }
153
+
154
+ }
155
+
156
+ }
157
+
158
+ new ITSEC_Ban_Users_Setup();
core/modules/brute-force/class-itsec-brute-force-admin.php ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Brute_Force_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $module_path;
9
+
10
+ function run( $core ) {
11
+
12
+ $this->core = $core;
13
+ $this->settings = get_site_option( 'itsec_brute_force' );
14
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
15
+
16
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); //enqueue scripts for admin page
17
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'itsec_add_admin_meta_boxes' ) ); //add meta boxes to admin page
18
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init' ) ); //initialize admin area
19
+
20
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'itsec_add_dashboard_status' ) ); //add information for plugin status
21
+ add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) ); //adds logs metaboxes
22
+ add_filter( 'itsec_one_click_settings', array( $this, 'itsec_one_click_settings' ) );
23
+ add_filter( 'itsec_tracking_vars', array( $this, 'itsec_tracking_vars' ) );
24
+
25
+ //manually save options on multisite
26
+ if ( is_multisite() ) {
27
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
28
+ }
29
+
30
+ }
31
+
32
+ /**
33
+ * Add Away mode Javascript
34
+ *
35
+ * @return void
36
+ */
37
+ public function admin_enqueue_scripts() {
38
+
39
+ global $itsec_globals;
40
+
41
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
42
+
43
+ wp_enqueue_script( 'itsec_brute_force_js', $this->module_path . 'js/admin-brute-force.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
44
+
45
+ }
46
+
47
+ }
48
+
49
+ /**
50
+ * Add meta boxes to primary options pages
51
+ *
52
+ * @return void
53
+ */
54
+ public function itsec_add_admin_meta_boxes() {
55
+
56
+ $id = 'brute_force_options';
57
+ $title = __( 'Brute Force Protection', 'better-wp-security' );
58
+
59
+ add_meta_box(
60
+ $id,
61
+ $title,
62
+ array( $this, 'metabox_brute_force_settings' ),
63
+ 'security_page_toplevel_page_itsec_settings',
64
+ 'advanced',
65
+ 'core'
66
+ );
67
+
68
+ $this->core->add_toc_item(
69
+ array(
70
+ 'id' => $id,
71
+ 'title' => $title,
72
+ )
73
+ );
74
+
75
+ }
76
+
77
+ /**
78
+ * Sets the status in the plugin dashboard
79
+ *
80
+ * @since 4.0
81
+ *
82
+ * @param array $statuses array of statuses
83
+ *
84
+ * @return array array of statuses
85
+ */
86
+ public function itsec_add_dashboard_status( $statuses ) {
87
+
88
+ $ipcheck = get_site_option( 'itsec_ipcheck' );
89
+ $api_ban = false;
90
+
91
+ if ( class_exists( 'ITSEC_IPCheck_Admin' ) && isset( $ipcheck['api_key'] ) && isset( $ipcheck['api_s'] ) && isset( $ipcheck['api_ban'] ) && $ipcheck['api_ban'] === true ) {
92
+ $api_ban = true;
93
+ }
94
+
95
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true && $api_ban === true ) {
96
+
97
+ $status_array = 'safe-high';
98
+ $status = array( 'text' => __( 'Your login area is protected from brute force attacks.', 'better-wp-security' ), 'link' => '#itsec_brute_force_settings', );
99
+
100
+ } elseif ( ( ( ! isset( $this->settings['enabled'] ) || $this->settings['enabled'] === false ) && $api_ban === true ) || ( ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) && $api_ban === false ) ) {
101
+
102
+ $status_array = 'medium';
103
+ $status = array( 'text' => __( 'Your login area is partially protected from brute force attacks. We recommend you use both network and local blocking for full security.', 'better-wp-security' ), 'link' => '#itsec_brute_force_settings', );
104
+
105
+ } else {
106
+
107
+ $status_array = 'high';
108
+ $status = array( 'text' => __( 'Your login area is not protected from brute force attacks.', 'better-wp-security' ), 'link' => '#itsec_brute_force_settings', );
109
+
110
+ }
111
+
112
+ array_push( $statuses[$status_array], $status );
113
+
114
+ return $statuses;
115
+
116
+ }
117
+
118
+ /**
119
+ * Execute admin initializations
120
+ *
121
+ * @return void
122
+ */
123
+ public function itsec_admin_init() {
124
+
125
+ //Add Settings sections
126
+ add_settings_section(
127
+ 'brute_force-enabled',
128
+ __( 'Enable Brute Force Protection', 'better-wp-security' ),
129
+ '__return_empty_string',
130
+ 'security_page_toplevel_page_itsec_settings'
131
+ );
132
+
133
+ add_settings_section(
134
+ 'brute_force-settings',
135
+ __( 'Brute Force Protection Settings', 'better-wp-security' ),
136
+ '__return_empty_string',
137
+ 'security_page_toplevel_page_itsec_settings'
138
+ );
139
+
140
+ //Brute Force Protection Fields
141
+ add_settings_field(
142
+ 'itsec_brute_force[enabled]',
143
+ __( 'Enable local brute force protection', 'better-wp-security' ),
144
+ array( $this, 'settings_field_enabled' ),
145
+ 'security_page_toplevel_page_itsec_settings',
146
+ 'brute_force-enabled'
147
+ );
148
+
149
+ add_settings_field(
150
+ 'itsec_brute_force[max_attempts_host]',
151
+ __( 'Max Login Attempts Per Host', 'better-wp-security' ),
152
+ array( $this, 'settings_field_max_attempts_host' ),
153
+ 'security_page_toplevel_page_itsec_settings',
154
+ 'brute_force-settings'
155
+ );
156
+
157
+ add_settings_field(
158
+ 'itsec_brute_force[max_attempts_user]',
159
+ __( 'Max Login Attempts Per User', 'better-wp-security' ),
160
+ array( $this, 'settings_field_max_attempts_user' ),
161
+ 'security_page_toplevel_page_itsec_settings',
162
+ 'brute_force-settings'
163
+ );
164
+
165
+ add_settings_field(
166
+ 'itsec_brute_force[check_period]',
167
+ __( 'Minutes to Remember Bad Login (check period)', 'better-wp-security' ),
168
+ array( $this, 'settings_field_check_period' ),
169
+ 'security_page_toplevel_page_itsec_settings',
170
+ 'brute_force-settings'
171
+ );
172
+
173
+ add_settings_field(
174
+ 'itsec_brute_force[auto_ban_admin]',
175
+ __( 'Automatically ban "admin" user', 'better-wp-security' ),
176
+ array( $this, 'settings_field_auto_ban_admin' ),
177
+ 'security_page_toplevel_page_itsec_settings',
178
+ 'brute_force-settings'
179
+ );
180
+
181
+ //Register the settings field for the entire module
182
+ register_setting(
183
+ 'security_page_toplevel_page_itsec_settings',
184
+ 'itsec_brute_force',
185
+ array( $this, 'sanitize_module_input' )
186
+ );
187
+
188
+ }
189
+
190
+ /**
191
+ * Array of metaboxes for the logs screen
192
+ *
193
+ * @since 4.0
194
+ *
195
+ * @param object $displays metabox array
196
+ *
197
+ * @return array metabox array
198
+ */
199
+ public function itsec_logger_displays( $displays ) {
200
+
201
+ //Don't attempt to display logs if brute force isn't enabled
202
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
203
+
204
+ $displays[] = array(
205
+ 'module' => 'brute_force',
206
+ 'title' => __( 'Invalid Login Attempts', 'better-wp-security' ),
207
+ 'callback' => array( $this, 'logs_metabox_content' ),
208
+ );
209
+
210
+ }
211
+
212
+ return $displays;
213
+
214
+ }
215
+
216
+ /**
217
+ * Register one-click settings
218
+ *
219
+ * @since 4.0
220
+ *
221
+ * @param array $one_click_settings array of one-click settings
222
+ *
223
+ * @return array array of one-click settings
224
+ */
225
+ public function itsec_one_click_settings( $one_click_settings ) {
226
+
227
+ $one_click_settings['itsec_brute_force'][] = array(
228
+ 'option' => 'enabled',
229
+ 'value' => 1,
230
+ );
231
+
232
+ return $one_click_settings;
233
+
234
+ }
235
+
236
+ /**
237
+ * Adds fields that will be tracked for Google Analytics
238
+ *
239
+ * @since 4.0
240
+ *
241
+ * @param array $vars tracking vars
242
+ *
243
+ * @return array tracking vars
244
+ */
245
+ public function itsec_tracking_vars( $vars ) {
246
+
247
+ $vars['itsec_brute_force'] = array(
248
+ 'enabled' => '0:b',
249
+ );
250
+
251
+ return $vars;
252
+
253
+ }
254
+
255
+ /**
256
+ * Render the settings metabox
257
+ *
258
+ * @since 4.0
259
+ *
260
+ * @return void
261
+ */
262
+ public function logs_metabox_content() {
263
+
264
+ if ( ! class_exists( 'ITSEC_Brute_Force_Log' ) ) {
265
+ require( dirname( __FILE__ ) . '/class-itsec-brute-force-log.php' );
266
+ }
267
+
268
+ $log_display = new ITSEC_Brute_Force_Log();
269
+ $log_display->prepare_items();
270
+ $log_display->display();
271
+
272
+ }
273
+
274
+ /**
275
+ * Render the settings metabox
276
+ *
277
+ * @since 4.0
278
+ *
279
+ * @return void
280
+ */
281
+ public function metabox_brute_force_settings() {
282
+
283
+ global $itsec_lockout;
284
+
285
+ echo '<div id="itsec_brute_force_settings">';
286
+
287
+ echo '<p>' . __( 'If one had unlimited time and wanted to try an unlimited number of password combinations to get into your site they eventually would, right? This method of attack, known as a brute force attack, is something that WordPress is acutely susceptible by default as the system doesn\'t care how many attempts a user makes to login. It will always let you try again. Enabling login limits will ban the host user from attempting to login again after the specified bad login threshold has been reached.', 'better-wp-security' ) . '</p>';
288
+
289
+ echo '<p><strong>' . __( 'Network vs Local Brute Force Protection', 'better-wp-security' ) . '</strong><br />';
290
+ echo __( 'Local brute force protection looks only at attempts to access your site and bans users per the lockout rules specified locally. Network brute force protection takes this a step further by banning users who have tried to break into other sites from breaking into yours. The network protection will automatically report the IP addresses of failed login attempts to iThemes and will block them for a length of time necessary to protect your site based on the number of other sites that have seen a similar attack.', 'better-wp-security' ) . '</p>';
291
+
292
+ if ( class_exists( 'ITSEC_IPCheck_Admin' ) ) {
293
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'ipcheck-settings-brute-force', false ); //show ipcheck settings if the module is present
294
+ }
295
+
296
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'brute_force-enabled', false );
297
+ echo '<div class="itsec_brute_force_lockout_information">' . $itsec_lockout->get_lockout_description() . '</div>';
298
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'brute_force-settings', false );
299
+
300
+ echo '<p>' . PHP_EOL;
301
+
302
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
303
+
304
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
305
+
306
+ echo '</p>' . PHP_EOL;
307
+
308
+ echo '</div>';
309
+
310
+ }
311
+
312
+ /**
313
+ * Sanitize and validate input
314
+ *
315
+ * @param Array $input array of input fields
316
+ *
317
+ * @return Array Sanitized array
318
+ */
319
+ public function sanitize_module_input( $input ) {
320
+
321
+ //process brute force settings
322
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
323
+ $input['auto_ban_admin'] = ( isset( $input['auto_ban_admin'] ) && intval( $input['auto_ban_admin'] == 1 ) ? true : false );
324
+ $input['max_attempts_host'] = isset( $input['max_attempts_host'] ) ? absint( $input['max_attempts_host'] ) : 5;
325
+ $input['max_attempts_user'] = isset( $input['max_attempts_user'] ) ? absint( $input['max_attempts_user'] ) : 10;
326
+ $input['check_period'] = isset( $input['check_period'] ) ? absint( $input['check_period'] ) : 5;
327
+
328
+ if ( is_multisite() ) {
329
+
330
+ $this->core->show_network_admin_notice( false );
331
+
332
+ $this->settings = $input;
333
+
334
+ }
335
+
336
+ return $input;
337
+
338
+ }
339
+
340
+ /**
341
+ * Prepare and save options in network settings
342
+ *
343
+ * @return void
344
+ */
345
+ public function save_network_options() {
346
+
347
+ if ( isset( $_POST['itsec_brute_force'] ) ) {
348
+
349
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
350
+ die( __( 'Security error!', 'better-wp-security' ) );
351
+ }
352
+
353
+ update_site_option( 'itsec_brute_force', $_POST['itsec_brute_force'] ); //we must manually save network options
354
+
355
+ }
356
+
357
+ }
358
+
359
+ /**
360
+ * echos Auto ban admin login Field
361
+ *
362
+ * @since 4.3
363
+ *
364
+ * @return void
365
+ */
366
+ public function settings_field_auto_ban_admin() {
367
+
368
+ if ( isset( $this->settings['auto_ban_admin'] ) && $this->settings['auto_ban_admin'] === true ) {
369
+
370
+ $auto_ban_admin = 1;
371
+
372
+ } else {
373
+
374
+ $auto_ban_admin = 0;
375
+
376
+ }
377
+
378
+ if ( ! username_exists( 'admin' ) ) {
379
+
380
+ echo '<input type="checkbox" id="itsec_brute_force_auto_ban_admin" name="itsec_brute_force[auto_ban_admin]" value="1" ' . checked( 1, $auto_ban_admin, false ) . '/>';
381
+ echo '<label for="itsec_brute_force_auto_ban_admin"> ' . __( 'Immediately ban a host that attempts to login using the "admin" username.', 'better-wp-security' ) . '</label>';
382
+
383
+ } else {
384
+
385
+ echo '<p>' . __( 'You are still using an account with the username "admin." Please rename it before using this feature', 'better-wp-security' ) . '</p>';
386
+
387
+ }
388
+
389
+ }
390
+
391
+ /**
392
+ * echos Check Period Field
393
+ *
394
+ * @since 4.0
395
+ *
396
+ * @return void
397
+ */
398
+ public function settings_field_check_period() {
399
+
400
+ if ( isset( $this->settings['check_period'] ) ) {
401
+
402
+ $check_period = absint( $this->settings['check_period'] );
403
+
404
+ } else {
405
+
406
+ $check_period = 5;
407
+
408
+ }
409
+
410
+ echo '<input class="small-text" name="itsec_brute_force[check_period]" id="itsec_brute_force_check_period" value="' . $check_period . '" type="text"> ';
411
+ echo '<label for="itsec_brute_force_check_period"> ' . __( 'Minutes', 'better-wp-security' ) . '</label>';
412
+ echo '<p class="description"> ' . __( 'The number of minutes in which bad logins should be remembered.', 'better-wp-security' ) . '</p>';
413
+
414
+ }
415
+
416
+ /**
417
+ * echos Enable Brute Force Field
418
+ *
419
+ * @since 4.0
420
+ *
421
+ * @return void
422
+ */
423
+ public function settings_field_enabled() {
424
+
425
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
426
+
427
+ $enabled = 1;
428
+
429
+ } else {
430
+
431
+ $enabled = 0;
432
+
433
+ }
434
+
435
+ echo '<input type="checkbox" id="itsec_brute_force_enabled" name="itsec_brute_force[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
436
+ echo '<label for="itsec_brute_force_enabled"> ' . __( 'Enable local brute force protection.', 'better-wp-security' ) . '</label>';
437
+
438
+ }
439
+
440
+ /**
441
+ * echos Max Attempts per host Field
442
+ *
443
+ * @since 4.0
444
+ *
445
+ * @return void
446
+ */
447
+ public function settings_field_max_attempts_host() {
448
+
449
+ if ( isset( $this->settings['max_attempts_host'] ) ) {
450
+
451
+ $max_attempts_host = absint( $this->settings['max_attempts_host'] );
452
+
453
+ } else {
454
+
455
+ $max_attempts_host = 5;
456
+
457
+ }
458
+
459
+ echo '<input class="small-text" name="itsec_brute_force[max_attempts_host]" id="itsec_brute_force_max_attempts_host" value="' . $max_attempts_host . '" type="text"> ';
460
+ echo '<label for="itsec_brute_force_max_attempts_host"> ' . __( 'Attempts', 'better-wp-security' ) . '</label>';
461
+ echo '<p class="description"> ' . __( 'The number of login attempts a user has before their host or computer is locked out of the system. Set to 0 to record bad login attempts without locking out the host.', 'better-wp-security' ) . '</p>';
462
+
463
+ }
464
+
465
+ /**
466
+ * echos Max Attempts per user Field
467
+ *
468
+ * @since 4.0
469
+ *
470
+ * @return void
471
+ */
472
+ public function settings_field_max_attempts_user() {
473
+
474
+ if ( isset( $this->settings['max_attempts_user'] ) ) {
475
+
476
+ $max_attempts_user = absint( $this->settings['max_attempts_user'] );
477
+
478
+ } else {
479
+
480
+ $max_attempts_user = 10;
481
+
482
+ }
483
+
484
+ echo '<input class="small-text" name="itsec_brute_force[max_attempts_user]" id="itsec_brute_force_max_attempts_user" value="' . $max_attempts_user . '" type="text"> ';
485
+ echo '<label for="itsec_brute_force_max_attempts_user"> ' . __( 'Attempts', 'better-wp-security' ) . '</label>';
486
+ echo '<p class="description"> ' . __( 'The number of login attempts a user has before their username is locked out of the system. Note that this is different from hosts in case an attacker is using multiple computers. In addition, if they are using your login name you could be locked out yourself. Set to zero to log bad login attempts per user without ever locking the user out (this is not recommended)', 'better-wp-security' ) . '</p>';
487
+
488
+ }
489
+
490
+ }
core/modules/brute-force/class-itsec-brute-force-log.php ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Log tables for Authentication Module
5
+ *
6
+ * @package iThemes-Security
7
+ * @subpackage Authentication
8
+ * @since 4.0
9
+ */
10
+ final class ITSEC_Brute_Force_Log extends ITSEC_WP_List_Table {
11
+
12
+ function __construct() {
13
+
14
+ parent::__construct(
15
+ array(
16
+ 'singular' => 'itsec_brute_force_log_item',
17
+ 'plural' => 'itsec_brute_force_log_items',
18
+ 'ajax' => true
19
+ )
20
+ );
21
+
22
+ }
23
+
24
+ /**
25
+ * Define time column
26
+ *
27
+ * @param array $item array of row data
28
+ *
29
+ * @return string formatted output
30
+ *
31
+ **/
32
+ function column_time( $item ) {
33
+
34
+ return $item['time'];
35
+
36
+ }
37
+
38
+ /**
39
+ * Define host column
40
+ *
41
+ * @param array $item array of row data
42
+ *
43
+ * @return string formatted output
44
+ *
45
+ **/
46
+ function column_host( $item ) {
47
+
48
+ $r = array();
49
+ if ( ! is_array( $item['host'] ) ) {
50
+ $item['host'] = array( $item['host'] );
51
+ }
52
+ foreach ( $item['host'] as $host ) {
53
+ $r[] = '<a href="http://ip-adress.com/ip_tracer/' . filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) . '" target="_blank">' . filter_var( $host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 ) . '</a>';
54
+ }
55
+ $return = implode( '<br />', $r );
56
+
57
+ return $return;
58
+
59
+ }
60
+
61
+ /**
62
+ * Define added column
63
+ *
64
+ * @param array $item array of row data
65
+ *
66
+ * @return string formatted output
67
+ *
68
+ **/
69
+ function column_user( $item ) {
70
+
71
+ return $item['user'];
72
+
73
+ }
74
+
75
+ /**
76
+ * Define Columns
77
+ *
78
+ * @return array array of column titles
79
+ */
80
+ public function get_columns() {
81
+
82
+ return array(
83
+ 'time' => __( 'Time', 'better-wp-security' ),
84
+ 'host' => __( 'Host', 'better-wp-security' ),
85
+ 'user' => __( 'Username', 'better-wp-security' ),
86
+ );
87
+
88
+ }
89
+
90
+ /**
91
+ * Define Sortable Columns
92
+ *
93
+ * @return array of column titles that can be sorted
94
+ */
95
+ public function get_sortable_columns() {
96
+
97
+ $order = ( empty( $_GET['order'] ) ) ? false : true;
98
+
99
+ $sortable_columns = array(
100
+ 'time' => array( 'time', $order ),
101
+ 'host' => array( 'host', $order ),
102
+ 'user' => array( 'user', $order ),
103
+ );
104
+
105
+ return $sortable_columns;
106
+
107
+ }
108
+
109
+ /**
110
+ * Prepare data for table
111
+ *
112
+ * @return void
113
+ */
114
+ public function prepare_items() {
115
+
116
+ global $itsec_logger;
117
+
118
+ $columns = $this->get_columns();
119
+ $hidden = array();
120
+ $sortable = $this->get_sortable_columns();
121
+ $this->_column_headers = array( $columns, $hidden, $sortable );
122
+
123
+ $items = $itsec_logger->get_events( 'brute_force' );
124
+
125
+ $table_data = array();
126
+
127
+ $count = 0;
128
+
129
+ foreach ( $items as $item ) { //loop through and group 404s
130
+
131
+ $table_data[$count]['time'] = sanitize_text_field( $item['log_date'] );
132
+ $table_data[$count]['host'] = sanitize_text_field( $item['log_host'] );
133
+ $table_data[$count]['user'] = sanitize_text_field( $item['log_username'] );
134
+
135
+ $count ++;
136
+
137
+ }
138
+
139
+ usort( $table_data, array( $this, 'sortrows' ) );
140
+
141
+ $per_page = 20; //20 items per page
142
+ $current_page = $this->get_pagenum();
143
+ $total_items = count( $table_data );
144
+
145
+ $table_data = array_slice( $table_data, ( ( $current_page - 1 ) * $per_page ), $per_page );
146
+
147
+ $this->items = $table_data;
148
+
149
+ $this->set_pagination_args(
150
+ array(
151
+ 'total_items' => $total_items,
152
+ 'per_page' => $per_page,
153
+ 'total_pages' => ceil( $total_items / $per_page )
154
+ )
155
+ );
156
+
157
+ }
158
+
159
+ /**
160
+ * Sorts rows by count in descending order
161
+ *
162
+ * @param array $a first array to compare
163
+ * @param array $b second array to compare
164
+ *
165
+ * @return int comparison result
166
+ */
167
+ function sortrows( $a, $b ) {
168
+
169
+ // If no sort, default to count
170
+ $orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'time';
171
+
172
+ // If no order, default to desc
173
+ $order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
174
+
175
+ // Determine sort order
176
+ $result = strcmp( $a[$orderby], $b[$orderby] );
177
+
178
+ // Send final sort direction to usort
179
+ return ( $order === 'asc' ) ? $result : - $result;
180
+
181
+ }
182
+
183
+ }
core/modules/brute-force/class-itsec-brute-force.php ADDED
@@ -0,0 +1,270 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Brute_Force {
4
+
5
+ private
6
+ $settings,
7
+ $username;
8
+
9
+ function run() {
10
+
11
+ $this->settings = get_site_option( 'itsec_brute_force' );
12
+ $this->username = null;
13
+
14
+ add_action( 'wp_login', array( $this, 'wp_login' ), 10, 2 );
15
+ add_action( 'wp_login_failed', array( $this, 'wp_login_failed' ), 1, 1 );
16
+
17
+ add_filter( 'authenticate', array( $this, 'authenticate' ), 10, 3 );
18
+ add_filter( 'itsec_lockout_modules', array( $this, 'itsec_lockout_modules' ) );
19
+ add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
20
+ add_filter( 'xmlrpc_login_error', array( $this, 'xmlrpc_login_error' ), 10, 2 );
21
+ add_filter( 'jetpack_get_default_modules', array( $this, 'jetpack_get_default_modules' ) ); //disable jetpack protect via Geoge Stephanis
22
+
23
+ }
24
+
25
+ /**
26
+ * Sends to lockout class when login form isn't completely filled out and process xml_rpc username
27
+ *
28
+ * @since 4.0
29
+ *
30
+ * @param object $user user or wordpress error
31
+ * @param string $username username attempted
32
+ * @param string $password password attempted
33
+ *
34
+ * @return user object or WordPress error
35
+ */
36
+ public function authenticate( $user, $username = '', $password = '' ) {
37
+
38
+ global $itsec_lockout, $itsec_logger;
39
+
40
+ //Look for the "admin" user name and ban it if it is set to auto-ban
41
+ if ( isset( $this->settings['auto_ban_admin'] ) && $this->settings['auto_ban_admin'] === true && trim( sanitize_text_field( $username ) ) == 'admin' ) {
42
+
43
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), sanitize_text_field( $username ) );
44
+
45
+ $itsec_lockout->do_lockout( 'brute_force_admin_user', sanitize_text_field( $username ) );
46
+
47
+ }
48
+
49
+ //Execute brute force if username or password are empty
50
+ if ( isset( $_POST['wp-submit'] ) && ( empty( $username ) || empty( $password ) ) ) {
51
+
52
+ $user_id = username_exists( sanitize_text_field( $username ) );
53
+
54
+ if ( $user_id === false || $user_id === null ) {
55
+
56
+ $itsec_lockout->check_lockout( false, $username );
57
+
58
+ } else {
59
+
60
+ $itsec_lockout->check_lockout( $user_id );
61
+
62
+ }
63
+
64
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), sanitize_text_field( $username ), intval( $user_id ) );
65
+
66
+ $itsec_lockout->do_lockout( 'brute_force', sanitize_text_field( $username ) );
67
+
68
+ }
69
+
70
+ //Set username for xml_rpc block
71
+ if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST === true ) {
72
+
73
+ $this->username = trim( sanitize_text_field( $username ) );
74
+
75
+ }
76
+
77
+ return $user;
78
+
79
+ }
80
+
81
+ /**
82
+ * Register Brute Force for lockout
83
+ *
84
+ * @since 4.0
85
+ *
86
+ * @param array $lockout_modules array of lockout modules
87
+ *
88
+ * @return array array of lockout modules
89
+ */
90
+ public function itsec_lockout_modules( $lockout_modules ) {
91
+
92
+ if ( $this->settings['enabled'] === true ) {
93
+
94
+ $lockout_modules['brute_force'] = array(
95
+ 'type' => 'brute_force',
96
+ 'reason' => __( 'too many bad login attempts', 'better-wp-security' ),
97
+ 'host' => $this->settings['max_attempts_host'],
98
+ 'user' => $this->settings['max_attempts_user'],
99
+ 'period' => $this->settings['check_period'],
100
+ );
101
+
102
+ $lockout_modules['brute_force_admin_user'] = array(
103
+ 'type' => 'brute_force',
104
+ 'reason' => __( 'user tried to login as "admin."', 'better-wp-security' ),
105
+ 'host' => 1,
106
+ 'user' => 1,
107
+ 'period' => $this->settings['check_period']
108
+ );
109
+
110
+ }
111
+
112
+ return $lockout_modules;
113
+
114
+ }
115
+
116
+ /**
117
+ * Register Brute Force for logger
118
+ *
119
+ * @since 4.0
120
+ *
121
+ * @param array $logger_modules array of logger modules
122
+ *
123
+ * @return array array of logger modules
124
+ */
125
+ public function itsec_logger_modules( $logger_modules ) {
126
+
127
+ if ( $this->settings['enabled'] === true ) {
128
+
129
+ $logger_modules['brute_force'] = array(
130
+ 'type' => 'brute_force',
131
+ 'function' => __( 'Invalid Login Attempt', 'better-wp-security' ),
132
+ );
133
+
134
+ }
135
+
136
+ return $logger_modules;
137
+
138
+ }
139
+
140
+ /**
141
+ * Disables the jetpack protect module
142
+ *
143
+ * Sent by George Stephanis
144
+ *
145
+ * @since 4.5
146
+ *
147
+ * @param array $modules array of Jetpack modules
148
+ *
149
+ * @return array array of Jetpack modules
150
+ */
151
+ public function jetpack_get_default_modules( $modules ) {
152
+
153
+ return array_diff( $modules, array( 'protect' ) );
154
+
155
+ }
156
+
157
+ /**
158
+ * Make sure user isn't already locked out even on successful form submission
159
+ *
160
+ * @since 4.0
161
+ *
162
+ * @param string $username the username attempted
163
+ * @param object wp_user the user
164
+ *
165
+ * @return void
166
+ */
167
+ public function wp_login( $username, $user = null ) {
168
+
169
+ global $itsec_lockout;
170
+
171
+ if ( ! $user === null ) {
172
+
173
+ $itsec_lockout->check_lockout( $user );
174
+
175
+ } elseif ( is_user_logged_in() ) {
176
+
177
+ $current_user = wp_get_current_user();
178
+
179
+ $itsec_lockout->check_lockout( $current_user->ID );
180
+
181
+ }
182
+
183
+ }
184
+
185
+ /**
186
+ * Sends to lockout class when username and password are filled out and wrong
187
+ *
188
+ * @since 4.0
189
+ *
190
+ * @param string $username the username attempted
191
+ *
192
+ * @return void
193
+ */
194
+ public function wp_login_failed( $username ) {
195
+
196
+ global $itsec_lockout, $itsec_logger;
197
+
198
+ if ( isset( $this->settings['auto_ban_admin'] ) && $this->settings['auto_ban_admin'] === true && trim( sanitize_text_field( $username ) ) == 'admin' ) {
199
+
200
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), sanitize_text_field( $username ) );
201
+
202
+ $itsec_lockout->do_lockout( 'brute_force_admin_user', sanitize_text_field( $username ) );
203
+
204
+ }
205
+
206
+ if ( isset( $_POST['log'] ) && $_POST['log'] != '' && isset( $_POST['pwd'] ) && $_POST['pwd'] != '' ) {
207
+
208
+ $user_id = username_exists( sanitize_text_field( $username ) );
209
+
210
+ if ( $user_id === false || $user_id === null ) {
211
+
212
+ $itsec_lockout->check_lockout( false, $username );
213
+
214
+ } else {
215
+
216
+ $itsec_lockout->check_lockout( $user_id );
217
+
218
+ };
219
+
220
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), sanitize_text_field( $username ), intval( $user_id ) );
221
+
222
+ $itsec_lockout->do_lockout( 'brute_force', sanitize_text_field( $username ) );
223
+
224
+ }
225
+
226
+ }
227
+
228
+ /**
229
+ * Execute brute force against xml_rpc login
230
+ *
231
+ * @Since 4.4
232
+ *
233
+ * @param mixed $error WordPress error
234
+ *
235
+ * @return mixed WordPress error
236
+ */
237
+ public function xmlrpc_login_error( $error ) {
238
+
239
+ global $itsec_lockout, $itsec_logger;
240
+
241
+ if ( isset( $this->settings['auto_ban_admin'] ) && $this->settings['auto_ban_admin'] === true && trim( sanitize_text_field( $this->username ) ) == 'admin' ) {
242
+
243
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), $this->username );
244
+
245
+ $itsec_lockout->do_lockout( 'brute_force_admin_user', $this->username );
246
+
247
+ } else {
248
+
249
+ $user_id = username_exists( $this->username );
250
+
251
+ if ( $user_id === false || $user_id === null ) {
252
+
253
+ $itsec_lockout->check_lockout( false, $this->username );
254
+
255
+ } else {
256
+
257
+ $itsec_lockout->check_lockout( $user_id );
258
+
259
+ };
260
+
261
+ $itsec_logger->log_event( 'brute_force', 5, array(), ITSEC_Lib::get_ip(), $this->username, intval( $user_id ) );
262
+
263
+ $itsec_lockout->do_lockout( 'brute_force', $this->username );
264
+
265
+ }
266
+
267
+ return $error;
268
+ }
269
+
270
+ }
core/modules/brute-force/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/brute-force/js/admin-brute-force.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_brute_force_enabled" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_brute_force_enabled" ).is( ':checked' ) ) {
6
+
7
+ jQuery( "#brute_force-settings, .itsec_brute_force_lockout_information" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#brute_force-settings, .itsec_brute_force_lockout_information" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ } );
core/modules/brute-force/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/brute-force/setup.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Brute_Force_Setup' ) ) {
4
+
5
+ class ITSEC_Brute_Force_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'max_attempts_host' => 5,
17
+ 'max_attempts_user' => 10,
18
+ 'check_period' => 5,
19
+ 'auto_ban_admin' => false,
20
+ );
21
+
22
+ if ( isset( $itsec_setup_action ) ) {
23
+
24
+ switch ( $itsec_setup_action ) {
25
+
26
+ case 'activate':
27
+ $this->execute_activate();
28
+ break;
29
+ case 'upgrade':
30
+ $this->execute_upgrade();
31
+ break;
32
+ case 'deactivate':
33
+ $this->execute_deactivate();
34
+ break;
35
+ case 'uninstall':
36
+ $this->execute_uninstall();
37
+ break;
38
+
39
+ }
40
+
41
+ } else {
42
+ wp_die( 'error' );
43
+ }
44
+
45
+ }
46
+
47
+ /**
48
+ * Execute module activation.
49
+ *
50
+ * @since 4.0
51
+ *
52
+ * @return void
53
+ */
54
+ public function execute_activate() {
55
+
56
+ $options = get_site_option( 'itsec_brute_force' );
57
+
58
+ if ( $options === false ) {
59
+
60
+ add_site_option( 'itsec_brute_force', $this->defaults );
61
+
62
+ }
63
+
64
+ }
65
+
66
+ /**
67
+ * Execute module deactivation
68
+ *
69
+ * @return void
70
+ */
71
+ public function execute_deactivate() {
72
+ }
73
+
74
+ /**
75
+ * Execute module uninstall
76
+ *
77
+ * @return void
78
+ */
79
+ public function execute_uninstall() {
80
+
81
+ $this->execute_deactivate();
82
+
83
+ delete_site_option( 'itsec_brute_force' );
84
+
85
+ }
86
+
87
+ /**
88
+ * Execute module upgrade
89
+ *
90
+ * @return void
91
+ */
92
+ public function execute_upgrade() {
93
+
94
+ global $itsec_old_version;
95
+
96
+ if ( $itsec_old_version < 4000 ) {
97
+
98
+ global $itsec_bwps_options;
99
+
100
+ $current_options = get_site_option( 'itsec_brute_force' );
101
+
102
+ if ( $current_options === false ) {
103
+ $current_options = $this->defaults;
104
+ }
105
+
106
+ $current_options['enabled'] = isset( $itsec_bwps_options['ll_enabled'] ) && $itsec_bwps_options['ll_enabled'] == 1 ? true : false;
107
+ $current_options['max_attempts_host'] = isset( $itsec_bwps_options['ll_maxattemptshost'] ) ? intval( $itsec_bwps_options['ll_maxattemptshost'] ) : 5;
108
+ $current_options['max_attempts_user'] = isset( $itsec_bwps_options['ll_maxattemptsuser'] ) ? intval( $itsec_bwps_options['ll_maxattemptsuser'] ) : 10;
109
+ $current_options['check_period'] = isset( $itsec_bwps_options['ll_checkinterval'] ) ? intval( $itsec_bwps_options['ll_checkinterval'] ) : 5;
110
+
111
+ update_site_option( 'itsec_brute_force', $current_options );
112
+
113
+ }
114
+
115
+ }
116
+
117
+ }
118
+
119
+ }
120
+
121
+ new ITSEC_Brute_Force_Setup();
core/modules/content-directory/class-itsec-content-directory-admin.php ADDED
@@ -0,0 +1,500 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Content_Directory_Admin {
4
+
5
+ private
6
+ $last_error,
7
+ $core,
8
+ $module_path,
9
+ $is_modified_by_it_security;
10
+
11
+ function run( $core ) {
12
+
13
+ $this->core = $core;
14
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
15
+
16
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
17
+
18
+ if ( ! empty( $_POST ) ) {
19
+ add_action( 'itsec_admin_init', array( $this, 'process_post_data' ) );
20
+ }
21
+
22
+ if ( ! $this->is_custom_directory() || $this->is_modified_by_it_security() ) {
23
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) );
24
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) );
25
+ }
26
+ }
27
+
28
+ protected function get_wp_config_define_warning() {
29
+ return __( 'Do not remove. Removing this line could break your site. Added by Security > Settings > Change Content Directory.', 'better-wp-security' );
30
+ }
31
+
32
+ protected function get_wp_config_define( $name, $value, $include_warning_comment = true ) {
33
+ $name = str_replace( "'", "\\'", $name );
34
+ $value = str_replace( "'", "\\'", $value );
35
+ $line = "define( '$name', '$value' );";
36
+
37
+ if ( $include_warning_comment ) {
38
+ $line .= ' // ' . $this->get_wp_config_define_warning();
39
+ }
40
+
41
+ return $line;
42
+ }
43
+
44
+ protected function get_wp_config_modification( $dir, $url, $include_warning_comment = true ) {
45
+ $modification = $this->get_wp_config_define( 'WP_CONTENT_DIR', $dir, $include_warning_comment ) . "\n";
46
+ $modification .= $this->get_wp_config_define( 'WP_CONTENT_URL', $url, $include_warning_comment );
47
+
48
+ return $modification;
49
+ }
50
+
51
+ protected function get_wp_config_define_expression( $include_warning_comment = true ) {
52
+ $expression = $this->get_wp_config_modification( 'WILDCARD', 'WILDCARD', $include_warning_comment );
53
+ $expression = preg_quote( $expression, '|' );
54
+ $expression = str_replace( ' ', '\s*', $expression );
55
+ $expression = str_replace( 'WILDCARD', "[^']+", $expression );
56
+ $expression = "|$expression|";
57
+
58
+ if ( $include_warning_comment ) {
59
+ $expression = str_replace( "\n", "\s*[\r\n]+\s*", $expression );
60
+ } else {
61
+ $expression = str_replace( "\n", "\s*", $expression );
62
+ }
63
+
64
+ return $expression;
65
+ }
66
+
67
+ /**
68
+ * Add meta boxes to primary options pages
69
+ *
70
+ * @param array $available_pages array of available page_hooks
71
+ */
72
+ public function add_admin_meta_boxes() {
73
+ add_meta_box(
74
+ 'content_directory_options',
75
+ __( 'Change Content Directory', 'better-wp-security' ),
76
+ array( $this, 'metabox_advanced_settings' ),
77
+ 'security_page_toplevel_page_itsec_advanced',
78
+ 'advanced',
79
+ 'core'
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Add Away mode Javascript
85
+ *
86
+ * @return void
87
+ */
88
+ public function admin_script() {
89
+
90
+ global $itsec_globals;
91
+
92
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_advanced' ) !== false ) {
93
+
94
+ wp_enqueue_script( 'itsec_content_directory_js', $this->module_path . 'js/admin-content_directory.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
95
+
96
+ }
97
+
98
+ }
99
+
100
+ /**
101
+ * Execute admin initializations
102
+ *
103
+ * @return void
104
+ */
105
+ public function process_post_data() {
106
+ if ( isset( $_POST['undo_change_content_directory'] ) ) {
107
+ $this->undo_change_content_directory();
108
+ } else if ( isset( $_POST['itsec_enable_content_dir'] ) && 'true' == $_POST['itsec_enable_content_dir'] && ! $this->is_custom_directory() ) {
109
+ $this->process_directory();
110
+ }
111
+ }
112
+
113
+ protected function undo_change_content_directory() {
114
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'itsec-undo-change-content-directory' ) ) {
115
+ $this->show_error( __( 'Unable to undo the change to the content directory due to a failed nonce verification.', 'better-wp-security' ) );
116
+ $this->show_network_admin_notice();
117
+
118
+ return;
119
+ }
120
+
121
+ $this->change_content_directory( 'wp-content' );
122
+ }
123
+
124
+ protected function show_redirect_message() {
125
+ if ( empty( $_GET['message'] ) ) {
126
+ return;
127
+ }
128
+
129
+ if ( false === strpos( $_GET['message'], '|' ) ) {
130
+ $name = $_GET['message'];
131
+ } else {
132
+ list( $name, $arg ) = explode( '|', $_GET['message'], 2 );
133
+ }
134
+
135
+ if ( 'undo-success' === $name ) {
136
+ echo '<div class="updated fade"><p><strong>' . __( 'The Content Directory change was successfully changed back to <code>wp-content</code>.', 'better-wp-security' ) . "</strong></p></div>\n";
137
+ } else if ( 'change-success' === $name ) {
138
+ echo '<div class="updated fade"><p><strong>' . sprintf( __( 'The Content Directory was successfully changed to <code>%s</code>.', 'better-wp-security' ), $arg ) . "</strong></p></div>\n";
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Render the settings metabox
144
+ *
145
+ * @return void
146
+ */
147
+ public function metabox_advanced_settings() {
148
+ global $itsec_globals;
149
+
150
+ $this->show_redirect_message();
151
+
152
+ if ( $this->is_custom_directory() || $this->is_modified_by_it_security() ) {
153
+ $dir_name = substr( WP_CONTENT_DIR, strrpos( WP_CONTENT_DIR, '/' ) + 1 );
154
+ echo '<p>' . sprintf( __( 'The <code>wp-content</code> directory is available at <code>%s</code>.', 'better-wp-security' ), $dir_name ) . '</p>';
155
+
156
+ if ( $this->is_modified_by_it_security() ) {
157
+
158
+ ?>
159
+ <form method="post" action="?page=toplevel_page_itsec_advanced&settings-updated=true" class="itsec-form">
160
+ <?php wp_nonce_field( 'itsec-undo-change-content-directory', 'wp_nonce' ); ?>
161
+
162
+ <div class="itsec-warning-message"><?php printf( __( '<span>IMPORTANT:</span> Ensure that you <a href="%s">create a database backup</a> before undoing the Content Directory change.', 'better-wp-security' ), admin_url( 'admin.php?page=toplevel_page_itsec_backups' ) ); ?></div>
163
+ <div class="itsec-warning-message"><?php _e( '<span>WARNING:</span> Undoing the Content Directory change when images and other content were added after the change <strong>will break your site</strong>. Only undo the Content Directory change if absolutely necessary.', 'better-wp-security' ); ?></div>
164
+
165
+ <p class="submit">
166
+ <input type="submit" class="button-primary" name="undo_change_content_directory" value="<?php _e( 'Undo Content Directory Change', 'better-wp-security' ); ?>" />
167
+ </p>
168
+ </form>
169
+ <?php
170
+
171
+ } else {
172
+ echo '<p>' . __( 'No further actions are available on this page.', 'better-wp-security' ) . '</p>';
173
+ }
174
+ } else {
175
+
176
+ echo '<p>' . __( 'By default, WordPress stores files for plugins, themes, and uploads in a directory called <code>wp-content</code>. Some older and less intelligent bots hard coded this directory in order to look for vulnerable files. Modern bots are intelligent enough to locate this folder programmatically, thus changing the Content Directory is no longer a recommended security step.', 'better-wp-security' ) . '</p>';
177
+ echo '<p>' . __( 'This tool provides an undo feature after changing the Content Directory. Since not all plugins, themes, or site contents function properly with a renamed Content Directory, please verify that the site is functioning correctly after the change. If any issues are encountered, the undo feature should be used to undo the change. Please note that the undo feature is only available when the changes added to the <code>wp-config.php</code> file for this feature are unmodified.', 'better-wp-security' ) . '</p>';
178
+ echo '<div class="itsec-warning-message">' . __( '<span>IMPORTANT:</span> Deactivating or uninstalling this plugin will not revert the changes made by this feature.', 'better-wp-security' ) . '</div>';
179
+ echo '<div class="itsec-warning-message">' . sprintf( __( '<span>IMPORTANT:</span> Ensure that you <a href="%s">create a database backup</a> before changing the Content Directory.', 'better-wp-security' ), admin_url( 'admin.php?page=toplevel_page_itsec_backups' ) ) . '</div>';
180
+ echo '<div class="itsec-warning-message">' . __( '<span>WARNING:</span> Changing the name of the Content Directory on a site that already has images and other content referencing it <strong>will break your site</strong>. For this reason, we highly recommend only changing the Content Directory on a fresh WordPress install.', 'better-wp-security' ) . '</div>';
181
+
182
+ if ( false !== apply_filters( 'itsec_filter_can_write_to_files', false ) ) {
183
+
184
+ ?>
185
+
186
+ <form method="post" action="?page=toplevel_page_itsec_advanced&settings-updated=true" class="itsec-form">
187
+
188
+ <?php wp_nonce_field( 'ITSEC_admin_save', 'wp_nonce' ); ?>
189
+
190
+ <table class="form-table">
191
+ <tr valign="top">
192
+ <th scope="row" class="settinglabel">
193
+ <label for="itsec_enable_content_dir"><?php _e( 'Enable Change Directory Name', 'better-wp-security' ); ?></label>
194
+ </th>
195
+ <td class="settingfield">
196
+ <input type="checkbox" id="itsec_enable_content_dir" name="itsec_enable_content_dir" value="true"/>
197
+
198
+ <p class="description"><?php _e( 'Check this box to enable Content Directory renaming.', 'better-wp-security' ); ?></p>
199
+ </td>
200
+ </tr>
201
+ <tr valign="top" id="content_directory_name_field">
202
+ <th scope="row" class="settinglabel">
203
+ <label for="itsec_content_name"><?php _e( 'Directory Name', 'better-wp-security' ); ?></label>
204
+ </th>
205
+ <td class="settingfield">
206
+ <input id="itsec_content_name" name="name" type="text" value="wp-content"/>
207
+
208
+ <p class="description"><?php _e( 'Enter a new directory name to replace "wp-content." You may need to log in again after performing this operation.', 'better-wp-security' ); ?></p>
209
+ </td>
210
+ </tr>
211
+ </table>
212
+ <p class="submit">
213
+ <input type="submit" class="button-primary" value="<?php _e( 'Change Content Directory', 'better-wp-security' ); ?>"/>
214
+ </p>
215
+ </form>
216
+
217
+ <?php
218
+
219
+ } else {
220
+ echo '<p>' . sprintf( __( 'You must allow this plugin to write to the wp-config.php file on the <a href="%s">Settings</a> page to use this feature.', 'better-wp-security' ), admin_url( 'admin.php?page=toplevel_page_itsec_settings' ) ) . '</p>';
221
+ }
222
+ }
223
+ }
224
+
225
+ public function process_directory() {
226
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'ITSEC_admin_save' ) ) {
227
+ $this->show_error( __( 'Unable to change the Content Directory due to a failed nonce verification.', 'better-wp-security' ) );
228
+ $this->show_network_admin_notice();
229
+
230
+ return;
231
+ }
232
+
233
+ if ( $this->is_custom_directory() ) {
234
+ $this->show_error( __( 'The <code>wp-content</code> directory has already been renamed. No Directory Name changes have been made.', 'better-wp-security' ) );
235
+ $this->show_network_admin_notice();
236
+
237
+ return;
238
+ }
239
+
240
+
241
+ $dir_name = sanitize_file_name( $_POST['name'] );
242
+
243
+ if ( empty( $dir_name ) ) {
244
+ $this->show_error( __( 'The Directory Name cannot be empty.', 'better-wp-security' ) );
245
+ $this->show_network_admin_notice();
246
+
247
+ return;
248
+ }
249
+
250
+ if ( 'wp-content' === $dir_name ) {
251
+ $this->show_error( __( 'You have not chosen a new name for wp-content. Nothing was saved.', 'better-wp-security' ) );
252
+ $this->show_network_admin_notice();
253
+
254
+ return;
255
+ }
256
+
257
+ if ( preg_match( '{^(?:/|\\|[a-z]:)}i', $dir_name ) ) {
258
+ $this->show_error( sprintf( __( 'The Directory Name cannot be an absolute path. Please supply a path that is relative to <code>ABSPATH</code> (<code>%s</code>).', 'better-wp-security' ), ABSPATH ) );
259
+ $this->show_network_admin_notice();
260
+
261
+ return;
262
+ }
263
+
264
+
265
+ $this->change_content_directory( $dir_name );
266
+ }
267
+
268
+ protected function change_content_directory( $dir_name ) {
269
+ if ( 'wp-content' == $dir_name ) {
270
+ $undo = true;
271
+ } else {
272
+ $undo = false;
273
+ }
274
+
275
+
276
+ if ( 0 === strpos( WP_CONTENT_DIR, ABSPATH ) ) {
277
+ $old_name = substr( WP_CONTENT_DIR, strlen( ABSPATH ) );
278
+ $new_name = $dir_name;
279
+ } else {
280
+ $old_name = WP_CONTENT_DIR;
281
+ $new_name = ABSPATH . $dir_name;
282
+ }
283
+
284
+ $old_dir = WP_CONTENT_DIR;
285
+ $new_dir = ABSPATH . $dir_name;
286
+
287
+ if ( file_exists( $new_dir ) ) {
288
+ if ( $undo ) {
289
+ $this->show_error( sprintf( __( 'A file or directory already exists at <code>%s</code>. The Content Directory change has not been undone. Please remove the existing file or directory and try again.', 'better-wp-security' ), $new_dir ) );
290
+ } else {
291
+ $this->show_error( sprintf( __( 'A file or directory already exists at <code>%s</code>. No Directory Name changes have been made. Please choose a new Directory Name or remove the existing file or directory and try again.', 'better-wp-security' ), $new_dir ) );
292
+ }
293
+
294
+ $this->show_network_admin_notice();
295
+
296
+ return false;
297
+ }
298
+
299
+
300
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
301
+
302
+
303
+ $old_permissions = ITSEC_Lib_Directory::get_permissions( $old_dir );
304
+ $result = rename( $old_dir, $new_dir );
305
+
306
+ if ( ! $result ) {
307
+ $this->show_error( sprintf( __( 'Unable to rename the <code>%1$s</code> directory to <code>%2$s</code>. This could indicate a file permission issue or that your server does not support the supplied name as a valid directory name. No config file or directory changes have been made.', 'better-wp-security' ), $old_name, $new_name ) );
308
+ $this->show_network_admin_notice();
309
+
310
+ return;
311
+ }
312
+
313
+ $new_permissions = ITSEC_Lib_Directory::get_permissions( $new_dir );
314
+
315
+ if ( is_int( $old_permissions) && is_int( $new_permissions ) && ( $old_permissions != $new_permissions ) ) {
316
+ $result = ITSEC_Lib_Directory::chmod( $new_dir, $old_permissions );
317
+
318
+ if ( is_wp_error( $result ) ) {
319
+ $this->show_error( sprintf( __( 'Unable to set the permissions of the new Directory Name (<code>%1$s</code>) to match the permissions of the old Directory Name. You may have to manually change the permissions of the directory to <code>%2$s</code> in order for your site to function properly.', 'better-wp-security' ), $new_name, $old_permissions ) );
320
+ }
321
+ }
322
+
323
+
324
+ if ( $undo ) {
325
+ $expression = $this->get_wp_config_define_expression();
326
+ $expression = substr( $expression, 0, -1 );
327
+ $expression .= "[\r\n]*|";
328
+
329
+ $modification_result = ITSEC_Lib_Config_File::remove_from_wp_config( $expression );
330
+ } else {
331
+ $modification = $this->get_wp_config_modification( $new_dir, get_option( 'siteurl' ) . "/$dir_name" );
332
+
333
+ $modification_result = ITSEC_Lib_Config_File::append_wp_config( $modification, true );
334
+ }
335
+
336
+
337
+ if ( is_wp_error( $modification_result ) ) {
338
+ $rename_result = rename( $new_dir, $old_dir );
339
+
340
+ if ( $rename_result ) {
341
+ ITSEC_Lib_Directory::chmod( $old_dir, $old_permissions );
342
+
343
+ $this->show_error( sprintf( __( 'Unable to update the <code>wp-config.php</code> file. No directory or config file changes have been made. %1$s (%2$s)', 'better-wp-security' ), $modification_result->get_error_message(), $modification_result->get_error_code() ) );
344
+
345
+ $this->show_error( sprintf( __( 'In order to change the content directory on your server, you will have to manually change the configuration and rename the directory. Details can be found <a href="%s">here</a>.', 'better-wp-security' ), 'https://codex.wordpress.org/Editing_wp-config.php#Moving_wp-content_folder' ) );
346
+ } else {
347
+ $this->show_error( sprintf( __( 'CRITICAL ERROR: The <code>%1$s</code> directory was successfully renamed to the new name (<code>%2$s</code>). However, an error occurred when updating the <code>wp-config.php</code> file to configure WordPress to use the new content directory. iThemes Security attempted to rename the directory back to its original name, but an unknown error prevented the rename from working as expected. In order for your site to function properly, you will either need to manually rename the <code>%2$s</code> directory back to <code>%1$s</code> or manually update the <code>wp-config.php</code> file with the necessary modifications. Instructions for making this modification can be found <a href="%3$s">here</a>.', 'better-wp-security' ), $old_name, $new_name, 'https://codex.wordpress.org/Editing_wp-config.php#Moving_wp-content_folder' ) );
348
+
349
+ $this->show_error( sprintf( __( 'Details on the error that prevented the <code>wp-config.php</code> file from updating is as follows: %1$s (%2$s)', 'better-wp-security' ), $modification_result->get_error_message(), $modification_result->get_error_code() ) );
350
+ }
351
+
352
+ return;
353
+ }
354
+
355
+
356
+ $backup = get_site_option( 'itsec_backup' );
357
+
358
+ if ( $backup !== false && isset( $backup['location'] ) ) {
359
+
360
+ $backup['location'] = str_replace( $old_dir, $new_dir, $backup['location'] );
361
+ update_site_option( 'itsec_backup', $backup );
362
+
363
+ }
364
+
365
+ $global = get_site_option( 'itsec_global' );
366
+
367
+ if ( $global !== false && ( isset( $global['log_location'] ) || isset( $global['nginx_file'] ) ) ) {
368
+
369
+ if ( isset( $global['log_location'] ) ) {
370
+ $global['log_location'] = str_replace( $old_dir, $new_dir, $global['log_location'] );
371
+ }
372
+
373
+ if ( isset( $global['nginx_file'] ) ) {
374
+ $global['nginx_file'] = str_replace( $old_dir, $new_dir, $global['nginx_file'] );
375
+ }
376
+
377
+ update_site_option( 'itsec_global', $global );
378
+
379
+ }
380
+
381
+ $this->show_network_admin_notice();
382
+
383
+ if ( $undo ) {
384
+ wp_redirect( admin_url( "admin.php?page={$_GET['page']}&message=undo-success" ) );
385
+ } else {
386
+ wp_redirect( admin_url( "admin.php?page={$_GET['page']}&message=change-success" . urlencode( "|$dir_name" ) ) );
387
+ }
388
+
389
+ exit();
390
+ }
391
+
392
+ // TODO: Created from old code. Needs to be rebuilt.
393
+ protected function show_error( $message ) {
394
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, 'error' );
395
+
396
+ $this->last_error = $message;
397
+ }
398
+
399
+ // TODO: Created from old code. Needs to be rebuilt.
400
+ protected function show_network_admin_notice() {
401
+ if ( is_multisite() ) {
402
+ if ( empty( $this->last_error ) ) {
403
+ $this->core->show_network_admin_notice( false );
404
+ } else {
405
+ $error_handler = new WP_Error();
406
+ $error_handler->add( 'error', $this->last_error );
407
+
408
+ $this->core->show_network_admin_notice( $error_handler );
409
+ }
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Adds fields that will be tracked for Google Analytics
415
+ *
416
+ * @since 4.0
417
+ *
418
+ * @param array $vars tracking vars
419
+ *
420
+ * @return array tracking vars
421
+ */
422
+ public function tracking_vars( $vars ) {
423
+
424
+ $vars['content_directory'] = array(
425
+ 'enabled' => '0:b',
426
+ );
427
+
428
+ return $vars;
429
+
430
+ }
431
+
432
+ protected function is_custom_directory() {
433
+ if ( isset( $GLOBALS['__itsec_content_directory_is_custom_directory'] ) ) {
434
+ return $GLOBALS['__itsec_content_directory_is_custom_directory'];
435
+ }
436
+
437
+ if ( ABSPATH . 'wp-content' !== WP_CONTENT_DIR ) {
438
+ $GLOBALS['__itsec_content_directory_is_custom_directory'] = true;
439
+ } else if ( get_option( 'siteurl' ) . '/wp-content' !== WP_CONTENT_URL ) {
440
+ $GLOBALS['__itsec_content_directory_is_custom_directory'] = true;
441
+ } else {
442
+ $GLOBALS['__itsec_content_directory_is_custom_directory'] = false;
443
+ }
444
+
445
+ return $GLOBALS['__itsec_content_directory_is_custom_directory'];
446
+ }
447
+
448
+ protected function is_modified_by_it_security() {
449
+ if ( ! $this->is_custom_directory() ) {
450
+ return false;
451
+ }
452
+ if ( isset( $this->is_modified_by_it_security ) ) {
453
+ return $this->is_modified_by_it_security;
454
+ }
455
+
456
+
457
+ $this->is_modified_by_it_security = false;
458
+
459
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
460
+
461
+ $wp_config_file = ITSEC_Lib_Config_File::get_wp_config_file_path();
462
+
463
+ if ( empty( $wp_config_file ) ) {
464
+ return false;
465
+ }
466
+
467
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' );
468
+
469
+ $wp_config = ITSEC_Lib_File::read( $wp_config_file );
470
+
471
+ if ( is_wp_error( $wp_config ) ) {
472
+ return false;
473
+ }
474
+
475
+ $define_expression = $this->get_wp_config_define_expression();
476
+
477
+ if ( ! preg_match( $define_expression, $wp_config ) ) {
478
+ return false;
479
+ }
480
+
481
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-utility.php' );
482
+
483
+ $wp_config_without_comments = ITSEC_Lib_Utility::strip_php_comments( $wp_config );
484
+
485
+ if ( is_wp_error( $wp_config_without_comments ) ) {
486
+ return false;
487
+ }
488
+
489
+ $define_expression_without_comment = $this->get_wp_config_define_expression( false );
490
+
491
+ if ( ! preg_match( $define_expression_without_comment, $wp_config_without_comments ) ) {
492
+ return false;
493
+ }
494
+
495
+
496
+ $this->is_modified_by_it_security = true;
497
+
498
+ return true;
499
+ }
500
+ }
core/modules/content-directory/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/content-directory/js/admin-content_directory.js ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_enable_content_dir" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_enable_content_dir" ).is( ':checked' ) ) {
6
+
7
+ jQuery( "#content_directory_name_field" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#content_directory_name_field" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ } );
core/modules/content-directory/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/core/class-itsec-core-admin.php ADDED
@@ -0,0 +1,390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Core_Admin {
4
+
5
+ function run() {
6
+
7
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
8
+
9
+ add_filter( 'itsec_meta_links', array( $this, 'add_plugin_meta_links' ) );
10
+
11
+ //Process support plugin nag
12
+ add_action( 'itsec_admin_init', array( $this, 'setup_nag' ) );
13
+
14
+ //Process support plugin nag
15
+ add_action( 'itsec_admin_init', array( $this, 'support_nag' ) );
16
+
17
+ }
18
+
19
+ /**
20
+ * Add meta boxes to primary options pages.
21
+ *
22
+ * @since 4.0
23
+ *
24
+ * @param array $available_pages array of available page_hooks
25
+ *
26
+ * @return void
27
+ */
28
+ public function add_admin_meta_boxes( $available_pages ) {
29
+
30
+ foreach ( $available_pages as $page ) {
31
+
32
+ add_meta_box(
33
+ 'itsec_security_updates',
34
+ __( 'Download Our WordPress Security Pocket Guide', 'better-wp-security' ),
35
+ array( $this, 'metabox_security_updates' ),
36
+ $page,
37
+ 'priority_side',
38
+ 'core'
39
+ );
40
+
41
+ if ( ! class_exists( 'backupbuddy_api' ) ) {
42
+
43
+ add_meta_box(
44
+ 'itsec_get_backup',
45
+ __( 'Complete Your Security Strategy With BackupBuddy', 'better-wp-security' ),
46
+ array( $this, 'metabox_get_backupbuddy' ),
47
+ $page,
48
+ 'priority_side',
49
+ 'core'
50
+ );
51
+
52
+ }
53
+
54
+ add_meta_box(
55
+ 'itsec_sync_integration',
56
+ __( 'Manage Your Sites Remotely', 'better-wp-security' ),
57
+ array( $this, 'metabox_sync_integration' ),
58
+ $page,
59
+ 'side',
60
+ 'core'
61
+ );
62
+
63
+ add_meta_box(
64
+ 'itsec_need_help',
65
+ __( 'Need Help Securing Your Site?', 'better-wp-security' ),
66
+ array( $this, 'metabox_need_help' ),
67
+ $page,
68
+ 'side',
69
+ 'core'
70
+ );
71
+
72
+ }
73
+
74
+ add_meta_box(
75
+ 'itsec_get_started',
76
+ __( 'Getting Started', 'better-wp-security' ),
77
+ array( $this, 'metabox_get_started' ),
78
+ 'toplevel_page_itsec',
79
+ 'normal',
80
+ 'core'
81
+ );
82
+
83
+ }
84
+
85
+ /**
86
+ * Adds links to the plugin row meta
87
+ *
88
+ * @since 4.0
89
+ *
90
+ * @param array $meta Existing meta
91
+ *
92
+ * @return array
93
+ */
94
+ public function add_plugin_meta_links( $meta ) {
95
+
96
+ $meta[] = '<a href="https://ithemes.com/security?utm_source=wordpressadmin&utm_medium=banner&utm_campaign=itsecfreecta" target="_blank">' . __( 'Get Support', 'better-wp-security' ) . '</a>';
97
+
98
+ return $meta;
99
+ }
100
+
101
+ /**
102
+ * Display the Get BackupBuddy metabox
103
+ *
104
+ * @since 4.0
105
+ *
106
+ * @return void
107
+ */
108
+ public function metabox_get_backupbuddy() {
109
+
110
+ echo '<p style="text-align: center;"><img src="' . plugins_url( 'img/backupbuddy-logo.png', __FILE__ ) . '" alt="BackupBuddy"></p>';
111
+ echo '<p>' . __( 'BackupBuddy is the complete backup, restore and migration solution for your WordPress site. Schedule automated backups, store your backups safely off-site and restore your site quickly & easily.', 'better-wp-security' ) . '</p>';
112
+ echo sprintf( '<p style="font-weight: bold; font-size: 1em;">%s<span style="display: block; text-align: center; font-size: 1.2em; background: #ebebeb; padding: .5em;">%s</span></p>', __( '25% off BackupBuddy with coupon code', 'better-wp-security' ), __( 'BACKUPPROTECT', 'better-wp-security' ) );
113
+ echo '<a href="http://ithemes.com/better-backups" class="button-secondary" target="_blank">' . __( 'Get BackupBuddy', 'better-wp-security' ) . '</a>';
114
+
115
+ }
116
+
117
+ /**
118
+ * Display the metabox for getting started
119
+ *
120
+ * @since 4.0
121
+ *
122
+ * @return void
123
+ */
124
+ public function metabox_get_started() {
125
+
126
+ echo '<div class="itsec_getting_started">';
127
+ echo '<div class="column">';
128
+ echo '<h2>' . __( 'Watch the Walk-Through Video', 'better-wp-security' ) . '</h2>';
129
+ echo '<a class="itsec-video-link" href="#" data-video-id="itsec_video"><img src="' . plugins_url( 'img/video.png', __FILE__ ) . '" /></a>';
130
+ echo sprintf( '<p class="itsec-video-description">%s <a href="http://ithem.es/6y" target="_blank">%s</a> %s </p>', __( 'In this short video, we walk through', 'better-wp-security' ), __( 'how to get started securing your site', 'better-wp-security' ), __( 'with iThemes Security.', 'better-wp-security' ) );
131
+ echo '<p class="itsec_video"><iframe src="//player.vimeo.com/video/89142424?title=0&amp;byline=0&amp;portrait=0" width="853" height="480" frameborder="0" ></iframe></p>';
132
+
133
+ echo '</div>';
134
+
135
+ echo '<div class="column two">';
136
+ echo '<h2>' . __( 'Website Security is a complicated subject, but we have experts that can help.', 'better-wp-security' ) . '</h2>';
137
+ echo '<p>' . __( 'Get added peace of mind with professional support from our expert team and pro features to take your site security to the next level with iThemes Security Pro.', 'better-wp-security' ) . '</p>';
138
+ echo '<p><a class="button-primary" href="https://ithemes.com/security?utm_source=wordpressadmin&utm_medium=banner&utm_campaign=itsecfreecta" target="_blank">' . __( 'Get Support and Pro Features', 'better-wp-security' ) . '</a></p>';
139
+ echo '</div>';
140
+ echo '</div>';
141
+
142
+ }
143
+
144
+ /**
145
+ * Display the Need Help metabox
146
+ *
147
+ * @since 4.0
148
+ *
149
+ * @return void
150
+ */
151
+ public function metabox_need_help() {
152
+
153
+ echo '<p>' . __( 'Since you are using the free version of iThemes Security from WordPress.org, you can get free support from the WordPress community.', 'better-wp-security' ) . '</p>';
154
+ echo '<p><a class="button-secondary" href="http://wordpress.org/support/plugin/better-wp-security" target="_blank">' . __( 'Get Free Support', 'better-wp-security' ) . '</a></p>';
155
+ echo '<p>' . __( 'Get added peace of mind with professional support from our expert team and pro features with iThemes Security Pro.', 'better-wp-security' ) . '</p>';
156
+ echo '<p><a class="button-secondary" href="https://ithemes.com/security/?utm_source=wordpressadmin&utm_medium=widget&utm_campaign=itsecfreecta" target="_blank">' . __( 'Get iThemes Security Pro', 'better-wp-security' ) . '</a></p>';
157
+
158
+ }
159
+
160
+ /**
161
+ * Display the Security Updates signup metabox.
162
+ *
163
+ * @since 4.0
164
+ *
165
+ * @return void
166
+ */
167
+ public function metabox_security_updates() {
168
+
169
+ ob_start();
170
+ ?>
171
+
172
+ <div id="mc_embed_signup">
173
+ <form
174
+ action="http://ithemes.us2.list-manage.com/subscribe/post?u=7acf83c7a47b32c740ad94a4e&amp;id=5176bfed9e"
175
+ method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate"
176
+ target="_blank" novalidate>
177
+ <div style="text-align: center;">
178
+ <img src="<?php echo plugins_url( 'img/security-ebook.png', __FILE__ ) ?>" width="145"
179
+ height="187" alt="WordPress Security - A Pocket Guide">
180
+ </div>
181
+ <p><?php _e( 'Get tips for securing your site + the latest WordPress security updates, news and releases from iThemes.', 'better-wp-security' ); ?></p>
182
+
183
+ <div id="mce-responses" class="clear">
184
+ <div class="response" id="mce-error-response" style="display:none"></div>
185
+ <div class="response" id="mce-success-response" style="display:none"></div>
186
+ </div>
187
+ <label for="mce-EMAIL"
188
+ style="display: block;margin-bottom: 3px;"><?php _e( 'Email Address', 'better-wp-security' ); ?></label>
189
+ <input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL"
190
+ placeholder="email@domain.com"> <br/><br/> <input type="submit"
191
+ value="<?php _e( 'Subscribe', 'better-wp-security' ); ?>"
192
+ name="subscribe"
193
+ id="mc-embedded-subscribe"
194
+ class="button button-secondary">
195
+ </form>
196
+ </div>
197
+
198
+ <?php
199
+ ob_end_flush();
200
+
201
+ }
202
+
203
+ /**
204
+ * Display the Need Help metabox
205
+ *
206
+ * @since 4.0
207
+ *
208
+ * @return void
209
+ */
210
+ public function metabox_sync_integration() {
211
+
212
+ ?>
213
+ <div style="text-align: center;">
214
+ <img src="<?php echo plugins_url( 'img/sync-logo.png', __FILE__ ) ?>" width="173"
215
+ height="65" alt="Manage Your Sites Remotely">
216
+ </div>
217
+ <?php
218
+
219
+ echo '<p>' . __( 'Manage updates remotely for up to 10 WordPress sites today for free!', 'better-wp-security' ) . '</p>';
220
+ echo '<p>' . __( 'Integrated with iThemes Security, so you can release lockouts and turn Away Mode on or off right from your Sync dashboard or your phone.', 'better-wp-security' ) . '</p>';
221
+ echo '<div style="text-align: center;">';
222
+ echo '<p><a class="button-primary" href="http://www.ithemes.com/sync" target="_blank">' . __( 'Try iThemes Sync for Free', 'better-wp-security' ) . '</a></p>';
223
+ echo '</div>';
224
+
225
+ }
226
+
227
+ /**
228
+ * Display (and hide) setup nag.
229
+ *
230
+ * @since 4.0
231
+ *
232
+ * @return void
233
+ */
234
+ public function setup_nag() {
235
+
236
+ global $blog_id, $itsec_globals;
237
+
238
+ if ( is_multisite() && ( $blog_id != 1 || ! current_user_can( 'manage_network_options' ) ) ) { //only display to network admin if in multisite
239
+ return;
240
+ }
241
+
242
+ $options = $itsec_globals['data'];
243
+
244
+ //display the notifcation if they haven't turned it off
245
+ if ( ( ! isset( $options['setup_completed'] ) || $options['setup_completed'] === false ) ) {
246
+
247
+ if ( ! function_exists( 'ithemes_plugin_setup_notice' ) ) {
248
+
249
+ function ithemes_plugin_setup_notice() {
250
+
251
+ global $itsec_globals;
252
+
253
+ echo '<div class="updated" id="itsec_setup_notice"><span class="it-icon-itsec"></span>'
254
+ . $itsec_globals['plugin_name'] . ' ' . __( 'is almost ready.', 'better-wp-security' ) . '<a href="#" class="itsec-notice-button" onclick="document.location.href=\'?itsec_setup=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">' . __( 'Secure Your Site Now', 'better-wp-security' ) . '</a><a target="_blank" href="http://ithemes.com/ithemes-security-4-is-here" class="itsec-notice-button">' . __( "See what's new in 4.0", 'better-wp-security' ) . '</a><a href="#" class="itsec-notice-hide" onclick="document.location.href=\'?itsec_setup=no&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">&times;</a>
255
+ </div>';
256
+
257
+ }
258
+
259
+ }
260
+
261
+ if ( is_multisite() ) {
262
+ add_action( 'network_admin_notices', 'ithemes_plugin_setup_notice' ); //register notification
263
+ } else {
264
+ add_action( 'admin_notices', 'ithemes_plugin_setup_notice' ); //register notification
265
+ }
266
+
267
+ }
268
+
269
+ //if they've clicked a button hide the notice
270
+ if ( isset( $_GET['itsec_setup'] ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'itsec-nag' ) ) {
271
+
272
+ $options = $itsec_globals['data'];
273
+
274
+ $options['setup_completed'] = true;
275
+
276
+ update_site_option( 'itsec_data', $options );
277
+
278
+ if ( is_multisite() ) {
279
+ remove_action( 'network_admin_notices', 'ithemes_plugin_setup_notice' );
280
+ } else {
281
+ remove_action( 'admin_notices', 'ithemes_plugin_setup_notice' );
282
+ }
283
+
284
+ if ( sanitize_text_field( $_GET['itsec_setup'] ) == 'no' && isset( $_SERVER['HTTP_REFERER'] ) ) {
285
+
286
+ wp_redirect( $_SERVER['HTTP_REFERER'], '302' );
287
+
288
+ } else {
289
+
290
+ wp_redirect( 'admin.php?page=itsec', '302' );
291
+
292
+ }
293
+
294
+ }
295
+
296
+ }
297
+
298
+ /**
299
+ * Display (and hide) support the plugin reminder.
300
+ *
301
+ * This will display a notice to the admin of the site only asking them to support
302
+ * the plugin after they have used it for 30 days.
303
+ *
304
+ * @since 4.0
305
+ *
306
+ * @return void
307
+ */
308
+ public function support_nag() {
309
+
310
+ global $blog_id, $itsec_globals;
311
+
312
+ if ( is_multisite() && ( $blog_id != 1 || ! current_user_can( 'manage_network_options' ) ) ) { //only display to network admin if in multisite
313
+ return;
314
+ }
315
+
316
+ $options = $itsec_globals['data'];
317
+
318
+ //display the notifcation if they haven't turned it off and they've been using the plugin at least 30 days
319
+ if ( ( ! isset( $options['already_supported'] ) || $options['already_supported'] === false ) && $options['activation_timestamp'] < ( $itsec_globals['current_time_gmt'] - 2592000 ) ) {
320
+
321
+ if ( ! function_exists( 'ithemes_plugin_support_notice' ) ) {
322
+
323
+ function ithemes_plugin_support_notice() {
324
+
325
+ global $itsec_globals;
326
+
327
+ echo '<div class="updated" id="itsec_support_notice">
328
+ <span class="itsec_notice_text">' . __( 'It looks like you\'ve been enjoying', 'better-wp-security' ) . ' ' . $itsec_globals['plugin_name'] . ' ' . __( "for at least 30 days. It's time to take the next step.", 'better-wp-security' ) . '</span><input type="button" class="itsec-notice-button" value="' . __( 'Upgrade to Pro', 'better-wp-security' ) . '" onclick="document.location.href=\'?itsec_donate=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';"> <input type="button" class="itsec-notice-button" value="' . __( 'Rate it 5★\'s', 'better-wp-security' ) . '" onclick="document.location.href=\'?itsec_rate=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';"> <input type="button" class="itsec-notice-button" value="' . __( 'Tell Your Followers', 'better-wp-security' ) . '" onclick="document.location.href=\'?itsec_tweet=yes&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';"> <input type="button" class="itsec-notice-hide" value="&times;" onclick="document.location.href=\'?itsec_no_nag=off&_wpnonce=' . wp_create_nonce( 'itsec-nag' ) . '\';">
329
+ </div>';
330
+
331
+ }
332
+
333
+ }
334
+
335
+ if ( is_multisite() ) {
336
+ add_action( 'network_admin_notices', 'ithemes_plugin_support_notice' ); //register notification
337
+ } else {
338
+ add_action( 'admin_notices', 'ithemes_plugin_support_notice' ); //register notification
339
+ }
340
+
341
+ }
342
+
343
+ //if they've clicked a button hide the notice
344
+ if ( ( isset( $_GET['itsec_no_nag'] ) || isset( $_GET['itsec_rate'] ) || isset( $_GET['itsec_tweet'] ) || isset( $_GET['itsec_donate'] ) ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'itsec-nag' ) ) {
345
+
346
+ $options = $itsec_globals['data'];
347
+
348
+ $options['already_supported'] = true;
349
+
350
+ update_site_option( 'itsec_data', $options );
351
+
352
+ if ( is_multisite() ) {
353
+ remove_action( 'network_admin_notices', 'ithemes_plugin_support_notice' );
354
+ } else {
355
+ remove_action( 'admin_notices', 'ithemes_plugin_support_notice' );
356
+ }
357
+
358
+ //take the user to paypal if they've clicked donate
359
+ if ( isset( $_GET['itsec_donate'] ) ) {
360
+ wp_redirect( 'https://ithemes.com/security', '302' );
361
+ exit();
362
+ }
363
+
364
+ //Go to the WordPress page to let them rate it.
365
+ if ( isset( $_GET['itsec_rate'] ) ) {
366
+ wp_redirect( 'http://wordpress.org/plugins/better-wp-security/', '302' );
367
+ exit();
368
+ }
369
+
370
+ //Compose a Tweet
371
+ if ( isset( $_GET['itsec_tweet'] ) ) {
372
+ wp_redirect( 'http://twitter.com/home?status=' . urlencode( 'I use ' . $itsec_globals['plugin_name'] . ' for WordPress by @iThemes and you should too - http://ithemes.com/security' ), '302' );
373
+ exit();
374
+ }
375
+
376
+ if ( sanitize_text_field( $_GET['itsec_no_nag'] ) == 'off' && isset( $_SERVER['HTTP_REFERER'] ) ) {
377
+
378
+ wp_redirect( $_SERVER['HTTP_REFERER'], '302' );
379
+
380
+ } else {
381
+
382
+ wp_redirect( 'admin.php?page=itsec', '302' );
383
+
384
+ }
385
+
386
+ }
387
+
388
+ }
389
+
390
+ }
core/modules/core/img/backupbuddy-logo.png ADDED
Binary file
core/modules/core/img/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/core/img/security-ebook.png ADDED
Binary file
core/modules/core/img/sync-logo.png ADDED
Binary file
core/modules/core/img/video.png ADDED
Binary file
core/modules/core/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/core/setup.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Core_Setup' ) ) {
4
+
5
+ class ITSEC_Core_Setup {
6
+
7
+ public function __construct() {
8
+
9
+ global $itsec_setup_action;
10
+
11
+ if ( isset( $itsec_setup_action ) ) {
12
+
13
+ switch ( $itsec_setup_action ) {
14
+
15
+ case 'activate':
16
+ $this->execute_activate();
17
+ break;
18
+ case 'upgrade':
19
+ $this->execute_upgrade();
20
+ break;
21
+ case 'deactivate':
22
+ $this->execute_deactivate();
23
+ break;
24
+ case 'uninstall':
25
+ $this->execute_uninstall();
26
+ break;
27
+
28
+ }
29
+
30
+ } else {
31
+ wp_die( 'error' );
32
+ }
33
+
34
+ }
35
+
36
+ /**
37
+ * Execute module activation.
38
+ *
39
+ * @since 4.0
40
+ *
41
+ * @return void
42
+ */
43
+ public function execute_activate() {
44
+
45
+ add_site_option( 'itsec_free_just_activated', true );
46
+
47
+ }
48
+
49
+ /**
50
+ * Execute module deactivation
51
+ *
52
+ * @return void
53
+ */
54
+ public function execute_deactivate() {
55
+
56
+ delete_site_option( 'itsec_free_just_activated' );
57
+
58
+ }
59
+
60
+ /**
61
+ * Execute module uninstall
62
+ *
63
+ * @return void
64
+ */
65
+ public function execute_uninstall() {
66
+
67
+ $this->execute_deactivate();
68
+
69
+ }
70
+
71
+ /**
72
+ * Execute module upgrade
73
+ *
74
+ * @return void
75
+ */
76
+ public function execute_upgrade() {
77
+
78
+ }
79
+
80
+ }
81
+
82
+ }
83
+
84
+ new ITSEC_Core_Setup();
core/modules/database-prefix/class-itsec-database-prefix-admin.php ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Database_Prefix_Admin {
4
+
5
+ private $settings, $core;
6
+
7
+ function run( $core ) {
8
+
9
+ global $wpdb;
10
+
11
+ $this->core = $core;
12
+
13
+ if ( $wpdb->base_prefix === 'wp_' ) {
14
+ $this->settings = true;
15
+ } else {
16
+ $this->settings = false;
17
+ }
18
+
19
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
20
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
21
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'dashboard_status' ) ); //add information for plugin status
22
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
23
+
24
+ if ( ! empty( $_POST ) ) {
25
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
26
+ }
27
+
28
+ }
29
+
30
+ /**
31
+ * Add meta boxes to primary options pages
32
+ *
33
+ * @param array $available_pages array of available page_hooks
34
+ */
35
+ public function add_admin_meta_boxes() {
36
+
37
+ //add metaboxes
38
+ add_meta_box(
39
+ 'database_prefix_options',
40
+ __( 'Change Database Prefix', 'better-wp-security' ),
41
+ array( $this, 'metabox_advanced_settings' ),
42
+ 'security_page_toplevel_page_itsec_advanced',
43
+ 'advanced',
44
+ 'core'
45
+ );
46
+
47
+ }
48
+
49
+ /**
50
+ * Sets the status in the plugin dashboard
51
+ *
52
+ * @since 4.0
53
+ *
54
+ * @return array statuses
55
+ */
56
+ public function dashboard_status( $statuses ) {
57
+
58
+ if ( $this->settings !== true ) {
59
+
60
+ $status_array = 'safe-medium';
61
+ $status = array(
62
+ 'text' => sprintf( '%s wp_.', __( 'Your database table prefix is not using', 'better-wp-security' ) ),
63
+ 'link' => '#itsec_change_table_prefix',
64
+ 'advanced' => true,
65
+ );
66
+
67
+ } else {
68
+
69
+ $status_array = 'medium';
70
+ $status = array(
71
+ 'text' => sprintf( '%s wp_.', __( 'Your database table prefix should not be', 'better-wp-security' ) ),
72
+ 'link' => '#itsec_change_table_prefix',
73
+ 'advanced' => true,
74
+ );
75
+
76
+ }
77
+
78
+ array_push( $statuses[ $status_array ], $status );
79
+
80
+ return $statuses;
81
+
82
+ }
83
+
84
+ /**
85
+ * Execute admin initializations
86
+ *
87
+ * @return void
88
+ */
89
+ public function initialize_admin() {
90
+
91
+ if ( isset( $_POST['itsec_change_table_prefix'] ) && $_POST['itsec_change_table_prefix'] == 'true' ) {
92
+
93
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'ITSEC_admin_save' ) ) {
94
+
95
+ die( __( 'Security check', 'better-wp-security' ) );
96
+
97
+ }
98
+
99
+ $this->process_database_prefix();
100
+
101
+ }
102
+
103
+ }
104
+
105
+ /**
106
+ * Render the settings metabox
107
+ *
108
+ * @return void
109
+ */
110
+ public function metabox_advanced_settings() {
111
+
112
+ echo '<p>' . __( 'By default, WordPress assigns the prefix "wp" to all tables in the database where your content, users, and objects exist. For potential attackers, this means it is easier to write scripts that can target WordPress databases as all the important table names for 95% of sites are already known. Changing the "wp" prefix makes it more difficult for tools that are trying to take advantage of vulnerabilities in other places to affect the database of your site.', 'better-wp-security' ) . '<strong>' . __( 'Before using this tool, we strongly recommend running a backup of your database.', 'better-wp-security' ) . '</strong></p>';
113
+ echo '<p>' . __( 'Note: The use of this tool requires quite a bit of system memory which may be more than some hosts can handle. If you back your database up you can\'t do any permanent damage but without a proper backup you risk breaking your site and having to perform a rather difficult fix.', 'better-wp-security' ) . '</p>';
114
+ echo sprintf( '<div class="itsec-warning-message"><span>%s: </span><a href="?page=toplevel_page_itsec_backups">%s</a> %s</div>', __( 'WARNING', 'better-wp-security' ), __( 'Backup your database', 'better-wp-security' ), __( 'before using this tool.', 'better-wp-security' ) );
115
+
116
+ global $itsec_globals;
117
+
118
+ if ( isset( $itsec_globals['settings']['write_files'] ) && $itsec_globals['settings']['write_files'] === true ) {
119
+
120
+ global $wpdb;
121
+
122
+ if ( $this->settings === true ) { //Show the correct info
123
+
124
+ ?>
125
+ <p><strong><?php _e( 'Your database is using the default table prefix', 'better-wp-security' ); ?>
126
+ <em>wp_</em>. <?php _e( 'You should change this.', 'better-wp-security' ); ?></strong></p>
127
+ <?php
128
+
129
+ } else {
130
+
131
+ $prefix = $this->settings === false ? $wpdb->base_prefix : $this->settings;
132
+
133
+ ?>
134
+ <p><?php _e( 'Your current database table prefix is', 'better-wp-security' ); ?>
135
+ <strong><em><?php echo $prefix; ?></em></strong></p>
136
+ <?php
137
+
138
+ }
139
+
140
+ ?>
141
+ <form method="post" action="?page=toplevel_page_itsec_advanced&settings-updated=true" class="itsec-form">
142
+
143
+ <?php wp_nonce_field( 'ITSEC_admin_save', 'wp_nonce' ); ?>
144
+
145
+ <table class="form-table">
146
+ <tbody>
147
+ <tr valign="top">
148
+ <th scope="row" class="settinglabel">
149
+ <label
150
+ for="itsec_change_table_prefix"><?php _e( 'Change Table Prefix', 'better-wp-security' ); ?></label>
151
+ </th>
152
+ <td class="settingfield">
153
+
154
+ <input type="checkbox" id="itsec_change_table_prefix" name="itsec_change_table_prefix"
155
+ value="true"/>
156
+
157
+ <p class="description"><?php _e( 'Check this box to generate a new database table prefix.', 'better-wp-security' ); ?></p>
158
+ </td>
159
+ </tr>
160
+ </tbody>
161
+ </table>
162
+
163
+ <p class="submit">
164
+ <input type="submit" class="button-primary"
165
+ value="<?php _e( 'Change Database Prefix', 'better-wp-security' ); ?>"/>
166
+ </p>
167
+ </form>
168
+
169
+ <?php
170
+
171
+ } else {
172
+
173
+ printf(
174
+ '<p>%s <a href="?page=toplevel_page_itsec_settings">%s</a> %s',
175
+ __( 'You must allow this plugin to write to the wp-config.php file on the', 'better-wp-security' ),
176
+ __( 'Settings', 'better-wp-security' ),
177
+ __( 'page to use this feature.', 'better-wp-security' )
178
+ );
179
+
180
+ }
181
+
182
+ }
183
+
184
+ /**
185
+ * Sanitize and validate input
186
+ *
187
+ */
188
+ public function process_database_prefix() {
189
+
190
+ global $wpdb, $itsec_files;
191
+
192
+ //suppress error messages due to timing
193
+ error_reporting( 0 );
194
+ @ini_set( 'display_errors', 0 );
195
+
196
+ $check_prefix = true; //Assume the first prefix we generate is unique
197
+
198
+ //generate a new table prefix that doesn't conflict with any other in use in the database
199
+ while ( $check_prefix ) {
200
+
201
+ $avail = 'abcdefghijklmnopqrstuvwxyz0123456789';
202
+
203
+ //first character should be alpha
204
+ $new_prefix = $avail[ mt_rand( 0, 25 ) ];
205
+
206
+ //length of new prefix
207
+ $prelength = mt_rand( 4, 9 );
208
+
209
+ //generate remaning characters
210
+ for ( $i = 0; $i < $prelength; $i ++ ) {
211
+ $new_prefix .= $avail[ mt_rand( 0, 35 ) ];
212
+ }
213
+
214
+ //complete with underscore
215
+ $new_prefix .= '_';
216
+
217
+ $new_prefix = esc_sql( $new_prefix ); //just be safe
218
+
219
+ $check_prefix = $wpdb->get_results( 'SHOW TABLES LIKE "' . $new_prefix . '%";', ARRAY_N ); //if there are no tables with that prefix in the database set checkPrefix to false
220
+
221
+ }
222
+
223
+ //assume this will work
224
+ $type = 'updated';
225
+ $message = __( 'Settings Updated', 'better-wp-security' );
226
+
227
+ $tables = $wpdb->get_results( 'SHOW TABLES LIKE "' . $wpdb->base_prefix . '%"', ARRAY_N ); //retrieve a list of all tables in the DB
228
+
229
+ //Rename each table
230
+ foreach ( $tables as $table ) {
231
+
232
+ $table = substr( $table[0], strlen( $wpdb->base_prefix ), strlen( $table[0] ) ); //Get the table name without the old prefix
233
+
234
+ //rename the table and generate an error if there is a problem
235
+ if ( $wpdb->query( 'RENAME TABLE `' . $wpdb->base_prefix . $table . '` TO `' . $new_prefix . $table . '`;' ) === false ) {
236
+
237
+ $type = 'error';
238
+ $message = sprintf( '%s %s%s. %s', __( 'Error: Could not rename table', 'better-wp-security' ), $wpdb->base_prefix, $table, __( 'You may have to rename the table manually.', 'better-wp-security' ) );
239
+
240
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
241
+
242
+ }
243
+
244
+ }
245
+
246
+ if ( is_multisite() ) { //multisite requires us to rename each blogs' options
247
+
248
+ $blogs = $wpdb->get_col( "SELECT blog_id FROM `" . $new_prefix . "blogs` WHERE public = '1' AND archived = '0' AND mature = '0' AND spam = '0' ORDER BY blog_id DESC" ); //get list of blog id's
249
+
250
+ if ( is_array( $blogs ) ) { //make sure there are other blogs to update
251
+
252
+ //update each blog's user_roles option
253
+ foreach ( $blogs as $blog ) {
254
+
255
+ $wpdb->query( 'UPDATE `' . $new_prefix . $blog . '_options` SET option_name = "' . $new_prefix . $blog . '_user_roles" WHERE option_name = "' . $wpdb->base_prefix . $blog . '_user_roles" LIMIT 1;' );
256
+
257
+ }
258
+
259
+ }
260
+
261
+ }
262
+
263
+ $upOpts = $wpdb->query( 'UPDATE `' . $new_prefix . 'options` SET option_name = "' . $new_prefix . 'user_roles" WHERE option_name = "' . $wpdb->base_prefix . 'user_roles" LIMIT 1;' ); //update options table and set flag to false if there's an error
264
+
265
+ if ( $upOpts === false ) { //set an error
266
+
267
+ $type = 'error';
268
+ $message = __( 'Could not update prefix references in options table.', 'better-wp-security' );;
269
+
270
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
271
+
272
+ }
273
+
274
+ $rows = $wpdb->get_results( 'SELECT * FROM `' . $new_prefix . 'usermeta`' ); //get all rows in usermeta
275
+
276
+ //update all prefixes in usermeta
277
+ foreach ( $rows as $row ) {
278
+
279
+ if ( substr( $row->meta_key, 0, strlen( $wpdb->base_prefix ) ) == $wpdb->base_prefix ) {
280
+
281
+ $pos = $new_prefix . substr( $row->meta_key, strlen( $wpdb->base_prefix ), strlen( $row->meta_key ) );
282
+
283
+ $result = $wpdb->query( 'UPDATE `' . $new_prefix . 'usermeta` SET meta_key="' . $pos . '" WHERE meta_key= "' . $row->meta_key . '" LIMIT 1;' );
284
+
285
+ if ( $result == false ) {
286
+
287
+ $type = 'error';
288
+ $message = __( 'Could not update prefix references in usermeta table.', 'better-wp-security' );
289
+
290
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
291
+
292
+ }
293
+
294
+ }
295
+
296
+ }
297
+
298
+
299
+
300
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
301
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' );
302
+
303
+ $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path();
304
+ $config = ITSEC_Lib_File::read( $config_file_path );
305
+ $error = '';
306
+
307
+ if ( is_wp_error( $config ) ) {
308
+ $error = sprintf( __( 'Unable to read the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() );
309
+ } else {
310
+ $regex = '/(\$table_prefix\s*=\s*)([\'"]).+?\\2(\s*;)/';
311
+ $config = preg_replace( $regex, "\${1}'$new_prefix'\${3}", $config );
312
+
313
+ $write_result = ITSEC_Lib_File::write( $config_file_path, $config );
314
+
315
+ if ( is_wp_error( $write_result ) ) {
316
+ $error = sprintf( __( 'Unable to update the <code>wp-config.php</code> file in order to update the Database Prefix. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() );
317
+ }
318
+ }
319
+
320
+ if ( ! empty( $error ) ) {
321
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $error, 'error' );
322
+ add_site_option( 'itsec_manual_update', true );
323
+ }
324
+
325
+
326
+ $this->settings = $new_prefix; //this tells the form field that all went well.
327
+
328
+ if ( is_multisite() ) {
329
+
330
+ if ( ! empty( $error ) ) {
331
+
332
+ $error_handler = new WP_Error();
333
+
334
+ $error_handler->add( 'error', $error );
335
+
336
+ $this->core->show_network_admin_notice( $error_handler );
337
+
338
+ } else {
339
+
340
+ $this->core->show_network_admin_notice( false );
341
+
342
+ }
343
+
344
+ $this->settings = false;
345
+
346
+ }
347
+
348
+ }
349
+
350
+ /**
351
+ * Adds fields that will be tracked for Google Analytics
352
+ *
353
+ * @since 4.0
354
+ *
355
+ * @param array $vars tracking vars
356
+ *
357
+ * @return array tracking vars
358
+ */
359
+ public function tracking_vars( $vars ) {
360
+
361
+ $vars['database_prefix'] = array(
362
+ 'enabled' => '0:b',
363
+ );
364
+
365
+ return $vars;
366
+
367
+ }
368
+
369
+ }
core/modules/database-prefix/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/class-ithemes-sync-verb-itsec-perform-file-scan.php ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Sync verb for file change detection
4
+ *
5
+ * Allows for file scanning to be performed remotely via iThemes Sync.
6
+ *
7
+ * @since 4.0.0
8
+ *
9
+ * @package iThemes_Security
10
+ */
11
+ class Ithemes_Sync_Verb_ITSEC_Perform_File_Scan extends Ithemes_Sync_Verb {
12
+ /**
13
+ * The name of the verb that can be called via Sync.
14
+ *
15
+ * @since 4.0.0
16
+ * @access public
17
+ * @var string
18
+ */
19
+ public static $name = 'itsec-perform-file-scan';
20
+
21
+ /**
22
+ * A description of the verb for use in Sync.
23
+ *
24
+ * @since 4.0.0
25
+ * @access public
26
+ * @var string
27
+ */
28
+ public static $description = 'Perform a one-time file scan';
29
+
30
+ /**
31
+ * Array of default arguments to process
32
+ *
33
+ * @since 4.0.0
34
+ * @access public
35
+ * @var array
36
+ */
37
+ public $default_arguments = array();
38
+
39
+ /**
40
+ * Functionaly to execute when calling the verb
41
+ *
42
+ * Functionality to execute when calling this verb VIA Sync.
43
+ *
44
+ * @since 4.0.0
45
+ *
46
+ * @return array response indicating result of the file scan
47
+ */
48
+ public function run( $arguments ) {
49
+ //We need the ITSEC_File_Change object to access the execution method.
50
+ $module = new ITSEC_File_Change();
51
+ $module->run();
52
+
53
+ $response = $module->execute_file_check( false, true );
54
+
55
+ return $response;
56
+ }
57
+ }
core/modules/file-change/class-itsec-file-change-admin.php ADDED
@@ -0,0 +1,1001 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * File Change Administrative Screens
4
+ *
5
+ * Sets up all administrative functions for the file change detection feature
6
+ * including fields, sanitation and all other privileged functions.
7
+ *
8
+ * @since 4.0.0
9
+ *
10
+ * @package iThemes_Security
11
+ */
12
+ class ITSEC_File_Change_Admin {
13
+
14
+ /**
15
+ * The module's saved options
16
+ *
17
+ * @since 4.0.0
18
+ * @access private
19
+ * @var array
20
+ */
21
+ private $settings;
22
+
23
+ /**
24
+ * The core plugin class utilized in order to set up admin and other screens
25
+ *
26
+ * @since 4.0.0
27
+ * @access private
28
+ * @var ITSEC_Core
29
+ */
30
+ private $core;
31
+
32
+ /**
33
+ * The absolute web patch to the module's files
34
+ *
35
+ * @since 4.0.0
36
+ * @access private
37
+ * @var string
38
+ */
39
+ private $module_path;
40
+
41
+ /**
42
+ * Setup the module's administrative functionality
43
+ *
44
+ * Loads the file change detection module's privileged functionality including
45
+ * settings fields.
46
+ *
47
+ * @since 4.0.0
48
+ *
49
+ * @param ITSEC_Core $core The core plugin instance
50
+ *
51
+ * @return void
52
+ */
53
+ public function run( $core ) {
54
+
55
+ $this->core = $core;
56
+ $this->settings = get_site_option( 'itsec_file_change' );
57
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
58
+
59
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); //enqueue scripts for admin page
60
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'itsec_add_admin_meta_boxes' ) ); //add meta boxes to admin page
61
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init' ) ); //initialize admin area
62
+ add_action( 'wp_ajax_itsec_file_change_ajax', array( $this, 'wp_ajax_itsec_file_change_ajax' ) );
63
+ add_action( 'wp_ajax_itsec_file_change_warning_ajax', array( $this, 'wp_ajax_itsec_file_change_warning_ajax' ) );
64
+ add_action( 'wp_ajax_itsec_jquery_filetree_ajax', array( $this, 'wp_ajax_itsec_jquery_filetree_ajax' ) );
65
+
66
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'itsec_add_dashboard_status' ) ); //add information for plugin status
67
+ add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) ); //adds logs metaboxes
68
+ add_filter( 'itsec_tracking_vars', array( $this, 'itsec_tracking_vars' ) );
69
+
70
+ //manually save options on multisite
71
+ if ( is_multisite() ) {
72
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init_multisite' ) ); //save multisite options
73
+ }
74
+
75
+ }
76
+
77
+ /**
78
+ * Add Files Admin Javascript
79
+ *
80
+ * Enqueues files used in the admin area for the file change module
81
+ *
82
+ * @since 4.0.0
83
+ *
84
+ * @return void
85
+ */
86
+ public function admin_enqueue_scripts() {
87
+
88
+ global $itsec_globals;
89
+
90
+ wp_register_script( 'itsec_file_change_warning_js', $this->module_path . 'js/admin-file-change-warning.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
91
+ wp_enqueue_script( 'itsec_file_change_warning_js' );
92
+ wp_localize_script(
93
+ 'itsec_file_change_warning_js',
94
+ 'itsec_file_change_warning',
95
+ array(
96
+ 'nonce' => wp_create_nonce( 'itsec_file_change_warning' ),
97
+ 'url' => admin_url() . 'admin.php?page=toplevel_page_itsec_logs&itsec_log_filter=file_change',
98
+ )
99
+ );
100
+
101
+ if ( isset( get_current_screen()->id ) && ( false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) || false !== strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_logs' ) || false !== strpos( get_current_screen()->id, 'dashboard' ) ) ) {
102
+
103
+ wp_register_script( 'itsec_file_change_js', $this->module_path . 'js/admin-file-change.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
104
+ wp_enqueue_script( 'itsec_file_change_js' );
105
+ wp_localize_script(
106
+ 'itsec_file_change_js',
107
+ 'itsec_file_change',
108
+ array(
109
+ 'mem_limit' => ITSEC_Lib::get_memory_limit(),
110
+ 'text' => __( 'Warning: Your server has less than 128MB of RAM dedicated to PHP. If you have many files in your installation or a lot of active plugins activating this feature may result in your site becoming disabled with a memory error. See the plugin homepage for more information.', 'better-wp-security' ),
111
+ 'module_path' => $this->module_path,
112
+ 'button_text' => isset( $this->settings['split'] ) && true === $this->settings['split'] ? __( 'Scan Next File Chunk', 'better-wp-security' ) : __( 'Scan Files Now', 'better-wp-security' ),
113
+ 'scanning_button_text' => __( 'Scanning...', 'better-wp-security' ),
114
+ 'no_changes' => __( 'No changes were detected.', 'better-wp-security' ),
115
+ 'changes' => __( 'Changes were detected. Please check the log page for details.', 'better-wp-security' ),
116
+ 'error' => __( 'An error occured. Please try again later', 'better-wp-security' ),
117
+ 'ABSPATH' => ITSEC_Lib::get_home_path(),
118
+ 'nonce' => wp_create_nonce( 'itsec_do_file_check' ),
119
+ )
120
+ );
121
+
122
+ wp_register_script( 'itsec_jquery_filetree', $this->module_path . 'filetree/jqueryFileTree.js', array( 'jquery' ), '1.01' );
123
+ wp_enqueue_script( 'itsec_jquery_filetree' );
124
+ wp_localize_script(
125
+ 'itsec_jquery_filetree',
126
+ 'itsec_jquery_filetree',
127
+ array(
128
+ 'nonce' => wp_create_nonce( 'itsec_jquery_filetree' ),
129
+ )
130
+ );
131
+
132
+ wp_register_style( 'itsec_jquery_filetree_style', $this->module_path . 'filetree/jqueryFileTree.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
133
+ wp_enqueue_style( 'itsec_jquery_filetree_style' );
134
+
135
+ wp_register_style( 'itsec_file_change_css', $this->module_path . 'css/admin-file-change.css', array(), $itsec_globals['plugin_build'] ); //add multi-select css
136
+ wp_enqueue_style( 'itsec_file_change_css' );
137
+
138
+ }
139
+
140
+ }
141
+
142
+ /**
143
+ * Display admin warning
144
+ *
145
+ * Displays a warning in the Dashboard to administrators when file changes have been detected
146
+ *
147
+ * @since 4.0.0
148
+ *
149
+ * @return void
150
+ */
151
+ public function dashboard_warning() {
152
+
153
+ global $blog_id; //get the current blog id
154
+
155
+ if ( ( is_multisite() && ( 1 != $blog_id || ! current_user_can( 'manage_network_options' ) ) ) || ! current_user_can( 'activate_plugins' ) ) { //only display to network admin if in multisite
156
+ return;
157
+ }
158
+
159
+ //if there is a warning to display
160
+ if ( '1' == get_site_option( 'itsec_file_change_warning' ) ) {
161
+
162
+ if ( ! function_exists( 'itsec_intrusion_warning' ) ) {
163
+
164
+ /**
165
+ * Echos warning markup.
166
+ *
167
+ * Build and echos the file change warning markup that appears in the dashboard
168
+ *
169
+ * @since 4.0.0
170
+ *
171
+ * @return void
172
+ */
173
+ function itsec_intrusion_warning() {
174
+
175
+ global $itsec_globals;
176
+
177
+ printf(
178
+ '<div id="itsec_file_change_warning_dialog" class="error"><p>%s %s</p> <p><input type="button" id="itsec_go_to_logs" class="button-primary" value="%s">&nbsp;<input type="button" id="itsec_dismiss_file_change_warning" class="button-secondary" value="%s"></p></div>',
179
+ $itsec_globals['plugin_name'],
180
+ __( 'has noticed a change to some files in your WordPress site. Please review the logs to make sure your system has not been compromised.', 'better-wp-security' ),
181
+ __( 'View Logs', 'better-wp-security' ),
182
+ __( 'Dismiss Warning', 'better-wp-security' )
183
+
184
+ );
185
+
186
+ }
187
+
188
+ }
189
+
190
+ //put the warning in the right spot
191
+ if ( is_multisite() ) {
192
+ add_action( 'network_admin_notices', 'itsec_intrusion_warning' ); //register notification
193
+ } else {
194
+ add_action( 'admin_notices', 'itsec_intrusion_warning' ); //register notification
195
+ }
196
+
197
+ }
198
+
199
+ //if they've clicked a button hide the notice
200
+ if ( ( isset( $_GET['bit51_view_logs'] ) || isset( $_GET['bit51_dismiss_warning'] ) ) && wp_verify_nonce( $_REQUEST['_wpnonce'], 'bit51-nag' ) ) {
201
+
202
+ //Get the options
203
+ if ( is_multisite() ) {
204
+
205
+ switch_to_blog( 1 );
206
+
207
+ delete_option( 'bwps_intrusion_warning' );
208
+
209
+ restore_current_blog();
210
+
211
+ } else {
212
+
213
+ delete_option( 'bwps_intrusion_warning' );
214
+
215
+ }
216
+
217
+ //take them back to where they started
218
+ if ( isset( $_GET['bit51_dismiss_warning'] ) ) {
219
+ wp_redirect( $_SERVER['HTTP_REFERER'], 302 );
220
+ }
221
+
222
+ //take them to the correct logs page
223
+ if ( isset( $_GET['bit51_view_logs'] ) ) {
224
+
225
+ if ( is_multisite() ) {
226
+
227
+ wp_redirect( admin_url() . 'network/admin.php?page=better-wp-security-logs#file-change', 302 );
228
+
229
+ } else {
230
+
231
+ wp_redirect( admin_url() . 'admin.php?page=better-wp-security-logs#file-change', 302 );
232
+
233
+ }
234
+
235
+ }
236
+
237
+ }
238
+
239
+ }
240
+
241
+ /**
242
+ * Echos the one-time file change scan form
243
+ *
244
+ * Echos the form necessary to perform one-time file scans which can then be accessed
245
+ * by various administrative screens including the plugin's settings and logs pages
246
+ *
247
+ * @since 4.0.0
248
+ *
249
+ * @param string $origin the origin
250
+ *
251
+ * @return void
252
+ */
253
+ public function file_change_form( $origin ) {
254
+
255
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
256
+
257
+ echo '<form id="itsec_one_time_file_check" method="post" action="">';
258
+ echo wp_nonce_field( 'itsec_do_file_check', 'wp_nonce' );
259
+ echo '<input type="hidden" name="itsec_file_change_origin" value="' . sanitize_text_field( $origin ) . '">';
260
+ echo '<p>' . __( "Press the button below to scan your site's files for changes. Note that if changes are found this will take you to the logs page for details.", 'better-wp-security' ) . '</p>';
261
+ echo '<p><input type="submit" id="itsec_one_time_file_check_submit" class="button-primary" value="' . ( isset( $this->settings['split'] ) && true === $this->settings['split'] ? __( 'Scan Next File Chunk', 'better-wp-security' ) : __( 'Scan Files Now', 'better-wp-security' ) ) . '" /></p>';
262
+ echo '<div id="itsec_file_change_status"><p></p></div>';
263
+ echo '</form>';
264
+
265
+ }
266
+
267
+ }
268
+
269
+ /**
270
+ * Add meta boxes to primary options pages
271
+ *
272
+ * Adds the module's meta settings box to the settings page and
273
+ * registers the added box in the page's table of contents.
274
+ *
275
+ * @since 4.0.0
276
+ *
277
+ * @return void
278
+ */
279
+ public function itsec_add_admin_meta_boxes() {
280
+
281
+ $id = 'file_change_options';
282
+ $title = __( 'File Change Detection', 'better-wp-security' );
283
+
284
+ add_meta_box(
285
+ $id,
286
+ $title,
287
+ array( $this, 'metabox_advanced_file_change_settings' ),
288
+ 'security_page_toplevel_page_itsec_settings',
289
+ 'advanced',
290
+ 'core'
291
+ );
292
+
293
+ $this->core->add_toc_item(
294
+ array(
295
+ 'id' => $id,
296
+ 'title' => $title,
297
+ )
298
+ );
299
+
300
+ }
301
+
302
+ /**
303
+ * Sets the status in the plugin dashboard
304
+ *
305
+ * Sets a medium priority item for the module's functionality in the plugin
306
+ * dashboard.
307
+ *
308
+ * @since 4.0.0
309
+ *
310
+ * @param array $statuses array of existing plugin dashboard statuses
311
+ *
312
+ * @return array statuses
313
+ */
314
+ public function itsec_add_dashboard_status( $statuses ) {
315
+
316
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
317
+
318
+ $status_array = 'safe-medium';
319
+ $status = array(
320
+ 'text' => __( 'Your site will detect changes to your files.', 'better-wp-security' ),
321
+ 'link' => '#itsec_file_change_enabled',
322
+ );
323
+
324
+ } else {
325
+
326
+ $status_array = 'medium';
327
+ $status = array(
328
+ 'text' => __( 'Your website is not looking for changed files. Consider turning on file change detections.', 'better-wp-security' ),
329
+ 'link' => '#itsec_file_change_enabled',
330
+ );
331
+
332
+ }
333
+
334
+ array_push( $statuses[ $status_array ], $status );
335
+
336
+ return $statuses;
337
+
338
+ }
339
+
340
+ /**
341
+ * Execute admin initializations
342
+ *
343
+ * Calls the dashboard warning method and sets up all module settings fields and
344
+ * sections.
345
+ *
346
+ * @since 4.0.0
347
+ *
348
+ * @return void
349
+ */
350
+ public function itsec_admin_init() {
351
+
352
+ $this->dashboard_warning();
353
+
354
+ //Add Settings sections
355
+ add_settings_section(
356
+ 'file_change-enabled',
357
+ __( 'File Change Detection', 'better-wp-security' ),
358
+ '__return_empty_string',
359
+ 'security_page_toplevel_page_itsec_settings'
360
+ );
361
+
362
+ add_settings_section(
363
+ 'file_change-settings',
364
+ __( 'File Change Detection Settings', 'better-wp-security' ),
365
+ '__return_empty_string',
366
+ 'security_page_toplevel_page_itsec_settings'
367
+ );
368
+
369
+ //File Change Detection Fields
370
+ add_settings_field(
371
+ 'itsec_file_change[enabled]',
372
+ __( 'File Change Detection', 'better-wp-security' ),
373
+ array( $this, 'settings_field_enabled' ),
374
+ 'security_page_toplevel_page_itsec_settings',
375
+ 'file_change-enabled'
376
+ );
377
+
378
+ add_settings_field(
379
+ 'itsec_file_change[split]',
380
+ __( 'Split File Scanning', 'better-wp-security' ),
381
+ array( $this, 'settings_field_split' ),
382
+ 'security_page_toplevel_page_itsec_settings',
383
+ 'file_change-settings'
384
+ );
385
+
386
+ add_settings_field(
387
+ 'itsec_file_change[method]',
388
+ __( 'Include/Exclude Files and Folders', 'better-wp-security' ),
389
+ array( $this, 'settings_field_method' ),
390
+ 'security_page_toplevel_page_itsec_settings',
391
+ 'file_change-settings'
392
+ );
393
+
394
+ add_settings_field(
395
+ 'itsec_file_change[file_list]',
396
+ __( 'Files and Folders List', 'better-wp-security' ),
397
+ array( $this, 'settings_field_file_list' ),
398
+ 'security_page_toplevel_page_itsec_settings',
399
+ 'file_change-settings'
400
+ );
401
+
402
+ add_settings_field(
403
+ 'itsec_file_change[types]',
404
+ __( 'Ignore File Types', 'better-wp-security' ),
405
+ array( $this, 'settings_field_types' ),
406
+ 'security_page_toplevel_page_itsec_settings',
407
+ 'file_change-settings'
408
+ );
409
+
410
+ add_settings_field(
411
+ 'itsec_file_change[email]',
412
+ __( 'Email File Change Notifications', 'better-wp-security' ),
413
+ array( $this, 'settings_field_email' ),
414
+ 'security_page_toplevel_page_itsec_settings',
415
+ 'file_change-settings'
416
+ );
417
+
418
+ add_settings_field(
419
+ 'itsec_file_change[notify_admin]',
420
+ __( 'Display File Change Admin Warning', 'better-wp-security' ),
421
+ array( $this, 'settings_field_notify_admin' ),
422
+ 'security_page_toplevel_page_itsec_settings',
423
+ 'file_change-settings'
424
+ );
425
+
426
+ //Register the settings field for the entire module
427
+ register_setting(
428
+ 'security_page_toplevel_page_itsec_settings',
429
+ 'itsec_file_change',
430
+ array( $this, 'sanitize_module_input' )
431
+ );
432
+
433
+ }
434
+
435
+ /**
436
+ * Prepare and save options in network settings
437
+ *
438
+ * Saves the options in a multi-site network where data sensitization and processing is not
439
+ * called automatically on form submission.
440
+ *
441
+ * @since 4.0.0
442
+ *
443
+ * @return void
444
+ */
445
+ public function itsec_admin_init_multisite() {
446
+
447
+ if ( isset( $_POST['itsec_file_change'] ) ) {
448
+
449
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
450
+ die( __( 'Security error!', 'better-wp-security' ) );
451
+ }
452
+
453
+ update_site_option( 'itsec_file_change', $_POST['itsec_file_change'] ); //we must manually save network options
454
+
455
+ }
456
+
457
+ }
458
+
459
+ /**
460
+ * Array of displays for the logs screen
461
+ *
462
+ * Registers the custom log page with the core plugin to allow for access from the log page's
463
+ * dropdown menu.
464
+ *
465
+ * @since 4.0.0
466
+ *
467
+ * @param array $displays metabox array
468
+ *
469
+ * @return array metabox array
470
+ */
471
+ public function itsec_logger_displays( $displays ) {
472
+
473
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
474
+
475
+ $displays[] = array(
476
+ 'module' => 'file_change',
477
+ 'title' => __( 'File Change History', 'better-wp-security' ),
478
+ 'callback' => array( $this, 'logs_metabox_content' )
479
+ );
480
+
481
+ }
482
+
483
+ return $displays;
484
+
485
+ }
486
+
487
+ /**
488
+ * Adds fields that will be tracked for Google Analytics
489
+ *
490
+ * Registers all settings in the module that will be tracked on change by
491
+ * Google Analytics if "allow tracking" is enabled.
492
+ *
493
+ * @since 4.0.0
494
+ *
495
+ * @param array $vars tracking vars
496
+ *
497
+ * @return array tracking vars
498
+ */
499
+ public function itsec_tracking_vars( $vars ) {
500
+
501
+ $vars['itsec_file_change'] = array(
502
+ 'enabled' => '0:b',
503
+ 'method' => '1:b',
504
+ 'email' => '1:b',
505
+ );
506
+
507
+ return $vars;
508
+
509
+ }
510
+
511
+ /**
512
+ * Render the file change log metabox
513
+ *
514
+ * Displays a metabox on the logs page, when filtered, showing all file change items.
515
+ *
516
+ * @since 4.0.0
517
+ *
518
+ * @return void
519
+ */
520
+ public function logs_metabox_content() {
521
+
522
+ global $itsec_globals;
523
+
524
+ $this->file_change_form( 'settings' );
525
+
526
+ if ( ! class_exists( 'ITSEC_File_Change_Log' ) ) {
527
+ require( dirname( __FILE__ ) . '/class-itsec-file-change-log.php' );
528
+ }
529
+
530
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
531
+
532
+ // If we're splitting the file check run it every 6 hours. Else daily.
533
+ if ( isset( $this->settings['split'] ) && true === $this->settings['split'] ) {
534
+
535
+ $interval = 12342;
536
+
537
+ } else {
538
+
539
+ $interval = 86400;
540
+
541
+ }
542
+
543
+ $next_run_raw = $this->settings['last_run'] + $interval;
544
+
545
+ if ( date( 'j', $next_run_raw ) == date( 'j', $itsec_globals['current_time'] ) ) {
546
+ $next_run_day = __( 'Today', 'better-wp-security' );
547
+ } else {
548
+ $next_run_day = __( 'Tomorrow', 'better-wp-security' );
549
+ }
550
+
551
+ $next_run = $next_run_day . ' at ' . date( 'g:i a', $next_run_raw );
552
+
553
+ echo '<p>' . __( 'Next automatic scan at: ', 'better-wp-security' ) . '<strong>' . $next_run . '*</strong></p>';
554
+ echo '<p><em>*' . __( 'Automatic file change scanning is triggered by a user visiting your page and may not happen exactly at the time listed.', 'better-wp-security' ) . '</em>';
555
+
556
+ }
557
+
558
+ $log_display = new ITSEC_File_Change_Log();
559
+
560
+ $log_display->prepare_items();
561
+ $log_display->display();
562
+
563
+ }
564
+
565
+ /**
566
+ * Render the settings metabox
567
+ *
568
+ * Displays the contents of the module's settings metabox on the "Settings"
569
+ * page with all module options.
570
+ *
571
+ * @since 4.0.0
572
+ *
573
+ * @return void
574
+ */
575
+ public function metabox_advanced_file_change_settings() {
576
+
577
+ echo '<p>' . __( 'Even the best security solutions can fail. How do you know if someone gets into your site? You will know because they will change something. File Change detection will tell you what files have changed in your WordPress installation alerting you to changes not made by yourself. Unlike other solutions this plugin will look only at your installation and compare files to the last check instead of comparing them with a remote installation thereby taking into account whether or not you modify the files yourself.', 'better-wp-security' ) . '</p>';
578
+
579
+ echo $this->file_change_form( 'logs' );
580
+
581
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'file_change-enabled', false );
582
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'file_change-settings', false );
583
+
584
+ echo '<p>' . PHP_EOL;
585
+
586
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
587
+
588
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
589
+
590
+ echo '</p>' . PHP_EOL;
591
+
592
+ }
593
+
594
+ /**
595
+ * Sanitize and validate input
596
+ *
597
+ * Sanitizes and validates module options saved on the settings page or via multisite.
598
+ *
599
+ * @since 4.0.0
600
+ *
601
+ * @param Array $input array of input fields
602
+ *
603
+ * @return Array Sanitized array
604
+ */
605
+ public function sanitize_module_input( $input ) {
606
+
607
+ global $itsec_globals;
608
+
609
+ //File Change Detection Fields
610
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
611
+ $input['split'] = ( isset( $input['split'] ) && intval( $input['split'] == 1 ) ? true : false );
612
+ $input['method'] = ( isset( $input['method'] ) && intval( $input['method'] == 1 ) ? true : false );
613
+ $input['email'] = ( isset( $input['email'] ) && intval( $input['email'] == 1 ) ? true : false );
614
+ $input['notify_admin'] = ( isset( $input['notify_admin'] ) && intval( $input['notify_admin'] == 1 ) ? true : false );
615
+ $input['last_chunk'] = ( isset( $input['last_chunk'] ) ? $input['last_chunk'] : false );
616
+
617
+ if ( ! is_array( $input['file_list'] ) ) {
618
+
619
+ $file_list = explode( PHP_EOL, $input['file_list'] );
620
+
621
+ } else {
622
+
623
+ $file_list = $input['file_list'];
624
+
625
+ }
626
+
627
+ $good_files = array();
628
+
629
+ foreach ( $file_list as $file ) {
630
+ $good_files[] = sanitize_text_field( trim( $file ) );
631
+ }
632
+
633
+ $input['file_list'] = $good_files;
634
+
635
+ if ( ! is_array( $input['types'] ) ) {
636
+
637
+ $file_types = explode( PHP_EOL, $input['types'] );
638
+
639
+ } else {
640
+
641
+ $file_types = $input['types'];
642
+
643
+ }
644
+
645
+ $good_types = array();
646
+
647
+ foreach ( $file_types as $file_type ) {
648
+
649
+ $file_type = trim( $file_type );
650
+
651
+ if ( 0 < strlen( $file_type ) && '.' != $file_type ) {
652
+
653
+ $good_type = sanitize_text_field( '.' . str_replace( '.', '', $file_type ) );
654
+
655
+ $good_types[] = sanitize_text_field( trim( $good_type ) );
656
+
657
+ }
658
+ }
659
+
660
+ $input['types'] = $good_types;
661
+
662
+ if ( isset( $input['split'] ) && true === $input['split'] ) {
663
+
664
+ $interval = 12282;
665
+
666
+ } else {
667
+
668
+ $interval = 86340;
669
+
670
+ }
671
+
672
+ if ( defined( 'ITSEC_DOING_FILE_CHECK' ) && true === ITSEC_DOING_FILE_CHECK ) {
673
+
674
+ $input['last_run'] = $itsec_globals['current_time'];
675
+
676
+ } else {
677
+
678
+ $input['last_run'] = isset( $this->settings['last_run'] ) && $this->settings['last_run'] > $itsec_globals['current_time'] - $interval ? $this->settings['last_run'] : ( $itsec_globals['current_time'] - $interval + 120 );
679
+
680
+ }
681
+
682
+ if ( is_multisite() ) {
683
+
684
+ $this->core->show_network_admin_notice( false );
685
+
686
+ $this->settings = $input;
687
+
688
+ }
689
+
690
+ return $input;
691
+
692
+ }
693
+
694
+ /**
695
+ * echos Email File Change Notifications Field
696
+ *
697
+ * Echo's the settings field that determines whether or not file change notifications
698
+ * will be emailed to the site admin.
699
+ *
700
+ * @since 4.0.0
701
+ *
702
+ * @return void
703
+ */
704
+ public function settings_field_email() {
705
+
706
+ if ( isset( $this->settings['email'] ) && false === $this->settings['email'] ) {
707
+
708
+ $email = 0;
709
+
710
+ } else {
711
+
712
+ $email = 1;
713
+
714
+ }
715
+
716
+ echo '<input type="checkbox" id="itsec_file_change_email" name="itsec_file_change[email]" value="1" ' . checked( 1, $email, false ) . '/>';
717
+ echo '<label for="itsec_file_change_email"> ' . __( 'Email file change notifications', 'better-wp-security' ) . '</label>';
718
+ echo '<p class="description">' . __( 'Notifications will be sent to all emails set to receive notifications on the global settings page.', 'better-wp-security' ) . '</p>';
719
+
720
+ }
721
+
722
+ /**
723
+ * echos Enable File Change Detection Field
724
+ *
725
+ * Echo's the settings field that determines whether or not the file change detection module is enabled.
726
+ *
727
+ * @since 4.0.0
728
+ *
729
+ * @return void
730
+ */
731
+ public function settings_field_enabled() {
732
+
733
+ if ( isset( $this->settings['enabled'] ) && true === $this->settings['enabled'] ) {
734
+
735
+ $enabled = 1;
736
+
737
+ } else {
738
+
739
+ $enabled = 0;
740
+
741
+ }
742
+
743
+ echo '<input type="checkbox" id="itsec_file_change_enabled" name="itsec_file_change[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
744
+ echo '<label for="itsec_file_change_enabled"> ' . __( 'Enable File Change detection', 'better-wp-security' ) . '</label>';
745
+
746
+ }
747
+
748
+ /**
749
+ * echos Enable File Change List Field
750
+ *
751
+ * Echo's the settings field that determines specific folders in the site root for exclusion or inclusion.
752
+ *
753
+ * @param array $args field arguments
754
+ *
755
+ * @return void
756
+ */
757
+ public function settings_field_file_list() {
758
+
759
+ if ( isset( $this->settings['file_list'] ) && is_array( $this->settings['file_list'] ) ) {
760
+
761
+ $file_list = implode( PHP_EOL, $this->settings['file_list'] );
762
+
763
+ } else {
764
+
765
+ $file_list = '';
766
+
767
+ }
768
+
769
+ echo '<p class="description">' . __( 'Exclude files or folders by clicking the red minus next to the file or folder name.', 'better-wp-security' ) . '</p>';
770
+ echo '<div class="file_list">';
771
+ echo '<div class="file_chooser"><div class="jquery_file_tree"></div></div>';
772
+ echo '<div class="list_field">';
773
+ echo '<textarea id="itsec_file_change_file_list" name="itsec_file_change[file_list]" wrap="off">' . $file_list . PHP_EOL . '</textarea>';
774
+ echo '</div></div>';
775
+
776
+ }
777
+
778
+ /**
779
+ * echos method Field
780
+ *
781
+ * Echo's the settings field that determines whether selected files and folders in the file_list
782
+ * field will be excluded or included.
783
+ *
784
+ * @since 4.0.0
785
+ *
786
+ * @return void
787
+ */
788
+ public function settings_field_method() {
789
+
790
+ if ( isset( $this->settings['method'] ) && true === $this->settings['method'] ) {
791
+
792
+ $method = 1;
793
+
794
+ } else {
795
+
796
+ $method = 0;
797
+
798
+ }
799
+
800
+ echo '<select id="itsec_file_change_method" name="itsec_file_change[method]">';
801
+ echo '<option value="1" ' . selected( $method, '1' ) . '>' . __( 'Exclude Selected', 'better-wp-security' ) . '</option>';
802
+ echo '<option value="0" ' . selected( $method, '0' ) . '>' . __( 'Include Selected', 'better-wp-security' ) . '</option>';
803
+ echo '</select><br />';
804
+ echo '<label for="itsec_file_change_method"> ' . __( 'Include/Exclude Files', 'better-wp-security' ) . '</label>';
805
+ echo '<p class="description">' . __( 'Select whether we should exclude files and folders selected or whether the scan should only include files and folders selected.' ) . '</p>';
806
+
807
+ }
808
+
809
+ /**
810
+ * echos Email File Change Notifications Field
811
+ *
812
+ * Echo's the settings field that determines if the notification banner will be displayed to
813
+ * administrators in the Dashboard.
814
+ *
815
+ * @since 4.0.0
816
+ *
817
+ * @return void
818
+ */
819
+ public function settings_field_notify_admin() {
820
+
821
+ if ( isset( $this->settings['notify_admin'] ) && false === $this->settings['notify_admin'] ) {
822
+
823
+ $notify_admin = 0;
824
+
825
+ } else {
826
+
827
+ $notify_admin = 1;
828
+
829
+ }
830
+
831
+ echo '<input type="checkbox" id="itsec_file_change_notify_admin" name="itsec_file_change[notify_admin]" value="1" ' . checked( 1, $notify_admin, false ) . '/>';
832
+ echo '<label for="itsec_file_change_notify_admin"> ' . __( 'Display file change admin warning', 'better-wp-security' ) . '</label>';
833
+ echo '<p class="description">' . __( 'Disabling this feature will prevent the file change warning from displaying to the site administrator in the WordPress Dashboard. Note that disabling both the error message and the email notification will result in no notifications of file changes. The only way you will be able to tell is by manually checking the log files.', 'better-wp-security' ) . '</p>';
834
+
835
+ }
836
+
837
+ /**
838
+ * echos split file checks Field
839
+ *
840
+ * Echo's the settings field that determines if file change scanning will be split into 7
841
+ * chunks throughout the day of if the entire site will be scanned in a single pass.
842
+ *
843
+ * @since 4.0.0
844
+ *
845
+ * @return void
846
+ */
847
+ public function settings_field_split() {
848
+
849
+ if ( isset( $this->settings['split'] ) && true === $this->settings['split'] ) {
850
+
851
+ $split = 1;
852
+
853
+ } else {
854
+
855
+ $split = 0;
856
+
857
+ }
858
+
859
+ echo '<input type="checkbox" id="itsec_file_change_split" name="itsec_file_change[split]" value="1" ' . checked( 1, $split, false ) . '/>';
860
+ echo '<label for="itsec_file_change_split"> ' . __( 'Split file checking into chunks.', 'better-wp-security' ) . '</label>';
861
+ echo '<p class="description"> ' . __( 'Splits file checking into 7 chunks (plugins, themes, wp-admin, wp-includes, uploads, the rest of wp-content and everything that is left over) and divides the checks evenly over the course of a day. This feature may result in more notifications but will allow for the scanning of bigger sites to continue even on a lower-end web host.', 'better-wp-security' ) . '</p>';
862
+
863
+ }
864
+
865
+ /**
866
+ * echos file change types Field
867
+ *
868
+ * Echo's the settings field that determines various file types that can be excluded from
869
+ * the file scan detection.
870
+ *
871
+ * @since 4.0.0
872
+ *
873
+ * @return void
874
+ */
875
+ public function settings_field_types() {
876
+
877
+ if ( isset( $this->settings['types'] ) && is_array( $this->settings['types'] ) ) {
878
+
879
+ $types = implode( PHP_EOL, $this->settings['types'] );
880
+
881
+ } else {
882
+
883
+ $types = implode( PHP_EOL, array(
884
+ '.jpg',
885
+ '.jpeg',
886
+ '.png',
887
+ '.log',
888
+ '.mo',
889
+ '.po',
890
+ ) );
891
+
892
+ }
893
+
894
+ echo '<textarea id="itsec_file_change_types" name="itsec_file_change[types]" wrap="off" cols="20" rows="10">' . $types . PHP_EOL . '</textarea><br />';
895
+ echo '<label for="itsec_file_change_types"> ' . __( 'File types listed here will not be checked for changes. While it is possible to change files such as images it is quite rare and nearly all known WordPress attacks exploit php, js and other text files.', 'better-wp-security' ) . '</label>';
896
+
897
+ }
898
+
899
+ /**
900
+ * Dismisses the file change notifications.
901
+ *
902
+ * Processes the ajax request for dismissing the file change notification box in the
903
+ * WordPress Dashboard.
904
+ *
905
+ * @since 4.0.0
906
+ *
907
+ * @return void
908
+ */
909
+ public function wp_ajax_itsec_file_change_warning_ajax() {
910
+
911
+ if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_file_change_warning' ) ) {
912
+ die( __( 'Security error!', 'better-wp-security' ) );
913
+ }
914
+
915
+ die( delete_site_option( 'itsec_file_change_warning' ) );
916
+
917
+ }
918
+
919
+ /**
920
+ * Gets file list for tree.
921
+ *
922
+ * Processes the ajax request for retreiving the list of files and folders that can later either
923
+ * excluded or included.
924
+ *
925
+ * @since 4.0.0
926
+ *
927
+ * @return void
928
+ */
929
+ public function wp_ajax_itsec_jquery_filetree_ajax() {
930
+
931
+ global $itsec_globals;
932
+
933
+ if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_jquery_filetree' ) || ! current_user_can( $itsec_globals['plugin_access_lvl'] ) ) {
934
+ die( __( 'Security error!', 'better-wp-security' ) );
935
+ }
936
+
937
+ $directory = sanitize_text_field( $_POST['dir'] );
938
+
939
+ $directory = urldecode( $directory );
940
+
941
+ if ( file_exists( $directory ) ) {
942
+
943
+ $files = scandir( $directory );
944
+
945
+ natcasesort( $files );
946
+
947
+ if ( 2 < count( $files ) ) { /* The 2 accounts for . and .. */
948
+
949
+ echo "<ul class=\"jqueryFileTree\" style=\"display: none;\">";
950
+
951
+ //two loops keep directories sorted before files
952
+
953
+ // All files and directories (alphabetical sorting)
954
+ foreach ( $files as $file ) {
955
+
956
+ if ( '.' != $file && '..' != $file && file_exists( $directory . $file ) && is_dir( $directory . $file ) ) {
957
+
958
+ echo '<li class="directory collapsed"><a href="#" rel="' . htmlentities( $directory . $file ) . '/">' . htmlentities( $file ) . '<div class="itsec_treeselect_control"><img src="' . plugins_url( 'images/redminus.png', __FILE__ ) . '" style="vertical-align: -3px;" title="Add to exclusions..." class="itsec_filetree_exclude"></div></a></li>';
959
+
960
+ } elseif ( '.' != $file && '..' != $file && file_exists( $directory . $file ) && ! is_dir( $directory . $file ) ) {
961
+
962
+ $ext = preg_replace( '/^.*\./', '', $file );
963
+ echo '<li class="file ext_' . $ext . '"><a href="#" rel="' . htmlentities( $directory . $file ) . '">' . htmlentities( $file ) . '<div class="itsec_treeselect_control"><img src="' . plugins_url( 'images/redminus.png', __FILE__ ) . '" style="vertical-align: -3px;" title="Add to exclusions..." class="itsec_filetree_exclude"></div></a></li>';
964
+
965
+ }
966
+
967
+ }
968
+
969
+ echo "</ul>";
970
+
971
+ }
972
+
973
+ }
974
+
975
+ exit;
976
+
977
+ }
978
+
979
+ /**
980
+ * Executes one-time file scan.
981
+ *
982
+ * Processes the ajax request to execute a one-time file scan.
983
+ *
984
+ * @since 4.0.0
985
+ *
986
+ * @return void
987
+ */
988
+ public function wp_ajax_itsec_file_change_ajax() {
989
+
990
+ if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_do_file_check' ) ) {
991
+ die( __( 'Security error!', 'better-wp-security' ) );
992
+ }
993
+
994
+ $module = new ITSEC_File_Change();
995
+ $module->run();
996
+
997
+ die( $module->execute_file_check( false ) );
998
+
999
+ }
1000
+
1001
+ }
core/modules/file-change/class-itsec-file-change-log.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Display File Change Log for Intrusion Detection Module
5
+ *
6
+ * @package iThemes-Security
7
+ * @subpackage Intrusion-Detection
8
+ * @since 4.0
9
+ */
10
+ final class ITSEC_File_Change_Log extends ITSEC_WP_List_Table {
11
+
12
+ function __construct() {
13
+
14
+ parent::__construct(
15
+ array(
16
+ 'singular' => 'itsec_file_change_log_item',
17
+ 'plural' => 'itsec_file_change_log_items',
18
+ 'ajax' => true
19
+ )
20
+ );
21
+
22
+ }
23
+
24
+ /**
25
+ * Define time column
26
+ *
27
+ * @param array $item array of row data
28
+ *
29
+ * @return string formatted output
30
+ *
31
+ **/
32
+ function column_time( $item ) {
33
+
34
+ return $item['time'];
35
+
36
+ }
37
+
38
+ /**
39
+ * Define added column
40
+ *
41
+ * @param array $item array of row data
42
+ *
43
+ * @return string formatted output
44
+ *
45
+ **/
46
+ function column_added( $item ) {
47
+
48
+ return $item['added'];
49
+
50
+ }
51
+
52
+ /**
53
+ * Define removed column
54
+ *
55
+ * @param array $item array of row data
56
+ *
57
+ * @return string formatted output
58
+ *
59
+ **/
60
+ function column_removed( $item ) {
61
+
62
+ return $item['removed'];
63
+
64
+ }
65
+
66
+ /**
67
+ * Define changed column
68
+ *
69
+ * @param array $item array of row data
70
+ *
71
+ * @return string formatted output
72
+ *
73
+ **/
74
+ function column_changed( $item ) {
75
+
76
+ return $item['changed'];
77
+
78
+ }
79
+
80
+ /**
81
+ * Define memory used column
82
+ *
83
+ * @param array $item array of row data
84
+ *
85
+ * @return string formatted output
86
+ *
87
+ **/
88
+ function column_memory( $item ) {
89
+
90
+ return $item['memory'] . __( 'MB', 'better-wp-security' );
91
+
92
+ }
93
+
94
+ /**
95
+ * Define detail column
96
+ *
97
+ * @param array $item array of row data
98
+ *
99
+ * @return string formatted output
100
+ *
101
+ **/
102
+ function column_detail( $item ) {
103
+
104
+ if ( $item['added'] > 0 || $item['removed'] > 0 || $item['changed'] > 0 ) {
105
+
106
+ echo '<a href="itsec-log-file-change-row-' . $item['detail'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
107
+
108
+ echo '<div id="itsec-log-file-change-row-' . $item['detail'] . '" style="display:none;">';
109
+
110
+ echo '<h3>' . __( 'Files Added', 'better-wp-security' ) . '</h3>';
111
+
112
+ echo '<ol class="file_change_detail_list">';
113
+
114
+ if ( sizeof( $item['added_detail'] ) > 0 ) {
115
+
116
+ foreach ( $item['added_detail'] as $file => $details ) {
117
+ echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>: ' . $file . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
118
+ }
119
+
120
+ } else {
121
+
122
+ echo '<li class="file_change_detail">' . __( 'There are no added files to report', 'better-wp-security' ) . '</li>';
123
+
124
+ }
125
+
126
+ echo '</ol>';
127
+
128
+ echo '<h3>' . __( 'Files Removed', 'better-wp-security' ) . '</h3>';
129
+
130
+ echo '<ol class="file_change_detail_list">';
131
+
132
+ if ( sizeof( $item['removed_detail'] ) > 0 ) {
133
+
134
+ foreach ( $item['removed_detail'] as $file => $details ) {
135
+ echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>:' . $file . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
136
+ }
137
+
138
+ } else {
139
+
140
+ echo '<li class="file_change_detail">' . __( 'There are no deleted files to report', 'better-wp-security' ) . '</li>';
141
+
142
+ }
143
+
144
+ echo '</ol>';
145
+
146
+ echo '<h3>' . __( 'Files Changed', 'better-wp-security' ) . '</h3>';
147
+
148
+ echo '<ol class="file_change_detail_list">';
149
+
150
+ if ( sizeof( $item['changed_detail'] ) > 0 ) {
151
+
152
+ foreach ( $item['changed_detail'] as $file => $details ) {
153
+ echo '<li class="file_change_detail"><strong>' . __( 'File', 'better-wp-security' ) . '</strong>: ' . $file . '<br /><strong>' . __( 'Date', 'better-wp-security' ) . '</strong>: ' . date( 'l F jS, Y \a\t g:i a e', ( isset( $details['mod_date'] ) ? $details['mod_date'] : $details['d'] ) ) . '</li>';
154
+ }
155
+
156
+ } else {
157
+
158
+ echo '<li class="file_change_detail">' . __( 'There are no changed files to report', 'better-wp-security' ) . '</li>';
159
+
160
+ }
161
+
162
+ echo '</ol>';
163
+ echo '</div>';
164
+
165
+ }
166
+
167
+ }
168
+
169
+ /**
170
+ * Define Columns
171
+ *
172
+ * @return array array of column titles
173
+ */
174
+ public function get_columns() {
175
+
176
+ return array(
177
+ 'time' => __( 'Check Time', 'better-wp-security' ),
178
+ 'added' => __( 'Files Added', 'better-wp-security' ),
179
+ 'removed' => __( 'Files Deleted', 'better-wp-security' ),
180
+ 'changed' => __( 'Files Changed', 'better-wp-security' ),
181
+ 'memory' => __( 'Memory Used', 'better-wp-security' ),
182
+ 'detail' => __( 'Details', 'better-wp-security' ),
183
+ );
184
+
185
+ }
186
+
187
+ /**
188
+ * Prepare data for table
189
+ *
190
+ * @return void
191
+ */
192
+ public function prepare_items() {
193
+
194
+ global $itsec_logger;
195
+
196
+ $columns = $this->get_columns();
197
+ $hidden = array();
198
+ $this->_column_headers = array( $columns, $hidden, false );
199
+
200
+ $items = $itsec_logger->get_events( 'file_change' );
201
+
202
+ $table_data = array();
203
+
204
+ $count = 0;
205
+
206
+ //Loop through results and take data we need
207
+ foreach ( $items as $item ) {
208
+
209
+ $data = maybe_unserialize( $item['log_data'] );
210
+
211
+ $table_data[$count]['time'] = $item['log_date'];
212
+ $table_data[$count]['detail'] = $item['log_id'];
213
+ $table_data[$count]['added'] = isset( $data['added'] ) ? sizeof( $data['added'] ) : 0;
214
+ $table_data[$count]['removed'] = isset( $data['removed'] ) ? sizeof( $data['removed'] ) : 0;
215
+ $table_data[$count]['changed'] = isset( $data['changed'] ) ? sizeof( $data['changed'] ) : 0;
216
+ $table_data[$count]['memory'] = isset( $data['memory'] ) ? $data['memory'] : 0;
217
+ $table_data[$count]['added_detail'] = $data['added'];
218
+ $table_data[$count]['removed_detail'] = $data['removed'];
219
+ $table_data[$count]['changed_detail'] = $data['changed'];
220
+
221
+ $count ++;
222
+
223
+ }
224
+
225
+ usort( $table_data, array( $this, 'sortrows' ) );
226
+
227
+ $this->items = $table_data;
228
+
229
+ }
230
+
231
+ /**
232
+ * Sorts rows by count in descending order
233
+ *
234
+ * @param array $a first array to compare
235
+ * @param array $b second array to compare
236
+ *
237
+ * @return int comparison result
238
+ */
239
+ function sortrows( $a, $b ) {
240
+
241
+ // If no sort, default to count
242
+ $orderby = 'time';
243
+
244
+ // If no order, default to desc
245
+ $order = 'desc';
246
+
247
+ // Determine sort order
248
+ $result = strcmp( $a[$orderby], $b[$orderby] );
249
+
250
+ // Send final sort direction to usort
251
+ return ( $order === 'asc' ) ? $result : - $result;
252
+
253
+ }
254
+
255
+ }
core/modules/file-change/class-itsec-file-change.php ADDED
@@ -0,0 +1,708 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * File Change Detection Execution and Processing
5
+ *
6
+ * Handles all file change detection execution once the feature has been
7
+ * enabled by the user.
8
+ *
9
+ * @since 4.0.0
10
+ *
11
+ * @package iThemes_Security
12
+ */
13
+ class ITSEC_File_Change {
14
+
15
+ /**
16
+ * Files and directories to be excluded from the scan
17
+ *
18
+ * @since 4.0.0
19
+ * @access private
20
+ * @var array
21
+ */
22
+ private $excludes;
23
+
24
+ /**
25
+ * Flag to indicate if a file change scan is in process
26
+ *
27
+ * @since 4.0.0
28
+ * @access private
29
+ * @var bool
30
+ */
31
+ private $running;
32
+
33
+ /**
34
+ * The module's saved options
35
+ *
36
+ * @since 4.0.0
37
+ * @access private
38
+ * @var array
39
+ */
40
+ private $settings;
41
+
42
+ /**
43
+ * Setup the module's functionality
44
+ *
45
+ * Loads the file change detection module's unpriviledged functionality including
46
+ * performing the scans themselves
47
+ *
48
+ * @since 4.0.0
49
+ *
50
+ * @return void
51
+ */
52
+ function run() {
53
+
54
+ global $itsec_globals;
55
+
56
+ $this->settings = get_site_option( 'itsec_file_change' );
57
+ $this->running = false;
58
+ $this->excludes = array(
59
+ 'file_change.lock',
60
+ $itsec_globals['ithemes_backup_dir'],
61
+ $itsec_globals['ithemes_log_dir'],
62
+ '.lock',
63
+ );
64
+ $interval = 86400; //Run daily
65
+
66
+ // If we're splitting the file check run it every 6 hours.
67
+ if ( isset( $this->settings['split'] ) && true === $this->settings['split'] ) {
68
+ $interval = 12342;
69
+ }
70
+
71
+ add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
72
+ add_filter( 'itsec_sync_modules', array( $this, 'itsec_sync_modules' ) ); //register sync modules
73
+ add_action( 'itsec_execute_file_check_cron', array( $this, 'execute_file_check' ) ); //Action to execute during a cron run.
74
+
75
+ if (
76
+ ( ! defined( 'DOING_AJAX' ) || DOING_AJAX === false ) &&
77
+ isset( $this->settings['enabled'] ) &&
78
+ true === $this->settings['enabled'] &&
79
+ isset( $this->settings['last_run'] ) &&
80
+ ( $itsec_globals['current_time'] - $interval ) > $this->settings['last_run'] &&
81
+ ( ! defined( 'ITSEC_FILE_CHECK_CRON' ) || false === ITSEC_FILE_CHECK_CRON )
82
+ ) {
83
+
84
+ wp_clear_scheduled_hook( 'itsec_file_check' );
85
+ add_action( 'init', array( $this, 'execute_file_check' ) );
86
+
87
+ } elseif ( defined( 'ITSEC_FILE_CHECK_CRON' ) && true === ITSEC_FILE_CHECK_CRON && ! wp_next_scheduled( 'itsec_execute_file_check_cron' ) ) { //Use cron if needed
88
+
89
+ wp_schedule_event( time(), 'daily', 'itsec_execute_file_check_cron' );
90
+
91
+ }
92
+
93
+ }
94
+
95
+ /**
96
+ * Builds table section for file report
97
+ *
98
+ * Builds the individual table areas for files added, changed and deleted that goes in the file
99
+ * change notification emails.
100
+ *
101
+ * @since 4.6.0
102
+ *
103
+ * @access private
104
+ *
105
+ * @param string $title User readable title to display
106
+ * @param array $files array of files to build the report on
107
+ *
108
+ * @return string the markup with the given files to be added to the report
109
+ */
110
+ private function build_table_section( $title, $files ) {
111
+
112
+ $section = '<h4>' . __( 'Files', 'better-wp-security' ) . ' ' . $title . '</h4>';
113
+ $section .= '<table border="1" style="width: 100%; text-align: center;">' . PHP_EOL;
114
+ $section .= '<tr>' . PHP_EOL;
115
+ $section .= '<th>' . __( 'File', 'better-wp-security' ) . '</th>' . PHP_EOL;
116
+ $section .= '<th>' . __( 'Modified', 'better-wp-security' ) . '</th>' . PHP_EOL;
117
+ $section .= '<th>' . __( 'File Hash', 'better-wp-security' ) . '</th>' . PHP_EOL;
118
+ $section .= '</tr>' . PHP_EOL;
119
+
120
+ if ( isset( $files ) && is_array( $files ) && 0 < sizeof( $files ) ) {
121
+
122
+ foreach ( $files as $item => $attr ) {
123
+
124
+ $section .= '<tr>' . PHP_EOL;
125
+ $section .= '<td>' . $item . '</td>' . PHP_EOL;
126
+ $section .= '<td>' . date( 'l F jS, Y \a\t g:i a e', ( isset( $attr['mod_date'] ) ? $attr['mod_date'] : $attr['d'] ) ) . '</td>' . PHP_EOL;
127
+ $section .= '<td>' . ( isset( $attr['hash'] ) ? $attr['hash'] : $attr['h'] ) . '</td>' . PHP_EOL;
128
+ $section .= '</tr>' . PHP_EOL;
129
+
130
+ }
131
+
132
+ } else {
133
+
134
+ $section .= '<tr>' . PHP_EOL;
135
+ $section .= '<td colspan="3">' . __( 'No files were changed.', 'better-wp-security' ) . '</td>' . PHP_EOL;
136
+ $section .= '</tr>' . PHP_EOL;
137
+
138
+ }
139
+
140
+ $section .= '</table>' . PHP_EOL;
141
+
142
+ return $section;
143
+
144
+ }
145
+
146
+ /**
147
+ * Executes file checking
148
+ *
149
+ * Performs the actual execution of a file scan after determining that such an execution is needed.
150
+ *
151
+ * @since 4.0.0
152
+ *
153
+ * @param bool $scheduled_call [optional] true if this is an automatic check
154
+ * @param bool $data whether to return a data array (true) or not (false)
155
+ *
156
+ * @return mixed
157
+ */
158
+ public function execute_file_check( $scheduled_call = true, $data = false ) {
159
+
160
+ global $itsec_files, $itsec_logger, $itsec_globals;
161
+
162
+ if ( false === $this->running ) {
163
+
164
+ $this->running = true;
165
+ $send_email = true;
166
+
167
+ ITSEC_Lib::set_minimum_memory_limit( '128M' );
168
+
169
+ if ( $itsec_files->get_file_lock( 'file_change', 300 ) ) { //make sure it isn't already running
170
+
171
+ define( 'ITSEC_DOING_FILE_CHECK', true );
172
+
173
+ //figure out what chunk we're on
174
+ if ( isset( $this->settings['split'] ) && true === $this->settings['split'] ) {
175
+
176
+ if ( isset( $this->settings['last_chunk'] ) && false !== $this->settings['last_chunk'] && 6 > $this->settings['last_chunk'] ) {
177
+
178
+ $chunk = $this->settings['last_chunk'] + 1;
179
+
180
+ } else {
181
+
182
+ $chunk = 0;
183
+
184
+ }
185
+
186
+ } else {
187
+
188
+ $chunk = false;
189
+
190
+ }
191
+
192
+ if ( false !== $chunk ) {
193
+
194
+ $db_field = 'itsec_local_file_list_' . $chunk;
195
+
196
+ } else {
197
+
198
+ $db_field = 'itsec_local_file_list';
199
+
200
+ }
201
+
202
+ //set base memory
203
+ $memory_used = @memory_get_peak_usage();
204
+
205
+ $logged_files = get_site_option( $db_field );
206
+
207
+ //if there are no old files old file list is an empty array
208
+ if ( false === $logged_files ) {
209
+
210
+ $send_email = false;
211
+
212
+ $logged_files = array();
213
+
214
+ if ( is_multisite() ) {
215
+
216
+ add_site_option( $db_field, $logged_files );
217
+
218
+ } else {
219
+
220
+ add_option( $db_field, $logged_files, '', 'no' );
221
+
222
+ }
223
+
224
+ }
225
+
226
+ $current_files = $this->scan_files( '', $scheduled_call, $chunk ); //scan current files
227
+
228
+ $itsec_files->release_file_lock( 'file_change' );
229
+
230
+ $files_added = @array_diff_assoc( $current_files, $logged_files ); //files added
231
+ $files_removed = @array_diff_assoc( $logged_files, $current_files ); //files deleted
232
+ $current_minus_added = @array_diff_key( $current_files, $files_added ); //remove all added files from current filelist
233
+ $logged_minus_deleted = @array_diff_key( $logged_files, $files_removed ); //remove all deleted files from old file list
234
+ $files_changed = array(); //array of changed files
235
+
236
+ //compare file hashes and mod dates
237
+ foreach ( $current_minus_added as $current_file => $current_attr ) {
238
+
239
+ if ( array_key_exists( $current_file, $logged_minus_deleted ) ) {
240
+
241
+ //if attributes differ added to changed files array
242
+ if (
243
+ (
244
+ (
245
+ isset( $current_attr['mod_date'] ) &&
246
+ 0 != strcmp( $current_attr['mod_date'], $logged_minus_deleted[ $current_file ]['mod_date'] )
247
+ ) ||
248
+ 0 != strcmp( $current_attr['d'], $logged_minus_deleted[ $current_file ]['d'] )
249
+ ) ||
250
+ (
251
+ (
252
+ isset( $current_attr['hash'] ) &&
253
+ 0 != strcmp( $current_attr['hash'], $logged_minus_deleted[ $current_file ]['hash'] ) ) ||
254
+ 0 != strcmp( $current_attr['h'], $logged_minus_deleted[ $current_file ]['h'] )
255
+ )
256
+ ) {
257
+
258
+ $remote_check = apply_filters( 'itsec_process_changed_file', true, $current_file, $current_attr['h'] ); //hook to run actions on a changed file at time of discovery
259
+
260
+ if ( true === $remote_check ) { //don't list the file if it matches the WordPress.org hash
261
+
262
+ $files_changed[ $current_file ]['h'] = isset( $current_attr['hash'] ) ? $current_attr['hash'] : $current_attr['h'];
263
+ $files_changed[ $current_file ]['d'] = isset( $current_attr['mod_date'] ) ? $current_attr['mod_date'] : $current_attr['d'];
264
+
265
+ }
266
+
267
+ }
268
+
269
+ }
270
+
271
+ }
272
+
273
+ //get count of changes
274
+ $files_added_count = sizeof( $files_added );
275
+ $files_deleted_count = sizeof( $files_removed );
276
+ $files_changed_count = sizeof( $files_changed );
277
+
278
+ if ( 0 < $files_added_count ) {
279
+
280
+ $files_added = apply_filters( 'itsec_process_added_files', $files_added ); //hook to run actions on all files added
281
+ $files_added_count = sizeof( $files_added );
282
+
283
+ }
284
+
285
+ if ( 0 < $files_deleted_count ) {
286
+ do_action( 'itsec_process_removed_files', $files_removed ); //hook to run actions on all files removed
287
+ }
288
+
289
+ //create single array of all changes
290
+ $full_change_list = array(
291
+ 'added' => $files_added,
292
+ 'removed' => $files_removed,
293
+ 'changed' => $files_changed,
294
+ );
295
+
296
+ update_site_option( $db_field, $current_files );
297
+
298
+ //Cleanup variables when we're done with them
299
+ unset( $files_added );
300
+ unset( $files_removed );
301
+ unset( $files_changed );
302
+ unset( $current_files );
303
+
304
+ $this->settings['last_run'] = $itsec_globals['current_time'];
305
+ $this->settings['last_chunk'] = $chunk;
306
+
307
+ update_site_option( 'itsec_file_change', $this->settings );
308
+
309
+ //get new max memory
310
+ $check_memory = @memory_get_peak_usage();
311
+ if ( $check_memory > $memory_used ) {
312
+ $memory_used = $check_memory - $memory_used;
313
+ }
314
+
315
+ $full_change_list['memory'] = round( ( $memory_used / 1000000 ), 2 );
316
+
317
+ $itsec_logger->log_event(
318
+ 'file_change',
319
+ 8,
320
+ $full_change_list
321
+ );
322
+
323
+ if (
324
+ true === $send_email &&
325
+ false !== $scheduled_call &&
326
+ isset( $this->settings['email'] ) &&
327
+ true === $this->settings['email'] &&
328
+ (
329
+ 0 < $files_added_count ||
330
+ 0 < $files_changed_count ||
331
+ 0 < $files_deleted_count
332
+ )
333
+ ) {
334
+
335
+ $email_details = array(
336
+ $files_added_count,
337
+ $files_deleted_count,
338
+ $files_changed_count,
339
+ $full_change_list
340
+ );
341
+
342
+ $this->send_notification_email( $email_details );
343
+ }
344
+
345
+ if (
346
+ function_exists( 'get_current_screen' ) &&
347
+ (
348
+ ! isset( get_current_screen()->id ) ||
349
+ false === strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_logs' )
350
+ ) &&
351
+ isset( $this->settings['notify_admin'] ) &&
352
+ true === $this->settings['notify_admin']
353
+ ) {
354
+ add_site_option( 'itsec_file_change_warning', true );
355
+ }
356
+
357
+ $itsec_files->release_file_lock( 'file_change' );
358
+
359
+ if ( 0 < $files_added_count || 0 < $files_changed_count || 0 < $files_deleted_count ) {
360
+
361
+ $this->running = false;
362
+
363
+ //There were changes found
364
+ if ( true === $data ) {
365
+
366
+ return $full_change_list;
367
+
368
+ } else {
369
+
370
+ return true;
371
+
372
+ }
373
+
374
+ } else {
375
+
376
+ $this->running = false;
377
+
378
+ return false; //No changes were found
379
+
380
+ }
381
+
382
+ }
383
+
384
+ $this->running = false;
385
+
386
+ return - 1; //An error occured
387
+
388
+ }
389
+
390
+ }
391
+
392
+ /**
393
+ * Get Report Details
394
+ *
395
+ * Creates the HTML markup for the email that is to be built
396
+ *
397
+ * @since 4.0.0
398
+ *
399
+ * @param array $email_details array of details to build email
400
+ *
401
+ * @return string report details
402
+ */
403
+ public function get_email_report( $email_details ) {
404
+
405
+ global $itsec_globals;
406
+
407
+ //seperate array by category
408
+ $added = $email_details[3]['added'];
409
+ $removed = $email_details[3]['removed'];
410
+ $changed = $email_details[3]['changed'];
411
+ $report = '<strong>' . __( 'Scan Time:', 'better-wp-security' ) . '</strong> ' . date( 'l, F jS g:i a e', $itsec_globals['current_time'] ) . "<br />" . PHP_EOL;
412
+ $report .= '<strong>' . __( 'Files Added:', 'better-wp-security' ) . '</strong> ' . $email_details[0] . "<br />" . PHP_EOL;
413
+ $report .= '<strong>' . __( 'Files Deleted:', 'better-wp-security' ) . '</strong> ' . $email_details[1] . "<br />" . PHP_EOL;
414
+ $report .= '<strong>' . __( 'Files Modified:', 'better-wp-security' ) . '</strong> ' . $email_details[2] . "<br />" . PHP_EOL;
415
+ $report .= '<strong>' . __( 'Memory Used:', 'better-wp-security' ) . '</strong> ' . $email_details[3]['memory'] . " MB<br />" . PHP_EOL;
416
+
417
+ $report .= $this->build_table_section( __( 'Added', 'better-wp-security' ), $added );
418
+ $report .= $this->build_table_section( __( 'Deleted', 'better-wp-security' ), $removed );
419
+ $report .= $this->build_table_section( __( 'Modified', 'better-wp-security' ), $changed );
420
+
421
+ return $report;
422
+
423
+ }
424
+
425
+ /**
426
+ * Check file list
427
+ *
428
+ * Checks if given file should be included in file check based on exclude/include options
429
+ *
430
+ * @since 4.0.0
431
+ *
432
+ * @access private
433
+ *
434
+ * @param string $file path of file to check from site root
435
+ *
436
+ * @return bool true if file should be checked false if not
437
+ */
438
+ private function is_checkable_file( $file ) {
439
+
440
+ //get file list from last check
441
+ $file_list = $this->settings['file_list'];
442
+ $type_list = $this->settings['types'];
443
+
444
+ //Make sure the file list is an array
445
+ if ( ! is_array( $file_list ) ) {
446
+ $file_list = array();
447
+ }
448
+
449
+ //lets check the absolute path too for excludes just to be sure
450
+ $abs_file = ITSEC_Lib::get_home_path() . $file;
451
+
452
+ //assume not a directory and not checked
453
+ $flag = false;
454
+
455
+ if ( is_array( $this->excludes ) && ( in_array( $file, $this->excludes ) || in_array( $abs_file, $this->excludes ) ) ) {
456
+ return false;
457
+ }
458
+
459
+ if ( in_array( $file, $file_list ) ) {
460
+ $flag = true;
461
+ }
462
+
463
+ if ( ! is_dir( $file ) ) {
464
+
465
+ $path_info = pathinfo( $file );
466
+
467
+ if ( isset( $path_info['extension'] ) && in_array( '.' . $path_info['extension'], $this->excludes ) ) {
468
+
469
+ return false;
470
+
471
+ }
472
+
473
+ if ( isset( $path_info['extension'] ) && in_array( '.' . $path_info['extension'], $type_list ) ) {
474
+ $flag = true;
475
+ }
476
+
477
+ }
478
+
479
+ if ( true === $this->settings['method'] ) {
480
+
481
+ if ( true === $flag ) { //if exclude reverse
482
+ return false;
483
+ } else {
484
+ return true;
485
+ }
486
+
487
+ } else { //return flag
488
+
489
+ return $flag;
490
+
491
+ }
492
+
493
+ }
494
+
495
+ /**
496
+ * Register file change detection for logger
497
+ *
498
+ * Registers the file change detection module with the core logger functionality.
499
+ *
500
+ * @since 4.0.0
501
+ *
502
+ * @param array $logger_modules array of logger modules
503
+ *
504
+ * @return array array of logger modules
505
+ */
506
+ public function itsec_logger_modules( $logger_modules ) {
507
+
508
+ $logger_modules['file_change'] = array(
509
+ 'type' => 'file_change',
510
+ 'function' => __( 'File Changes Detected', 'better-wp-security' ),
511
+ );
512
+
513
+ return $logger_modules;
514
+
515
+ }
516
+
517
+ /**
518
+ * Register file change detection for Sync
519
+ *
520
+ * Reigsters iThemes Sync verbs for the file change detection module.
521
+ *
522
+ * @since 4.0.0
523
+ *
524
+ * @param array $sync_modules array of sync modules
525
+ *
526
+ * @return array array of sync modules
527
+ */
528
+ public function itsec_sync_modules( $sync_modules ) {
529
+
530
+ $sync_modules['file-change'] = array(
531
+ 'verbs' => array(
532
+ 'itsec-perform-file-scan' => 'Ithemes_Sync_Verb_ITSEC_Perform_File_Scan',
533
+ ),
534
+ 'path' => dirname( __FILE__ ),
535
+ );
536
+
537
+ return $sync_modules;
538
+
539
+ }
540
+
541
+ /**
542
+ * Scans all files in a given path
543
+ *
544
+ * Scans all items in a given path recursively building an array of items including
545
+ * hashes, filenames and modification dates
546
+ *
547
+ * @since 4.0.0
548
+ *
549
+ * @access private
550
+ *
551
+ * @param string $path [optional] path to scan, defaults to WordPress root
552
+ * @param bool $scheduled_call is this a scheduled call
553
+ * @param mixed $chunk the current chunk or false
554
+ *
555
+ * @return array array of files found and their information
556
+ *
557
+ */
558
+ private function scan_files( $path = '', $scheduled_call, $chunk ) {
559
+
560
+ if ( $chunk !== false ) {
561
+
562
+ $content_dir = explode( '/', WP_CONTENT_DIR );
563
+ $plugin_dir = explode( '/', WP_PLUGIN_DIR );
564
+
565
+ $dirs = array(
566
+ 'wp-admin/',
567
+ 'wp-includes/',
568
+ $content_dir[ sizeof( $content_dir ) - 1 ] . '/',
569
+ $content_dir[ sizeof( $content_dir ) - 1 ] . '/uploads/',
570
+ $content_dir[ sizeof( $content_dir ) - 1 ] . '/themes/',
571
+ $content_dir[ sizeof( $content_dir ) - 1 ] . '/' . $plugin_dir[ sizeof( $plugin_dir ) - 1 ] . '/',
572
+ ''
573
+ );
574
+
575
+ $path = $dirs[ $chunk ];
576
+
577
+ unset( $dirs[ $chunk ] );
578
+
579
+ $this->excludes = $dirs;
580
+
581
+ }
582
+
583
+ $data = array();
584
+
585
+ $clean_path = sanitize_text_field( $path );
586
+
587
+ if ( $directory_handle = @opendir( ITSEC_Lib::get_home_path() . $clean_path ) ) { //get the directory
588
+
589
+ while ( false !== ( $item = @readdir( $directory_handle ) ) ) { // loop through dirs
590
+
591
+ if ( '.' != $item && '..' != $item ) { //don't scan parents
592
+
593
+ $relname = $path . $item;
594
+
595
+ $absname = ITSEC_Lib::get_home_path() . $relname;
596
+
597
+ if ( is_dir( $absname ) && 'dir' == filetype( $absname ) ) {
598
+
599
+ $is_dir = true;
600
+ $check_name = trailingslashit( $relname );
601
+
602
+ } else {
603
+
604
+ $is_dir = false;
605
+ $check_name = $relname;
606
+
607
+ }
608
+
609
+ if ( true === $this->is_checkable_file( $check_name ) ) { //make sure the user wants this file scanned
610
+
611
+ if ( true === $is_dir ) { //if directory scan it
612
+
613
+ $data = array_merge( $data, $this->scan_files( $relname . '/', $scheduled_call, false ) );
614
+
615
+ } else { //is file so add to array
616
+
617
+ $data[ $relname ] = array();
618
+ $data[ $relname ]['d'] = @filemtime( $absname );
619
+ $data[ $relname ]['h'] = @md5_file( $absname );
620
+
621
+ }
622
+
623
+ }
624
+
625
+ }
626
+
627
+ }
628
+
629
+ @closedir( $directory_handle ); //close the directory we're working with
630
+
631
+ }
632
+
633
+ return $data; // return the files we found in this dir
634
+
635
+ }
636
+
637
+ /**
638
+ * Builds and sends notification email
639
+ *
640
+ * Sends the notication email too all applicable administrative users notifying them
641
+ * that file changes have been detected
642
+ *
643
+ * @since 4.0.0
644
+ *
645
+ * @access private
646
+ *
647
+ * @param array $email_details array of details for the email messge
648
+ *
649
+ * @return void
650
+ */
651
+ private function send_notification_email( $email_details ) {
652
+
653
+ global $itsec_globals, $itsec_notify;
654
+
655
+ if ( ! isset( $itsec_globals['settings']['digest_email'] ) || false === $itsec_globals['settings']['digest_email'] ) {
656
+
657
+ $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
658
+ $subject = '[' . get_option( 'siteurl' ) . '] ' . __( 'WordPress File Change Warning', 'better-wp-security' ) . ' ' . date( 'l, F jS, Y \a\\t g:i a e', $itsec_globals['current_time'] );
659
+
660
+ $body = '<p>' . __( 'A file (or files) on your site at ', 'better-wp-security' ) . ' ' . get_option( 'siteurl' ) . __( ' have been changed. Please review the report below to verify changes are not the result of a compromise.', 'better-wp-security' ) . '</p>';
661
+ $body .= $this->get_email_report( $email_details ); //get report
662
+
663
+ $args = array(
664
+ 'headers' => $headers,
665
+ 'message' => $body,
666
+ 'subject' => $subject,
667
+ );
668
+
669
+ $itsec_notify->notify( $args );
670
+
671
+ } else {
672
+
673
+ $changed = $email_details[0] + $email_details[1] + $email_details[2];
674
+
675
+ if ( 0 < $changed ) {
676
+
677
+ $message = sprintf(
678
+ '<strong>%s:</strong> %s %s.',
679
+ __( 'File changes detected', 'better-wp-security' ),
680
+ $itsec_globals['plugin_name'],
681
+ __( 'detected file changes on your system', 'better-wp-security' )
682
+ );
683
+
684
+ $itsec_notify->notify( $message );
685
+
686
+ }
687
+
688
+ }
689
+
690
+ }
691
+
692
+ /**
693
+ * Set HTML content type for email
694
+ *
695
+ * This filter allows for the content type of the file change notification emails to be set to
696
+ * HTML in order to send the tables and related data included in file change reporting.
697
+ *
698
+ * @since 4.0.0
699
+ *
700
+ * @return string html content type
701
+ */
702
+ public function set_html_content_type() {
703
+
704
+ return 'text/html';
705
+
706
+ }
707
+
708
+ }
core/modules/file-change/css/admin-file-change.css ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .jquery_file_tree {
2
+ height : 200px;
3
+ width : 90%;
4
+ overflow : auto;
5
+ border : 1px solid #dfdfdf;
6
+ background-color : #f9f9f9;
7
+ padding : 3px;
8
+ }
9
+
10
+ div.inside .jqueryFileTree {
11
+ margin : 0 5px;
12
+ }
13
+
14
+ div.inside .jqueryFileTree li {
15
+ list-style : none;
16
+ }
17
+
18
+ .file_list {
19
+ width : 100%;
20
+ position : relative;
21
+ }
22
+
23
+ .file_chooser {
24
+ float : left;
25
+ width : 49%;
26
+ }
27
+
28
+ .list_field {
29
+ float : left;
30
+ height : 200px;
31
+ width : 50%;
32
+ }
33
+
34
+ .list_field textarea {
35
+ width : 90% !important;
36
+ height : 200px;
37
+ overflow : auto;
38
+ padding : 3px;
39
+ }
40
+
41
+ .itsec_treeselect_control {
42
+ visibility : hidden;
43
+ margin-left : 10px;
44
+ position : absolute;
45
+ right : 4px;
46
+ top : 4px;
47
+ }
48
+
49
+ .itsec_treeselect_control img {
50
+ margin-right : 4px;
51
+ margin-left : 4px;
52
+ margin-top : 2px;
53
+ float : right;
54
+ cursor : pointer;
55
+ }
56
+
57
+ ul.jqueryFileTree a {
58
+ padding : 5px;
59
+
60
+ }
61
+
62
+ UL.jqueryFileTree LI {
63
+ position : relative;
64
+ }
65
+
66
+ .file_change_detail_list {
67
+ margin : 1em 1em 1em 1.5em;
68
+ border : 1px solid #ebebeb;
69
+ }
70
+
71
+ .file_change_detail_list .file_change_detail {
72
+ padding : .75em 1em .75em .25em;
73
+ margin : 0;
74
+ }
75
+
76
+ .file_change_detail_list .file_change_detail:nth-child(2n) {
77
+ background : #ebebeb;
78
+ }
79
+
80
+ @media only screen and (max-width : 850px) {
81
+ .file_chooser {
82
+ float : none;
83
+ width : 100%;
84
+ }
85
+
86
+ .list_field {
87
+ float : none;
88
+ margin-left : 0;
89
+ margin-top : 1em;
90
+ width : 100%;
91
+ }
92
+ }
core/modules/file-change/css/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/filetree/images/application.png ADDED
Binary file
core/modules/file-change/filetree/images/code.png ADDED
Binary file
core/modules/file-change/filetree/images/css.png ADDED
Binary file
core/modules/file-change/filetree/images/db.png ADDED
Binary file
core/modules/file-change/filetree/images/directory.png ADDED
Binary file
core/modules/file-change/filetree/images/doc.png ADDED
Binary file
core/modules/file-change/filetree/images/file.png ADDED
Binary file
core/modules/file-change/filetree/images/film.png ADDED
Binary file
core/modules/file-change/filetree/images/flash.png ADDED
Binary file
core/modules/file-change/filetree/images/folder_open.png ADDED
Binary file
core/modules/file-change/filetree/images/html.png ADDED
Binary file
core/modules/file-change/filetree/images/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/filetree/images/java.png ADDED
Binary file
core/modules/file-change/filetree/images/linux.png ADDED
Binary file
core/modules/file-change/filetree/images/music.png ADDED
Binary file
core/modules/file-change/filetree/images/pdf.png ADDED
Binary file
core/modules/file-change/filetree/images/php.png ADDED
Binary file
core/modules/file-change/filetree/images/picture.png ADDED
Binary file
core/modules/file-change/filetree/images/ppt.png ADDED
Binary file
core/modules/file-change/filetree/images/psd.png ADDED
Binary file
core/modules/file-change/filetree/images/ruby.png ADDED
Binary file
core/modules/file-change/filetree/images/script.png ADDED
Binary file
core/modules/file-change/filetree/images/spinner.gif ADDED
Binary file
core/modules/file-change/filetree/images/txt.png ADDED
Binary file
core/modules/file-change/filetree/images/xls.png ADDED
Binary file
core/modules/file-change/filetree/images/zip.png ADDED
Binary file
core/modules/file-change/filetree/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/filetree/jqueryFileTree.css ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ UL.jqueryFileTree {
2
+ font-family : Verdana, sans-serif;
3
+ font-size : 11px;
4
+ line-height : 18px;
5
+ padding : 0px;
6
+ margin : 0px;
7
+ }
8
+
9
+ UL.jqueryFileTree LI {
10
+ list-style : none;
11
+ padding : 0px;
12
+ padding-left : 20px;
13
+ margin : 0px;
14
+ white-space : nowrap;
15
+ }
16
+
17
+ UL.jqueryFileTree A {
18
+ color : #333;
19
+ text-decoration : none;
20
+ display : block;
21
+ padding : 0px 2px;
22
+ }
23
+
24
+ UL.jqueryFileTree A:hover {
25
+ background : #bdf;
26
+ }
27
+
28
+ /* Core Styles */
29
+ .jqueryFileTree LI.directory {
30
+ background : url(images/directory.png) left top no-repeat;
31
+ }
32
+
33
+ .jqueryFileTree LI.expanded {
34
+ background : url(images/folder_open.png) left top no-repeat;
35
+ }
36
+
37
+ .jqueryFileTree LI.file {
38
+ background : url(images/file.png) left top no-repeat;
39
+ }
40
+
41
+ .jqueryFileTree LI.wait {
42
+ background : url(images/spinner.gif) left top no-repeat;
43
+ }
44
+
45
+ /* File Extensions*/
46
+ .jqueryFileTree LI.ext_3gp {
47
+ background : url(images/film.png) left top no-repeat;
48
+ }
49
+
50
+ .jqueryFileTree LI.ext_afp {
51
+ background : url(images/code.png) left top no-repeat;
52
+ }
53
+
54
+ .jqueryFileTree LI.ext_afpa {
55
+ background : url(images/code.png) left top no-repeat;
56
+ }
57
+
58
+ .jqueryFileTree LI.ext_asp {
59
+ background : url(images/code.png) left top no-repeat;
60
+ }
61
+
62
+ .jqueryFileTree LI.ext_aspx {
63
+ background : url(images/code.png) left top no-repeat;
64
+ }
65
+
66
+ .jqueryFileTree LI.ext_avi {
67
+ background : url(images/film.png) left top no-repeat;
68
+ }
69
+
70
+ .jqueryFileTree LI.ext_bat {
71
+ background : url(images/application.png) left top no-repeat;
72
+ }
73
+
74
+ .jqueryFileTree LI.ext_bmp {
75
+ background : url(images/picture.png) left top no-repeat;
76
+ }
77
+
78
+ .jqueryFileTree LI.ext_c {
79
+ background : url(images/code.png) left top no-repeat;
80
+ }
81
+
82
+ .jqueryFileTree LI.ext_cfm {
83
+ background : url(images/code.png) left top no-repeat;
84
+ }
85
+
86
+ .jqueryFileTree LI.ext_cgi {
87
+ background : url(images/code.png) left top no-repeat;
88
+ }
89
+
90
+ .jqueryFileTree LI.ext_com {
91
+ background : url(images/application.png) left top no-repeat;
92
+ }
93
+
94
+ .jqueryFileTree LI.ext_cpp {
95
+ background : url(images/code.png) left top no-repeat;
96
+ }
97
+
98
+ .jqueryFileTree LI.ext_css {
99
+ background : url(images/css.png) left top no-repeat;
100
+ }
101
+
102
+ .jqueryFileTree LI.ext_doc {
103
+ background : url(images/doc.png) left top no-repeat;
104
+ }
105
+
106
+ .jqueryFileTree LI.ext_exe {
107
+ background : url(images/application.png) left top no-repeat;
108
+ }
109
+
110
+ .jqueryFileTree LI.ext_gif {
111
+ background : url(images/picture.png) left top no-repeat;
112
+ }
113
+
114
+ .jqueryFileTree LI.ext_fla {
115
+ background : url(images/flash.png) left top no-repeat;
116
+ }
117
+
118
+ .jqueryFileTree LI.ext_h {
119
+ background : url(images/code.png) left top no-repeat;
120
+ }
121
+
122
+ .jqueryFileTree LI.ext_htm {
123
+ background : url(images/html.png) left top no-repeat;
124
+ }
125
+
126
+ .jqueryFileTree LI.ext_html {
127
+ background : url(images/html.png) left top no-repeat;
128
+ }
129
+
130
+ .jqueryFileTree LI.ext_jar {
131
+ background : url(images/java.png) left top no-repeat;
132
+ }
133
+
134
+ .jqueryFileTree LI.ext_jpg {
135
+ background : url(images/picture.png) left top no-repeat;
136
+ }
137
+
138
+ .jqueryFileTree LI.ext_jpeg {
139
+ background : url(images/picture.png) left top no-repeat;
140
+ }
141
+
142
+ .jqueryFileTree LI.ext_js {
143
+ background : url(images/script.png) left top no-repeat;
144
+ }
145
+
146
+ .jqueryFileTree LI.ext_lasso {
147
+ background : url(images/code.png) left top no-repeat;
148
+ }
149
+
150
+ .jqueryFileTree LI.ext_log {
151
+ background : url(images/txt.png) left top no-repeat;
152
+ }
153
+
154
+ .jqueryFileTree LI.ext_m4p {
155
+ background : url(images/music.png) left top no-repeat;
156
+ }
157
+
158
+ .jqueryFileTree LI.ext_mov {
159
+ background : url(images/film.png) left top no-repeat;
160
+ }
161
+
162
+ .jqueryFileTree LI.ext_mp3 {
163
+ background : url(images/music.png) left top no-repeat;
164
+ }
165
+
166
+ .jqueryFileTree LI.ext_mp4 {
167
+ background : url(images/film.png) left top no-repeat;
168
+ }
169
+
170
+ .jqueryFileTree LI.ext_mpg {
171
+ background : url(images/film.png) left top no-repeat;
172
+ }
173
+
174
+ .jqueryFileTree LI.ext_mpeg {
175
+ background : url(images/film.png) left top no-repeat;
176
+ }
177
+
178
+ .jqueryFileTree LI.ext_ogg {
179
+ background : url(images/music.png) left top no-repeat;
180
+ }
181
+
182
+ .jqueryFileTree LI.ext_pcx {
183
+ background : url(images/picture.png) left top no-repeat;
184
+ }
185
+
186
+ .jqueryFileTree LI.ext_pdf {
187
+ background : url(images/pdf.png) left top no-repeat;
188
+ }
189
+
190
+ .jqueryFileTree LI.ext_php {
191
+ background : url(images/php.png) left top no-repeat;
192
+ }
193
+
194
+ .jqueryFileTree LI.ext_png {
195
+ background : url(images/picture.png) left top no-repeat;
196
+ }
197
+
198
+ .jqueryFileTree LI.ext_ppt {
199
+ background : url(images/ppt.png) left top no-repeat;
200
+ }
201
+
202
+ .jqueryFileTree LI.ext_psd {
203
+ background : url(images/psd.png) left top no-repeat;
204
+ }
205
+
206
+ .jqueryFileTree LI.ext_pl {
207
+ background : url(images/script.png) left top no-repeat;
208
+ }
209
+
210
+ .jqueryFileTree LI.ext_py {
211
+ background : url(images/script.png) left top no-repeat;
212
+ }
213
+
214
+ .jqueryFileTree LI.ext_rb {
215
+ background : url(images/ruby.png) left top no-repeat;
216
+ }
217
+
218
+ .jqueryFileTree LI.ext_rbx {
219
+ background : url(images/ruby.png) left top no-repeat;
220
+ }
221
+
222
+ .jqueryFileTree LI.ext_rhtml {
223
+ background : url(images/ruby.png) left top no-repeat;
224
+ }
225
+
226
+ .jqueryFileTree LI.ext_rpm {
227
+ background : url(images/linux.png) left top no-repeat;
228
+ }
229
+
230
+ .jqueryFileTree LI.ext_ruby {
231
+ background : url(images/ruby.png) left top no-repeat;
232
+ }
233
+
234
+ .jqueryFileTree LI.ext_sql {
235
+ background : url(images/db.png) left top no-repeat;
236
+ }
237
+
238
+ .jqueryFileTree LI.ext_swf {
239
+ background : url(images/flash.png) left top no-repeat;
240
+ }
241
+
242
+ .jqueryFileTree LI.ext_tif {
243
+ background : url(images/picture.png) left top no-repeat;
244
+ }
245
+
246
+ .jqueryFileTree LI.ext_tiff {
247
+ background : url(images/picture.png) left top no-repeat;
248
+ }
249
+
250
+ .jqueryFileTree LI.ext_txt {
251
+ background : url(images/txt.png) left top no-repeat;
252
+ }
253
+
254
+ .jqueryFileTree LI.ext_vb {
255
+ background : url(images/code.png) left top no-repeat;
256
+ }
257
+
258
+ .jqueryFileTree LI.ext_wav {
259
+ background : url(images/music.png) left top no-repeat;
260
+ }
261
+
262
+ .jqueryFileTree LI.ext_wmv {
263
+ background : url(images/film.png) left top no-repeat;
264
+ }
265
+
266
+ .jqueryFileTree LI.ext_xls {
267
+ background : url(images/xls.png) left top no-repeat;
268
+ }
269
+
270
+ .jqueryFileTree LI.ext_xml {
271
+ background : url(images/code.png) left top no-repeat;
272
+ }
273
+
274
+ .jqueryFileTree LI.ext_zip {
275
+ background : url(images/zip.png) left top no-repeat;
276
+ }
core/modules/file-change/filetree/jqueryFileTree.js ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // jQuery File Tree Plugin
2
+ //
3
+ // Abandoned; new maintainer?: See https://github.com/daverogers/jQueryFileTree
4
+ //
5
+ // Version 1.01
6
+ //
7
+ // Cory S.N. LaViska
8
+ // A Beautiful Site (http://abeautifulsite.net/)
9
+ // 24 March 2008
10
+ //
11
+ // Visit http://abeautifulsite.net/notebook.php?article=58 for more information
12
+ //
13
+ // Usage: $('.fileTreeDemo').fileTree( options, callback )
14
+ //
15
+ // Options: root - root folder to display; default = /
16
+ // script - location of the serverside AJAX file to use; default = jqueryFileTree.php
17
+ // folderEvent - event to trigger expand/collapse; default = click
18
+ // expandSpeed - default = 500 (ms); use -1 for no animation
19
+ // collapseSpeed - default = 500 (ms); use -1 for no animation
20
+ // expandEasing - easing function to use on expand (optional)
21
+ // collapseEasing - easing function to use on collapse (optional)
22
+ // multiFolder - whether or not to limit the browser to one subfolder at a time
23
+ // loadMessage - Message to display while initial tree loads (can be HTML)
24
+ //
25
+ // History:
26
+ //
27
+ // 1.01 - updated to work with foreign characters in directory/file names (12 April 2008)
28
+ // 1.00 - released (24 March 2008)
29
+ //
30
+ // TERMS OF USE
31
+ //
32
+ // This plugin is dual-licensed under the GNU General Public License and the MIT License and
33
+ // is copyright 2008 A Beautiful Site, LLC.
34
+ //
35
+ if ( jQuery ) (function ( $ ) {
36
+
37
+ $.extend( $.fn, {
38
+ fileTree: function ( o, h, j ) {
39
+ // Defaults
40
+ if ( ! o ) var o = {};
41
+ if ( o.root == undefined ) o.root = '/';
42
+ if ( o.script == undefined ) o.script = 'jqueryFileTree.php';
43
+ if ( o.folderEvent == undefined ) o.folderEvent = 'click';
44
+ if ( o.expandSpeed == undefined ) o.expandSpeed = 500;
45
+ if ( o.collapseSpeed == undefined ) o.collapseSpeed = 500;
46
+ if ( o.expandEasing == undefined ) o.expandEasing = null;
47
+ if ( o.collapseEasing == undefined ) o.collapseEasing = null;
48
+ if ( o.multiFolder == undefined ) o.multiFolder = true;
49
+ if ( o.loadMessage == undefined ) o.loadMessage = 'Loading...';
50
+
51
+ $( this ).each( function () {
52
+
53
+ function showTree( c, t ) {
54
+ $( c ).addClass( 'wait' );
55
+ $( ".jqueryFileTree.start" ).remove();
56
+
57
+ var send_data = {
58
+ action: 'itsec_jquery_filetree_ajax',
59
+ nonce: itsec_jquery_filetree.nonce,
60
+ dir: t
61
+ };
62
+
63
+ $.post( o.script, send_data, function ( data ) {
64
+ $( c ).find( '.start' ).html( '' );
65
+ $( c ).removeClass( 'wait' ).append( data );
66
+ if ( o.root == t ) $( c ).find( 'UL:hidden' ).show(); else $( c ).find( 'UL:hidden' ).slideDown( { duration: o.expandSpeed, easing: o.expandEasing } );
67
+ bindTree( c );
68
+ } );
69
+ }
70
+
71
+ function bindTree( t ) {
72
+ $( t ).find( 'LI A' ).bind( o.folderEvent, function ( e ) {
73
+ if ( $( this ).parent().hasClass( 'directory' ) ) {
74
+ // If control key held on click, then add to list and dont expandcontract.
75
+ if ( $( e.target ).is( 'img' ) ) {
76
+ j( $( this ).attr( 'rel' ) );
77
+ return false;
78
+ }
79
+ if ( $( this ).parent().hasClass( 'collapsed' ) ) {
80
+ // Expand
81
+ if ( ! o.multiFolder ) {
82
+ $( this ).parent().parent().find( 'UL' ).slideUp( { duration: o.collapseSpeed, easing: o.collapseEasing } );
83
+ $( this ).parent().parent().find( 'LI.directory' ).removeClass( 'expanded' ).addClass( 'collapsed' );
84
+ }
85
+ $( this ).parent().find( 'UL' ).remove(); // cleanup
86
+ showTree( $( this ).parent(), escape( $( this ).attr( 'rel' ).match( /.*\// ) ) );
87
+ $( this ).parent().removeClass( 'collapsed' ).addClass( 'expanded' );
88
+ } else {
89
+ // Collapse
90
+ $( this ).parent().find( 'UL' ).slideUp( { duration: o.collapseSpeed, easing: o.collapseEasing } );
91
+ $( this ).parent().removeClass( 'expanded' ).addClass( 'collapsed' );
92
+ }
93
+ } else {
94
+ if ( $( this ).parent().hasClass( 'file' ) ) { // If file then we dont want to exclude on click, only when hitting the minus image next to it.
95
+ if ( ! $( e.target ).is( 'img' ) ) {
96
+ return false;
97
+ }
98
+ }
99
+ h( $( this ).attr( 'rel' ) );
100
+ }
101
+ return false;
102
+ } );
103
+ // Prevent A from triggering the # on non-click events
104
+ if ( o.folderEvent.toLowerCase != 'click' ) $( t ).find( 'LI A' ).bind( 'click', function () {
105
+ return false;
106
+ } );
107
+ }
108
+
109
+ // Loading message
110
+ $( this ).html( '<ul class="jqueryFileTree start"><li class="wait">' + o.loadMessage + '<li></ul>' );
111
+ // Get the initial file list
112
+ showTree( $( this ), escape( o.root ) );
113
+ } );
114
+ }
115
+ } );
116
+
117
+ })( jQuery );
core/modules/file-change/images/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/images/redminus.png ADDED
Binary file
core/modules/file-change/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/js/admin-file-change-warning.js ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function ( $ ) {
2
+
3
+ /**
4
+ * process the action button on the warning that a file change has been detected
5
+ */
6
+ $( '#itsec_go_to_logs, #itsec_dismiss_file_change_warning' ).click( function ( event ) {
7
+
8
+ event.preventDefault();
9
+
10
+ var button = this.value;
11
+
12
+ var data = {
13
+ action: 'itsec_file_change_warning_ajax',
14
+ nonce : itsec_file_change_warning.nonce
15
+ };
16
+
17
+ //call the ajax
18
+ $.post( ajaxurl, data, function () {
19
+
20
+ $( '#itsec_file_change_warning_dialog' ).remove();
21
+
22
+ if ( button == 'View Logs' ) {
23
+ window.location.replace( itsec_file_change_warning.url )
24
+ }
25
+
26
+ } );
27
+
28
+ } );
29
+
30
+ } );
31
+
32
+
33
+
core/modules/file-change/js/admin-file-change.js ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function ( $ ) {
2
+
3
+ /**
4
+ * Show the file change settings when file change detection is enabled.
5
+ */
6
+ $( "#itsec_file_change_enabled" ).change( function () {
7
+
8
+ if ( $( "#itsec_file_change_enabled" ).is( ':checked' ) ) {
9
+
10
+ $( "#file_change-settings" ).show();
11
+
12
+ } else {
13
+
14
+ $( "#file_change-settings" ).hide();
15
+
16
+ }
17
+
18
+ } ).change();
19
+
20
+ /**
21
+ * Warns the user that they probably don't have enough RAM to perform a file scan
22
+ */
23
+ if ( itsec_file_change.mem_limit <= 128 ) {
24
+
25
+ $( "#itsec_file_change_enabled" ).change( function () {
26
+
27
+ if ( this.checked ) {
28
+ alert( itsec_file_change.text );
29
+
30
+ }
31
+
32
+ } );
33
+
34
+ }
35
+
36
+ /**
37
+ * Show the file tree in the settings.
38
+ */
39
+ $( '.jquery_file_tree' ).fileTree(
40
+ {
41
+ root : itsec_file_change.ABSPATH,
42
+ script : ajaxurl,
43
+ expandSpeed : - 1,
44
+ collapseSpeed: - 1,
45
+ multiFolder : false
46
+
47
+ }, function ( file ) {
48
+
49
+ $( '#itsec_file_change_file_list' ).val( file.substring( itsec_file_change.ABSPATH.length ) + "\n" + $( '#itsec_file_change_file_list' ).val() );
50
+
51
+ }, function ( directory ) {
52
+
53
+ $( '#itsec_file_change_file_list' ).val( directory.substring( itsec_file_change.ABSPATH.length ) + "\n" + $( '#itsec_file_change_file_list' ).val() );
54
+
55
+ }
56
+ );
57
+
58
+ /**
59
+ * Performs a one-time file scan
60
+ */
61
+ $( '#itsec_one_time_file_check' ).submit( function ( event ) {
62
+
63
+ event.preventDefault();
64
+
65
+ var data = {
66
+ action: 'itsec_file_change_ajax',
67
+ nonce : itsec_file_change.nonce
68
+ };
69
+
70
+ //let user know we're working
71
+ $( "#itsec_one_time_file_check_submit" ).removeClass( 'button-primary' ).addClass( 'button-secondary' ).attr( 'value', itsec_file_change.scanning_button_text );
72
+
73
+ //call the ajax
74
+ $.ajax(
75
+ {
76
+ url : ajaxurl,
77
+ type : 'POST',
78
+ data : data,
79
+ complete: function ( response ) {
80
+
81
+ if ( response.responseText == 1 || response.responseText == - 1 ) {
82
+ window.location.replace( 'admin.php?page=toplevel_page_itsec_logs&itsec_log_filter=file_change' )
83
+ }
84
+
85
+ $( "#itsec_one_time_file_check_submit" ).removeClass( 'button-secondary' ).addClass( 'button-primary' ).attr( 'value', itsec_file_change.button_text );
86
+
87
+ if ( response.responseText == 0 ) {
88
+ $( "#itsec_one_time_file_check_submit" ).hide();
89
+ $( "#itsec_file_change_status" ).show().find( 'p' ).text( itsec_file_change.no_changes );
90
+ }
91
+
92
+ }
93
+ }
94
+ );
95
+
96
+ } );
97
+
98
+ } );
99
+
100
+ jQuery( window ).load( function () {
101
+
102
+ /**
103
+ * Shows and hides the red selector icon on the file tree allowing users to select an
104
+ * individual element.
105
+ */
106
+ jQuery( document ).on( 'mouseover mouseout', '.jqueryFileTree > li a', function ( event ) {
107
+
108
+ if ( event.type == 'mouseover' ) {
109
+
110
+ jQuery( this ).children( '.itsec_treeselect_control' ).css( 'visibility', 'visible' );
111
+
112
+ } else {
113
+
114
+ jQuery( this ).children( '.itsec_treeselect_control' ).css( 'visibility', 'hidden' );
115
+
116
+ }
117
+
118
+ } );
119
+
120
+ } );
core/modules/file-change/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/file-change/setup.php ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_File_Change_Setup' ) ) {
4
+
5
+ class ITSEC_File_Change_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'split' => false,
17
+ 'file_list' => array(),
18
+ 'method' => true,
19
+ 'types' => array(
20
+ '.jpg',
21
+ '.jpeg',
22
+ '.png',
23
+ '.log',
24
+ '.mo',
25
+ '.po'
26
+ ),
27
+ 'email' => true,
28
+ 'last_run' => 0,
29
+ 'notify_admin' => true,
30
+ );
31
+
32
+ if ( isset( $itsec_setup_action ) ) {
33
+
34
+ switch ( $itsec_setup_action ) {
35
+
36
+ case 'activate':
37
+ $this->execute_activate();
38
+ break;
39
+ case 'upgrade':
40
+ $this->execute_upgrade();
41
+ break;
42
+ case 'deactivate':
43
+ $this->execute_deactivate();
44
+ break;
45
+ case 'uninstall':
46
+ $this->execute_uninstall();
47
+ break;
48
+
49
+ }
50
+
51
+ } else {
52
+ wp_die( 'error' );
53
+ }
54
+
55
+ }
56
+
57
+ /**
58
+ * Execute module activation.
59
+ *
60
+ * @since 4.0
61
+ *
62
+ * @return void
63
+ */
64
+ public function execute_activate() {
65
+
66
+ $options = get_site_option( 'itsec_file_change' );
67
+
68
+ if ( $options === false ) {
69
+
70
+ add_site_option( 'itsec_file_change', $this->defaults );
71
+
72
+ }
73
+
74
+ }
75
+
76
+ /**
77
+ * Execute module deactivation
78
+ *
79
+ * @return void
80
+ */
81
+ public function execute_deactivate() {
82
+
83
+ wp_clear_scheduled_hook( 'itsec_file_check' );
84
+
85
+ }
86
+
87
+ /**
88
+ * Execute module uninstall
89
+ *
90
+ * @return void
91
+ */
92
+ public function execute_uninstall() {
93
+
94
+ $this->execute_deactivate();
95
+
96
+ delete_site_option( 'itsec_file_change' );
97
+ delete_site_option( 'itsec_local_file_list' );
98
+ delete_site_option( 'itsec_local_file_list_0' );
99
+ delete_site_option( 'itsec_local_file_list_1' );
100
+ delete_site_option( 'itsec_local_file_list_2' );
101
+ delete_site_option( 'itsec_local_file_list_3' );
102
+ delete_site_option( 'itsec_local_file_list_4' );
103
+ delete_site_option( 'itsec_local_file_list_5' );
104
+ delete_site_option( 'itsec_local_file_list_6' );
105
+ delete_site_option( 'itsec_file_change_warning' );
106
+
107
+ }
108
+
109
+ /**
110
+ * Execute module upgrade
111
+ *
112
+ * @return void
113
+ */
114
+ public function execute_upgrade() {
115
+
116
+ global $itsec_old_version;
117
+
118
+ if ( $itsec_old_version < 4000 ) {
119
+
120
+ global $itsec_bwps_options;
121
+
122
+ $current_options = get_site_option( 'itsec_file_change' );
123
+
124
+ if ( $current_options === false ) {
125
+ $current_options = $this->defaults;
126
+ }
127
+
128
+ $current_options['enabled'] = isset( $itsec_bwps_options['id_fileenabled'] ) && $itsec_bwps_options['id_fileenabled'] == 1 ? true : false;
129
+ $current_options['email'] = isset( $itsec_bwps_options['id_fileemailnotify'] ) && $itsec_bwps_options['id_fileemailnotify'] == 0 ? false : true;
130
+ $current_options['notify_admin'] = isset( $itsec_bwps_options['id_filedisplayerror'] ) && $itsec_bwps_options['id_filedisplayerror'] == 0 ? false : true;
131
+ $current_options['method'] = isset( $itsec_bwps_options['id_fileincex'] ) && $itsec_bwps_options['id_fileincex'] == 0 ? false : true;
132
+
133
+ if ( isset( $itsec_bwps_options['id_specialfile'] ) && ! is_array( $itsec_bwps_options['id_specialfile'] ) && strlen( $itsec_bwps_options['id_specialfile'] ) > 1 ) {
134
+
135
+ $current_options['file_list'] .= explode( PHP_EOL, $itsec_bwps_options['id_specialfile'] );
136
+
137
+ }
138
+
139
+ update_site_option( 'itsec_file_change', $current_options );
140
+
141
+ }
142
+
143
+ if ( $itsec_old_version < 4028 ) {
144
+
145
+ if ( ! is_multisite() ) {
146
+
147
+ $options = array(
148
+ 'itsec_local_file_list',
149
+ 'itsec_local_file_list_0',
150
+ 'itsec_local_file_list_1',
151
+ 'itsec_local_file_list_2',
152
+ 'itsec_local_file_list_3',
153
+ 'itsec_local_file_list_4',
154
+ 'itsec_local_file_list_5',
155
+ 'itsec_local_file_list_6',
156
+ );
157
+
158
+ foreach ( $options as $option ) {
159
+
160
+ $list = get_site_option( $option );
161
+
162
+ if ( $list !== false ) {
163
+
164
+ delete_site_option( $option );
165
+ add_option( $option, $list, '', 'no' );
166
+
167
+ }
168
+
169
+ }
170
+
171
+ }
172
+
173
+ }
174
+
175
+ }
176
+
177
+ }
178
+
179
+ }
180
+
181
+ new ITSEC_File_Change_Setup();
core/modules/four-oh-four/class-itsec-four-oh-four-admin.php ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Four_Oh_Four_Admin {
4
+
5
+ private
6
+ $default_white_list,
7
+ $settings,
8
+ $core,
9
+ $module_path;
10
+
11
+ function run( $core ) {
12
+
13
+ $this->core = $core;
14
+ $this->settings = get_site_option( 'itsec_four_oh_four' );
15
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
16
+
17
+ $this->default_white_list = array(
18
+ '/favicon.ico',
19
+ '/robots.txt',
20
+ '/apple-touch-icon.png',
21
+ '/apple-touch-icon-precomposed.png',
22
+ );
23
+
24
+ add_action( 'itsec_add_admin_meta_boxes', array(
25
+ $this, 'add_admin_meta_boxes'
26
+ ) ); //add meta boxes to admin page
27
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
28
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
29
+ add_filter( 'itsec_add_dashboard_status', array(
30
+ $this, 'dashboard_status'
31
+ ) ); //add information for plugin status
32
+ add_filter( 'itsec_logger_displays', array( $this, 'register_logger_displays' ) ); //adds logs metaboxes
33
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
34
+
35
+ //manually save options on multisite
36
+ if ( is_multisite() ) {
37
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
38
+ }
39
+
40
+ }
41
+
42
+ /**
43
+ * Add meta boxes to primary options pages
44
+ *
45
+ * @return void
46
+ */
47
+ public function add_admin_meta_boxes() {
48
+
49
+ $id = 'intrusion_detection_404_options';
50
+ $title = __( '404 Detection', 'better-wp-security' );
51
+
52
+ add_meta_box(
53
+ $id,
54
+ $title,
55
+ array( $this, 'metabox_advanced_four_oh_four_settings' ),
56
+ 'security_page_toplevel_page_itsec_settings',
57
+ 'advanced',
58
+ 'core'
59
+ );
60
+
61
+ $this->core->add_toc_item(
62
+ array(
63
+ 'id' => $id,
64
+ 'title' => $title,
65
+ )
66
+ );
67
+
68
+ }
69
+
70
+ /**
71
+ * Add Files Admin Javascript
72
+ *
73
+ * @since 4.0
74
+ *
75
+ * @return void
76
+ */
77
+ public function admin_script() {
78
+
79
+ global $itsec_globals;
80
+
81
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id,
82
+ 'security_page_toplevel_page_itsec_settings' ) !== false
83
+ ) {
84
+
85
+ wp_enqueue_script( 'itsec_four_oh_four_js', $this->module_path . 'js/admin-four-oh-four.js',
86
+ array( 'jquery' ), $itsec_globals['plugin_build'] );
87
+
88
+ }
89
+
90
+ }
91
+
92
+ /**
93
+ * Sets the status in the plugin dashboard
94
+ *
95
+ * @since 4.0
96
+ *
97
+ * @return array statuses
98
+ */
99
+ public function dashboard_status( $statuses ) {
100
+
101
+ if ( $this->settings['enabled'] === true ) {
102
+
103
+ $status_array = 'safe-medium';
104
+ $status = array(
105
+ 'text' => __( 'Your site is protecting against bots looking for known vulnerabilities.', 'better-wp-security' ),
106
+ 'link' => '#itsec_four_oh_four_enabled',
107
+ );
108
+
109
+ } else {
110
+
111
+ $status_array = 'medium';
112
+ $status = array(
113
+ 'text' => __( 'Your website is not protected against bots looking for known vulnerabilities. Consider turning on 404 protection.',
114
+ 'better-wp-security' ), 'link' => '#itsec_four_oh_four_enabled',
115
+ );
116
+
117
+ }
118
+
119
+ array_push( $statuses[$status_array], $status );
120
+
121
+ return $statuses;
122
+
123
+ }
124
+
125
+ /**
126
+ * echos Check Period Field
127
+ *
128
+ * @param array $args field arguments
129
+ *
130
+ * @return void
131
+ */
132
+ public function check_period() {
133
+
134
+ if ( isset( $this->settings['check_period'] ) ) {
135
+ $check_period = absint( $this->settings['check_period'] );
136
+ } else {
137
+ $check_period = 5;
138
+ }
139
+
140
+ $content = '<input class="small-text" name="itsec_four_oh_four[check_period]" id="itsec_four_oh_four_check_period" value="' . $check_period . '" type="text"> ';
141
+ $content .= '<label for="itsec_four_oh_four_check_period"> ' . __( 'Minutes', 'better-wp-security' ) . '</label>';
142
+ $content .= '<p class="description"> ' . __( 'The number of minutes in which 404 errors should be remembered and counted towards lockouts.',
143
+ 'better-wp-security' ) . '</p>';
144
+
145
+ echo $content;
146
+
147
+ }
148
+
149
+ /**
150
+ * echos Enable 404 Detection Field
151
+ *
152
+ * @param array $args field arguments
153
+ *
154
+ * @return void
155
+ */
156
+ public function enabled() {
157
+
158
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
159
+
160
+ $adminurl = is_multisite() ? admin_url() . 'network/' : admin_url();
161
+
162
+ $content = sprintf( '<p class="noPermalinks">%s <a href="%soptions-permalink.php">%s</a> %s</p>',
163
+ __( 'You must turn on', 'better-wp-security' ), $adminurl, __( 'WordPress permalinks', 'better-wp-security' ),
164
+ __( 'to use this feature.', 'better-wp-security' ) );
165
+
166
+ } else {
167
+
168
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
169
+ $enabled = 1;
170
+ } else {
171
+ $enabled = 0;
172
+ }
173
+
174
+ $content = '<input type="checkbox" id="itsec_four_oh_four_enabled" name="itsec_four_oh_four[enabled]" value="1" ' . checked( 1,
175
+ $enabled,
176
+ false ) . '/>';
177
+ $content .= '<label for="itsec_four_oh_four_enabled"> ' . __( 'Enable 404 detection', 'better-wp-security' ) . '</label>';
178
+
179
+ }
180
+
181
+ echo $content;
182
+
183
+ }
184
+
185
+ /**
186
+ * echos Error Threshold Field
187
+ *
188
+ * @param array $args field arguments
189
+ *
190
+ * @return void
191
+ */
192
+ public function error_threshold() {
193
+
194
+ if ( isset( $this->settings['error_threshold'] ) ) {
195
+ $error_threshold = absint( $this->settings['error_threshold'] );
196
+ } else {
197
+ $error_threshold = 20;
198
+ }
199
+
200
+ $content = '<input class="small-text" name="itsec_four_oh_four[error_threshold]" id="itsec_four_oh_four_error_threshold" value="' . $error_threshold . '" type="text"> ';
201
+ $content .= '<label for="itsec_four_oh_four_error_threshold"> ' . __( 'Errors', 'better-wp-security' ) . '</label>';
202
+ $content .= '<p class="description"> ' . __( 'The numbers of errors (within the check period time frame) that will trigger a lockout. Set to zero (0) to record 404 errors without locking out users. This can be useful for troubleshooting content or other errors. The default is 20.',
203
+ 'better-wp-security' ) . '</p>';
204
+
205
+ echo $content;
206
+
207
+ }
208
+
209
+ /**
210
+ * Render the settings metabox
211
+ *
212
+ * @return void
213
+ */
214
+ public function logs_metabox_content() {
215
+
216
+ if ( ! class_exists( 'ITSEC_Four_Oh_Four_Log' ) ) {
217
+ require( dirname( __FILE__ ) . '/class-itsec-four-oh-four-log.php' );
218
+ }
219
+
220
+ $log_display = new ITSEC_Four_Oh_Four_Log();
221
+
222
+ $log_display->prepare_items();
223
+ $log_display->display();
224
+
225
+ }
226
+
227
+ /**
228
+ * echos 404 white list field
229
+ *
230
+ * @since 4.0
231
+ *
232
+ * @return void
233
+ */
234
+ public function white_list() {
235
+
236
+ if ( isset( $this->settings['white_list'] ) && is_array( $this->settings['white_list'] ) ) {
237
+ $white_list = implode( PHP_EOL, $this->settings['white_list'] );
238
+ } else {
239
+ $white_list = implode( PHP_EOL, $this->default_white_list );
240
+ }
241
+
242
+ $content = '<textarea id="itsec_four_oh_four_white_list" name="itsec_four_oh_four[white_list]" rows="10" cols="50">' . $white_list . PHP_EOL . '</textarea>';
243
+ $content .= '<p class="description">' . __( 'Use the white list above to prevent recording common 404 errors. If you know a common file on your site is missing and you do not want it to count towards a lockout record it here. You must list the full path beginning with the "/"', 'better-wp-security' ) . '</p>';
244
+
245
+ echo $content;
246
+
247
+ }
248
+
249
+ /**
250
+ * Execute admin initializations
251
+ *
252
+ * @return void
253
+ */
254
+ public function initialize_admin() {
255
+
256
+ //Add Settings sections
257
+ add_settings_section(
258
+ 'four_oh_four-enabled',
259
+ __( 'Enable 404 Detection', 'better-wp-security' ),
260
+ '__return_empty_string',
261
+ 'security_page_toplevel_page_itsec_settings'
262
+ );
263
+
264
+ add_settings_section(
265
+ 'four_oh_four-settings',
266
+ __( '404 Detection Settings', 'better-wp-security' ),
267
+ '__return_empty_string',
268
+ 'security_page_toplevel_page_itsec_settings'
269
+ );
270
+
271
+ //404 Detection Fields
272
+ add_settings_field(
273
+ 'itsec_four_oh_four[enabled]',
274
+ __( '404 Detection', 'better-wp-security' ),
275
+ array( $this, 'enabled' ),
276
+ 'security_page_toplevel_page_itsec_settings',
277
+ 'four_oh_four-enabled'
278
+ );
279
+
280
+ add_settings_field(
281
+ 'itsec_four_oh_four[check_period]',
282
+ __( 'Minutes to Remember 404 Error (Check Period)', 'better-wp-security' ),
283
+ array( $this, 'check_period' ),
284
+ 'security_page_toplevel_page_itsec_settings',
285
+ 'four_oh_four-settings'
286
+ );
287
+
288
+ add_settings_field(
289
+ 'itsec_four_oh_four[error_threshold]',
290
+ __( 'Error Threshold', 'better-wp-security' ),
291
+ array( $this, 'error_threshold' ),
292
+ 'security_page_toplevel_page_itsec_settings',
293
+ 'four_oh_four-settings'
294
+ );
295
+
296
+ add_settings_field(
297
+ 'itsec_four_oh_four[white_list]',
298
+ __( '404 File/Folder White List', 'better-wp-security' ),
299
+ array( $this, 'white_list' ),
300
+ 'security_page_toplevel_page_itsec_settings',
301
+ 'four_oh_four-settings'
302
+ );
303
+
304
+ add_settings_field(
305
+ 'itsec_four_oh_four[types]',
306
+ __( 'Ignored File Types', 'better-wp-security' ),
307
+ array( $this, 'types' ),
308
+ 'security_page_toplevel_page_itsec_settings',
309
+ 'four_oh_four-settings'
310
+ );
311
+
312
+ //Register the settings field for the entire module
313
+ register_setting(
314
+ 'security_page_toplevel_page_itsec_settings',
315
+ 'itsec_four_oh_four',
316
+ array( $this, 'sanitize_module_input' )
317
+ );
318
+
319
+ }
320
+
321
+ /**
322
+ * Render the settings metabox
323
+ *
324
+ * @return void
325
+ */
326
+ public function metabox_advanced_four_oh_four_settings() {
327
+
328
+ global $itsec_lockout;
329
+
330
+ echo '<p>' . __( '404 detection looks at a user who is hitting a large number of non-existent pages and getting a large number of 404 errors. 404 detection assumes that a user who hits a lot of 404 errors in a short period of time is scanning for something (presumably a vulnerability) and locks them out accordingly. This also gives the added benefit of helping you find hidden problems causing 404 errors on unseen parts of your site. All errors will be logged in the \"View Logs\" page. You can set thresholds for this feature below.', 'better-wp-security' ) . '</p>';
331
+ echo $itsec_lockout->get_lockout_description();
332
+
333
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'four_oh_four-enabled', false );
334
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'four_oh_four-settings',
335
+ false );
336
+
337
+ echo '<p>' . PHP_EOL;
338
+
339
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
340
+
341
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes',
342
+ 'better-wp-security' ) . '" />' . PHP_EOL;
343
+
344
+ echo '</p>' . PHP_EOL;
345
+
346
+ }
347
+
348
+ /**
349
+ * Array of displays for the logs screen
350
+ *
351
+ * @since 4.0
352
+ *
353
+ * @param array $logger_displays metabox array
354
+ *
355
+ * @return array metabox array
356
+ */
357
+ public function register_logger_displays( $logger_displays ) {
358
+
359
+ //Don't attempt to display logs if brute force isn't enabled
360
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
361
+
362
+ $logger_displays[] = array(
363
+ 'module' => 'four_oh_four',
364
+ 'title' => __( '404 Errors Found', 'better-wp-security' ),
365
+ 'callback' => array( $this, 'logs_metabox_content' )
366
+ );
367
+
368
+ }
369
+
370
+ return $logger_displays;
371
+
372
+ }
373
+
374
+ /**
375
+ * Sanitize and validate input
376
+ *
377
+ * @param Array $input array of input fields
378
+ *
379
+ * @return Array Sanitized array
380
+ */
381
+ public function sanitize_module_input( $input ) {
382
+
383
+ //process brute force settings
384
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
385
+ $input['check_period'] = isset( $input['check_period'] ) ? absint( $input['check_period'] ) : 5;
386
+ $input['error_threshold'] = isset( $input['error_threshold'] ) ? absint( $input['error_threshold'] ) : 20;
387
+
388
+ if ( isset ( $input['white_list'] ) ) {
389
+
390
+ if ( ! is_array( $input['white_list'] ) ) {
391
+ $raw_paths = explode( PHP_EOL, $input['white_list'] );
392
+ } else {
393
+ $raw_paths = $input['white_list'];
394
+ }
395
+
396
+ $good_paths = array();
397
+
398
+ foreach ( $raw_paths as $path ) {
399
+
400
+ $path = sanitize_text_field( trim( $path ) );
401
+
402
+ if ( strlen( $path ) > 0 && $path[0] != '/' ) {
403
+ $path = '/' . $path;
404
+ }
405
+
406
+ if ( strlen( $path ) > 1 ) {
407
+ $good_paths[] = $path;
408
+ }
409
+
410
+ }
411
+
412
+ $input['white_list'] = $good_paths;
413
+
414
+ } else {
415
+
416
+ $input['white_list'] = array();
417
+
418
+ }
419
+
420
+ if ( ! is_array( $input['types'] ) ) {
421
+ $file_types = explode( PHP_EOL, $input['types'] );
422
+ } else {
423
+ $file_types = $input['types'];
424
+ }
425
+
426
+ $good_types = array();
427
+
428
+ foreach ( $file_types as $file_type ) {
429
+
430
+ $file_type = trim( $file_type );
431
+
432
+ if ( strlen( $file_type ) > 0 && $file_type != '.' ) {
433
+
434
+ $good_type = sanitize_text_field( '.' . str_replace( '.', '', $file_type ) );
435
+
436
+ $good_types[] = sanitize_text_field( trim( $good_type ) );
437
+
438
+ }
439
+ }
440
+
441
+ $input['types'] = $good_types;
442
+
443
+ if ( is_multisite() ) {
444
+
445
+ $this->core->show_network_admin_notice( false );
446
+
447
+ $this->settings = $input;
448
+
449
+ }
450
+
451
+ return $input;
452
+
453
+ }
454
+
455
+ /**
456
+ * Prepare and save options in network settings
457
+ *
458
+ * @return void
459
+ */
460
+ public function save_network_options() {
461
+
462
+ if ( isset( $_POST['itsec_four_oh_four'] ) ) {
463
+
464
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
465
+ die( __( 'Security error!', 'better-wp-security' ) );
466
+ }
467
+
468
+ update_site_option( 'itsec_four_oh_four',
469
+ $_POST['itsec_four_oh_four'] ); //we must manually save network options
470
+
471
+ }
472
+
473
+ }
474
+
475
+ /**
476
+ * Adds fields that will be tracked for Google Analytics
477
+ *
478
+ * @since 4.0
479
+ *
480
+ * @param array $vars tracking vars
481
+ *
482
+ * @return array tracking vars
483
+ */
484
+ public function tracking_vars( $vars ) {
485
+
486
+ $vars['itsec_four_oh_four'] = array(
487
+ 'enabled' => '0:b',
488
+ );
489
+
490
+ return $vars;
491
+
492
+ }
493
+
494
+ /**
495
+ * echos 404 file types Field
496
+ *
497
+ * @since 4.5
498
+ *
499
+ * @return void
500
+ */
501
+ public function types() {
502
+
503
+ if ( isset( $this->settings['types'] ) && is_array( $this->settings['types'] ) ) {
504
+ $types = implode( PHP_EOL, $this->settings['types'] );
505
+ } else {
506
+ $types = implode( PHP_EOL, array(
507
+ '.jpg',
508
+ '.jpeg',
509
+ '.png',
510
+ '.gif',
511
+ '.css',
512
+ ) );
513
+ }
514
+
515
+ echo '<textarea id="itsec_four_oh_four_types" name="itsec_four_oh_four[types]" wrap="off" cols="20" rows="10">' . $types . PHP_EOL . '</textarea><br />';
516
+ echo '<label for="itsec_four_oh_four_types"> ' . __( 'File types listed here will be recorded as 404 errors but will not lead to lockouts.', 'better-wp-security' ) . '</label>';
517
+
518
+ }
519
+
520
+ }
core/modules/four-oh-four/class-itsec-four-oh-four-log.php ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Log 404 errors for Intrusion Detection Module
5
+ *
6
+ * @package iThemes-Security
7
+ * @subpackage Intrusion-Detection
8
+ * @since 4.0
9
+ */
10
+ final class ITSEC_Four_Oh_Four_Log extends ITSEC_WP_List_Table {
11
+
12
+ function __construct() {
13
+
14
+ parent::__construct(
15
+ array(
16
+ 'singular' => 'itsec_four_oh_four_log_item',
17
+ 'plural' => 'itsec_four_oh_four_log_items',
18
+ 'ajax' => true
19
+ )
20
+ );
21
+
22
+ }
23
+
24
+ /**
25
+ * Define first time column
26
+ *
27
+ * @param array $item array of row data
28
+ *
29
+ * @return string formatted output
30
+ *
31
+ **/
32
+ function column_first_time( $item ) {
33
+
34
+ return $item['first_time'];
35
+
36
+ }
37
+
38
+ /**
39
+ * Define time column
40
+ *
41
+ * @param array $item array of row data
42
+ *
43
+ * @return string formatted output
44
+ *
45
+ **/
46
+ function column_last_time( $item ) {
47
+
48
+ return $item['last_time'];
49
+
50
+ }
51
+
52
+ /**
53
+ * Define count column
54
+ *
55
+ * @param array $item array of row data
56
+ *
57
+ * @return string formatted output
58
+ *
59
+ **/
60
+ function column_count( $item ) {
61
+
62
+ return $item['count'];
63
+
64
+ }
65
+
66
+ /**
67
+ * Define uri column
68
+ *
69
+ * @param array $item array of row data
70
+ *
71
+ * @return string formatted output
72
+ *
73
+ **/
74
+ function column_uri( $item ) {
75
+
76
+ global $itsec_logger;
77
+
78
+ $items = $itsec_logger->get_events( 'four_oh_four', array( 'log_url' => $item['uri'] ) );
79
+
80
+ echo '<a href="itsec_404_details_' . $item['id'] . '" class="dialog">' . $item['uri'] . '</a>';
81
+
82
+ echo '<div id="itsec_404_details_' . $item['id'] . '" style="display:none;">';
83
+
84
+ echo '<h3>' . __( 'Details for ' . $item['uri'], 'better-wp-security' ) . '</h3>';
85
+
86
+ echo '<ol class="file_change_detail_list">';
87
+
88
+ foreach ( $items as $item => $details ) {
89
+ $data = maybe_unserialize( $details['log_data'] );
90
+ echo '<li class="404_detail"><strong>' . __( 'Time', 'better-wp-security' ) . '</strong>: ' . $details['log_date'] . '<br /><strong>' . __( 'Host', 'better-wp-security' ) . '</strong>: ' . $details['log_host'] . '<br /><strong>' . __( 'Referrer', 'better-wp-security' ) . '</strong>: ' . esc_html( $details['log_referrer'] ) . '<br /><strong>' . __( 'Query', 'better-wp-security' ) . '</strong>: ' . esc_html( $data['query_string'] ) . '</li>';
91
+ }
92
+
93
+ echo '</ol>';
94
+
95
+ echo '</div>';
96
+
97
+ }
98
+
99
+ /**
100
+ * Define Columns
101
+ *
102
+ * @return array array of column titles
103
+ */
104
+ public function get_columns() {
105
+
106
+ return array(
107
+ 'uri' => __( 'Location', 'better-wp-security' ),
108
+ 'count' => __( 'Count', 'better-wp-security' ),
109
+ 'first_time' => __( 'First Recorded', 'better-wp-security' ),
110
+ 'last_time' => __( 'Last Recorded', 'better-wp-security' ),
111
+ );
112
+
113
+ }
114
+
115
+ /**
116
+ * Define Sortable Columns
117
+ *
118
+ * @return array of column titles that can be sorted
119
+ */
120
+ public function get_sortable_columns() {
121
+
122
+ $order = ( empty( $_GET['order'] ) ) ? false : true;
123
+
124
+ $sortable_columns = array(
125
+ 'uri' => array( 'uri', $order ),
126
+ 'count' => array( 'count', $order ),
127
+ 'first_time' => array( 'first_time', $order ),
128
+ 'last_time' => array( 'last_time', $order ),
129
+ );
130
+
131
+ return $sortable_columns;
132
+
133
+ }
134
+
135
+ /**
136
+ * Prepare data for table
137
+ *
138
+ * @return void
139
+ */
140
+ public function prepare_items() {
141
+
142
+ global $itsec_logger;
143
+
144
+ $columns = $this->get_columns();
145
+ $hidden = array();
146
+ $sortable = $this->get_sortable_columns();
147
+ $this->_column_headers = array( $columns, $hidden, $sortable );
148
+
149
+ $items = $itsec_logger->get_events( 'four_oh_four' );
150
+
151
+ $table_data = array();
152
+
153
+ foreach ( $items as $item ) { //loop through and group 404s
154
+
155
+ if ( isset( $table_data[ $item['log_url'] ] ) ) {
156
+
157
+ $table_data[ $item['log_url'] ]['id'] = $item['log_id'];
158
+ $table_data[ $item['log_url'] ]['count'] = $table_data[ $item['log_url'] ]['count'] + 1;
159
+ $table_data[ $item['log_url'] ]['last_time'] = strtotime( $table_data[ $item['log_url'] ]['last_time'] ) > strtotime( $item['log_date'] ) ? $table_data[ $item['log_url'] ]['last_time'] : sanitize_text_field( $item['log_date'] );
160
+ $table_data[ $item['log_url'] ]['first_time'] = strtotime( $table_data[ $item['log_url'] ]['first_time'] ) < strtotime( $item['log_date'] ) ? $table_data[ $item['log_url'] ]['first_time'] : sanitize_text_field( $item['log_date'] );
161
+ $table_data[ $item['log_url'] ]['uri'] = sanitize_text_field( $item['log_url'] );
162
+
163
+ } else {
164
+
165
+ $table_data[ $item['log_url'] ]['id'] = $item['log_id'];
166
+ $table_data[ $item['log_url'] ]['count'] = 1;
167
+ $table_data[ $item['log_url'] ]['last_time'] = sanitize_text_field( $item['log_date'] );
168
+ $table_data[ $item['log_url'] ]['first_time'] = sanitize_text_field( $item['log_date'] );
169
+ $table_data[ $item['log_url'] ]['uri'] = sanitize_text_field( $item['log_url'] );
170
+
171
+ }
172
+
173
+ }
174
+
175
+ usort( $table_data, array( $this, 'sortrows' ) );
176
+
177
+ $per_page = 20; //20 items per page
178
+ $current_page = $this->get_pagenum();
179
+ $total_items = count( $table_data );
180
+
181
+ $table_data = array_slice( $table_data, ( ( $current_page - 1 ) * $per_page ), $per_page );
182
+
183
+ $this->items = $table_data;
184
+
185
+ $this->set_pagination_args(
186
+ array(
187
+ 'total_items' => $total_items,
188
+ 'per_page' => $per_page,
189
+ 'total_pages' => ceil( $total_items / $per_page )
190
+ )
191
+ );
192
+
193
+ }
194
+
195
+ /**
196
+ * Sorts rows by count in descending order
197
+ *
198
+ * @param array $a first array to compare
199
+ * @param array $b second array to compare
200
+ *
201
+ * @return int comparison result
202
+ */
203
+ function sortrows( $a, $b ) {
204
+
205
+ // If no sort, default to count
206
+ $orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'last_time';
207
+
208
+ // If no order, default to desc
209
+ $order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
210
+
211
+ if ( $orderby == 'count' ) {
212
+
213
+ if ( intval( $a[ $orderby ] ) < intval( $b[ $orderby ] ) ) {
214
+ $result = - 1;
215
+ } elseif ( intval( $a[ $orderby ] ) === intval( $b[ $orderby ] ) ) {
216
+ $result = 0;
217
+ } else {
218
+ $result = 1;
219
+ }
220
+
221
+ } else {
222
+
223
+ // Determine sort order
224
+ $result = strcmp( $a[ $orderby ], $b[ $orderby ] );
225
+
226
+ }
227
+
228
+ // Send final sort direction to usort
229
+ return ( $order === 'asc' ) ? $result : - $result;
230
+
231
+ }
232
+
233
+ }
core/modules/four-oh-four/class-itsec-four-oh-four.php ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Four_Oh_Four {
4
+
5
+ private
6
+ $settings;
7
+
8
+ function run() {
9
+
10
+ $this->settings = get_site_option( 'itsec_four_oh_four' );
11
+
12
+ add_filter( 'itsec_lockout_modules', array( $this, 'register_lockout' ) );
13
+ add_filter( 'itsec_logger_modules', array( $this, 'register_logger' ) );
14
+
15
+ add_action( 'wp_head', array( $this, 'check_404' ) );
16
+
17
+ }
18
+
19
+ /**
20
+ * If the page is a WordPress 404 error log it and register for lockout
21
+ *
22
+ * @return void
23
+ */
24
+ public function check_404() {
25
+
26
+ global $itsec_logger, $itsec_lockout;
27
+
28
+ if ( $this->settings['enabled'] === true && is_404() ) {
29
+
30
+ $uri = explode( '?', $_SERVER['REQUEST_URI'] );
31
+
32
+ if ( isset( $this->settings['white_list'] ) && ! is_array( $this->settings['white_list'] ) ) {
33
+ $this->settings['white_list'] = explode( PHP_EOL, $this->settings['white_list'] );
34
+ } elseif ( ! isset( $this->settings['white_list'] ) ) {
35
+ $this->settings['white_list'] = array();
36
+ }
37
+
38
+ if ( in_array( $uri[0], $this->settings['white_list'] ) === false ) {
39
+
40
+ $itsec_logger->log_event(
41
+ 'four_oh_four',
42
+ 3,
43
+ array(
44
+ 'query_string' => isset( $uri[1] ) ? esc_sql( $uri[1] ) : '',
45
+ ),
46
+ ITSEC_Lib::get_ip(),
47
+ '',
48
+ '',
49
+ esc_sql( $uri[0] ),
50
+ isset( $_SERVER['HTTP_REFERER'] ) ? esc_sql( $_SERVER['HTTP_REFERER'] ) : ''
51
+ );
52
+
53
+ $path_info = pathinfo( $uri[0] );
54
+
55
+ if ( ! isset( $path_info['extension'] ) || ( isset( $this->settings['types'] ) && is_array( $this->settings['types'] ) && in_array( '.' . $path_info['extension'], $this->settings['types'] ) === false ) ) {
56
+
57
+ $itsec_lockout->do_lockout( 'four_oh_four' );
58
+
59
+ }
60
+
61
+ }
62
+
63
+ }
64
+
65
+ }
66
+
67
+ /**
68
+ * Register 404 detection for lockout
69
+ *
70
+ * @param array $lockout_modules array of lockout modules
71
+ *
72
+ * @return array array of lockout modules
73
+ */
74
+ public function register_lockout( $lockout_modules ) {
75
+
76
+ if ( $this->settings['enabled'] === true ) {
77
+
78
+ $lockout_modules['four_oh_four'] = array(
79
+ 'type' => 'four_oh_four',
80
+ 'reason' => __( 'too many attempts to access a file that does not exist', 'better-wp-security' ),
81
+ 'host' => $this->settings['error_threshold'],
82
+ 'period' => $this->settings['check_period']
83
+ );
84
+
85
+ }
86
+
87
+ return $lockout_modules;
88
+
89
+ }
90
+
91
+ /**
92
+ * Register 404 and file change detection for logger
93
+ *
94
+ * @param array $logger_modules array of logger modules
95
+ *
96
+ * @return array array of logger modules
97
+ */
98
+ public function register_logger( $logger_modules ) {
99
+
100
+ if ( $this->settings['enabled'] === true ) {
101
+
102
+ $logger_modules['four_oh_four'] = array(
103
+ 'type' => 'four_oh_four',
104
+ 'function' => __( '404 Error', 'better-wp-security' ),
105
+ );
106
+
107
+ }
108
+
109
+ return $logger_modules;
110
+
111
+ }
112
+
113
+ }
core/modules/four-oh-four/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/four-oh-four/js/admin-four-oh-four.js ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_four_oh_four_enabled" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_four_oh_four_enabled" ).is( ':checked' ) ) {
6
+
7
+ jQuery( "#four_oh_four-settings" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#four_oh_four-settings" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ if ( jQuery( 'p.noPermalinks' ).length ) {
18
+ jQuery( "#four_oh_four-settings" ).hide();
19
+ }
20
+
21
+ } );
22
+
core/modules/four-oh-four/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/four-oh-four/setup.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Four_Oh_Four_Setup' ) ) {
4
+
5
+ class ITSEC_Four_Oh_Four_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'check_period' => 5,
17
+ 'error_threshold' => 20,
18
+ 'white_list' => array(
19
+ '/favicon.ico',
20
+ '/robots.txt',
21
+ '/apple-touch-icon.png',
22
+ '/apple-touch-icon-precomposed.png',
23
+ '/wp-content/cache',
24
+ '/browserconfig.xml',
25
+ '/crossdomain.xml',
26
+ '/labels.rdf',
27
+ '/trafficbasedsspsitemap.xml',
28
+ ),
29
+ 'types' => array(
30
+ '.jpg',
31
+ '.jpeg',
32
+ '.png',
33
+ '.gif',
34
+ '.css',
35
+ ),
36
+ );
37
+
38
+ if ( isset( $itsec_setup_action ) ) {
39
+
40
+ switch ( $itsec_setup_action ) {
41
+
42
+ case 'activate':
43
+ $this->execute_activate();
44
+ break;
45
+ case 'upgrade':
46
+ $this->execute_upgrade();
47
+ break;
48
+ case 'deactivate':
49
+ $this->execute_deactivate();
50
+ break;
51
+ case 'uninstall':
52
+ $this->execute_uninstall();
53
+ break;
54
+
55
+ }
56
+
57
+ } else {
58
+ wp_die( 'error' );
59
+ }
60
+
61
+ }
62
+
63
+ /**
64
+ * Execute module activation.
65
+ *
66
+ * @since 4.0
67
+ *
68
+ * @return void
69
+ */
70
+ public function execute_activate() {
71
+
72
+ $options = get_site_option( 'itsec_four_oh_four' );
73
+
74
+ if ( $options === false ) {
75
+
76
+ add_site_option( 'itsec_four_oh_four', $this->defaults );
77
+
78
+ }
79
+
80
+ }
81
+
82
+ /**
83
+ * Execute module deactivation
84
+ *
85
+ * @return void
86
+ */
87
+ public function execute_deactivate() {
88
+ }
89
+
90
+ /**
91
+ * Execute module uninstall
92
+ *
93
+ * @return void
94
+ */
95
+ public function execute_uninstall() {
96
+
97
+ $this->execute_deactivate();
98
+
99
+ delete_site_option( 'itsec_four_oh_four' );
100
+
101
+ }
102
+
103
+ /**
104
+ * Execute module upgrade
105
+ *
106
+ * @return void
107
+ */
108
+ public function execute_upgrade() {
109
+
110
+ global $itsec_old_version;
111
+
112
+ if ( $itsec_old_version < 4000 ) {
113
+
114
+ global $itsec_bwps_options;
115
+
116
+ $current_options = get_site_option( 'itsec_four_oh_four' );
117
+
118
+ if ( $current_options === false ) {
119
+ $current_options = $this->defaults;
120
+ }
121
+
122
+ $current_options['enabled'] = isset( $itsec_bwps_options['id_enabled'] ) && $itsec_bwps_options['id_enabled'] == 1 ? true : false;
123
+ $current_options['check_period'] = isset( $itsec_bwps_options['id_checkinterval'] ) ? intval( $itsec_bwps_options['id_checkinterval'] ) : 5;
124
+ $current_options['error_threshold'] = isset( $itsec_bwps_options['id_threshold'] ) ? intval( $itsec_bwps_options['id_threshold'] ) : 20;
125
+
126
+ if ( isset( $itsec_bwps_options['id_whitelist'] ) && ! is_array( $itsec_bwps_options['id_whitelist'] ) && strlen( $itsec_bwps_options['id_whitelist'] ) > 1 ) {
127
+
128
+ $current_options['white_list'] .= explode( PHP_EOL, $itsec_bwps_options['id_whitelist'] );
129
+
130
+ }
131
+
132
+ update_site_option( 'itsec_four_oh_four', $current_options );
133
+
134
+ }
135
+
136
+ }
137
+
138
+ }
139
+
140
+ }
141
+
142
+ new ITSEC_Four_Oh_Four_Setup();
core/modules/help/class-itsec-help-admin.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Help_Admin {
4
+
5
+ function run() {
6
+
7
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
8
+
9
+ }
10
+
11
+ /**
12
+ * Add meta boxes to primary options pages
13
+ *
14
+ */
15
+ public function add_admin_meta_boxes() {
16
+
17
+ add_meta_box(
18
+ 'itsec_help_info',
19
+ __( 'Help', 'better-wp-security' ),
20
+ array( $this, 'add_help_intro' ),
21
+ 'security_page_toplevel_page_itsec_help',
22
+ 'normal',
23
+ 'core'
24
+ );
25
+
26
+ }
27
+
28
+ /**
29
+ * Build and echo the away mode description
30
+ *
31
+ * @return void
32
+ */
33
+ public function add_help_intro() {
34
+
35
+ echo '<p>' . __( 'Website security is a complicated subject, but we have experts that can help.', 'better-wp-security' ) . '</p>';
36
+
37
+ echo '<p><strong>' . __( 'Community Support from WordPress.org', 'better-wp-security' ) . '</strong><br />';
38
+ echo __( 'Since you are using the free version of iThemes Security from WordPress.org, you can get free support from the WordPress community.', 'better-wp-security' ) . '</p>';
39
+ echo '<p><a class="button-secondary" href="http://wordpress.org/support/plugin/better-wp-security" target="_blank">' . __( 'Get Free Support', 'better-wp-security' ) . '</a></p>';
40
+ echo '<hr>';
41
+
42
+ echo '<p><strong>' . __( 'Support & Pro Features with iThemes Security Pro', 'better-wp-security' ) . '</strong><br />';
43
+ echo __( 'Get added peace of mind with professional support from our expert team and pro features to take your site security to the next level with iThemes Security Pro.', 'better-wp-security' ) . '</p>';
44
+ echo '<p><a class="button-secondary" href="http://www.ithemes.com/security" target="_blank">' . __( 'Get iThemes Security Pro', 'better-wp-security' ) . '</a></p>';
45
+ echo '<hr>';
46
+
47
+ echo '<p><strong>' . __( 'Hack Repair', 'better-wp-security' ) . '</strong><br />';
48
+ echo __( 'Has your site been hacked? Contact Sucuri, our recommended hack repair partner, to get things back in order.', 'better-wp-security' ) . '</p>';
49
+ echo '<p><a class="button-secondary" href="https://ithemes.com/sucuri" target="_blank">' . __( 'Get hack repair', 'better-wp-security' ) . '</a></p>';
50
+
51
+ }
52
+
53
+ }
core/modules/help/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/hide-backend/class-itsec-hide-backend-admin.php ADDED
@@ -0,0 +1,724 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Hide_Backend_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $module_path;
9
+
10
+ function run( $core ) {
11
+
12
+ $this->core = $core;
13
+ $this->settings = get_site_option( 'itsec_hide_backend' );
14
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
15
+
16
+ add_filter( 'itsec_file_modules', array( $this, 'register_file' ) ); //register tooltip action
17
+ add_filter( 'itsec_tooltip_modules', array( $this, 'register_tooltip' ) ); //register tooltip action
18
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
19
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
20
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
21
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'dashboard_status' ) ); //add information for plugin status
22
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
23
+
24
+ //manually save options on multisite
25
+ if ( is_multisite() ) {
26
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
27
+ }
28
+
29
+
30
+ add_filter( 'itsec_filter_apache_server_config_modification', array( $this, 'filter_apache_server_config_modification' ) );
31
+ add_filter( 'itsec_filter_litespeed_server_config_modification', array( $this, 'filter_apache_server_config_modification' ) );
32
+ add_filter( 'itsec_filter_nginx_server_config_modification', array( $this, 'filter_nginx_server_config_modification' ) );
33
+ }
34
+
35
+ /**
36
+ * Add meta boxes to primary options pages
37
+ *
38
+ * @return void
39
+ */
40
+ public function add_admin_meta_boxes() {
41
+
42
+ $id = 'hide_backend_options';
43
+ $title = __( 'Hide Login Area', 'better-wp-security' );
44
+
45
+ add_meta_box(
46
+ $id,
47
+ $title,
48
+ array( $this, 'metabox_hide_backend_settings' ),
49
+ 'security_page_toplevel_page_itsec_settings',
50
+ 'advanced',
51
+ 'core'
52
+ );
53
+
54
+ $this->core->add_toc_item(
55
+ array(
56
+ 'id' => $id,
57
+ 'title' => $title,
58
+ )
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Add Away mode Javascript
64
+ *
65
+ * @return void
66
+ */
67
+ public function admin_script() {
68
+
69
+ global $itsec_globals;
70
+
71
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
72
+
73
+ $new_slug = get_site_option( 'itsec_hide_backend_new_slug' );
74
+
75
+ if ( $new_slug !== false ) {
76
+
77
+ delete_site_option( 'itsec_hide_backend_new_slug' );
78
+
79
+ $new_slug = get_site_url() . '/' . $new_slug;
80
+
81
+ $slug_text = sprintf(
82
+ '%s%s%s%s%s',
83
+ __( 'Warning: Your admin URL has changed. Use the following URL to login to your site', 'better-wp-security' ),
84
+ PHP_EOL . PHP_EOL,
85
+ $new_slug,
86
+ PHP_EOL . PHP_EOL,
87
+ __( 'Please note this may be different than what you sent as the URL was sanitized to meet various requirements. A reminder has also been sent to the notification email(s) set in this plugins global settings.', 'better-wp-security' )
88
+ );
89
+
90
+ $this->send_new_slug( $new_slug );
91
+
92
+ } else {
93
+ $slug_text = false;
94
+ }
95
+
96
+ sprintf(
97
+ '%s %s %s',
98
+ __( 'Warning: Your admin URL has changed. Use the following URL to login to your site', 'better-wp-security' ),
99
+ get_site_url() . '/' . $new_slug,
100
+ __( 'Please note this may be different than what you sent as the URL was sanitized to meet various requirements.', 'better-wp-security' )
101
+ );
102
+
103
+ wp_enqueue_script( 'itsec_hide_backend_js', $this->module_path . 'js/admin-hide-backend.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
104
+ wp_localize_script(
105
+ 'itsec_hide_backend_js',
106
+ 'itsec_hide_backend',
107
+ array(
108
+ 'new_slug' => $slug_text,
109
+ )
110
+ );
111
+
112
+ }
113
+
114
+ }
115
+
116
+ public function filter_apache_server_config_modification( $modification ) {
117
+ $input = get_site_option( 'itsec_hide_backend' );
118
+
119
+ if ( true != $input['enabled'] ) {
120
+ return $modification;
121
+ }
122
+
123
+
124
+ $home_root = ITSEC_Lib::get_home_root();
125
+
126
+ $modification .= "\n";
127
+ $modification .= "\t# " . __( 'Enable the hide backend feature - Security > Settings > Hide Login Area > Hide Backend', 'better-wp-security' ) . "\n";
128
+ $modification .= "\tRewriteRule ^($home_root)?{$input['slug']}/?$ {$home_root}wp-login.php [QSA,L]\n";
129
+
130
+ if ( 'wp-register.php' != $input['register'] ) {
131
+ $modification .= "\tRewriteRule ^($home_root)?{$input['register']}/?$ /wplogin?action=register [QSA,L]\n";
132
+ }
133
+
134
+ return $modification;
135
+ }
136
+
137
+ public function filter_nginx_server_config_modification( $modification ) {
138
+ $input = get_site_option( 'itsec_hide_backend' );
139
+
140
+ if ( true != $input['enabled'] ) {
141
+ return $modification;
142
+ }
143
+
144
+
145
+ $home_root = ITSEC_Lib::get_home_root();
146
+
147
+ $modification .= "\n";
148
+ $modification .= "\t# " . __( 'Enable the hide backend feature - Security > Settings > Hide Login Area > Hide Backend', 'better-wp-security' ) . "\n";
149
+ $modification .= "\trewrite ^($home_root)?{$input['slug']}/?$ {$home_root}wp-login.php?\$query_string break;\n";
150
+
151
+ if ( 'wp-register.php' != $input['register'] ) {
152
+ $modification .= "\trewrite ^($home_root)?{$input['register']}/?$ {$home_root}{$input['slug']}?action=register break;\n";
153
+ }
154
+
155
+ return $modification;
156
+ }
157
+
158
+ /**
159
+ * Sets the status in the plugin dashboard
160
+ *
161
+ * @since 4.0
162
+ *
163
+ * @return array array of statuses
164
+ */
165
+ public function dashboard_status( $statuses ) {
166
+
167
+ if ( $this->settings['enabled'] === true ) {
168
+
169
+ $status_array = 'safe-medium';
170
+ $status = array(
171
+ 'text' => __( 'Your WordPress Dashboard is hidden.', 'better-wp-security' ), 'link' => '#itsec_hide_backend_enabled',
172
+ );
173
+
174
+ } else {
175
+
176
+ $status_array = 'medium';
177
+ $status = array(
178
+ 'text' => __( 'Your WordPress Dashboard is using the default addresses. This can make a brute force attack much easier.', 'better-wp-security' ),
179
+ 'link' => '#itsec_hide_backend_enabled',
180
+ );
181
+
182
+ }
183
+
184
+ array_push( $statuses[$status_array], $status );
185
+
186
+ return $statuses;
187
+
188
+ }
189
+
190
+ /**
191
+ * echos Hide Backend Enabled Field
192
+ *
193
+ * @since 4.0
194
+ *
195
+ * @return void
196
+ */
197
+ public function hide_backend_enabled() {
198
+
199
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
200
+
201
+ $adminurl = is_multisite() ? admin_url() . 'network/' : admin_url();
202
+
203
+ $content = sprintf( '<p class="noPermalinks">%s <a href="%soptions-permalink.php">%s</a> %s</p>', __( 'You must turn on', 'better-wp-security' ), $adminurl, __( 'WordPress permalinks', 'better-wp-security' ), __( 'to use this feature.', 'better-wp-security' ) );
204
+
205
+ } else {
206
+
207
+ if ( isset( $this->settings['enabled'] ) && $this->settings['enabled'] === true ) {
208
+ $enabled = 1;
209
+ } else {
210
+ $enabled = 0;
211
+ }
212
+
213
+ $content = '<input type="checkbox" id="itsec_hide_backend_enabled" name="itsec_hide_backend[enabled]" value="1" ' . checked( 1, $enabled, false ) . '/>';
214
+ $content .= '<label for="itsec_hide_backend_enabled"> ' . __( 'Enable the hide backend feature.', 'better-wp-security' ) . '</label>';
215
+
216
+ }
217
+
218
+ echo $content;
219
+
220
+ }
221
+
222
+ /**
223
+ * echos Hide Backend Slug Field
224
+ *
225
+ * @since 4.0
226
+ *
227
+ * @return void
228
+ */
229
+ public function hide_backend_slug() {
230
+
231
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
232
+
233
+ $content = '';
234
+
235
+ } else {
236
+
237
+ $content = '<input name="itsec_hide_backend[slug]" id="itsec_hide_backend_strong_passwords_slug" value="' . sanitize_title( $this->settings['slug'] ) . '" type="text"><br />';
238
+ $content .= '<label for="itsec_hide_backend_strong_passwords_slug">' . __( 'Login URL:', 'better-wp-security' ) . trailingslashit( get_option( 'siteurl' ) ) . '<span style="color: #4AA02C">' . sanitize_title( $this->settings['slug'] ) . '</span></label>';
239
+ $content .= '<p class="description">' . __( 'The login url slug cannot be "login," "admin," "dashboard," or "wp-login.php" as these are use by default in WordPress.', 'better-wp-security' ) . '</p>';
240
+ $content .= '<p class="description"><em>' . __( 'Note: The output is limited to alphanumeric characters, underscore (_) and dash (-). Special characters such as "." and "/" are not allowed and will be converted in the same manner as a post title. Please review your selection before logging out.', 'better-wp-security' ) . '</em></p>';
241
+
242
+ }
243
+
244
+ echo $content;
245
+
246
+ }
247
+
248
+ /**
249
+ * echos Hide Backend Slug Field
250
+ *
251
+ * @since 4.0.6
252
+ *
253
+ * @return void
254
+ */
255
+ public function hide_backend_theme_compat_slug() {
256
+
257
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
258
+
259
+ $content = '';
260
+
261
+ } else {
262
+
263
+ $slug = sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' );
264
+
265
+ $content = '<input name="itsec_hide_backend[theme_compat_slug]" id="itsec_hide_backend_strong_passwords_theme_compat_slug" value="' . $slug . '" type="text"><br />';
266
+ $content .= '<label for="itsec_hide_backend_strong_passwords_theme_compat_slug">' . __( '404 Slug:', 'better-wp-security' ) . trailingslashit( get_option( 'siteurl' ) ) . '<span style="color: #4AA02C">' . $slug . '</span></label>';
267
+ $content .= '<p class="description">' . __( 'The slug to redirect folks to when theme compatibility mode is enabled (just make sure it does not exist in your site).', 'better-wp-security' ) . '</p>';
268
+
269
+ }
270
+
271
+ echo $content;
272
+
273
+ }
274
+
275
+ /**
276
+ * echos Hide Backend Slug Field
277
+ *
278
+ * @since 4.0.6
279
+ *
280
+ * @return void
281
+ */
282
+ public function hide_backend_post_logout_slug() {
283
+
284
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
285
+
286
+ $content = '';
287
+
288
+ } else {
289
+
290
+ $slug = sanitize_title( isset( $this->settings['post_logout_slug'] ) ? $this->settings['post_logout_slug'] : '' );
291
+
292
+ $content = '<input name="itsec_hide_backend[post_logout_slug]" id="itsec_hide_backend_strong_passwords_post_logout_slug" value="' . $slug . '" type="text"><br />';
293
+ $content .= '<label for="itsec_hide_backend_strong_passwords_post_logout_slug">' . __( 'Custom Action:', 'better-wp-security' ) . '</label>';
294
+ $content .= '<p class="description">' . __( 'WordPress uses the "action" variable to handle many login and logout functions. By default this plugin can handle the normal ones but some plugins and themes may utilize a custom action (such as logging out of a private post). If you need a custom action please enter it here.', 'better-wp-security' ) . '</p>';
295
+
296
+ }
297
+
298
+ echo $content;
299
+
300
+ }
301
+
302
+ /**
303
+ * echos Hide Backend theme compatibility Field
304
+ *
305
+ * @since 4.0.6
306
+ *
307
+ * @return void
308
+ */
309
+ public function hide_backend_theme_compat() {
310
+
311
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
312
+
313
+ $adminurl = is_multisite() ? admin_url() . 'network/' : admin_url();
314
+
315
+ $content = sprintf( '<p class="noPermalinks">%s <a href="%soptions-permalink.php">%s</a> %s</p>', __( 'You must turn on', 'better-wp-security' ), $adminurl, __( 'WordPress permalinks', 'better-wp-security' ), __( 'to use this feature.', 'better-wp-security' ) );
316
+
317
+ } else {
318
+
319
+ if ( isset( $this->settings['theme_compat'] ) && $this->settings['theme_compat'] === true ) {
320
+ $enabled = 1;
321
+ } else {
322
+ $enabled = 0;
323
+ }
324
+
325
+ $content = '<input type="checkbox" id="itsec_hide_backend_theme_compat" name="itsec_hide_backend[theme_compat]" value="1" ' . checked( 1, $enabled, false ) . '/>';
326
+ $content .= '<label for="itsec_hide_backend_theme_compat"> ' . __( 'Enable theme compatibility. If you see errors in your theme when using hide backend, in particular when going to wp-admin while not logged in, turn this on to fix them.', 'better-wp-security' ) . '</label>';
327
+
328
+ }
329
+
330
+ echo $content;
331
+
332
+ }
333
+
334
+ /**
335
+ * echos Register Slug Field
336
+ *
337
+ * @since 4.0
338
+ *
339
+ * @return void
340
+ */
341
+ public function hide_backend_register() {
342
+
343
+ if ( ( get_option( 'permalink_structure' ) == '' || get_option( 'permalink_structure' ) == false ) && ! is_multisite() ) {
344
+
345
+ $content = '';
346
+
347
+ } else {
348
+
349
+ $content = '<input name="itsec_hide_backend[register]" id="itsec_hide_backend_strong_passwords_register" value="' . ( $this->settings['register'] !== 'wp-register.php' ? sanitize_title( $this->settings['register'] ) : 'wp-register.php' ) . '" type="text"><br />';
350
+ $content .= '<label for="itsec_hide_backend_strong_passwords_register">' . __( 'Registration URL:', 'better-wp-security' ) . trailingslashit( get_option( 'siteurl' ) ) . '<span style="color: #4AA02C">' . sanitize_title( $this->settings['register'] ) . '</span></label>';
351
+
352
+ }
353
+
354
+ echo $content;
355
+
356
+ }
357
+
358
+ /**
359
+ * Execute admin initializations
360
+ *
361
+ * @return void
362
+ */
363
+ public function initialize_admin() {
364
+
365
+ //Add Settings sections
366
+ add_settings_section(
367
+ 'hide_backend-enabled',
368
+ __( 'Hide Login and Admin', 'better-wp-security' ),
369
+ '__return_empty_string',
370
+ 'security_page_toplevel_page_itsec_settings'
371
+ );
372
+
373
+ add_settings_section(
374
+ 'hide_backend-settings',
375
+ __( 'Hide Login and Admin', 'better-wp-security' ),
376
+ '__return_empty_string',
377
+ 'security_page_toplevel_page_itsec_settings'
378
+ );
379
+
380
+ //Hide Backend Fields
381
+ add_settings_field(
382
+ 'itsec_hide_backend[enabled]',
383
+ __( 'Hide Backend', 'better-wp-security' ),
384
+ array( $this, 'hide_backend_enabled' ),
385
+ 'security_page_toplevel_page_itsec_settings',
386
+ 'hide_backend-enabled'
387
+ );
388
+
389
+ add_settings_field(
390
+ 'itsec_hide_backend[slug]',
391
+ __( 'Login Slug', 'better-wp-security' ),
392
+ array( $this, 'hide_backend_slug' ),
393
+ 'security_page_toplevel_page_itsec_settings',
394
+ 'hide_backend-settings'
395
+ );
396
+
397
+ if ( get_site_option( 'users_can_register' ) ) {
398
+
399
+ add_settings_field(
400
+ 'itsec_hide_backend[register]',
401
+ __( 'Register Slug', 'better-wp-security' ),
402
+ array( $this, 'hide_backend_register' ),
403
+ 'security_page_toplevel_page_itsec_settings',
404
+ 'hide_backend-settings'
405
+ );
406
+
407
+ }
408
+
409
+ add_settings_field(
410
+ 'itsec_hide_backend[theme_compat]',
411
+ __( 'Enable Theme Compatibility', 'better-wp-security' ),
412
+ array( $this, 'hide_backend_theme_compat' ),
413
+ 'security_page_toplevel_page_itsec_settings',
414
+ 'hide_backend-settings'
415
+ );
416
+
417
+ add_settings_field(
418
+ 'itsec_hide_backend[theme_compat_slug]',
419
+ __( 'Theme Compatibility Slug', 'better-wp-security' ),
420
+ array( $this, 'hide_backend_theme_compat_slug' ),
421
+ 'security_page_toplevel_page_itsec_settings',
422
+ 'hide_backend-settings'
423
+ );
424
+
425
+ add_settings_field(
426
+ 'itsec_hide_backend[post_logout_slug]',
427
+ __( 'Custom Login Action', 'better-wp-security' ),
428
+ array( $this, 'hide_backend_post_logout_slug' ),
429
+ 'security_page_toplevel_page_itsec_settings',
430
+ 'hide_backend-settings'
431
+ );
432
+
433
+ //Register the settings field for the entire module
434
+ register_setting(
435
+ 'security_page_toplevel_page_itsec_settings',
436
+ 'itsec_hide_backend',
437
+ array( $this, 'sanitize_module_input' )
438
+ );
439
+
440
+ }
441
+
442
+ /**
443
+ * Render the settings metabox
444
+ *
445
+ * @since 4.0
446
+ *
447
+ * @return void
448
+ */
449
+ public function metabox_hide_backend_settings() {
450
+
451
+ echo '<p>' . __( 'Hides the login page (wp-login.php, wp-admin, admin and login) making it harder to find by automated attacks and making it easier for users unfamiliar with the WordPress platform.', 'better-wp-security' ) . '</p>';
452
+
453
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'hide_backend-enabled', false );
454
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'hide_backend-settings', false );
455
+
456
+ echo '<p>' . PHP_EOL;
457
+
458
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
459
+
460
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
461
+
462
+ echo '</p>' . PHP_EOL;
463
+
464
+ }
465
+
466
+ /**
467
+ * Register ban users for file writer
468
+ *
469
+ * @param array $file_modules array of file writer modules
470
+ *
471
+ * @return array array of file writer modules
472
+ */
473
+ public function register_file( $file_modules ) {
474
+
475
+ $file_modules['hide-backend'] = array(
476
+ 'rewrite' => array( $this, 'save_rewrite_rules' ),
477
+ );
478
+
479
+ return $file_modules;
480
+
481
+ }
482
+
483
+ /**
484
+ * Register backups for tooltips
485
+ *
486
+ * @param array $tooltip_modules array of tooltip modules
487
+ *
488
+ * @return array array of tooltip modules
489
+ */
490
+ public function register_tooltip( $tooltip_modules ) {
491
+
492
+ if ( get_site_transient( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP' ) || ( isset( $this->settings['show-tooltip'] ) && $this->settings['show-tooltip'] === true ) ) {
493
+
494
+ $tooltip_modules['hide-backend'] = array(
495
+ 'priority' => 0,
496
+ 'class' => 'itsec_tooltip_hide_backend',
497
+ 'heading' => __( 'Review Hide Backend Settings', 'better-wp-security' ),
498
+ 'text' => __( 'The hide backend system has been rewritten. You must re-activate the feature to continue using the feature.', 'better-wp-security' ),
499
+ 'link_text' => __( 'Review Settings', 'better-wp-security' ),
500
+ 'link' => '?page=toplevel_page_itsec_settings#itsec_hide_backend_enabled',
501
+ 'success' => '',
502
+ 'failure' => '',
503
+ );
504
+
505
+ }
506
+
507
+ return $tooltip_modules;
508
+
509
+ }
510
+
511
+ /**
512
+ * Sanitize and validate input
513
+ *
514
+ * @param Array $input array of input fields
515
+ *
516
+ * @return Array Sanitized array
517
+ */
518
+ public function sanitize_module_input( $input ) {
519
+
520
+ global $itsec_globals;
521
+
522
+ //Process hide backend settings
523
+ $input['enabled'] = ( isset( $input['enabled'] ) && intval( $input['enabled'] == 1 ) ? true : false );
524
+ $input['theme_compat'] = ( isset( $input['theme_compat'] ) && intval( $input['theme_compat'] == 1 ) ? true : false );
525
+ $input['show-tooltip'] = ( isset( $this->settings['show-tooltip'] ) ? $this->settings['show-tooltip'] : false );
526
+
527
+ if ( isset( $input['slug'] ) ) {
528
+
529
+ $input['slug'] = sanitize_title( $input['slug'] );
530
+
531
+ } else {
532
+
533
+ $input['slug'] = 'wplogin';
534
+
535
+ }
536
+
537
+ if ( isset( $input['post_logout_slug'] ) ) {
538
+
539
+ $input['post_logout_slug'] = sanitize_title( $input['post_logout_slug'] );
540
+
541
+ } else {
542
+
543
+ $input['post_logout_slug'] = '';
544
+
545
+ }
546
+
547
+ if ( $input['slug'] != $this->settings['slug'] && $input['enabled'] === true ) {
548
+ add_site_option( 'itsec_hide_backend_new_slug', $input['slug'] );
549
+ }
550
+
551
+ if ( isset( $input['register'] ) && $input['register'] !== 'wp-register.php' ) {
552
+ $input['register'] = sanitize_title( $input['register'] );
553
+ } else {
554
+ $input['register'] = 'wp-register.php';
555
+ }
556
+
557
+ if ( isset( $input['theme_compat_slug'] ) ) {
558
+ $input['theme_compat_slug'] = sanitize_title( $input['theme_compat_slug'] );
559
+ } else {
560
+ $input['theme_compat_slug'] = 'not_found';
561
+ }
562
+
563
+ $forbidden_slugs = array( 'admin', 'login', 'wp-login.php', 'dashboard', 'wp-admin', '' );
564
+
565
+ if ( in_array( trim( $input['slug'] ), $forbidden_slugs ) && $input['enabled'] === true ) {
566
+
567
+ $invalid_login_slug = true;
568
+
569
+ $type = 'error';
570
+ $message = __( 'Invalid hide login slug used. The login url slug cannot be \"login,\" \"admin,\" \"dashboard,\" or \"wp-login.php\" or \"\" (blank) as these are use by default in WordPress.', 'better-wp-security' );
571
+
572
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
573
+
574
+ } else {
575
+
576
+ $invalid_login_slug = false;
577
+
578
+ }
579
+
580
+ if ( $invalid_login_slug === false ) {
581
+
582
+ if (
583
+ ! isset( $type ) &&
584
+ (
585
+ $input['slug'] !== $this->settings['slug'] ||
586
+ $input['register'] !== $this->settings['register'] ||
587
+ $input['enabled'] !== $this->settings['enabled']
588
+ ) ||
589
+ isset( $itsec_globals['settings']['write_files'] ) && $itsec_globals['settings']['write_files'] === true
590
+ ) {
591
+
592
+ add_site_option( 'itsec_rewrites_changed', true );
593
+
594
+ }
595
+
596
+ }
597
+
598
+ if ( is_multisite() ) {
599
+
600
+ if ( isset( $type ) ) {
601
+
602
+ $error_handler = new WP_Error();
603
+
604
+ $error_handler->add( $type, $message );
605
+
606
+ $this->core->show_network_admin_notice( $error_handler );
607
+
608
+ } else {
609
+
610
+ $this->core->show_network_admin_notice( false );
611
+
612
+ }
613
+
614
+ $this->settings = $input;
615
+
616
+ }
617
+
618
+ return $input;
619
+
620
+ }
621
+
622
+ /**
623
+ * Prepare and save options in network settings
624
+ *
625
+ * @return void
626
+ */
627
+ public function save_network_options() {
628
+
629
+ if ( isset( $_POST['itsec_hide_backend'] ) ) {
630
+
631
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
632
+ die( __( 'Security error!', 'better-wp-security' ) );
633
+ }
634
+
635
+ update_site_option( 'itsec_hide_backend', $_POST['itsec_hide_backend'] ); //we must manually save network options
636
+
637
+ }
638
+
639
+ }
640
+
641
+ /**
642
+ * Sends an email to notify site admins of the new login url
643
+ *
644
+ * @param string $new_slug the new login url
645
+ *
646
+ * @return void
647
+ */
648
+ private function send_new_slug( $new_slug ) {
649
+
650
+ global $itsec_globals;
651
+
652
+ //Put the copy all together
653
+ $body = sprintf(
654
+ '<p>%s,</p><p>%s <a href="%s">%s</a>. %s <a href="%s">%s</a> %s.</p>',
655
+ __( 'Dear Site Admin', 'better-wp-security' ),
656
+ __( 'This friendly email is just a reminder that you have changed the dashboard login address on', 'better-wp-security' ),
657
+ get_site_url(),
658
+ get_site_url(),
659
+ __( 'You must now use', 'better-wp-security' ),
660
+ $new_slug,
661
+ $new_slug,
662
+ __( 'to login to your WordPress website', 'better-wp-security' )
663
+ );
664
+
665
+ //Setup the remainder of the email
666
+ $recipients = $itsec_globals['settings']['notification_email'];
667
+ $subject = '[' . get_option( 'siteurl' ) . '] ' . __( 'WordPress Login Address Changed', 'better-wp-security' );
668
+ $subject = apply_filters( 'itsec_lockout_email_subject', $subject );
669
+ $headers = 'From: ' . get_bloginfo( 'name' ) . ' <' . get_option( 'admin_email' ) . '>' . "\r\n";
670
+
671
+ //Use HTML Content type
672
+ add_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) );
673
+
674
+ //Send emails to all recipients
675
+ foreach ( $recipients as $recipient ) {
676
+
677
+ if ( is_email( trim( $recipient ) ) ) {
678
+
679
+ if ( defined( 'ITSEC_DEBUG' ) && ITSEC_DEBUG === true ) {
680
+ $body .= '<p>' . __( 'Debug info (source page): ' . esc_url( $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] ) ) . '</p>';
681
+ }
682
+
683
+ wp_mail( trim( $recipient ), $subject, '<html>' . $body . '</html>', $headers );
684
+
685
+ }
686
+
687
+ }
688
+
689
+ //Remove HTML Content type
690
+ remove_filter( 'wp_mail_content_type', array( $this, 'set_html_content_type' ) );
691
+
692
+ }
693
+
694
+ /**
695
+ * Set HTML content type for email
696
+ *
697
+ * @return string html content type
698
+ */
699
+ public function set_html_content_type() {
700
+
701
+ return 'text/html';
702
+
703
+ }
704
+
705
+ /**
706
+ * Adds fields that will be tracked for Google Analytics
707
+ *
708
+ * @since 4.0
709
+ *
710
+ * @param array $vars tracking vars
711
+ *
712
+ * @return array tracking vars
713
+ */
714
+ public function tracking_vars( $vars ) {
715
+
716
+ $vars['itsec_hide_backend'] = array(
717
+ 'enabled' => '0:b',
718
+ );
719
+
720
+ return $vars;
721
+
722
+ }
723
+
724
+ }
core/modules/hide-backend/class-itsec-hide-backend.php ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Hide_Backend {
4
+
5
+ private
6
+ $settings,
7
+ $auth_cookie_expired;
8
+
9
+ function run() {
10
+
11
+ $this->settings = get_site_option( 'itsec_hide_backend' );
12
+
13
+ //Execute module functions on frontend init
14
+ if ( $this->settings['enabled'] === true ) {
15
+
16
+ $jetpack_active_modules = get_option( 'jetpack_active_modules' );
17
+
18
+ if ( is_multisite() && function_exists( 'is_plugin_active_for_network' ) ) { //see if Jetpack is active
19
+
20
+ $is_jetpack_active = in_array( 'jetpack/jetpack.php', (array) get_option( 'active_plugins', array() ) ) || is_plugin_active_for_network( 'jetpack/jetpack.php' );
21
+
22
+ } else {
23
+
24
+ $is_jetpack_active = in_array( 'jetpack/jetpack.php', (array) get_option( 'active_plugins', array() ) );
25
+
26
+ }
27
+
28
+ if (
29
+ ! (
30
+ $is_jetpack_active === true &&
31
+ is_array( $jetpack_active_modules ) &&
32
+ in_array( 'json-api', $jetpack_active_modules ) &&
33
+ isset( $_GET['action'] ) &&
34
+ $_GET['action'] == 'jetpack_json_api_authorization'
35
+ )
36
+ ) {
37
+
38
+ $this->auth_cookie_expired = false;
39
+
40
+ add_action( 'auth_cookie_expired', array( $this, 'auth_cookie_expired' ) );
41
+ add_action( 'init', array( $this, 'execute_hide_backend' ), 1000 );
42
+ add_action( 'login_init', array( $this, 'execute_hide_backend_login' ) );
43
+ add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ), 11 );
44
+
45
+ add_filter( 'body_class', array( $this, 'remove_admin_bar' ) );
46
+ add_filter( 'loginout', array( $this, 'filter_loginout' ) );
47
+ add_filter( 'wp_redirect', array( $this, 'filter_login_url' ), 10, 2 );
48
+ add_filter( 'lostpassword_url', array( $this, 'filter_login_url' ), 10, 2 );
49
+ add_filter( 'site_url', array( $this, 'filter_login_url' ), 10, 2 );
50
+ add_filter( 'retrieve_password_message', array( $this, 'retrieve_password_message' ) );
51
+ add_filter( 'comment_moderation_text', array( $this, 'comment_moderation_text' ) );
52
+
53
+ remove_action( 'template_redirect', 'wp_redirect_admin_locations', 1000 );
54
+
55
+ }
56
+
57
+ }
58
+
59
+ }
60
+
61
+ /**
62
+ * Lets the module know that this is a reauthorization
63
+ *
64
+ * @since 4.1
65
+ *
66
+ * @return void
67
+ */
68
+ public function auth_cookie_expired() {
69
+
70
+ $this->auth_cookie_expired = true;
71
+ wp_clear_auth_cookie();
72
+
73
+ }
74
+
75
+ /**
76
+ * @param $notify_message
77
+ *
78
+ * @since 4.5
79
+ *
80
+ * @param sting $notify_message Notification message
81
+ *
82
+ * @return string Notification message
83
+ */
84
+ public function comment_moderation_text( $notify_message ) {
85
+
86
+ preg_match_all( "#(https?:\/\/((.*)wp-admin(.*)))#", $notify_message, $urls );
87
+
88
+ if ( isset( $urls ) && is_array( $urls ) && isset( $urls[0] ) ) {
89
+
90
+ foreach ( $urls[0] as $url ) {
91
+
92
+ $notify_message = str_replace( trim( $url ), wp_login_url( trim( $url ) ), $notify_message );
93
+
94
+ }
95
+
96
+ }
97
+
98
+ return $notify_message;
99
+
100
+ }
101
+
102
+ /**
103
+ * Execute hide backend functionality
104
+ *
105
+ * @since 4.0
106
+ *
107
+ * @return void
108
+ */
109
+ public
110
+ function execute_hide_backend() {
111
+
112
+ if ( get_site_option( 'users_can_register' ) == 1 && isset( $_SERVER['REQUEST_URI'] ) && $_SERVER['REQUEST_URI'] == ITSEC_Lib::get_home_root() . $this->settings['register'] ) {
113
+
114
+ wp_redirect( wp_login_url() . '?action=register' );
115
+ exit;
116
+
117
+ }
118
+
119
+ //redirect wp-admin and wp-register.php to 404 when not logged in
120
+ if (
121
+ (
122
+ (
123
+ get_site_option( 'users_can_register' ) == false &&
124
+ (
125
+ isset( $_SERVER['REQUEST_URI'] ) && strpos( $_SERVER['REQUEST_URI'], 'wp-register.php' ) ||
126
+ isset( $_SERVER['REQUEST_URI'] ) && strpos( $_SERVER['REQUEST_URI'], 'wp-signup.php' )
127
+ )
128
+ ) ||
129
+ (
130
+ isset( $_SERVER['REQUEST_URI'] ) && strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) && is_user_logged_in() !== true
131
+ ) ||
132
+ ( is_admin() && is_user_logged_in() !== true ) ||
133
+ (
134
+ $this->settings['register'] != 'wp-register.php' &&
135
+ strpos( $_SERVER['REQUEST_URI'], 'wp-register.php' ) !== false ||
136
+ strpos( $_SERVER['REQUEST_URI'], 'wp-signup.php' ) !== false ||
137
+ (
138
+ isset( $_REQUEST['redirect_to'] ) &&
139
+ strpos( $_REQUEST['redirect_to'], 'wp-admin/customize.php' ) !== false
140
+
141
+ )
142
+ )
143
+ ) &&
144
+ strpos( $_SERVER['REQUEST_URI'], 'admin-ajax.php' ) === false
145
+ && $this->auth_cookie_expired === false
146
+ ) {
147
+
148
+ global $itsec_is_old_admin;
149
+
150
+ $itsec_is_old_admin = true;
151
+
152
+ if ( isset( $this->settings['theme_compat'] ) && $this->settings['theme_compat'] === true ) { //theme compat (process theme and redirect to a 404)
153
+
154
+ wp_redirect( ITSEC_Lib::get_home_root() . sanitize_title( isset( $this->settings['theme_compat_slug'] ) ? $this->settings['theme_compat_slug'] : 'not_found' ), 302 );
155
+ exit;
156
+
157
+ } else { //just set the current page as a 404
158
+
159
+ add_action( 'wp_loaded', array( $this, 'set_404' ) );
160
+
161
+ }
162
+
163
+ }
164
+
165
+ $url_info = parse_url( $_SERVER['REQUEST_URI'] );
166
+ $login_path = site_url( $this->settings['slug'], 'relative' );
167
+ $login_path_trailing_slash = site_url( $this->settings['slug'] . '/', 'relative' );
168
+
169
+ if ( $url_info['path'] === $login_path || $url_info['path'] === $login_path_trailing_slash ) {
170
+
171
+ if ( ! is_user_logged_in() ) {
172
+ //Add the login form
173
+
174
+ if ( isset( $this->settings['post_logout_slug'] ) && strlen( trim( $this->settings['post_logout_slug'] ) ) > 0 && isset( $_GET['action'] ) && sanitize_text_field( $_GET['action'] ) == trim( $this->settings['post_logout_slug'] ) ) {
175
+ do_action( 'itsec_custom_login_slug' ); //add hook here for custom users
176
+ }
177
+
178
+ //suppress error messages due to timing
179
+ error_reporting( 0 );
180
+ @ini_set( 'display_errors', 0 );
181
+
182
+ status_header( 200 );
183
+
184
+ //don't allow domain mapping to redirect
185
+ if ( defined( 'DOMAIN_MAPPING' ) && DOMAIN_MAPPING == 1 ) {
186
+ remove_action( 'login_head', 'redirect_login_to_orig' );
187
+ }
188
+
189
+ if ( ! function_exists( 'login_header' ) ) {
190
+
191
+ include( ABSPATH . 'wp-login.php' );
192
+ exit;
193
+
194
+ }
195
+
196
+ } elseif ( ! isset( $_GET['action'] ) || ( sanitize_text_field( $_GET['action'] ) != 'logout' && sanitize_text_field( $_GET['action'] ) != 'postpass' && ( isset( $this->settings['post_logout_slug'] ) && strlen( trim( $this->settings['post_logout_slug'] ) ) > 0 && sanitize_text_field( $_GET['action'] ) != trim( $this->settings['post_logout_slug'] ) ) ) ) {
197
+ //Just redirect them to the dashboard (for logged in users)
198
+
199
+ if ( $this->auth_cookie_expired === false ) {
200
+
201
+ wp_redirect( get_admin_url() );
202
+ exit();
203
+
204
+ }
205
+
206
+ } elseif ( isset( $_GET['action'] ) && ( sanitize_text_field( $_GET['action'] ) == 'postpass' || ( isset( $this->settings['post_logout_slug'] ) && strlen( trim( $this->settings['post_logout_slug'] ) ) > 0 && sanitize_text_field( $_GET['action'] ) == trim( $this->settings['post_logout_slug'] ) ) ) ) {
207
+ //handle private posts for
208
+
209
+ if ( isset( $this->settings['post_logout_slug'] ) && strlen( trim( $this->settings['post_logout_slug'] ) ) > 0 && sanitize_text_field( $_GET['action'] ) == trim( $this->settings['post_logout_slug'] ) ) {
210
+ do_action( 'itsec_custom_login_slug' ); //add hook here for custom users
211
+ }
212
+
213
+ //suppress error messages due to timing
214
+ error_reporting( 0 );
215
+ @ini_set( 'display_errors', 0 );
216
+
217
+ status_header( 200 ); //its a good login page. make sure we say so
218
+
219
+ //include the login page where we need it
220
+ if ( ! function_exists( 'login_header' ) ) {
221
+ include( ABSPATH . '/wp-login.php' );
222
+ exit;
223
+ }
224
+
225
+ //Take them back to the page if we need to
226
+ if ( isset( $_SERVER['HTTP_REFERRER'] ) ) {
227
+ wp_redirect( sanitize_text_field( $_SERVER['HTTP_REFERRER'] ) );
228
+ exit();
229
+ }
230
+
231
+ }
232
+
233
+ }
234
+
235
+ }
236
+
237
+ /**
238
+ * Filter the old login page out
239
+ *
240
+ * @return void
241
+ */
242
+ public function execute_hide_backend_login() {
243
+
244
+ if ( strpos( $_SERVER['REQUEST_URI'], 'wp-login.php' ) ) { //are we on the login page
245
+
246
+ global $itsec_is_old_admin;
247
+
248
+ $itsec_is_old_admin = true;
249
+
250
+ ITSEC_Lib::set_404();
251
+
252
+ }
253
+
254
+ }
255
+
256
+ /**
257
+ * Filters redirects for correct login URL
258
+ *
259
+ * @since 4.0
260
+ *
261
+ * @param string $url URL redirecting to
262
+ *
263
+ * @return string Correct redirect URL
264
+ */
265
+ public function filter_login_url( $url ) {
266
+
267
+ $t = str_replace( 'wp-login.php', $this->settings['slug'], $url );
268
+
269
+ return str_replace( 'wp-login.php', $this->settings['slug'], $url );
270
+
271
+ }
272
+
273
+ /**
274
+ * Filter meta link
275
+ *
276
+ * @since 4.2
277
+ *
278
+ * @param string $link the link
279
+ *
280
+ * @return string the link
281
+ */
282
+ public function filter_loginout( $link ) {
283
+
284
+ return str_replace( 'wp-login.php', $this->settings['slug'], $link );
285
+
286
+ }
287
+
288
+ /**
289
+ * Actions for plugins loaded.
290
+ *
291
+ * Makes certain logout is processed on NGINX.
292
+ *
293
+ * @return void
294
+ */
295
+ public function plugins_loaded() {
296
+
297
+ if ( is_user_logged_in() && isset( $_GET['action'] ) && sanitize_text_field( $_GET['action'] ) == 'logout' ) {
298
+
299
+ check_admin_referer( 'log-out' );
300
+ wp_logout();
301
+
302
+ $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : 'wp-login.php?loggedout=true';
303
+ wp_safe_redirect( $redirect_to );
304
+ exit();
305
+
306
+ }
307
+
308
+ }
309
+
310
+ /**
311
+ * Removes the admin bar class from the body tag
312
+ *
313
+ * @param array $classes body tag classes
314
+ *
315
+ * @return array body tag classes
316
+ */
317
+ public function remove_admin_bar( $classes ) {
318
+
319
+ if ( is_admin() && is_user_logged_in() !== true ) {
320
+
321
+ foreach ( $classes as $key => $value ) {
322
+
323
+ if ( $value == 'admin-bar' ) {
324
+ unset( $classes[ $key ] );
325
+ }
326
+
327
+ }
328
+
329
+ }
330
+
331
+ return $classes;
332
+
333
+ }
334
+
335
+ public function retrieve_password_message( $message ) {
336
+
337
+ return str_replace( 'wp-login.php', $this->settings['slug'], $message );
338
+
339
+ return $message;
340
+
341
+ }
342
+
343
+ /**
344
+ * Sets 404 error at later time.
345
+ *
346
+ * @since 4.0.6
347
+ *
348
+ * @return void
349
+ */
350
+ public function set_404() {
351
+
352
+ ITSEC_Lib::set_404();
353
+
354
+ }
355
+
356
+ }
core/modules/hide-backend/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/hide-backend/js/admin-hide-backend.js ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( "#itsec_hide_backend_enabled" ).change(function () {
4
+
5
+ if ( jQuery( "#itsec_hide_backend_enabled" ).is( ':checked' ) ) {
6
+
7
+ jQuery( "#hide_backend-settings" ).show();
8
+
9
+ } else {
10
+
11
+ jQuery( "#hide_backend-settings" ).hide();
12
+
13
+ }
14
+
15
+ } ).change();
16
+
17
+ if ( jQuery( 'p.noPermalinks' ).length ) {
18
+ jQuery( "#hide_backend-settings" ).hide();
19
+ }
20
+
21
+ if ( itsec_hide_backend.new_slug != false ) {
22
+
23
+ alert( itsec_hide_backend.new_slug );
24
+
25
+ }
26
+
27
+ } );
core/modules/hide-backend/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/hide-backend/setup.php ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Hide_Backend_Setup' ) ) {
4
+
5
+ class ITSEC_Hide_Backend_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'slug' => 'wplogin',
17
+ 'register' => 'wp-register.php',
18
+ 'theme_compat' => true,
19
+ 'theme_compat_slug' => 'not_found',
20
+ 'post_logout_slug' => '',
21
+ );
22
+
23
+ if ( isset( $itsec_setup_action ) ) {
24
+
25
+ switch ( $itsec_setup_action ) {
26
+
27
+ case 'activate':
28
+ $this->execute_activate();
29
+ break;
30
+ case 'upgrade':
31
+ $this->execute_upgrade();
32
+ break;
33
+ case 'deactivate':
34
+ $this->execute_deactivate();
35
+ break;
36
+ case 'uninstall':
37
+ $this->execute_uninstall();
38
+ break;
39
+
40
+ }
41
+
42
+ } else {
43
+ wp_die( 'error' );
44
+ }
45
+
46
+ }
47
+
48
+ /**
49
+ * Execute module activation.
50
+ *
51
+ * @since 4.0
52
+ *
53
+ * @return void
54
+ */
55
+ public function execute_activate() {
56
+
57
+ $options = get_site_option( 'itsec_hide_backend' );
58
+
59
+ if ( is_multisite() ) {
60
+
61
+ switch_to_blog( 1 );
62
+
63
+ $bwps_options = get_option( 'bit51_bwps' );
64
+
65
+ restore_current_blog();
66
+
67
+ } else {
68
+
69
+ $bwps_options = get_option( 'bit51_bwps' );
70
+
71
+ }
72
+
73
+ if ( $bwps_options !== false && isset( $bwps_options['hb_enabled'] ) && $bwps_options['hb_enabled'] == 1 ) {
74
+
75
+ $this->defaults['show-tooltip'] = true;
76
+ define( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP', true );
77
+
78
+ } else {
79
+
80
+ $this->defaults['show-tooltip'] = false;
81
+
82
+ }
83
+
84
+ if ( $options === false ) {
85
+
86
+ add_site_option( 'itsec_hide_backend', $this->defaults );
87
+
88
+ }
89
+
90
+ add_site_option( 'itsec_rewrites_changed', true );
91
+
92
+ }
93
+
94
+ /**
95
+ * Execute module deactivation
96
+ *
97
+ * @return void
98
+ */
99
+ public function execute_deactivate() {
100
+
101
+ delete_site_transient( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP' );
102
+ delete_site_option( 'itsec_hide_backend_new_slug' );
103
+
104
+ }
105
+
106
+ /**
107
+ * Execute module uninstall
108
+ *
109
+ * @return void
110
+ */
111
+ public function execute_uninstall() {
112
+
113
+ $this->execute_deactivate();
114
+
115
+ delete_site_option( 'itsec_hide_backend' );
116
+
117
+ }
118
+
119
+ /**
120
+ * Execute module upgrade
121
+ *
122
+ * @return void
123
+ */
124
+ public function execute_upgrade() {
125
+
126
+ global $itsec_old_version;
127
+
128
+ if ( $itsec_old_version < 4000 ) {
129
+
130
+ global $itsec_bwps_options;
131
+
132
+ $current_options = get_site_option( 'itsec_hide_backend' );
133
+
134
+ if ( $current_options === false ) {
135
+ $current_options = $this->defaults;
136
+ }
137
+
138
+ $current_options['enabled'] = isset( $itsec_bwps_options['hb_enabled'] ) && $itsec_bwps_options['hb_enabled'] == 1 ? true : false;
139
+ $current_options['register'] = isset( $itsec_bwps_options['hb_register'] ) ? sanitize_text_field( $itsec_bwps_options['hb_register'] ) : 'wp-register.php';
140
+
141
+ if ( $current_options['enabled'] === true ) {
142
+
143
+ $current_options['show-tooltip'] = true;
144
+ set_site_transient( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP', true, 600 );
145
+
146
+ } else {
147
+
148
+ $current_options['show-tooltip'] = false;
149
+
150
+ }
151
+
152
+ $forbidden_slugs = array( 'admin', 'login', 'wp-login.php', 'dashboard', 'wp-admin', '' );
153
+
154
+ if ( isset( $itsec_bwps_options['hb_login'] ) && ! in_array( trim( $itsec_bwps_options['hb_login'] ), $forbidden_slugs ) ) {
155
+
156
+ $current_options['slug'] = $itsec_bwps_options['hb_login'];
157
+ set_site_transient( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP', true, 600 );
158
+
159
+ } else {
160
+
161
+ $current_options['enabled'] = false;
162
+ set_site_transient( 'ITSEC_SHOW_HIDE_BACKEND_TOOLTIP', true, 600 );
163
+
164
+ }
165
+
166
+ update_site_option( 'itsec_hide_backend', $current_options );
167
+ add_site_option( 'itsec_rewrites_changed', true );
168
+
169
+ }
170
+
171
+ if ( $itsec_old_version < 4027 ) {
172
+
173
+ $current_options = get_site_option( 'itsec_hide_backend' );
174
+
175
+ if ( isset( $current_options['enabled'] ) && $current_options['enabled'] === true ) {
176
+
177
+ $config_file = ITSEC_Lib::get_htaccess();
178
+
179
+ //Make sure we can write to the file
180
+ $perms = substr( sprintf( '%o', @fileperms( $config_file ) ), - 4 );
181
+
182
+ @chmod( $config_file, 0664 );
183
+
184
+ add_action( 'admin_init', array( $this, 'flush_rewrite_rules' ) );
185
+
186
+ //reset file permissions if we changed them
187
+ if ( $perms == '0444' ) {
188
+ @chmod( $config_file, 0444 );
189
+ }
190
+
191
+ add_site_option( 'itsec_rewrites_changed', true );
192
+
193
+ }
194
+
195
+ }
196
+
197
+ }
198
+
199
+ /**
200
+ * Flush rewrite rules.
201
+ *
202
+ * @since 4.0.6
203
+ *
204
+ * @return void
205
+ */
206
+ public function flush_rewrite_rules() {
207
+
208
+ flush_rewrite_rules();
209
+ }
210
+
211
+ }
212
+
213
+ }
214
+
215
+ new ITSEC_Hide_Backend_Setup();
core/modules/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ipcheck/class-itsec-ipcheck-admin.php ADDED
@@ -0,0 +1,448 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_IPCheck_Admin {
4
+
5
+ private
6
+ $module_path,
7
+ $settings,
8
+ $core;
9
+
10
+ private static $endpoint = 'http://ipcheck-api.ithemes.com/?action=';
11
+
12
+ function run( $core ) {
13
+
14
+ $this->core = $core;
15
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
16
+ $this->settings = get_site_option( 'itsec_ipcheck' );
17
+
18
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); //enqueue scripts for admin page
19
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init' ) ); //initialize admin area
20
+ add_action( 'wp_ajax_itsec_api_key_ajax', array( $this, 'wp_ajax_itsec_api_key_ajax' ) );
21
+
22
+ //manually save options on multisite
23
+ if ( is_multisite() ) {
24
+ add_action( 'itsec_admin_init', array( $this, 'itsec_admin_init_multisite' ) ); //save multisite options
25
+ }
26
+
27
+ }
28
+
29
+ /**
30
+ * Activate an IPCheck API Key
31
+ *
32
+ * @since 4.5
33
+ *
34
+ * @param string $api_key the API key to activate
35
+ *
36
+ * @return bool|string IPCheck activation secret or false if there is an error
37
+ */
38
+ private function activate_api_key( $api_key ) {
39
+
40
+ $activated = isset( $this->settings['api_s'] ) ? $this->settings['api_s'] : false;
41
+
42
+ //if we already have an api secret just return it
43
+ if ( $activated != false && $activated != '' ) { //see if the key was already saved correctly
44
+ return $activated;
45
+ }
46
+
47
+ //Return false if we don't have an api key to check
48
+ if ( $api_key == false || $api_key == '' ) {
49
+ return false;
50
+ }
51
+
52
+ $response = wp_remote_get( self::$endpoint . 'activate-key&apikey=' . trim( sanitize_text_field( $api_key ) ) . '&site=' . home_url( '', 'http' ) );
53
+
54
+ if ( is_array( $response ) && isset( $response['body'] ) ) { //verify we have a good response body
55
+
56
+ $body = json_decode( $response['body'], true );
57
+
58
+ if ( is_array( $body ) && isset( $body['secret'] ) ) { //verify we retrieved an apikey
59
+
60
+ $activated = trim( sanitize_text_field( $body['secret'] ) );
61
+
62
+ return $activated;
63
+
64
+ }
65
+
66
+ }
67
+
68
+ return false; //assume an error
69
+
70
+ }
71
+
72
+ /**
73
+ * Add IPCheck admin Javascript
74
+ *
75
+ * @since 4.5
76
+ *
77
+ * @return void
78
+ */
79
+ public function admin_enqueue_scripts() {
80
+
81
+ global $itsec_globals;
82
+
83
+ if ( isset( get_current_screen()->id ) && ( strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) ) {
84
+
85
+ wp_enqueue_script( 'itsec_ipcheck_js', $this->module_path . 'js/admin-ipcheck.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
86
+ wp_localize_script(
87
+ 'itsec_ipcheck_js',
88
+ 'itsec_ipcheck',
89
+ array(
90
+ 'api_nonce' => wp_create_nonce( 'itsec_api_nonce' ),
91
+ 'text1' => __( 'Receive email updates from iThemes', 'better-wp-security' ),
92
+ 'text2' => __( 'Leverage the power of the iThemes Brute Force Protection network to ban IPs hitting your site.', 'better-wp-security' ),
93
+ 'text3' => __( 'Enter email and click Save All Changes', 'better-wp-security' ),
94
+ )
95
+ );
96
+
97
+ }
98
+
99
+ }
100
+
101
+ /**
102
+ * Retrieve and API key from the IPCheck server
103
+ *
104
+ * @since 4.5
105
+ *
106
+ * @param string $email the email address to associate with the key
107
+ * @param bool $optin true to optin to mailing list else false
108
+ *
109
+ * @return bool|string the api key retrieced or false
110
+ */
111
+ private function get_api_key( $email, $optin = true ) {
112
+
113
+ $email = sanitize_text_field( trim( $email ) );
114
+ $good_email = is_email( $email ); //check that email is valid
115
+
116
+ if ( $good_email !== false ) {
117
+
118
+ $response = wp_remote_get( self::$endpoint . 'request-key&email=' . $email . '&optin=' . $optin );
119
+
120
+ if ( is_array( $response ) && isset( $response['body'] ) ) { //verify we have a good response body
121
+
122
+ $body = json_decode( $response['body'], true );
123
+
124
+ if ( is_array( $body ) && isset( $body['apikey'] ) ) { //verify we retrieved an apikey
125
+
126
+ $key = trim( sanitize_text_field( $body['apikey'] ) );
127
+
128
+ if ( strlen( $key ) > 0 ) {
129
+
130
+ return $key;
131
+
132
+ }
133
+
134
+ }
135
+
136
+ }
137
+
138
+ }
139
+
140
+ return false;
141
+
142
+ }
143
+
144
+ /**
145
+ * Execute admin initializations
146
+ *
147
+ * @since 4.5
148
+ *
149
+ * @return void
150
+ */
151
+ public function itsec_admin_init() {
152
+
153
+ //Add Settings sections
154
+ add_settings_section(
155
+ 'ipcheck-settings-brute-force',
156
+ __( 'Get your iThemes Brute Force Protection API Key', 'better-wp-security' ),
157
+ '__return_empty_string',
158
+ 'security_page_toplevel_page_itsec_settings'
159
+ );
160
+
161
+ /*
162
+ add_settings_section(
163
+ 'ipcheck-settings-malware',
164
+ __( 'Get your iThemes Brute Force Protection API Key', 'better-wp-security' ),
165
+ '__return_empty_string',
166
+ 'security_page_toplevel_page_itsec_settings'
167
+ );
168
+ */
169
+
170
+ //Add Settings Fields
171
+ add_settings_field(
172
+ 'itsec_ipcheck',
173
+ __( 'Get your iThemes Brute Force Protection API Key', 'better-wp-security' ),
174
+ array( $this, 'settings_field_api_key_brute_force' ),
175
+ 'security_page_toplevel_page_itsec_settings',
176
+ 'ipcheck-settings-brute-force'
177
+ );
178
+
179
+ /*
180
+ add_settings_field(
181
+ 'itsec_ipcheck',
182
+ __( 'Get your iThemes Malware API Key', 'better-wp-security' ),
183
+ array( $this, 'settings_field_api_key_malware' ),
184
+ 'security_page_toplevel_page_itsec_settings',
185
+ 'ipcheck-settings-malware'
186
+ );
187
+ */
188
+
189
+ if ( isset( $this->settings['api_key'] ) && isset( $this->settings['api_s'] ) ) {
190
+
191
+ add_settings_field(
192
+ 'itsec_ipcheck[api_ban]',
193
+ __( 'Enable iThemes Brute Force Network Protection', 'better-wp-security' ),
194
+ array( $this, 'settings_field_api_ban' ),
195
+ 'security_page_toplevel_page_itsec_settings',
196
+ 'ipcheck-settings-brute-force'
197
+ );
198
+
199
+ }
200
+
201
+ //Register the settings field for the entire module
202
+ register_setting(
203
+ 'security_page_toplevel_page_itsec_settings',
204
+ 'itsec_ipcheck',
205
+ array( $this, 'sanitize_module_input' )
206
+ );
207
+
208
+ }
209
+
210
+ /**
211
+ * Prepare and save options in network settings
212
+ *
213
+ * @since 4.5
214
+ *
215
+ * @return void
216
+ */
217
+ public function itsec_admin_init_multisite() {
218
+
219
+ if ( isset( $_POST['itsec_ipcheck'] ) ) {
220
+
221
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
222
+ die( __( 'Security error!', 'better-wp-security' ) );
223
+ }
224
+
225
+ update_site_option( 'itsec_ipcheck', $_POST['itsec_ipcheck'] ); //we must manually save network options
226
+
227
+ }
228
+
229
+ }
230
+
231
+ /**
232
+ * Sanitize and validate input
233
+ *
234
+ * @since 4.5
235
+ *
236
+ * @param Array $input array of input fields
237
+ *
238
+ * @return Array Sanitized array
239
+ */
240
+ public function sanitize_module_input( $input ) {
241
+
242
+ $input['api_ban'] = isset( $input['api_ban'] ) && $input['api_ban'] == 1 ? true : false;
243
+ $this->settings['api_ban'] = $input['api_ban'];
244
+
245
+ if ( isset( $input['reset'] ) ) {
246
+
247
+ unset( $input['api_key'] );
248
+ unset( $input['api_s'] );
249
+ unset( $input['email'] );
250
+ unset( $input['reset'] );
251
+
252
+ } elseif ( ( isset( $input['email-brute-force'] ) && strlen( trim( $input['email-brute-force'] ) ) > 0 ) || ( isset( $input['email-malware'] ) && strlen( trim( $input['email-malware'] ) ) > 0 ) ) {
253
+
254
+ if ( isset( $input['email-brute-force'] ) && strlen( trim( $input['email-brute-force'] ) ) > 0 ) {
255
+
256
+ $raw_email = trim( sanitize_text_field( $input['email-brute-force'] ) );
257
+ $optin = isset( $input['optin-brute-force'] ) && $input['optin-brute-force'] == 1 ? true : false;
258
+
259
+ } else {
260
+
261
+ $raw_email = trim( sanitize_text_field( $input['email-malware'] ) );
262
+ $optin = isset( $input['optin-malware'] ) && $input['optin-malware'] == 1 ? true : false;
263
+
264
+ }
265
+
266
+ //We don't need to save the email addresses
267
+ unset( $input['email-brute-force'] );
268
+ unset( $input['email-malware'] );
269
+
270
+ //but make sure we keep the optin
271
+ $input['optin'] = $optin;
272
+
273
+ if ( strlen( $raw_email ) > 0 ) {
274
+
275
+ $email = is_email( $raw_email ) ? $raw_email : false;
276
+ $api_error = false;
277
+
278
+ if ( $email === false ) {
279
+
280
+ $type = 'error';
281
+ $message = __( 'The email address you used to get an iThemes API key appears to be invalid. Please enter a valid email address', 'better-wp-security' );
282
+
283
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
284
+
285
+ } else {
286
+
287
+ $key = $this->get_api_key( $email, $optin );
288
+
289
+ if ( $key != false && $key != '' ) {
290
+
291
+ $input['api_key'] = $key;
292
+
293
+ $api_s = $this->activate_api_key( $key );
294
+
295
+ if ( $api_s != false && $api_s != '' ) {
296
+
297
+ $input['api_s'] = $api_s;
298
+ $input['api_ban'] = true;
299
+
300
+ } else {
301
+
302
+ unset( $input['api_key'] );
303
+ $api_error = true;
304
+
305
+ }
306
+
307
+ } else {
308
+
309
+ $api_error = true;
310
+
311
+ }
312
+
313
+ }
314
+
315
+ if ( $api_error === true && ! isset( $input['api_key'] ) && ! isset( $input['api_s'] ) ) {
316
+
317
+ $type = 'error';
318
+ $message = __( 'There was an error getting an API key from the IPCheck server. If the problem persists please contact support.', 'better-wp-security' );
319
+
320
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $message, $type );
321
+
322
+ }
323
+
324
+ unset( $input['optin'] );
325
+
326
+ }
327
+
328
+ } else {
329
+
330
+ $input = $this->settings;
331
+
332
+ }
333
+
334
+ if ( is_multisite() ) {
335
+
336
+ $this->core->show_network_admin_notice( false );
337
+
338
+ $this->settings = $input;
339
+
340
+ }
341
+
342
+ return $input;
343
+
344
+ }
345
+
346
+ /**
347
+ * echos api ban Field in brute force
348
+ *
349
+ * @since 4.5
350
+ *
351
+ * @return void
352
+ */
353
+ public function settings_field_api_ban() {
354
+
355
+ if ( isset( $this->settings['api_ban'] ) && $this->settings['api_ban'] === true ) {
356
+ $api_ban = 1;
357
+ } else {
358
+ $api_ban = 0;
359
+ }
360
+
361
+ echo '<input type="checkbox" id="itsec_ipcheck_api_ban" name="itsec_ipcheck[api_ban]" value="1" ' . checked( 1, $api_ban, false ) . '/>';
362
+ echo '<label for="itsec_ipcheck_api_ban"> ' . __( 'Use the iThemes IPCheck Service to ban IPs reported as a problem by other users in the community.', 'better-wp-security' ) . '</label>';
363
+
364
+ }
365
+
366
+ /**
367
+ * echos api key Field in brute force settings
368
+ *
369
+ * @since 4.5
370
+ *
371
+ * @return void
372
+ */
373
+ public function settings_field_api_key_brute_force() {
374
+
375
+ $this->settings_field_api_key_contents( 'brute-force' );
376
+
377
+ }
378
+
379
+ /**
380
+ * Echos the field contents
381
+ *
382
+ * @since 4.5
383
+ *
384
+ * @param string $module the module
385
+ *
386
+ * @return void
387
+ */
388
+ private function settings_field_api_key_contents( $module ) {
389
+
390
+ $api_key = isset( $this->settings['api_key'] ) ? trim( sanitize_text_field( $this->settings['api_key'] ) ) : false;
391
+ $activated = isset( $this->settings['api_s'] ) ? trim( sanitize_text_field( $this->settings['api_s'] ) ) : false;
392
+ $email = isset( $this->settings['email'] ) && is_email( $this->settings['email'] ) ? trim( $this->settings['email'] ) : '';
393
+
394
+ if ( $api_key == '' || $api_key == false || $activated == '' || $activated == false ) {
395
+
396
+ echo '<input type="text" class="regular-text" id="itsec_ipcheck_email" name="itsec_ipcheck[email-' . $module . ']" value="' . $email . '" placeholder="' . __( 'Enter email and click Save All Changes', 'better-wp-security' ) . '" /><br />';
397
+ echo '<input type="checkbox" id="itsec_ipcheck_optin" name="itsec_ipcheck[optin-' . $module . ']" value="1" checked/>';
398
+ echo '<label for="itsec_ipcheck_optin">' . __( 'Receive email updates about WP Security from iThemes', 'better-wp-security' ) . '</label>';
399
+
400
+ echo '<p class="description">' . __( 'Leverage the power of the iThemes Brute Force Protection network to ban IPs hitting your site. ', 'better-wp-security' ) . '</p>';
401
+
402
+ } else {
403
+
404
+ echo '<div class="itsec_api_key_field">';
405
+ echo '<input type="text" class="regular-text" id="itsec_ipcheck_api_key" name="itsec_ipcheck[api_key]" value="' . $api_key . '" readonly />';
406
+ echo '<span class="submit"><a class="itsec_reset_ipcheck_api_key button-primary">' . __( 'Reset API Key', 'better-wp-security' ) . '</a></span>';
407
+ echo '<p class="description">' . __( 'Your API key for the iThemes Security community appears to be valid.', 'better-wp-security' ) . '</p>';
408
+ echo '</div>';
409
+
410
+ }
411
+
412
+ }
413
+
414
+ /**
415
+ * echos api key Field in malware settings
416
+ *
417
+ * @since 4.5
418
+ *
419
+ * @return void
420
+ */
421
+ public function settings_field_api_key_malware() {
422
+
423
+ $this->settings_field_api_key_contents( 'malware' );
424
+
425
+ }
426
+
427
+ /**
428
+ * Resets API Key
429
+ *
430
+ * @since 4.5
431
+ *
432
+ * @return void
433
+ */
434
+ public function wp_ajax_itsec_api_key_ajax() {
435
+
436
+ if ( ! wp_verify_nonce( sanitize_text_field( $_POST['nonce'] ), 'itsec_api_nonce' ) ) {
437
+ die( __( 'Security error!', 'better-wp-security' ) );
438
+ }
439
+
440
+ $this->settings['reset'] = true;
441
+
442
+ update_site_option( 'itsec_ipcheck', $this->settings );
443
+
444
+ die( true );
445
+
446
+ }
447
+
448
+ }
core/modules/ipcheck/class-itsec-ipcheck.php ADDED
@@ -0,0 +1,394 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * iThemes IPCheck API Wrapper.
5
+ *
6
+ * Provides static calls to the iThemes IPCheck API
7
+ *
8
+ * @package iThemes_Security
9
+ *
10
+ * @since 4.5
11
+ *
12
+ */
13
+ class ITSEC_IPCheck extends ITSEC_Lockout {
14
+
15
+ private static $endpoint = 'http://ipcheck-api.ithemes.com/?action=';
16
+
17
+ private
18
+ $settings;
19
+
20
+ function run() {
21
+
22
+ $this->settings = get_site_option( 'itsec_ipcheck' );
23
+
24
+ //Execute API Brute force protection
25
+ if ( isset( $this->settings['api_ban'] ) && $this->settings['api_ban'] === true ) {
26
+
27
+ add_action( 'wp_login', array( $this, 'wp_login' ), 10, 2 );
28
+ add_action( 'wp_login_failed', array( $this, 'wp_login_failed' ), 1, 1 );
29
+
30
+ add_filter( 'authenticate', array( $this, 'authenticate' ), 10, 3 );
31
+ add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
32
+
33
+ }
34
+
35
+ }
36
+
37
+ /**
38
+ * Sends to lockout class when login form isn't completely filled out and process xml_rpc username
39
+ *
40
+ * @since 4.5
41
+ *
42
+ * @param object $user user or wordpress error
43
+ * @param string $username username attempted
44
+ * @param string $password password attempted
45
+ *
46
+ * @return user object or WordPress error
47
+ */
48
+ public function authenticate( $user, $username = '', $password = '' ) {
49
+
50
+ global $itsec_logger;
51
+
52
+ //Execute brute force if username or password are empty
53
+ if ( isset( $_POST['wp-submit'] ) && ( empty( $username ) || empty( $password ) ) ) {
54
+
55
+ if ( $this->report_ip() === 1 ) {
56
+
57
+ $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
58
+
59
+ $this->execute_lock( false, true );
60
+
61
+ }
62
+
63
+ }
64
+
65
+ return $user;
66
+
67
+ }
68
+
69
+ /**
70
+ * Set transient for caching IPs
71
+ *
72
+ * @since 4.5
73
+ *
74
+ * @param string $ip IP Address
75
+ * @param bool $status if the IP is blocked or not
76
+ * @param int $time length, in seconds, to cache
77
+ *
78
+ * @return void
79
+ */
80
+ private function cache_ip( $ip, $status, $time ) {
81
+
82
+ //@todo one size fits all is too long. Need to adjust time
83
+ set_site_transient( 'itsec_ip_cache_' . esc_sql( $ip ), $status, $time );
84
+
85
+ }
86
+
87
+ /**
88
+ * IP to check for blacklist
89
+ *
90
+ * @since 4.5
91
+ *
92
+ * @param string|null $ip ip to report
93
+ *
94
+ * @return bool true if successfully reported else false
95
+ */
96
+ public function check_ip( $ip = null ) {
97
+
98
+ global $itsec_globals, $itsec_logger;
99
+
100
+ //get current IP if needed
101
+ if ( $ip === null ) {
102
+
103
+ $ip = ITSEC_Lib::get_ip();
104
+
105
+ } else {
106
+
107
+ $ip = trim( sanitize_text_field( $ip ) );
108
+
109
+ }
110
+
111
+ if ( $this->is_ip_whitelisted( $ip ) ) {
112
+ return false;
113
+ }
114
+
115
+ //See if we've checked this IP in the last hour
116
+ $cache_check = get_site_transient( 'itsec_ip_cache_' . esc_sql( $ip ) );
117
+
118
+ if ( is_array( $cache_check ) && isset( $cache_check['status'] ) ) {
119
+ return $cache_check['status'];
120
+ }
121
+
122
+ $action = 'check-ip';
123
+
124
+ if ( ITSEC_Lib::validates_ip_address( $ip ) ) { //verify IP address is valid
125
+
126
+ if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
127
+ return false; //invalid key or secret
128
+ }
129
+
130
+ $args = json_encode(
131
+ array(
132
+ 'apikey' => $this->settings['api_key'], //the api key
133
+ 'behavior' => 'brute-force-login', //type of behanvior we're reporting
134
+ 'ip' => $ip, //the ip to report
135
+ 'site' => home_url( '', 'http' ), //the current site URL
136
+ 'timestamp' => $itsec_globals['current_time_gmt'], //current time (GMT)
137
+ )
138
+ );
139
+
140
+ //Build the request parameters
141
+ $request = array(
142
+ 'body' => array(
143
+ 'request' => $args,
144
+ 'signature' => self::hmac_sha1( $this->settings['api_s'], $action . $args ),
145
+ ),
146
+ );
147
+
148
+ $response = wp_remote_post( self::$endpoint . $action, $request );
149
+
150
+ //Make sure the request was valid and has a valid body
151
+ if ( is_array( $response ) && isset( $response['body'] ) ) {
152
+
153
+ $response = json_decode( $response['body'], true );
154
+
155
+ if ( is_array( $response ) && isset( $response['success'] ) && $response['success'] == true ) {
156
+
157
+ $cache = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
158
+
159
+ if ( isset( $response['block'] ) && $response['block'] == true ) {
160
+
161
+ $expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $cache );
162
+ $expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $cache );
163
+
164
+ $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array(
165
+ 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => 'host'
166
+ ), $ip );
167
+
168
+ self::cache_ip( $ip, array( 'status' => true ), $cache );
169
+
170
+ return true; //API reports IP is blocked
171
+
172
+ } else {
173
+
174
+ self::cache_ip( $ip, array( 'status' => false ), $cache );
175
+
176
+ return false; //API reports IP is not blocked or no report (default to no block)
177
+
178
+ }
179
+
180
+ }
181
+
182
+ }
183
+
184
+ }
185
+
186
+ return false;
187
+
188
+ }
189
+
190
+ /**
191
+ * Calculates the HMAC of a string using SHA1.
192
+ *
193
+ * there is a native PHP hmac function, but we use this one for
194
+ * the widest compatibility with older PHP versions
195
+ *
196
+ * @param string $key the shared secret key used to generate the mac
197
+ * @param string $data data to be signed
198
+ *
199
+ *
200
+ * @return string base64 encoded hmac
201
+ */
202
+ private function hmac_sha1( $key, $data ) {
203
+
204
+ if ( strlen( $key ) > 64 ) {
205
+ $key = pack( 'H*', sha1( $key ) );
206
+ }
207
+
208
+ $key = str_pad( $key, 64, chr( 0x00 ) );
209
+
210
+ $ipad = str_repeat( chr( 0x36 ), 64 );
211
+
212
+ $opad = str_repeat( chr( 0x5c ), 64 );
213
+
214
+ $hmac = pack( 'H*', sha1( ( $key ^ $opad ) . pack( 'H*', sha1( ( $key ^ $ipad ) . $data ) ) ) );
215
+
216
+ return base64_encode( $hmac );
217
+
218
+ }
219
+
220
+ /**
221
+ * Register IPCheck for logger
222
+ *
223
+ * @since 4.5
224
+ *
225
+ * @param array $logger_modules array of logger modules
226
+ *
227
+ * @return array array of logger modules
228
+ */
229
+ public function itsec_logger_modules( $logger_modules ) {
230
+
231
+ $logger_modules['ipcheck'] = array(
232
+ 'type' => 'ipcheck',
233
+ 'function' => __( 'IP Flagged as bad by iThemes IPCheck', 'better-wp-security' ),
234
+ );
235
+
236
+ return $logger_modules;
237
+
238
+ }
239
+
240
+ /**
241
+ * Send offending IP to IPCheck API
242
+ *
243
+ * @since 4.5
244
+ *
245
+ * @param string|null $ip ip to report
246
+ * @param int $type type of behavior to report
247
+ *
248
+ * @return int -1 on failure, 0 if report successful and IP not blocked, 1 if IP successful and IP blocked
249
+ */
250
+ public function report_ip( $ip = null, $type = 1 ) {
251
+
252
+ global $itsec_globals, $itsec_logger;
253
+
254
+ $action = 'report-ip';
255
+
256
+ /**
257
+ * Switch types or return false if no valid type
258
+ *
259
+ * Valid types:
260
+ * 1 = invalid/failed login
261
+ *
262
+ */
263
+ switch ( $type ) {
264
+
265
+ case 1:
266
+ $behavior = 'brute-force-login';
267
+ break;
268
+ default:
269
+ return -1;
270
+
271
+ }
272
+
273
+ //get current IP if needed
274
+ if ( $ip === null ) {
275
+
276
+ $ip = ITSEC_Lib::get_ip();
277
+
278
+ } else {
279
+
280
+ $ip = trim( sanitize_text_field( $ip ) );
281
+
282
+ }
283
+
284
+ if ( $this->is_ip_whitelisted( $ip ) ) {
285
+ return 0;
286
+ }
287
+
288
+ if ( ITSEC_Lib::validates_ip_address( $ip ) ) { //verify IP address is valid
289
+
290
+ if ( ! isset( $this->settings['api_key'] ) || ! isset( $this->settings['api_s'] ) ) {
291
+ return -1; //invalid key or secret
292
+ }
293
+
294
+ $args = json_encode(
295
+ array(
296
+ 'apikey' => $this->settings['api_key'], //the api key
297
+ 'behavior' => $behavior, //type of behanvior we're reporting
298
+ 'ip' => $ip, //the ip to report
299
+ 'site' => home_url( '', 'http' ), //the current site URL
300
+ 'timestamp' => $itsec_globals['current_time_gmt'], //current time (GMT)
301
+ )
302
+ );
303
+
304
+ //Build the request parameters
305
+ $request = array(
306
+ 'body' => array(
307
+ 'request' => $args,
308
+ 'signature' => self::hmac_SHA1( $this->settings['api_s'], $action . $args ),
309
+ ),
310
+ );
311
+
312
+ $response = wp_remote_post( self::$endpoint . $action, $request );
313
+
314
+ //Make sure the request was valid and has a valid body
315
+ if ( is_array( $response ) && isset( $response['body'] ) ) {
316
+
317
+ $response = json_decode( $response['body'], true );
318
+
319
+ if ( is_array( $response ) && isset( $response['success'] ) && $response['success'] == true ) {
320
+
321
+ if ( isset( $response['block'] ) && $response['block'] == true ) {
322
+
323
+ $cache = isset( $response['cache_ttl'] ) ? absint( $response['cache_ttl'] ) : 3600;
324
+
325
+ $expiration = date( 'Y-m-d H:i:s', $itsec_globals['current_time'] + $cache );
326
+ $expiration_gmt = date( 'Y-m-d H:i:s', $itsec_globals['current_time_gmt'] + $cache );
327
+
328
+ $itsec_logger->log_event( __( 'lockout', 'better-wp-security' ), 10, array(
329
+ 'expires' => $expiration, 'expires_gmt' => $expiration_gmt, 'type' => 'host'
330
+ ), $ip );
331
+
332
+ self::cache_ip( $ip, array( 'status' => true ), $cache );
333
+
334
+ return 1; //ip report success. Just return true for now
335
+
336
+ } else {
337
+
338
+ return 0;
339
+
340
+ }
341
+
342
+ }
343
+
344
+ }
345
+
346
+ }
347
+
348
+ return -1;
349
+
350
+ }
351
+
352
+ /**
353
+ * Make sure user isn't already locked out even on successful form submission
354
+ *
355
+ * @since 4.5
356
+ *
357
+ * @return void
358
+ */
359
+ public function wp_login() {
360
+
361
+ global $itsec_logger;
362
+
363
+ if ( $this->check_ip() === true ) {
364
+
365
+ $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
366
+
367
+ $this->execute_lock( false, true );
368
+
369
+ }
370
+
371
+ }
372
+
373
+ /**
374
+ * Sends to lockout class when username and password are filled out and wrong
375
+ *
376
+ * @since 4.5
377
+ *
378
+ * @return void
379
+ */
380
+ public function wp_login_failed() {
381
+
382
+ global $itsec_logger;
383
+
384
+ if ( $this->report_ip() === 1 ) {
385
+
386
+ $itsec_logger->log_event( 'ipcheck', 10, array(), ITSEC_Lib::get_ip() );
387
+
388
+ $this->execute_lock( false, true );
389
+
390
+ }
391
+
392
+ }
393
+
394
+ }
core/modules/ipcheck/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ipcheck/js/admin-ipcheck.js ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ //process tooltip actions
4
+ jQuery( '.itsec_reset_ipcheck_api_key' ).click( function ( event ) {
5
+
6
+ event.preventDefault();
7
+
8
+ var button = this;
9
+
10
+ var data = {
11
+ action : 'itsec_api_key_ajax',
12
+ nonce : itsec_ipcheck.api_nonce
13
+ };
14
+
15
+ //call the ajax
16
+ jQuery.ajax(
17
+ {
18
+ url : ajaxurl,
19
+ type : 'POST',
20
+ data : data,
21
+ complete : function ( response ) {
22
+
23
+ var new_content = '<input id="itsec_ipcheck_email" class="regular-text" name="itsec_ipcheck[email-brute-force]" value="" type="text" placeholder="' + itsec_ipcheck.text3 + '"><br /><input id="itsec_ipcheck_optin" name="itsec_ipcheck[optin-brute-force]" value="1" checked="" type="checkbox"><label for="itsec_ipcheck_optin">' + itsec_ipcheck.text1 + '</label><p class="description">' + itsec_ipcheck.text2 + '</p>';
24
+ var new_enable = '';
25
+
26
+ jQuery( '.itsec_api_key_field' ).replaceWith( new_content );
27
+ jQuery( '#itsec_ipcheck_api_ban' ).parent().parent().replaceWith( new_enable );
28
+
29
+ }
30
+ }
31
+ );
32
+
33
+ } );
34
+
35
+ } );
core/modules/ipcheck/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ipcheck/setup.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_IPCheck_Setup' ) ) {
4
+
5
+ class ITSEC_IPCheck_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'api_ban' => true,
16
+ );
17
+
18
+ if ( isset( $itsec_setup_action ) ) {
19
+
20
+ switch ( $itsec_setup_action ) {
21
+
22
+ case 'activate':
23
+ $this->execute_activate();
24
+ break;
25
+ case 'upgrade':
26
+ $this->execute_upgrade();
27
+ break;
28
+ case 'deactivate':
29
+ $this->execute_deactivate();
30
+ break;
31
+ case 'uninstall':
32
+ $this->execute_uninstall();
33
+ break;
34
+
35
+ }
36
+
37
+ } else {
38
+ wp_die( 'error' );
39
+ }
40
+
41
+ }
42
+
43
+ /**
44
+ * Execute module activation.
45
+ *
46
+ * @since 4.0
47
+ *
48
+ * @return void
49
+ */
50
+ public function execute_activate() {
51
+
52
+ $options = get_site_option( 'itsec_ipcheck' );
53
+
54
+ if ( $options === false ) {
55
+
56
+ add_site_option( 'itsec_ipcheck', $this->defaults );
57
+
58
+ }
59
+
60
+ }
61
+
62
+ /**
63
+ * Execute module deactivation
64
+ *
65
+ * @return void
66
+ */
67
+ public function execute_deactivate() {
68
+
69
+ global $wpdb;
70
+
71
+ $wpdb->query( "DELETE FROM `" . $wpdb->base_prefix . "options` WHERE `option_name` LIKE ('%_itsec_ip_cache%')" );
72
+
73
+ }
74
+
75
+ /**
76
+ * Execute module uninstall
77
+ *
78
+ * @return void
79
+ */
80
+ public function execute_uninstall() {
81
+
82
+ $this->execute_deactivate();
83
+
84
+ delete_site_option( 'itsec_ipcheck' );
85
+
86
+ }
87
+
88
+ /**
89
+ * Execute module upgrade
90
+ *
91
+ * @return void
92
+ */
93
+ public function execute_upgrade() {
94
+ }
95
+
96
+ }
97
+
98
+ }
99
+
100
+ new ITSEC_IPCheck_Setup();
core/modules/malware/class-ithemes-sync-verb-itsec-do-malware-scan.php ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Malware_Do_Scan extends Ithemes_Sync_Verb {
4
+ public static $name = 'itsec-do-malware-scan';
5
+ public static $description = '';
6
+
7
+ public $default_arguments = array();
8
+
9
+ public function run( $arguments ) {
10
+ $arguments = Ithemes_Sync_Functions::merge_defaults( $arguments, $this->default_arguments );
11
+
12
+ require_once( dirname( __FILE__ ) . '/class-itsec-malware-scanner.php' );
13
+
14
+ return ITSEC_Malware_Scanner::scan();
15
+ }
16
+
17
+ }
core/modules/malware/class-ithemes-sync-verb-itsec-get-malware-scan-log.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Log extends Ithemes_Sync_Verb {
4
+ public static $name = 'itsec-get-malware-scan-log';
5
+ public static $description = '';
6
+
7
+ public $default_arguments = array(
8
+ 'count' => 10,
9
+ );
10
+
11
+ public function run( $arguments ) {
12
+ $arguments = Ithemes_Sync_Functions::merge_defaults( $arguments, $this->default_arguments );
13
+
14
+ global $itsec_logger;
15
+
16
+ $items = $itsec_logger->get_events( 'malware' );
17
+ $response = array();
18
+
19
+ foreach ( $items as $item ) {
20
+ $item['log_data'] = maybe_unserialize( $item['log_data'] );
21
+
22
+ if ( ! isset( $item['log_data']['SCAN']['SITE'] ) ) {
23
+ // Don't return old scan data.
24
+ continue;
25
+ }
26
+
27
+ $response[] = $item;
28
+
29
+ if ( ( $arguments['count'] > 0 ) && ( count( $response ) == $arguments['count'] ) ) {
30
+ break;
31
+ }
32
+ }
33
+
34
+ return $response;
35
+ }
36
+ }
core/modules/malware/class-itsec-malware-admin.php ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Malware_Admin {
4
+ private $settings;
5
+ private $core;
6
+
7
+ function run( $core ) {
8
+ global $pagenow;
9
+
10
+ $this->core = $core;
11
+ $this->settings = get_site_option( 'itsec_malware' );
12
+
13
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) );
14
+ add_action( 'wp_ajax_itsec_malware_scan', array( $this, 'get_ajax_scan_response' ) );
15
+
16
+ add_filter( 'itsec_logger_displays', array( $this, 'itsec_logger_displays' ) );
17
+ add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
18
+ add_filter( 'itsec_logger_filter_malware_data_column_details', array( $this, 'filter_logger_data_column_details' ), 10, 2 );
19
+
20
+ if (
21
+ ! empty( $pagenow ) &&
22
+ 'admin.php' === $pagenow &&
23
+ ! empty( $_GET['page'] ) &&
24
+ in_array( $_GET['page'], array( 'toplevel_page_itsec_settings', 'toplevel_page_itsec_logs' ) )
25
+ ) {
26
+ add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) );
27
+ }
28
+ }
29
+
30
+ public function filter_logger_data_column_details( $details, $data ) {
31
+ if ( is_wp_error( $data ) || ( ! empty( $data ) && ! empty( $data['SCAN']['SITE'] ) ) ) {
32
+ // Results for Sucuri scans.
33
+
34
+ require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
35
+
36
+ $details = "<div class='itsec-malware-scan-results-wrapper'>\n";
37
+ $details .= ITSEC_Malware_Scan_Results_Template::get_html( $data );
38
+ $details .= "</div>\n";
39
+ }
40
+
41
+ return $details;
42
+ }
43
+
44
+ /**
45
+ * Register malware for logger
46
+ *
47
+ * @since 4.4
48
+ *
49
+ * @param array $logger_modules array of logger modules
50
+ *
51
+ * @return array array of logger modules
52
+ */
53
+ public function itsec_logger_modules( $logger_modules ) {
54
+ $logger_modules['malware'] = array(
55
+ 'type' => 'malware',
56
+ 'function' => __( 'Malware Scan', 'better-wp-security' ),
57
+ );
58
+
59
+ return $logger_modules;
60
+ }
61
+
62
+ public function enqueue_admin_scripts() {
63
+ global $itsec_globals;
64
+
65
+ $script_id = 'itsec-malware-scan-script';
66
+ $script_url = plugins_url( 'js/malware.js', __FILE__ );
67
+ $js_var = 'itsecMalwareScanData';
68
+
69
+ $data = array(
70
+ 'nonce' => wp_create_nonce( 'itsec-malware-get-ajax-scan-response' ),
71
+ 'clickedButtonText' => __( 'Scanning. Please Wait.', 'better-wp-security' ),
72
+ 'hideDetailsText' => __( 'Hide Details', 'better-wp-security' ),
73
+ 'errorMessages' => array(
74
+ 'ajaxUnknown' => esc_html__( 'An error prevented the scan from completing as expected. The browser was unable to successfully request scan results from the site. This could be due to a plugin conflict or a server configuration issue. The following error message was received: %1$s', 'better-wp-security' ),
75
+ 'ajaxTimeout' => esc_html__( 'An error prevented the scan from completing as expected. The site took too long to respond. This could be due to a plugin conflict or a server configuration issue. The following error message was received: %1$s', 'better-wp-security' ),
76
+ 'nonceFailure' => esc_html__( 'An error prevented the scan from completing as expected. The nonce check, which is used to protect your site, failed. Please refresh the page and try the scan again.', 'better-wp-security' ),
77
+ 'invalidUser' => esc_html__( 'An error prevented the scan from completing as expected. The currently logged in user does not have sufficient permissions to run this scan. You may need to log out of the site and log back in.', 'better-wp-security' ),
78
+ 'parseError' => esc_html__( 'An error prevented the scan from completing as expected. The site returned data in an unexpected format. This could be due to a plugin conflict or a server configuration issue. The received data was in the following format: %1$s', 'better-wp-security' ),
79
+ ),
80
+ );
81
+
82
+ wp_enqueue_script( $script_id, $script_url, array( 'jquery' ), $itsec_globals['plugin_build'] );
83
+ wp_localize_script( $script_id, $js_var, $data );
84
+
85
+
86
+ $style_id = 'itsec-malware-scan-style';
87
+ $style_url = plugins_url( 'css/malware.css', __FILE__ );
88
+
89
+ wp_enqueue_style( $style_id, $style_url );
90
+ }
91
+
92
+ protected function get_scan_details() {
93
+ require_once( dirname( __FILE__ ) . '/class-itsec-malware-scanner.php' );
94
+ require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
95
+
96
+ $results = ITSEC_Malware_Scanner::scan();
97
+ $html = ITSEC_Malware_Scan_Results_Template::get_html( $results );
98
+
99
+ return $html;
100
+ }
101
+
102
+ public function get_ajax_scan_response() {
103
+ global $itsec_globals;
104
+
105
+ check_ajax_referer( 'itsec-malware-get-ajax-scan-response' );
106
+
107
+ if ( ! current_user_can( $itsec_globals['plugin_access_lvl'] ) ) {
108
+ die( '-2' );
109
+ }
110
+
111
+ echo $this->get_scan_details();
112
+
113
+ wp_die();
114
+ }
115
+
116
+ /**
117
+ * Add meta boxes to primary options pages
118
+ *
119
+ * @return void
120
+ */
121
+ public function add_admin_meta_boxes() {
122
+
123
+ $id = 'malware_options';
124
+ $title = __( 'Malware Scanning', 'better-wp-security' );
125
+
126
+ add_meta_box(
127
+ $id,
128
+ $title,
129
+ array( $this, 'metabox_malware_settings' ),
130
+ 'security_page_toplevel_page_itsec_settings',
131
+ 'advanced',
132
+ 'core'
133
+ );
134
+
135
+ $this->core->add_toc_item(
136
+ array(
137
+ 'id' => $id,
138
+ 'title' => $title,
139
+ )
140
+ );
141
+
142
+ }
143
+
144
+ /**
145
+ * Array of metaboxes for the logs screen
146
+ *
147
+ * @since 4.4
148
+ *
149
+ * @param array $displays metabox array
150
+ *
151
+ * @return array metabox array
152
+ */
153
+ public function itsec_logger_displays( $displays ) {
154
+ $displays[] = array(
155
+ 'module' => 'malware',
156
+ 'title' => __( 'Malware Scan', 'better-wp-security' ),
157
+ 'callback' => array( $this, 'logs_metabox_content' )
158
+ );
159
+
160
+ return $displays;
161
+ }
162
+
163
+ /**
164
+ * Render the settings metabox
165
+ *
166
+ * @return void
167
+ */
168
+ public function logs_metabox_content() {
169
+
170
+ $this->malware_scan_form( 'logs' );
171
+
172
+ if ( ! class_exists( 'ITSEC_Malware_Log' ) ) {
173
+ require( dirname( __FILE__ ) . '/class-itsec-malware-log.php' );
174
+ }
175
+
176
+ $log_display = new ITSEC_Malware_Log();
177
+
178
+ $log_display->prepare_items();
179
+ $log_display->display();
180
+
181
+ }
182
+
183
+ /**
184
+ * Echos the one-time malware scan form
185
+ *
186
+ * @since 4.0
187
+ *
188
+ * @param string $origin the origin screen where the button was pressed
189
+ *
190
+ * @return void
191
+ */
192
+ public function malware_scan_form( $origin ) {
193
+ $this->show_malware_scan_button( false );
194
+ }
195
+
196
+ /**
197
+ * Render the settings metabox
198
+ *
199
+ * @since 4.0
200
+ *
201
+ * @return void
202
+ */
203
+ public function metabox_malware_settings() {
204
+ $this->show_malware_scan_button();
205
+ }
206
+
207
+ protected function show_malware_scan_button( $show_log_link = true ) {
208
+ echo '<p>' . __( 'This malware scan is powered by <a href="https://ithemes.com/sitecheck">Sucuri SiteCheck</a>. It checks for known malware, blacklisting status, website errors, and out-of-date software. Although the Sucuri team does its best to provide the best results, 100% accuracy is not realistic and is not guaranteed.', 'better-wp-security' ) . "</p>\n";
209
+
210
+ if ( $show_log_link ) {
211
+ echo '<p>' . sprintf( __( 'Results of previous malware scans can be found on the <a href="%s">logs page</a>.', 'better-wp-security' ), admin_url( 'admin.php?page=toplevel_page_itsec_logs&itsec_log_filter=malware#itsec_log_all' ) ) . "</p>\n";
212
+ }
213
+
214
+ echo "<div class='itsec-malware-scan-results-wrapper'></div>\n";
215
+ echo '<input class="button-primary hide-if-no-js" type="submit" id="itsec-malware-scan" name="do-malware-scan" value="' . esc_attr__( 'Scan Homepage for Malware', 'better-wp-security' ) . "\" />\n";
216
+ echo '<div class="error inline hide-if-js"><p>' . __( 'The malware scanner requires Javascript in order to function. If Javascript is disabled in your browser, please enable it. If Javascript is not disabled, a script from another plugin, the theme, or a broken WordPress file is preventing the malware scanner\'s script from executing properly. Please try disabling other plugins to see if that resolves the issue.', 'better-wp-security' ) . "</p></div>\n";
217
+ }
218
+ }
core/modules/malware/class-itsec-malware-log.php ADDED
@@ -0,0 +1,340 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Log malware scan reports
5
+ *
6
+ * @package iThemes-Security
7
+ * @subpackage Intrusion-Detection
8
+ * @since 4.4
9
+ */
10
+ final class ITSEC_Malware_Log extends ITSEC_WP_List_Table {
11
+
12
+ function __construct() {
13
+
14
+ parent::__construct(
15
+ array(
16
+ 'singular' => 'itsec_malware_log_item',
17
+ 'plural' => 'itsec_malware_log_items',
18
+ 'ajax' => true
19
+ )
20
+ );
21
+
22
+ }
23
+
24
+ /**
25
+ * Define first time column
26
+ *
27
+ * @param array $item array of row data
28
+ *
29
+ * @return string formatted output
30
+ *
31
+ **/
32
+ function column_details( $item ) {
33
+
34
+ $content = '';
35
+
36
+ if ( ! empty( $item['results'] ) && in_array( $item['results'], array( 'clean', 'warn', 'error' ) ) ) {
37
+ // Results for Sucuri scans.
38
+
39
+ require_once( dirname( __FILE__ ) . '/class-itsec-malware-scan-results-template.php' );
40
+
41
+ $content .= '<a href="itsec-log-malware-row-' . $item['count'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
42
+ $content .= '<div id="itsec-log-malware-row-' . $item['count'] . '" style="display:none;">';
43
+ $content .= "<div class='itsec-malware-scan-results-wrapper'>\n";
44
+
45
+ $content .= ITSEC_Malware_Scan_Results_Template::get_html( $item['data'] );
46
+
47
+ $content .= "</div>\n";
48
+ $content .= "</div>\n";
49
+ } else if ( isset( $item['report'] ) ) {
50
+ // Results for legacy VirusTotal scans.
51
+
52
+ $content .= '<a href="itsec-log-malware-row-' . $item['count'] . '" class="dialog">' . __( 'Details', 'better-wp-security' ) . '</a>';
53
+
54
+ $content .= '<div id="itsec-log-malware-row-' . $item['count'] . '" style="display:none;">';
55
+
56
+ if ( isset( $item['report']['resource'] ) ) {
57
+ $content .= '<strong>' . __( 'Resource Scanned', 'better-wp-security' ) . ':</strong> ' . $item['report']['resource'] . '<br />';
58
+ }
59
+
60
+ if ( isset( $item['report']['results'] ) ) {
61
+
62
+ if ( isset( $item['report']['results']['total'] ) ) {
63
+ $content .= '<strong>' . __( 'Total Scans', 'better-wp-security' ) . ':</strong> ' . $item['report']['results']['total'] . '<br />';
64
+ }
65
+
66
+ if ( isset( $item['report']['results']['positives'] ) ) {
67
+ $content .= '<strong>' . __( 'Problems Found', 'better-wp-security' ) . ':</strong> ' . $item['report']['results']['positives'] . '<br />';
68
+ }
69
+
70
+ if ( isset( $item['report']['results']['scans'] ) ) {
71
+
72
+ $content .= '<h3>' . __( 'Scan Details', 'better-wp-security' ) . '</h3>';
73
+
74
+ $content .= '<ol class="malware_detail_list">';
75
+
76
+ foreach ( $item['report']['results']['scans'] as $service => $results ) {
77
+ $content .= '<li class="malware_detail"><strong>' . $service . '</strong>: ' . $results['result'] . ' ' . $results['detected'] . '<br /></li>';
78
+ }
79
+
80
+ $content .= '</ol>';
81
+
82
+ }
83
+
84
+ }
85
+
86
+ $content .= '</div>';
87
+
88
+ }
89
+
90
+ return $content;
91
+
92
+ }
93
+
94
+ /**
95
+ * Define results column
96
+ *
97
+ * @param array $item array of row data
98
+ *
99
+ * @return string formatted output
100
+ *
101
+ **/
102
+ function column_results( $item ) {
103
+
104
+ $content = '';
105
+
106
+ if ( ! empty( $item['results'] ) && in_array( $item['results'], array( 'clean', 'warn', 'error' ) ) ) {
107
+ // Results for Sucuri scans.
108
+ if ( 'error' === $item['results'] ) {
109
+ $content = '<span style="color: red">' . __( 'Error', 'better-wp-security' ) . '</span>';
110
+ } else if ( 'warn' === $item['results'] ) {
111
+ $content = '<span style="color: red">' . __( 'Warning', 'better-wp-security' ) . '</span>';
112
+ } else {
113
+ $content = '<span style="color: green">' . __( 'Clean', 'better-wp-security' ) . '</span>';
114
+ }
115
+ } else if ( isset( $item['report']['results']['positives'] ) ) {
116
+ // Results for legacy VirusTotal scans.
117
+
118
+ if ( $item['report']['results']['positives'] == 0 ) {
119
+ $content = '<span style="color: green">' . __( 'Clean', 'better-wp-security' ) . '</span>';
120
+ } else {
121
+ $content = '<span style="color: red">' . __( 'Issues Detected', 'better-wp-security' ) . '</span>';
122
+ }
123
+
124
+ }
125
+
126
+ return $content;
127
+
128
+ }
129
+
130
+ /**
131
+ * Define time column
132
+ *
133
+ * @param array $item array of row data
134
+ *
135
+ * @return string formatted output
136
+ *
137
+ **/
138
+ function column_time( $item ) {
139
+
140
+ return $item['time'];
141
+
142
+ }
143
+
144
+ /**
145
+ * Define count column
146
+ *
147
+ * @param array $item array of row data
148
+ *
149
+ * @return string formatted output
150
+ *
151
+ **/
152
+ function column_user( $item ) {
153
+
154
+ $user = get_user_by( 'login', $item['user'] );
155
+
156
+ if ( $user !== false ) {
157
+
158
+ return '<a href="/wp-admin/user-edit.php?user_id=' . $user->ID . '">' . $item['user'] . '</a>';
159
+
160
+ } else {
161
+
162
+ return $item['user'];
163
+
164
+ }
165
+
166
+ }
167
+
168
+ /**
169
+ * Define uri column
170
+ *
171
+ * @param array $item array of row data
172
+ *
173
+ * @return string formatted output
174
+ *
175
+ **/
176
+ function column_action( $item ) {
177
+
178
+ return $item['action'];
179
+
180
+ }
181
+
182
+ /**
183
+ * Define Columns
184
+ *
185
+ * @return array array of column titles
186
+ */
187
+ public function get_columns() {
188
+
189
+ return array(
190
+ 'time' => __( 'Time', 'better-wp-security' ),
191
+ 'action' => __( 'Action', 'better-wp-security' ),
192
+ 'results' => __( 'Results', 'better-wp-security' ),
193
+ 'user' => __( 'User', 'better-wp-security' ),
194
+ 'details' => __( 'Details', 'better-wp-security' ),
195
+ );
196
+
197
+ }
198
+
199
+ /**
200
+ * Prepare data for table
201
+ *
202
+ * @return void
203
+ */
204
+ public function prepare_items() {
205
+ global $itsec_logger, $wpdb;
206
+
207
+ $columns = $this->get_columns();
208
+ $hidden = array();
209
+ $this->_column_headers = array( $columns, $hidden, false );
210
+ $per_page = 20;
211
+ $current_page = $this->get_pagenum();
212
+ $total_items = $wpdb->get_var( "SELECT COUNT(*) FROM `" . $wpdb->base_prefix . "itsec_log` WHERE `log_type`='malware'" );
213
+
214
+ $items = $itsec_logger->get_events( 'malware', array(), $per_page, ( ( $current_page - 1 ) * $per_page ), 'log_date' );
215
+
216
+ $table_data = array();
217
+
218
+ $count = 0;
219
+
220
+ foreach ( $items as $item ) { //loop through and group 404s
221
+
222
+ $log_data = maybe_unserialize( $item['log_data'] );
223
+
224
+ $table_data[$count]['time'] = sanitize_text_field( $item['log_date'] );
225
+ $table_data[$count]['host'] = sanitize_text_field( $item['log_host'] );
226
+
227
+ if ( strlen( trim( sanitize_text_field( $item['log_username'] ) ) ) > 0 ) {
228
+
229
+ $table_data[$count]['user'] = sanitize_text_field( $item['log_username'] );
230
+
231
+ } elseif ( intval( $item['log_user'] ) > 0 && ITSEC_Lib::user_id_exists( $item['log_user'] ) ) {
232
+
233
+ $user = get_user_by( 'id', $item['log_user'] );
234
+
235
+ $table_data[$count]['user'] = $user->data->user_login;
236
+
237
+ } else {
238
+
239
+ $table_data[$count]['user'] = '';
240
+
241
+ }
242
+
243
+ $table_data[$count]['action'] = ( is_array( $log_data ) && isset( $log_data['type'] ) ) ? sanitize_text_field( $log_data['type'] ) : __( 'Malware Scan Report', 'better-wp-security' );
244
+
245
+ if ( is_wp_error( $log_data ) ) {
246
+ $table_data[$count]['results'] = 'error';
247
+ $table_data[$count]['data'] = $log_data;
248
+ } else if ( isset( $log_data['SCAN']['SITE'] ) ) {
249
+ // New log data from Sucuri scan.
250
+
251
+ if (
252
+ empty( $log_data['SYSTEM']['WARN'] ) &&
253
+ empty( $log_data['MALWARE']['WARN'] ) &&
254
+ empty( $log_data['BLACKLIST']['WARN'] )
255
+ ) {
256
+ $table_data[$count]['results'] = 'clean';
257
+ } else {
258
+ $table_data[$count]['results'] = 'warn';
259
+ }
260
+
261
+ $table_data[$count]['data'] = $log_data;
262
+ } else {
263
+ // Legacy log data from VirusTotal scan.
264
+
265
+ if ( isset( $log_data['resource'] ) ) {
266
+
267
+ $table_data[$count]['report'] = array(
268
+ 'resource' => $log_data['resource'],
269
+ );
270
+
271
+ } else {
272
+
273
+ $table_data[$count]['report'] = array();
274
+
275
+ }
276
+
277
+ if ( isset( $log_data['report'] ) ) {
278
+ $table_data[$count]['report']['results'] = $log_data['report'];
279
+ }
280
+
281
+ }
282
+
283
+ $table_data[$count]['count'] = $count;
284
+
285
+ $count ++;
286
+
287
+ }
288
+
289
+ // usort( $table_data, array( $this, 'sortrows' ) );
290
+
291
+ $this->items = $table_data;
292
+
293
+ $this->set_pagination_args(
294
+ array(
295
+ 'total_items' => $total_items,
296
+ 'per_page' => $per_page,
297
+ 'total_pages' => ceil( $total_items / $per_page )
298
+ )
299
+ );
300
+ }
301
+
302
+ /**
303
+ * Sorts rows by count in descending order
304
+ *
305
+ * @param array $a first array to compare
306
+ * @param array $b second array to compare
307
+ *
308
+ * @return int comparison result
309
+ */
310
+ function sortrows( $a, $b ) {
311
+
312
+ // If no sort, default to count
313
+ $orderby = ( ! empty( $_GET['orderby'] ) ) ? esc_attr( $_GET['orderby'] ) : 'time';
314
+
315
+ // If no order, default to desc
316
+ $order = ( ! empty( $_GET['order'] ) ) ? esc_attr( $_GET['order'] ) : 'desc';
317
+
318
+ if ( $orderby == 'count' ) {
319
+
320
+ if ( intval( $a[$orderby] ) < intval( $b[$orderby] ) ) {
321
+ $result = - 1;
322
+ } elseif ( intval( $a[$orderby] ) === intval( $b[$orderby] ) ) {
323
+ $result = 0;
324
+ } else {
325
+ $result = 1;
326
+ }
327
+
328
+ } else {
329
+
330
+ // Determine sort order
331
+ $result = strcmp( $a[$orderby], $b[$orderby] );
332
+
333
+ }
334
+
335
+ // Send final sort direction to usort
336
+ return ( $order === 'asc' ) ? $result : - $result;
337
+
338
+ }
339
+
340
+ }
core/modules/malware/class-itsec-malware-scan-results-template.php ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Malware_Scan_Results_Template {
4
+ public static function get_html( $results ) {
5
+ $html = "<div class='itsec-malware-scan-results'>\n";
6
+
7
+ if ( is_wp_error( $results ) ) {
8
+ $html .= self::get_wp_error_details( $results );
9
+ } else {
10
+ $html .= self::get_system_error_details( $results );
11
+ $html .= self::get_malware_details( $results );
12
+ $html .= self::get_blacklist_details( $results );
13
+ }
14
+
15
+ $html .= "</div>\n";
16
+
17
+ return $html;
18
+ }
19
+
20
+ protected static function get_wp_error_details( $results ) {
21
+ $status = 'error';
22
+ $description = __( 'The scan failed to properly scan the site.', 'better-wp-security' );
23
+
24
+ $details = '<p>' . sprintf( __( 'Error Message: %s', 'better-wp-security' ), $results->get_error_message() ) . "</p>\n";
25
+ $details .= '<p>' . sprintf( __( 'Error Code: <code>%s</code>', 'better-wp-security' ), esc_html( $results->get_error_code() ) ) . "</p>\n";
26
+
27
+ return self::get_wrapped_section( 'wp-error', $status, $description, $details );
28
+ }
29
+
30
+ protected static function get_blacklist_details( $results ) {
31
+ $status = 'clean';
32
+ $description = __( 'Blacklist', 'better-wp-security' );
33
+ $details = false;
34
+
35
+ $list = '';
36
+
37
+ if ( ! empty( $results['BLACKLIST']['WARN'] ) ) {
38
+ $status = 'warn';
39
+
40
+ foreach ( (array) $results['BLACKLIST']['WARN'] as $entry ) {
41
+ $list .= sprintf( '<li class="itsec-malware-scan-warn"><span><a href="%1$s">%2$s</a><span></li>', $entry[1], $entry[0] ) . "\n";
42
+ }
43
+ }
44
+
45
+ if ( ! empty( $results['BLACKLIST']['INFO'] ) ) {
46
+ foreach ( (array) $results['BLACKLIST']['INFO'] as $entry ) {
47
+ $list .= sprintf( '<li class="itsec-malware-scan-clean"><span><a href="%1$s">%2$s</a></span></li>', $entry[1], $entry[0] ) . "\n";
48
+ }
49
+ }
50
+
51
+ if ( ! empty( $list ) ) {
52
+ $details = "<ul>\n$list</ul>\n";
53
+ }
54
+
55
+ return self::get_wrapped_section( 'malware', $status, $description, $details );
56
+ }
57
+
58
+ protected static function get_malware_details( $results ) {
59
+ $status = 'clean';
60
+ $description = __( 'Malware', 'better-wp-security' );
61
+ $details = false;
62
+
63
+ $list = '';
64
+
65
+ if ( ! empty( $results['MALWARE']['WARN'] ) ) {
66
+ $status = 'warn';
67
+
68
+ foreach ( (array) $results['MALWARE']['WARN'] as $entry ) {
69
+ list( $message, $url ) = explode( ':', $entry[0], 2 );
70
+ $message = trim( $message );
71
+ $url = trim( $url );
72
+
73
+ $message_lowercase = strtolower( $message );
74
+
75
+ if ( 'security warning in the url' === $message_lowercase ) {
76
+ $message = __( 'Security warning in the URL', 'better-wp-security' );
77
+ } else if ( 'malware found on url' === $message_lowercase ) {
78
+ $message = __( 'Malware found on URL', 'better-wp-security' );
79
+ }
80
+
81
+ if ( '&amp;' === substr( $url, 0, 5 ) ) {
82
+ $payload = html_entity_decode( $url );
83
+ $url = get_site_url();
84
+ }
85
+
86
+ $message .= "<br>\n";
87
+ $message .= sprintf( __( 'Infected URL: <a href="%1$s" target="_blank">%2$s</a>', 'better-wp-security' ), esc_url( $url ), esc_html( $url ) );
88
+
89
+ $parts = explode( "\n", $entry[1], 2 );
90
+
91
+ if ( isset( $parts[1] ) ) {
92
+ if ( preg_match( '/(.+)\. Details: (.+)/', $parts[0], $match ) ) {
93
+ $type = $match[1];
94
+ $documentation_url = $match[2];
95
+
96
+ if ( '*Known Spam detected' === $type ) {
97
+ $type = __( '*Known Spam detected', 'better-wp-security' );
98
+ }
99
+
100
+ $message .= "<br>\n";
101
+ $message .= sprintf( __( 'Type: %1$s', 'better-wp-security' ), $type );
102
+ $message .= "<br>\n";
103
+ $message .= sprintf( __( 'Documentation: <a href="%1$s" target="_blank">%2$s</a>', 'better-wp-security' ), esc_url( $documentation_url ), esc_html( $documentation_url ) );
104
+ }
105
+
106
+ $payload_raw = trim( $parts[1] );
107
+ $payload_raw = html_entity_decode( $payload_raw );
108
+
109
+ if ( preg_match( '/<div id=\'HiddenDiv\'>(.+)<\/div>/', $payload_raw, $match ) ) {
110
+ $payload = trim( $match[1] );
111
+ }
112
+ }
113
+
114
+ if ( ! empty( $payload ) ) {
115
+ $message .= "<br>\n";
116
+ $message .= sprintf( __( 'Payload:<pre>%s</pre>', 'better-wp-security' ), esc_html( $payload ) );
117
+ }
118
+
119
+ $list .= sprintf( '<li class="itsec-malware-scan-warn"><span>%s</span></li>', $message ) . "\n";
120
+ }
121
+ }
122
+
123
+ if ( ! empty( $list ) ) {
124
+ $details = "<ul>\n$list</ul>\n";
125
+ }
126
+
127
+ return self::get_wrapped_section( 'malware', $status, $description, $details );
128
+ }
129
+
130
+ protected static function get_system_error_details( $results ) {
131
+ if ( empty( $results['SYSTEM']['ERROR'] ) ) {
132
+ return '';
133
+ }
134
+
135
+ $status = 'error';
136
+ $description = __( 'The scan failed to properly scan the site.', 'better-wp-security' );
137
+
138
+ $details = "<ul>\n";
139
+
140
+ foreach ( $results['SYSTEM']['ERROR'] as $entry ) {
141
+ $details .= sprintf( '<li class="itsec-malware-scan-error"><span>%s</span></li>', $entry ) . "\n";
142
+ }
143
+
144
+ $details .= "</ul>\n";
145
+
146
+ return self::get_wrapped_section( 'system-error', $status, $description, $details );
147
+ }
148
+
149
+ protected static function get_wrapped_section( $type, $status, $description, $details = false ) {
150
+ $html = "<div class='itsec-malware-scan-results-section itsec-malware-scan-results-$type-section'>\n";
151
+
152
+ if ( 'clean' === $status ) {
153
+ $status_text = __( 'Clean', 'better-wp-security' );
154
+ } else if ( 'warn' === $status ) {
155
+ $status_text = __( 'Warning', 'better-wp-security' );
156
+ } else if ( 'error' === $status ) {
157
+ $status_text = __( 'Error', 'better-wp-security' );
158
+ }
159
+
160
+ $status = "<span class='itsec-malware-scan-$status'>$status_text</span>";
161
+
162
+ if ( false === $details ) {
163
+ /* translators: Scan result listing. %1$s is the status. %2$s is the description. */
164
+ $html .= "<p>" . sprintf( _x( '%1$s %2$s', 'scan status, scan description', 'better-wp-security' ), $status, $description ) . "</p>\n";
165
+ } else {
166
+ $details_link = "<a href='#' class='itsec-malware-scan-toggle-details'>" . __( 'Show Details', 'better-wp-security' ) . "</a>\n";
167
+
168
+ /* translators: Scan result listing. %1$s is the status. %2$s is the description. %3$s is the details link. */
169
+ $html .= "<p>" . sprintf( _x( '%1$s %2$s %3$s', 'scan status, scan description, scan details link', 'better-wp-security' ), $status, $description, $details_link ) . "</p>\n";
170
+
171
+ $html .= "<div class='itsec-malware-scan-details'>\n$details\n</div>\n";
172
+ }
173
+
174
+ $html .= "</div>\n";
175
+
176
+ return $html;
177
+ }
178
+ }
core/modules/malware/class-itsec-malware-scanner.php ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Malware_Scanner {
4
+ protected static $transient_name = 'itsec_cached_sucuri_scan';
5
+
6
+ public static function scan() {
7
+ global $itsec_logger;
8
+
9
+
10
+ $results = self::get_scan_results();
11
+
12
+ if ( is_array( $results ) && isset( $results['cached'] ) && $results['cached'] ) {
13
+ return $results;
14
+ }
15
+
16
+
17
+ $user = wp_get_current_user();
18
+ $itsec_logger->log_event( 'malware', 3, $results, ITSEC_Lib::get_ip(), $user->user_login, $user->ID );
19
+
20
+ return $results;
21
+ }
22
+
23
+ protected static function get_scan_results() {
24
+ $response = get_site_transient( self::$transient_name );
25
+ $cached = true;
26
+
27
+ if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE' ) && ITSEC_TEST_MALWARE_SCAN_SKIP_CACHE ) {
28
+ $cached = false;
29
+ $response = false;
30
+ }
31
+
32
+
33
+ if ( false === $response ) {
34
+ $cached = false;
35
+
36
+ $scanner_url = 'http://sitecheck.sucuri.net/';
37
+ $site_url = apply_filters( 'itsec_test_malware_scan_site_url', get_site_url() );
38
+
39
+ if ( defined( 'ITSEC_TEST_MALWARE_SCAN_SITE_URL' ) ) {
40
+ $site_url = ITSEC_TEST_MALWARE_SCAN_SITE_URL;
41
+ }
42
+
43
+ $site_url = preg_replace( '|^https?://|i', '', $site_url );
44
+
45
+ $query_args = array(
46
+ 'scan' => $site_url,
47
+ 'p' => 'ithemes',
48
+ 'clear' => 1,
49
+ 'json' => 1,
50
+ 'time' => time(),
51
+ );
52
+
53
+ $key = apply_filters( 'itsec_sucuri_key', '' );
54
+
55
+ if ( defined( 'ITSEC_SUCURI_KEY' ) ) {
56
+ $key = ITSEC_SUCURI_KEY;
57
+ }
58
+
59
+ if ( ! empty( $key ) ) {
60
+ $query_args['k'] = $key;
61
+ }
62
+
63
+ $scan_url = "$scanner_url?" . http_build_query( $query_args );
64
+
65
+ $req_args = array(
66
+ 'timeout' => 300,
67
+ );
68
+
69
+ if ( defined( 'ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY' ) && ITSEC_TEST_MALWARE_SCAN_DISABLE_SSLVERIFY ) {
70
+ $req_args['sslverify'] = false;
71
+ }
72
+
73
+ $response = wp_remote_get( $scan_url, $req_args );
74
+
75
+ if ( is_wp_error( $response ) ) {
76
+ return $response;
77
+ }
78
+ }
79
+
80
+
81
+ if (
82
+ ! is_array( $response ) ||
83
+ ! array_key_exists( 'body', $response ) ||
84
+ ! array_key_exists( 'headers', $response ) ||
85
+ ! array_key_exists( 'response', $response ) ||
86
+ ! isset( $response['headers']['content-type'] )
87
+ ) {
88
+ return new WP_Error( 'itsec-malware-scanner-malformed-wp-remote-get-response', __( 'The response from the wp_remote_get function was malformed. This could indicate an issue with WordPress.', 'better-wp-security' ) );
89
+ }
90
+
91
+ if ( 'application/json' !== $response['headers']['content-type'] ) {
92
+ return new WP_Error( 'itsec-malware-scanner-invalid-content-type-in-scan-response', sprintf( __( 'The Sucuri server returned an invalid content type. A content type of <code>%s</code> was received when a content type of <code>application/json</code> was expected. This could indicate a temporary issue with the Sucuri servers.', 'better-wp-security' ), esc_html( $response['headers']['content-type'] ) ) );
93
+ }
94
+
95
+ $body = @json_decode( $response['body'], true );
96
+
97
+ if ( ! is_array( $body ) ) {
98
+ if ( 'ERROR' === substr( $response['body'], 0, 5 ) ) {
99
+ return new WP_Error( 'itsec-malware-scanner-error-received', sprintf( __( 'The scan did not complete successfully. Sucuri sent the following error: %s', 'better-wp-security' ), $response['body'] ) );
100
+ }
101
+
102
+ return new WP_Error( 'itsec-malware-scanner-unknown-scan-error', sprintf( __( 'An unknown error prevented the scan from completing successfully. The Sucuri server responded with a <code>%s</code> error code.', 'better-wp-security' ), $response['response']['code'] ) );
103
+ }
104
+
105
+
106
+ if ( ! $cached ) {
107
+ set_site_transient( self::$transient_name, $response, 10 * MINUTE_IN_SECONDS );
108
+ }
109
+
110
+ if ( is_array( $body ) ) {
111
+ $body['cached'] = $cached;
112
+ }
113
+
114
+ return $body;
115
+ }
116
+ }
core/modules/malware/class-itsec-malware.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_Malware {
4
+
5
+ function run() {
6
+ global $itsec_malware;
7
+
8
+ $itsec_malware = $this;
9
+
10
+ add_filter( 'itsec_logger_modules', array( $this, 'itsec_logger_modules' ) );
11
+ add_filter( 'itsec_sync_modules', array( $this, 'itsec_sync_modules' ) ); //register sync modules
12
+
13
+ }
14
+
15
+ /**
16
+ * Register malware for logger
17
+ *
18
+ * @since 4.4
19
+ *
20
+ * @param array $logger_modules array of logger modules
21
+ *
22
+ * @return array array of logger modules
23
+ */
24
+ public function itsec_logger_modules( $logger_modules ) {
25
+ $logger_modules['malware'] = array(
26
+ 'type' => 'malware',
27
+ 'function' => __( 'Malware Scan', 'better-wp-security' ),
28
+ );
29
+
30
+ return $logger_modules;
31
+
32
+ }
33
+
34
+ /**
35
+ * Register malware for Sync
36
+ *
37
+ * @param array $sync_modules array of malware modules
38
+ *
39
+ * @return array array of logger modules
40
+ */
41
+ public function itsec_sync_modules( $sync_modules ) {
42
+
43
+ $sync_modules['malware'] = array(
44
+ 'verbs' => array(
45
+ 'itsec-do-malware-scan' => 'Ithemes_Sync_Verb_ITSEC_Malware_Do_Scan',
46
+ 'itsec-get-malware-scan-log' => 'Ithemes_Sync_Verb_ITSEC_Get_Malware_Scan_Log',
47
+ ),
48
+ 'everything' => 'itsec-get-malware-scan-log',
49
+ 'path' => dirname( __FILE__ ),
50
+ );
51
+
52
+ return $sync_modules;
53
+
54
+ }
55
+
56
+ }
core/modules/malware/css/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php
2
+ // Silence is golden.
core/modules/malware/css/malware.css ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .itsec-malware-scan-results ul {
2
+ margin-left: 20px;
3
+ }
4
+ .itsec-malware-scan-results ul li {
5
+ line-height: 16px;
6
+ list-style: outside none disc;
7
+ }
8
+ .itsec-malware-scan-details {
9
+ display: none;
10
+ }
11
+ .itsec-malware-scan-details pre {
12
+ white-space: pre-wrap;
13
+ }
14
+ .itsec-malware-scan-results-section {
15
+ border: 1px solid #ddd;
16
+ border-bottom-color: transparent;
17
+ padding: 0 1em;
18
+ }
19
+ .itsec-malware-scan-results-section:last-child {
20
+ border-bottom-color: #ddd;
21
+ }
22
+ .itsec-malware-scan-clean,
23
+ .itsec-malware-scan-warn,
24
+ .itsec-malware-scan-error {
25
+ padding: 2px 6px;
26
+ color: #fff;
27
+ margin-right: 1em;
28
+ width: 60px;
29
+ text-align: center;
30
+ }
31
+ span.itsec-malware-scan-clean,
32
+ span.itsec-malware-scan-warn,
33
+ span.itsec-malware-scan-error {
34
+ display:inline-block;
35
+ }
36
+ .itsec-malware-scan-clean {
37
+ background: #7ad03a;
38
+ }
39
+ .itsec-malware-scan-warn,
40
+ .itsec-malware-scan-error {
41
+ background: #dd3d36;
42
+ }
43
+ .itsec-malware-scan-details .itsec-malware-scan-warn,
44
+ .itsec-malware-scan-details .itsec-malware-scan-error,
45
+ .itsec-malware-scan-details .itsec-malware-scan-clean {
46
+ background: none;
47
+ padding: 0;
48
+ color: inherit;
49
+ margin: 0;
50
+ width: 100%;
51
+ text-align: left;
52
+ }
53
+ .itsec-malware-scan-details .itsec-malware-scan-warn,
54
+ .itsec-malware-scan-details .itsec-malware-scan-error {
55
+ color: #dd3d36;
56
+ }
57
+ .itsec-malware-scan-details .itsec-malware-scan-clean {
58
+ color: #7ad03a;
59
+ }
60
+ .itsec-malware-scan-results-section .itsec-malware-scan-details li {
61
+ margin-bottom: .25em;
62
+ padding-bottom: .5em;
63
+ border-bottom: 1px solid #ddd;
64
+ }
65
+ .itsec-malware-scan-results-section .itsec-malware-scan-details li:last-child {
66
+ border:none;
67
+ }
68
+ .itsec-malware-scan-results-section .itsec-malware-scan-details li span {
69
+ color: #444;
70
+ }
71
+ .itsec-malware-scan-toggle-details {
72
+ margin-left: 1em;
73
+ }
core/modules/malware/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/malware/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/malware/js/malware.js ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+
3
+ (function( $ ) {
4
+ var itsecMalware = {
5
+ init: function() {
6
+ this.bindEvents();
7
+ },
8
+
9
+ bindEvents: function() {
10
+ $('#itsec-malware-scan').on('click', this.startScan);
11
+ $('.itsec-malware-scan-results-wrapper').on('click', '.itsec-malware-scan-toggle-details', this.toggleDetails);
12
+ },
13
+
14
+ toggleDetails: function( event ) {
15
+ event.preventDefault();
16
+
17
+ var $container = $(this).parents('.itsec-malware-scan-results-section');
18
+ var $details = $container.find('.itsec-malware-scan-details');
19
+
20
+ if ( $details.is(':visible') ) {
21
+ $(this).html('Show Details');
22
+ $details.hide();
23
+ } else {
24
+ $(this).html('Hide Details');
25
+ $details.show();
26
+ }
27
+ },
28
+
29
+ startScan: function( event ) {
30
+ event.preventDefault();
31
+
32
+ itsecMalwareScanData.originalSubmitButtonText = $(this).val();
33
+
34
+ $(this)
35
+ .prop( 'disabled', true )
36
+ .val( itsecMalwareScanData.clickedButtonText );
37
+
38
+ var postData = {
39
+ action: 'itsec_malware_scan',
40
+ _wpnonce: itsecMalwareScanData.nonce
41
+ };
42
+
43
+ $.ajax( ajaxurl, {
44
+ type: 'POST',
45
+ data: postData,
46
+ success: itsecMalware.handleSuccessResponse,
47
+ error: itsecMalware.handleErrorResponse,
48
+ timeout: 0
49
+ });
50
+ },
51
+
52
+ handleSuccessResponse: function( data, status, jqXHR ) {
53
+ $('#itsec-malware-scan').hide();
54
+
55
+ if ( 'string' !== typeof data ) {
56
+ itsecMalware.showError( itsecMalwareScanData.errorMessages.parseError );
57
+ } else if ( '-1' === data ) {
58
+ itsecMalware.showError( itsecMalwareScanData.errorMessages.nonceFailure );
59
+ } else if ( '-2' === data ) {
60
+ itsecMalware.showError( itsecMalwareScanData.errorMessages.invalidUser );
61
+ } else {
62
+ $('.itsec-malware-scan-results-wrapper').html( data );
63
+ }
64
+ },
65
+
66
+ handleErrorResponse: function( jqXHR, status, exception ) {
67
+ $('#itsec-malware-scan').hide();
68
+
69
+ var message = itsecMalwareScanData.errorMessages.ajaxUnknown;
70
+
71
+ if ( 'timeout' === status ) {
72
+ message = itsecMalwareScanData.errorMessages.ajaxTimeout;
73
+ } else if ( 'parsererror' === status ) {
74
+ message = itsecMalwareScanData.errorMessages.parseError;
75
+ }
76
+
77
+ itsecMalware.showError( message, '(' + status + ') ' + exception )
78
+ },
79
+
80
+ showError: function( message, replacement ) {
81
+ if ( 'string' === typeof replacement ) {
82
+ message = message.replace( '%1$s', replacement );
83
+ }
84
+
85
+ message = '<div class="error inline"><p><strong>' + message + '</strong></p></div>';
86
+
87
+ $('.itsec-malware-scan-results-wrapper').html( message );
88
+ }
89
+ };
90
+
91
+ $(document).ready(function() {
92
+ itsecMalware.init();
93
+ });
94
+ })( jQuery );
core/modules/malware/setup.php ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Malware_Setup' ) ) {
4
+
5
+ class ITSEC_Malware_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'enabled' => false,
16
+ 'api_key' => '',
17
+ );
18
+
19
+ if ( isset( $itsec_setup_action ) ) {
20
+
21
+ switch ( $itsec_setup_action ) {
22
+
23
+ case 'activate':
24
+ $this->execute_activate();
25
+ break;
26
+ case 'upgrade':
27
+ $this->execute_upgrade();
28
+ break;
29
+ case 'deactivate':
30
+ $this->execute_deactivate();
31
+ break;
32
+ case 'uninstall':
33
+ $this->execute_uninstall();
34
+ break;
35
+
36
+ }
37
+
38
+ } else {
39
+ wp_die( 'error' );
40
+ }
41
+
42
+ }
43
+
44
+ /**
45
+ * Execute module activation.
46
+ *
47
+ * @since 4.0
48
+ *
49
+ * @return void
50
+ */
51
+ public function execute_activate() {
52
+
53
+ $options = get_site_option( 'itsec_malware' );
54
+
55
+ if ( $options === false ) {
56
+
57
+ add_site_option( 'itsec_malware', $this->defaults );
58
+
59
+ }
60
+
61
+ }
62
+
63
+ /**
64
+ * Execute module deactivation
65
+ *
66
+ * @return void
67
+ */
68
+ public function execute_deactivate() {
69
+ }
70
+
71
+ /**
72
+ * Execute module uninstall
73
+ *
74
+ * @return void
75
+ */
76
+ public function execute_uninstall() {
77
+
78
+ $this->execute_deactivate();
79
+
80
+ delete_site_option( 'itsec_malware' );
81
+
82
+ }
83
+
84
+ /**
85
+ * Execute module upgrade
86
+ *
87
+ * @return void
88
+ */
89
+ public function execute_upgrade() {
90
+
91
+ }
92
+
93
+ }
94
+
95
+ }
96
+
97
+ new ITSEC_Malware_Setup();
core/modules/salts/class-itsec-salts-admin.php ADDED
@@ -0,0 +1,358 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Change WordPress Salts
5
+ *
6
+ * Sets up all administrative functions for the Change WordPress Salts feature
7
+ * including fields, sanitation and all other privileged functions.
8
+ *
9
+ * @since 4.6.0
10
+ *
11
+ * @package iThemes_Security
12
+ */
13
+ class ITSEC_Salts_Admin {
14
+
15
+ /**
16
+ * The module's saved options
17
+ *
18
+ * @since 4.6.0
19
+ * @access private
20
+ * @var array
21
+ */
22
+ private $settings;
23
+
24
+ /**
25
+ * The core plugin class utilized in order to set up admin and other screens
26
+ *
27
+ * @since 4.6.0
28
+ * @access private
29
+ * @var ITSEC_Core
30
+ */
31
+ private $core;
32
+
33
+ /**
34
+ * The absolute web patch to the module's files
35
+ *
36
+ * @since 4.6.0
37
+ * @access private
38
+ * @var string
39
+ */
40
+ private $module_path;
41
+
42
+ /**
43
+ * Setup the module's administrative functionality
44
+ *
45
+ * Loads the file change detection module's priviledged functionality including
46
+ * changing the folder itself.
47
+ *
48
+ * @since 4.6.0
49
+ *
50
+ * @param ITSEC_Core $core The core plugin instance
51
+ *
52
+ * @return void
53
+ */
54
+ function run( $core ) {
55
+
56
+ $this->core = $core;
57
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
58
+ $this->settings = false;
59
+
60
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'itsec_add_admin_meta_boxes' ) ); //add meta boxes to admin page
61
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'itsec_add_dashboard_status' ) ); //add information for plugin status
62
+ add_filter( 'itsec_tracking_vars', array( $this, 'itsec_tracking_vars' ) ); //Usage information tracked via Google Analytics (opt-in)
63
+
64
+ if ( ! empty( $_POST ) ) {
65
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //Process the WordPress Salts change if a form has been submitted
66
+ }
67
+
68
+ }
69
+
70
+ /**
71
+ * Add meta boxes to primary options pages
72
+ *
73
+ * Adds the module's meta settings box to the advanced page.
74
+ *
75
+ * @since 4.6.0
76
+ *
77
+ * @return void
78
+ */
79
+ public function itsec_add_admin_meta_boxes() {
80
+
81
+ add_meta_box(
82
+ 'salts_options',
83
+ __( 'WordPress Salts', 'better-wp-security' ),
84
+ array( $this, 'metabox_advanced_settings' ),
85
+ 'security_page_toplevel_page_itsec_advanced',
86
+ 'advanced',
87
+ 'core'
88
+ );
89
+
90
+ }
91
+
92
+ /**
93
+ * Sets the status in the plugin dashboard
94
+ *
95
+ * Sets a low priority item for the module's functionality in the plugin
96
+ * dashboard.
97
+ *
98
+ * @since 4.6.0
99
+ *
100
+ * @param array $statuses array of existing plugin dashboard statuses
101
+ *
102
+ * @return array statuses
103
+ */
104
+ public function itsec_add_dashboard_status( $statuses ) {
105
+
106
+ global $itsec_globals;
107
+
108
+ $last_update = get_site_option( 'itsec_salts' );
109
+
110
+ if ( false === $last_update ) {
111
+
112
+ $status_array = 'low';
113
+ $status = array(
114
+ 'text' => __( 'Your WordPress Salts have not been changed. You should change them now.', 'better-wp-security' ),
115
+ 'link' => '#itsec_enable_salts', 'advanced' => true,
116
+ );
117
+
118
+ } elseif ( absint( $last_update ) < ( $itsec_globals['current_time_gmt'] - ( 30 * 24 * 60 * 60 ) ) ) {
119
+
120
+ $status_array = 'low';
121
+ $status = array(
122
+ 'text' => __( 'Your WordPress Salts have not been changed 30 days. You should change them now.', 'better-wp-security' ),
123
+ 'link' => '#itsec_enable_salts', 'advanced' => true,
124
+ );
125
+
126
+ } else {
127
+
128
+ $status_array = 'safe-low';
129
+ $status = array(
130
+ 'text' => __( 'You have recently changed your WordPress Salts.', 'better-wp-security' ),
131
+ 'link' => '#itsec_enable_salts', 'advanced' => true,
132
+ );
133
+
134
+ }
135
+
136
+ array_push( $statuses[$status_array], $status );
137
+
138
+ return $statuses;
139
+
140
+ }
141
+
142
+ /**
143
+ * Execute admin initializations
144
+ *
145
+ * @return void
146
+ */
147
+ public function initialize_admin() {
148
+
149
+ if ( ! $this->settings === true && isset( $_POST['itsec_enable_salts'] ) && $_POST['itsec_enable_salts'] == 'true' ) {
150
+
151
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'ITSEC_admin_save' ) ) {
152
+
153
+ die( __( 'Security check', 'better-wp-security' ) );
154
+
155
+ }
156
+
157
+ $this->process_salts();
158
+
159
+ }
160
+
161
+ }
162
+
163
+ /**
164
+ * Adds fields that will be tracked for Google Analytics
165
+ *
166
+ * Allows the tracking of when the content directory has been changed (although
167
+ * not the new name of the directory) via our Google Analytics tracking
168
+ * system.
169
+ *
170
+ * @since 4.6.0
171
+ *
172
+ * @param array $vars tracking vars
173
+ *
174
+ * @return array tracking vars
175
+ */
176
+ public function itsec_tracking_vars( $vars ) {
177
+
178
+ $vars['salts'] = array(
179
+ 'enabled' => '0:b',
180
+ );
181
+
182
+ return $vars;
183
+
184
+ }
185
+
186
+ /**
187
+ * Render the settings metabox
188
+ *
189
+ * Displays the contents of the module's settings metabox on the "Advanced"
190
+ * page with all module options.
191
+ *
192
+ * @since 4.6.0
193
+ *
194
+ * @return void
195
+ */
196
+ public function metabox_advanced_settings() {
197
+
198
+ global $itsec_globals;
199
+
200
+ $content = '';
201
+
202
+ if ( false === $this->settings ) {
203
+
204
+ $content .= '<p>' . __( 'A secret key makes your site harder to hack and access by adding random elements to the password.', 'better-wp-security' ) . '</p>';
205
+ $content .= '<p>' . __( 'In simple terms, a secret key is a password with elements that make it harder to generate enough options to break through your security barriers. A password like "password" or "test" is simple and easily broken. A random, unpredictable password such as "88a7da62429ba6ad3cb3c76a09641fc" takes years to come up with the right combination. A salt is used to further enhance the security of the generated result.', 'better-wp-security' ) . '</p>';
206
+ $content .= '<p><strong>' . __( 'Note that enabling this feature will log you out of your WordPress site.', 'better-wp-security' ) . '</strong></p>';
207
+ }
208
+
209
+ echo $content;
210
+
211
+ if ( isset( $itsec_globals['settings']['write_files'] ) && true === $itsec_globals['settings']['write_files'] ) {
212
+ ?>
213
+
214
+ <form method="post" action="?page=toplevel_page_itsec_advanced&settings-updated=true" class="itsec-form">
215
+
216
+ <?php wp_nonce_field( 'ITSEC_admin_save', 'wp_nonce' ); ?>
217
+
218
+ <table class="form-table">
219
+ <tr valign="top">
220
+ <th scope="row" class="settinglabel">
221
+ <label for="itsec_enable_salts"><?php _e( 'Change WordPress Salts', 'better-wp-security' ); ?></label>
222
+ </th>
223
+ <td class="settingfield">
224
+ <input type="checkbox" id="itsec_enable_salts" name="itsec_enable_salts" value="true"/>
225
+ <p class="description"><?php _e( 'Check this box to change your WordPress Salts.', 'better-wp-security' ); ?></p>
226
+ </td>
227
+ </tr>
228
+ </table>
229
+ <p class="submit">
230
+ <input type="submit" class="button-primary"
231
+ value="<?php _e( 'Change WordPress Salts', 'better-wp-security' ); ?>"/>
232
+ </p>
233
+ </form>
234
+
235
+ <?php
236
+
237
+ } else {
238
+
239
+ $content = sprintf(
240
+ '<p>%s <a href="?page=toplevel_page_itsec_settings">%s</a> %s',
241
+ __( 'You must allow this plugin to write to the wp-config.php file on the', 'better-wp-security' ),
242
+ __( 'Settings', 'better-wp-security' ),
243
+ __( 'page to use this feature.', 'better-wp-security' )
244
+ );
245
+
246
+ echo $content;
247
+ }
248
+
249
+ }
250
+
251
+ /**
252
+ * Get a random salt value
253
+ *
254
+ * @since 1.15.0
255
+ *
256
+ * @access protected
257
+ *
258
+ * @return string The generated salt.
259
+ */
260
+ protected function get_salt() {
261
+ $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()-_=+[]{}|;:<>,./? ';
262
+ $salt = '';
263
+
264
+ for ( $count = 0; $count < 64; $count++ ) {
265
+ $character_index = rand( 0, strlen( $characters ) - 1 );
266
+ $salt .= $characters[$character_index];
267
+ }
268
+
269
+ return $salt;
270
+ }
271
+
272
+ /**
273
+ * Sanitize and validate input
274
+ *
275
+ * @since 4.6.0
276
+ */
277
+ public function process_salts() {
278
+ global $itsec_globals;
279
+
280
+
281
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-config-file.php' );
282
+ require_once( trailingslashit( $GLOBALS['itsec_globals']['plugin_dir'] ) . 'core/lib/class-itsec-lib-file.php' );
283
+
284
+ $config_file_path = ITSEC_Lib_Config_File::get_wp_config_file_path();
285
+ $config = ITSEC_Lib_File::read( $config_file_path );
286
+ $error = '';
287
+
288
+ if ( is_wp_error( $config ) ) {
289
+ $error = sprintf( __( 'Unable to read the <code>wp-config.php</code> file in order to update the salts. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() );
290
+ } else {
291
+ $defines = array(
292
+ 'AUTH_KEY',
293
+ 'SECURE_AUTH_KEY',
294
+ 'LOGGED_IN_KEY',
295
+ 'NONCE_KEY',
296
+ 'AUTH_SALT',
297
+ 'SECURE_AUTH_SALT',
298
+ 'LOGGED_IN_SALT',
299
+ 'NONCE_SALT',
300
+ );
301
+
302
+ foreach ( $defines as $define ) {
303
+ $new_salt = $this->get_salt();
304
+ $new_salt = str_replace( '$', '\\$', $new_salt );
305
+
306
+ $regex = "/(define\s*\(\s*(['\"])$define\\2\s*,\s*)(['\"]).+?\\3(\s*\)\s*;)/";
307
+ $config = preg_replace( $regex, "\${1}'$new_salt'\${4}", $config );
308
+ }
309
+
310
+ $write_result = ITSEC_Lib_File::write( $config_file_path, $config );
311
+
312
+ if ( is_wp_error( $write_result ) ) {
313
+ $error = sprintf( __( 'Unable to update the <code>wp-config.php</code> file in order to update the salts. Error details as follows: %1$s (%2$s)', 'better-wp-security' ), $config->get_error_message(), $config->get_error_code() );
314
+ }
315
+ }
316
+
317
+ if ( ! empty( $error ) ) {
318
+ add_settings_error( 'itsec', esc_attr( 'settings_updated' ), $error, 'error' );
319
+ add_site_option( 'itsec_manual_update', true );
320
+ }
321
+
322
+
323
+ $this->settings = true; //this tells the form field that all went well.
324
+
325
+ if ( is_multisite() ) {
326
+
327
+ if ( ! empty( $error ) ) {
328
+
329
+ $error_handler = new WP_Error();
330
+
331
+ $error_handler->add( 'error', $error );
332
+
333
+ $this->core->show_network_admin_notice( $error_handler );
334
+
335
+ } else {
336
+
337
+ $this->core->show_network_admin_notice( false );
338
+
339
+ }
340
+
341
+ $this->settings = true;
342
+
343
+ }
344
+
345
+ if ( $this->settings === true ) {
346
+
347
+ update_site_option( 'itsec_salts', $itsec_globals['current_time_gmt'] );
348
+
349
+ wp_clear_auth_cookie();
350
+ $redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : ITSEC_Lib::get_home_root() . 'wp-login.php?loggedout=true';
351
+ wp_safe_redirect( $redirect_to );
352
+
353
+ }
354
+
355
+ }
356
+
357
+ }
358
+
core/modules/salts/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/salts/setup.php ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_Salts_Setup' ) ) {
4
+
5
+ class ITSEC_Salts_Setup {
6
+
7
+ public function __construct() {
8
+
9
+ global $itsec_setup_action;
10
+
11
+ if ( isset( $itsec_setup_action ) ) {
12
+
13
+ switch ( $itsec_setup_action ) {
14
+
15
+ case 'activate':
16
+ $this->execute_activate();
17
+ break;
18
+ case 'upgrade':
19
+ $this->execute_upgrade();
20
+ break;
21
+ case 'deactivate':
22
+ $this->execute_deactivate();
23
+ break;
24
+ case 'uninstall':
25
+ $this->execute_uninstall();
26
+ break;
27
+
28
+ }
29
+
30
+ } else {
31
+ wp_die( 'error' );
32
+ }
33
+
34
+ }
35
+
36
+ /**
37
+ * Execute module activation.
38
+ *
39
+ * @since 4.7.0
40
+ *
41
+ * @return void
42
+ */
43
+ public function execute_activate() {
44
+
45
+ }
46
+
47
+ /**
48
+ * Execute module deactivation
49
+ *
50
+ * @since 4.7.0
51
+ *
52
+ * @return void
53
+ */
54
+ public function execute_deactivate() {
55
+
56
+ }
57
+
58
+ /**
59
+ * Execute module uninstall
60
+ *
61
+ * @since 4.7.0
62
+ *
63
+ * @return void
64
+ */
65
+ public function execute_uninstall() {
66
+
67
+ $this->execute_deactivate();
68
+
69
+ delete_site_option( 'itsec_salts' );
70
+
71
+ }
72
+
73
+ /**
74
+ * Execute module upgrade
75
+ *
76
+ * @return void
77
+ */
78
+ public function execute_upgrade() {
79
+
80
+ }
81
+
82
+ }
83
+
84
+ }
85
+
86
+ new ITSEC_Salts_Setup();
core/modules/ssl/class-itsec-ssl-admin.php ADDED
@@ -0,0 +1,425 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_SSL_Admin {
4
+
5
+ private
6
+ $settings,
7
+ $core,
8
+ $module_path,
9
+ $has_ssl;
10
+
11
+ function run( $core ) {
12
+
13
+ $this->core = $core;
14
+ $this->settings = get_site_option( 'itsec_ssl' );
15
+ $this->module_path = ITSEC_Lib::get_module_path( __FILE__ );
16
+
17
+ add_filter( 'itsec_file_modules', array( $this, 'register_file' ) ); //register tooltip action
18
+ add_action( 'current_screen', array( $this, 'plugin_init' ) );
19
+ add_action( 'itsec_add_admin_meta_boxes', array( $this, 'add_admin_meta_boxes' ) ); //add meta boxes to admin page
20
+ add_action( 'itsec_admin_init', array( $this, 'initialize_admin' ) ); //initialize admin area
21
+ add_filter( 'itsec_add_dashboard_status', array( $this, 'dashboard_status' ) ); //add information for plugin status
22
+ add_action( 'admin_enqueue_scripts', array( $this, 'admin_script' ) ); //enqueue scripts for admin page
23
+ add_filter( 'itsec_tracking_vars', array( $this, 'tracking_vars' ) );
24
+
25
+ //manually save options on multisite
26
+ if ( is_multisite() ) {
27
+ add_action( 'itsec_admin_init', array( $this, 'save_network_options' ) ); //save multisite options
28
+ }
29
+
30
+ if ( isset( $this->settings['frontend'] ) && $this->settings['frontend'] == 1 ) {
31
+
32
+ add_action( 'post_submitbox_misc_actions', array( $this, 'ssl_enable_per_content' ) );
33
+ add_action( 'save_post', array( $this, 'save_post' ) );
34
+
35
+ }
36
+
37
+
38
+ add_filter( 'itsec_filter_wp_config_modification', array( $this, 'filter_wp_config_modification' ) );
39
+ }
40
+
41
+ /**
42
+ * Add checkbox to post meta for SSL
43
+ *
44
+ * @return void
45
+ */
46
+ function ssl_enable_per_content() {
47
+
48
+ global $post;
49
+
50
+ wp_nonce_field( 'ITSEC_Admin_Save', 'wp_nonce' );
51
+
52
+ $enabled = false;
53
+
54
+ if ( $post->ID ) {
55
+ $enabled = get_post_meta( $post->ID, 'itsec_enable_ssl', true );
56
+ }
57
+
58
+ $content = '<div id="itsec" class="misc-pub-section">';
59
+ $content .= '<label for="enable_ssl">Enable SSL:</label> ';
60
+ $content .= '<input type="checkbox" value="1" name="enable_ssl" id="enable_ssl"' . checked( 1, $enabled, false ) . ' />';
61
+ $content .= '</div>';
62
+
63
+ echo $content;
64
+
65
+ }
66
+
67
+ /**
68
+ * Save post meta for SSL selection
69
+ *
70
+ * @param int $id post id
71
+ *
72
+ * @return bool value of itsec_enable_ssl
73
+ */
74
+ function save_post( $id ) {
75
+
76
+ if ( isset( $_POST['wp_nonce'] ) ) {
77
+
78
+ if ( ! wp_verify_nonce( $_POST['wp_nonce'], 'ITSEC_Admin_Save' ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( $_POST['post_type'] == 'page' && ! current_user_can( 'edit_page', $id ) ) || ( $_POST['post_type'] == 'post' && ! current_user_can( 'edit_post', $id ) ) ) {
79
+ return $id;
80
+ }
81
+
82
+ $itsec_enable_ssl = ( ( isset( $_POST['enable_ssl'] ) && $_POST['enable_ssl'] == true ) ? true : false );
83
+
84
+ if ( $itsec_enable_ssl ) {
85
+ update_post_meta( $id, 'itsec_enable_ssl', true );
86
+ } else {
87
+ delete_post_meta( $id, 'itsec_enable_ssl' );
88
+ }
89
+
90
+ return $itsec_enable_ssl;
91
+
92
+ }
93
+
94
+ return false;
95
+
96
+ }
97
+
98
+ /**
99
+ * Add meta boxes to primary options pages
100
+ *
101
+ * @param array $available_pages array of available page_hooks
102
+ */
103
+ public function add_admin_meta_boxes() {
104
+
105
+ $id = 'ssl_options';
106
+ $title = __( 'Secure Socket Layers (SSL)', 'better-wp-security' );
107
+
108
+ add_meta_box(
109
+ $id,
110
+ $title,
111
+ array( $this, 'metabox_advanced_settings' ),
112
+ 'security_page_toplevel_page_itsec_settings',
113
+ 'advanced',
114
+ 'core'
115
+ );
116
+
117
+ $this->core->add_toc_item(
118
+ array(
119
+ 'id' => $id,
120
+ 'title' => $title,
121
+ )
122
+ );
123
+
124
+ }
125
+
126
+ /**
127
+ * Add SSL Javascript
128
+ *
129
+ * @return void
130
+ */
131
+ public function admin_script() {
132
+
133
+ global $itsec_globals;
134
+
135
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
136
+
137
+ wp_enqueue_script( 'itsec_ssl_js', $this->module_path . 'js/admin-ssl.js', array( 'jquery' ), $itsec_globals['plugin_build'] );
138
+
139
+ //make sure the text of the warning is translatable
140
+ wp_localize_script( 'itsec_ssl_js', 'ssl_warning_text', array( 'text' => __( 'Are you sure you want to enable SSL? If your server does not support SSL you will be locked out of your WordPress Dashboard.', 'better-wp-security' ) ) );
141
+
142
+ }
143
+
144
+ }
145
+
146
+ /**
147
+ * Sets the status in the plugin dashboard
148
+ *
149
+ * @since 4.0
150
+ *
151
+ * @return array statuses
152
+ */
153
+ public function dashboard_status( $statuses ) {
154
+ if ( ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN ) || ( defined( 'FORCE_SSL_LOGIN' ) && FORCE_SSL_LOGIN ) ) {
155
+ $status_array = 'safe-low';
156
+ $status = array(
157
+ 'text' => __( 'You are requiring a secure connection for accessing the dashboard.', 'better-wp-security' ),
158
+ 'link' => '#itsec_ssl_admin',
159
+ );
160
+ } else {
161
+ $status_array = 'low';
162
+ $status = array(
163
+ 'text' => __( 'You are not requiring a secure connection for accessing the dashboard.', 'better-wp-security' ),
164
+ 'link' => '#itsec_ssl_admin',
165
+ );
166
+ }
167
+
168
+ array_push( $statuses[$status_array], $status );
169
+
170
+ return $statuses;
171
+ }
172
+
173
+ /**
174
+ * Execute admin initializations.
175
+ *
176
+ * Adds settings fields and tries to determine whether SSL is even possible.
177
+ *
178
+ * @since 4.0
179
+ *
180
+ * @return void
181
+ */
182
+ public function initialize_admin() {
183
+
184
+ //primary settings section
185
+ add_settings_section(
186
+ 'ssl_settings',
187
+ __( 'Configure SSL', 'better-wp-security' ),
188
+ '__return_empty_string',
189
+ 'security_page_toplevel_page_itsec_settings'
190
+ );
191
+
192
+ //enabled field
193
+ add_settings_field(
194
+ 'itsec_ssl[frontend]',
195
+ __( 'Front End SSL Mode', 'better-wp-security' ),
196
+ array( $this, 'ssl_frontend' ),
197
+ 'security_page_toplevel_page_itsec_settings',
198
+ 'ssl_settings'
199
+ );
200
+
201
+ //enabled field
202
+ add_settings_field(
203
+ 'itsec_ssl[admin]',
204
+ __( 'SSL for Dashboard', 'better-wp-security' ),
205
+ array( $this, 'ssl_admin' ),
206
+ 'security_page_toplevel_page_itsec_settings',
207
+ 'ssl_settings'
208
+ );
209
+
210
+ //Register the settings field for the entire module
211
+ register_setting(
212
+ 'security_page_toplevel_page_itsec_settings',
213
+ 'itsec_ssl',
214
+ array( $this, 'sanitize_module_input' )
215
+ );
216
+
217
+ }
218
+
219
+ /**
220
+ * echos front end Field
221
+ *
222
+ * @since 4.0
223
+ *
224
+ * @return void
225
+ */
226
+ public function ssl_frontend() {
227
+ if ( isset( $this->settings['frontend'] ) ) {
228
+ $frontend = $this->settings['frontend'];
229
+ } else {
230
+ $frontend = 0;
231
+ }
232
+
233
+ echo '<select id="itsec_ssl_frontend" name="itsec_ssl[frontend]">';
234
+
235
+ echo '<option value="0" ' . selected( $frontend, '0', false ) . '>' . __( 'Off', 'better-wp-security' ) . '</option>';
236
+ echo '<option value="1" ' . selected( $frontend, '1', false ) . '>' . __( 'Per Content', 'better-wp-security' ) . '</option>';
237
+ echo '<option value="2" ' . selected( $frontend, '2', false ) . '>' . __( 'Whole Site', 'better-wp-security' ) . '</option>';
238
+ echo '</select><br />';
239
+ echo '<label for="itsec_ssl_frontend"> ' . __( 'Front End SSL Mode', 'better-wp-security' ) . '</label>';
240
+ echo '<p class="description">' . __( 'Enables secure SSL connection for the front-end (public parts of your site). Turning this off will disable front-end SSL control, turning this on "Per Content" will place a checkbox on the edit page for all posts and pages (near the publish settings) allowing you to turn on SSL for selected pages or posts, and selecting "Whole Site" will force the whole site to use SSL (not recommended unless you have a really good reason to use it', 'better-wp-security' ) . '</p>';
241
+ }
242
+
243
+ /**
244
+ * echos admin Field
245
+ *
246
+ * @since 4.0
247
+ *
248
+ * @return void
249
+ */
250
+ public function ssl_admin() {
251
+
252
+ if ( ( isset( $this->settings['admin'] ) && ( true === $this->settings['admin'] ) ) || ( isset( $this->settings['login'] ) && ( true === $this->settings['login'] ) ) ) {
253
+ $admin = 1;
254
+ } else {
255
+ $admin = 0;
256
+ }
257
+
258
+ $content = '<input onchange="forcessl()" type="checkbox" id="itsec_ssl_admin" name="itsec_ssl[admin]" value="1" ' . checked( 1, $admin, false ) . '/>';
259
+ $content .= '<label for="itsec_ssl_admin">' . __( 'Force SSL for Dashboard', 'better-wp-security' ) . '</label>';
260
+ $content .= '<p class="description">' . __( 'Forces all dashboard access to be served only over an SSL connection.', 'better-wp-security' ) . '</p>';
261
+
262
+ echo $content;
263
+
264
+ }
265
+
266
+ /**
267
+ * Render the settings metabox
268
+ *
269
+ * @return void
270
+ */
271
+ public function metabox_advanced_settings() {
272
+
273
+ $content = '<p>' . __( 'Secure Socket Layers (SSL) is a technology that is used to encrypt the data sent between your server or host and a visitor to your web page. When SSL is activated, it makes it almost impossible for an attacker to intercept data in transit, therefore making the transmission of form, password or other encrypted data much safer.', 'better-wp-security' ) . '</p>';
274
+ $content .= '<p>' . __( 'This plugin gives you the option of turning on SSL (if your server or host supports it) for all or part of your site. The options below allow you to automatically use SSL for major parts of your site such as the login page, the admin dashboard or the site as a whole. You can also turn on SSL for any post or page by editing the content and selecting "Enable SSL" in the publishing options of the content in question.', 'better-wp-security' ) . '</p>';
275
+ $content .= '<p>' . __( 'Note: While this plugin does give you the option of encrypting everything, SSL may not be for you. SSL does add overhead to your site which will increase download times slightly. Therefore we recommend you enable SSL at a minimum on the login page, then on the whole admin section and finally on individual pages or posts with forms that require sensitive information.', 'better-wp-security' ) . '</p>';
276
+
277
+ if ( $this->has_ssl === false ) {
278
+
279
+ $content .= sprintf( '<div class="itsec-warning-message"><span>%s: </span>%s</div>', __( 'WARNING', 'better-wp-security' ), __( 'Your server does not appear to support SSL. Your server MUST support SSL to use these features. Using these features without SSL support on your server or host will cause some or all of your site to become unavailable.', 'better-wp-security' ) );
280
+
281
+ } else {
282
+
283
+ $content .= sprintf( '<div class="itsec-notice-message"><span>%s: </span>%s</div>', __( 'WARNING', 'better-wp-security' ), __( 'Your server does appear to support SSL. Using these features without SSL support on your server or host will cause some or all of your site to become unavailable.', 'better-wp-security' ) );
284
+
285
+ }
286
+
287
+ $content .= '<p>' . __( 'Note: When turning SSL on you will be logged out and you will have to log back in. This is to prevent possible cookie conflicts that could make it more difficult to get in otherwise.', 'better-wp-security' ) . '</p>';
288
+
289
+ echo $content;
290
+
291
+ $this->core->do_settings_section( 'security_page_toplevel_page_itsec_settings', 'ssl_settings', false );
292
+
293
+ echo '<p>' . PHP_EOL;
294
+
295
+ settings_fields( 'security_page_toplevel_page_itsec_settings' );
296
+
297
+ echo '<input class="button-primary" name="submit" type="submit" value="' . __( 'Save All Changes', 'better-wp-security' ) . '" />' . PHP_EOL;
298
+
299
+ echo '</p>' . PHP_EOL;
300
+
301
+ }
302
+
303
+ public function filter_wp_config_modification( $modification ) {
304
+ $input = get_site_option( 'itsec_ssl', false );
305
+
306
+ if ( ! is_array( $input ) ) {
307
+ return $modification;
308
+ }
309
+
310
+
311
+ if ( ( isset( $input['login'] ) && ( true == $input['login'] ) ) || ( isset( $input['admin'] ) && ( true == $input['admin'] ) ) ) {
312
+ $modification .= "define( 'FORCE_SSL_LOGIN', true ); // " . __( 'Force SSL for Dashboard - Security > Settings > Secure Socket Layers (SSL) > SSL for Dashboard', 'better-wp-security' ) . "\n";
313
+ $modification .= "define( 'FORCE_SSL_ADMIN', true ); // " . __( 'Force SSL for Dashboard - Security > Settings > Secure Socket Layers (SSL) > SSL for Dashboard', 'better-wp-security' ) . "\n";
314
+ }
315
+
316
+ return $modification;
317
+ }
318
+
319
+ /**
320
+ * Calls lib function to determine whether ssl is available.
321
+ *
322
+ * @since 4.0
323
+ *
324
+ * @return void
325
+ */
326
+ public function plugin_init() {
327
+
328
+ if ( isset( get_current_screen()->id ) && strpos( get_current_screen()->id, 'security_page_toplevel_page_itsec_settings' ) !== false ) {
329
+
330
+ $this->has_ssl = ITSEC_Lib::get_ssl();
331
+
332
+ }
333
+
334
+ }
335
+
336
+ /**
337
+ * Register ban users for file writer
338
+ *
339
+ * @param array $file_modules array of file writer modules
340
+ *
341
+ * @return array array of file writer modules
342
+ */
343
+ public function register_file( $file_modules ) {
344
+
345
+ $file_modules['ssl'] = array(
346
+ 'config' => array( $this, 'save_config_rules' ),
347
+ );
348
+
349
+ return $file_modules;
350
+
351
+ }
352
+
353
+ /**
354
+ * Sanitize and validate input
355
+ *
356
+ * @param Array $input array of input fields
357
+ *
358
+ * @return Array Sanitized array
359
+ */
360
+ public function sanitize_module_input( $input ) {
361
+
362
+ $input['frontend'] = isset( $input['frontend'] ) ? intval( $input['frontend'] ) : 0;
363
+ $input['admin'] = ( isset( $input['admin'] ) && intval( $input['admin'] == 1 ) ? true : false );
364
+
365
+ if ( $input['admin'] !== $this->settings['admin'] ) {
366
+
367
+ add_site_option( 'itsec_config_changed', true );
368
+
369
+ if ( $input['admin'] === true || $input['admin'] === true ) {
370
+ add_site_option( 'itsec_clear_login', true );
371
+ }
372
+
373
+ }
374
+
375
+ if ( is_multisite() ) {
376
+
377
+ $this->settings = $input;
378
+
379
+ }
380
+
381
+ return $input;
382
+
383
+ }
384
+
385
+ /**
386
+ * Prepare and save options in network settings
387
+ *
388
+ * @return void
389
+ */
390
+ public function save_network_options() {
391
+
392
+ if ( isset( $_POST['itsec_ssl'] ) ) {
393
+
394
+ if ( ! wp_verify_nonce( $_POST['_wpnonce'], 'security_page_toplevel_page_itsec_settings-options' ) ) {
395
+ die( __( 'Security error!', 'better-wp-security' ) );
396
+ }
397
+
398
+ update_site_option( 'itsec_ssl', $_POST['itsec_ssl'] ); //we must manually save network options
399
+
400
+ }
401
+
402
+ }
403
+
404
+ /**
405
+ * Adds fields that will be tracked for Google Analytics
406
+ *
407
+ * @since 4.0
408
+ *
409
+ * @param array $vars tracking vars
410
+ *
411
+ * @return array tracking vars
412
+ */
413
+ public function tracking_vars( $vars ) {
414
+
415
+ $vars['itsec_ssl'] = array(
416
+ 'login' => '0:b',
417
+ 'admin' => '0:b',
418
+ 'frontend' => '0:s',
419
+ );
420
+
421
+ return $vars;
422
+
423
+ }
424
+
425
+ }
core/modules/ssl/class-itsec-ssl.php ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ class ITSEC_SSL {
4
+ private $http_site_url;
5
+ private $https_site_url;
6
+
7
+ public function run() {
8
+ add_action( 'template_redirect', array( $this, 'do_conditional_ssl_redirect' ), 0 );
9
+
10
+ if ( is_ssl() ) {
11
+ $this->http_site_url = site_url( '', 'http' );
12
+ $this->https_site_url = site_url( '', 'https' );
13
+
14
+ add_filter( 'the_content', array( $this, 'replace_content_urls' ) );
15
+ add_filter( 'script_loader_src', array( $this, 'script_loader_src' ) );
16
+ add_filter( 'style_loader_src', array( $this, 'style_loader_src' ) );
17
+ add_filter( 'upload_dir', array( $this, 'upload_dir' ) );
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Redirects to or from SSL where appropriate
23
+ *
24
+ * @since 4.0
25
+ *
26
+ * @return void
27
+ */
28
+ public function do_conditional_ssl_redirect() {
29
+ $hide_options = get_site_option( 'itsec_hide_backend', array() );
30
+
31
+ if ( isset( $hide_options['enabled'] ) && ( $hide_options['enabled'] === true ) && ( $_SERVER['REQUEST_URI'] == ITSEC_Lib::get_home_root() . $hide_options['slug'] ) ) {
32
+ return;
33
+ }
34
+
35
+
36
+ $settings = get_site_option( 'itsec_ssl', array() );
37
+
38
+ if ( 2 == $settings['frontend'] ) {
39
+ $protocol = 'https';
40
+ } else if ( ( 1 == $settings['frontend'] ) && is_singular() ) {
41
+ global $post;
42
+
43
+ $bwps_ssl = get_post_meta( $post->ID, 'bwps_enable_ssl' );
44
+
45
+ if ( ! empty( $bwps_ssl ) ) {
46
+ if ( $bwps_ssl[0] ) {
47
+ $protocol = 'https';
48
+ update_post_meta( $post->ID, 'itsec_enable_ssl', true );
49
+ }
50
+
51
+ delete_post_meta( $post->ID, 'bwps_enable_ssl' );
52
+ }
53
+
54
+ if ( ! isset( $protocol ) ) {
55
+ $enable_ssl = get_post_meta( $post->ID, 'itsec_enable_ssl' );
56
+
57
+ if ( ! empty( $enable_ssl ) ) {
58
+ if ( $enable_ssl[0] ) {
59
+ $protocol = 'https';
60
+ } else {
61
+ delete_post_meta( $post->ID, 'itsec_enable_ssl' );
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ if ( ! isset( $protocol ) ) {
68
+ $protocol = 'http';
69
+ }
70
+
71
+ $is_ssl = is_ssl();
72
+
73
+ if ( $is_ssl && ( 'http' == $protocol ) ) {
74
+ $redirect = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
75
+ } else if ( ! $is_ssl && ( 'https' == $protocol ) ) {
76
+ $redirect = "https://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}";
77
+ }
78
+
79
+ if ( isset( $redirect ) ) {
80
+ wp_redirect( $redirect, 302 );
81
+ exit();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Replace urls in content with ssl
87
+ *
88
+ * @since 4.1
89
+ *
90
+ * @param string $content the content
91
+ *
92
+ * @return string the content
93
+ */
94
+ public function replace_content_urls( $content ) {
95
+ return str_replace( $this->http_site_url, $this->https_site_url, $content );
96
+ }
97
+
98
+ /**
99
+ * Replace urls in scripts with ssl
100
+ *
101
+ * @since 4.4
102
+ *
103
+ * @param string $script_loader_src the url
104
+ *
105
+ * @return string the url
106
+ */
107
+ public function script_loader_src( $script_loader_src ) {
108
+ return str_replace( $this->http_site_url, $this->https_site_url, $script_loader_src );
109
+ }
110
+
111
+ /**
112
+ * Replace urls in styles with ssl
113
+ *
114
+ * @since 4.4
115
+ *
116
+ * @param string $style_loader_src the url
117
+ *
118
+ * @return string the url
119
+ */
120
+ public function style_loader_src( $style_loader_src ) {
121
+ return str_replace( $this->http_site_url, $this->https_site_url, $style_loader_src );
122
+ }
123
+
124
+ /**
125
+ * filter uploads dir so that plugins using it to determine upload URL also work
126
+ *
127
+ * @since 4.0
128
+ *
129
+ * @param array $uploads
130
+ *
131
+ * @return array
132
+ */
133
+ public function upload_dir( $upload_dir ) {
134
+ $upload_dir['url'] = str_replace( $this->http_site_url, $this->https_site_url, $upload_dir['url'] );
135
+ $upload_dir['baseurl'] = str_replace( $this->http_site_url, $this->https_site_url, $upload_dir['baseurl'] );
136
+
137
+ return $upload_dir;
138
+ }
139
+ }
core/modules/ssl/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ssl/js/admin-ssl.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ jQuery( document ).ready( function () {
2
+
3
+ jQuery( '#itsec_ssl_admin' ).change( function () {
4
+
5
+ if ( this.checked ) {
6
+
7
+ var ssl_confirm = confirm( ssl_warning_text.text );
8
+
9
+ if ( ssl_confirm == false ) {
10
+
11
+ jQuery( '#itsec_ssl_admin' ).attr( 'checked', false );
12
+
13
+ }
14
+
15
+ }
16
+
17
+ } );
18
+
19
+ } );
core/modules/ssl/js/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php //You don't belong here. ?>
core/modules/ssl/setup.php ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if ( ! class_exists( 'ITSEC_SSL_Setup' ) ) {
4
+
5
+ class ITSEC_SSL_Setup {
6
+
7
+ private
8
+ $defaults;
9
+
10
+ public function __construct() {
11
+
12
+ global $itsec_setup_action;
13
+
14
+ $this->defaults = array(
15
+ 'frontend' => 0,
16
+ 'admin' => false,
17
+ 'login' => false,
18
+ );
19
+
20
+ if ( isset( $itsec_setup_action ) ) {
21
+
22
+ switch ( $itsec_setup_action ) {
23
+
24
+ case 'activate':
25
+ $this->execute_activate();
26
+ break;
27
+ case 'upgrade':
28
+ $this->execute_upgrade();
29
+ break;
30
+ // case 'deactivate':
31
+ // $this->execute_deactivate();
32
+ // break;
33
+ case 'uninstall':
34
+ $this->execute_uninstall();
35
+ break;
36
+
37
+ }
38
+
39
+ } else {
40
+ wp_die( 'error' );
41
+ }
42
+
43
+ }
44
+
45
+ /**
46
+ * Execute module activation.
47
+ *
48
+ * @since 4.0
49
+ *
50
+ * @return void
51
+ */
52
+ public function execute_activate() {
53
+
54
+ $options = get_site_option( 'itsec_ssl' );
55
+ $initials = get_site_option( 'itsec_initials' );
56
+
57
+ if ( defined( 'FORCE_SSL_LOGIN' ) && FORCE_SSL_LOGIN === true ) {
58
+ $initials['login'] = true;
59
+ } else {
60
+ $initials['login'] = false;
61
+ }
62
+
63
+ if ( defined( 'FORCE_SSL_ADMIN' ) && FORCE_SSL_ADMIN === true ) {
64
+ $initials['admin'] = true;
65
+ } else {
66
+ $initials['admin'] = false;
67
+ }
68
+
69
+ update_site_option( 'itsec_initials', $initials );
70
+
71
+ if ( $options === false ) {
72
+
73
+ if ( defined( 'FORCE_SSL_LOGIN' ) &