Version Description
- Add separate logging for REST authentication.
- Fix conflict with earlier versions pre-installed in
mu-plugins
. See Is WPf2b Already Installed?.
Download this release
Release Info
Developer | invisnet |
Plugin | WP fail2ban |
Version | 4.1.0 |
Comparing to | |
See all releases |
Code changes from version 4.0.2 to 4.1.0
- admin/summary.php +49 -43
- feature/comments.php +137 -105
- feature/lib.php +11 -4
- feature/password.php +20 -14
- feature/spam.php +40 -34
- feature/user-enum.php +78 -55
- feature/user.php +40 -34
- feature/xmlrpc.php +67 -53
- feature/xmlrpc/pingback.php +21 -15
- filters.d/wordpress-extra.conf +1 -1
- filters.d/wordpress-hard.conf +2 -3
- filters.d/wordpress-soft.conf +2 -1
- readme.txt +73 -49
- vendor/freemius/wordpress-sdk/includes/class-freemius.php +22 -1
- vendor/freemius/wordpress-sdk/start.php +1 -1
- vendor/freemius/wordpress-sdk/templates/debug.php +2 -0
- vendor/freemius/wordpress-sdk/templates/forms/deactivation/form.php +497 -497
- wp-fail2ban-main.php +151 -34
- wp-fail2ban.php +4 -10
admin/summary.php
CHANGED
@@ -40,7 +40,7 @@ function _doc_link($define, $name)
|
|
40 |
static $wp_f2b_ver;
|
41 |
|
42 |
if (empty($wp_f2b_ver)) {
|
43 |
-
$wp_f2b_ver = substr(
|
44 |
}
|
45 |
|
46 |
return sprintf('<a href="https://wp-fail2ban.readthedocs.io/en/%s/defines.html#%s" target="_blank">%s <span class="dashicons dashicons-external"></span></a>', $wp_f2b_ver, str_replace('_', '-', strtolower($define)), $name);
|
@@ -228,48 +228,54 @@ function summary()
|
|
228 |
<hr>
|
229 |
<?php endif; ?>
|
230 |
<?php endif; ?>
|
231 |
-
<
|
232 |
-
|
233 |
-
<
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
273 |
</div>
|
274 |
<?php
|
275 |
}
|
40 |
static $wp_f2b_ver;
|
41 |
|
42 |
if (empty($wp_f2b_ver)) {
|
43 |
+
$wp_f2b_ver = substr(WP_FAIL2BAN_VER, 0, strrpos(WP_FAIL2BAN_VER, '.'));
|
44 |
}
|
45 |
|
46 |
return sprintf('<a href="https://wp-fail2ban.readthedocs.io/en/%s/defines.html#%s" target="_blank">%s <span class="dashicons dashicons-external"></span></a>', $wp_f2b_ver, str_replace('_', '-', strtolower($define)), $name);
|
228 |
<hr>
|
229 |
<?php endif; ?>
|
230 |
<?php endif; ?>
|
231 |
+
<div class="card">
|
232 |
+
<h2 class="title"><?php _e('Logging'); ?></h2>
|
233 |
+
<table class="form-table">
|
234 |
+
<tbody>
|
235 |
+
<?php
|
236 |
+
_log(__('Comments'), 'WP_FAIL2BAN_LOG_COMMENTS', 'WP_FAIL2BAN_COMMENT_LOG');
|
237 |
+
_log(__('Password Requests'), 'WP_FAIL2BAN_LOG_PASSWORD_REQUEST', 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG');
|
238 |
+
_log(__('Pingbacks'), 'WP_FAIL2BAN_LOG_PINGBACKS', 'WP_FAIL2BAN_PINGBACK_LOG');
|
239 |
+
_log(__('Spam'), 'WP_FAIL2BAN_LOG_SPAM', 'WP_FAIL2BAN_SPAM_LOG');
|
240 |
+
_log_options();
|
241 |
+
?>
|
242 |
+
</tbody>
|
243 |
+
</table>
|
244 |
+
</div>
|
245 |
+
<div class="card">
|
246 |
+
<h2 class="title"><?php _e('Workarounds'); ?></h2>
|
247 |
+
<table class="form-table">
|
248 |
+
<tbody>
|
249 |
+
<?php
|
250 |
+
_yes_no_array(__('Short Tag'), 'WP_FAIL2BAN_SYSLOG_SHORT_TAG');
|
251 |
+
_yes_no_array(__('Specify Host'), 'WP_FAIL2BAN_HTTP_HOST');
|
252 |
+
_yes_no_array(__('Truncate Host'), 'WP_FAIL2BAN_TRUNCATE_HOST');
|
253 |
+
?>
|
254 |
+
</tbody>
|
255 |
+
</table>
|
256 |
+
</div>
|
257 |
+
<div class="card">
|
258 |
+
<h2 class="title"><?php _e('Block'); ?></h2>
|
259 |
+
<table class="form-table">
|
260 |
+
<tbody>
|
261 |
+
<?php
|
262 |
+
_yes_no_array(__('User Enumeration'), 'WP_FAIL2BAN_BLOCK_USER_ENUMERATION');
|
263 |
+
_yes_no_array(__('Usernames'), 'WP_FAIL2BAN_BLOCKED_USERS', null);
|
264 |
+
?>
|
265 |
+
</tbody>
|
266 |
+
</table>
|
267 |
+
</div>
|
268 |
+
<div class="card">
|
269 |
+
<h2 class="title"><?php _e('Misc'); ?></h2>
|
270 |
+
<table class="form-table">
|
271 |
+
<tbody>
|
272 |
+
<?php
|
273 |
+
_yes_no_array(__('Proxies'), 'WP_FAIL2BAN_PROXIES', true);
|
274 |
+
_yes_no_array(__('Remote Address'), 'WP_FAIL2BAN_REMOTE_ADDR', null);
|
275 |
+
?>
|
276 |
+
</tbody>
|
277 |
+
</table>
|
278 |
+
</div>
|
279 |
</div>
|
280 |
<?php
|
281 |
}
|
feature/comments.php
CHANGED
@@ -12,144 +12,176 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 3.5.0
|
18 |
-
*
|
19 |
-
* @param bool $maybe_notify
|
20 |
-
* @param int $comment_ID
|
21 |
-
*
|
22 |
-
* @return bool
|
23 |
-
*
|
24 |
-
* @wp-f2b-extra Comment \d+
|
25 |
*/
|
26 |
-
|
27 |
-
{
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
}
|
33 |
|
34 |
-
add_filter(
|
35 |
-
'notify_post_author',
|
36 |
-
__NAMESPACE__ . '\\notify_post_author',
|
37 |
-
10,
|
38 |
-
2
|
39 |
-
);
|
40 |
|
41 |
if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
|
42 |
/** WPF2B_ACTION_COMMENT_NOT_FOUND */
|
43 |
-
|
44 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20002 ) {
|
45 |
/**
|
46 |
-
*
|
47 |
-
*
|
48 |
-
* @since 4.0.0
|
49 |
-
*
|
50 |
-
* @param int $comment_post_ID
|
51 |
-
*
|
52 |
-
* @wp-f2b-extra Comment post not found \d+
|
53 |
*/
|
54 |
-
function comment_id_not_found( $comment_post_ID )
|
55 |
-
{
|
56 |
-
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
57 |
-
syslog( LOG_INFO, "Comment post not found {$comment_post_ID}" );
|
58 |
-
// @codeCoverageIgnoreEnd
|
59 |
-
}
|
60 |
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
|
|
64 |
/** LOG_ACTION_LOG_COMMENT_CLOSED */
|
65 |
-
|
66 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20004 ) {
|
67 |
/**
|
68 |
-
*
|
69 |
-
*
|
70 |
-
* @since 4.0.0
|
71 |
-
*
|
72 |
-
* @param int $comment_post_ID
|
73 |
-
*
|
74 |
-
* @wp-f2b-extra Comments closed on post \d+
|
75 |
*/
|
76 |
-
function comment_closed( $comment_post_ID )
|
77 |
-
{
|
78 |
-
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
79 |
-
syslog( LOG_INFO, "Comments closed on post {$comment_post_ID}" );
|
80 |
-
// @codeCoverageIgnoreEnd
|
81 |
-
}
|
82 |
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
|
|
|
86 |
/** LOG_ACTION_LOG_COMMENT_TRASH */
|
87 |
-
|
88 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20008 ) {
|
89 |
/**
|
90 |
-
*
|
91 |
-
*
|
92 |
-
* @since 4.0.2 Fix message
|
93 |
-
* @since 4.0.0
|
94 |
-
*
|
95 |
-
* @param int $comment_post_ID
|
96 |
-
*
|
97 |
-
* @wp-f2b-extra Comment attempt on trash post \d+
|
98 |
*/
|
99 |
-
function comment_on_trash( $comment_post_ID )
|
100 |
-
{
|
101 |
-
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
102 |
-
syslog( LOG_INFO, "Comment attempt on trash post {$comment_post_ID}" );
|
103 |
-
// @codeCoverageIgnoreEnd
|
104 |
-
}
|
105 |
|
106 |
-
|
107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
|
|
109 |
/** LOG_ACTION_LOG_COMMENT_DRAFT */
|
110 |
-
|
111 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20010 ) {
|
112 |
/**
|
113 |
-
*
|
114 |
-
*
|
115 |
-
* @since 4.0.2 Fix message
|
116 |
-
* @since 4.0.0
|
117 |
-
*
|
118 |
-
* @param int $comment_post_ID
|
119 |
-
*
|
120 |
-
* @wp-f2b-extra Comment attempt on draft post \d+
|
121 |
*/
|
122 |
-
function comment_on_draft( $comment_post_ID )
|
123 |
-
{
|
124 |
-
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
125 |
-
syslog( LOG_INFO, "Comment attempt on draft post {$comment_post_ID}" );
|
126 |
-
// @codeCoverageIgnoreEnd
|
127 |
-
}
|
128 |
|
129 |
-
|
130 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
131 |
|
|
|
132 |
/** LOG_ACTION_LOG_COMMENT_PASSWORD */
|
133 |
-
|
134 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20020 ) {
|
135 |
/**
|
136 |
-
*
|
137 |
-
*
|
138 |
-
* @since 4.0.2 Fix message
|
139 |
-
* @since 4.0.0
|
140 |
-
*
|
141 |
-
* @param int $comment_post_ID
|
142 |
-
*
|
143 |
-
* @wp-f2b-extra Comment attempt on password-protected post \d+
|
144 |
*/
|
145 |
-
function comment_on_password_protected( $comment_post_ID )
|
146 |
-
{
|
147 |
-
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
148 |
-
syslog( LOG_INFO, "Comment attempt on password-protected post {$comment_post_ID}" );
|
149 |
-
// @codeCoverageIgnoreEnd
|
150 |
-
}
|
151 |
|
152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
}
|
154 |
-
|
155 |
}
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\notify_post_author' ) ) {
|
19 |
+
/**
|
20 |
+
* Log new comment
|
21 |
+
*
|
22 |
+
* @since 3.5.0
|
23 |
+
*
|
24 |
+
* @param bool $maybe_notify
|
25 |
+
* @param int $comment_ID
|
26 |
+
*
|
27 |
+
* @return bool
|
28 |
+
*
|
29 |
+
* @wp-f2b-extra Comment \d+
|
30 |
+
*/
|
31 |
+
function notify_post_author( $maybe_notify, $comment_ID )
|
32 |
+
{
|
33 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
34 |
+
syslog( LOG_INFO, "Comment {$comment_ID}" );
|
35 |
+
// @codeCoverageIgnoreEnd
|
36 |
+
return $maybe_notify;
|
37 |
+
}
|
38 |
+
|
39 |
+
add_filter(
|
40 |
+
'notify_post_author',
|
41 |
+
__NAMESPACE__ . '\\notify_post_author',
|
42 |
+
10,
|
43 |
+
2
|
44 |
+
);
|
45 |
}
|
46 |
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
if ( defined( 'WP_FAIL2BAN_LOG_COMMENTS_EXTRA' ) ) {
|
49 |
/** WPF2B_ACTION_COMMENT_NOT_FOUND */
|
|
|
50 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20002 ) {
|
51 |
/**
|
52 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
+
if ( !function_exists( __NAMESPACE__ . '\\comment_id_not_found' ) ) {
|
56 |
+
/**
|
57 |
+
* Log attempted comment on non-existent post
|
58 |
+
*
|
59 |
+
* @since 4.0.0
|
60 |
+
*
|
61 |
+
* @param int $comment_post_ID
|
62 |
+
*
|
63 |
+
* @wp-f2b-extra Comment post not found \d+
|
64 |
+
*/
|
65 |
+
function comment_id_not_found( $comment_post_ID )
|
66 |
+
{
|
67 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
68 |
+
syslog( LOG_NOTICE, "Comment post not found {$comment_post_ID}" );
|
69 |
+
// @codeCoverageIgnoreEnd
|
70 |
+
}
|
71 |
+
|
72 |
+
add_action( 'comment_id_not_found', __NAMESPACE__ . '\\comment_id_not_found' );
|
73 |
+
}
|
74 |
|
75 |
+
}
|
76 |
/** LOG_ACTION_LOG_COMMENT_CLOSED */
|
|
|
77 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20004 ) {
|
78 |
/**
|
79 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
+
if ( !function_exists( __NAMESPACE__ . '\\comment_closed' ) ) {
|
83 |
+
/**
|
84 |
+
* Log attempted comment on closed post
|
85 |
+
*
|
86 |
+
* @since 4.0.0
|
87 |
+
*
|
88 |
+
* @param int $comment_post_ID
|
89 |
+
*
|
90 |
+
* @wp-f2b-extra Comments closed on post \d+
|
91 |
+
*/
|
92 |
+
function comment_closed( $comment_post_ID )
|
93 |
+
{
|
94 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
95 |
+
syslog( LOG_NOTICE, "Comments closed on post {$comment_post_ID}" );
|
96 |
+
// @codeCoverageIgnoreEnd
|
97 |
+
}
|
98 |
+
|
99 |
+
add_action( 'comment_closed', __NAMESPACE__ . '\\comment_closed' );
|
100 |
+
}
|
101 |
|
102 |
+
}
|
103 |
/** LOG_ACTION_LOG_COMMENT_TRASH */
|
|
|
104 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20008 ) {
|
105 |
/**
|
106 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
|
109 |
+
if ( !function_exists( __NAMESPACE__ . '\\comment_on_trash' ) ) {
|
110 |
+
/**
|
111 |
+
* Log attempted comment on trashed post
|
112 |
+
*
|
113 |
+
* @since 4.0.2 Fix message
|
114 |
+
* @since 4.0.0
|
115 |
+
*
|
116 |
+
* @param int $comment_post_ID
|
117 |
+
*
|
118 |
+
* @wp-f2b-extra Comment attempt on trash post \d+
|
119 |
+
*/
|
120 |
+
function comment_on_trash( $comment_post_ID )
|
121 |
+
{
|
122 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
123 |
+
syslog( LOG_NOTICE, "Comment attempt on trash post {$comment_post_ID}" );
|
124 |
+
// @codeCoverageIgnoreEnd
|
125 |
+
}
|
126 |
+
|
127 |
+
add_action( 'comment_on_trash', __NAMESPACE__ . '\\comment_on_trash' );
|
128 |
+
}
|
129 |
|
130 |
+
}
|
131 |
/** LOG_ACTION_LOG_COMMENT_DRAFT */
|
|
|
132 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20010 ) {
|
133 |
/**
|
134 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
+
if ( !function_exists( __NAMESPACE__ . '\\comment_on_draft' ) ) {
|
138 |
+
/**
|
139 |
+
* Log attempted comment on draft post
|
140 |
+
*
|
141 |
+
* @since 4.0.2 Fix message
|
142 |
+
* @since 4.0.0
|
143 |
+
*
|
144 |
+
* @param int $comment_post_ID
|
145 |
+
*
|
146 |
+
* @wp-f2b-extra Comment attempt on draft post \d+
|
147 |
+
*/
|
148 |
+
function comment_on_draft( $comment_post_ID )
|
149 |
+
{
|
150 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
151 |
+
syslog( LOG_NOTICE, "Comment attempt on draft post {$comment_post_ID}" );
|
152 |
+
// @codeCoverageIgnoreEnd
|
153 |
+
}
|
154 |
+
|
155 |
+
add_action( 'comment_on_draft', __NAMESPACE__ . '\\comment_on_draft' );
|
156 |
+
}
|
157 |
|
158 |
+
}
|
159 |
/** LOG_ACTION_LOG_COMMENT_PASSWORD */
|
|
|
160 |
if ( WP_FAIL2BAN_LOG_COMMENTS_EXTRA & 0x20020 ) {
|
161 |
/**
|
162 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
|
165 |
+
if ( !function_exists( __NAMESPACE__ . '\\comment_on_password_protected' ) ) {
|
166 |
+
/**
|
167 |
+
* Log attempted comment on password-protected post
|
168 |
+
*
|
169 |
+
* @since 4.0.2 Fix message
|
170 |
+
* @since 4.0.0
|
171 |
+
*
|
172 |
+
* @param int $comment_post_ID
|
173 |
+
*
|
174 |
+
* @wp-f2b-extra Comment attempt on password-protected post \d+
|
175 |
+
*/
|
176 |
+
function comment_on_password_protected( $comment_post_ID )
|
177 |
+
{
|
178 |
+
openlog( 'WP_FAIL2BAN_COMMENT_LOG' );
|
179 |
+
syslog( LOG_NOTICE, "Comment attempt on password-protected post {$comment_post_ID}" );
|
180 |
+
// @codeCoverageIgnoreEnd
|
181 |
+
}
|
182 |
+
|
183 |
+
add_action( 'comment_on_password_protected', __NAMESPACE__ . '\\comment_on_password_protected' );
|
184 |
+
}
|
185 |
+
|
186 |
}
|
|
|
187 |
}
|
feature/lib.php
CHANGED
@@ -71,13 +71,20 @@ function syslog( $level, $msg, $remote_addr = null )
|
|
71 |
/**
|
72 |
* Graceful immediate exit
|
73 |
*
|
|
|
74 |
* @since 3.5.0 Refactored for unit testing
|
75 |
*/
|
76 |
-
function bail()
|
77 |
{
|
78 |
-
|
79 |
-
|
80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
}
|
82 |
|
83 |
/**
|
71 |
/**
|
72 |
* Graceful immediate exit
|
73 |
*
|
74 |
+
* @since 4.0.5 Add JSON support
|
75 |
* @since 3.5.0 Refactored for unit testing
|
76 |
*/
|
77 |
+
function bail( $is_json = false )
|
78 |
{
|
79 |
+
|
80 |
+
if ( $is_json ) {
|
81 |
+
return new \WP_Error( 403, 'Forbidden' );
|
82 |
+
} else {
|
83 |
+
wp_die( 'Forbidden', 'Forbidden', array(
|
84 |
+
'response' => 403,
|
85 |
+
) );
|
86 |
+
}
|
87 |
+
|
88 |
}
|
89 |
|
90 |
/**
|
feature/password.php
CHANGED
@@ -12,19 +12,25 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 3.5.0
|
18 |
-
*
|
19 |
-
* @param string $user_login
|
20 |
-
*
|
21 |
-
* @wp-f2b-extra Password reset requested for .*
|
22 |
*/
|
23 |
-
function retrieve_password( $user_login )
|
24 |
-
{
|
25 |
-
openlog( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' );
|
26 |
-
syslog( LOG_NOTICE, "Password reset requested for {$user_login}" );
|
27 |
-
// @codeCoverageIgnoreEnd
|
28 |
-
}
|
29 |
|
30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\retrieve_password' ) ) {
|
19 |
+
/**
|
20 |
+
* Log password reset requests
|
21 |
+
*
|
22 |
+
* @since 3.5.0
|
23 |
+
*
|
24 |
+
* @param string $user_login
|
25 |
+
*
|
26 |
+
* @wp-f2b-extra Password reset requested for .*
|
27 |
+
*/
|
28 |
+
function retrieve_password( $user_login )
|
29 |
+
{
|
30 |
+
openlog( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' );
|
31 |
+
syslog( LOG_NOTICE, "Password reset requested for {$user_login}" );
|
32 |
+
// @codeCoverageIgnoreEnd
|
33 |
+
}
|
34 |
+
|
35 |
+
add_action( 'retrieve_password', __NAMESPACE__ . '\\retrieve_password' );
|
36 |
+
}
|
feature/spam.php
CHANGED
@@ -12,42 +12,48 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 3.5.0
|
18 |
-
*
|
19 |
-
* @param int $comment_id
|
20 |
-
* @param string $comment_status
|
21 |
-
*
|
22 |
-
* @wp-f2b-hard Spam comment \d+
|
23 |
*/
|
24 |
-
|
25 |
-
{
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
-
if ( is_null( $comment = get_comment( $comment_id, ARRAY_A ) ) ) {
|
29 |
-
/**
|
30 |
-
* @todo: decide what to do about this
|
31 |
-
*/
|
32 |
-
} else {
|
33 |
-
$remote_addr = ( empty($comment['comment_author_IP']) ? 'unknown' : $comment['comment_author_IP'] );
|
34 |
-
openlog( 'WP_FAIL2BAN_SPAM_LOG' );
|
35 |
-
syslog( LOG_INFO, "Spam comment {$comment_id}", $remote_addr );
|
36 |
-
// @codeCoverageIgnoreEnd
|
37 |
}
|
38 |
-
|
39 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
}
|
41 |
-
|
42 |
-
add_action(
|
43 |
-
'comment_post',
|
44 |
-
__NAMESPACE__ . '\\log_spam_comment',
|
45 |
-
10,
|
46 |
-
2
|
47 |
-
);
|
48 |
-
add_action(
|
49 |
-
'wp_set_comment_status',
|
50 |
-
__NAMESPACE__ . '\\log_spam_comment',
|
51 |
-
10,
|
52 |
-
2
|
53 |
-
);
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\log_spam_comment' ) ) {
|
19 |
+
/**
|
20 |
+
* Catch comments marked as spam
|
21 |
+
*
|
22 |
+
* @since 3.5.0
|
23 |
+
*
|
24 |
+
* @param int $comment_id
|
25 |
+
* @param string $comment_status
|
26 |
+
*
|
27 |
+
* @wp-f2b-hard Spam comment \d+
|
28 |
+
*/
|
29 |
+
function log_spam_comment( $comment_id, $comment_status )
|
30 |
+
{
|
31 |
+
if ( 'spam' === $comment_status ) {
|
32 |
+
|
33 |
+
if ( is_null( $comment = get_comment( $comment_id, ARRAY_A ) ) ) {
|
34 |
+
/**
|
35 |
+
* @todo: decide what to do about this
|
36 |
+
*/
|
37 |
+
} else {
|
38 |
+
$remote_addr = ( empty($comment['comment_author_IP']) ? 'unknown' : $comment['comment_author_IP'] );
|
39 |
+
openlog( 'WP_FAIL2BAN_SPAM_LOG' );
|
40 |
+
syslog( LOG_NOTICE, "Spam comment {$comment_id}", $remote_addr );
|
41 |
+
// @codeCoverageIgnoreEnd
|
42 |
+
}
|
43 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
}
|
|
|
45 |
}
|
46 |
+
|
47 |
+
add_action(
|
48 |
+
'comment_post',
|
49 |
+
__NAMESPACE__ . '\\log_spam_comment',
|
50 |
+
10,
|
51 |
+
2
|
52 |
+
);
|
53 |
+
add_action(
|
54 |
+
'wp_set_comment_status',
|
55 |
+
__NAMESPACE__ . '\\log_spam_comment',
|
56 |
+
10,
|
57 |
+
2
|
58 |
+
);
|
59 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
feature/user-enum.php
CHANGED
@@ -12,69 +12,92 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 4.0.0
|
18 |
-
*
|
19 |
-
* @wp-f2b-hard Blocked user enumeration attempt
|
20 |
*/
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
|
|
29 |
/**
|
30 |
-
*
|
31 |
-
*
|
32 |
-
* @see \WP::parse_request()
|
33 |
-
*
|
34 |
-
* @since 3.5.0 Refactored for unit testing
|
35 |
-
* @since 2.1.0
|
36 |
-
*
|
37 |
-
* @param \WP $query
|
38 |
-
*
|
39 |
-
* @return \WP
|
40 |
*/
|
41 |
-
|
42 |
-
{
|
43 |
-
|
44 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
}
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
|
49 |
-
add_filter(
|
50 |
-
'parse_request',
|
51 |
-
__NAMESPACE__ . '\\parse_request',
|
52 |
-
1,
|
53 |
-
2
|
54 |
-
);
|
55 |
/**
|
56 |
-
*
|
57 |
-
*
|
58 |
-
* @see \WP_REST_Users_Controller::get_items()
|
59 |
-
*
|
60 |
-
* @since 4.0.0
|
61 |
-
*
|
62 |
-
* @param array $prepared_args
|
63 |
-
* @param \WP_REST_Request $request
|
64 |
-
*
|
65 |
-
* @return array
|
66 |
*/
|
67 |
-
|
68 |
-
{
|
69 |
-
|
70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
71 |
}
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
-
|
75 |
-
add_filter(
|
76 |
-
'rest_user_query',
|
77 |
-
__NAMESPACE__ . '\\rest_user_query',
|
78 |
-
10,
|
79 |
-
2
|
80 |
-
);
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
if ( !function_exists( __NAMESPACE__ . '\\_log_bail_user_enum' ) ) {
|
18 |
+
/**
|
19 |
+
* Common enumeration handling
|
20 |
+
*
|
21 |
+
* @since 4.1.0 Add JSON support
|
22 |
+
* @since 4.0.0
|
23 |
+
*
|
24 |
+
* @param bool $is_json
|
25 |
+
*
|
26 |
+
* @return \WP_Error
|
27 |
+
*
|
28 |
+
* @wp-f2b-hard Blocked user enumeration attempt
|
29 |
+
*/
|
30 |
+
function _log_bail_user_enum( $is_json = false )
|
31 |
+
{
|
32 |
+
openlog();
|
33 |
+
syslog( LOG_NOTICE, 'Blocked user enumeration attempt' );
|
34 |
+
// @codeCoverageIgnoreEnd
|
35 |
+
return bail( $is_json );
|
36 |
+
}
|
37 |
|
38 |
+
}
|
39 |
/**
|
40 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
*/
|
42 |
+
|
43 |
+
if ( !function_exists( __NAMESPACE__ . '\\parse_request' ) ) {
|
44 |
+
/**
|
45 |
+
* Catch traditional user enum
|
46 |
+
*
|
47 |
+
* @see \WP::parse_request()
|
48 |
+
*
|
49 |
+
* @since 3.5.0 Refactored for unit testing
|
50 |
+
* @since 2.1.0
|
51 |
+
*
|
52 |
+
* @param \WP $query
|
53 |
+
*
|
54 |
+
* @return \WP
|
55 |
+
*/
|
56 |
+
function parse_request( $query )
|
57 |
+
{
|
58 |
+
if ( !current_user_can( 'list_users' ) && intval( @$query->query_vars['author'] ) ) {
|
59 |
+
_log_bail_user_enum();
|
60 |
+
}
|
61 |
+
return $query;
|
62 |
}
|
63 |
+
|
64 |
+
add_filter(
|
65 |
+
'parse_request',
|
66 |
+
__NAMESPACE__ . '\\parse_request',
|
67 |
+
1,
|
68 |
+
2
|
69 |
+
);
|
70 |
}
|
71 |
|
|
|
|
|
|
|
|
|
|
|
|
|
72 |
/**
|
73 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
*/
|
75 |
+
|
76 |
+
if ( !function_exists( __NAMESPACE__ . '\\rest_user_query' ) ) {
|
77 |
+
/**
|
78 |
+
* Catch RESTful user list
|
79 |
+
*
|
80 |
+
* @see \WP_REST_Users_Controller::get_items()
|
81 |
+
*
|
82 |
+
* @since 4.0.0
|
83 |
+
*
|
84 |
+
* @param array $prepared_args
|
85 |
+
* @param \WP_REST_Request $request
|
86 |
+
*
|
87 |
+
* @return array|\WP_Error
|
88 |
+
*/
|
89 |
+
function rest_user_query( $prepared_args, $request )
|
90 |
+
{
|
91 |
+
if ( !current_user_can( 'list_users' ) ) {
|
92 |
+
return _log_bail_user_enum( true );
|
93 |
+
}
|
94 |
+
return $prepared_args;
|
95 |
}
|
96 |
+
|
97 |
+
add_filter(
|
98 |
+
'rest_user_query',
|
99 |
+
__NAMESPACE__ . '\\rest_user_query',
|
100 |
+
10,
|
101 |
+
2
|
102 |
+
);
|
103 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
feature/user.php
CHANGED
@@ -12,43 +12,49 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 3.5.0 Refactored for unit testing
|
18 |
-
* @since 2.0.0
|
19 |
-
*
|
20 |
-
* @param mixed|null $user
|
21 |
-
* @param string $username
|
22 |
-
* @param string $password
|
23 |
-
*
|
24 |
-
* @return mixed|null
|
25 |
-
*
|
26 |
-
* @wp-f2b-hard Blocked authentication attempt for .*
|
27 |
*/
|
28 |
-
|
29 |
-
{
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
-
if ( $matched ) {
|
38 |
-
openlog();
|
39 |
-
syslog( LOG_NOTICE, "Blocked authentication attempt for {$username}" );
|
40 |
-
// @codeCoverageIgnoreEnd
|
41 |
-
bail();
|
42 |
}
|
43 |
-
|
|
|
44 |
}
|
45 |
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
47 |
}
|
48 |
-
|
49 |
-
add_filter(
|
50 |
-
'authenticate',
|
51 |
-
__NAMESPACE__ . '\\authenticate',
|
52 |
-
1,
|
53 |
-
3
|
54 |
-
);
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\authenticate' ) ) {
|
19 |
+
/**
|
20 |
+
* Catched blocked users
|
21 |
+
*
|
22 |
+
* @since 3.5.0 Refactored for unit testing
|
23 |
+
* @since 2.0.0
|
24 |
+
*
|
25 |
+
* @param mixed|null $user
|
26 |
+
* @param string $username
|
27 |
+
* @param string $password
|
28 |
+
*
|
29 |
+
* @return mixed|null
|
30 |
+
*
|
31 |
+
* @wp-f2b-hard Blocked authentication attempt for .*
|
32 |
+
*/
|
33 |
+
function authenticate( $user, $username, $password )
|
34 |
+
{
|
35 |
+
|
36 |
+
if ( !empty($username) ) {
|
37 |
+
/**
|
38 |
+
* @since 3.5.0 Arrays allowed in PHP 7
|
39 |
+
*/
|
40 |
+
$matched = ( is_array( WP_FAIL2BAN_BLOCKED_USERS ) ? in_array( $username, WP_FAIL2BAN_BLOCKED_USERS ) : preg_match( '/' . WP_FAIL2BAN_BLOCKED_USERS . '/i', $username ) );
|
41 |
+
|
42 |
+
if ( $matched ) {
|
43 |
+
openlog();
|
44 |
+
syslog( LOG_NOTICE, "Blocked authentication attempt for {$username}" );
|
45 |
+
// @codeCoverageIgnoreEnd
|
46 |
+
bail();
|
47 |
+
}
|
48 |
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
+
|
51 |
+
return $user;
|
52 |
}
|
53 |
|
54 |
+
add_filter(
|
55 |
+
'authenticate',
|
56 |
+
__NAMESPACE__ . '\\authenticate',
|
57 |
+
1,
|
58 |
+
3
|
59 |
+
);
|
60 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
feature/xmlrpc.php
CHANGED
@@ -12,70 +12,84 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @see \wp_xmlrpc_server::login()
|
18 |
-
*
|
19 |
-
* @since 4.0.0 Return $error
|
20 |
-
* @since 3.5.0 Refactored for unit testing
|
21 |
-
* @since 3.0.0
|
22 |
-
*
|
23 |
-
* @param \IXR_Error $error
|
24 |
-
* @param \WP_Error $user
|
25 |
-
*
|
26 |
-
* @return \IXR_Error
|
27 |
-
*
|
28 |
-
* @wp-f2b-hard XML-RPC multicall authentication failure
|
29 |
*/
|
30 |
-
|
31 |
-
{
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
-
if ( ++$attempts > 1 ) {
|
35 |
-
openlog();
|
36 |
-
syslog( LOG_NOTICE, 'XML-RPC multicall authentication failure' );
|
37 |
-
// @codeCoverageIgnoreEnd
|
38 |
-
bail();
|
39 |
-
} else {
|
40 |
-
return $error;
|
41 |
}
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
}
|
44 |
|
45 |
-
add_action(
|
46 |
-
'xmlrpc_login_error',
|
47 |
-
__NAMESPACE__ . '\\xmlrpc_login_error',
|
48 |
-
10,
|
49 |
-
2
|
50 |
-
);
|
51 |
/**
|
52 |
-
*
|
53 |
-
*
|
54 |
-
* @see \wp_xmlrpc_server::pingback_error()
|
55 |
-
*
|
56 |
-
* @since 4.0.0 Return $ixr_error
|
57 |
-
* @since 3.5.0 Refactored for unit testing
|
58 |
-
* @since 3.0.0
|
59 |
-
*
|
60 |
-
* @param \IXR_Error $ixr_error
|
61 |
-
*
|
62 |
-
* @return \IXR_Error
|
63 |
-
*
|
64 |
-
* @wp-f2b-hard Pingback error .* generated
|
65 |
*/
|
66 |
-
|
67 |
-
{
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
|
75 |
-
|
76 |
}
|
77 |
|
78 |
-
add_filter( 'xmlrpc_pingback_error', __NAMESPACE__ . '\\xmlrpc_pingback_error', 5 );
|
79 |
/**
|
80 |
* @since 4.0.0 Refactored
|
81 |
* @since 2.2.0
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_login_error' ) ) {
|
19 |
+
/**
|
20 |
+
* Catch multiple XML-RPC authentication failures
|
21 |
+
*
|
22 |
+
* @see \wp_xmlrpc_server::login()
|
23 |
+
*
|
24 |
+
* @since 4.0.0 Return $error
|
25 |
+
* @since 3.5.0 Refactored for unit testing
|
26 |
+
* @since 3.0.0
|
27 |
+
*
|
28 |
+
* @param \IXR_Error $error
|
29 |
+
* @param \WP_Error $user
|
30 |
+
*
|
31 |
+
* @return \IXR_Error
|
32 |
+
*
|
33 |
+
* @wp-f2b-hard XML-RPC multicall authentication failure
|
34 |
+
*/
|
35 |
+
function xmlrpc_login_error( $error, $user )
|
36 |
+
{
|
37 |
+
static $attempts = 0 ;
|
38 |
+
|
39 |
+
if ( ++$attempts > 1 ) {
|
40 |
+
openlog();
|
41 |
+
syslog( LOG_NOTICE, 'XML-RPC multicall authentication failure' );
|
42 |
+
// @codeCoverageIgnoreEnd
|
43 |
+
bail();
|
44 |
+
} else {
|
45 |
+
return $error;
|
46 |
+
}
|
47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
}
|
49 |
+
|
50 |
+
add_action(
|
51 |
+
'xmlrpc_login_error',
|
52 |
+
__NAMESPACE__ . '\\xmlrpc_login_error',
|
53 |
+
10,
|
54 |
+
2
|
55 |
+
);
|
56 |
}
|
57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
/**
|
59 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
60 |
*/
|
61 |
+
|
62 |
+
if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_pingback_error' ) ) {
|
63 |
+
/**
|
64 |
+
* Catch failed pingbacks
|
65 |
+
*
|
66 |
+
* @see \wp_xmlrpc_server::pingback_error()
|
67 |
+
*
|
68 |
+
* @since 4.0.0 Return $ixr_error
|
69 |
+
* @since 3.5.0 Refactored for unit testing
|
70 |
+
* @since 3.0.0
|
71 |
+
*
|
72 |
+
* @param \IXR_Error $ixr_error
|
73 |
+
*
|
74 |
+
* @return \IXR_Error
|
75 |
+
*
|
76 |
+
* @wp-f2b-hard Pingback error .* generated
|
77 |
+
*/
|
78 |
+
function xmlrpc_pingback_error( $ixr_error )
|
79 |
+
{
|
80 |
+
|
81 |
+
if ( 48 !== $ixr_error->code ) {
|
82 |
+
openlog();
|
83 |
+
syslog( LOG_NOTICE, 'Pingback error ' . $ixr_error->code . ' generated' );
|
84 |
+
// @codeCoverageIgnoreEnd
|
85 |
+
}
|
86 |
+
|
87 |
+
return $ixr_error;
|
88 |
}
|
89 |
|
90 |
+
add_filter( 'xmlrpc_pingback_error', __NAMESPACE__ . '\\xmlrpc_pingback_error', 5 );
|
91 |
}
|
92 |
|
|
|
93 |
/**
|
94 |
* @since 4.0.0 Refactored
|
95 |
* @since 2.2.0
|
feature/xmlrpc/pingback.php
CHANGED
@@ -12,22 +12,28 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
-
*
|
16 |
-
*
|
17 |
-
* @since 3.5.0 Refactored for unit testing
|
18 |
-
* @since 2.2.0
|
19 |
-
*
|
20 |
-
* @param string $call
|
21 |
*/
|
22 |
-
|
23 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
|
25 |
-
if ( 'pingback.ping' == $call ) {
|
26 |
-
openlog( 'WP_FAIL2BAN_PINGBACK_LOG' );
|
27 |
-
syslog( LOG_INFO, 'Pingback requested' );
|
28 |
-
// @codeCoverageIgnoreEnd
|
29 |
}
|
30 |
-
|
|
|
31 |
}
|
32 |
-
|
33 |
-
add_action( 'xmlrpc_call', __NAMESPACE__ . '\\xmlrpc_call' );
|
12 |
exit;
|
13 |
}
|
14 |
/**
|
15 |
+
* @since 4.0.5 Guard
|
|
|
|
|
|
|
|
|
|
|
16 |
*/
|
17 |
+
|
18 |
+
if ( !function_exists( __NAMESPACE__ . '\\xmlrpc_call' ) ) {
|
19 |
+
/**
|
20 |
+
* Log pingbacks
|
21 |
+
*
|
22 |
+
* @since 3.5.0 Refactored for unit testing
|
23 |
+
* @since 2.2.0
|
24 |
+
*
|
25 |
+
* @param string $call
|
26 |
+
*/
|
27 |
+
function xmlrpc_call( $call )
|
28 |
+
{
|
29 |
+
|
30 |
+
if ( 'pingback.ping' == $call ) {
|
31 |
+
openlog( 'WP_FAIL2BAN_PINGBACK_LOG' );
|
32 |
+
syslog( LOG_INFO, 'Pingback requested' );
|
33 |
+
// @codeCoverageIgnoreEnd
|
34 |
+
}
|
35 |
|
|
|
|
|
|
|
|
|
36 |
}
|
37 |
+
|
38 |
+
add_action( 'xmlrpc_call', __NAMESPACE__ . '\\xmlrpc_call' );
|
39 |
}
|
|
|
|
filters.d/wordpress-extra.conf
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# Fail2Ban filter for WordPress extra failures
|
2 |
-
# Auto-generated: 2019-
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
1 |
# Fail2Ban filter for WordPress extra failures
|
2 |
+
# Auto-generated: 2019-03-13T01:12:18+00:00
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
filters.d/wordpress-hard.conf
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# Fail2Ban filter for WordPress hard failures
|
2 |
-
# Auto-generated: 2019-
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
@@ -11,14 +11,13 @@ before = common.conf
|
|
11 |
_daemon = (?:wordpress|wp)
|
12 |
|
13 |
failregex = ^%(__prefix_line)sAuthentication attempt for unknown user .* from <HOST>$
|
|
|
14 |
^%(__prefix_line)sXML-RPC authentication attempt for unknown user .* from <HOST>$
|
15 |
^%(__prefix_line)sSpam comment \d+ from <HOST>$
|
16 |
^%(__prefix_line)sBlocked user enumeration attempt from <HOST>$
|
17 |
^%(__prefix_line)sBlocked authentication attempt for .* from <HOST>$
|
18 |
^%(__prefix_line)sXML-RPC multicall authentication failure from <HOST>$
|
19 |
^%(__prefix_line)sPingback error .* generated from <HOST>$
|
20 |
-
^%(__prefix_line)sXML-RPC multicall authentication failure from <HOST>$
|
21 |
-
^%(__prefix_line)sPingback error .* generated from <HOST>$
|
22 |
|
23 |
ignoreregex =
|
24 |
|
1 |
# Fail2Ban filter for WordPress hard failures
|
2 |
+
# Auto-generated: 2019-03-13T01:12:18+00:00
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
11 |
_daemon = (?:wordpress|wp)
|
12 |
|
13 |
failregex = ^%(__prefix_line)sAuthentication attempt for unknown user .* from <HOST>$
|
14 |
+
^%(__prefix_line)sREST authentication attempt for unknown user .* from <HOST>$
|
15 |
^%(__prefix_line)sXML-RPC authentication attempt for unknown user .* from <HOST>$
|
16 |
^%(__prefix_line)sSpam comment \d+ from <HOST>$
|
17 |
^%(__prefix_line)sBlocked user enumeration attempt from <HOST>$
|
18 |
^%(__prefix_line)sBlocked authentication attempt for .* from <HOST>$
|
19 |
^%(__prefix_line)sXML-RPC multicall authentication failure from <HOST>$
|
20 |
^%(__prefix_line)sPingback error .* generated from <HOST>$
|
|
|
|
|
21 |
|
22 |
ignoreregex =
|
23 |
|
filters.d/wordpress-soft.conf
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
# Fail2Ban filter for WordPress soft failures
|
2 |
-
# Auto-generated: 2019-
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
@@ -11,6 +11,7 @@ before = common.conf
|
|
11 |
_daemon = (?:wordpress|wp)
|
12 |
|
13 |
failregex = ^%(__prefix_line)sAuthentication failure for .* from <HOST>$
|
|
|
14 |
^%(__prefix_line)sXML-RPC authentication failure for .* from <HOST>$
|
15 |
|
16 |
ignoreregex =
|
1 |
# Fail2Ban filter for WordPress soft failures
|
2 |
+
# Auto-generated: 2019-03-13T01:12:18+00:00
|
3 |
#
|
4 |
|
5 |
[INCLUDES]
|
11 |
_daemon = (?:wordpress|wp)
|
12 |
|
13 |
failregex = ^%(__prefix_line)sAuthentication failure for .* from <HOST>$
|
14 |
+
^%(__prefix_line)sREST authentication failure for .* from <HOST>$
|
15 |
^%(__prefix_line)sXML-RPC authentication failure for .* from <HOST>$
|
16 |
|
17 |
ignoreregex =
|
readme.txt
CHANGED
@@ -5,8 +5,8 @@ Author URI: https://charles.lecklider.org/
|
|
5 |
Plugin URI: https://wp-fail2ban.com/
|
6 |
Tags: fail2ban, login, security, syslog
|
7 |
Requires at least: 3.4
|
8 |
-
Tested up to: 5.
|
9 |
-
Stable tag: 4.0
|
10 |
Requires PHP: 5.3
|
11 |
License: GPLv2 or later
|
12 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
@@ -27,48 +27,60 @@ Write a myriad of WordPress events to syslog for integration with fail2ban.
|
|
27 |
= Features =
|
28 |
|
29 |
* **CloudFlare and Proxy Servers**
|
30 |
-
*WPf2b* can be configured to work with CloudFlare and other proxy servers. For an overview see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
31 |
|
32 |
* **Comments**
|
33 |
-
*WPf2b* can log comments (see [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.
|
34 |
|
35 |
* **Pingbacks**
|
36 |
-
*WPf2b* logs failed pingbacks, and can log all pingbacks. For an overview see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.
|
37 |
|
38 |
* **Spam**
|
39 |
-
*WPf2b* can log comments marked as spam. See [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.
|
40 |
|
41 |
* **Block User Enumeration**
|
42 |
-
*WPf2b* can block user enumeration. See [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.
|
43 |
|
44 |
* **Work-Arounds for Broken syslogd**
|
45 |
-
*WPf2b* can be configured to work around most syslogd weirdness. For an overview see [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.
|
46 |
|
47 |
* **Blocking Users**
|
48 |
-
*WPf2b* can be configured to short-cut the login process when the username matches a regex. For an overview see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.
|
49 |
|
50 |
* **`mu-plugins` Support**
|
51 |
-
*WPf2b* can easily be configured as a must-use plugin - see [Configuration](https://docs.wp-fail2ban.com/en/4.
|
52 |
|
53 |
== Installation ==
|
54 |
|
55 |
1. Install via the Plugin Directory, or upload to your plugins directory.
|
56 |
1. Activate the plugin through the 'Plugins' menu in WordPress.
|
57 |
-
1. Edit `wp-config.php` to suit your needs - see [Configuration](https://docs.wp-fail2ban.com/en/4.
|
58 |
|
59 |
== Changelog ==
|
60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
= 4.0.2 =
|
62 |
* Fix PHP 5.3 compatibility.
|
63 |
-
* Bugfix for WP_FAIL2BAN_LOG_COMMENTS_EXTRA
|
64 |
-
* Bugfix for WP_FAIL2BAN_REMOTE_ADDR summary.
|
65 |
|
66 |
= 4.0.1 =
|
67 |
* Add extra features via Freemius. **This is entirely optional.** *WPf2b* works as before, including new features listed here.
|
68 |
* Add settings summary page (Settings -> WP fail2ban).
|
69 |
-
* Add [`WP_FAIL2BAN_PASSWORD_REQUEST_LOG`](https://docs.wp-fail2ban.com/en/4.
|
70 |
-
* Add [`WP_FAIL2BAN_SPAM_LOG`](https://docs.wp-fail2ban.com/en/4.
|
71 |
-
* Add [`WP_FAIL2BAN_LOG_COMMENTS_EXTRA`](https://docs.wp-fail2ban.com/en/4.
|
72 |
* not found,
|
73 |
* closed for commenting,
|
74 |
* in the trash,
|
@@ -80,70 +92,70 @@ Write a myriad of WordPress events to syslog for integration with fail2ban.
|
|
80 |
* Not released.
|
81 |
|
82 |
= 3.6.0 =
|
83 |
-
* The [filter files](https://docs.wp-fail2ban.com/en/4.
|
84 |
-
* Added [PHPUnit tests](https://docs.wp-fail2ban.com/en/4.
|
85 |
-
* Bugfix for [`wordpress-soft.conf`](https://docs.wp-fail2ban.com/en/4.
|
86 |
-
* Add [`WP_FAIL2BAN_XMLRPC_LOG`](https://docs.wp-fail2ban.com/en/4.
|
87 |
-
* Add [`WP_FAIL2BAN_REMOTE_ADDR`](https://docs.wp-fail2ban.com/en/4.
|
88 |
-
* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
89 |
* Moved all documentation to [https://docs.wp-fail2ban.com/](https://docs.wp-fail2ban.com/).
|
90 |
|
91 |
= 3.5.3 =
|
92 |
-
* Bugfix for [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.
|
93 |
|
94 |
= 3.5.1 =
|
95 |
-
* Bugfix for [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.
|
96 |
|
97 |
= 3.5.0 =
|
98 |
-
* Add [`WP_FAIL2BAN_OPENLOG_OPTIONS`](https://docs.wp-fail2ban.com/en/4.
|
99 |
-
* Add [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.
|
100 |
-
* Add [`WP_FAIL2BAN_LOG_PASSWORD_REQUEST`](https://docs.wp-fail2ban.com/en/4.
|
101 |
-
* Add [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.
|
102 |
-
* Add [`WP_FAIL2BAN_TRUNCATE_HOST`](https://docs.wp-fail2ban.com/en/4.
|
103 |
-
* [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.
|
104 |
|
105 |
= 3.0.3 =
|
106 |
-
* Fix regex in [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.
|
107 |
|
108 |
= 3.0.2 =
|
109 |
* Prevent double logging in WP 4.5.x for XML-RPC authentication failure
|
110 |
|
111 |
= 3.0.1 =
|
112 |
-
* Fix regex in [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.
|
113 |
|
114 |
= 3.0.0 =
|
115 |
-
* Add [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.
|
116 |
-
* Add [`WP_FAIL2BAN_HTTP_HOST`](https://docs.wp-fail2ban.com/en/4.
|
117 |
* Log XML-RPC authentication failure.
|
118 |
* Add better support for MU deployment.
|
119 |
|
120 |
= 2.3.2 =
|
121 |
-
* Bugfix [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.
|
122 |
|
123 |
= 2.3.0 =
|
124 |
-
* Bugfix in *experimental* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
125 |
|
126 |
= 2.2.1 =
|
127 |
-
* Fix stupid mistake with [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.
|
128 |
|
129 |
= 2.2.0 =
|
130 |
-
* Custom authentication log is now called [`WP_FAIL2BAN_AUTH_LOG`](https://docs.wp-fail2ban.com/en/4.
|
131 |
-
* Add logging for pingbacks; see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.
|
132 |
-
* Custom pingback log is called [`WP_FAIL2BAN_PINGBACK_LOG`](https://docs.wp-fail2ban.com/en/4.
|
133 |
|
134 |
= 2.1.1 =
|
135 |
* Minor bugfix.
|
136 |
|
137 |
= 2.1.0 =
|
138 |
-
* Add support for blocking user enumeration; see [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.
|
139 |
-
* Add support for CIDR notation in [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
140 |
|
141 |
= 2.0.1 =
|
142 |
-
* Bugfix in *experimental* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
143 |
|
144 |
= 2.0.0 =
|
145 |
-
* Add *experimental* support for X-Forwarded-For header; see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
146 |
-
* Add *experimental* support for regex-based login blocking; see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.
|
147 |
|
148 |
= 1.2.1 =
|
149 |
* Update FAQ.
|
@@ -159,6 +171,18 @@ Write a myriad of WordPress events to syslog for integration with fail2ban.
|
|
159 |
|
160 |
== Upgrade Notice ==
|
161 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
162 |
= 4.0.2 =
|
163 |
This is a bugfix. You do not need to update your filters from 4.0.1.
|
164 |
|
@@ -172,7 +196,7 @@ You will need up update your `fail2ban` filters.
|
|
172 |
You will need up update your `fail2ban` filters.
|
173 |
|
174 |
= 3.5.1 =
|
175 |
-
Bugfix: disable [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.
|
176 |
|
177 |
= 3.5.0 =
|
178 |
You will need up update your `fail2ban` filters.
|
@@ -184,13 +208,13 @@ You will need up update your `fail2ban` filters.
|
|
184 |
BREAKING CHANGE: The `fail2ban` filters have been split into two files. You will need up update your `fail2ban` configuration.
|
185 |
|
186 |
= 2.3.0 =
|
187 |
-
Fix for [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.
|
188 |
|
189 |
= 2.2.1 =
|
190 |
Bugfix.
|
191 |
|
192 |
= 2.2.0 =
|
193 |
-
BREAKING CHANGE: `WP_FAIL2BAN_LOG` has been renamed to [`WP_FAIL2BAN_AUTH_LOG`](https://docs.wp-fail2ban.com/en/4.
|
194 |
|
195 |
Pingbacks are getting a lot of attention recently, so *WPf2b* can now log them.
|
196 |
The `wordpress.conf` filter has been updated; you will need to update your `fail2ban` configuration.
|
@@ -202,4 +226,4 @@ The `wordpress.conf` filter has been updated; you will need to update your `fail
|
|
202 |
Bugfix in experimental code; still an experimental release.
|
203 |
|
204 |
= 2.0.0 =
|
205 |
-
This is an experimental release. If your current version is working and you're not interested in the new features, skip this version - wait for 2.1.0. For those that do want to test this release, note that `wordpress.conf` has changed - you'll need to copy it to `fail2ban/filters.d` again.
|
5 |
Plugin URI: https://wp-fail2ban.com/
|
6 |
Tags: fail2ban, login, security, syslog
|
7 |
Requires at least: 3.4
|
8 |
+
Tested up to: 5.1
|
9 |
+
Stable tag: 4.1.0
|
10 |
Requires PHP: 5.3
|
11 |
License: GPLv2 or later
|
12 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
27 |
= Features =
|
28 |
|
29 |
* **CloudFlare and Proxy Servers**
|
30 |
+
*WPf2b* can be configured to work with CloudFlare and other proxy servers. For an overview see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-proxies).
|
31 |
|
32 |
* **Comments**
|
33 |
+
*WPf2b* can log comments (see [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-comments)) and attempted comments (see [`WP_FAIL2BAN_LOG_COMMENTS_EXTRA`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-comments-extra)).
|
34 |
|
35 |
* **Pingbacks**
|
36 |
+
*WPf2b* logs failed pingbacks, and can log all pingbacks. For an overview see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-pingbacks).
|
37 |
|
38 |
* **Spam**
|
39 |
+
*WPf2b* can log comments marked as spam. See [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-log-spam).
|
40 |
|
41 |
* **Block User Enumeration**
|
42 |
+
*WPf2b* can block user enumeration. See [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-block-user-enumeration).
|
43 |
|
44 |
* **Work-Arounds for Broken syslogd**
|
45 |
+
*WPf2b* can be configured to work around most syslogd weirdness. For an overview see [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-syslog-short-tag) and [`WP_FAIL2BAN_HTTP_HOST`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-http-host).
|
46 |
|
47 |
* **Blocking Users**
|
48 |
+
*WPf2b* can be configured to short-cut the login process when the username matches a regex. For an overview see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines.html#wp-fail2ban-blocked-users).
|
49 |
|
50 |
* **`mu-plugins` Support**
|
51 |
+
*WPf2b* can easily be configured as a must-use plugin - see [Configuration](https://docs.wp-fail2ban.com/en/4.1/configuration.html#mu-plugins-support).
|
52 |
|
53 |
== Installation ==
|
54 |
|
55 |
1. Install via the Plugin Directory, or upload to your plugins directory.
|
56 |
1. Activate the plugin through the 'Plugins' menu in WordPress.
|
57 |
+
1. Edit `wp-config.php` to suit your needs - see [Configuration](https://docs.wp-fail2ban.com/en/4.1/configuration.html).
|
58 |
|
59 |
== Changelog ==
|
60 |
|
61 |
+
= 4.1.0 =
|
62 |
+
* Add separate logging for REST authentication.
|
63 |
+
* Fix conflict with earlier versions pre-installed in `mu-plugins`. See [Is *WPf2b* Already Installed?](https://docs.wp-fail2ban.com/en/4.1/installation.html#is-wp-fail2ban-already-installed).
|
64 |
+
|
65 |
+
= 4.0.5 =
|
66 |
+
* Add [`WP_FAIL2BAN_COMMENT_EXTRA_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_COMMENT_EXTRA_LOG.html).
|
67 |
+
* Add [`WP_FAIL2BAN_PINGBACK_ERROR_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PINGBACK_ERROR_LOG.html) (future functionality).
|
68 |
+
* Change `WP_FAIL2BAN_LOG_SPAM` to use `LOG_NOTICE`.
|
69 |
+
* Change `WP_FAIL2BAN_SPAM_LOG` to `LOG_AUTH`.
|
70 |
+
* Change `WP_FAIL2BAN_LOG_COMMENTS_EXTRA` events to use `LOG_NOTICE` by default.
|
71 |
+
* Fix conflict with 3.x in `mu-plugins`.
|
72 |
+
|
73 |
= 4.0.2 =
|
74 |
* Fix PHP 5.3 compatibility.
|
75 |
+
* Bugfix for `WP_FAIL2BAN_LOG_COMMENTS_EXTRA`.
|
76 |
+
* Bugfix for `WP_FAIL2BAN_REMOTE_ADDR` summary.
|
77 |
|
78 |
= 4.0.1 =
|
79 |
* Add extra features via Freemius. **This is entirely optional.** *WPf2b* works as before, including new features listed here.
|
80 |
* Add settings summary page (Settings -> WP fail2ban).
|
81 |
+
* Add [`WP_FAIL2BAN_PASSWORD_REQUEST_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PASSWORD_REQUEST_LOG.html).
|
82 |
+
* Add [`WP_FAIL2BAN_SPAM_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_SPAM_LOG.html).
|
83 |
+
* Add [`WP_FAIL2BAN_LOG_COMMENTS_EXTRA`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_LOG_COMMENTS_EXTRA.html) - enable logging for attempted comments on posts which are:
|
84 |
* not found,
|
85 |
* closed for commenting,
|
86 |
* in the trash,
|
92 |
* Not released.
|
93 |
|
94 |
= 3.6.0 =
|
95 |
+
* The [filter files](https://docs.wp-fail2ban.com/en/4.1/filters.html) are now generated from PHPDoc in the code. There were too many times when the filters were out of sync with the code (programmer error) - this should resolve that by bringing the patterns closer to the code that emits them.
|
96 |
+
* Added [PHPUnit tests](https://docs.wp-fail2ban.com/en/4.1/tests.html). Almost 100% code coverage, with the exception of [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html) which is quite hard to test properly.
|
97 |
+
* Bugfix for [`wordpress-soft.conf`](https://docs.wp-fail2ban.com/en/4.1/filters.html#wordpress-soft-conf).
|
98 |
+
* Add [`WP_FAIL2BAN_XMLRPC_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_XMLRPC_LOG.html).
|
99 |
+
* Add [`WP_FAIL2BAN_REMOTE_ADDR`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_REMOTE_ADDR.html).
|
100 |
+
* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html) now supports an array of IPs with PHP 7.
|
101 |
* Moved all documentation to [https://docs.wp-fail2ban.com/](https://docs.wp-fail2ban.com/).
|
102 |
|
103 |
= 3.5.3 =
|
104 |
+
* Bugfix for [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.1/filters.html#wordpress-hard-conf).
|
105 |
|
106 |
= 3.5.1 =
|
107 |
+
* Bugfix for [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCK_USER_ENUMERATION.html).
|
108 |
|
109 |
= 3.5.0 =
|
110 |
+
* Add [`WP_FAIL2BAN_OPENLOG_OPTIONS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_OPENLOG_OPTIONS.html).
|
111 |
+
* Add [`WP_FAIL2BAN_LOG_COMMENTS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_LOG_COMMENTS.html) and [`WP_FAIL2BAN_COMMENT_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_COMMENT_LOG.html).
|
112 |
+
* Add [`WP_FAIL2BAN_LOG_PASSWORD_REQUEST`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_LOG_PASSWORD_REQUEST.html).
|
113 |
+
* Add [`WP_FAIL2BAN_LOG_SPAM`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_LOG_SPAM.html).
|
114 |
+
* Add [`WP_FAIL2BAN_TRUNCATE_HOST`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_TRUNCATE_HOST.html).
|
115 |
+
* [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCKED_USERS.html) now supports an array of users with PHP 7.
|
116 |
|
117 |
= 3.0.3 =
|
118 |
+
* Fix regex in [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.1/filters.html#wordpress-hard-conf).
|
119 |
|
120 |
= 3.0.2 =
|
121 |
* Prevent double logging in WP 4.5.x for XML-RPC authentication failure
|
122 |
|
123 |
= 3.0.1 =
|
124 |
+
* Fix regex in [`wordpress-hard.conf`](https://docs.wp-fail2ban.com/en/4.1/filters.html#wordpress-hard-conf).
|
125 |
|
126 |
= 3.0.0 =
|
127 |
+
* Add [`WP_FAIL2BAN_SYSLOG_SHORT_TAG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_SYSLOG_SHORT_TAG.html).
|
128 |
+
* Add [`WP_FAIL2BAN_HTTP_HOST`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_HTTP_HOST.html).
|
129 |
* Log XML-RPC authentication failure.
|
130 |
* Add better support for MU deployment.
|
131 |
|
132 |
= 2.3.2 =
|
133 |
+
* Bugfix [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCKED_USERS.html).
|
134 |
|
135 |
= 2.3.0 =
|
136 |
+
* Bugfix in *experimental* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html) code (thanks to KyleCartmell).
|
137 |
|
138 |
= 2.2.1 =
|
139 |
+
* Fix stupid mistake with [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCKED_USERS.html).
|
140 |
|
141 |
= 2.2.0 =
|
142 |
+
* Custom authentication log is now called [`WP_FAIL2BAN_AUTH_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_AUTH_LOG.html).
|
143 |
+
* Add logging for pingbacks; see [`WP_FAIL2BAN_LOG_PINGBACKS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_LOG_PINGBACKS.html).
|
144 |
+
* Custom pingback log is called [`WP_FAIL2BAN_PINGBACK_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PINGBACK_LOG.html).
|
145 |
|
146 |
= 2.1.1 =
|
147 |
* Minor bugfix.
|
148 |
|
149 |
= 2.1.0 =
|
150 |
+
* Add support for blocking user enumeration; see [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCK_USER_ENUMERATION.html).
|
151 |
+
* Add support for CIDR notation in [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html).
|
152 |
|
153 |
= 2.0.1 =
|
154 |
+
* Bugfix in *experimental* [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html) code.
|
155 |
|
156 |
= 2.0.0 =
|
157 |
+
* Add *experimental* support for X-Forwarded-For header; see [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html).
|
158 |
+
* Add *experimental* support for regex-based login blocking; see [`WP_FAIL2BAN_BLOCKED_USERS`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCKED_USERS.html).
|
159 |
|
160 |
= 1.2.1 =
|
161 |
* Update FAQ.
|
171 |
|
172 |
== Upgrade Notice ==
|
173 |
|
174 |
+
= 4.1.0 =
|
175 |
+
To take advantage of the new features you will need up update your `fail2ban` filters; existing filters will continue to work as before.
|
176 |
+
|
177 |
+
= 4.0.5 =
|
178 |
+
This is a security fix: all 4.x users are strongly advised to upgrade immediately. You do not need to update your filters from 4.0.1.
|
179 |
+
|
180 |
+
= 4.0.4 =
|
181 |
+
This is a bugfix. You do not need to update your filters from 4.0.1.
|
182 |
+
|
183 |
+
= 4.0.3 =
|
184 |
+
This is a bugfix. You do not need to update your filters from 4.0.1.
|
185 |
+
|
186 |
= 4.0.2 =
|
187 |
This is a bugfix. You do not need to update your filters from 4.0.1.
|
188 |
|
196 |
You will need up update your `fail2ban` filters.
|
197 |
|
198 |
= 3.5.1 =
|
199 |
+
Bugfix: disable [`WP_FAIL2BAN_BLOCK_USER_ENUMERATION`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_BLOCK_USER_ENUMERATION.html) in admin area....
|
200 |
|
201 |
= 3.5.0 =
|
202 |
You will need up update your `fail2ban` filters.
|
208 |
BREAKING CHANGE: The `fail2ban` filters have been split into two files. You will need up update your `fail2ban` configuration.
|
209 |
|
210 |
= 2.3.0 =
|
211 |
+
Fix for [`WP_FAIL2BAN_PROXIES`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_PROXIES.html); if you're not using it you can safely skip this release.
|
212 |
|
213 |
= 2.2.1 =
|
214 |
Bugfix.
|
215 |
|
216 |
= 2.2.0 =
|
217 |
+
BREAKING CHANGE: `WP_FAIL2BAN_LOG` has been renamed to [`WP_FAIL2BAN_AUTH_LOG`](https://docs.wp-fail2ban.com/en/4.1/defines/WP_FAIL2BAN_AUTH_LOG.html).
|
218 |
|
219 |
Pingbacks are getting a lot of attention recently, so *WPf2b* can now log them.
|
220 |
The `wordpress.conf` filter has been updated; you will need to update your `fail2ban` configuration.
|
226 |
Bugfix in experimental code; still an experimental release.
|
227 |
|
228 |
= 2.0.0 =
|
229 |
+
This is an experimental release. If your current version is working and you're not interested in the new features, skip this version - wait for 2.1.0. For those that do want to test this release, note that `wordpress.conf` has changed - you'll need to copy it to `fail2ban/filters.d` again.
|
vendor/freemius/wordpress-sdk/includes/class-freemius.php
CHANGED
@@ -2977,6 +2977,10 @@
|
|
2977 |
* @since 1.1.7.3
|
2978 |
*/
|
2979 |
static function _toggle_debug_mode() {
|
|
|
|
|
|
|
|
|
2980 |
$is_on = fs_request_get( 'is_on', false, 'post' );
|
2981 |
|
2982 |
if ( fs_request_is_post() && in_array( $is_on, array( 0, 1 ) ) ) {
|
@@ -3008,8 +3012,16 @@
|
|
3008 |
* @since 1.2.1.7
|
3009 |
*/
|
3010 |
static function _get_db_option() {
|
|
|
|
|
3011 |
$option_name = fs_request_get( 'option_name' );
|
3012 |
|
|
|
|
|
|
|
|
|
|
|
|
|
3013 |
$value = get_option( $option_name );
|
3014 |
|
3015 |
$result = array(
|
@@ -3032,7 +3044,16 @@
|
|
3032 |
* @since 1.2.1.7
|
3033 |
*/
|
3034 |
static function _set_db_option() {
|
3035 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3036 |
$option_value = fs_request_get( 'option_value' );
|
3037 |
|
3038 |
if ( ! empty( $option_value ) ) {
|
2977 |
* @since 1.1.7.3
|
2978 |
*/
|
2979 |
static function _toggle_debug_mode() {
|
2980 |
+
if ( ! is_super_admin() ) {
|
2981 |
+
return;
|
2982 |
+
}
|
2983 |
+
|
2984 |
$is_on = fs_request_get( 'is_on', false, 'post' );
|
2985 |
|
2986 |
if ( fs_request_is_post() && in_array( $is_on, array( 0, 1 ) ) ) {
|
3012 |
* @since 1.2.1.7
|
3013 |
*/
|
3014 |
static function _get_db_option() {
|
3015 |
+
check_admin_referer( 'fs_get_db_option' );
|
3016 |
+
|
3017 |
$option_name = fs_request_get( 'option_name' );
|
3018 |
|
3019 |
+
if ( ! is_super_admin() ||
|
3020 |
+
! fs_starts_with( $option_name, 'fs_' )
|
3021 |
+
) {
|
3022 |
+
self::shoot_ajax_failure();
|
3023 |
+
}
|
3024 |
+
|
3025 |
$value = get_option( $option_name );
|
3026 |
|
3027 |
$result = array(
|
3044 |
* @since 1.2.1.7
|
3045 |
*/
|
3046 |
static function _set_db_option() {
|
3047 |
+
check_admin_referer( 'fs_set_db_option' );
|
3048 |
+
|
3049 |
+
$option_name = fs_request_get( 'option_name' );
|
3050 |
+
|
3051 |
+
if ( ! is_super_admin() ||
|
3052 |
+
! fs_starts_with( $option_name, 'fs_' )
|
3053 |
+
) {
|
3054 |
+
self::shoot_ajax_failure();
|
3055 |
+
}
|
3056 |
+
|
3057 |
$option_value = fs_request_get( 'option_value' );
|
3058 |
|
3059 |
if ( ! empty( $option_value ) ) {
|
vendor/freemius/wordpress-sdk/start.php
CHANGED
@@ -15,7 +15,7 @@
|
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
-
$this_sdk_version = '2.2.
|
19 |
|
20 |
#region SDK Selection Logic --------------------------------------------------------------------
|
21 |
|
15 |
*
|
16 |
* @var string
|
17 |
*/
|
18 |
+
$this_sdk_version = '2.2.4';
|
19 |
|
20 |
#region SDK Selection Logic --------------------------------------------------------------------
|
21 |
|
vendor/freemius/wordpress-sdk/templates/debug.php
CHANGED
@@ -113,6 +113,7 @@
|
|
113 |
if (optionName) {
|
114 |
$.post(ajaxurl, {
|
115 |
action : 'fs_get_db_option',
|
|
|
116 |
option_name: optionName
|
117 |
}, function (response) {
|
118 |
if (response.data.value)
|
@@ -132,6 +133,7 @@
|
|
132 |
if (optionValue) {
|
133 |
$.post(ajaxurl, {
|
134 |
action : 'fs_set_db_option',
|
|
|
135 |
option_name : optionName,
|
136 |
option_value: optionValue
|
137 |
}, function () {
|
113 |
if (optionName) {
|
114 |
$.post(ajaxurl, {
|
115 |
action : 'fs_get_db_option',
|
116 |
+
_wpnonce : '<?php echo wp_create_nonce( 'fs_get_db_option' ) ?>',
|
117 |
option_name: optionName
|
118 |
}, function (response) {
|
119 |
if (response.data.value)
|
133 |
if (optionValue) {
|
134 |
$.post(ajaxurl, {
|
135 |
action : 'fs_set_db_option',
|
136 |
+
_wpnonce : '<?php echo wp_create_nonce( 'fs_set_db_option' ) ?>',
|
137 |
option_name : optionName,
|
138 |
option_value: optionValue
|
139 |
}, function () {
|
vendor/freemius/wordpress-sdk/templates/forms/deactivation/form.php
CHANGED
@@ -1,497 +1,497 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* @package Freemius
|
4 |
-
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
-
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
-
* @since 1.1.2
|
7 |
-
*/
|
8 |
-
|
9 |
-
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
-
exit;
|
11 |
-
}
|
12 |
-
|
13 |
-
/**
|
14 |
-
* @var array $VARS
|
15 |
-
*/
|
16 |
-
$fs = freemius( $VARS['id'] );
|
17 |
-
$slug = $fs->get_slug();
|
18 |
-
|
19 |
-
$confirmation_message = $fs->apply_filters( 'uninstall_confirmation_message', '' );
|
20 |
-
|
21 |
-
$reasons = $VARS['reasons'];
|
22 |
-
|
23 |
-
$reasons_list_items_html = '';
|
24 |
-
|
25 |
-
foreach ( $reasons as $reason ) {
|
26 |
-
$list_item_classes = 'reason' . ( ! empty( $reason['input_type'] ) ? ' has-input' : '' );
|
27 |
-
|
28 |
-
if ( isset( $reason['internal_message'] ) && ! empty( $reason['internal_message'] ) ) {
|
29 |
-
$list_item_classes .= ' has-internal-message';
|
30 |
-
$reason_internal_message = $reason['internal_message'];
|
31 |
-
} else {
|
32 |
-
$reason_internal_message = '';
|
33 |
-
}
|
34 |
-
|
35 |
-
$reason_input_type = ( ! empty( $reason['input_type'] ) ? $reason['input_type'] : '' );
|
36 |
-
$reason_input_placeholder = ( ! empty( $reason['input_placeholder'] ) ? $reason['input_placeholder'] : '' );
|
37 |
-
|
38 |
-
$reason_list_item_html = <<< HTML
|
39 |
-
<li class="{$list_item_classes}"
|
40 |
-
data-input-type="{$reason_input_type}"
|
41 |
-
data-input-placeholder="{$reason_input_placeholder}">
|
42 |
-
<label>
|
43 |
-
<span>
|
44 |
-
<input type="radio" name="selected-reason" value="{$reason['id']}"/>
|
45 |
-
</span>
|
46 |
-
<span>{$reason['text']}</span>
|
47 |
-
</label>
|
48 |
-
<div class="internal-message">{$reason_internal_message}</div>
|
49 |
-
</li>
|
50 |
-
HTML;
|
51 |
-
|
52 |
-
$reasons_list_items_html .= $reason_list_item_html;
|
53 |
-
}
|
54 |
-
|
55 |
-
$is_anonymous = ( ! $fs->is_registered() );
|
56 |
-
if ( $is_anonymous ) {
|
57 |
-
$anonymous_feedback_checkbox_html = sprintf(
|
58 |
-
'<label class="anonymous-feedback-label"><input type="checkbox" class="anonymous-feedback-checkbox"> %s</label>',
|
59 |
-
fs_esc_html_inline( 'Anonymous feedback', 'anonymous-feedback', $slug )
|
60 |
-
);
|
61 |
-
} else {
|
62 |
-
$anonymous_feedback_checkbox_html = '';
|
63 |
-
}
|
64 |
-
|
65 |
-
// Aliases.
|
66 |
-
$deactivate_text = fs_text_inline( 'Deactivate', 'deactivate', $slug );
|
67 |
-
$theme_text = fs_text_inline( 'Theme', 'theme', $slug );
|
68 |
-
$activate_x_text = fs_text_inline( 'Activate %s', 'activate-x', $slug );
|
69 |
-
|
70 |
-
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
71 |
-
?>
|
72 |
-
<?php $fs->_maybe_add_subscription_cancellation_dialog_box() ?>
|
73 |
-
<script type="text/javascript">
|
74 |
-
(function ($) {
|
75 |
-
var reasonsHtml = <?php echo json_encode( $reasons_list_items_html ) ?>,
|
76 |
-
modalHtml =
|
77 |
-
'<div class="fs-modal fs-modal-deactivation-feedback<?php echo empty( $confirmation_message ) ? ' no-confirmation-message' : ''; ?>">'
|
78 |
-
+ ' <div class="fs-modal-dialog">'
|
79 |
-
+ ' <div class="fs-modal-header">'
|
80 |
-
+ ' <h4><?php fs_esc_attr_echo_inline( 'Quick Feedback', 'quick-feedback' , $slug ) ?></h4>'
|
81 |
-
+ ' </div>'
|
82 |
-
+ ' <div class="fs-modal-body">'
|
83 |
-
+ ' <div class="fs-modal-panel" data-panel-id="confirm"><p><?php echo $confirmation_message; ?></p></div>'
|
84 |
-
+ ' <div class="fs-modal-panel active" data-panel-id="reasons"><h3><strong><?php echo esc_js( sprintf( fs_text_inline( 'If you have a moment, please let us know why you are %s', 'deactivation-share-reason' , $slug ), ( $fs->is_plugin() ? fs_text_inline( 'deactivating', 'deactivating', $slug ) : fs_text_inline( 'switching', 'switching', $slug ) ) ) ) ?>:</strong></h3><ul id="reasons-list">' + reasonsHtml + '</ul></div>'
|
85 |
-
+ ' </div>'
|
86 |
-
+ ' <div class="fs-modal-footer">'
|
87 |
-
+ ' <?php echo $anonymous_feedback_checkbox_html ?>'
|
88 |
-
+ ' <a href="#" class="button button-secondary button-deactivate"></a>'
|
89 |
-
+ ' <a href="#" class="button button-primary button-close"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></a>'
|
90 |
-
+ ' </div>'
|
91 |
-
+ ' </div>'
|
92 |
-
+ '</div>',
|
93 |
-
$modal = $(modalHtml),
|
94 |
-
$deactivateLink = $('#the-list .deactivate > [data-module-id=<?php echo $fs->get_id() ?>].fs-module-id').prev(),
|
95 |
-
selectedReasonID = false,
|
96 |
-
redirectLink = '',
|
97 |
-
$anonymousFeedback = $modal.find( '.anonymous-feedback-label' ),
|
98 |
-
isAnonymous = <?php echo ( $is_anonymous ? 'true' : 'false' ); ?>,
|
99 |
-
otherReasonID = <?php echo Freemius::REASON_OTHER; ?>,
|
100 |
-
dontShareDataReasonID = <?php echo Freemius::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION; ?>,
|
101 |
-
deleteThemeUpdateData = <?php echo $fs->is_theme() && $fs->is_premium() && ! $fs->has_any_active_valid_license() ? 'true' : 'false' ?>,
|
102 |
-
$subscriptionCancellationModal = $( '.fs-modal-subscription-cancellation-<?php echo $fs->get_id() ?>' );
|
103 |
-
|
104 |
-
$modal.appendTo($('body'));
|
105 |
-
|
106 |
-
if ( 0 !== $subscriptionCancellationModal.length ) {
|
107 |
-
$subscriptionCancellationModal.on( '<?php echo $fs->get_action_tag( 'subscription_cancellation_action' ) ?>', function( evt, cancelSubscription ) {
|
108 |
-
if ( false === cancelSubscription ) {
|
109 |
-
showModal();
|
110 |
-
|
111 |
-
$subscriptionCancellationModal.trigger( 'closeModal' );
|
112 |
-
} else {
|
113 |
-
var $errorMessage = $subscriptionCancellationModal.find( '.notice-error' );
|
114 |
-
|
115 |
-
<?php
|
116 |
-
$subscription_cancellation_context = $fs->is_paid_trial() ?
|
117 |
-
fs_text_inline( 'trial', 'trial', $slug ) :
|
118 |
-
fs_text_inline( 'subscription', 'subscription', $slug );
|
119 |
-
?>
|
120 |
-
|
121 |
-
$.ajax({
|
122 |
-
url : ajaxurl,
|
123 |
-
method : 'POST',
|
124 |
-
data : {
|
125 |
-
action : '<?php echo $fs->get_ajax_action( 'cancel_subscription_or_trial' ) ?>',
|
126 |
-
security : '<?php echo $fs->get_ajax_security( 'cancel_subscription_or_trial' ) ?>',
|
127 |
-
module_id: '<?php echo $fs->get_id() ?>'
|
128 |
-
},
|
129 |
-
beforeSend: function() {
|
130 |
-
$errorMessage.hide();
|
131 |
-
|
132 |
-
$subscriptionCancellationModal.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
|
133 |
-
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).text( '<?php echo esc_js(
|
134 |
-
sprintf( fs_text_inline( 'Cancelling %s...', 'cancelling-x' , $slug ), $subscription_cancellation_context )
|
135 |
-
) ?>' );
|
136 |
-
},
|
137 |
-
success: function( result ) {
|
138 |
-
if ( result.success ) {
|
139 |
-
$subscriptionCancellationModal.removeClass( 'has-subscription-actions' );
|
140 |
-
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).removeClass( 'warn' );
|
141 |
-
|
142 |
-
$subscriptionCancellationModal.remove();
|
143 |
-
showModal();
|
144 |
-
} else {
|
145 |
-
$errorMessage.find( '> p' ).html( result.error );
|
146 |
-
$errorMessage.show();
|
147 |
-
|
148 |
-
$subscriptionCancellationModal.find( '.fs-modal-footer .button' ).removeClass( 'disabled' );
|
149 |
-
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).html( <?php echo json_encode( sprintf(
|
150 |
-
fs_text_inline( 'Cancel %s & Proceed', 'cancel-x-and-proceed', $slug ),
|
151 |
-
ucfirst( $subscription_cancellation_context )
|
152 |
-
) ) ?> );
|
153 |
-
}
|
154 |
-
}
|
155 |
-
});
|
156 |
-
}
|
157 |
-
});
|
158 |
-
}
|
159 |
-
|
160 |
-
registerEventHandlers();
|
161 |
-
|
162 |
-
function registerEventHandlers() {
|
163 |
-
$deactivateLink.click(function (evt) {
|
164 |
-
evt.preventDefault();
|
165 |
-
|
166 |
-
redirectLink = $(this).attr('href');
|
167 |
-
|
168 |
-
if ( 0 == $subscriptionCancellationModal.length ) {
|
169 |
-
showModal();
|
170 |
-
} else {
|
171 |
-
$subscriptionCancellationModal.trigger( 'showModal' );
|
172 |
-
}
|
173 |
-
});
|
174 |
-
|
175 |
-
<?php
|
176 |
-
if ( ! $fs->is_plugin() ) {
|
177 |
-
/**
|
178 |
-
* For "theme" module type, the modal is shown when the current user clicks on
|
179 |
-
* the "Activate" button of any other theme. The "Activate" button is actually
|
180 |
-
* a link to the "Themes" page (/wp-admin/themes.php) containing query params
|
181 |
-
* that tell WordPress to deactivate the current theme and activate a different theme.
|
182 |
-
*
|
183 |
-
* @author Leo Fajardo (@leorw)
|
184 |
-
* @since 1.2.2
|
185 |
-
*
|
186 |
-
* @since 1.2.2.7 Don't trigger the deactivation feedback form if activating the premium version of the theme.
|
187 |
-
*/
|
188 |
-
?>
|
189 |
-
$('body').on('click', '.theme-browser .theme:not([data-slug=<?php echo $fs->get_premium_slug() ?>]) .theme-actions .button.activate', function (evt) {
|
190 |
-
evt.preventDefault();
|
191 |
-
|
192 |
-
redirectLink = $(this).attr('href');
|
193 |
-
|
194 |
-
showModal();
|
195 |
-
});
|
196 |
-
<?php
|
197 |
-
} ?>
|
198 |
-
|
199 |
-
$modal.on('input propertychange', '.reason-input input', function () {
|
200 |
-
if (!isOtherReasonSelected()) {
|
201 |
-
return;
|
202 |
-
}
|
203 |
-
|
204 |
-
var reason = $(this).val().trim();
|
205 |
-
|
206 |
-
/**
|
207 |
-
* If reason is not empty, remove the error-message class of the message container
|
208 |
-
* to change the message color back to default.
|
209 |
-
*/
|
210 |
-
if (reason.length > 0) {
|
211 |
-
$('.message').removeClass('error-message');
|
212 |
-
enableDeactivateButton();
|
213 |
-
}
|
214 |
-
});
|
215 |
-
|
216 |
-
$modal.on('blur', '.reason-input input', function () {
|
217 |
-
var $userReason = $(this);
|
218 |
-
|
219 |
-
setTimeout(function () {
|
220 |
-
if (!isOtherReasonSelected()) {
|
221 |
-
return;
|
222 |
-
}
|
223 |
-
|
224 |
-
/**
|
225 |
-
* If reason is empty, add the error-message class to the message container
|
226 |
-
* to change the message color to red.
|
227 |
-
*/
|
228 |
-
if (0 === $userReason.val().trim().length) {
|
229 |
-
$('.message').addClass('error-message');
|
230 |
-
disableDeactivateButton();
|
231 |
-
}
|
232 |
-
}, 150);
|
233 |
-
});
|
234 |
-
|
235 |
-
$modal.on('click', '.fs-modal-footer .button', function (evt) {
|
236 |
-
evt.preventDefault();
|
237 |
-
|
238 |
-
if ($(this).hasClass('disabled')) {
|
239 |
-
return;
|
240 |
-
}
|
241 |
-
|
242 |
-
var _parent = $(this).parents('.fs-modal:first');
|
243 |
-
var _this = $(this);
|
244 |
-
|
245 |
-
if (_this.hasClass('allow-deactivate')) {
|
246 |
-
var $radio = $('input[type="radio"]:checked');
|
247 |
-
|
248 |
-
if (0 === $radio.length) {
|
249 |
-
if ( ! deleteThemeUpdateData ) {
|
250 |
-
// If no selected reason, just deactivate the plugin.
|
251 |
-
window.location.href = redirectLink;
|
252 |
-
} else {
|
253 |
-
$.ajax({
|
254 |
-
url : ajaxurl,
|
255 |
-
method : 'POST',
|
256 |
-
data : {
|
257 |
-
action : '<?php echo $fs->get_ajax_action( 'delete_theme_update_data' ) ?>',
|
258 |
-
security : '<?php echo $fs->get_ajax_security( 'delete_theme_update_data' ) ?>',
|
259 |
-
module_id: '<?php echo $fs->get_id() ?>'
|
260 |
-
},
|
261 |
-
beforeSend: function() {
|
262 |
-
_parent.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
|
263 |
-
_parent.find( '.fs-modal-footer .button-secondary' ).text( 'Processing...' );
|
264 |
-
},
|
265 |
-
complete : function() {
|
266 |
-
window.location.href = redirectLink;
|
267 |
-
}
|
268 |
-
});
|
269 |
-
}
|
270 |
-
|
271 |
-
return;
|
272 |
-
}
|
273 |
-
|
274 |
-
var $selected_reason = $radio.parents('li:first'),
|
275 |
-
$input = $selected_reason.find('textarea, input[type="text"]'),
|
276 |
-
userReason = ( 0 !== $input.length ) ? $input.val().trim() : '';
|
277 |
-
|
278 |
-
if (isOtherReasonSelected() && ( '' === userReason )) {
|
279 |
-
return;
|
280 |
-
}
|
281 |
-
|
282 |
-
$.ajax({
|
283 |
-
url : ajaxurl,
|
284 |
-
method : 'POST',
|
285 |
-
data : {
|
286 |
-
action : '<?php echo $fs->get_ajax_action( 'submit_uninstall_reason' ) ?>',
|
287 |
-
security : '<?php echo $fs->get_ajax_security( 'submit_uninstall_reason' ) ?>',
|
288 |
-
module_id : '<?php echo $fs->get_id() ?>',
|
289 |
-
reason_id : $radio.val(),
|
290 |
-
reason_info : userReason,
|
291 |
-
is_anonymous: isAnonymousFeedback()
|
292 |
-
},
|
293 |
-
beforeSend: function () {
|
294 |
-
_parent.find('.fs-modal-footer .button').addClass('disabled');
|
295 |
-
_parent.find('.fs-modal-footer .button-secondary').text('Processing...');
|
296 |
-
},
|
297 |
-
complete : function () {
|
298 |
-
// Do not show the dialog box, deactivate the plugin.
|
299 |
-
window.location.href = redirectLink;
|
300 |
-
}
|
301 |
-
});
|
302 |
-
} else if (_this.hasClass('button-deactivate')) {
|
303 |
-
// Change the Deactivate button's text and show the reasons panel.
|
304 |
-
_parent.find('.button-deactivate').addClass('allow-deactivate');
|
305 |
-
|
306 |
-
showPanel('reasons');
|
307 |
-
}
|
308 |
-
});
|
309 |
-
|
310 |
-
$modal.on('click', 'input[type="radio"]', function () {
|
311 |
-
var $selectedReasonOption = $( this );
|
312 |
-
|
313 |
-
// If the selection has not changed, do not proceed.
|
314 |
-
if (selectedReasonID === $selectedReasonOption.val())
|
315 |
-
return;
|
316 |
-
|
317 |
-
selectedReasonID = $selectedReasonOption.val();
|
318 |
-
|
319 |
-
if ( isAnonymous ) {
|
320 |
-
if ( isReasonSelected( dontShareDataReasonID ) ) {
|
321 |
-
$anonymousFeedback.hide();
|
322 |
-
} else {
|
323 |
-
$anonymousFeedback.show();
|
324 |
-
}
|
325 |
-
}
|
326 |
-
|
327 |
-
var _parent = $(this).parents('li:first');
|
328 |
-
|
329 |
-
$modal.find('.reason-input').remove();
|
330 |
-
$modal.find( '.internal-message' ).hide();
|
331 |
-
$modal.find('.button-deactivate').html('<?php echo esc_js( sprintf(
|
332 |
-
fs_text_inline( 'Submit & %s', 'deactivation-modal-button-submit' , $slug ),
|
333 |
-
$fs->is_plugin() ?
|
334 |
-
$deactivate_text :
|
335 |
-
sprintf( $activate_x_text, $theme_text )
|
336 |
-
) ) ?>');
|
337 |
-
|
338 |
-
enableDeactivateButton();
|
339 |
-
|
340 |
-
if ( _parent.hasClass( 'has-internal-message' ) ) {
|
341 |
-
_parent.find( '.internal-message' ).show();
|
342 |
-
}
|
343 |
-
|
344 |
-
if (_parent.hasClass('has-input')) {
|
345 |
-
var inputType = _parent.data('input-type'),
|
346 |
-
inputPlaceholder = _parent.data('input-placeholder'),
|
347 |
-
reasonInputHtml = '<div class="reason-input"><span class="message"></span>' + ( ( 'textfield' === inputType ) ? '<input type="text" />' : '<textarea rows="5"></textarea>' ) + '</div>';
|
348 |
-
|
349 |
-
_parent.append($(reasonInputHtml));
|
350 |
-
_parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
|
351 |
-
|
352 |
-
if (isOtherReasonSelected()) {
|
353 |
-
showMessage('<?php echo esc_js( fs_text_inline( 'Kindly tell us the reason so we can improve.', 'ask-for-reason-message' , $slug ) ); ?>');
|
354 |
-
disableDeactivateButton();
|
355 |
-
}
|
356 |
-
}
|
357 |
-
});
|
358 |
-
|
359 |
-
// If the user has clicked outside the window, cancel it.
|
360 |
-
$modal.on('click', function (evt) {
|
361 |
-
var $target = $(evt.target);
|
362 |
-
|
363 |
-
// If the user has clicked anywhere in the modal dialog, just return.
|
364 |
-
if ($target.hasClass('fs-modal-body') || $target.hasClass('fs-modal-footer')) {
|
365 |
-
return;
|
366 |
-
}
|
367 |
-
|
368 |
-
// If the user has not clicked the close button and the clicked element is inside the modal dialog, just return.
|
369 |
-
if (
|
370 |
-
! $target.hasClass( 'button-close' ) &&
|
371 |
-
( $target.parents( '.fs-modal-body' ).length > 0 || $target.parents( '.fs-modal-footer' ).length > 0 )
|
372 |
-
) {
|
373 |
-
return;
|
374 |
-
}
|
375 |
-
|
376 |
-
closeModal();
|
377 |
-
|
378 |
-
return false;
|
379 |
-
});
|
380 |
-
}
|
381 |
-
|
382 |
-
function isAnonymousFeedback() {
|
383 |
-
if ( ! isAnonymous ) {
|
384 |
-
return false;
|
385 |
-
}
|
386 |
-
|
387 |
-
return ( isReasonSelected( dontShareDataReasonID ) || $anonymousFeedback.find( 'input' ).prop( 'checked' ) );
|
388 |
-
}
|
389 |
-
|
390 |
-
function isReasonSelected( reasonID ) {
|
391 |
-
// Get the selected radio input element.
|
392 |
-
var $selectedReasonOption = $modal.find('input[type="radio"]:checked');
|
393 |
-
|
394 |
-
return ( reasonID == $selectedReasonOption.val() );
|
395 |
-
}
|
396 |
-
|
397 |
-
function isOtherReasonSelected() {
|
398 |
-
return isReasonSelected( otherReasonID );
|
399 |
-
}
|
400 |
-
|
401 |
-
function showModal() {
|
402 |
-
resetModal();
|
403 |
-
|
404 |
-
// Display the dialog box.
|
405 |
-
$modal.addClass('active');
|
406 |
-
|
407 |
-
$('body').addClass('has-fs-modal');
|
408 |
-
}
|
409 |
-
|
410 |
-
function closeModal() {
|
411 |
-
$modal.removeClass('active');
|
412 |
-
|
413 |
-
$('body').removeClass('has-fs-modal');
|
414 |
-
}
|
415 |
-
|
416 |
-
function resetModal() {
|
417 |
-
selectedReasonID = false;
|
418 |
-
|
419 |
-
enableDeactivateButton();
|
420 |
-
|
421 |
-
// Uncheck all radio buttons.
|
422 |
-
$modal.find('input[type="radio"]').prop('checked', false);
|
423 |
-
|
424 |
-
// Remove all input fields ( textfield, textarea ).
|
425 |
-
$modal.find('.reason-input').remove();
|
426 |
-
|
427 |
-
$modal.find('.message').hide();
|
428 |
-
|
429 |
-
if ( isAnonymous ) {
|
430 |
-
$anonymousFeedback.find( 'input' ).prop( 'checked', false );
|
431 |
-
|
432 |
-
// Hide, since by default there is no selected reason.
|
433 |
-
$anonymousFeedback.hide();
|
434 |
-
}
|
435 |
-
|
436 |
-
var $deactivateButton = $modal.find('.button-deactivate');
|
437 |
-
|
438 |
-
/*
|
439 |
-
* If the modal dialog has no confirmation message, that is, it has only one panel, then ensure
|
440 |
-
* that clicking the deactivate button will actually deactivate the plugin.
|
441 |
-
*/
|
442 |
-
if ( $modal.hasClass( 'no-confirmation-message' ) ) {
|
443 |
-
$deactivateButton.addClass( 'allow-deactivate' );
|
444 |
-
|
445 |
-
showPanel( 'reasons' );
|
446 |
-
} else {
|
447 |
-
$deactivateButton.removeClass( 'allow-deactivate' );
|
448 |
-
|
449 |
-
showPanel( 'confirm' );
|
450 |
-
}
|
451 |
-
}
|
452 |
-
|
453 |
-
function showMessage(message) {
|
454 |
-
$modal.find('.message').text(message).show();
|
455 |
-
}
|
456 |
-
|
457 |
-
function enableDeactivateButton() {
|
458 |
-
$modal.find('.button-deactivate').removeClass('disabled');
|
459 |
-
}
|
460 |
-
|
461 |
-
function disableDeactivateButton() {
|
462 |
-
$modal.find('.button-deactivate').addClass('disabled');
|
463 |
-
}
|
464 |
-
|
465 |
-
function showPanel(panelType) {
|
466 |
-
$modal.find( '.fs-modal-panel' ).removeClass( 'active' );
|
467 |
-
$modal.find( '[data-panel-id="' + panelType + '"]' ).addClass( 'active' );
|
468 |
-
|
469 |
-
updateButtonLabels();
|
470 |
-
}
|
471 |
-
|
472 |
-
function updateButtonLabels() {
|
473 |
-
var $deactivateButton = $modal.find( '.button-deactivate' );
|
474 |
-
|
475 |
-
// Reset the deactivate button's text.
|
476 |
-
if ( 'confirm' === getCurrentPanel() ) {
|
477 |
-
$deactivateButton.text( <?php echo json_encode( sprintf(
|
478 |
-
fs_text_inline( 'Yes - %s', 'deactivation-modal-button-confirm', $slug ),
|
479 |
-
$fs->is_plugin() ?
|
480 |
-
$deactivate_text :
|
481 |
-
sprintf( $activate_x_text, $theme_text )
|
482 |
-
) ) ?> );
|
483 |
-
} else {
|
484 |
-
$deactivateButton.html( <?php echo json_encode( sprintf(
|
485 |
-
fs_text_inline('Skip & %s', 'skip-and-x', $slug ),
|
486 |
-
$fs->is_plugin() ?
|
487 |
-
$deactivate_text :
|
488 |
-
sprintf( $activate_x_text, $theme_text )
|
489 |
-
) ) ?> );
|
490 |
-
}
|
491 |
-
}
|
492 |
-
|
493 |
-
function getCurrentPanel() {
|
494 |
-
return $modal.find('.fs-modal-panel.active').attr('data-panel-id');
|
495 |
-
}
|
496 |
-
})(jQuery);
|
497 |
-
</script>
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* @package Freemius
|
4 |
+
* @copyright Copyright (c) 2015, Freemius, Inc.
|
5 |
+
* @license https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License Version 3
|
6 |
+
* @since 1.1.2
|
7 |
+
*/
|
8 |
+
|
9 |
+
if ( ! defined( 'ABSPATH' ) ) {
|
10 |
+
exit;
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @var array $VARS
|
15 |
+
*/
|
16 |
+
$fs = freemius( $VARS['id'] );
|
17 |
+
$slug = $fs->get_slug();
|
18 |
+
|
19 |
+
$confirmation_message = $fs->apply_filters( 'uninstall_confirmation_message', '' );
|
20 |
+
|
21 |
+
$reasons = $VARS['reasons'];
|
22 |
+
|
23 |
+
$reasons_list_items_html = '';
|
24 |
+
|
25 |
+
foreach ( $reasons as $reason ) {
|
26 |
+
$list_item_classes = 'reason' . ( ! empty( $reason['input_type'] ) ? ' has-input' : '' );
|
27 |
+
|
28 |
+
if ( isset( $reason['internal_message'] ) && ! empty( $reason['internal_message'] ) ) {
|
29 |
+
$list_item_classes .= ' has-internal-message';
|
30 |
+
$reason_internal_message = $reason['internal_message'];
|
31 |
+
} else {
|
32 |
+
$reason_internal_message = '';
|
33 |
+
}
|
34 |
+
|
35 |
+
$reason_input_type = ( ! empty( $reason['input_type'] ) ? $reason['input_type'] : '' );
|
36 |
+
$reason_input_placeholder = ( ! empty( $reason['input_placeholder'] ) ? $reason['input_placeholder'] : '' );
|
37 |
+
|
38 |
+
$reason_list_item_html = <<< HTML
|
39 |
+
<li class="{$list_item_classes}"
|
40 |
+
data-input-type="{$reason_input_type}"
|
41 |
+
data-input-placeholder="{$reason_input_placeholder}">
|
42 |
+
<label>
|
43 |
+
<span>
|
44 |
+
<input type="radio" name="selected-reason" value="{$reason['id']}"/>
|
45 |
+
</span>
|
46 |
+
<span>{$reason['text']}</span>
|
47 |
+
</label>
|
48 |
+
<div class="internal-message">{$reason_internal_message}</div>
|
49 |
+
</li>
|
50 |
+
HTML;
|
51 |
+
|
52 |
+
$reasons_list_items_html .= $reason_list_item_html;
|
53 |
+
}
|
54 |
+
|
55 |
+
$is_anonymous = ( ! $fs->is_registered() );
|
56 |
+
if ( $is_anonymous ) {
|
57 |
+
$anonymous_feedback_checkbox_html = sprintf(
|
58 |
+
'<label class="anonymous-feedback-label"><input type="checkbox" class="anonymous-feedback-checkbox"> %s</label>',
|
59 |
+
fs_esc_html_inline( 'Anonymous feedback', 'anonymous-feedback', $slug )
|
60 |
+
);
|
61 |
+
} else {
|
62 |
+
$anonymous_feedback_checkbox_html = '';
|
63 |
+
}
|
64 |
+
|
65 |
+
// Aliases.
|
66 |
+
$deactivate_text = fs_text_inline( 'Deactivate', 'deactivate', $slug );
|
67 |
+
$theme_text = fs_text_inline( 'Theme', 'theme', $slug );
|
68 |
+
$activate_x_text = fs_text_inline( 'Activate %s', 'activate-x', $slug );
|
69 |
+
|
70 |
+
fs_enqueue_local_style( 'fs_dialog_boxes', '/admin/dialog-boxes.css' );
|
71 |
+
?>
|
72 |
+
<?php $fs->_maybe_add_subscription_cancellation_dialog_box() ?>
|
73 |
+
<script type="text/javascript">
|
74 |
+
(function ($) {
|
75 |
+
var reasonsHtml = <?php echo json_encode( $reasons_list_items_html ) ?>,
|
76 |
+
modalHtml =
|
77 |
+
'<div class="fs-modal fs-modal-deactivation-feedback<?php echo empty( $confirmation_message ) ? ' no-confirmation-message' : ''; ?>">'
|
78 |
+
+ ' <div class="fs-modal-dialog">'
|
79 |
+
+ ' <div class="fs-modal-header">'
|
80 |
+
+ ' <h4><?php fs_esc_attr_echo_inline( 'Quick Feedback', 'quick-feedback' , $slug ) ?></h4>'
|
81 |
+
+ ' </div>'
|
82 |
+
+ ' <div class="fs-modal-body">'
|
83 |
+
+ ' <div class="fs-modal-panel" data-panel-id="confirm"><p><?php echo $confirmation_message; ?></p></div>'
|
84 |
+
+ ' <div class="fs-modal-panel active" data-panel-id="reasons"><h3><strong><?php echo esc_js( sprintf( fs_text_inline( 'If you have a moment, please let us know why you are %s', 'deactivation-share-reason' , $slug ), ( $fs->is_plugin() ? fs_text_inline( 'deactivating', 'deactivating', $slug ) : fs_text_inline( 'switching', 'switching', $slug ) ) ) ) ?>:</strong></h3><ul id="reasons-list">' + reasonsHtml + '</ul></div>'
|
85 |
+
+ ' </div>'
|
86 |
+
+ ' <div class="fs-modal-footer">'
|
87 |
+
+ ' <?php echo $anonymous_feedback_checkbox_html ?>'
|
88 |
+
+ ' <a href="#" class="button button-secondary button-deactivate"></a>'
|
89 |
+
+ ' <a href="#" class="button button-primary button-close"><?php fs_esc_js_echo_inline( 'Cancel', 'cancel', $slug ) ?></a>'
|
90 |
+
+ ' </div>'
|
91 |
+
+ ' </div>'
|
92 |
+
+ '</div>',
|
93 |
+
$modal = $(modalHtml),
|
94 |
+
$deactivateLink = $('#the-list .deactivate > [data-module-id=<?php echo $fs->get_id() ?>].fs-module-id').prev(),
|
95 |
+
selectedReasonID = false,
|
96 |
+
redirectLink = '',
|
97 |
+
$anonymousFeedback = $modal.find( '.anonymous-feedback-label' ),
|
98 |
+
isAnonymous = <?php echo ( $is_anonymous ? 'true' : 'false' ); ?>,
|
99 |
+
otherReasonID = <?php echo Freemius::REASON_OTHER; ?>,
|
100 |
+
dontShareDataReasonID = <?php echo Freemius::REASON_DONT_LIKE_TO_SHARE_MY_INFORMATION; ?>,
|
101 |
+
deleteThemeUpdateData = <?php echo $fs->is_theme() && $fs->is_premium() && ! $fs->has_any_active_valid_license() ? 'true' : 'false' ?>,
|
102 |
+
$subscriptionCancellationModal = $( '.fs-modal-subscription-cancellation-<?php echo $fs->get_id() ?>' );
|
103 |
+
|
104 |
+
$modal.appendTo($('body'));
|
105 |
+
|
106 |
+
if ( 0 !== $subscriptionCancellationModal.length ) {
|
107 |
+
$subscriptionCancellationModal.on( '<?php echo $fs->get_action_tag( 'subscription_cancellation_action' ) ?>', function( evt, cancelSubscription ) {
|
108 |
+
if ( false === cancelSubscription ) {
|
109 |
+
showModal();
|
110 |
+
|
111 |
+
$subscriptionCancellationModal.trigger( 'closeModal' );
|
112 |
+
} else {
|
113 |
+
var $errorMessage = $subscriptionCancellationModal.find( '.notice-error' );
|
114 |
+
|
115 |
+
<?php
|
116 |
+
$subscription_cancellation_context = $fs->is_paid_trial() ?
|
117 |
+
fs_text_inline( 'trial', 'trial', $slug ) :
|
118 |
+
fs_text_inline( 'subscription', 'subscription', $slug );
|
119 |
+
?>
|
120 |
+
|
121 |
+
$.ajax({
|
122 |
+
url : ajaxurl,
|
123 |
+
method : 'POST',
|
124 |
+
data : {
|
125 |
+
action : '<?php echo $fs->get_ajax_action( 'cancel_subscription_or_trial' ) ?>',
|
126 |
+
security : '<?php echo $fs->get_ajax_security( 'cancel_subscription_or_trial' ) ?>',
|
127 |
+
module_id: '<?php echo $fs->get_id() ?>'
|
128 |
+
},
|
129 |
+
beforeSend: function() {
|
130 |
+
$errorMessage.hide();
|
131 |
+
|
132 |
+
$subscriptionCancellationModal.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
|
133 |
+
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).text( '<?php echo esc_js(
|
134 |
+
sprintf( fs_text_inline( 'Cancelling %s...', 'cancelling-x' , $slug ), $subscription_cancellation_context )
|
135 |
+
) ?>' );
|
136 |
+
},
|
137 |
+
success: function( result ) {
|
138 |
+
if ( result.success ) {
|
139 |
+
$subscriptionCancellationModal.removeClass( 'has-subscription-actions' );
|
140 |
+
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).removeClass( 'warn' );
|
141 |
+
|
142 |
+
$subscriptionCancellationModal.remove();
|
143 |
+
showModal();
|
144 |
+
} else {
|
145 |
+
$errorMessage.find( '> p' ).html( result.error );
|
146 |
+
$errorMessage.show();
|
147 |
+
|
148 |
+
$subscriptionCancellationModal.find( '.fs-modal-footer .button' ).removeClass( 'disabled' );
|
149 |
+
$subscriptionCancellationModal.find( '.fs-modal-footer .button-primary' ).html( <?php echo json_encode( sprintf(
|
150 |
+
fs_text_inline( 'Cancel %s & Proceed', 'cancel-x-and-proceed', $slug ),
|
151 |
+
ucfirst( $subscription_cancellation_context )
|
152 |
+
) ) ?> );
|
153 |
+
}
|
154 |
+
}
|
155 |
+
});
|
156 |
+
}
|
157 |
+
});
|
158 |
+
}
|
159 |
+
|
160 |
+
registerEventHandlers();
|
161 |
+
|
162 |
+
function registerEventHandlers() {
|
163 |
+
$deactivateLink.click(function (evt) {
|
164 |
+
evt.preventDefault();
|
165 |
+
|
166 |
+
redirectLink = $(this).attr('href');
|
167 |
+
|
168 |
+
if ( 0 == $subscriptionCancellationModal.length ) {
|
169 |
+
showModal();
|
170 |
+
} else {
|
171 |
+
$subscriptionCancellationModal.trigger( 'showModal' );
|
172 |
+
}
|
173 |
+
});
|
174 |
+
|
175 |
+
<?php
|
176 |
+
if ( ! $fs->is_plugin() ) {
|
177 |
+
/**
|
178 |
+
* For "theme" module type, the modal is shown when the current user clicks on
|
179 |
+
* the "Activate" button of any other theme. The "Activate" button is actually
|
180 |
+
* a link to the "Themes" page (/wp-admin/themes.php) containing query params
|
181 |
+
* that tell WordPress to deactivate the current theme and activate a different theme.
|
182 |
+
*
|
183 |
+
* @author Leo Fajardo (@leorw)
|
184 |
+
* @since 1.2.2
|
185 |
+
*
|
186 |
+
* @since 1.2.2.7 Don't trigger the deactivation feedback form if activating the premium version of the theme.
|
187 |
+
*/
|
188 |
+
?>
|
189 |
+
$('body').on('click', '.theme-browser .theme:not([data-slug=<?php echo $fs->get_premium_slug() ?>]) .theme-actions .button.activate', function (evt) {
|
190 |
+
evt.preventDefault();
|
191 |
+
|
192 |
+
redirectLink = $(this).attr('href');
|
193 |
+
|
194 |
+
showModal();
|
195 |
+
});
|
196 |
+
<?php
|
197 |
+
} ?>
|
198 |
+
|
199 |
+
$modal.on('input propertychange', '.reason-input input', function () {
|
200 |
+
if (!isOtherReasonSelected()) {
|
201 |
+
return;
|
202 |
+
}
|
203 |
+
|
204 |
+
var reason = $(this).val().trim();
|
205 |
+
|
206 |
+
/**
|
207 |
+
* If reason is not empty, remove the error-message class of the message container
|
208 |
+
* to change the message color back to default.
|
209 |
+
*/
|
210 |
+
if (reason.length > 0) {
|
211 |
+
$('.message').removeClass('error-message');
|
212 |
+
enableDeactivateButton();
|
213 |
+
}
|
214 |
+
});
|
215 |
+
|
216 |
+
$modal.on('blur', '.reason-input input', function () {
|
217 |
+
var $userReason = $(this);
|
218 |
+
|
219 |
+
setTimeout(function () {
|
220 |
+
if (!isOtherReasonSelected()) {
|
221 |
+
return;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* If reason is empty, add the error-message class to the message container
|
226 |
+
* to change the message color to red.
|
227 |
+
*/
|
228 |
+
if (0 === $userReason.val().trim().length) {
|
229 |
+
$('.message').addClass('error-message');
|
230 |
+
disableDeactivateButton();
|
231 |
+
}
|
232 |
+
}, 150);
|
233 |
+
});
|
234 |
+
|
235 |
+
$modal.on('click', '.fs-modal-footer .button', function (evt) {
|
236 |
+
evt.preventDefault();
|
237 |
+
|
238 |
+
if ($(this).hasClass('disabled')) {
|
239 |
+
return;
|
240 |
+
}
|
241 |
+
|
242 |
+
var _parent = $(this).parents('.fs-modal:first');
|
243 |
+
var _this = $(this);
|
244 |
+
|
245 |
+
if (_this.hasClass('allow-deactivate')) {
|
246 |
+
var $radio = $('input[type="radio"]:checked');
|
247 |
+
|
248 |
+
if (0 === $radio.length) {
|
249 |
+
if ( ! deleteThemeUpdateData ) {
|
250 |
+
// If no selected reason, just deactivate the plugin.
|
251 |
+
window.location.href = redirectLink;
|
252 |
+
} else {
|
253 |
+
$.ajax({
|
254 |
+
url : ajaxurl,
|
255 |
+
method : 'POST',
|
256 |
+
data : {
|
257 |
+
action : '<?php echo $fs->get_ajax_action( 'delete_theme_update_data' ) ?>',
|
258 |
+
security : '<?php echo $fs->get_ajax_security( 'delete_theme_update_data' ) ?>',
|
259 |
+
module_id: '<?php echo $fs->get_id() ?>'
|
260 |
+
},
|
261 |
+
beforeSend: function() {
|
262 |
+
_parent.find( '.fs-modal-footer .button' ).addClass( 'disabled' );
|
263 |
+
_parent.find( '.fs-modal-footer .button-secondary' ).text( 'Processing...' );
|
264 |
+
},
|
265 |
+
complete : function() {
|
266 |
+
window.location.href = redirectLink;
|
267 |
+
}
|
268 |
+
});
|
269 |
+
}
|
270 |
+
|
271 |
+
return;
|
272 |
+
}
|
273 |
+
|
274 |
+
var $selected_reason = $radio.parents('li:first'),
|
275 |
+
$input = $selected_reason.find('textarea, input[type="text"]'),
|
276 |
+
userReason = ( 0 !== $input.length ) ? $input.val().trim() : '';
|
277 |
+
|
278 |
+
if (isOtherReasonSelected() && ( '' === userReason )) {
|
279 |
+
return;
|
280 |
+
}
|
281 |
+
|
282 |
+
$.ajax({
|
283 |
+
url : ajaxurl,
|
284 |
+
method : 'POST',
|
285 |
+
data : {
|
286 |
+
action : '<?php echo $fs->get_ajax_action( 'submit_uninstall_reason' ) ?>',
|
287 |
+
security : '<?php echo $fs->get_ajax_security( 'submit_uninstall_reason' ) ?>',
|
288 |
+
module_id : '<?php echo $fs->get_id() ?>',
|
289 |
+
reason_id : $radio.val(),
|
290 |
+
reason_info : userReason,
|
291 |
+
is_anonymous: isAnonymousFeedback()
|
292 |
+
},
|
293 |
+
beforeSend: function () {
|
294 |
+
_parent.find('.fs-modal-footer .button').addClass('disabled');
|
295 |
+
_parent.find('.fs-modal-footer .button-secondary').text('Processing...');
|
296 |
+
},
|
297 |
+
complete : function () {
|
298 |
+
// Do not show the dialog box, deactivate the plugin.
|
299 |
+
window.location.href = redirectLink;
|
300 |
+
}
|
301 |
+
});
|
302 |
+
} else if (_this.hasClass('button-deactivate')) {
|
303 |
+
// Change the Deactivate button's text and show the reasons panel.
|
304 |
+
_parent.find('.button-deactivate').addClass('allow-deactivate');
|
305 |
+
|
306 |
+
showPanel('reasons');
|
307 |
+
}
|
308 |
+
});
|
309 |
+
|
310 |
+
$modal.on('click', 'input[type="radio"]', function () {
|
311 |
+
var $selectedReasonOption = $( this );
|
312 |
+
|
313 |
+
// If the selection has not changed, do not proceed.
|
314 |
+
if (selectedReasonID === $selectedReasonOption.val())
|
315 |
+
return;
|
316 |
+
|
317 |
+
selectedReasonID = $selectedReasonOption.val();
|
318 |
+
|
319 |
+
if ( isAnonymous ) {
|
320 |
+
if ( isReasonSelected( dontShareDataReasonID ) ) {
|
321 |
+
$anonymousFeedback.hide();
|
322 |
+
} else {
|
323 |
+
$anonymousFeedback.show();
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
var _parent = $(this).parents('li:first');
|
328 |
+
|
329 |
+
$modal.find('.reason-input').remove();
|
330 |
+
$modal.find( '.internal-message' ).hide();
|
331 |
+
$modal.find('.button-deactivate').html('<?php echo esc_js( sprintf(
|
332 |
+
fs_text_inline( 'Submit & %s', 'deactivation-modal-button-submit' , $slug ),
|
333 |
+
$fs->is_plugin() ?
|
334 |
+
$deactivate_text :
|
335 |
+
sprintf( $activate_x_text, $theme_text )
|
336 |
+
) ) ?>');
|
337 |
+
|
338 |
+
enableDeactivateButton();
|
339 |
+
|
340 |
+
if ( _parent.hasClass( 'has-internal-message' ) ) {
|
341 |
+
_parent.find( '.internal-message' ).show();
|
342 |
+
}
|
343 |
+
|
344 |
+
if (_parent.hasClass('has-input')) {
|
345 |
+
var inputType = _parent.data('input-type'),
|
346 |
+
inputPlaceholder = _parent.data('input-placeholder'),
|
347 |
+
reasonInputHtml = '<div class="reason-input"><span class="message"></span>' + ( ( 'textfield' === inputType ) ? '<input type="text" />' : '<textarea rows="5"></textarea>' ) + '</div>';
|
348 |
+
|
349 |
+
_parent.append($(reasonInputHtml));
|
350 |
+
_parent.find('input, textarea').attr('placeholder', inputPlaceholder).focus();
|
351 |
+
|
352 |
+
if (isOtherReasonSelected()) {
|
353 |
+
showMessage('<?php echo esc_js( fs_text_inline( 'Kindly tell us the reason so we can improve.', 'ask-for-reason-message' , $slug ) ); ?>');
|
354 |
+
disableDeactivateButton();
|
355 |
+
}
|
356 |
+
}
|
357 |
+
});
|
358 |
+
|
359 |
+
// If the user has clicked outside the window, cancel it.
|
360 |
+
$modal.on('click', function (evt) {
|
361 |
+
var $target = $(evt.target);
|
362 |
+
|
363 |
+
// If the user has clicked anywhere in the modal dialog, just return.
|
364 |
+
if ($target.hasClass('fs-modal-body') || $target.hasClass('fs-modal-footer')) {
|
365 |
+
return;
|
366 |
+
}
|
367 |
+
|
368 |
+
// If the user has not clicked the close button and the clicked element is inside the modal dialog, just return.
|
369 |
+
if (
|
370 |
+
! $target.hasClass( 'button-close' ) &&
|
371 |
+
( $target.parents( '.fs-modal-body' ).length > 0 || $target.parents( '.fs-modal-footer' ).length > 0 )
|
372 |
+
) {
|
373 |
+
return;
|
374 |
+
}
|
375 |
+
|
376 |
+
closeModal();
|
377 |
+
|
378 |
+
return false;
|
379 |
+
});
|
380 |
+
}
|
381 |
+
|
382 |
+
function isAnonymousFeedback() {
|
383 |
+
if ( ! isAnonymous ) {
|
384 |
+
return false;
|
385 |
+
}
|
386 |
+
|
387 |
+
return ( isReasonSelected( dontShareDataReasonID ) || $anonymousFeedback.find( 'input' ).prop( 'checked' ) );
|
388 |
+
}
|
389 |
+
|
390 |
+
function isReasonSelected( reasonID ) {
|
391 |
+
// Get the selected radio input element.
|
392 |
+
var $selectedReasonOption = $modal.find('input[type="radio"]:checked');
|
393 |
+
|
394 |
+
return ( reasonID == $selectedReasonOption.val() );
|
395 |
+
}
|
396 |
+
|
397 |
+
function isOtherReasonSelected() {
|
398 |
+
return isReasonSelected( otherReasonID );
|
399 |
+
}
|
400 |
+
|
401 |
+
function showModal() {
|
402 |
+
resetModal();
|
403 |
+
|
404 |
+
// Display the dialog box.
|
405 |
+
$modal.addClass('active');
|
406 |
+
|
407 |
+
$('body').addClass('has-fs-modal');
|
408 |
+
}
|
409 |
+
|
410 |
+
function closeModal() {
|
411 |
+
$modal.removeClass('active');
|
412 |
+
|
413 |
+
$('body').removeClass('has-fs-modal');
|
414 |
+
}
|
415 |
+
|
416 |
+
function resetModal() {
|
417 |
+
selectedReasonID = false;
|
418 |
+
|
419 |
+
enableDeactivateButton();
|
420 |
+
|
421 |
+
// Uncheck all radio buttons.
|
422 |
+
$modal.find('input[type="radio"]').prop('checked', false);
|
423 |
+
|
424 |
+
// Remove all input fields ( textfield, textarea ).
|
425 |
+
$modal.find('.reason-input').remove();
|
426 |
+
|
427 |
+
$modal.find('.message').hide();
|
428 |
+
|
429 |
+
if ( isAnonymous ) {
|
430 |
+
$anonymousFeedback.find( 'input' ).prop( 'checked', false );
|
431 |
+
|
432 |
+
// Hide, since by default there is no selected reason.
|
433 |
+
$anonymousFeedback.hide();
|
434 |
+
}
|
435 |
+
|
436 |
+
var $deactivateButton = $modal.find('.button-deactivate');
|
437 |
+
|
438 |
+
/*
|
439 |
+
* If the modal dialog has no confirmation message, that is, it has only one panel, then ensure
|
440 |
+
* that clicking the deactivate button will actually deactivate the plugin.
|
441 |
+
*/
|
442 |
+
if ( $modal.hasClass( 'no-confirmation-message' ) ) {
|
443 |
+
$deactivateButton.addClass( 'allow-deactivate' );
|
444 |
+
|
445 |
+
showPanel( 'reasons' );
|
446 |
+
} else {
|
447 |
+
$deactivateButton.removeClass( 'allow-deactivate' );
|
448 |
+
|
449 |
+
showPanel( 'confirm' );
|
450 |
+
}
|
451 |
+
}
|
452 |
+
|
453 |
+
function showMessage(message) {
|
454 |
+
$modal.find('.message').text(message).show();
|
455 |
+
}
|
456 |
+
|
457 |
+
function enableDeactivateButton() {
|
458 |
+
$modal.find('.button-deactivate').removeClass('disabled');
|
459 |
+
}
|
460 |
+
|
461 |
+
function disableDeactivateButton() {
|
462 |
+
$modal.find('.button-deactivate').addClass('disabled');
|
463 |
+
}
|
464 |
+
|
465 |
+
function showPanel(panelType) {
|
466 |
+
$modal.find( '.fs-modal-panel' ).removeClass( 'active' );
|
467 |
+
$modal.find( '[data-panel-id="' + panelType + '"]' ).addClass( 'active' );
|
468 |
+
|
469 |
+
updateButtonLabels();
|
470 |
+
}
|
471 |
+
|
472 |
+
function updateButtonLabels() {
|
473 |
+
var $deactivateButton = $modal.find( '.button-deactivate' );
|
474 |
+
|
475 |
+
// Reset the deactivate button's text.
|
476 |
+
if ( 'confirm' === getCurrentPanel() ) {
|
477 |
+
$deactivateButton.text( <?php echo json_encode( sprintf(
|
478 |
+
fs_text_inline( 'Yes - %s', 'deactivation-modal-button-confirm', $slug ),
|
479 |
+
$fs->is_plugin() ?
|
480 |
+
$deactivate_text :
|
481 |
+
sprintf( $activate_x_text, $theme_text )
|
482 |
+
) ) ?> );
|
483 |
+
} else {
|
484 |
+
$deactivateButton.html( <?php echo json_encode( sprintf(
|
485 |
+
fs_text_inline('Skip & %s', 'skip-and-x', $slug ),
|
486 |
+
$fs->is_plugin() ?
|
487 |
+
$deactivate_text :
|
488 |
+
sprintf( $activate_x_text, $theme_text )
|
489 |
+
) ) ?> );
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
function getCurrentPanel() {
|
494 |
+
return $modal.find('.fs-modal-panel.active').attr('data-panel-id');
|
495 |
+
}
|
496 |
+
})(jQuery);
|
497 |
+
</script>
|
wp-fail2ban-main.php
CHANGED
@@ -11,6 +11,7 @@ namespace org\lecklider\charles\wordpress\wp_fail2ban;
|
|
11 |
if ( !defined( 'ABSPATH' ) ) {
|
12 |
exit;
|
13 |
}
|
|
|
14 |
/**
|
15 |
* Defaults
|
16 |
*
|
@@ -21,7 +22,75 @@ define( 'DEFAULT_WP_FAIL2BAN_AUTH_LOG', LOG_AUTH );
|
|
21 |
define( 'DEFAULT_WP_FAIL2BAN_COMMENT_LOG', LOG_USER );
|
22 |
define( 'DEFAULT_WP_FAIL2BAN_PINGBACK_LOG', LOG_USER );
|
23 |
define( 'DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG', LOG_USER );
|
24 |
-
define( 'DEFAULT_WP_FAIL2BAN_SPAM_LOG',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
/**
|
26 |
* Allow custom openlog options.
|
27 |
* e.g. you may not want the PID if logging remotely.
|
@@ -54,52 +123,100 @@ if ( !defined( 'WP_FAIL2BAN_PASSWORD_REQUEST_LOG' ) ) {
|
|
54 |
if ( !defined( 'WP_FAIL2BAN_SPAM_LOG' ) ) {
|
55 |
define( 'WP_FAIL2BAN_SPAM_LOG', DEFAULT_WP_FAIL2BAN_SPAM_LOG );
|
56 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
|
58 |
if ( is_admin() ) {
|
59 |
require 'admin/summary.php';
|
60 |
} else {
|
61 |
require dirname( __FILE__ ) . '/feature/lib.php';
|
62 |
/**
|
63 |
-
* @since
|
64 |
-
* @since 1.0.0
|
65 |
-
*
|
66 |
-
* @param string $user_login
|
67 |
*/
|
68 |
-
|
69 |
-
{
|
70 |
-
|
71 |
-
|
72 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
}
|
74 |
|
75 |
-
add_action(
|
76 |
-
'wp_login',
|
77 |
-
__NAMESPACE__ . '\\wp_login',
|
78 |
-
10,
|
79 |
-
2
|
80 |
-
);
|
81 |
/**
|
82 |
-
* @since
|
83 |
-
* @since 1.0.0
|
84 |
-
*
|
85 |
-
* @param string $username
|
86 |
-
*
|
87 |
-
* @wp-f2b-hard Authentication attempt for unknown user .*
|
88 |
-
* @wp-f2b-hard XML-RPC authentication attempt for unknown user .*
|
89 |
-
* @wp-f2b-soft Authentication failure for .*
|
90 |
-
* @wp-f2b-soft XML-RPC authentication failure for .*
|
91 |
*/
|
92 |
-
|
93 |
-
{
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
}
|
101 |
|
102 |
-
add_action( 'wp_login_failed', __NAMESPACE__ . '\\wp_login_failed' );
|
103 |
/**
|
104 |
* Comments
|
105 |
*
|
11 |
if ( !defined( 'ABSPATH' ) ) {
|
12 |
exit;
|
13 |
}
|
14 |
+
// phpcs:disable Generic.Functions.FunctionCallArgumentSpacing
|
15 |
/**
|
16 |
* Defaults
|
17 |
*
|
22 |
define( 'DEFAULT_WP_FAIL2BAN_COMMENT_LOG', LOG_USER );
|
23 |
define( 'DEFAULT_WP_FAIL2BAN_PINGBACK_LOG', LOG_USER );
|
24 |
define( 'DEFAULT_WP_FAIL2BAN_PASSWORD_REQUEST_LOG', LOG_USER );
|
25 |
+
define( 'DEFAULT_WP_FAIL2BAN_SPAM_LOG', LOG_AUTH );
|
26 |
+
/**
|
27 |
+
* @since 4.0.5
|
28 |
+
*/
|
29 |
+
define( 'DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG', LOG_AUTH );
|
30 |
+
define( 'DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG', LOG_AUTH );
|
31 |
+
register_activation_hook( WP_FAIL2BAN_FILE, function () {
|
32 |
+
foreach ( get_mu_plugins() as $plugin => $data ) {
|
33 |
+
|
34 |
+
if ( 'WP fail2ban' == substr( $data['Name'], 0, 11 ) ) {
|
35 |
+
$wp_f2b_ver = substr( WP_FAIL2BAN_VER, 0, strrpos( WP_FAIL2BAN_VER, '.' ) );
|
36 |
+
$wpf2b = 'WP fail2ban';
|
37 |
+
$error_msg = "<h1>Cannot activate {$wpf2b}</h1>";
|
38 |
+
$mu_file = WPMU_PLUGIN_DIR . '/' . $plugin;
|
39 |
+
|
40 |
+
if ( is_link( $mu_file ) ) {
|
41 |
+
|
42 |
+
if ( false === ($link = readlink( $mu_file )) || false === ($path = realpath( $mu_file )) ) {
|
43 |
+
$error_msg .= <<<__ERROR__
|
44 |
+
<h3>A broken symbolic link was found in <tt>mu-plugins</tt>:</h3>
|
45 |
+
<p><tt>{$mu_file}</tt></p>
|
46 |
+
__ERROR__;
|
47 |
+
} elseif ( WP_FAIL2BAN_FILE == $path ) {
|
48 |
+
// OK, we're linking to ourself
|
49 |
+
} else {
|
50 |
+
$mu_file = str_replace( '/', '/<wbr>', $mu_file );
|
51 |
+
$mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
|
52 |
+
$error_msg .= <<<__ERROR__
|
53 |
+
<h3>A conflicting symbolic link was found in <tt>mu-plugins</tt>:</h3>
|
54 |
+
<style>
|
55 |
+
table { text-align: center; }
|
56 |
+
td { width: 50%; }
|
57 |
+
th { font-size: 200%; }
|
58 |
+
td, th { font-family: monospace; }
|
59 |
+
span.tt { font-weight: bold; }
|
60 |
+
</style>
|
61 |
+
<table>
|
62 |
+
<tr>
|
63 |
+
<td>{$mu_file}</td>
|
64 |
+
<th>⇒</th>
|
65 |
+
<td>{$link}</td>
|
66 |
+
</tr>
|
67 |
+
<tr>
|
68 |
+
<td colspan="3"><span class="tt">≡</span> <span>{$path}</span></td>
|
69 |
+
</tr>
|
70 |
+
<tr>
|
71 |
+
<td colspan="3"></td>
|
72 |
+
</tr>
|
73 |
+
</table>
|
74 |
+
__ERROR__;
|
75 |
+
}
|
76 |
+
|
77 |
+
} else {
|
78 |
+
$mu_file = str_replace( '/', '/<wbr>', $mu_file );
|
79 |
+
$mu_file = substr( $mu_file, strlen( WPMU_PLUGIN_DIR ) - 1 );
|
80 |
+
$error_msg .= <<<__ERROR__
|
81 |
+
<h3>A conflicting file was found in <tt>mu-plugins</tt>:</h3>
|
82 |
+
<p><tt>{$mu_file}</tt></p>
|
83 |
+
__ERROR__;
|
84 |
+
}
|
85 |
+
|
86 |
+
$error_msg .= "<p>Please see the <a href=\"https://docs.wp-fail2ban.com/en/{$wp_f2b_ver}/configuration.html#mu-plugins-support\" target=\"_blank\">documentation</a> for how to configure {$wpf2b} for <tt>mu-plugins</tt>.</p>";
|
87 |
+
$error_msg .= '<p>Click <a href="?">here</a> to return to the plugins page.</p>';
|
88 |
+
deactivate_plugins( plugin_basename( WP_FAIL2BAN_FILE ) );
|
89 |
+
wp_die( $error_msg );
|
90 |
+
}
|
91 |
+
|
92 |
+
}
|
93 |
+
} );
|
94 |
/**
|
95 |
* Allow custom openlog options.
|
96 |
* e.g. you may not want the PID if logging remotely.
|
123 |
if ( !defined( 'WP_FAIL2BAN_SPAM_LOG' ) ) {
|
124 |
define( 'WP_FAIL2BAN_SPAM_LOG', DEFAULT_WP_FAIL2BAN_SPAM_LOG );
|
125 |
}
|
126 |
+
/**
|
127 |
+
* @since 4.0.5
|
128 |
+
*/
|
129 |
+
if ( !defined( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG' ) ) {
|
130 |
+
define( 'WP_FAIL2BAN_COMMENT_EXTRA_LOG', DEFAULT_WP_FAIL2BAN_COMMENT_EXTRA_LOG );
|
131 |
+
}
|
132 |
+
if ( !defined( 'WP_FAIL2BAN_PINGBACK_ERROR_LOG' ) ) {
|
133 |
+
define( 'WP_FAIL2BAN_PINGBACK_ERROR_LOG', DEFAULT_WP_FAIL2BAN_PINGBACK_ERROR_LOG );
|
134 |
+
}
|
135 |
|
136 |
if ( is_admin() ) {
|
137 |
require 'admin/summary.php';
|
138 |
} else {
|
139 |
require dirname( __FILE__ ) . '/feature/lib.php';
|
140 |
/**
|
141 |
+
* @since 4.0.5
|
|
|
|
|
|
|
142 |
*/
|
143 |
+
|
144 |
+
if ( !function_exists( __NAMESPACE__ . '\\wp_login' ) ) {
|
145 |
+
/**
|
146 |
+
* @since 4.1.0 Add REST support
|
147 |
+
* @since 3.5.0 Refactored for unit testing
|
148 |
+
* @since 1.0.0
|
149 |
+
*
|
150 |
+
* @param string $user_login
|
151 |
+
*/
|
152 |
+
function wp_login( $user_login, $user )
|
153 |
+
{
|
154 |
+
global $wp_xmlrpc_server ;
|
155 |
+
|
156 |
+
if ( defined( 'REST_REQUEST' ) ) {
|
157 |
+
$action = 'WPF2B_ACTION_REST_AUTH_OK';
|
158 |
+
} elseif ( $wp_xmlrpc_server ) {
|
159 |
+
$action = 'WPF2B_ACTION_XMLRPC_AUTH_OK';
|
160 |
+
} else {
|
161 |
+
$action = 'WPF2B_ACTION_AUTH_OK';
|
162 |
+
}
|
163 |
+
|
164 |
+
openlog();
|
165 |
+
syslog( LOG_INFO, "Accepted password for {$user_login}" );
|
166 |
+
// @codeCoverageIgnoreEnd
|
167 |
+
}
|
168 |
+
|
169 |
+
add_action(
|
170 |
+
'wp_login',
|
171 |
+
__NAMESPACE__ . '\\wp_login',
|
172 |
+
10,
|
173 |
+
2
|
174 |
+
);
|
175 |
}
|
176 |
|
|
|
|
|
|
|
|
|
|
|
|
|
177 |
/**
|
178 |
+
* @since 4.0.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
*/
|
180 |
+
|
181 |
+
if ( !function_exists( __NAMESPACE__ . '\\wp_login_failed' ) ) {
|
182 |
+
/**
|
183 |
+
* @since 4.1.0 Add REST support
|
184 |
+
* @since 3.5.0 Refactored for unit testing
|
185 |
+
* @since 1.0.0
|
186 |
+
*
|
187 |
+
* @param string $username
|
188 |
+
*
|
189 |
+
* @wp-f2b-hard Authentication attempt for unknown user .*
|
190 |
+
* @wp-f2b-hard REST authentication attempt for unknown user .*
|
191 |
+
* @wp-f2b-hard XML-RPC authentication attempt for unknown user .*
|
192 |
+
* @wp-f2b-soft Authentication failure for .*
|
193 |
+
* @wp-f2b-soft REST authentication failure for .*
|
194 |
+
* @wp-f2b-soft XML-RPC authentication failure for .*
|
195 |
+
*/
|
196 |
+
function wp_login_failed( $username )
|
197 |
+
{
|
198 |
+
global $wp_xmlrpc_server ;
|
199 |
+
|
200 |
+
if ( defined( 'REST_REQUEST' ) ) {
|
201 |
+
$action = 'WPF2B_ACTION_REST_AUTH_FAIL';
|
202 |
+
$msg = 'REST a';
|
203 |
+
} elseif ( $wp_xmlrpc_server ) {
|
204 |
+
$action = 'WPF2B_ACTION_XMLRPC_AUTH_FAIL';
|
205 |
+
$msg = 'XML-RPC a';
|
206 |
+
} else {
|
207 |
+
$action = 'WPF2B_ACTION_AUTH_FAIL';
|
208 |
+
$msg = 'A';
|
209 |
+
}
|
210 |
+
|
211 |
+
$msg .= ( wp_cache_get( $username, 'userlogins' ) ? "uthentication failure for {$username}" : "uthentication attempt for unknown user {$username}" );
|
212 |
+
openlog();
|
213 |
+
syslog( LOG_NOTICE, $msg );
|
214 |
+
// @codeCoverageIgnoreEnd
|
215 |
+
}
|
216 |
+
|
217 |
+
add_action( 'wp_login_failed', __NAMESPACE__ . '\\wp_login_failed' );
|
218 |
}
|
219 |
|
|
|
220 |
/**
|
221 |
* Comments
|
222 |
*
|
wp-fail2ban.php
CHANGED
@@ -5,14 +5,13 @@
|
|
5 |
* Plugin URI: https://wp-fail2ban.com/
|
6 |
* Description: Write a myriad of WordPress events to syslog for integration with fail2ban.
|
7 |
* Text Domain: wp-fail2ban
|
8 |
-
* Version: 4.0
|
9 |
* Author: Charles Lecklider
|
10 |
* Author URI: https://charles.lecklider.org/
|
11 |
* License: GPLv2
|
12 |
* SPDX-License-Identifier: GPL-2.0
|
13 |
* Requires PHP: 5.3
|
14 |
*
|
15 |
-
* @fs_premium_only /admin/premium/, composer.json, composer.lock, /premium/, /vendor/autoload.php, /vendor/composer/, /vendor/geoip2/, /vendor/maxmind/, /vendor/maxmind-db/
|
16 |
*/
|
17 |
/*
|
18 |
* Copyright 2012-19 Charles Lecklider (email : wordpress@charles.lecklider.org)
|
@@ -36,15 +35,10 @@
|
|
36 |
namespace org\lecklider\charles\wordpress\wp_fail2ban;
|
37 |
|
38 |
/**
|
39 |
-
*
|
40 |
-
*
|
41 |
-
* @since 4.0.0 Leave here for easy version bump
|
42 |
*/
|
43 |
-
|
44 |
-
|
45 |
-
return;
|
46 |
-
}
|
47 |
-
define( 'WP_FAIL2BAN', '4.0.2' );
|
48 |
/**
|
49 |
* Freemius integration
|
50 |
*
|
5 |
* Plugin URI: https://wp-fail2ban.com/
|
6 |
* Description: Write a myriad of WordPress events to syslog for integration with fail2ban.
|
7 |
* Text Domain: wp-fail2ban
|
8 |
+
* Version: 4.1.0
|
9 |
* Author: Charles Lecklider
|
10 |
* Author URI: https://charles.lecklider.org/
|
11 |
* License: GPLv2
|
12 |
* SPDX-License-Identifier: GPL-2.0
|
13 |
* Requires PHP: 5.3
|
14 |
*
|
|
|
15 |
*/
|
16 |
/*
|
17 |
* Copyright 2012-19 Charles Lecklider (email : wordpress@charles.lecklider.org)
|
35 |
namespace org\lecklider\charles\wordpress\wp_fail2ban;
|
36 |
|
37 |
/**
|
38 |
+
* @since 4.0.5
|
|
|
|
|
39 |
*/
|
40 |
+
define( 'WP_FAIL2BAN_VER', '4.1.0' );
|
41 |
+
define( 'WP_FAIL2BAN_FILE', __FILE__ );
|
|
|
|
|
|
|
42 |
/**
|
43 |
* Freemius integration
|
44 |
*
|