Shield Security for WordPress - Version 1.9.2

Version Description

  • CHANGED: Simplified the automatic WordPress Plugin updates into 1 filter for consistency
Download this release

Release Info

Developer paultgoodchild
Plugin Icon 128x128 Shield Security for WordPress
Version 1.9.2
Comparing to
See all releases

Code changes from version 1.0 to 1.9.2

Files changed (38) hide show
  1. .htaccess +4 -0
  2. icwp-wpsf.php +1072 -401
  3. mode.login_throttled +0 -0
  4. readme.txt +376 -36
  5. src/icwp-base-processor.php +353 -0
  6. src/icwp-basedb-processor.php +130 -0
  7. src/icwp-data-processor.php +193 -17
  8. src/icwp-firewall-processor.php +0 -502
  9. src/icwp-import-base-processor.php +84 -0
  10. src/icwp-import-wpf2-processor.php +147 -0
  11. src/icwp-optionshandler-autoupdates.php +148 -0
  12. src/icwp-optionshandler-base.php +569 -0
  13. src/icwp-optionshandler-commentsfilter.php +164 -0
  14. src/icwp-optionshandler-firewall.php +255 -0
  15. src/icwp-optionshandler-lockdown.php +123 -0
  16. src/icwp-optionshandler-loginprotect.php +150 -0
  17. src/icwp-optionshandler-wpsf.php +275 -0
  18. src/icwp-plugins-base.php +233 -59
  19. src/icwp-processor-autoupdates.php +81 -0
  20. src/icwp-processor-commentsfilter.php +431 -0
  21. src/icwp-processor-email.php +168 -0
  22. src/icwp-processor-firewall.php +620 -0
  23. src/icwp-processor-lockdown.php +109 -0
  24. src/icwp-processor-logging.php +108 -0
  25. src/icwp-processor-loginprotect.php +686 -0
  26. src/icwp-wpfilesystem.php +193 -0
  27. src/icwp-wpfunctions.php +118 -0
  28. views/icwp_options_helper.php +77 -4
  29. views/icwp_wpsf_access_key_request_index.php +64 -0
  30. views/{icwp_wpsf_firewall_config_index.php → icwp_wpsf_config_autoupdates_index.php} +3 -7
  31. views/icwp_wpsf_config_comments_filter_index.php +35 -0
  32. views/icwp_wpsf_config_firewall_index.php +38 -0
  33. views/icwp_wpsf_config_lockdown_index.php +35 -0
  34. views/icwp_wpsf_config_login_protect_index.php +35 -0
  35. views/icwp_wpsf_firewall_log_index.php +62 -30
  36. views/icwp_wpsf_index.php +208 -38
  37. views/widgets/icwp_common_widgets.php +5 -5
  38. views/widgets/icwp_widgets.php +3 -2
.htaccess ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ Order Allow,Deny
2
+ <FilesMatch "^.*\.(css|js|png)$">
3
+ Allow from all
4
+ </FilesMatch>
icwp-wpsf.php CHANGED
@@ -1,11 +1,11 @@
1
  <?php
2
  /*
3
  Plugin Name: WordPress Simple Firewall
4
- Plugin URI: http://www.icontrolwp.com/
5
  Description: A Simple WordPress Firewall
6
- Version: 1.0
7
  Author: iControlWP
8
- Author URI: http://icwp.io/v
9
  */
10
 
11
  /**
@@ -27,342 +27,594 @@ Author URI: http://icwp.io/v
27
  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
- *
31
  */
32
 
33
  require_once( dirname(__FILE__).'/src/icwp-plugins-base.php' );
 
34
 
35
  if ( !class_exists('ICWP_Wordpress_Simple_Firewall') ):
36
 
37
  class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Base_Plugin {
38
 
39
- const InputPrefix = 'icwp_wpsf_';
40
- const OptionPrefix = 'icwp_wpsf_'; //ALL database options use this as the prefix.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
- static public $VERSION = '1.0'; //SHOULD BE UPDATED UPON EACH NEW RELEASE
 
 
 
43
 
44
- protected $m_aAllPluginOptions;
45
- protected $m_aPluginOptions_Base;
46
- protected $m_aPluginOptions_BlockTypesSection;
47
- protected $m_aPluginOptions_BlockSection;
48
- protected $m_aPluginOptions_WhitelistSection;
49
- protected $m_aPluginOptions_BlacklistSection;
50
 
51
  public function __construct() {
 
 
52
  parent::__construct();
53
 
54
  register_activation_hook( __FILE__, array( $this, 'onWpActivatePlugin' ) );
55
  register_deactivation_hook( __FILE__, array( $this, 'onWpDeactivatePlugin' ) );
56
  // register_uninstall_hook( __FILE__, array( &$this, 'onWpUninstallPlugin' ) );
57
-
 
58
  self::$PLUGIN_NAME = basename(__FILE__);
59
  self::$PLUGIN_PATH = plugin_basename( dirname(__FILE__) );
60
- self::$PLUGIN_DIR = WP_PLUGIN_DIR.WORPIT_DS.self::$PLUGIN_PATH.WORPIT_DS;
 
61
  self::$PLUGIN_URL = plugins_url( '/', __FILE__ ) ;
62
  self::$OPTION_PREFIX = self::OptionPrefix;
63
-
64
- $this->m_sParentMenuIdSuffix = 'wpsf';
65
 
 
 
 
 
 
 
66
  $this->override();
67
 
68
- if ( self::getOption( 'enable_firewall' ) == 'Y' ) {
69
- require_once( dirname(__FILE__).'/src/icwp-firewall-processor.php' );
70
- $this->runFirewallProcess();
71
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
- }//__construct
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
  protected function override() {
76
  if ( is_file( self::$PLUGIN_DIR . 'forceOff' ) ) {
77
- self::updateOption( 'enable_firewall', 'N' );
 
 
 
 
78
  }
79
  else if ( is_file( self::$PLUGIN_DIR . 'forceOn' ) ) {
80
- self::updateOption( 'enable_firewall', 'Y' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
 
 
 
 
 
 
 
 
 
 
 
82
  }
83
 
84
  /**
85
- * Updates the current log data with new data.
86
- *
87
- * @param array $inaNewLogData
88
  * @return boolean
89
  */
90
- protected function updateLogStore( $inaNewLogData ) {
91
 
92
- if ( self::getOption( 'enable_firewall_log' ) != 'Y' ) {
 
 
 
93
  return true;
94
  }
95
 
96
- //Save the firewall log
97
- $aFullLog = self::getOption( 'firewall_log' );
98
- if ( !$aFullLog ) {
99
- $aFullLog = array();
 
 
 
 
 
 
 
 
 
 
100
  }
101
- return self::updateOption( 'firewall_log', array_merge( $inaNewLogData, $aFullLog ) );
102
  }
103
 
104
  /**
105
- * Should be called from the constructor so as to ensure it is called as early as possible.
106
  *
107
- * @param array $inaNewLogData
 
108
  * @return boolean
109
  */
110
- public function runFirewallProcess() {
 
 
111
 
112
- $oFP = self::getOption( 'firewall_processor', $oFP );
113
- if ( empty( $oFP ) ) {
114
-
115
- //collect up all the settings to pass to the processor
116
- $aSettingSlugs = array(
117
- 'block_wplogin_access',
118
- 'block_dir_traversal',
119
- 'block_sql_queries',
120
- 'block_wordpress_terms',
121
- 'block_field_truncation',
122
- 'block_exe_file_uploads',
123
- 'block_leading_schema'
124
- );
125
- $aBlockSettings = array();
126
- foreach( $aSettingSlugs as $sSetting ) {
127
- $aBlockSettings[ $sSetting ] = self::getOption( $sSetting ) == 'Y';
 
 
 
 
 
 
 
 
128
  }
129
- $aIpWhitelist = self::getOption( 'ips_whitelist' );
130
- if ( empty($aIpWhitelist) ) {
131
- $aIpWhitelist = array();
 
 
 
 
132
  }
133
- $aIpBlacklist = self::getOption( 'ips_blacklist' );
134
- if ( empty($aIpBlacklist) ) {
135
- $aIpBlacklist = array();
 
 
 
 
 
 
 
 
 
136
  }
137
- $sBlockResponse = self::getOption( 'block_response' );
138
- $oFP = new ICWP_FirewallProcessor( $aBlockSettings, $aIpWhitelist, $aIpBlacklist, $sBlockResponse );
139
-
140
- self::updateOption( 'firewall_processor', $oFP );
141
-
142
- } else {
143
- $oFP->reset();
144
  }
 
 
 
 
145
 
146
- $fFirewallBlockUser = !$oFP->doFirewallCheck();
 
 
 
 
 
 
 
 
 
147
 
148
- if ( $fFirewallBlockUser ) {
149
- switch( $sBlockResponse ) {
150
- case 'redirect_home':
151
- $oFP->logWarning(
152
- sprintf( 'Firewall Block: Visitor was sent HOME: %s', home_url() )
153
- );
154
- break;
155
- case 'redirect_404':
156
- $oFP->logWarning(
157
- sprintf( 'Firewall Block: Visitor was sent 404: %s', home_url().'/404?testfirewall' )
158
- );
159
- break;
160
- case 'redirect_die':
161
- $oFP->logWarning(
162
- sprintf( 'Firewall Block: Visitor connection was killed with %s', 'die()' )
163
- );
164
- break;
165
- }
166
- $this->updateLogStore( $oFP->getLog() );
 
 
 
167
 
168
- if ( self::getOption( 'block_send_email' ) === 'Y' ) {
169
- $sEmail = get_option('admin_email');
170
- if ( is_email( $sEmail ) ) {
171
- $oFP->sendBlockEmail( $sEmail );
 
 
 
 
 
 
 
172
  }
173
  }
174
- $oFP->doFirewallBlock();
175
-
176
  }
177
- else {
178
- $this->updateLogStore( $oFP->getLog() );
 
 
 
 
 
 
 
 
 
 
179
  }
180
  }
181
 
182
- protected function initPluginOptions() {
 
 
 
 
 
 
183
 
184
- $this->m_aPluginOptions_Base = array(
185
- 'section_title' => 'WordPress Firewall Options',
186
- 'section_options' => array(
187
- array( 'enable_firewall', '', 'N', 'checkbox', 'Enable Firewall', 'Completely turn on/off the firewall', 'Regardless of any settings anywhere else, this option will turn off the whole firewall, or enable your desired options below.' ),
188
- array( 'enable_firewall_log', '', 'N', 'checkbox', 'Firewall Logging', 'Turn on a detailed Firewall Log', 'Will log every visit to the site and how the firewall processes it. Not recommended to leave on unless you want to debug something and check the firewall is working as you expect.' )
189
- )
190
- );
191
- $this->m_aPluginOptions_BlockTypesSection = array(
192
- 'section_title' => 'Firewall Blocking Options',
193
- 'section_options' => array(
194
- array(
195
- 'block_wplogin_access',
196
- '',
197
- 'N',
198
- 'checkbox',
199
- 'Login Access',
200
- 'Block WP Login Access',
201
- 'This will block access to the WordPress Login (wp-login.php) except to IP Addresses on the whitelist. If the IP whitelist is empty, this setting is ignored (so you do not lock yourself out!)'
202
- ),
203
- array(
204
- 'block_dir_traversal',
205
- '',
206
- 'N',
207
- 'checkbox',
208
- 'Directory Traversals',
209
- 'Block Directory Traversals',
210
- 'This will block directory traversal paths in in application parameters (../, ../../etc/passwd, etc.)'
211
- ),
212
- array(
213
- 'block_sql_queries',
214
- '',
215
- 'N',
216
- 'checkbox',
217
- 'SQL Queries',
218
- 'Block SQL Queries',
219
- 'This will block in application parameters (union select, concat(, /**/, etc.).'
220
- ),
221
- array(
222
- 'block_wordpress_terms',
223
- '',
224
- 'N',
225
- 'checkbox',
226
- 'WordPress Terms',
227
- 'Block WordPress Specific Terms',
228
- 'This will block WordPress specific terms in application parameters (wp_, user_login, etc.).'
229
- ),
230
- array(
231
- 'block_field_truncation',
232
- '',
233
- 'N',
234
- 'checkbox',
235
- 'Field Truncation',
236
- 'Block Field Truncation Attacks',
237
- 'This will block field truncation attacks in application parameters.'
238
- ),
239
- array(
240
- 'block_exe_file_uploads',
241
- '',
242
- 'N',
243
- 'checkbox',
244
- 'Exe File Uploads',
245
- 'Block Executable File Uploads',
246
- 'This will block executable file uploads (.php, .exe, etc.).'
247
- ),
248
- array(
249
- 'block_leading_schema',
250
- '',
251
- 'N',
252
- 'checkbox',
253
- 'Leading Schemas',
254
- 'Block Leading Schemas (HTTPS / HTTP)',
255
- 'This will block leading schemas http:// and https:// in application parameters (off by default; may cause problems with many plugins).'
256
- )
257
- ),
258
- );
259
- $this->m_aRedirectOptions = array( 'select',
260
- array( 'redirect_die', 'Die' ),
261
- array( 'redirect_home', 'Redirect To Home Page' ),
262
- array( 'redirect_404', 'Return 404' ),
263
- );
264
- $this->m_aPluginOptions_BlockSection = array(
265
- 'section_title' => 'Choose Firewall Block Response',
266
- 'section_options' => array(
267
- array( 'block_response', '', 'none', $this->m_aRedirectOptions, 'Block Response', 'Choose how the firewall responds when it blocks a request', '' ),
268
- array( 'block_send_email', '', 'N', 'checkbox', 'Send Email Report', 'When a visitor is blocked it will send an email to the blog admin', 'Use with caution - if you get hit by automated bots you may send out too many emails and you could get blocked by your host.' ),
269
- )
270
- );
271
-
272
- $this->m_aPluginOptions_WhitelistSection = array(
273
- 'section_title' => 'Choose IP Addresses To Whitelist',
274
- 'section_options' => array(
275
- array(
276
- 'ips_whitelist',
277
- '',
278
- 'none',
279
- 'ip_addresses',
280
- 'Whitelist IP Addresses',
281
- 'Choose IP Addresses that are never subjected to Firewall Rules',
282
- 'Take a new line per address. Each IP Address must be valid and will be checked.'
283
- )
284
- )
285
- );
286
 
287
- $this->m_aPluginOptions_BlacklistSection = array(
288
- 'section_title' => 'Choose IP Addresses To Blacklist',
289
- 'section_options' => array(
290
- array(
291
- 'ips_blacklist',
292
- '',
293
- 'none',
294
- 'ip_addresses',
295
- 'Blacklist IP Addresses',
296
- 'Choose IP Addresses that are always blocked access to the site',
297
- 'Take a new line per address. Each IP Address must be valid and will be checked.'
298
- )
299
- )
300
- );
301
 
302
- $this->m_aAllPluginOptions = array(
303
- &$this->m_aPluginOptions_Base,
304
- &$this->m_aPluginOptions_BlockSection,
305
- &$this->m_aPluginOptions_WhitelistSection,
306
- &$this->m_aPluginOptions_BlacklistSection,
307
- &$this->m_aPluginOptions_BlockTypesSection
308
- );
309
 
310
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
 
312
- }//initPluginOptions
 
313
 
314
- public function onWpPluginsLoaded() {
315
- parent::onWpPluginsLoaded();
316
- }//onWpPluginsLoaded
 
 
 
317
 
318
- public function onWpInit() {
319
- parent::onWpInit();
320
- add_action( 'wp_enqueue_scripts', array( $this, 'onWpPrintStyles' ) );
321
- add_action( 'wp_enqueue_scripts', array( $this, 'onWpEnqueueScripts' ) );
322
- }//onWpInit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
323
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  public function onWpAdminInit() {
325
  parent::onWpAdminInit();
326
-
327
  // If it's a plugin admin page, we do certain things we don't do anywhere else.
328
  if ( $this->isIcwpPluginAdminPage()) {
329
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueueBootstrapAdminCss' ), 99 );
330
  }
331
 
332
- // This is only done on WP Admin loads so as not to affect the front-end.
333
- $this->filterIpsLists();
 
 
334
 
335
  //Multilingual support.
336
- load_plugin_textdomain( 'hlt-wordpress-bootstrap-css', false, basename( dirname( __FILE__ ) ) . '/languages' );
337
  }
338
 
339
- protected function createPluginSubMenuItems(){
 
 
 
 
 
340
  $this->m_aPluginMenu = array(
341
  //Menu Page Title => Menu Item name, page ID (slug), callback function for this page - i.e. what to do/load.
342
- $this->getSubmenuPageTitle( 'Firewall' ) => array( 'Firewall', $this->getSubmenuId('firewall-config'), 'onDisplayFirewallConfig' ),
343
- $this->getSubmenuPageTitle( 'Log' ) => array( 'Log', $this->getSubmenuId('firewall-log'), 'onDisplayFirewallLog' )
 
 
 
 
344
  );
345
- }//createPluginSubMenuItems
346
-
347
- /**
348
- * Handles the upgrade from version 1 to version 2 of Twitter Bootstrap as well as any other plugin upgrade
349
- */
350
  protected function handlePluginUpgrade() {
 
351
 
352
- $sCurrentPluginVersion = self::getOption( 'current_plugin_version' );
353
 
354
  if ( $sCurrentPluginVersion !== self::$VERSION && current_user_can( 'manage_options' ) ) {
 
 
 
 
 
 
 
 
 
 
355
 
356
- //Do any upgrade handling here.
 
357
 
358
- //Set the flag so that this update handler isn't run again for this version.
359
- self::updateOption( 'current_plugin_version', self::$VERSION );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  }//if
361
 
362
  //Someone clicked the button to acknowledge the update
363
  if ( isset( $_POST[self::OptionPrefix.'hide_update_notice'] ) && isset( $_POST['user_id'] ) ) {
364
- $result = update_user_meta( $_POST['user_id'], self::OptionPrefix.'current_version', self::$VERSION );
365
-
366
  if ( $this->isShowMarketing() ) {
367
  wp_redirect( admin_url( "admin.php?page=".$this->getFullParentMenuId() ) );
368
  }
@@ -372,225 +624,552 @@ class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Base_Plugin {
372
  }
373
  }
374
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  public function onWpAdminNotices() {
376
-
377
- //Do we have admin priviledges?
378
  if ( !current_user_can( 'manage_options' ) ) {
379
  return;
380
  }
381
- $this->adminNoticeVersionUpgrade();
382
  $this->adminNoticeOptionsUpdated();
 
 
 
 
383
  }
384
 
385
  /**
386
- * Shows the update notification - will bail out if the current user is not an admin
 
387
  */
388
- private function adminNoticeVersionUpgrade() {
389
-
390
- $oCurrentUser = wp_get_current_user();
391
- if ( !($oCurrentUser instanceof WP_User) ) {
392
  return;
393
  }
394
- $nUserId = $oCurrentUser->ID;
395
- $sCurrentVersion = get_user_meta( $nUserId, self::OptionPrefix.'current_version', true );
396
- // A guard whereby if we can't ever get a value for this meta, it means we can never set it.
397
- // If we can never set it, we shouldn't force the Ads on those users who can't get rid of it.
398
- if ( $sCurrentVersion === false ) { //the value has never been set, or it's been installed for the first time.
399
- $result = update_user_meta( $nUserId, self::OptionPrefix.'current_version', self::$VERSION );
400
- return; //meaning we don't show the update notice upon new installations and for those people who can't set the version in their meta.
401
- }
402
-
403
- if ( $sCurrentVersion !== self::$VERSION ) {
404
-
405
- $sRedirectPage = isset( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : 'index.php';
406
- ob_start();
407
- ?>
408
- <style>
409
- a#fromIcwp { padding: 0 5px; border-bottom: 1px dashed rgba(0,0,0,0.1); color: blue; font-weight: bold; }
410
- </style>
411
- <form id="IcwpUpdateNotice" method="post" action="admin.php?page=<?php echo $this->getSubmenuId('firewall-config'); ?>">
412
- <input type="hidden" value="<?php echo $sRedirectPage; ?>" name="redirect_page" id="redirect_page">
413
- <input type="hidden" value="1" name="<?php echo self::OptionPrefix; ?>hide_update_notice" id="<?php echo self::OptionPrefix; ?>hide_update_notice">
414
- <input type="hidden" value="<?php echo $nUserId; ?>" name="user_id" id="user_id">
415
- <h4 style="margin:10px 0 3px;">WordPress Firewall plugin has been updated- there may or may not be <a href="http://icwp.io/1v" id="fromIcwp" title="Twitter Bootstrap Plugin Shortcodes" target="_blank">updates to shortcodes</a> or the Bootstrap CSS may have changed quite a bit.</h4>
416
- <input type="submit" value="Show me and hide this notice." name="submit" class="button" style="float:left; margin-bottom:10px;">
417
- <div style="clear:both;"></div>
418
- </form>
419
- <?php
420
- $sNotice = ob_get_contents();
421
- ob_end_clean();
422
-
423
- $this->getAdminNotice( $sNotice, 'updated', true );
424
- }
425
 
426
- }//adminNoticeVersionUpgrade
427
-
428
- private function adminNoticeOptionsUpdated() {
429
 
430
- $sAdminFeedbackNotice = $this->getOption( 'feedback_admin_notice' );
431
- if ( !empty( $sAdminFeedbackNotice ) ) {
432
- $sNotice = '<p>'.$sAdminFeedbackNotice.'</p>';
433
- $this->getAdminNotice( $sNotice, 'updated', true );
434
- $this->updateOption( 'feedback_admin_notice', '' );
 
 
 
 
 
 
 
 
435
  }
436
-
437
- }//adminNoticeOptionsUpdated
 
 
 
 
 
 
 
 
 
 
 
 
438
 
439
  public function onDisplayMainMenu() {
440
 
441
- //populates plugin options with existing configuration
442
- $this->readyAllPluginOptions();
 
 
 
 
 
443
 
444
  $aData = array(
445
  'plugin_url' => self::$PLUGIN_URL,
 
446
  'aAllOptions' => $aAvailableOptions,
447
  'fShowAds' => $this->isShowMarketing(),
448
-
449
- 'fFirewallOn' => self::getOption( 'enable_firewall' ) == 'Y',
450
- 'fFirewallLogOn' => self::getOption( 'enable_firewall_log' )== 'Y',
451
- 'sBlockResponse' => self::getOption( 'block_response' ),
452
- 'fBlockSendEmail' => self::getOption( 'block_send_email' ) == 'Y',
453
- 'aIpWhitelist' => self::getOption( 'ips_whitelist' ),
454
- 'aIpBlacklist' => self::getOption( 'ips_blacklist' ),
455
- 'fBlockLogin' => self::getOption( 'block_wplogin_access' )== 'Y',
456
- 'fBlockDirTrav' => self::getOption( 'block_dir_traversal' )== 'Y',
457
- 'fBlockSql' => self::getOption( 'block_sql_queries' )== 'Y',
458
- 'fBlockWpTerms' => self::getOption( 'block_wordpress_terms' )== 'Y',
459
- 'fBlockFieldTrun' => self::getOption( 'block_field_truncation' )== 'Y',
460
- 'fBlockExeFile' => self::getOption( 'block_exe_file_uploads' )== 'Y',
461
- 'fBlockSchema' => self::getOption( 'block_leading_schema' )== 'Y'
462
  );
463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  $this->display( 'icwp_'.$this->m_sParentMenuIdSuffix.'_index', $aData );
465
  }
466
 
467
- public function onDisplayFirewallConfig() {
468
-
469
- //populates plugin options with existing configuration
470
- $this->readyAllPluginOptions();
 
471
 
472
- //Specify what set of options are available for this page
473
- $aAvailableOptions = array(
474
- &$this->m_aPluginOptions_Base,
475
- &$this->m_aPluginOptions_BlockSection,
476
- &$this->m_aPluginOptions_WhitelistSection,
477
- &$this->m_aPluginOptions_BlacklistSection,
478
- &$this->m_aPluginOptions_BlockTypesSection
479
- );
480
 
481
- $sAllFormInputOptions = $this->collateAllFormInputsForAllOptions( $aAvailableOptions );
482
  $aData = array(
483
  'plugin_url' => self::$PLUGIN_URL,
484
  'var_prefix' => self::OptionPrefix,
 
 
 
485
  'fShowAds' => $this->isShowMarketing(),
486
- 'aAllOptions' => $aAvailableOptions,
487
- 'all_options_input' => $sAllFormInputOptions,
488
- 'nonce_field' => $this->getSubmenuId('firewall').'_config',
489
- 'form_action' => 'admin.php?page='.$this->getSubmenuId('firewall-config'),
490
  );
491
 
492
- $this->display( 'icwp_wpsf_firewall_config_index', $aData );
493
  }
494
 
495
- public function onDisplayFirewallLog() {
 
 
 
 
 
496
 
497
- $sAllFormInputOptions = $this->collateAllFormInputsForAllOptions( $aAvailableOptions );
 
 
498
  $aData = array(
499
  'plugin_url' => self::$PLUGIN_URL,
500
  'var_prefix' => self::OptionPrefix,
501
- 'firewall_log' => self::getOption( 'firewall_log' ),
502
  'fShowAds' => $this->isShowMarketing(),
503
- 'nonce_field' => $this->getSubmenuId('firewall').'_log',
504
- 'form_action' => 'admin.php?page='.$this->getSubmenuId('firewall-log'),
 
 
505
  );
506
-
507
- $this->display( 'icwp_wpsf_firewall_log_index', $aData );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
508
  }
509
 
510
  protected function handlePluginFormSubmit() {
511
 
512
- if ( !isset( $_POST['icwp_plugin_form_submit'] ) ) {
513
- return;
 
 
 
 
 
 
 
 
 
514
  }
515
 
516
  if ( isset( $_GET['page'] ) ) {
517
  switch ( $_GET['page'] ) {
518
- case $this->getSubmenuId( 'firewall-config' ):
519
- $this->handleSubmit_FirewallConfigOptions();
520
  break;
521
- case $this->getSubmenuId( 'firewall-log' ):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
522
  $this->handleSubmit_FirewallLog();
523
  break;
 
 
 
524
  }
525
  }
 
 
 
 
 
526
 
527
- if ( !self::$m_fUpdateSuccessTracker ) {
528
- self::updateOption( 'feedback_admin_notice', 'Updating Settings <strong>Failed</strong>.' );
 
 
 
529
  }
530
  else {
531
- self::updateOption( 'feedback_admin_notice', 'Updating Settings <strong>Succeeded</strong>.' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  }
533
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
534
  }
535
 
536
- protected function handleSubmit_FirewallConfigOptions() {
537
  //Ensures we're actually getting this request from WP.
538
- check_admin_referer( $this->getSubmenuId('firewall').'_config' );
 
 
 
 
539
 
540
- self::updateOption( 'firewall_processor', false );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
541
 
542
  if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
543
  return;
544
  }
545
- $this->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
 
 
 
546
  }
547
 
548
- protected function handleSubmit_FirewallLog() {
549
  //Ensures we're actually getting this request from WP.
550
- check_admin_referer( $this->getSubmenuId('firewall').'_log' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
551
 
552
  // At the time of writing the page only has 1 form submission item - clear log
553
- self::updateOption( 'firewall_log', array() );
554
- wp_safe_redirect( admin_url( "admin.php?page=".$this->getSubmenuId('firewall-log') ) ); //means no admin message is displayed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
555
  exit();
556
  }
557
 
558
- public function onWpPrintStyles() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
559
  }
560
 
561
- public function onWpEnqueueScripts() {
 
 
 
 
562
  }
563
 
564
- public function onWpPluginActionLinks( $inaLinks, $insFile ) {
 
565
  if ( $insFile == plugin_basename( __FILE__ ) ) {
566
- $sSettingsLink = '<a href="'.admin_url( "admin.php" ).'?page='.$this->getSubmenuId('firewall-config').'">' . 'Firewall' . '</a>';
567
- array_unshift( $inaLinks, $sSettingsLink );
 
 
 
 
 
 
 
 
568
  }
569
- return $inaLinks;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
570
  }
571
 
572
  protected function deleteAllPluginDbOptions() {
573
 
574
  parent::deleteAllPluginDbOptions();
575
-
576
  if ( !current_user_can( 'manage_options' ) ) {
577
  return;
578
  }
579
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
580
  }
581
 
582
  public function onWpDeactivatePlugin() {
583
-
584
- if ( $this->getOption('delete_on_deactivate') == 'Y' ) {
585
  $this->deleteAllPluginDbOptions();
586
  }
587
-
588
- $this->deleteOption( 'current_plugin_version' );
589
- $this->deleteOption( 'feedback_admin_notice' );
590
-
591
- }//onWpDeactivatePlugin
592
-
593
- public function onWpActivatePlugin() { }
594
 
595
  public function enqueueBootstrapAdminCss() {
596
  wp_register_style( 'worpit_bootstrap_wpadmin_css', $this->getCssUrl( 'bootstrap-wpadmin.css' ), false, self::$VERSION );
@@ -599,52 +1178,144 @@ class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Base_Plugin {
599
  wp_enqueue_style( 'worpit_bootstrap_wpadmin_css_fixes' );
600
  }
601
 
602
- /**
603
- *
604
- */
605
- protected function filterIpsLists() {
606
 
607
- $aWhitelistIps = self::getOption('ips_whitelist');
608
- if ( is_array($aWhitelistIps) ) {
609
- $aWhitelistIps = array();
610
- self::updateOption( 'ips_whitelist', $aWhitelistIps );
 
611
  }
612
- $aWhitelistIpsFiltered = apply_filters( 'icwp_simple_firewall_whitelist_ips', $aWhitelistIps );
613
- $aDiff = array_diff( $aWhitelistIpsFiltered, $aWhitelistIps );
614
- if ( !empty( $aDiff ) ) {
615
- self::updateOption( 'ips_whitelist', $this->verifyIpAddressList( $aWhitelistIpsFiltered ) );
616
  }
 
 
 
 
 
617
 
618
- $aBlacklistIps = self::getOption('ips_blacklist');
619
- if ( !is_array($aBlacklistIps) ) {
620
- $aBlacklistIps = array();
621
- self::updateOption( 'ips_blacklist', $aBlacklistIps );
 
622
  }
623
- $aBlacklistIpsFiltered = apply_filters( 'icwp_simple_firewall_blacklist_ips', $aBlacklistIps );
624
- $aDiff = array_diff( $aBlacklistIpsFiltered, $aBlacklistIps );
625
- if ( !empty( $aDiff ) ) {
626
- self::updateOption( 'ips_blacklist', $this->verifyIpAddressList( $aBlacklistIpsFiltered ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
627
  }
628
  }
629
 
630
- protected function verifyIpAddressList( $inaIpList ) {
631
-
632
- if ( function_exists('filter_var') && defined( FILTER_VALIDATE_IP ) ) {
633
- $fUseFilter = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
634
  }
635
- else {
636
- $fUseFilter = false;
 
 
 
 
 
 
 
 
 
637
  }
638
-
639
- if ( !class_exists('ICWP_DataProcessor') ) {
640
- require_once ( dirname(__FILE__).'/src/icwp-data-processor.php' );
 
 
 
 
641
  }
642
- foreach( $inaIpList as $sKey => $sIpAddress ) {
643
- if ( !ICWP_DataProcessor::Verify_Ip( $sIpAddress, $fUseFilter ) ) {
644
- unset( $inaIpList[ $sKey ] );
645
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
  }
647
- return $inaIpList;
648
  }
649
  }
650
 
1
  <?php
2
  /*
3
  Plugin Name: WordPress Simple Firewall
4
+ Plugin URI: http://icwp.io/2f
5
  Description: A Simple WordPress Firewall
6
+ Version: 1.9.2
7
  Author: iControlWP
8
+ Author URI: http://icwp.io/2e
9
  */
10
 
11
  /**
27
  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
30
  */
31
 
32
  require_once( dirname(__FILE__).'/src/icwp-plugins-base.php' );
33
+ require_once( dirname(__FILE__).'/src/icwp-data-processor.php' );
34
 
35
  if ( !class_exists('ICWP_Wordpress_Simple_Firewall') ):
36
 
37
  class ICWP_Wordpress_Simple_Firewall extends ICWP_WPSF_Base_Plugin {
38
 
39
+ const InputPrefix = 'icwp_wpsf_';
40
+ const OptionPrefix = 'icwp_wpsf_'; //ALL database options use this as the prefix.
41
+ const AdminAccessKeyCookieName = "icwp_wpsf_aakcook";
42
+
43
+ /**
44
+ * Should be updated each new release.
45
+ * @var string
46
+ */
47
+ static public $VERSION = '1.9.2';
48
+
49
+ /**
50
+ * @var ICWP_OptionsHandler_Wpsf
51
+ */
52
+ protected $m_oWpsfOptions;
53
+
54
+ /**
55
+ * @var ICWP_OptionsHandler_Firewall
56
+ */
57
+ protected $m_oFirewallOptions;
58
+
59
+ /**
60
+ * @var ICWP_OptionsHandler_LoginProtect
61
+ */
62
+ protected $m_oLoginProtectOptions;
63
+
64
+ /**
65
+ * @var ICWP_OptionsHandler_CommentsFilter
66
+ */
67
+ protected $m_oCommentsFilterOptions;
68
+
69
+ /**
70
+ * @var ICWP_OptionsHandler_Lockdown
71
+ */
72
+ protected $m_oLockdownOptions;
73
+
74
+ /**
75
+ * @var ICWP_OptionsHandler_AutoUpdates
76
+ */
77
+ protected $m_oAutoUpdatesOptions;
78
+
79
+ /**
80
+ * @var ICWP_FirewallProcessor
81
+ */
82
+ protected $m_oFirewallProcessor;
83
+
84
+ /**
85
+ * @var ICWP_LoginProtectProcessor
86
+ */
87
+ protected $m_oLoginProtectProcessor;
88
+
89
+ /**
90
+ * @var ICWP_CommentsFilterProcessor
91
+ */
92
+ protected $m_oCommentsFilterProcessor;
93
+
94
+ /**
95
+ * @var ICWP_LockdownProcessor
96
+ */
97
+ protected $m_oLockdownProcessor;
98
+
99
+ /**
100
+ * @var ICWP_AutoUpdatesProcessor
101
+ */
102
+ protected $m_oAutoUpdatesProcessor;
103
 
104
+ /**
105
+ * @var ICWP_LoggingProcessor
106
+ */
107
+ protected $m_oLoggingProcessor;
108
 
109
+ /**
110
+ * @var ICWP_EmailProcessor
111
+ */
112
+ protected $m_oEmailProcessor;
 
 
113
 
114
  public function __construct() {
115
+
116
+ $this->m_fNetworkAdminOnly = true;
117
  parent::__construct();
118
 
119
  register_activation_hook( __FILE__, array( $this, 'onWpActivatePlugin' ) );
120
  register_deactivation_hook( __FILE__, array( $this, 'onWpDeactivatePlugin' ) );
121
  // register_uninstall_hook( __FILE__, array( &$this, 'onWpUninstallPlugin' ) );
122
+
123
+ self::$PLUGIN_HUMAN_NAME = "WordPress Simple Firewall";
124
  self::$PLUGIN_NAME = basename(__FILE__);
125
  self::$PLUGIN_PATH = plugin_basename( dirname(__FILE__) );
126
+ self::$PLUGIN_FILE = plugin_basename(__FILE__);
127
+ self::$PLUGIN_DIR = WP_PLUGIN_DIR.ICWP_DS.self::$PLUGIN_PATH.ICWP_DS;
128
  self::$PLUGIN_URL = plugins_url( '/', __FILE__ ) ;
129
  self::$OPTION_PREFIX = self::OptionPrefix;
 
 
130
 
131
+ $this->m_sParentMenuIdSuffix = 'wpsf';
132
+ // loads the base plugin options from 1 db call
133
+ $this->loadOptionsHandler( 'Wpsf' );
134
+ $this->m_fAutoPluginUpgrade = false && $this->m_oWpsfOptions->getOpt( 'enable_auto_plugin_upgrade' ) == 'Y';
135
+
136
+ // checks for filesystem based firewall overrides
137
  $this->override();
138
 
139
+ if ( $this->getIsMainFeatureEnabled( 'firewall' ) ) {
140
+ add_action( 'plugins_loaded', array( $this, 'runFirewallProcess' ), 1 );
 
141
  }
142
+
143
+ if ( $this->getIsMainFeatureEnabled( 'login_protect' ) ) {
144
+ add_action( 'plugins_loaded', array( $this, 'runLoginProtect' ), 1 );
145
+ }
146
+
147
+ if ( $this->getIsMainFeatureEnabled( 'comments_filter' ) ) {
148
+ add_action( 'plugins_loaded', array( $this, 'runCommentsFilter' ), 1 );
149
+ }
150
+
151
+ if ( $this->getIsMainFeatureEnabled( 'lockdown' ) ) {
152
+ add_action( 'plugins_loaded', array( $this, 'runLockdown' ), 1 );
153
+ }
154
+
155
+ if ( $this->getIsMainFeatureEnabled( 'autoupdates' ) ) {
156
+ add_action( 'plugins_loaded', array( $this, 'runAutoUpdates' ), 1 );
157
+ }
158
+
159
+ add_action( 'in_plugin_update_message-'.self::$PLUGIN_FILE, array( $this, 'onWpPluginUpdateMessage' ) );
160
 
161
+ if ( isset( $_GET['turnoffperm'] ) ) {
162
+ $this->setPermissionToSubmit( false );
163
+ }
164
+ }
165
+
166
+ public function removePluginConflicts() {
167
+ if ( class_exists('AIO_WP_Security') && isset( $GLOBALS['aio_wp_security'] ) ) {
168
+ remove_action( 'init', array( $GLOBALS['aio_wp_security'], 'wp_security_plugin_init'), 0 );
169
+ }
170
+ }
171
+
172
+ /**
173
+ */
174
+ public function onWpInit() {
175
+ parent::onWpInit();
176
+ add_action( 'plugin_action_links', array( $this, 'onWpPluginActionLinks' ), 10, 4 );
177
+ }
178
+
179
+ /**
180
+ * @param array $aPlugins
181
+ * @return unknown
182
+ */
183
+ public function hide_plugin( $inaPlugins ) {
184
+ foreach ( $inaPlugins as $sSlug => $aData ) {
185
+ if ( strpos( $sSlug, 'icwp-wpsf.php' ) !== false ) {
186
+ unset( $inaPlugins[$sSlug] );
187
+ }
188
+ }
189
+ return $inaPlugins;
190
+ }
191
 
192
  protected function override() {
193
  if ( is_file( self::$PLUGIN_DIR . 'forceOff' ) ) {
194
+ $this->setSharedOption( 'enable_firewall', 'N' );
195
+ $this->setSharedOption( 'enable_login_protect', 'N' );
196
+ $this->setSharedOption( 'enable_comments_filter', 'N' );
197
+ $this->setSharedOption( 'enable_autoupdates', 'N' );
198
+ $this->setSharedOption( 'enable_admin_access_restriction', 'N' );
199
  }
200
  else if ( is_file( self::$PLUGIN_DIR . 'forceOn' ) ) {
201
+ $this->setSharedOption( 'enable_firewall', 'Y' );
202
+ $this->setSharedOption( 'enable_login_protect', 'Y' );
203
+ $this->setSharedOption( 'enable_comments_filter', 'Y' );
204
+ $this->setSharedOption( 'enable_autoupdates', 'Y' );
205
+ $this->setSharedOption( 'enable_admin_access_restriction', 'Y' );
206
+ }
207
+ else {
208
+ return true;
209
+ }
210
+ $this->resetFirewallProcessor();
211
+ $this->resetLoginProcessor();
212
+ }
213
+
214
+ protected function genSecretKey() {
215
+ $sKey = $this->m_oWpsfOptions->getOpt( 'secret_key' );
216
+ if ( empty( $sKey ) ) {
217
+ $sKey = md5( mt_rand() );
218
+ $this->m_oWpsfOptions->setOpt( 'secret_key', $sKey );
219
  }
220
+ return $sKey;
221
+ }
222
+
223
+ protected function getFeaturesMap() {
224
+ return array(
225
+ 'firewall' => 'Firewall',
226
+ 'login_protect' => 'LoginProtect',
227
+ 'comments_filter' => 'CommentsFilter',
228
+ 'lockdown' => 'Lockdown',
229
+ 'autoupdates' => 'AutoUpdates'
230
+ );
231
  }
232
 
233
  /**
234
+ * @param string $insFeature - firewall, login_protect, comments_filter, lockdown
 
 
235
  * @return boolean
236
  */
237
+ public function getIsMainFeatureEnabled( $insFeature ) {
238
 
239
+ if ( is_file( self::$PLUGIN_DIR . 'forceOff' ) ) {
240
+ return false;
241
+ }
242
+ else if ( is_file( self::$PLUGIN_DIR . 'forceOn' ) ) {
243
  return true;
244
  }
245
 
246
+ $aFeatures = $this->getFeaturesMap();
247
+
248
+ switch ( $insFeature ) {
249
+ case 'admin_access':
250
+ $fEnabled = $this->m_oWpsfOptions->getOpt( 'enable_admin_access_restriction' ) == 'Y';
251
+ break;
252
+ default:
253
+ if ( array_key_exists( $insFeature, $aFeatures ) ) {
254
+ $fEnabled = $this->m_oWpsfOptions->getOpt( 'enable_'.$insFeature ) == 'Y';
255
+ }
256
+ else {
257
+ $fEnabled = false;
258
+ }
259
+ break;
260
  }
261
+ return $fEnabled;
262
  }
263
 
264
  /**
265
+ * This is necessary because we store these values in several places and we need to always keep it in sync.
266
  *
267
+ * @param string $sFeature
268
+ * @param boolean $infEnabled
269
  * @return boolean
270
  */
271
+ public function setSharedOption( $insOption, $inmValue ) {
272
+
273
+ $aFeatures = $this->getFeaturesMap();
274
 
275
+ $sFeature = str_replace( 'enable_', '', $insOption );
276
+ if ( !array_key_exists( $sFeature, $aFeatures ) ) {
277
+ return;
278
+ }
279
+
280
+ $this->loadOptionsHandler( $aFeatures[$sFeature] );
281
+ $sOptions = 'm_o'.$aFeatures[$sFeature].'Options';// e.g. m_oFirewallOptions
282
+ $this->{$sOptions}->setOpt( $insOption, $inmValue );
283
+ $this->m_oWpsfOptions->setOpt( $insOption, $inmValue );
284
+ }
285
+
286
+ /**
287
+ * Updates the current log data with new data.
288
+ *
289
+ * @param array $inaNewLogData
290
+ * @return boolean
291
+ */
292
+ protected function updateLogStore() {
293
+
294
+ if ( isset( $this->m_oFirewallProcessor ) && is_object( $this->m_oFirewallProcessor ) && $this->getIsMainFeatureEnabled( 'firewall' ) ) {
295
+ $aLogData = $this->m_oFirewallProcessor->flushLogData();
296
+ if ( !is_null( $aLogData ) && !empty( $aLogData ) ) {
297
+ $this->loadProcessor( 'Logging' );
298
+ $this->m_oLoggingProcessor->writeLog( $aLogData );
299
  }
300
+ }
301
+
302
+ if ( isset( $this->m_oLoginProtectProcessor ) && is_object( $this->m_oLoginProtectProcessor ) && $this->getIsMainFeatureEnabled( 'login_protect' ) ) {
303
+ $aLogData = $this->m_oLoginProtectProcessor->flushLogData();
304
+ if ( !is_null( $aLogData ) && !empty( $aLogData ) ) {
305
+ $this->loadProcessor( 'Logging' );
306
+ $this->m_oLoggingProcessor->writeLog( $aLogData );
307
  }
308
+ }
309
+ }
310
+
311
+ protected function loadOptionsHandler( $insOptionHandler, $infFullBuild = false ) {
312
+
313
+ $aAllHandlers = array_values( $this->getFeaturesMap() );
314
+ $aAllHandlers[] = 'Wpsf';
315
+
316
+ // special case
317
+ if ( $insOptionHandler == 'all' ) {
318
+ foreach( $aAllHandlers as $sHandler ) {
319
+ $this->loadOptionsHandler( $sHandler, $infFullBuild );
320
  }
321
+ return;
322
+ }
323
+
324
+ if ( !in_array( $insOptionHandler, $aAllHandlers ) ) {
325
+ wp_die( 'Options handler is not permitted: '.$insOptionHandler );
 
 
326
  }
327
+
328
+ $sOptionsVarName = 'm_o'.$insOptionHandler.'Options'; // e.g. m_oWpsfOptions
329
+ $sSourceFile = dirname(__FILE__).'/src/icwp-optionshandler-'.strtolower($insOptionHandler).'.php'; // e.g. icwp-optionshandler-wpsf.php
330
+ $sClassName = 'ICWP_OptionsHandler_'.$insOptionHandler; // e.g. ICWP_OptionsHandler_Wpsf
331
 
332
+ require_once( $sSourceFile );
333
+ if ( !isset( $this->{$sOptionsVarName} ) ) {
334
+ $this->{$sOptionsVarName} = new $sClassName( self::OptionPrefix, self::$VERSION, $infFullBuild );
335
+ }
336
+ if ( $infFullBuild ) {
337
+ $this->{$sOptionsVarName}->buildOptions();
338
+ }
339
+ }
340
+
341
+ protected function loadProcessor( $insProcessorName, $infReset = false ) {
342
 
343
+ $aAllProcessors = array(
344
+ 'Firewall' => 'firewall',
345
+ 'LoginProtect' => 'login',
346
+ 'CommentsFilter' => 'comments',
347
+ 'Lockdown' => 'lockdown',
348
+ 'AutoUpdates' => 'autoupdates',
349
+ 'Logging' => '',
350
+ 'Email' => ''
351
+ );
352
+
353
+ if ( !array_key_exists( $insProcessorName, $aAllProcessors ) ) {
354
+ wp_die( 'Processor is not permitted here.' );
355
+ }
356
+
357
+ $sProcessorVarName = 'm_o'.$insProcessorName.'Processor'; // e.g. m_oFirewallProcessor
358
+ $sSourceFile = dirname(__FILE__).'/src/icwp-processor-'.strtolower($insProcessorName).'.php'; // e.g. icwp-optionshandler-wpsf.php
359
+ $sClassName = 'ICWP_'.$insProcessorName.'Processor'; // e.g. ICWP_FirewallProcessor
360
+ $sStoredOptionName = $aAllProcessors[$insProcessorName].'_processor'; // e.g. firewall_processor
361
+ $sOptionsHandlerVarName = 'm_o'.$insProcessorName.'Options'; // e.g. m_oFirewallOptions
362
+
363
+ require_once( $sSourceFile );
364
+ if ( empty( $this->{$sProcessorVarName} ) ) {
365
 
366
+ $this->{$sProcessorVarName} = self::getOption( $sStoredOptionName );
367
+ if ( is_object( $this->{$sProcessorVarName} ) && ( $this->{$sProcessorVarName} instanceof $sClassName ) ) {
368
+ $this->{$sProcessorVarName}->reset();
369
+ }
370
+ else {
371
+ $this->{$sProcessorVarName} = new $sClassName();
372
+ // Also loads the options handler where appropriate
373
+ if ( !empty( $aAllProcessors[ $insProcessorName ] ) ) {
374
+ $this->loadOptionsHandler( $insProcessorName );
375
+ // $this->{$sProcessorVarName}->setOptionsHandler( $this->{$sOptionsHandlerVarName} );
376
+ $this->{$sProcessorVarName}->setOptions( $this->{$sOptionsHandlerVarName}->getPluginOptionsValues() );
377
  }
378
  }
 
 
379
  }
380
+ else if ( $infReset ) {
381
+ $this->{$sProcessorVarName}->reset();
382
+ }
383
+ // Now we handle any custom processor stuff
384
+ if ( $insProcessorName == 'LoginProtect' ) {
385
+ $this->m_oLoginProtectProcessor->setSecretKey( $this->genSecretKey() );
386
+ }
387
+ else if ( $insProcessorName == 'Email' ) {
388
+ $this->m_oEmailProcessor->setDefaultRecipientAddress( $this->m_oWpsfOptions->getOpt( 'block_send_email_address' ) );
389
+ $this->m_oEmailProcessor->setThrottleLimit( $this->m_oWpsfOptions->getOpt( 'send_email_throttle_limit' ) );
390
+ $sSiteName = function_exists( 'get_bloginfo' )? get_bloginfo('name') : '';
391
+ $this->m_oEmailProcessor->setSiteName( $sSiteName );
392
  }
393
  }
394
 
395
+ /**
396
+ * Should be called from the constructor so as to ensure it is called as early as possible.
397
+ *
398
+ * @param array $inaNewLogData
399
+ * @return boolean
400
+ */
401
+ public function runFirewallProcess() {
402
 
403
+ if ( is_super_admin() && $this->getOption( 'whitelist_admins' ) == 'Y' ) {
404
+ return;
405
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
+ $this->loadProcessor( 'Firewall' );
408
+ $fFirewallBlockUser = !$this->m_oFirewallProcessor->doFirewallCheck();
 
 
 
 
 
 
 
 
 
 
 
 
409
 
410
+ if ( $fFirewallBlockUser ) {
 
 
 
 
 
 
411
 
412
+ if ( $this->m_oFirewallProcessor->getNeedsEmailHandler() ) {
413
+ $this->loadProcessor( 'Email' );
414
+ $this->m_oFirewallProcessor->setEmailHandler( $this->m_oEmailProcessor );
415
+ $this->m_oFirewallProcessor->doPreFirewallBlock();
416
+ $this->m_oEmailProcessor->store( self::getKey( 'email_processor' ) );
417
+ }
418
+ else {
419
+ $this->m_oFirewallProcessor->doPreFirewallBlock();
420
+ }
421
+ }
422
+ $this->updateLogStore();
423
+ $this->m_oFirewallProcessor->store( self::getKey( 'firewall_processor' ) );
424
+
425
+ if ( $fFirewallBlockUser ) {
426
+ $this->m_oFirewallProcessor->doFirewallBlock();
427
+ }
428
 
429
+ unset( $this->m_oFirewallProcessor );
430
+ }
431
 
432
+ /**
433
+ * Handles the running of all Login Protection processes.
434
+ */
435
+ public function runLoginProtect() {
436
+ $this->loadProcessor( 'LoginProtect' );
437
+ $this->m_oLoginProtectProcessor->run();
438
 
439
+ // We don't want to load the email handler unless we really need it.
440
+ // 29 is just before we'll need it if we do
441
+ if ( $this->m_oLoginProtectProcessor->getNeedsEmailHandler() ) {
442
+ $this->loadProcessor( 'Email' );
443
+ $this->m_oLoginProtectProcessor->setEmailHandler( $this->m_oEmailProcessor );
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Handles the running of all Comments Filter processes.
449
+ */
450
+ public function runCommentsFilter() {
451
+ $this->loadProcessor( 'CommentsFilter' );
452
+ $this->m_oCommentsFilterProcessor->run();
453
+ }
454
+
455
+ /**
456
+ * Handles the running of all Lockdown processes.
457
+ */
458
+ public function runLockdown() {
459
+ $this->loadProcessor( 'Lockdown' );
460
+ $this->m_oLockdownProcessor->run();
461
+ }
462
+
463
+ /**
464
+ * Handles the running of all Lockdown processes.
465
+ */
466
+ public function runAutoUpdates() {
467
+ $this->loadProcessor( 'AutoUpdates' );
468
+ $this->m_oAutoUpdatesProcessor->run();
469
+ }
470
+
471
+ protected function getAllOptions() {
472
+ $aOptionNames = array(
473
+ 'm_oWpsfOptions',
474
+ 'm_oFirewallOptions',
475
+ 'm_oLoginProtectOptions',
476
+ 'm_oCommentsFilterOptions',
477
+ 'm_oLockdownOptions',
478
+ 'm_oAutoUpdatesOptions'
479
+ );
480
+
481
+ $this->loadOptionsHandler('all');
482
+ $aOptions = array();
483
+ foreach( $aOptionNames as $sName ) {
484
+ if ( isset( $this->{$sName} ) ) {
485
+ $aOptions[] = &$this->{$sName};
486
+ }
487
+ }
488
+ return $aOptions;
489
+ }
490
+
491
+ protected function getAllProcessors() {
492
+ $aProcessorNames = array(
493
+ 'firewall_processor' => 'm_oFirewallProcessor',
494
+ 'login_processor' => 'm_oLoginProtectProcessor',
495
+ 'comments_processor' => 'm_oCommentsFilterProcessor',
496
+ 'lockdown_processor' => 'm_oLockdownProcessor',
497
+ 'autoupdates_processor' => 'm_oAutoUpdatesProcessor',
498
+ 'logging_processor' => 'm_oLoggingProcessor',
499
+ 'email_processor' => 'm_oEmailProcessor'
500
+ );
501
+ $aProcessors = array();
502
+ foreach( $aProcessorNames as $sKey => $sName ) {
503
+ if ( isset( $this->{$sName} ) ) {
504
+ $aProcessors[$sKey] = &$this->{$sName};
505
+ }
506
+ }
507
+ return $aProcessors;
508
+ }
509
 
510
+ /**
511
+ * Make sure and cache the processors after all is said and done.
512
+ */
513
+ public function saveProcessors_Action() {
514
+
515
+ $this->updateLogStore();
516
+
517
+ $aOptions = $this->getAllOptions();
518
+ foreach( $aOptions as &$oOption ) {
519
+ if ( isset( $oOption ) ) {
520
+ $oOption->savePluginOptions();
521
+ }
522
+ }
523
+ $aProcessors = $this->getAllProcessors();
524
+ foreach( $aProcessors as $sKey => &$oProcessor ) {
525
+ $oProcessor->store( self::getKey( $sKey ) );
526
+ }
527
+ }
528
+
529
  public function onWpAdminInit() {
530
  parent::onWpAdminInit();
531
+
532
  // If it's a plugin admin page, we do certain things we don't do anywhere else.
533
  if ( $this->isIcwpPluginAdminPage()) {
534
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueueBootstrapAdminCss' ), 99 );
535
  }
536
 
537
+ // This is only done on WP Admin loads so as not to affect the front-end and only if the firewall is enabled
538
+ if ( $this->getIsMainFeatureEnabled( 'firewall' ) ) {
539
+ $this->filterIpLists();
540
+ }
541
 
542
  //Multilingual support.
543
+ // load_plugin_textdomain( 'icwp-wpsf', false, basename( dirname( __FILE__ ) ) . '/languages' );
544
  }
545
 
546
+ protected function createPluginSubMenuItems() {
547
+
548
+ if ( !$this->hasPermissionToView() ) {
549
+ return;
550
+ }
551
+
552
  $this->m_aPluginMenu = array(
553
  //Menu Page Title => Menu Item name, page ID (slug), callback function for this page - i.e. what to do/load.
554
+ $this->getSubmenuPageTitle( 'Firewall' ) => array( 'Firewall', $this->getSubmenuId('firewall'), 'onDisplayAll' ),
555
+ $this->getSubmenuPageTitle( 'Login Protect' ) => array( 'Login Protect', $this->getSubmenuId('login_protect'), 'onDisplayAll' ),
556
+ $this->getSubmenuPageTitle( 'Comments Filter' ) => array( 'Comments Filter', $this->getSubmenuId('comments_filter'), 'onDisplayAll' ),
557
+ $this->getSubmenuPageTitle( 'Lockdown' ) => array( 'Lockdown', $this->getSubmenuId('lockdown'), 'onDisplayAll' ),
558
+ $this->getSubmenuPageTitle( 'Auto Updates' ) => array( 'Auto Updates', $this->getSubmenuId('autoupdates'), 'onDisplayAll' ),
559
+ $this->getSubmenuPageTitle( 'Log' ) => array( 'Log', $this->getSubmenuId('firewall_log'), 'onDisplayAll' )
560
  );
561
+ }
562
+
 
 
 
563
  protected function handlePluginUpgrade() {
564
+ parent::handlePluginUpgrade();
565
 
566
+ $sCurrentPluginVersion = $this->m_oWpsfOptions->getOpt( 'current_plugin_version' );
567
 
568
  if ( $sCurrentPluginVersion !== self::$VERSION && current_user_can( 'manage_options' ) ) {
569
+
570
+ self::deleteOption( 'enable_firewall' );
571
+ self::deleteOption( 'enable_login_protect' );
572
+ self::deleteOption( 'enable_comments_filter' );
573
+
574
+ $this->loadProcessor( 'Logging' );
575
+ $this->m_oLoggingProcessor->handleInstallUpgrade( $sCurrentPluginVersion );
576
+
577
+ // handles migration to new dedicated options system
578
+ $this->loadOptionsHandler( 'all' );
579
 
580
+ // clears all the processor caches
581
+ $this->clearCaches();
582
 
583
+ // delete all the old stuff
584
+ $aOldOptionKeys = array (
585
+ 'current_plugin_version',
586
+ 'feedback_admin_notice',
587
+ 'secret_key',
588
+ 'block_send_email',
589
+ 'block_send_email_address',
590
+ 'send_email_throttle_limit',
591
+ 'delete_on_deactivate',
592
+ 'include_cookie_checks',
593
+ 'block_dir_traversal',
594
+ 'block_sql_queries',
595
+ 'block_wordpress_terms',
596
+ 'block_field_truncation',
597
+ 'block_exe_file_uploads',
598
+ 'block_leading_schema',
599
+ 'ips_whitelist',
600
+ 'ips_blacklist',
601
+ 'page_params_whitelist',
602
+ 'block_response',
603
+ 'enable_firewall_log',
604
+ 'enable_two_factor_auth_by_ip',
605
+ 'enable_two_factor_bypass_on_email_fail',
606
+ 'login_limit_interval',
607
+ 'enable_login_gasp_check',
608
+ 'enable_login_protect_log'
609
+ );
610
+ foreach( $aOldOptionKeys as $sOptionKey ) {
611
+ self::deleteOption( $sOptionKey );
612
+ }
613
  }//if
614
 
615
  //Someone clicked the button to acknowledge the update
616
  if ( isset( $_POST[self::OptionPrefix.'hide_update_notice'] ) && isset( $_POST['user_id'] ) ) {
617
+ $this->updateVersionUserMeta( $_POST['user_id'] );
 
618
  if ( $this->isShowMarketing() ) {
619
  wp_redirect( admin_url( "admin.php?page=".$this->getFullParentMenuId() ) );
620
  }
624
  }
625
  }
626
 
627
+ /**
628
+ * Updates the current (or supplied user ID) user meta data with the version of the plugin
629
+ *
630
+ * @param unknown_type $innId
631
+ */
632
+ protected function updateVersionUserMeta( $innId = null ) {
633
+ if ( is_null( $innId ) ) {
634
+ $oCurrentUser = wp_get_current_user();
635
+ if ( !($oCurrentUser instanceof WP_User) ) {
636
+ return;
637
+ }
638
+ $nUserId = $oCurrentUser->ID;
639
+ }
640
+ else {
641
+ $nUserId = $innId;
642
+ }
643
+ update_user_meta( $nUserId, self::OptionPrefix.'current_version', self::$VERSION );
644
+ }
645
+
646
  public function onWpAdminNotices() {
647
+ // Do we have admin priviledges?
 
648
  if ( !current_user_can( 'manage_options' ) ) {
649
  return;
650
  }
651
+ parent::onWpAdminNotices();
652
  $this->adminNoticeOptionsUpdated();
653
+
654
+ if ( $this->hasPermissionToView() ) {
655
+ $this->adminNoticeVersionUpgrade();
656
+ }
657
  }
658
 
659
  /**
660
+ * Displaying all views now goes through this central function and we work out
661
+ * what to display based on the name of current hook/filter being processed.
662
  */
663
+ public function onDisplayAll() {
664
+
665
+ if ( !$this->hasPermissionToView() ) {
666
+ $this->onDisplayAccessKeyRequest();
667
  return;
668
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
 
670
+ $sPrefix = 'simple-firewall_page_icwp-wpsf-';
671
+ $sCurrent = str_replace( $sPrefix, '', current_filter() );
 
672
 
673
+ switch( $sCurrent ) {
674
+ case 'toplevel_page_'.self::ParentMenuId.'-wpsf' : //special case
675
+ $this->onDisplayMainMenu();
676
+ break;
677
+ case 'firewall_log' :
678
+ $this->onDisplayFirewallLog();
679
+ break;
680
+ default:
681
+ $aFeatures = $this->getFeaturesMap();
682
+ $this->loadOptionsHandler( $aFeatures[$sCurrent] );
683
+ $sOptionsName = 'm_o'.$aFeatures[$sCurrent].'Options';
684
+ $this->onDisplayConfig( $this->{$sOptionsName}, $sCurrent );
685
+ break;
686
  }
687
+ }
688
+
689
+ public function onDisplayAccessKeyRequest() {
690
+
691
+ // Since this is always called until we have Permission, we have to handle the plugin
692
+ // form submission here too.
693
+ $aData = array(
694
+ 'plugin_url' => self::$PLUGIN_URL,
695
+ 'var_prefix' => self::OptionPrefix,
696
+ 'nonce_field' => $this->getSubmenuId( 'wpsf-access-key' ),
697
+ 'form_action' => 'admin.php?page='.$this->getSubmenuId()
698
+ );
699
+ $this->display( 'icwp_wpsf_access_key_request_index', $aData );
700
+ }
701
 
702
  public function onDisplayMainMenu() {
703
 
704
+ // Just to ensure the nag bar disappears if/when they visit the dashboard
705
+ // regardless of clicking the button.
706
+ $this->updateVersionUserMeta();
707
+
708
+ $this->loadOptionsHandler( 'all' );
709
+ $aAvailableOptions = $this->m_oWpsfOptions->getOptions();
710
+ $sAllFormInputOptions = $this->m_oWpsfOptions->collateAllFormInputsForAllOptions();
711
 
712
  $aData = array(
713
  'plugin_url' => self::$PLUGIN_URL,
714
+ 'var_prefix' => self::OptionPrefix,
715
  'aAllOptions' => $aAvailableOptions,
716
  'fShowAds' => $this->isShowMarketing(),
717
+ 'all_options_input' => $sAllFormInputOptions,
718
+ 'nonce_field' => $this->getSubmenuId('wpsf-dashboard'),
719
+ 'form_action' => 'admin.php?page='.$this->getSubmenuId()
 
 
 
 
 
 
 
 
 
 
 
720
  );
721
 
722
+ $aData['aMainOptions'] = $this->m_oWpsfOptions->getPluginOptionsValues();
723
+
724
+ if ( $this->getIsMainFeatureEnabled('firewall') ) {
725
+ $this->loadOptionsHandler( 'Firewall' );
726
+ $aData['aFirewallOptions'] = $this->m_oFirewallOptions->getPluginOptionsValues();
727
+ }
728
+ if ( $this->getIsMainFeatureEnabled('login_protect') ) {
729
+ $this->loadOptionsHandler( 'LoginProtect' );
730
+ $aData['aLoginProtectOptions'] = $this->m_oLoginProtectOptions->getPluginOptionsValues();
731
+ }
732
+ if ( $this->getIsMainFeatureEnabled('comments_filter') ) {
733
+ $this->loadOptionsHandler( 'CommentsFilter' );
734
+ $aData['aCommentsFilterOptions'] = $this->m_oCommentsFilterOptions->getPluginOptionsValues();
735
+ }
736
+ if ( $this->getIsMainFeatureEnabled('lockdown') ) {
737
+ $this->loadOptionsHandler( 'Lockdown' );
738
+ $aData['aLockdownOptions'] = $this->m_oLockdownOptions->getPluginOptionsValues();
739
+ }
740
+ if ( $this->getIsMainFeatureEnabled('autoupdates') ) {
741
+ $this->loadOptionsHandler( 'AutoUpdates' );
742
+ $aData['aAutoUpdatesOptions'] = $this->m_oAutoUpdatesOptions->getPluginOptionsValues();
743
+ }
744
+
745
  $this->display( 'icwp_'.$this->m_sParentMenuIdSuffix.'_index', $aData );
746
  }
747
 
748
+ protected function onDisplayFirewallLog() {
749
+
750
+ $this->loadOptionsHandler( 'Firewall' );
751
+ $aIpWhitelist = $this->m_oFirewallOptions->getOpt( 'ips_whitelist' );
752
+ $aIpBlacklist = $this->m_oFirewallOptions->getOpt( 'ips_blacklist' );
753
 
754
+ $this->loadProcessor( 'Logging' );
 
 
 
 
 
 
 
755
 
 
756
  $aData = array(
757
  'plugin_url' => self::$PLUGIN_URL,
758
  'var_prefix' => self::OptionPrefix,
759
+ 'firewall_log' => $this->m_oLoggingProcessor->getLogs( true ),
760
+ 'ip_whitelist' => isset( $aIpWhitelist['ips'] )? $aIpWhitelist['ips'] : array(),
761
+ 'ip_blacklist' => isset( $aIpBlacklist['ips'] )? $aIpBlacklist['ips'] : array(),
762
  'fShowAds' => $this->isShowMarketing(),
763
+ 'nonce_field' => $this->getSubmenuId('firewall_log'),
764
+ 'form_action' => 'admin.php?page='.$this->getSubmenuId('firewall_log'),
 
 
765
  );
766
 
767
+ $this->display( 'icwp_wpsf_firewall_log_index', $aData );
768
  }
769
 
770
+ /**
771
+ *
772
+ * @param ICWP_OptionsHandler_Base_WPSF $inoOptions
773
+ * @param string $insSlug
774
+ */
775
+ protected function onDisplayConfig( $inoOptions, $insSlug ) {
776
 
777
+ $aAvailableOptions = $inoOptions->getOptions();
778
+ $sAllFormInputOptions = $inoOptions->collateAllFormInputsForAllOptions();
779
+
780
  $aData = array(
781
  'plugin_url' => self::$PLUGIN_URL,
782
  'var_prefix' => self::OptionPrefix,
 
783
  'fShowAds' => $this->isShowMarketing(),
784
+ 'aAllOptions' => $aAvailableOptions,
785
+ 'all_options_input' => $sAllFormInputOptions,
786
+ 'nonce_field' => $this->getSubmenuId( $insSlug ),
787
+ 'form_action' => 'admin.php?page='.$this->getSubmenuId( $insSlug ),
788
  );
789
+ $this->display( 'icwp_wpsf_config_'.$insSlug.'_index', $aData );
790
+ }
791
+
792
+ protected function isPluginFormSubmit() {
793
+
794
+ $aPostSubmitOptions = array(
795
+ 'icwp_plugin_form_submit',
796
+ 'icwp_link_action',
797
+ 'icwp_wpsf_admin_access_key_request'
798
+ );
799
+
800
+ foreach( $aPostSubmitOptions as $sOption ) {
801
+ if ( isset( $_POST[$sOption] ) || isset( $_GET[$sOption] ) ) {
802
+ return true;
803
+ }
804
+ }
805
+ return false;
806
  }
807
 
808
  protected function handlePluginFormSubmit() {
809
 
810
+ //should have already been checked, but just to make sure.
811
+ if ( !$this->isPluginFormSubmit() ) {
812
+ return false;
813
+ }
814
+
815
+ if ( isset( $_POST['icwp_wpsf_admin_access_key_request'] ) ) {
816
+ return $this->handleSubmit_AccessKeyRequest();
817
+ }
818
+
819
+ if ( !$this->hasPermissionToSubmit() ) {
820
+ return false;
821
  }
822
 
823
  if ( isset( $_GET['page'] ) ) {
824
  switch ( $_GET['page'] ) {
825
+ case $this->getSubmenuId():
826
+ $this->handleSubmit_Dashboard();
827
  break;
828
+ case $this->getSubmenuId( 'firewall' ):
829
+ $this->handleSubmit_FirewallConfig();
830
+ break;
831
+ case $this->getSubmenuId( 'login_protect' ):
832
+ $this->handleSubmit_LoginProtect();
833
+ break;
834
+ case $this->getSubmenuId( 'comments_filter' ):
835
+ $this->handleSubmit_CommentsFilter();
836
+ break;
837
+ case $this->getSubmenuId( 'lockdown' ):
838
+ $this->handleSubmit_Lockdown();
839
+ break;
840
+ case $this->getSubmenuId( 'autoupdates' ):
841
+ $this->handleSubmit_AutoUpdates();
842
+ break;
843
+ case $this->getSubmenuId( 'firewall_log' ):
844
  $this->handleSubmit_FirewallLog();
845
  break;
846
+ default:
847
+ return false;
848
+ break;
849
  }
850
  }
851
+ $this->resetLoggingProcessor();
852
+ return true;
853
+ }
854
+
855
+ protected function setPermissionToSubmit( $infPermission = false ) {
856
 
857
+ if ( $infPermission ) {
858
+ $sValue = $this->m_oWpsfOptions->getOpt( 'admin_access_key' );
859
+ $sTimeout = $this->m_oWpsfOptions->getOpt( 'admin_access_timeout' ) * 60;
860
+ $_COOKIE[ self::AdminAccessKeyCookieName ] = 1;
861
+ setcookie( self::AdminAccessKeyCookieName, $sValue, time()+$sTimeout, COOKIEPATH, COOKIE_DOMAIN, false );
862
  }
863
  else {
864
+ unset( $_COOKIE[ self::AdminAccessKeyCookieName ] );
865
+ setcookie( self::AdminAccessKeyCookieName, "", time()-3600, COOKIEPATH, COOKIE_DOMAIN, false );
866
+ }
867
+ }
868
+
869
+ /**
870
+ * @return boolean
871
+ */
872
+ protected function hasPermissionToView() {
873
+ // For now we just use the submit permissions and we can adapt it later
874
+ return $this->hasPermissionToSubmit();
875
+ }
876
+
877
+ /**
878
+ * @return boolean
879
+ */
880
+ protected function hasPermissionToSubmit() {
881
+
882
+ // first a basic admin check
883
+ if ( !is_super_admin() ) {
884
+ return false;
885
  }
886
 
887
+ if ( $this->m_oWpsfOptions->getOpt( 'enable_admin_access_restriction' ) == 'Y' ) {
888
+ $sAccessKey = $this->m_oWpsfOptions->getOpt( 'admin_access_key' );
889
+ if ( !empty( $sAccessKey ) ) {
890
+ if ( isset( $_COOKIE[ self::AdminAccessKeyCookieName ] )
891
+ // && $_COOKIE[ self::AdminAccessKeyCookieName ] == $sAccessKey
892
+ ) {
893
+ return true;
894
+ }
895
+ else {
896
+ return false;
897
+ }
898
+ }
899
+ }
900
+ return true;
901
  }
902
 
903
+ protected function handleSubmit_AccessKeyRequest() {
904
  //Ensures we're actually getting this request from WP.
905
+ check_admin_referer( $this->getSubmenuId('wpsf-access-key') );
906
+
907
+ $this->loadOptionsHandler( 'Wpsf' );
908
+ $sAccessKey = md5( trim( $_POST['icwp_wpsf_admin_access_key_request'] ) );
909
+ $sStoredAccessKey = $this->m_oWpsfOptions->getOpt( 'admin_access_key' );
910
 
911
+ if ( $sAccessKey === $sStoredAccessKey ) {
912
+ $this->setPermissionToSubmit( true );
913
+ header( 'Location: '.admin_url('admin.php?page=icwp-wpsf') );
914
+ exit();
915
+ }
916
+ return false;
917
+ }
918
+
919
+ protected function handleSubmit_Dashboard() {
920
+ //Ensures we're actually getting this request from WP.
921
+ check_admin_referer( $this->getSubmenuId('wpsf-dashboard') );
922
+
923
+ if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
924
+ return false;
925
+ }
926
+
927
+ $this->loadOptionsHandler( 'Wpsf' );
928
+ $this->m_oWpsfOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
929
+
930
+ $this->setSharedOption( 'enable_firewall', $this->m_oWpsfOptions->getOpt( 'enable_firewall' ) );
931
+ $this->setSharedOption( 'enable_login_protect', $this->m_oWpsfOptions->getOpt( 'enable_login_protect' ) );
932
+ $this->setSharedOption( 'enable_comments_filter', $this->m_oWpsfOptions->getOpt( 'enable_comments_filter' ) );
933
+ $this->setSharedOption( 'enable_lockdown', $this->m_oWpsfOptions->getOpt( 'enable_lockdown' ) );
934
+ $this->setSharedOption( 'enable_autoupdates', $this->m_oWpsfOptions->getOpt( 'enable_autoupdates' ) );
935
+
936
+ $this->clearCaches();
937
+ }
938
+
939
+ protected function handleSubmit_FirewallConfig() {
940
+ //Ensures we're actually getting this request from WP.
941
+ check_admin_referer( $this->getSubmenuId( 'firewall' ) );
942
+
943
+ if ( isset($_POST[ 'import-wpf2-submit' ] ) ) {
944
+ $this->importFromFirewall2Plugin();
945
+ }
946
+ else if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
947
+ return;
948
+ }
949
+ else {
950
+ $this->loadOptionsHandler( 'Firewall' );
951
+ $this->m_oFirewallOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
952
+ }
953
+ $this->setSharedOption( 'enable_firewall', $this->m_oFirewallOptions->getOpt( 'enable_firewall' ) );
954
+ $this->resetFirewallProcessor();
955
+ }
956
+
957
+ protected function handleSubmit_LoginProtect() {
958
+ //Ensures we're actually getting this request from WP.
959
+ check_admin_referer( $this->getSubmenuId('login_protect' ) );
960
 
961
  if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
962
  return;
963
  }
964
+ $this->loadOptionsHandler( 'LoginProtect' );
965
+ $this->m_oLoginProtectOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
966
+ $this->setSharedOption( 'enable_login_protect', $this->m_oLoginProtectOptions->getOpt( 'enable_login_protect' ) );
967
+ $this->resetLoginProcessor();
968
  }
969
 
970
+ protected function handleSubmit_CommentsFilter() {
971
  //Ensures we're actually getting this request from WP.
972
+ check_admin_referer( $this->getSubmenuId('comments_filter' ) );
973
+
974
+ if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
975
+ return;
976
+ }
977
+ $this->loadOptionsHandler( 'CommentsFilter' );
978
+ $this->m_oCommentsFilterOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
979
+ $this->setSharedOption( 'enable_comments_filter', $this->m_oCommentsFilterOptions->getOpt( 'enable_comments_filter' ) );
980
+ $this->resetCommentsProcessor();
981
+ }
982
+
983
+ protected function handleSubmit_Lockdown() {
984
+ //Ensures we're actually getting this request from WP.
985
+ check_admin_referer( $this->getSubmenuId('lockdown' ) );
986
+
987
+ if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
988
+ return;
989
+ }
990
+ $this->loadOptionsHandler( 'Lockdown' );
991
+ $this->m_oLockdownOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
992
+ $this->setSharedOption( 'enable_lockdown', $this->m_oLockdownOptions->getOpt( 'enable_lockdown' ) );
993
+ $this->resetLockdownProcessor();
994
+ }
995
+
996
+ protected function handleSubmit_AutoUpdates() {
997
+ //Ensures we're actually getting this request from WP.
998
+ check_admin_referer( $this->getSubmenuId( 'autoupdates' ) );
999
+
1000
+ if ( !isset($_POST[self::OptionPrefix.'all_options_input']) ) {
1001
+ return;
1002
+ }
1003
+ $this->loadOptionsHandler( 'AutoUpdates' );
1004
+ $this->m_oAutoUpdatesOptions->updatePluginOptionsFromSubmit( $_POST[self::OptionPrefix.'all_options_input'] );
1005
+ $this->setSharedOption( 'enable_autoupdates', $this->m_oAutoUpdatesOptions->getOpt( 'enable_autoupdates' ) );
1006
+ $this->resetAutoUpdatesProcessor();
1007
+ }
1008
+
1009
+ protected function handleSubmit_FirewallLog() {
1010
+
1011
+ // Ensures we're actually getting this request from a valid WP submission.
1012
+ if ( !isset( $_REQUEST['_wpnonce'] ) || !wp_verify_nonce( $_REQUEST['_wpnonce'], $this->getSubmenuId( 'firewall_log' ) ) ) {
1013
+ wp_die();
1014
+ }
1015
 
1016
  // At the time of writing the page only has 1 form submission item - clear log
1017
+ if ( isset( $_POST['clear_log_submit'] ) ) {
1018
+ $this->loadProcessor( 'Logging' );
1019
+ $this->m_oLoggingProcessor->recreateTable();
1020
+ }
1021
+ else if ( isset( $_GET['blackip'] ) ) {
1022
+ $this->addRawIpsToFirewallList( 'ips_blacklist', array( $_GET['blackip'] ) );
1023
+ }
1024
+ else if ( isset( $_GET['unblackip'] ) ) {
1025
+ $this->removeRawIpsFromFirewallList( 'ips_blacklist', array( $_GET['unblackip'] ) );
1026
+ }
1027
+ else if ( isset( $_GET['whiteip'] ) ) {
1028
+ $this->addRawIpsToFirewallList( 'ips_whitelist', array( $_GET['whiteip'] ) );
1029
+ }
1030
+ else if ( isset( $_GET['unwhiteip'] ) ) {
1031
+ $this->removeRawIpsFromFirewallList( 'ips_whitelist', array( $_GET['unwhiteip'] ) );
1032
+ }
1033
+ wp_safe_redirect( admin_url( "admin.php?page=".$this->getSubmenuId('firewall_log') ) ); //means no admin message is displayed
1034
  exit();
1035
  }
1036
 
1037
+ public function clearCaches() {
1038
+ $this->resetFirewallProcessor();
1039
+ $this->resetLoginProcessor();
1040
+ $this->resetLoggingProcessor();
1041
+ $this->resetCommentsProcessor();
1042
+ }
1043
+
1044
+ protected function resetEmailProcessor() {
1045
+ $this->m_oEmailProcessor = false;
1046
+ self::deleteOption( 'email_processor' );
1047
+ $this->loadProcessor( 'Email' );
1048
+ }
1049
+
1050
+ protected function resetFirewallProcessor() {
1051
+ $this->resetEmailProcessor();
1052
+ $this->m_oFirewallProcessor = false;
1053
+ self::deleteOption( 'firewall_processor' );
1054
+ $this->loadProcessor( 'Firewall' );
1055
+ }
1056
+
1057
+ protected function resetLoginProcessor() {
1058
+ $this->m_oLoginProtectProcessor = false;
1059
+ self::deleteOption( 'login_processor' );
1060
+ $this->loadProcessor( 'LoginProtect' );
1061
+ }
1062
+
1063
+ protected function resetCommentsProcessor() {
1064
+ $this->m_oCommentsFilterProcessor = false;
1065
+ self::deleteOption( 'comments_processor' );
1066
+ $this->loadProcessor( 'CommentsFilter' );
1067
+ }
1068
+
1069
+ protected function resetLockdownProcessor() {
1070
+ $this->m_oLockdownProcessor = false;
1071
+ self::deleteOption( 'lockdown_processor' );
1072
+ $this->loadProcessor( 'Lockdown' );
1073
+ }
1074
+
1075
+ protected function resetAutoUpdatesProcessor() {
1076
+ $this->m_oLockdownProcessor = false;
1077
+ self::deleteOption( 'autoupdates_processor' );
1078
+ $this->loadProcessor( 'AutoUpdates' );
1079
+ }
1080
+
1081
+ protected function resetLoggingProcessor() {
1082
+ $this->m_oLoggingProcessor = false;
1083
+ self::deleteOption( 'logging_processor' );
1084
+ $this->loadProcessor( 'Logging' );
1085
  }
1086
 
1087
+ protected function importFromFirewall2Plugin() {
1088
+ $this->loadOptionsHandler( 'all' );
1089
+ require_once( dirname(__FILE__).'/src/icwp-import-wpf2-processor.php' );
1090
+ $oImportProcessor = new ICWP_ImportWpf2Processor( $this->m_oWpsfOptions, $this->m_oFirewallOptions );
1091
+ $oImportProcessor->runImport();
1092
  }
1093
 
1094
+ public function onWpPluginActionLinks( $inaActionLinks, $insFile ) {
1095
+
1096
  if ( $insFile == plugin_basename( __FILE__ ) ) {
1097
+ if ( !$this->hasPermissionToSubmit() ) {
1098
+ if ( array_key_exists( 'edit', $inaActionLinks ) ) {
1099
+ unset( $inaActionLinks['edit'] );
1100
+ }
1101
+ if ( array_key_exists( 'deactivate', $inaActionLinks ) ) {
1102
+ unset( $inaActionLinks['deactivate'] );
1103
+ }
1104
+ }
1105
+ $sSettingsLink = '<a href="'.admin_url( "admin.php" ).'?page='.$this->getSubmenuId().'">' . 'Dashboard' . '</a>';
1106
+ array_unshift( $inaActionLinks, $sSettingsLink );
1107
  }
1108
+ return $inaActionLinks;
1109
+ }
1110
+
1111
+ public function onWpPluginsLoaded() {
1112
+ parent::onWpPluginsLoaded();
1113
+
1114
+ if ( $this->isValidAdminArea()
1115
+ && $this->m_oWpsfOptions->getOpt('enable_upgrade_admin_notice') == 'Y'
1116
+ && $this->hasPermissionToSubmit()
1117
+ ) {
1118
+ $this->m_fDoAutoUpdateCheck = true;
1119
+ }
1120
+
1121
+ add_action( 'deactivate_plugin', array( $this, 'preventDeactivation' ), 1, 1 );
1122
+ $this->removePluginConflicts(); // removes conflicts with other plugins
1123
+ }
1124
+
1125
+ /**
1126
+ * @param string $insPlugin - the path to the plugin file
1127
+ */
1128
+ public function preventDeactivation( $insPlugin ) {
1129
+ if ( strpos( $insPlugin, basename(__FILE__) ) !== false && !$this->hasPermissionToSubmit() ) {
1130
+ wp_die( 'Sorry, you do not have permission to disable this plugin. You need to authenticate first.' );
1131
+ }
1132
+ }
1133
+
1134
+ public function onWpShutdown() {
1135
+ parent::onWpShutdown();
1136
+ $this->saveProcessors_Action();
1137
  }
1138
 
1139
  protected function deleteAllPluginDbOptions() {
1140
 
1141
  parent::deleteAllPluginDbOptions();
 
1142
  if ( !current_user_can( 'manage_options' ) ) {
1143
  return;
1144
  }
1145
 
1146
+ $this->loadProcessor( 'Logging' );
1147
+ $this->m_oLoggingProcessor->dropTable();
1148
+
1149
+ $this->loadProcessor( 'LoginProtect' );
1150
+ $this->m_oLoginProtectProcessor->dropTable();
1151
+
1152
+ $this->loadProcessor( 'CommentsFilter' );
1153
+ $this->m_oCommentsFilterProcessor->dropTable();
1154
+
1155
+ $aOptions = $this->getAllOptions();
1156
+ foreach( $aOptions as &$oOption ) {
1157
+ $oOption->deletePluginOptions();
1158
+ }
1159
+ remove_action( 'shutdown', array( $this, 'onWpShutdown' ) );
1160
+ }
1161
+
1162
+ public function onWpPluginUpdateMessage() {
1163
+ echo '<div style="color: #dd3333;">'
1164
+ ."Upgrade Now To Keep Your Firewall Up-To-Date With The Latest Features."
1165
+ . '</div>';
1166
  }
1167
 
1168
  public function onWpDeactivatePlugin() {
1169
+ if ( $this->m_oWpsfOptions->getOpt( 'delete_on_deactivate' ) == 'Y' ) {
 
1170
  $this->deleteAllPluginDbOptions();
1171
  }
1172
+ }
 
 
 
 
 
 
1173
 
1174
  public function enqueueBootstrapAdminCss() {
1175
  wp_register_style( 'worpit_bootstrap_wpadmin_css', $this->getCssUrl( 'bootstrap-wpadmin.css' ), false, self::$VERSION );
1178
  wp_enqueue_style( 'worpit_bootstrap_wpadmin_css_fixes' );
1179
  }
1180
 
1181
+ public function addRawIpsToFirewallList( $insListName, $inaNewIps ) {
 
 
 
1182
 
1183
+ $this->loadOptionsHandler( 'Firewall' );
1184
+
1185
+ $aIplist = $this->m_oFirewallOptions->getOpt( $insListName );
1186
+ if ( empty( $aIplist ) ) {
1187
+ $aIplist = array();
1188
  }
1189
+ $aNewList = array();
1190
+ foreach( $inaNewIps as $sAddress ) {
1191
+ $aNewList[ $sAddress ] = '';
 
1192
  }
1193
+ $aIplist = $this->m_oFirewallOptions->setOpt( $insListName, ICWP_DataProcessor::Add_New_Raw_Ips( $aIplist, $aNewList ) );
1194
+ $this->resetFirewallProcessor();
1195
+ }
1196
+
1197
+ public function removeRawIpsFromFirewallList( $insListName, $inaRemoveIps ) {
1198
 
1199
+ $this->loadOptionsHandler( 'Firewall' );
1200
+
1201
+ $aIplist = $this->m_oFirewallOptions->getOpt( $insListName );
1202
+ if ( empty( $aIplist ) || empty( $inaRemoveIps ) ) {
1203
+ return;
1204
  }
1205
+ $aIplist = $this->m_oFirewallOptions->setOpt( $insListName, ICWP_DataProcessor::Remove_Raw_Ips( $aIplist, $inaRemoveIps ) );
1206
+ $this->resetFirewallProcessor();
1207
+ }
1208
+
1209
+ /**
1210
+ */
1211
+ protected function filterIpLists() {
1212
+
1213
+ $nNewAddedCount = 0;
1214
+ $mResult = $this->processIpFilter( 'ips_whitelist', 'icwp_simple_firewall_whitelist_ips', $nNewAddedCount );
1215
+ if ( $mResult !== false && $nNewAddedCount > 0 ) {
1216
+ $this->m_oFirewallOptions->setOpt( 'ips_whitelist', $mResult );
1217
+ $this->resetFirewallProcessor();
1218
+ }
1219
+
1220
+ $nNewAddedCount = 0;
1221
+ $mResult = $this->processIpFilter( 'ips_blacklist', 'icwp_simple_firewall_blacklist_ips', $nNewAddedCount );
1222
+ if ( $mResult !== false && $nNewAddedCount > 0 ) {
1223
+ $this->m_oFirewallOptions->setOpt( 'ips_blacklist', $mResult );
1224
+ $this->resetFirewallProcessor();
1225
  }
1226
  }
1227
 
1228
+ /**
1229
+ * @param string $insExistingListKey
1230
+ * @param string $insFilterName
1231
+ * @param integer $outnNewAdded
1232
+ * @return array|false
1233
+ */
1234
+ protected function processIpFilter( $insExistingListKey, $insFilterName, &$outnNewAdded = 0 ) {
1235
+
1236
+ $aFilterIps = array();
1237
+ $aFilterIps = apply_filters( $insFilterName, $aFilterIps );
1238
+
1239
+ if ( !empty( $aFilterIps ) ) {
1240
+
1241
+ $aNewIps = array();
1242
+ foreach( $aFilterIps as $mKey => $sValue ) {
1243
+
1244
+ if ( is_string( $mKey ) ) { //it's the IP
1245
+ $sIP = $mKey;
1246
+ $sLabel = $sValue;
1247
+ }
1248
+ else { //it's not an associative array, so the value is the IP
1249
+ $sIP = $sValue;
1250
+ $sLabel = '';
1251
+ }
1252
+ $aNewIps[ $sIP ] = $sLabel;
1253
+ }
1254
+
1255
+ // Get the existing list
1256
+ $this->loadOptionsHandler( 'Firewall' );
1257
+ $aExistingIpList = $this->m_oFirewallOptions->getOpt( $insExistingListKey );
1258
+ if ( !is_array( $aExistingIpList ) ) {
1259
+ $aExistingIpList = array();
1260
+ }
1261
+ return ICWP_DataProcessor::Add_New_Raw_Ips( $aExistingIpList, $aNewIps, $outnNewAdded );
1262
  }
1263
+ return false;
1264
+ }
1265
+
1266
+ /**
1267
+ * Shows the update notification - will bail out if the current user is not an admin
1268
+ */
1269
+ private function adminNoticeVersionUpgrade() {
1270
+
1271
+ $oCurrentUser = wp_get_current_user();
1272
+ if ( !($oCurrentUser instanceof WP_User) ) {
1273
+ return;
1274
  }
1275
+ $nUserId = $oCurrentUser->ID;
1276
+ $sCurrentVersion = get_user_meta( $nUserId, self::OptionPrefix.'current_version', true );
1277
+ // A guard whereby if we can't ever get a value for this meta, it means we can never set it.
1278
+ // If we can never set it, we shouldn't force the Ads on those users who can't get rid of it.
1279
+ if ( empty( $sCurrentVersion ) ) { //the value has never been set, or it's been installed for the first time.
1280
+ $result = update_user_meta( $nUserId, self::OptionPrefix.'current_version', self::$VERSION );
1281
+ return; //meaning we don't show the update notice upon new installations and for those people who can't set the version in their meta.
1282
  }
1283
+
1284
+ if ( $sCurrentVersion !== self::$VERSION ) {
1285
+
1286
+ $sRedirectPage = isset( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : 'index.php';
1287
+ $sRedirectPage = 'admin.php?page=icwp-wpsf';
1288
+ ob_start();
1289
+ ?>
1290
+ <style>
1291
+ a#fromIcwp { padding: 0 5px; border-bottom: 1px dashed rgba(0,0,0,0.1); color: blue; font-weight: bold; }
1292
+ </style>
1293
+ <form id="IcwpUpdateNotice" method="post" action="admin.php?page=<?php echo $this->getSubmenuId('firewall'); ?>">
1294
+ <input type="hidden" value="<?php echo $sRedirectPage; ?>" name="redirect_page" id="redirect_page">
1295
+ <input type="hidden" value="1" name="<?php echo self::OptionPrefix; ?>hide_update_notice" id="<?php echo self::OptionPrefix; ?>hide_update_notice">
1296
+ <input type="hidden" value="<?php echo $nUserId; ?>" name="user_id" id="user_id">
1297
+ <h4 style="margin:10px 0 3px;">
1298
+ Note: WordPress Simple Firewall plugin <u>does not automatically turn on</u> when you install/update. There may also be
1299
+ <a href="http://icwp.io/27" id="fromIcwp" title="WordPress Simple Firewall Plugin" target="_blank">important updates to read about</a>.
1300
+ </h4>
1301
+ <input type="submit" value="Okay, show me the dashboard." name="submit" class="button" style="float:left; margin-bottom:10px;">
1302
+ <div style="clear:both;"></div>
1303
+ </form>
1304
+ <?php
1305
+ $sNotice = ob_get_contents();
1306
+ ob_end_clean();
1307
+ $this->getAdminNotice( $sNotice, 'updated', true );
1308
+ }
1309
+ }
1310
+
1311
+ private function adminNoticeOptionsUpdated() {
1312
+
1313
+ $sAdminFeedbackNotice = $this->m_oWpsfOptions->getOpt( 'feedback_admin_notice' );
1314
+ if ( !empty( $sAdminFeedbackNotice ) ) {
1315
+ $sNotice = '<p>'.$sAdminFeedbackNotice.'</p>';
1316
+ $this->getAdminNotice( $sNotice, 'updated', true );
1317
+ $this->m_oWpsfOptions->setOpt( 'feedback_admin_notice', '' );
1318
  }
 
1319
  }
1320
  }
1321
 
mode.login_throttled ADDED
File without changes
readme.txt CHANGED
@@ -1,46 +1,131 @@
1
  === Plugin Name ===
2
- Contributors: dlgoodchild, paultgoodchild
3
  Donate link: http://icwp.io/q
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
- Tags: WordPress Firewall, protection, whitelist
7
  Requires at least: 3.2.0
8
- Tested up to: 3.6
9
- Stable tag: 1.0
10
 
11
- WordPress Simple Firewall
12
 
13
  == Description ==
14
 
15
- Principally based on functionality provided by the [WordPress Firewall 2 plugin](http://wordpress.org/plugins/wordpress-firewall-2/).
 
16
 
17
- The WordPress Simple Firewall is built to be principally easy to use. It takes a lead from the WordPress Firewall 2 plugin that is already quite old and
18
- that hasn't been updated for more than 2 years.
19
 
20
- This plugin is built for reliability of operation, ease of extension, and overall... simplicity.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  It adds extra features over WordPress Firewall 2, including:
23
 
24
- * Option to completely block access to wp-login.php based on IP Address whitelist
25
- * Added a Blacklist option so you can completely block based on IP address.
26
- * Option to easily turn on / off the whole firewall. This means you don't have to disable certain settings or even disable the plugin to temporarily turn it off.
27
- To debug the plugin, just turn off the firewall in the Firewall Options screen and all settings are ignored.
28
- * Filesystem based plugin override. This means if you accidentally lock yourself out, you can forcefully turn off the firewall using FTP. You can also
 
 
 
29
  turn back on the firewall using the same method.
30
- * Automatic caching to reduce database calls when determining Firewall settings: 1-3 database calls per page load.
31
- * Ability to easily turn on and off firewall logging.
32
- * Ability to view the complete log of the firewall and all its messages.
33
- * Ability to clear the whole log.
34
- * For developers - ability to programmatically add to the IP address whitelist/blacklist - this is
35
- useful for 3rd party services that connect to the site using other plugins. E.g. [iControlWP](http://www.icontrolwp.com/).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  == Installation ==
38
 
39
  See FAQs.
40
 
41
- == Frequently Asked Questions ==
42
 
43
- = How can I install the plugin? =
 
 
 
 
44
 
45
  This plugin should install as any other WordPress.org respository plugin.
46
 
@@ -58,25 +143,32 @@ Alternatively using FTP:
58
 
59
  A new menu item will appear on the left-hand side called 'Simple Firewall'.
60
 
 
 
 
 
 
 
 
 
 
 
61
  = How does the IP Whitelist work? =
62
 
63
  Any IP address that is on the whitelist will not be subject to any of the firewall scanning/processing. This setting takes priority over all other settings.
64
 
65
- = How does the wp-login.php block work? =
66
 
67
- If the IP whitelist is empty, this setting is ignored. This stops you from easily locking yourself out.
68
 
69
- When enabled, and there are valid IP addresses in the whitelist, any access to wp-login.php will be blocked from a visitor
70
- who's IP address is not on the whitelist.
71
 
72
- = I've locked myself out from the WordPress login screen! =
73
 
74
- This happens when ALL the following 3 conditions are all met:
75
- * you turn on the option to restrict access to wp-login.php,
76
- * you have at least 1 IP address in the IP Whitelist, and
77
- * your current IP address is not on the IP Whitelist.
78
 
79
- This plugin offers the ability to completely turn off (and on) the WordPress Simple Firewall by creating a specific file in the plugin folder.
80
 
81
  Here's how:
82
 
@@ -87,20 +179,268 @@ Here's how:
87
 
88
  If you want to turn the firewall on in the same way, create a file called "forceOn".
89
 
90
- Remember: If you leave one of these files on the server, it will override your on/off settings, so you should delete it when you're done.
91
 
92
  = Which takes precedence... whitelist or blacklist? =
93
 
94
  Whitelist. So if you have the same address in both lists, it'll be whitelisted and allowed to pass before the blacklist comes into effect.
95
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  == Screenshots ==
97
 
98
  == Changelog ==
99
 
100
- = 1.0 =
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  * First Release
102
 
103
  == Upgrade Notice ==
104
 
105
- = 1.0 =
106
- * First Release
 
 
 
 
 
 
 
1
  === Plugin Name ===
2
+ Contributors: paultgoodchild, dlgoodchild
3
  Donate link: http://icwp.io/q
4
  License: GPLv3
5
  License URI: http://www.gnu.org/licenses/gpl.html
6
+ Tags: WordPress Firewall, protection, whitelist, blacklist, two-factor authentication, GASP, comment spam, automatic updates
7
  Requires at least: 3.2.0
8
+ Tested up to: 3.7
9
+ Stable tag: 1.9.2
10
 
11
+ Complete and Simple WordPress Security. Unrestricted, with no premium features.
12
 
13
  == Description ==
14
 
15
+ The WordPress Simple Firewall is the only WordPress security plugin that *protects itself* - this plugin
16
+ will prevent access to itself so that unauthorized users can't deactivate or screw with your security settings.
17
 
18
+ A basic intro to all the features:
 
19
 
20
+ [youtube http://www.youtube.com/watch?v=r307fu3Eqbo]
21
+
22
+ Protects your WordPress site in 5 main ways:
23
+
24
+ = Plugin Self-Protection =
25
+
26
+ This plugins locks itself down - you can add access restriction to the plugin itself!
27
+
28
+ = A Simple, Effective Firewall =
29
+
30
+ Builds upon the simplicity and effectiveness of the WordPress Firewall 2 plugin.
31
+
32
+ = WordPress Login Protection =
33
+
34
+ Adds several layers of protection to the WordPress login screen through identity verification and Brute Force Login hacking prevention.
35
+
36
+ = Comments and SPAM Protection =
37
+
38
+ Uses and builds upon tried and tested SPAM prevention and filtering techniques with some unique approaches found only in this plugin.
39
+
40
+ = WordPress Lockdown =
41
+
42
+ Provides options for locking down your WordPress site from both legitimate users and people who may have gained unauthorized access.
43
+
44
+ Read more on each section below...
45
+
46
+ = A Simple Firewall =
47
+
48
+ The WordPress Simple Firewall is built to be reliable, and easy to use by **anyone**. Seriously, the interface is simple! :)
49
 
50
  It adds extra features over WordPress Firewall 2, including:
51
 
52
+ * 7 Simple, clear, Firewall blocking options - pick and choose for ultimate protection and compatibility.
53
+ * Option: Ignore already logged-in Administrators so you don't firewall yourself as you work on the site.
54
+ * Option: IP Address Whitelist. So you can vet your own IP addresses and 3rd Party Services.
55
+ * Option: Developer option for 3rd Party Services to dynamically add IP Addresses to whitelist
56
+ (our plugin is built to work with others!) E.g. [iControlWP](http://www.icontrolwp.com/).
57
+ * Option: IP Address Blacklist so you can completely block sites/services based on their IP address.
58
+ * Option: to easily turn on / off the whole firewall without disabling the whole plugin! (so simple, but important)
59
+ * Recovery Option: You can use FTP to manually turn ON/OFF the Firewall. This means if you accidentally lock yourself out, you can forcefully turn off the firewall using FTP. You can also
60
  turn back on the firewall using the same method.
61
+ * Performance: When the firewall is running it is processing EVERY page load. So your firewall checking needs to be fast.
62
+ This plugin is written to cache settings and minimize database access: 1-3 database calls per page load.
63
+ * Logging: Full logging of Firewall (and other options) to analyse and debug your traffic and settings.
64
+ * Option: Email when firewall blocks a page access - with option to specify recipient.
65
+ * Option: Email throttling. If you get hit by a bot you wont get 1000s of email... you can throttle how many emails are sent.
66
+ useful for 3rd party services that connect to the site using other plugins.
67
+
68
+ Basic functionality is based on the principles employed by the [WordPress Firewall 2 plugin](http://wordpress.org/plugins/wordpress-firewall-2/).
69
+
70
+ = Login and Identity Protection - Stops Brute Force Attacks =
71
+
72
+ Note: Login Protection is a completely independent feature to the Firewall. IP Address whitelists are not shared.
73
+
74
+ With our Login Protection features this plugin will single-handling prevent brute force login attack on all your WordPress sites.
75
+
76
+ It doesn't need IP Address Ban Lists (which are actually useless anyway), and instead puts hard limits on your WordPress site,
77
+ and force users to verify themselves when they login.
78
+
79
+ As of version 1.2.0+ you now have several ways to add simple protection to your WordPress Login system.
80
+
81
+ 1. [Email-based 2-Factor Login Authentication](http://icwp.io/2v) based on IP address! (prevents brute force login attacks)
82
+ 1. [Login Cooldown Interval](http://icwp.io/2t) - WordPress will only process 1 login per interval in seconds (prevents brute force login attacks)
83
+ 1. [GASP Anti-Bot Login Form Protection](http://icwp.io/2u) - Adds 2 protection checks for all WordPress login attempts (prevents brute force login attacks using Bots)
84
+
85
+ These options alone will protect your WordPress sites from nearly all forms of Brute Force
86
+ login attacks.
87
+
88
+ And you hardly need to configure anything! Simply check the options to turn them on, set a cooldown interval and you're instantly protected.
89
+
90
+ = SPAM and Comments Filtering =
91
+
92
+ As of version 1.6, this plugin integrates [GASP Spambot Protection](http://wordpress.org/plugins/growmap-anti-spambot-plugin/).
93
+
94
+ We have taken this functionality a level further and added the concept of unique, per-page visit, Comment Tokens.
95
+
96
+ **Comment Tokens** are unique keys that are created every time a page loads and they are uniquely generated based on 3 factors:
97
+
98
+ 1. The visitors IP address.
99
+ 1. The Page they are viewing
100
+ 1. A unique, random number, generated at the time the page is loaded.
101
+
102
+ This is all handle automatically and your users will not be affected - they'll still just have a checkbox like the original GASP plugin.
103
+
104
+ These comment tokens are then embedded in the comment form and must be presented to your WordPress site when a comment is posted. The plugin
105
+ will then examine the token, the IP address from which the comment is coming, and page upon which the comment is being posted. They must
106
+ all match before the comment is accepted.
107
+
108
+ Furthermore, we place a cooldown (i.e. you must wait X seconds before you can post using that token) and an expiration on these comment tokens.
109
+ The reasons for this are:
110
+
111
+ 1. Cooldown means that a spambot cannot load a page, read the unique comment token and immediately re-post a comment to that page. It must wait
112
+ a while. This has the effect of slowing down the spambots, and, if the spambots get it wrong, they've wasted that token - as tokens can only
113
+ be used once.
114
+ 1. Expirations mean that a spambot cannot get the token and use it whenever it likes, it must use it within the specfied time.
115
+
116
+ This all combines to make it much more difficult for spambots (and also human spammers as they have to now wait) to work their dirty magic :)
117
 
118
  == Installation ==
119
 
120
  See FAQs.
121
 
122
+ == Installation ==
123
 
124
+ Note: When you enable the plugin, the firewall is not automatically turned on. This plugin contains various different sections of
125
+ protection for your site and you should choose which you need based on your own requirements.
126
+
127
+ Why do we do this? Simple, performance and optimization - there is no reason to automatically turn on features for people that don't
128
+ need it as each site and set of requirements is different.
129
 
130
  This plugin should install as any other WordPress.org respository plugin.
131
 
143
 
144
  A new menu item will appear on the left-hand side called 'Simple Firewall'.
145
 
146
+ == Frequently Asked Questions ==
147
+
148
+ = My server has a firewall, why do I need this plugin? =
149
+
150
+ This plugin is more of an application firewall, rather than a server/network firewall. It is designed to interpret web calls to your site to
151
+ look for and find attempts to circumvent it and gain unauthorized access or cause damage.
152
+
153
+ Your network firewall is designed to restrict access to your server based on certain types of network traffic. The WordPress Simple Firewall
154
+ is designed to restrict access to your site, based on certain type of web calls.
155
+
156
  = How does the IP Whitelist work? =
157
 
158
  Any IP address that is on the whitelist will not be subject to any of the firewall scanning/processing. This setting takes priority over all other settings.
159
 
160
+ = Does the IP Whitelist/Blacklist support IP ranges? =
161
 
162
+ Yes. To specify a range you do something like: 192.168.1.10-192.168.1.20
163
 
164
+ = I've locked myself out from my own site! =
 
165
 
166
+ This happens when any the following 3 conditions are met:
167
 
168
+ * you have added your IP address to the firewall blacklist,
169
+ * you have enabled 2 factor authentication and email doesn't work on your site (and you haven't chosen the override option)
 
 
170
 
171
+ You can completely turn OFF (and ON) the WordPress Simple Firewall by creating a special file in the plugin folder.
172
 
173
  Here's how:
174
 
179
 
180
  If you want to turn the firewall on in the same way, create a file called "forceOn".
181
 
182
+ Remember: If you leave one of these files on the server, it will override your on/off settings, so you should delete it when you no longer need it.
183
 
184
  = Which takes precedence... whitelist or blacklist? =
185
 
186
  Whitelist. So if you have the same address in both lists, it'll be whitelisted and allowed to pass before the blacklist comes into effect.
187
 
188
+ = How does the pages/parameters whitelist work? =
189
+
190
+ It is a comma-separated list of pages and parameters. A NEW LINE should be taken for each new page name and its associated parameters.
191
+
192
+ The first entry on each line (before the first comma) is the page name. The rest of the items on the line are the parameters.
193
+
194
+ The following are some simple examples to illustrate:
195
+
196
+ **edit.php, featured**
197
+
198
+ On the edit.php page, the parameter with the name 'featured' will be ignored.
199
+
200
+ **admin.php, url, param01, password**
201
+
202
+ Any parameters that are passed to the page ending in 'admin.php' with the names 'url', 'param01' and 'password' will
203
+ be excluded from the firewall processing.
204
+
205
+ *, url, param, password
206
+
207
+ Putting a star first means that these exclusions apply to all pages. So for every page that is accessed, all the parameters
208
+ that are url, param and password will be ignored by the firewall.
209
+
210
+ = How does the login cooldown work? =
211
+
212
+ When enabled the plugin will prevent more than 1 login attempt to your site every "so-many" seconds. So if you enable a login cooldown
213
+ of 60 seconds, only 1 login attempt will be processed every 60 seconds. If you login incorrectly, you wont be able to attempt another
214
+ login for a further 60 seconds.
215
+
216
+ More Info: http://icwp.io/2t
217
+
218
+ = How does the GASP login protection work? =
219
+
220
+ This is best described on the blog: http://icwp.io/2u
221
+
222
+ = How does the 2-factor authentication work? =
223
+
224
+ Best described here: http://icwp.io/2v
225
+
226
  == Screenshots ==
227
 
228
  == Changelog ==
229
 
230
+ = TODO =
231
+
232
+ * ADD: Add various WordPress security features dynamically that would otherwise require wp-config.php editing.
233
+ * CHANGE: Interface to give a better "At-A-Glance" Dashboard summary view, that also allows you to turn on/off core features.
234
+
235
+ = 1.9.2 =
236
+
237
+ * CHANGED: Simplified the automatic WordPress Plugin updates into 1 filter for consistency
238
+
239
+ = 1.9.1 =
240
+
241
+ * ADDED: Increased admin access security features - blocks the deactivation of itself if you're not authenticated fully with the plugin.
242
+ * ADDED: If you're not authenticated with the plugin, the plugin listing view wont have 'Deactivate' or 'Edit' links.
243
+
244
+ = 1.9.0 =
245
+
246
+ * ADDED: New WordPress Automatic Updates Configuration settings
247
+
248
+ = 1.8.2 =
249
+
250
+ * ADDED: Notification of available plugin upgrade is now an option under the 'Dashboard'
251
+ * CHANGED: Certain admin and upgrade notices now only appear when you're authenticated with the plugin (if this is enabled)
252
+ * FIXED: PHP Notice with undefined index.
253
+
254
+ = 1.8.1 =
255
+
256
+ * ADDED: Feature- Access Key Restriction [more info](http://icwp.io/2s).
257
+ * ADDED: Feature- WordPress Lockdown. Currently only provides 1 option, but more to come.
258
+
259
+ = 1.7.3 =
260
+
261
+ * CHANGED: Reworked a lot of the plugin to optimize for further performance.
262
+ * FIX: Potential infinite loop in processing firewall.
263
+
264
+ = 1.7.1 =
265
+
266
+ * ADDED: Much more efficiency yet again in the loading/saving of the plugin options.
267
+
268
+ = 1.7.0 =
269
+
270
+ * ADDED: Preliminary WordPress Multisite (WPMS/WPMU) Support.
271
+ * CHANGED: The Firewall now kicks in on the 'plugins_loaded' hook instead of as the actual firewall plugin is initialized (as a result
272
+ of WP Multisite support).
273
+
274
+ = 1.6.2 =
275
+
276
+ * REMOVED: Automatic upgrade option until I can ascertain what caused the plugin to auto-disable.
277
+
278
+ = 1.6.1 =
279
+
280
+ * ADDED: Options to fully customize the text displayed by the GASP comments section.
281
+ * ADDED: Option to include logged-in users in the GASP Comments Filter.
282
+
283
+ = 1.6.0 =
284
+
285
+ * ADDED: A new section - 'Comments Filtering' that will form the basis for filtering comments with SPAM etc.
286
+ * ADDED: Option to add enhanced GASP based comments filtering to prevent SPAM bots posting comments to your site.
287
+
288
+ = 1.5.6 =
289
+
290
+ * IMPROVED: Whitelist/Blacklist IP range processing to better cater for ranges when saving, with more thorough checking.
291
+ * IMPROVED: Whitelist/Blacklist IP range processing for 32-bit systems.
292
+ * FIXED: A bug with Whitelist/Blacklist IP checking.
293
+
294
+ = 1.5.5 =
295
+
296
+ * FIXED: Quite a few bugs fixed.
297
+
298
+ = 1.5.4 =
299
+
300
+ * FIXED: Typo error.
301
+
302
+ = 1.5.3 =
303
+
304
+ * FIXED: Some of the firewall processors were saving unnecessary data.
305
+
306
+ = 1.5.2 =
307
+
308
+ * CHANGED: The method for finding the client IP address is more thorough, in a bid to work with Proxy servers etc.
309
+ * FIXED: PHP notice reported here: http://wordpress.org/support/topic/getting-errors-when-logged-in
310
+
311
+ = 1.5.1 =
312
+
313
+ * FIXED: Bug fix where IP address didn't show in email.
314
+ * FIXED: Attempt to fix problem where update message never hides.
315
+
316
+ = 1.5.0 =
317
+
318
+ * ADDED: A new IP whitelist on the Login Protect that lets you by-pass login protect rules for given IP addresses.
319
+ * REMOVED: Firewall rule for wp-login.php and whitelisted IPs.
320
+
321
+ = 1.4.2 =
322
+
323
+ * ADDED: The plugin now has an option to automatically upgrade itself when an update is detected - enabled by default.
324
+
325
+ = 1.4.1 =
326
+
327
+ * ADDED: The plugin will now displays an admin notice when a plugin upgrade is available with a link to immediately update.
328
+ * ADDED: Plugin collision: removes the main hook by 'All In One WordPress Security'. No need to have both plugins running.
329
+ * ADDED: Improved Login Cooldown Feature- works more like email throttling as it now uses an extra filesystem-based level of protection.
330
+ * FIXED: Login Cooldown Feature didn't take effect in certain circumstances.
331
+
332
+ = 1.4.0 =
333
+
334
+ * ADDED: All-new plugin options handling making them more efficient, easier to manage/update, using far fewer WordPress database options.
335
+ * CHANGED: Huge improvements on database calls and efficiency in loading plugin options.
336
+ * FIXED: Nonce implementation.
337
+
338
+ = 1.3.2 =
339
+
340
+ * FIXED: Small compatibility issue with Quick Cache menu not showing.
341
+
342
+ = 1.3.0 =
343
+
344
+ * ADDED: Email Throttle Feature - this will prevent you getting bombarded by 1000s of emails in case you're hit by a bot.
345
+ * ADDED: Another Firewall die() option. New option will print a message and uses the wp_die() function instead.
346
+ * ADDED: Refactored and improved the logging system (upgrading will delete your current logs!).
347
+ * ADDED: Option to separately log Login Protect features.
348
+ * ADDED: Option to by-pass 2-factor authentication in the case sending the verification email fails
349
+ (so you don't get locked out if your hosting doesn't support email!).
350
+ * CHANGED: Login Protect checking now better logs out users immediately with a redirect.
351
+ * CHANGED: We now escape the log data being printed - just in case there's any HTML/JS etc in there we don't want.
352
+ * CHANGED: Optimized and cleaned a lot of the option caching code to improve reliability and performance (more to come).
353
+
354
+ = 1.2.7 =
355
+
356
+ * FIX: Bug where the GASP Login protection was only working when you had 2-factor authentication enabled.
357
+
358
+ = 1.2.6 =
359
+
360
+ * ADDED: Ability to import settings from WordPress Firewall 2 plugin options - note, doesn't import page and variables whitelisting.
361
+ * FIX: A reported bug - parameter values could also be arrays.
362
+
363
+ = 1.2.5 =
364
+
365
+ * ADDED: New Feature - Option to add a checkbox that blocks automated SPAM Bots trying to log into your site.
366
+ * ADDED: Added a clear user message when they verify their 2-factor authentication.
367
+ * FIX: A few bugfixes and logic corrections.
368
+
369
+ = 1.2.4 =
370
+
371
+ * CHANGED: Documentation on the dashboard, and the message after installing the firewall have been updated to be clearer and more informative.
372
+ * FIX: A few bugfixes and logic corrections.
373
+
374
+ = 1.2.3 =
375
+
376
+ * FIX: bugfix.
377
+
378
+ = 1.2.2 =
379
+
380
+ * FIX: Some warnings and display bugs.
381
+
382
+ = 1.2.1 =
383
+
384
+ * ADDED: New Feature - Login Wait Interval. To reduce the effectiveness of brute force login attacks, you can add an interval by
385
+ which WordPress will wait before processing any more login attempts on a site.
386
+ * CHANGED: Optimized some settings for performance.
387
+ * CHANGED: Cleaned up the UI when the Firewall / Login Protect features are disabled (more to come).
388
+ * CHANGED: Further code improvements (more to come).
389
+
390
+ = 1.2.0 =
391
+
392
+ * ADDED: New Feature - **Login Protect**. Added 2-Factor Login Authentication for all users and their associated IP addresses.
393
+ * CHANGED: The method for processing the IP address lists is improved.
394
+ * CHANGED: Improved .htaccess rules (thanks MickeyRoush)
395
+ * CHANGED: Mailing method now uses WP_MAIL
396
+ * CHANGED: Lot's of code improvements.
397
+
398
+ = 1.1.6 =
399
+
400
+ * ADDED: Option to include Cookies in the firewall checking.
401
+
402
+ = 1.1.5 =
403
+
404
+ * ADDED: Ability to whitelist particular pages and their parameters (see FAQ)
405
+ * CHANGED: Quite a few improvements made to the reliability of the firewall processing.
406
+
407
+ = 1.1.4 =
408
+
409
+ * FIX: Left test path in plugin.
410
+
411
+ = 1.1.3 =
412
+
413
+ * ADDED: Option to completely ignore logged-in Administrators from the Firewall processing (they wont even trigger logging etc).
414
+ * ADDED: Ability to (un)blacklist and (un)whitelist IP addresses directly from within the log.
415
+ * ADDED: helpful link to IP WHOIS from within the log.
416
+
417
+ = 1.1.2 =
418
+
419
+ * CHANGED: Logging now has its own dedicated database table.
420
+
421
+ = 1.1.1 =
422
+
423
+ * Fix: Block notification emails weren't showing the user-friendly IP Address format.
424
+
425
+ = 1.1.0 =
426
+
427
+ * You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hypen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10
428
+ * You can now specify which email address to send the notification emails.
429
+ * You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (don't take a new line).
430
+ * You can now set to delete ALL firewall settings when you deactivate the plugin.
431
+ * Improved formatting of the firewall log.
432
+
433
+ = 1.0.2 =
434
  * First Release
435
 
436
  == Upgrade Notice ==
437
 
438
+ = 1.1.2 =
439
+
440
+ * CHANGED: Logging now has its own dedicated database table.
441
+ * Fix: Block notification emails weren't showing the user-friendly IP Address format.
442
+ * You can now specify IP ranges in whitelists and blacklists. To do this separate the start and end address with a hypen (-) E.g. For everything between 1.2.3.4 and 1.2.3.10, you would do: 1.2.3.4-1.2.3.10
443
+ * You can now specify which email address to send the notification emails.
444
+ * You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (don't take a new line).
445
+ * You can now set to delete ALL firewall settings when you deactivate the plugin.
446
+ * Improved formatting of the firewall log.
src/icwp-base-processor.php ADDED
@@ -0,0 +1,353 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * Version: 2013-08-27-B
7
+ *
8
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
9
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
12
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
14
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
15
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
16
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
17
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
+ *
19
+ */
20
+
21
+ if ( !class_exists('ICWP_BaseProcessor_WPSF') ):
22
+
23
+ class ICWP_BaseProcessor_WPSF {
24
+
25
+ const PcreDelimiter = '/';
26
+ const LOG_MESSAGE_LEVEL_INFO = 0;
27
+ const LOG_MESSAGE_LEVEL_WARNING = 1;
28
+ const LOG_MESSAGE_LEVEL_CRITICAL = 2;
29
+
30
+ const LOG_CATEGORY_DEFAULT = 0;
31
+ const LOG_CATEGORY_FIREWALL = 1;
32
+ const LOG_CATEGORY_LOGINPROTECT = 2;
33
+
34
+ /**
35
+ * @var boolean
36
+ */
37
+ protected $m_fNeedSave;
38
+
39
+ /**
40
+ * @var array
41
+ */
42
+ protected $m_aLog;
43
+ /**
44
+ * @var array
45
+ */
46
+ protected $m_aLogMessages;
47
+
48
+ /**
49
+ * @var long
50
+ */
51
+ protected $m_nRequestIp;
52
+
53
+ /**
54
+ * @var boolean
55
+ */
56
+ protected $m_fLoggingEnabled;
57
+
58
+ /**
59
+ * @var ICWP_EmailProcessor
60
+ */
61
+ protected $m_oEmailHandler;
62
+
63
+ /**
64
+ * @var array
65
+ */
66
+ protected $m_aOptions;
67
+
68
+ /**
69
+ * @var ICWP_OptionsHandler_Base_WPSF
70
+ */
71
+ protected $m_oOptionsHandler;
72
+
73
+ public function __construct() {
74
+ $this->m_fNeedSave = true;
75
+ $this->reset();
76
+ }
77
+
78
+ /**
79
+ * Resets the object values to be re-used anew
80
+ */
81
+ public function reset() {
82
+ $this->m_nRequestIp = self::GetVisitorIpAddress();
83
+ $this->resetLog();
84
+ }
85
+
86
+ /**
87
+ * Ensure that when we save the object later, it doesn't save unnecessary data.
88
+ */
89
+ public function doPreStore() {
90
+ unset( $this->m_oEmailHandler );
91
+ }
92
+
93
+ /**
94
+ * @param string $infKey
95
+ */
96
+ public function store( $infKey ) {
97
+ if ( $this->getNeedSave() ) {
98
+ $this->doPreStore();
99
+ $this->setNeedSave( false );
100
+ update_option( $infKey, $this );
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @return boolean
106
+ */
107
+ public function getNeedSave() {
108
+ return $this->m_fNeedSave;
109
+ }
110
+
111
+ /**
112
+ * @param boolean $infNeedSave
113
+ */
114
+ public function setNeedSave( $infNeedSave = true ) {
115
+ $this->m_fNeedSave = $infNeedSave;
116
+ }
117
+
118
+ /**
119
+ *
120
+ * @param array $inoOptions
121
+ */
122
+ public function setOptions( &$inaOptions ) {
123
+ $this->m_aOptions = $inaOptions;
124
+ }
125
+ /**
126
+ *
127
+ * @param array $inoOptionsHandler
128
+ */
129
+ public function setOptionsHandler( ICWP_OptionsHandler_Base_WPSF &$inoOptionsHandler ) {
130
+ $this->m_oOptionsHandler = $inoOptionsHandler;
131
+ $this->m_aOptions = $this->m_oOptionsHandler->getPluginOptionsValues();
132
+ }
133
+
134
+ /**
135
+ * Resets the log
136
+ */
137
+ public function resetLog() {
138
+ $this->m_aLogMessages = array();
139
+ }
140
+
141
+ /**
142
+ * @param boolean $infEnableLogging
143
+ */
144
+ public function setLogging( $infEnableLogging = true ) {
145
+ $this->m_fLoggingEnabled = $infEnableLogging;
146
+ }
147
+
148
+ /**
149
+ * Builds and returns the full log.
150
+ *
151
+ * @return array (associative)
152
+ */
153
+ public function getLogData() {
154
+
155
+ if ( $this->m_fLoggingEnabled ) {
156
+ $this->m_aLog = array(
157
+ 'messages' => serialize( $this->m_aLogMessages ),
158
+ );
159
+ }
160
+ else {
161
+ $this->m_aLog = false;
162
+ }
163
+
164
+ return $this->m_aLog;
165
+ }
166
+
167
+ /**
168
+ * @param string $insLogMessage
169
+ * @param string $insMessageType
170
+ */
171
+ public function writeLog( $insLogMessage = '', $insMessageType = self::LOG_MESSAGE_LEVEL_INFO ) {
172
+ if ( !is_array( $this->m_aLogMessages ) ) {
173
+ $this->resetLog();
174
+ }
175
+ $this->m_aLogMessages[] = array( $insMessageType, $insLogMessage );
176
+ }
177
+ /**
178
+ * @param string $insLogMessage
179
+ */
180
+ public function logInfo( $insLogMessage ) {
181
+ $this->writeLog( $insLogMessage, self::LOG_MESSAGE_LEVEL_INFO );
182
+ }
183
+ /**
184
+ * @param string $insLogMessage
185
+ */
186
+ public function logWarning( $insLogMessage ) {
187
+ $this->writeLog( $insLogMessage, self::LOG_MESSAGE_LEVEL_WARNING );
188
+ }
189
+ /**
190
+ * @param string $insLogMessage
191
+ */
192
+ public function logCritical( $insLogMessage ) {
193
+ $this->writeLog( $insLogMessage, self::LOG_MESSAGE_LEVEL_CRITICAL );
194
+ }
195
+
196
+ /**
197
+ * Cloudflare compatible.
198
+ *
199
+ * @return number - visitor IP Address as IP2Long
200
+ */
201
+ public static function GetVisitorIpAddress( $infAsLong = true ) {
202
+
203
+ $aAddressSourceOptions = array(
204
+ 'HTTP_CLIENT_IP',
205
+ 'HTTP_X_FORWARDED_FOR',
206
+ 'HTTP_X_FORWARDED',
207
+ 'HTTP_FORWARDED',
208
+ 'REMOTE_ADDR'
209
+ );
210
+
211
+ $fCanUseFilter = function_exists( 'filter_var' ) && defined( 'FILTER_FLAG_NO_PRIV_RANGE' ) && defined( 'FILTER_FLAG_IPV4' );
212
+
213
+ $aIpAddresses = array();
214
+ foreach( $aAddressSourceOptions as $sOption ) {
215
+ if ( empty( $_SERVER[ $sOption ] ) ) {
216
+ continue;
217
+ }
218
+ $sIpAddressToTest = $_SERVER[ $sOption ];
219
+
220
+ $aIpAddresses = explode( ',', $sIpAddressToTest ); //sometimes a comma-separated list is returned
221
+ foreach( $aIpAddresses as $sIpAddress ) {
222
+
223
+ if ( $fCanUseFilter && !self::IsAddressInPublicIpRange( $sIpAddress ) ) {
224
+ continue;
225
+ }
226
+ else {
227
+ return $infAsLong? ip2long( $sIpAddress ) : $sIpAddress;
228
+ }
229
+ }
230
+ }
231
+ return false;
232
+ }
233
+
234
+ /**
235
+ * Assumes a valid IPv4 address is provided as we're only testing for a whether the IP is public or not.
236
+ *
237
+ * @param string $insIpAddress
238
+ * @uses filter_var
239
+ */
240
+ public static function IsAddressInPublicIpRange( $insIpAddress ) {
241
+ return filter_var( $insIpAddress, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE );
242
+ }
243
+
244
+ /**
245
+ * @param array $inaIpList
246
+ * @param integer $innIpAddress
247
+ * @return boolean
248
+ */
249
+ public function isIpOnlist( $inaIpList, $innIpAddress = '', &$outsLabel = '' ) {
250
+
251
+ if ( empty( $innIpAddress ) || !isset( $inaIpList['ips'] ) ) {
252
+ return false;
253
+ }
254
+
255
+ $outsLabel = '';
256
+ foreach( $inaIpList['ips'] as $mWhitelistAddress ) {
257
+
258
+ $aIps = $this->parseIpAddress( $mWhitelistAddress );
259
+ if ( count( $aIps ) === 1 ) { //not a range
260
+ if ( $innIpAddress == $aIps[0] ) {
261
+ $outsLabel = $inaIpList['meta'][ md5( $mWhitelistAddress ) ];
262
+ return true;
263
+ }
264
+ }
265
+ else if ( count( $aIps ) == 2 ) {
266
+ if ( $aIps[0] <= $innIpAddress && $innIpAddress <= $aIps[1] ) {
267
+ $outsLabel = $inaIpList['meta'][ md5( $mWhitelistAddress ) ];
268
+ return true;
269
+ }
270
+ }
271
+ }
272
+ return false;
273
+ }
274
+
275
+ /**
276
+ * @param string $insIpAddress - an IP or IP address range in LONG format.
277
+ * @return array - with 1 ip address, or 2 addresses if it is a range.
278
+ */
279
+ protected function parseIpAddress( $insIpAddress ) {
280
+
281
+ $aIps = array();
282
+
283
+ if ( empty($insIpAddress) ) {
284
+ return $aIps;
285
+ }
286
+
287
+ // offset=1 in the case that it's a range and the first number is negative on 32-bit systems
288
+ $mPos = strpos( $insIpAddress, '-', 1 );
289
+
290
+ if ( $mPos === false ) { //plain IP address
291
+ $aIps[] = $insIpAddress;
292
+ }
293
+ else {
294
+ //we remove the first character in case this is '-'
295
+ $aParts = array( substr( $insIpAddress, 0, 1 ), substr( $insIpAddress, 1 ) );
296
+ list( $sStart, $sEnd ) = explode( '-', $aParts[1], 2 );
297
+ $aIps[] = $aParts[0].$sStart;
298
+ $aIps[] = $sEnd;
299
+ }
300
+ return $aIps;
301
+ }
302
+
303
+ /**
304
+ * We force PHP to pass by reference in case of older versions of PHP (?)
305
+ *
306
+ * @param ICWP_EmailProcessor $inoEmailHandler
307
+ */
308
+ public function setEmailHandler( ICWP_EmailProcessor &$inoEmailHandler ) {
309
+ $this->m_oEmailHandler = $inoEmailHandler;
310
+ }
311
+
312
+ /**
313
+ * @param string $insEmailSubject - message subject
314
+ * @param array $inaMessage - message content
315
+ * @return boolean - message sending success (remember that if throttled, returns true)
316
+ */
317
+ public function sendEmail( $insEmailSubject, $inaMessage ) {
318
+ return $this->m_oEmailHandler->sendEmail( $insEmailSubject, $inaMessage );
319
+ }
320
+
321
+ /**
322
+ * @param string $insEmailAddress - message recipient
323
+ * @param string $insEmailSubject - message subject
324
+ * @param array $inaMessage - message content
325
+ * @return boolean - message sending success (remember that if throttled, returns true)
326
+ */
327
+ public function sendEmailTo( $insEmailAddress, $insEmailSubject, $inaMessage ) {
328
+ return $this->m_oEmailHandler->sendEmailTo( $insEmailAddress, $insEmailSubject, $inaMessage );
329
+ }
330
+
331
+ /**
332
+ * Checks the $inaData contains valid key values as laid out in $inaChecks
333
+ *
334
+ * @param array $inaData
335
+ * @param array $inaChecks
336
+ * @return boolean
337
+ */
338
+ protected function validateParameters( $inaData, $inaChecks ) {
339
+
340
+ if ( !is_array( $inaData ) ) {
341
+ return false;
342
+ }
343
+
344
+ foreach( $inaChecks as $sCheck ) {
345
+ if ( !array_key_exists( $sCheck, $inaData ) || empty( $inaData[ $sCheck ] ) ) {
346
+ return false;
347
+ }
348
+ }
349
+ return true;
350
+ }
351
+ }
352
+
353
+ endif;
src/icwp-basedb-processor.php ADDED
@@ -0,0 +1,130 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ *
17
+ */
18
+
19
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
20
+
21
+ if ( !class_exists('ICWP_BaseDbProcessor_WPSF') ):
22
+
23
+ class ICWP_BaseDbProcessor_WPSF extends ICWP_BaseProcessor_WPSF {
24
+
25
+ const DB_TABLE_PREFIX = 'icwp_';
26
+
27
+ /**
28
+ * A link to the WordPress Database object so we don't have to "global" that every time.
29
+ * @var wpdb
30
+ */
31
+ protected $m_oWpdb;
32
+
33
+ /**
34
+ * The full database table name.
35
+ * @var string
36
+ */
37
+ protected $m_sTableName;
38
+
39
+ public function __construct( $insTableName ) {
40
+ parent::__construct();
41
+ $this->reset();
42
+ $this->setTableName( $insTableName );
43
+ }
44
+
45
+ /**
46
+ * Ensure that when we save the object later, it doesn't save unnecessary data.
47
+ */
48
+ public function doPreStore() {
49
+ parent::doPreStore();
50
+ unset( $this->m_oWpdb );
51
+ }
52
+
53
+ /**
54
+ * Resets the object values to be re-used anew
55
+ */
56
+ public function reset() {
57
+ parent::reset();
58
+ global $wpdb;
59
+ $this->m_oWpdb = $wpdb;
60
+ }
61
+
62
+ public function insertIntoTable( $inaData ) {
63
+ return $this->m_oWpdb->insert( $this->m_sTableName, $inaData );
64
+ }
65
+
66
+ public function selectAllFromTable( $innFormat = ARRAY_A ) {
67
+ $sQuery = sprintf( "SELECT * FROM `%s` WHERE `deleted_at` = '0'", $this->m_sTableName );
68
+ return $this->m_oWpdb->get_results( $sQuery, $innFormat );
69
+ }
70
+
71
+ public function selectCustomFromTable( $insQuery ) {
72
+ return $this->m_oWpdb->get_results( $insQuery, ARRAY_A );
73
+ }
74
+
75
+ public function selectRowFromTable( $insQuery ) {
76
+ return $this->m_oWpdb->get_row( $insQuery, ARRAY_A );
77
+ }
78
+
79
+ public function updateRowsFromTable( $inaData, $inaWhere ) {
80
+ return $this->m_oWpdb->update( $this->m_sTableName, $inaData, $inaWhere );
81
+ }
82
+
83
+ public function deleteRowsFromTable( $inaWhere ) {
84
+ return $this->m_oWpdb->delete( $this->m_sTableName, $inaWhere );
85
+ }
86
+
87
+ public function createTable() {
88
+ //Override this function to create the Table you want.
89
+ }
90
+
91
+ /**
92
+ * Will remove all data from this table (to delete the table see dropTable)
93
+ */
94
+ public function emptyTable() {
95
+ $sQuery = sprintf( "TRUNCATE TABLE `%s`", $this->m_sTableName );
96
+ return $this->doSql( $sQuery );
97
+ }
98
+
99
+ /**
100
+ * Will recreate the whole table
101
+ */
102
+ public function recreateTable() {
103
+ $this->dropTable();
104
+ $this->createTable();
105
+ }
106
+
107
+ /**
108
+ * Will completely remove this table from the database
109
+ */
110
+ public function dropTable() {
111
+ $sQuery = sprintf( 'DROP TABLE IF EXISTS `%s`', $this->m_sTableName ) ;
112
+ return $this->doSql( $sQuery );
113
+ }
114
+
115
+ /**
116
+ * Given any SQL query, will perform it using the WordPress database object.
117
+ *
118
+ * @param string $insSql
119
+ */
120
+ public function doSql( $insSql ) {
121
+ return $this->m_oWpdb->query( $insSql );
122
+ }
123
+
124
+ private function setTableName( $insTableName ) {
125
+ return $this->m_sTableName = $this->m_oWpdb->base_prefix . self::DB_TABLE_PREFIX . $insTableName;
126
+ }
127
+
128
+ }
129
+
130
+ endif;
src/icwp-data-processor.php CHANGED
@@ -3,6 +3,8 @@
3
  /**
4
  * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
5
  * All rights reserved.
 
 
6
  *
7
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
8
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
@@ -21,47 +23,167 @@ if ( !class_exists('ICWP_DataProcessor') ):
21
 
22
  class ICWP_DataProcessor {
23
 
 
 
24
  static public function ExtractIpAddresses( $insAddresses = '' ) {
25
 
26
- $aAddresses = array();
27
 
28
  if ( empty( $insAddresses ) ) {
29
- return $aAddresses;
30
  }
31
  $aRawList = array_map( 'trim', explode( "\n", $insAddresses ) );
32
-
33
- foreach( $aRawList as $sKey => $sRawAddress ) {
34
- $aRawList[ $sKey ] = self::Clean_Ip( $sRawAddress );;
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
 
 
 
 
36
 
37
- if ( function_exists('filter_var') && defined( FILTER_VALIDATE_IP ) ) {
38
- $fUseFilter = true;
 
39
  }
40
- else {
41
- $fUseFilter = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- foreach( $aRawList as $sAddress ) {
45
- if ( self::Verify_Ip( $sAddress, $fUseFilter ) ) {
46
- $aAddresses[] = $sAddress;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  }
48
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
- return array_unique( $aAddresses );
 
 
 
 
 
 
 
 
 
 
 
51
  }
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  public static function Clean_Ip( $insRawAddress ) {
54
  $insRawAddress = preg_replace( '/[a-z\s]/i', '', $insRawAddress );
55
  $insRawAddress = str_replace( '.', 'PERIOD', $insRawAddress );
 
56
  $insRawAddress = preg_replace( '/[^a-z0-9]/i', '', $insRawAddress );
57
  $insRawAddress = str_replace( 'PERIOD', '.', $insRawAddress );
 
58
  return $insRawAddress;
59
  }
60
 
61
- public static function Verify_Ip( $insIpAddress, $infUseFilter = false ) {
62
- if ( $infUseFilter ) {
63
  if ( filter_var( $insIpAddress, FILTER_VALIDATE_IP ) ) {
64
- return true;
65
  }
66
  }
67
  else {
@@ -73,11 +195,65 @@ class ICWP_DataProcessor {
73
  return false;
74
  }
75
  }
76
- return true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  }
 
78
  }
 
79
  }
 
 
 
 
 
 
 
 
 
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  }
82
 
83
  endif;
3
  /**
4
  * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
5
  * All rights reserved.
6
+ *
7
+ * Version: 2013-08-27-A
8
  *
9
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
10
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23
 
24
  class ICWP_DataProcessor {
25
 
26
+ public static $fUseFilter = false;
27
+
28
  static public function ExtractIpAddresses( $insAddresses = '' ) {
29
 
30
+ $aRawAddresses = array();
31
 
32
  if ( empty( $insAddresses ) ) {
33
+ return $aRawAddresses;
34
  }
35
  $aRawList = array_map( 'trim', explode( "\n", $insAddresses ) );
36
+
37
+ self::$fUseFilter = function_exists('filter_var') && defined( FILTER_VALIDATE_IP );
38
+
39
+ foreach( $aRawList as $sKey => $sRawAddressLine ) {
40
+
41
+ if ( empty( $sRawAddressLine ) ) {
42
+ continue;
43
+ }
44
+
45
+ // Each line can have a Label which is the IP separated with a space.
46
+ $aParts = explode( ' ', $sRawAddressLine, 2 );
47
+ if ( count( $aParts ) == 1 ) {
48
+ $aParts[] = '';
49
+ }
50
+ $aRawAddresses[ $aParts[0] ] = trim( $aParts[1] );
51
  }
52
+ return self::Add_New_Raw_Ips( array(), $aRawAddresses );
53
+ }
54
+
55
+ static public function ExtractCommaSeparatedList( $insRawList = '' ) {
56
 
57
+ $aRawList = array();
58
+ if ( empty( $insRawList ) ) {
59
+ return $aRawList;
60
  }
61
+ // $aRawList = array_map( 'trim', explode( "\n", $insRawList ) );
62
+ $aRawList = array_map( 'trim', preg_split( '/\r\n|\r|\n/', $insRawList ) );
63
+ $aNewList = array();
64
+ $fHadStar = false;
65
+ foreach( $aRawList as $sKey => $sRawLine ) {
66
+
67
+ if ( empty( $sRawLine ) ) {
68
+ continue;
69
+ }
70
+ $sRawLine = str_replace( ' ', '', $sRawLine );
71
+ $aParts = explode( ',', $sRawLine, 2 );
72
+ // we only permit 1x line beginning with *
73
+ if ( $aParts[0] == '*' ) {
74
+ if ( $fHadStar ) {
75
+ continue;
76
+ }
77
+ $fHadStar = true;
78
+ }
79
+ else {
80
+ //If there's only 1 item on the line, we assume it to be a global
81
+ // parameter rule
82
+ if ( count( $aParts ) == 1 || empty( $aParts[1] ) ) { // there was no comma in this line in the first place
83
+ array_unshift( $aParts, '*' );
84
+ }
85
+ }
86
+
87
+ $aParams = empty( $aParts[1] )? array() : explode( ',', $aParts[1] );
88
+ $aNewList[ $aParts[0] ] = $aParams;
89
  }
90
+ return $aNewList;
91
+ }
92
+
93
+ /**
94
+ * Given a list of new IPv4 address ($inaNewRawAddresses) it'll add them to the existing list
95
+ * ($inaCurrent) where they're not already found
96
+ *
97
+ * @param array $inaCurrent - the list to which to add the new addresses
98
+ * @param array $inaNewRawAddresses - the new IPv4 addresses
99
+ * @param int $outnNewAdded - the count of newly added IPs
100
+ * @return unknown|Ambigous <multitype:multitype: , string>
101
+ */
102
+ public static function Add_New_Raw_Ips( $inaCurrent, $inaNewRawAddresses, &$outnNewAdded = 0 ) {
103
 
104
+ if ( empty( $inaNewRawAddresses ) ) {
105
+ return $inaCurrent;
106
+ }
107
+
108
+ if ( !array_key_exists( 'ips', $inaCurrent ) ) {
109
+ $inaCurrent['ips'] = array();
110
+ }
111
+ if ( !array_key_exists( 'meta', $inaCurrent ) ) {
112
+ $inaCurrent['meta'] = array();
113
+ }
114
+
115
+ foreach( $inaNewRawAddresses as $sRawIpAddress => $sLabel ) {
116
+ $mVerifiedIp = self::Verify_Ip( $sRawIpAddress );
117
+ if ( $mVerifiedIp !== false && !in_array( $mVerifiedIp, $inaCurrent['ips'] ) ) {
118
+ $inaCurrent['ips'][] = $mVerifiedIp;
119
+ if ( empty($sLabel) ) {
120
+ $sLabel = 'no label';
121
+ }
122
+ $inaCurrent['meta'][ md5( $mVerifiedIp ) ] = $sLabel;
123
+ $outnNewAdded++;
124
  }
125
  }
126
+ return $inaCurrent;
127
+ }
128
+
129
+ /**
130
+ * @param array $inaCurrent
131
+ * @param array $inaRawAddresses - should be a plain numerical array of IPv4 addresses
132
+ * @return array:
133
+ */
134
+ public static function Remove_Raw_Ips( $inaCurrent, $inaRawAddresses ) {
135
+ if ( empty( $inaRawAddresses ) ) {
136
+ return $inaCurrent;
137
+ }
138
+
139
+ if ( !array_key_exists( 'ips', $inaCurrent ) ) {
140
+ $inaCurrent['ips'] = array();
141
+ }
142
+ if ( !array_key_exists( 'meta', $inaCurrent ) ) {
143
+ $inaCurrent['meta'] = array();
144
+ }
145
 
146
+ foreach( $inaRawAddresses as $sRawIpAddress ) {
147
+ $mVerifiedIp = self::Verify_Ip( $sRawIpAddress );
148
+ if ( $mVerifiedIp === false ) {
149
+ continue;
150
+ }
151
+ $mKey = array_search( $mVerifiedIp, $inaCurrent['ips'] );
152
+ if ( $mKey !== false ) {
153
+ unset( $inaCurrent['ips'][$mKey] );
154
+ unset( $inaCurrent['meta'][ md5( $mVerifiedIp ) ] );
155
+ }
156
+ }
157
+ return $inaCurrent;
158
  }
159
 
160
+ public static function Verify_Ip( $insIpAddress ) {
161
+
162
+ $sAddress = self::Clean_Ip( $insIpAddress );
163
+
164
+ // Now, determine if this is an IP range, or just a plain IP address.
165
+ if ( strpos( $sAddress, '-' ) === false ) { //plain IP address
166
+ return self::Verify_Ip_Address( $sAddress );
167
+ }
168
+ else {
169
+ return self::Verify_Ip_Range( $sAddress );
170
+ }
171
+ }
172
+
173
  public static function Clean_Ip( $insRawAddress ) {
174
  $insRawAddress = preg_replace( '/[a-z\s]/i', '', $insRawAddress );
175
  $insRawAddress = str_replace( '.', 'PERIOD', $insRawAddress );
176
+ $insRawAddress = str_replace( '-', 'HYPEN', $insRawAddress );
177
  $insRawAddress = preg_replace( '/[^a-z0-9]/i', '', $insRawAddress );
178
  $insRawAddress = str_replace( 'PERIOD', '.', $insRawAddress );
179
+ $insRawAddress = str_replace( 'HYPEN', '-', $insRawAddress );
180
  return $insRawAddress;
181
  }
182
 
183
+ public static function Verify_Ip_Address( $insIpAddress ) {
184
+ if ( self::$fUseFilter ) {
185
  if ( filter_var( $insIpAddress, FILTER_VALIDATE_IP ) ) {
186
+ return ip2long( $insIpAddress );
187
  }
188
  }
189
  else {
195
  return false;
196
  }
197
  }
198
+ return ip2long( $insIpAddress );
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+
204
+ /**
205
+ * The only ranges currently accepted are a.b.c.d-f.g.h.j
206
+ * @param string $insIpAddressRange
207
+ * @return string|boolean
208
+ */
209
+ public static function Verify_Ip_Range( $insIpAddressRange ) {
210
+
211
+ list( $sIpRangeStart, $sIpRangeEnd ) = explode( '-', $insIpAddressRange, 2 );
212
+
213
+ if ( $sIpRangeStart == $sIpRangeEnd ) {
214
+ return self::Verify_Ip_Address( $sIpRangeStart );
215
+ }
216
+ else if ( self::Verify_Ip_Address( $sIpRangeStart ) && self::Verify_Ip_Address( $sIpRangeEnd ) ) {
217
+ $nStart = ip2long( $sIpRangeStart );
218
+ $nEnd = ip2long( $sIpRangeEnd );
219
+
220
+ // do our best to order it
221
+ if (
222
+ ( $nStart > 0 && $nEnd > 0 && $nStart > $nEnd )
223
+ || ( $nStart < 0 && $nEnd < 0 && $nStart > $nEnd )
224
+ ) {
225
+ $nTemp = $nStart;
226
+ $nStart = $nEnd;
227
+ $nEnd = $nTemp;
228
  }
229
+ return $nStart.'-'.$nEnd;
230
  }
231
+ return false;
232
  }
233
+
234
+ /**
235
+ * @param integer $innLength
236
+ * @param boolean $infBeginLetter
237
+ * @return string
238
+ */
239
+ static public function GenerateRandomString( $innLength = 10, $infBeginLetter = false ) {
240
+ $aChars = array( 'abcdefghijkmnopqrstuvwxyz' );
241
+ $aChars[] = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
242
 
243
+ $sCharset = implode( '', $aChars );
244
+ if ( $infBeginLetter ) {
245
+ $sPassword = $sCharset[ ( rand() % strlen( $sCharset ) ) ];
246
+ }
247
+ else {
248
+ $sPassword = '';
249
+ }
250
+ $sCharset .= '023456789';
251
+
252
+ for ( $i = $infBeginLetter? 1 : 0; $i < $innLength; $i++ ) {
253
+ $sPassword .= $sCharset[ ( rand() % strlen( $sCharset ) ) ];
254
+ }
255
+ return $sPassword;
256
+ }
257
  }
258
 
259
  endif;
src/icwp-firewall-processor.php DELETED
@@ -1,502 +0,0 @@
1
- <?php
2
- /**
3
- * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
- * All rights reserved.
5
- *
6
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
- * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
- *
17
- */
18
-
19
- if ( !class_exists('ICWP_DataProcessor') ):
20
-
21
- class ICWP_FirewallProcessor {
22
-
23
- const PcreDelimiter = '/';
24
- const LOG_MESSAGE_TYPE_INFO = 0;
25
- const LOG_MESSAGE_TYPE_WARNING = 1;
26
- const LOG_MESSAGE_TYPE_CRITICAL = 2;
27
-
28
- protected $m_sRequestId;
29
- protected $m_sRequestIp;
30
-
31
- protected $m_aBlockSettings;
32
- protected $m_sBlockResponse;
33
- protected $m_aWhitelistIps;
34
- protected $m_aBlacklistIps;
35
-
36
- protected $m_aWhitelistPages;
37
- protected $m_aWhitelistPagesPatterns;
38
-
39
- protected $m_aRequestUriParts;
40
-
41
- protected $m_aLog;
42
-
43
- /**
44
- * A combination of the current request $_GET and $_POST
45
- * @var array
46
- */
47
- protected $m_aPageParams;
48
-
49
- /**
50
- * All the remaining values of the page parameters after they've been filtered
51
- * @var array
52
- */
53
- protected $m_aPageParamValues;
54
-
55
- public function __construct( $inaBlockSettings, $inaIpWhitelist, $inaIpBlacklist, $insBlockResponse ) {
56
-
57
- $this->reset();
58
- $this->m_aBlockSettings = $inaBlockSettings;
59
- $this->m_sBlockResponse = $insBlockResponse;
60
- $this->m_aWhitelistIps = $inaIpWhitelist;
61
- $this->m_aBlacklistIps = $inaIpBlacklist;
62
- }
63
-
64
- public function reset() {
65
- $this->m_sRequestIp = self::GetVisitorIpAddress();
66
- $this->m_sRequestId = $this->m_sRequestIp.'_'.uniqid();
67
- $this->m_aRequestUriParts = array();
68
- $this->m_aPageParams = array();
69
- $this->m_aPageParamValues = array();
70
- $this->resetLog();
71
- }
72
-
73
- public function resetLog() {
74
- if ( !is_array($this->m_aLog) ) {
75
- $this->m_aLog = array();
76
- }
77
- $this->m_aLog[ $this->m_sRequestId ] = array();
78
- }
79
-
80
- public function getLog() {
81
- return $this->m_aLog;
82
- }
83
-
84
- /**
85
- *
86
- * @param string $insLogMessage
87
- * @param string $insMessageType
88
- */
89
- public function writeLog( $insLogMessage = '', $insMessageType = self::LOG_MESSAGE_TYPE_INFO ) {
90
- if ( !isset( $this->m_aLog[ $this->m_sRequestId ] ) ) {
91
- $this->resetLog();
92
- }
93
- $this->m_aLog[ $this->m_sRequestId ][] = array( time(), $insMessageType, $insLogMessage );
94
- }
95
-
96
- public function doFirewallCheck() {
97
-
98
- //Check if the visitor is excluded from the firewall from the outset.
99
- if ( $this->isVisitorOnWhitelist() ) {
100
- $this->logInfo( 'Visitor is whitelisted by IP Address' );
101
- return true;
102
- }
103
- else {
104
- $this->logInfo( 'Visitor is NOT whitelisted by IP Address' );
105
- }
106
-
107
- //Check if the visitor is excluded from the firewall from the outset.
108
- if ( $this->isVisitorOnBlacklist() ) {
109
- $this->logWarning( 'Visitor is blacklisted by IP Address' );
110
- return false;
111
- }
112
- else {
113
- $this->logInfo( 'Visitor is NOT blacklisted by IP Address' );
114
- }
115
-
116
- // if we can't process the REQUEST_URI parts, we can't firewall so we effectively whitelist without erroring.
117
- if ( !$this->setRequestUriPageParts() ) {
118
- return true;
119
- }
120
-
121
- $fIsPermittedVisitor = true;
122
-
123
- //Checking this comes before all else but after the IP whitelist check.
124
- if ( $this->m_aBlockSettings[ 'block_wplogin_access' ] ) {
125
- $fIsPermittedVisitor = $this->doPassCheckBlockWpLogin();
126
- }
127
-
128
- // Set up the page parameters ($_GET and $_POST). If there are none, quit since there's nothing for the firewall to check.
129
- $this->setPageParams();
130
- if ( $fIsPermittedVisitor && empty( $this->m_aPageParams ) ) {
131
- $this->logInfo( 'There were no page parameters to check on this visit.' );
132
- return true;
133
- }
134
-
135
- // Check if the page and its parameters are whitelisted.
136
- if ( $fIsPermittedVisitor && $this->isPageWhitelisted() ) {
137
- return true;
138
- }
139
-
140
- // ensures we have a simple array with all the values that need to be checked.
141
- $this->m_aPageParamValues = array_values( $this->m_aPageParams );
142
-
143
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_dir_traversal' ] ) {
144
- $fIsPermittedVisitor = $this->doPassCheckBlockDirTraversal();
145
- }
146
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_sql_queries' ] ) {
147
- $fIsPermittedVisitor = $this->doPassCheckBlockSqlQueries();
148
- }
149
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_wordpress_terms' ] ) {
150
- $fIsPermittedVisitor = $this->doPassCheckBlockWordpressTerms();
151
- }
152
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_field_truncation' ] ) {
153
- $fIsPermittedVisitor = $this->doPassCheckBlockFieldTruncation();
154
- }
155
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_exe_file_uploads' ] ) {
156
- $fIsPermittedVisitor = $this->doPassCheckBlockExeFileUploads();
157
- }
158
- if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_leading_schema' ] ) {
159
- $fIsPermittedVisitor = $this->doPassCheckBlockLoadingSchema();
160
- }
161
-
162
- return $fIsPermittedVisitor || isset($_GET['testfirewall']); //testing
163
- }
164
-
165
- /**
166
- * This function assumes that isVisitorOnWhitelist() check has been run previously. Meaning the
167
- * current visitor is NOT on the whitelist and so if the page they're accessing is wp-login then
168
- * they do not pass this check.
169
- *
170
- * If whitelisted IPs is empty, we never block access.
171
- *
172
- * @return boolean
173
- */
174
- protected function doPassCheckBlockWpLogin() {
175
-
176
- list( $sRequestPage, $sRequestQuery ) = $this->m_aRequestUriParts;
177
-
178
- if ( substr_count( $sRequestPage, '/wp-login.php' ) > 0 ) {
179
-
180
- // We don't block wp-login.php if there are no whitelisted IPs.
181
- if ( count( $this->m_aWhitelistIps ) < 1 ) {
182
- $this->logInfo( 'Requested: Block access to wp-login.php, but this was skipped because whitelisted IPs list was empty.' );
183
- return true;
184
- }
185
-
186
- $this->logWarning( 'Requested: Block access to wp-login.php. Visitor not on IP whitelist and blocked by firewall.' );
187
- return false;
188
- }
189
- return true;
190
- }
191
-
192
- protected function doPassCheckBlockDirTraversal() {
193
- $aTerms = array(
194
- 'etc/passwd',
195
- 'proc/self/environ',
196
- '../'
197
- );
198
- $fPass = $this->doPassCheck( $this->m_aPageParamValues, $aTerms );
199
- if ( !$fPass ) {
200
- $this->logWarning( 'Requested: Block Directory Traversal. This visitor.' );
201
- }
202
- return $fPass;
203
- }
204
-
205
- protected function doPassCheckBlockSqlQueries() {
206
- $aTerms = array(
207
- '/concat\s*\(/i',
208
- '/group_concat/i',
209
- '/union.*select/i'
210
- );
211
- $fPass = $this->doPassCheck( $this->m_aPageParamValues, $aTerms, true );
212
- if ( !$fPass ) {
213
- $this->logWarning( 'Requested: Block access to wp-login.php, but this was skipped because whitelisted IPs list was empty.' );
214
- }
215
- return $fPass;
216
- }
217
-
218
- protected function doPassCheckBlockWordpressTerms() {
219
- $aTerms = array(
220
- '/wp_/i',
221
- '/user_login/i',
222
- '/user_pass/i',
223
- '/0x[0-9a-f][0-9a-f]/i',
224
- '/\/\*\*\//'
225
- );
226
- return $this->doPassCheck( $this->m_aPageParamValues, $aTerms, true );
227
- }
228
-
229
- protected function doPassCheckBlockFieldTruncation() {
230
- $aTerms = array(
231
- '/\s{49,}/i',
232
- '/\x00/'
233
- );
234
- return $this->doPassCheck( $this->m_aPageParamValues, $aTerms, true );
235
- }
236
-
237
- protected function doPassCheckBlockExeFileUploads() {
238
- $aTerms = array(
239
- '/\.dll$/i', '/\.rb$/i', '/\.py$/i', '/\.exe$/i', '/\.php[3-6]?$/i', '/\.pl$/i',
240
- '/\.perl$/i', '/\.ph[34]$/i', '/\.phl$/i', '/\.phtml$/i', '/\.phtm$/i'
241
- );
242
-
243
- if ( isset( $_FILES ) && !empty( $_FILES ) ) {
244
- $aFileNames = array();
245
- foreach( $_FILES as $aFile ) {
246
- if ( !empty( $aFile['name'] ) ) {
247
- $aFileNames[] = $aFile['name'];
248
- }
249
- }
250
- return $this->doPassCheck( $aFileNames, $aTerms, true );
251
- }
252
-
253
- return true;
254
- }
255
-
256
- protected function doPassCheckBlockLoadingSchema() {
257
- $aTerms = array(
258
- '/^http/i', '/\.shtml$/i'
259
- );
260
- return $this->doPassCheck( $this->m_aPageParamValues, $aTerms, true );
261
- }
262
-
263
- /**
264
- * Returns false when check fails - that is to say, it should be blocked by the firewall.
265
- *
266
- * @param array $inaValues
267
- * @param array $inaTerms
268
- * @param boolean $infRegex
269
- * @return boolean
270
- */
271
- private function doPassCheck( $inaValues, $inaTerms, $infRegex = false ) {
272
-
273
- foreach ( $inaValues as $sValue ) {
274
- foreach ( $inaTerms as $sTerm ) {
275
-
276
- if ( $infRegex && preg_match( $sTerm, $sValue ) ) { //dodgy term pattern found in a parameter value
277
- $this->logWarning(
278
- sprintf( 'Page parameter failed firewall check. The value was %s and the term is matched was %s', $sValue, $sTerm )
279
- );
280
- return false;
281
- }
282
- else {
283
- if ( strpos( $sValue, $sTerm ) !== false ) { //dodgy term found in a parameter value
284
- $this->logWarning(
285
- sprintf( 'Page parameter failed firewall check. The value was %s and the search term it matched was %s', $sValue, $sTerm )
286
- );
287
- return false;
288
- }
289
- }
290
- }
291
- }
292
- return true;
293
- }
294
-
295
- public function doFirewallBlock() {
296
-
297
- switch( $this->m_sBlockResponse ) {
298
-
299
- case 'redirect_home':
300
- header( "Location: ".home_url().'?testfirewall' );
301
- exit();
302
- break;
303
- case 'redirect_404':
304
- header( "Location: ".home_url().'/404?testfirewall' );
305
- exit();
306
- break;
307
- case 'redirect_die':
308
- die();
309
- }
310
- }
311
-
312
- public function isPageWhitelisted() {
313
-
314
- if ( empty( $this->m_aWhitelistPages ) ) {
315
- $this->setWhitelistPages();
316
- }
317
- if ( empty( $this->m_aWhitelistPages ) ) {
318
- return false;
319
- }
320
-
321
- // Check normal whitelisting pages without patterns.
322
- if ( $this->checkPagesForWhiteListing( $this->m_aWhitelistPages ) ) {
323
- return true;
324
- }
325
- // Check pattern-based whitelisting pages.
326
- if ( $this->checkPagesForWhiteListing( $this->m_aWhitelistPagesPatterns ) ) {
327
- return true;
328
- }
329
-
330
- return false;
331
- }
332
-
333
- /**
334
- *
335
- * @param array $inaWhitelistPagesParams
336
- * @param boolean $infUseRegex
337
- * @return boolean
338
- */
339
- protected function checkPagesForWhiteListing( $inaWhitelistPagesParams = array(), $infUseRegex = false ) {
340
-
341
- list( $sRequestPage, $sRequestQuery ) = $this->m_aRequestUriParts;
342
-
343
- // Now we compare pages in the whitelist with the parts of the request uri. If we get a match, that page is whitelisted
344
- $aWhitelistPages = array_keys( $inaWhitelistPagesParams );
345
-
346
- // 1. Is the page in the list of white pages?
347
- $fPageWhitelisted = false;
348
- if ( $infUseRegex ) {
349
- foreach ( $aWhitelistPages as $sPagePattern ) {
350
- if ( preg_match( $sPagePattern, $sRequestPage ) ) {
351
- $fPageWhitelisted = true;
352
- break;
353
- }
354
- }
355
- }
356
- else if ( in_array( $sRequestPage, $aWhitelistPages ) ) {
357
- $fPageWhitelisted = true;
358
- }
359
-
360
- if ( $fPageWhitelisted ) {
361
- // the current page is whitelisted - now check if it has request parameters.
362
- if ( empty( $inaWhitelistPagesParams[$sRequestPage] ) ) {
363
- return true; //because it's just plain whitelisted
364
- }
365
- foreach ( $inaWhitelistPagesParams[$sRequestPage] as $sWhitelistParam ) {
366
- if ( array_key_exists( $sWhitelistParam, $this->m_aPageParams ) ) {
367
- unset( $this->m_aPageParams[ $sWhitelistParam ] );
368
- }
369
- }
370
- // After removing all the whitelisted params, we now check if there are any params left that'll
371
- // need matched later in the firewall checking. If there are no parameters left, we return true.
372
- if ( empty( $this->m_aPageParams ) ) {
373
- return true;
374
- }
375
- }
376
- return false;
377
- }
378
-
379
- protected function setRequestUriPageParts() {
380
-
381
- if ( !isset( $_SERVER['REQUEST_URI'] ) || empty( $_SERVER['REQUEST_URI'] ) ) {
382
- $this->m_aRequestUriParts = false;
383
- $this->logWarning( 'Could not parse the details of this page call as $_SERVER[REQUEST_URI] was empty or not defined.' );
384
- return false;
385
- }
386
- $this->m_aRequestUriParts = explode( '?', $_SERVER['REQUEST_URI'] );
387
- $this->logInfo( sprintf( 'Page Request URI: %s', $_SERVER['REQUEST_URI'] ) );
388
- return true;
389
- }
390
-
391
- protected function setPageParams() {
392
- $this->m_aPageParams = array_merge( $_GET, $_POST );
393
- return true;
394
- }
395
-
396
- private function setWhitelistPages() {
397
-
398
- $aDefaultWlPages = array(
399
- '/wp-admin/options-general.php' => array(),
400
- '/wp-admin/post-new.php' => array(),
401
- '/wp-admin/page-new.php' => array(),
402
- '/wp-admin/link-add.php' => array(),
403
- '/wp-admin/media-upload.php' => array(),
404
- '/wp-admin/post.php' => array(),
405
- '/wp-admin/page.php' => array(),
406
- '/wp-admin/admin-ajax.php' => array(),
407
- '/wp-comments-post.php' => array(
408
- 'url',
409
- 'comment'
410
- ),
411
- '/wp-login.php' => array(
412
- 'redirect_to'
413
- )
414
- );
415
-
416
- // add in custom whitelisted pages later
417
-
418
- $this->m_aWhitelistPages = $aDefaultWlPages;
419
- $this->m_aWhitelistPagesPatterns = array(
420
- '/wp-admin/\*' => array(
421
- '_wp_original_http_referer',
422
- '_wp_http_referer'
423
- ),
424
- );
425
- }
426
-
427
- public function isVisitorOnWhitelist() {
428
- return $this->isIpOnlist( $this->m_aWhitelistIps, $this->m_sRequestIp );
429
- }
430
-
431
- public function isVisitorOnBlacklist() {
432
- return $this->isIpOnlist( $this->m_aBlacklistIps, $this->m_sRequestIp );
433
- }
434
-
435
- public function isIpOnlist( $inaList, $insIpAddress = '' ) {
436
-
437
- if ( empty( $insIpAddress ) ) {
438
- return true;
439
- }
440
- return in_array( $insIpAddress, $inaList );
441
- }
442
-
443
- public static function GetVisitorIpAddress() {
444
-
445
- $sIpAddress = empty($_SERVER["HTTP_X_FORWARDED_FOR"]) ? $_SERVER["REMOTE_ADDR"] : $_SERVER["HTTP_X_FORWARDED_FOR"];
446
-
447
- if( strpos($sIpAddress, ',') !== false ) {
448
- $sIpAddress = explode(',', $sIpAddress);
449
- $sIpAddress = $sIpAddress[0];
450
- }
451
-
452
- return $sIpAddress;
453
-
454
- }//GetVisitorIpAddress
455
-
456
- /**
457
- * @param string $insLogMessage
458
- */
459
- public function logInfo( $insLogMessage ) {
460
- $this->writeLog( $insLogMessage, self::LOG_MESSAGE_TYPE_INFO );
461
- }
462
- /**
463
- * @param string $insLogMessage
464
- */
465
- public function logWarning( $insLogMessage ) {
466
- $this->writeLog( $insLogMessage, self::LOG_MESSAGE_TYPE_WARNING );
467
- }
468
- /**
469
- * @param string $insLogMessage
470
- */
471
- public function logCritical( $insLogMessage ) {
472
- $this->writeLog( $insLogMessage, self::LOG_MESSAGE_TYPE_CRITICAL );
473
- }
474
-
475
- public function sendBlockEmail( $insEmailAddress ) {
476
-
477
- $aMessage = array(
478
- 'WordPress Simple Firewall has blocked a visitor to your site.',
479
- 'Log details for this visitor are below:',
480
- '- IP Address: '.$this->m_sRequestIp
481
- );
482
- foreach( $this->m_aLog[ $this->m_sRequestId ] as $aLogItem ) {
483
- list( $sTime, $sLogType, $sLogMessage ) = $aLogItem;
484
- $aMessage[] = '- '.$aLogItem[2];
485
- }
486
-
487
- $aMessage[] = 'You could look up the offending IP Address here: http://ip-lookup.net/?ip='. $this->m_sRequestIp;
488
- $sEmailSubject = 'Firewall Block Alert: ' . home_url();
489
- $sSiteName = get_bloginfo('name');
490
- $aHeaders = array(
491
- 'MIME-Version: 1.0',
492
- 'Content-type: text/plain; charset=iso-8859-1',
493
- "From: Simple Firewall Plugin - $sSiteName <$insEmailAddress>",
494
- 'Reply-To: Site Admin <receiver@domain3.com>',
495
- 'Subject: '.$sEmailSubject,
496
- 'X-Mailer: PHP/'.phpversion()
497
- );
498
- mail( $insEmailAddress, $sEmailSubject, implode( "\r\n", $aMessage ), implode( "\r\n", $aHeaders ) );
499
- }
500
- }
501
-
502
- endif;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/icwp-import-base-processor.php ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ *
17
+ */
18
+
19
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
20
+ require_once( dirname(__FILE__).'/icwp-data-processor.php' );
21
+
22
+ if ( !class_exists('ICWP_ImportBaseProcessor') ):
23
+
24
+ class ICWP_ImportBaseProcessor extends ICWP_BaseProcessor_WPSF {
25
+
26
+ /**
27
+ * The options prefix used by the target plugin
28
+ * @var string
29
+ */
30
+ protected $m_sOptionPrefix;
31
+
32
+ /**
33
+ * The value of all the options of the source plugin (keys of the options map array)
34
+ * @var array
35
+ */
36
+ protected $m_aSourceValues;
37
+
38
+ /**
39
+ * An associative array of option keys from the source plugin mapped to the keys of the target plugin
40
+ * @var unknown_type
41
+ */
42
+ protected $m_aOptionsMap;
43
+
44
+ public function __construct( $sTargetOptionPrefix = '' ) {
45
+ $this->m_sOptionPrefix = $sTargetOptionPrefix;
46
+ }
47
+
48
+ public function runImport() {
49
+ $this->populateSourceOptions();
50
+ $this->mapOptionsToTarget();
51
+ }
52
+
53
+ protected function mapOptionsToTarget() { }
54
+
55
+ /**
56
+ * Uses the keys from the Options Map array and populates the source value array with the database values.
57
+ */
58
+ protected function populateSourceOptions() {
59
+ foreach( $this->m_aOptionsMap as $sOptionName => $sTarget ) {
60
+ $this->m_aSourceValues[ $sOptionName ] = get_option( $sOptionName );
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Updates the target plugin options with the values (using the options prefix)
66
+ *
67
+ * @param string $insKey
68
+ * @param string|mixed $inmValue
69
+ */
70
+ protected function updateTargetOption( $insKey, $inmValue ) {
71
+ $fResult = update_option( $this->m_sOptionPrefix.$insKey, $inmValue );
72
+ }
73
+
74
+ /**
75
+ * Gets the target plugin option value for the key (using the options prefix)
76
+ *
77
+ * @param string $insKey
78
+ */
79
+ protected function getTargetOption( $insKey ) {
80
+ return get_option( $this->m_sOptionPrefix.$insKey );
81
+ }
82
+ }
83
+
84
+ endif;
src/icwp-import-wpf2-processor.php ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-import-base-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_ImportWpf2Processor') ):
21
+
22
+ class ICWP_ImportWpf2Processor extends ICWP_ImportBaseProcessor {
23
+
24
+ /**
25
+ * @var ICWP_OptionsHandler_Wpsf
26
+ */
27
+ protected $m_oWpsfOptions;
28
+ /**
29
+ * @var ICWP_OptionsHandler_Firewall
30
+ */
31
+ protected $m_oFirewallOptions;
32
+
33
+ public function __construct( $inoPluginOptions, $inoFirewallOptions ) {
34
+
35
+ $this->m_oWpsfOptions = $inoPluginOptions;
36
+ $this->m_oFirewallOptions = $inoFirewallOptions;
37
+
38
+ $this->m_aOptionsMap = array(
39
+ 'WP_firewall_redirect_page' => 'block_response',
40
+ 'WP_firewall_email_enable' => 'block_send_email',
41
+ /* 'WP_firewall_email_type' => '', unused */
42
+ 'WP_firewall_email_address' => 'block_send_email_address',
43
+ 'WP_firewall_exclude_directory' => 'block_dir_traversal',
44
+ 'WP_firewall_exclude_queries' => 'block_sql_queries',
45
+ 'WP_firewall_exclude_terms' => 'block_wordpress_terms',
46
+ 'WP_firewall_exclude_spaces' => 'block_field_truncation',
47
+ 'WP_firewall_exclude_file' => 'block_exe_file_uploads',
48
+ 'WP_firewall_exclude_http' => 'block_leading_schema',
49
+ 'WP_firewall_whitelisted_ip' => 'ips_whitelist',
50
+ 'WP_firewall_whitelisted_page' => 'page_params_whitelist',
51
+ 'WP_firewall_whitelisted_variable' => 'page_params_whitelist',
52
+ /* 'WP_firewall_plugin_url' => '', unused */
53
+ /* 'WP_firewall_default_whitelisted_page' => '', unused */
54
+ /* 'WP_firewall_previous_attack_var' => '', unused */
55
+ /* 'WP_firewall_previous_attack_ip' => '', unused */
56
+ /* 'WP_firewall_email_limit' => '', unused */
57
+ );
58
+ }
59
+
60
+ protected function mapOptionsToTarget() {
61
+
62
+ $aPluginOptions = array(
63
+ 'block_send_email_address'
64
+ );
65
+
66
+ $aFirewallOptions = array(
67
+ 'block_response',
68
+ 'block_send_email',
69
+ 'block_send_email_address',
70
+ 'block_dir_traversal',
71
+ 'block_sql_queries',
72
+ 'block_wordpress_terms',
73
+ 'block_field_truncation',
74
+ 'block_exe_file_uploads',
75
+ 'block_leading_schema',
76
+ 'ips_whitelist',
77
+ 'page_params_whitelist',
78
+ 'page_params_whitelist'
79
+ );
80
+
81
+ //redirect option
82
+ if ( $this->m_aSourceValues[ 'WP_firewall_redirect_page' ] == 'homepage' ) {
83
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_redirect_page'], 'redirect_home' );
84
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_redirect_page'], 'redirect_home' );
85
+ }
86
+ else if ( $this->m_aSourceValues[ 'WP_firewall_redirect_page' ] == '404page' ) {
87
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_redirect_page'], 'redirect_404' );
88
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_redirect_page'], 'redirect_404' );
89
+ }
90
+
91
+ //Email enable
92
+ if ( $this->m_aSourceValues[ 'WP_firewall_email_enable' ] == 'enable' ) {
93
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_email_enable'], 'Y' );
94
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_email_enable'], 'Y' );
95
+ }
96
+ else { // actually the WPF2 doesn't give the option to turn off email(!)
97
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_email_enable'], 'N' );
98
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_email_enable'], 'N' );
99
+ }
100
+
101
+ //Email address
102
+ $this->m_oWpsfOptions->setOpt( $this->m_aOptionsMap['WP_firewall_email_address'], $this->m_aSourceValues[ 'WP_firewall_email_address' ] );
103
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_email_address'], $this->m_aSourceValues[ 'WP_firewall_email_address' ] );
104
+
105
+ //Firewall block options - uses 'allow' to signify the block is in place. :|
106
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_directory' ] == 'allow' )? 'Y' : 'N';
107
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_directory'], $sTargetValue );
108
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_directory'], $sTargetValue );
109
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_queries' ] == 'allow' )? 'Y' : 'N';
110
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_queries'], $sTargetValue );
111
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_queries'], $sTargetValue );
112
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_terms' ] == 'allow' )? 'Y' : 'N';
113
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_terms'], $sTargetValue );
114
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_terms'], $sTargetValue );
115
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_spaces' ] == 'allow' )? 'Y' : 'N';
116
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_spaces'], $sTargetValue );
117
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_spaces'], $sTargetValue );
118
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_file' ] == 'allow' )? 'Y' : 'N';
119
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_file'], $sTargetValue );
120
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_file'], $sTargetValue );
121
+ $sTargetValue = ( $this->m_aSourceValues[ 'WP_firewall_exclude_http' ] == 'allow' )? 'Y' : 'N';
122
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_exclude_http'], $sTargetValue );
123
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_exclude_http'], $sTargetValue );
124
+
125
+ // Cookie checking - WPF2 does this by default
126
+ // $this->updateTargetOption( 'include_cookie_checks', 'Y' );
127
+ $this->m_oFirewallOptions->setOpt( 'include_cookie_checks', 'Y' );
128
+
129
+ // Whitelisted IPs
130
+ $aSourceIps = maybe_unserialize( $this->m_aSourceValues[ 'WP_firewall_whitelisted_ip' ] );
131
+ $aNewList = array();
132
+ foreach( $aSourceIps as $sIp ) {
133
+ $aNewList[ $sIp ] = '';
134
+ }
135
+ $aTargetIpWhitelist = $this->m_oFirewallOptions->getOpt( $this->m_aOptionsMap['WP_firewall_whitelisted_ip'] );
136
+ if ( empty( $aTargetIpWhitelist ) ) {
137
+ $aTargetIpWhitelist = array();
138
+ }
139
+ $this->m_oFirewallOptions->setOpt( $this->m_aOptionsMap['WP_firewall_whitelisted_ip'], ICWP_DataProcessor::Add_New_Raw_Ips( $aTargetIpWhitelist, $aNewList ) );
140
+ // $this->updateTargetOption( $this->m_aOptionsMap['WP_firewall_whitelisted_ip'], ICWP_DataProcessor::Add_New_Raw_Ips( $aTargetIpWhitelist, $aNewList ) );
141
+
142
+ // Whitelisted Pages and Vars... maybe later :|
143
+ }
144
+
145
+ }
146
+
147
+ endif;
src/icwp-optionshandler-autoupdates.php ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_AutoUpdates') ):
21
+
22
+ class ICWP_OptionsHandler_AutoUpdates extends ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const StoreName = 'autoupdates_options';
25
+
26
+ public function __construct( $insPrefix, $insVersion ) {
27
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
28
+ }
29
+
30
+ public function doPrePluginOptionsSave() {}
31
+
32
+ public function defineOptions() {
33
+
34
+ $aAutoUpdatesBase = array(
35
+ 'section_title' => 'Enable Automatic Updates Section',
36
+ 'section_options' => array(
37
+ array(
38
+ 'enable_autoupdates',
39
+ '',
40
+ 'N',
41
+ 'checkbox',
42
+ 'Enable Auto Updates',
43
+ 'Enable (or Disable) The WordPress Automatic Updates Feature',
44
+ 'Regardless of any other settings, this option will turn Off the Auto Updates feature, or enable your selected Auto Updates options.'
45
+ )
46
+ )
47
+ );
48
+ $aAutoUpdateOptions = array( 'select',
49
+ array( 'core_never', 'Never' ),
50
+ array( 'core_minor', 'Minor Versions Only' ),
51
+ array( 'core_major', 'Major and Minor Versions' ),
52
+ );
53
+ $aAutoUpdateCore = array(
54
+ 'section_title' => 'Automatic Plugin Self-Update',
55
+ 'section_options' => array(
56
+ array(
57
+ 'autoupdate_plugin_wpsf',
58
+ '',
59
+ 'Y',
60
+ 'checkbox',
61
+ 'Auto Update Plugin',
62
+ 'Always Automatically Update This Plugin',
63
+ 'Regardless of any component settings below, automatically update the WordPress Simple Firewall plugin.'
64
+ )
65
+ )
66
+ );
67
+ $aAutoUpdateComponents = array(
68
+ 'section_title' => 'Choose Which WordPress Components To Allow Automatic Updates',
69
+ 'section_options' => array(
70
+ array(
71
+ 'autoupdate_core',
72
+ '',
73
+ 'core_minor',
74
+ $aAutoUpdateOptions,
75
+ 'WordPress Core Updates',
76
+ 'Decide how the WordPress Core will automatically update, if at all.',
77
+ 'At least automatically upgrading minor versions is recommended (and is the WordPress default).'
78
+ ),
79
+ array(
80
+ 'enable_autoupdate_translations',
81
+ '',
82
+ 'Y',
83
+ 'checkbox',
84
+ 'Translations',
85
+ 'Automatically Update Translations',
86
+ 'Note: Automatic updates for translations are enabled on WordPress by default.'
87
+ ),
88
+ array(
89
+ 'enable_autoupdate_plugins',
90
+ '',
91
+ 'N',
92
+ 'checkbox',
93
+ 'Plugins',
94
+ 'Automatically Update Plugins',
95
+ 'Note: Automatic updates for plugins are disabled on WordPress by default.'
96
+ ),
97
+ array(
98
+ 'enable_autoupdate_themes',
99
+ '',
100
+ 'N',
101
+ 'checkbox',
102
+ 'Themes',
103
+ 'Automatically Update Themes',
104
+ 'Note: Automatic updates for themes are disabled on WordPress by default.'
105
+ ),
106
+ array(
107
+ 'enable_autoupdate_ignore_vcs',
108
+ '',
109
+ 'N',
110
+ 'checkbox',
111
+ 'Ignore Version Control',
112
+ 'Ignore Version Control Systems Such As GIT and SVN',
113
+ 'If you use SVN or GIT and WordPress detects it, automatic updates are disabled by default. Check this box to ignore version control systems and allow automatic updates'
114
+ )
115
+ )
116
+ );
117
+ $aAutoUpdateAll = array(
118
+ 'section_title' => 'Disable ALL Automatic Updates',
119
+ 'section_options' => array(
120
+ array(
121
+ 'enable_autoupdate_disable_all',
122
+ '',
123
+ 'N',
124
+ 'checkbox',
125
+ 'Disable All',
126
+ 'Completely Disable Automatic Updates',
127
+ 'When selected, regardless of any setting above, all automatic updates on this site will be completely disabled'
128
+ )
129
+ )
130
+ );
131
+
132
+ $this->m_aOptions = array(
133
+ $aAutoUpdatesBase,
134
+ $aAutoUpdateCore,
135
+ $aAutoUpdateComponents,
136
+ $aAutoUpdateAll
137
+ );
138
+ }
139
+
140
+ public function updateHandler() {
141
+
142
+ $sCurrentVersion = empty( $this->m_aOptionsValues[ 'current_plugin_version' ] )? '0.0' : $this->m_aOptionsValues[ 'current_plugin_version' ];
143
+ if ( version_compare( $sCurrentVersion, '1.9.0', '<' ) ) {
144
+ }//v1.9.0
145
+ }
146
+ }
147
+
148
+ endif;
src/icwp-optionshandler-base.php ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * Version: 2013-08-27-A
7
+ *
8
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
9
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
12
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
14
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
15
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
16
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
17
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
+ */
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_Base_WPSF') ):
21
+
22
+ class ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const CollateSeparator = '--SEP--';
25
+
26
+ /**
27
+ * @var boolean
28
+ */
29
+ protected $m_fNeedSave;
30
+
31
+ /**
32
+ * @var boolean
33
+ */
34
+ protected $m_fFullInit;
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ protected $m_sOptionPrefix;
40
+
41
+ /**
42
+ * @var string
43
+ */
44
+ protected $m_sVersion;
45
+
46
+ /**
47
+ * @var array
48
+ */
49
+ protected $m_aOptions;
50
+
51
+ /**
52
+ * @var array
53
+ */
54
+ protected $m_aDirectSaveOptions;
55
+
56
+ /**
57
+ * @var boolean
58
+ */
59
+ protected $m_fIsMultisite;
60
+
61
+ /**
62
+ * This is used primarily for the options deletion/cleanup. We store the names
63
+ * of options here that are not modified directly by the user/UI so that we can
64
+ * cleanup later on.
65
+ *
66
+ * @var array
67
+ */
68
+ protected $m_aIndependentOptions;
69
+
70
+ /**
71
+ * These are options that need to be stored, but are never set by the UI.
72
+ *
73
+ * @var array
74
+ */
75
+ protected $m_aNonUiOptions;
76
+
77
+ /**
78
+ * @var array
79
+ */
80
+ protected $m_aOptionsValues;
81
+
82
+ /**
83
+ * @var array
84
+ */
85
+ protected $m_aOptionsStoreName;
86
+
87
+ public function __construct( $insPrefix, $insStoreName, $insVersion ) {
88
+ $this->m_sOptionPrefix = $insPrefix;
89
+ $this->m_aOptionsStoreName = $insStoreName;
90
+ $this->m_sVersion = $insVersion;
91
+
92
+ $this->m_fIsMultisite = function_exists( 'is_multisite' ) && is_multisite();
93
+
94
+ // Handle any upgrades as necessary (only go near this if it's the admin area)
95
+ add_action( 'plugins_loaded', array( $this, 'doUpdates' ) );
96
+ }
97
+
98
+ public function doUpdates() {
99
+ if ( $this->hasPluginManageRights() ) {
100
+ $this->buildOptions();
101
+ $this->updateHandler();
102
+ }
103
+ }
104
+
105
+ public function hasPluginManageRights() {
106
+ if ( !current_user_can( 'manage_options' ) ) {
107
+ return false;
108
+ }
109
+ if ( $this->m_fIsMultisite && is_network_admin() ) {
110
+ return true;
111
+ }
112
+ else if ( !$this->m_fIsMultisite && is_admin() ) {
113
+ return true;
114
+ }
115
+ return false;
116
+ }
117
+
118
+ /**
119
+ * @return string
120
+ */
121
+ public function getVersion() {
122
+ return $this->m_sVersion;
123
+ }
124
+
125
+ /**
126
+ * @return string
127
+ */
128
+ public function setVersion( $insVersion ) {
129
+ return $this->m_sVersion = $insVersion;
130
+ }
131
+
132
+ /**
133
+ * Sets the value for the given option key
134
+ *
135
+ * @param string $insKey
136
+ * @param mixed $inmValue
137
+ * @return boolean
138
+ */
139
+ public function setOpt( $insKey, $inmValue ) {
140
+
141
+ if ( !isset( $this->m_aOptionsValues ) ) {
142
+ $this->loadStoredOptionsValues();
143
+ }
144
+
145
+ if ( $this->getOpt( $insKey ) === $inmValue ) {
146
+ return true;
147
+ }
148
+
149
+ $this->m_aOptionsValues[ $insKey ] = $inmValue;
150
+
151
+ if ( !$this->m_fNeedSave ) {
152
+ $this->m_fNeedSave = true;
153
+ }
154
+ return true;
155
+ }
156
+
157
+ /**
158
+ * @param string $insKey
159
+ * @return Ambigous <boolean, multitype:>
160
+ */
161
+ public function getOpt( $insKey ) {
162
+ if ( !isset( $this->m_aOptionsValues ) ) {
163
+ $this->loadStoredOptionsValues();
164
+ }
165
+ return ( isset( $this->m_aOptionsValues[ $insKey ] )? $this->m_aOptionsValues[ $insKey ] : false );
166
+ }
167
+
168
+ /**
169
+ * Retrieves the full array of options->values
170
+ *
171
+ * @return array
172
+ */
173
+ public function getOptions() {
174
+ if ( !isset( $this->m_aOptions ) ) {
175
+ $this->buildOptions();
176
+ }
177
+ return $this->m_aOptions;
178
+ }
179
+
180
+ /**
181
+ * Loads the options and their stored values from the WordPress Options store.
182
+ *
183
+ * @return array
184
+ */
185
+ public function getPluginOptionsValues() {
186
+ $this->generateOptionsValues();
187
+ return $this->m_aOptionsValues;
188
+ }
189
+
190
+ /**
191
+ * Saves the options to the WordPress Options store.
192
+ *
193
+ * It will also update the stored plugin options version.
194
+ */
195
+ public function savePluginOptions() {
196
+
197
+ $this->doPrePluginOptionsSave();
198
+ $this->updateOptionsVersion();
199
+
200
+ if ( !$this->m_fNeedSave ) {
201
+ return true;
202
+ }
203
+
204
+ $this->updateOption( $this->m_aOptionsStoreName, $this->m_aOptionsValues );
205
+
206
+ // Direct save options allow us to get fast access to certain values without loading the whole thing
207
+ if ( isset( $this->m_aDirectSaveOptions ) && is_array( $this->m_aDirectSaveOptions ) ) {
208
+ foreach( $this->m_aDirectSaveOptions as $sOptionKey ) {
209
+ $this->updateOption( $sOptionKey, $this->getOpt( $sOptionKey ) );
210
+ }
211
+ }
212
+
213
+ $this->m_fNeedSave = false;
214
+ }
215
+
216
+ public function collateAllFormInputsForAllOptions() {
217
+
218
+ if ( !isset( $this->m_aOptions ) ) {
219
+ $this->buildOptions();
220
+ }
221
+
222
+ $aToJoin = array();
223
+ foreach ( $this->m_aOptions as $aOptionsSection ) {
224
+
225
+ if ( empty( $aOptionsSection ) ) {
226
+ continue;
227
+ }
228
+ foreach ( $aOptionsSection['section_options'] as $aOption ) {
229
+ list($sKey, $fill1, $fill2, $sType) = $aOption;
230
+ $aToJoin[] = $sType.':'.$sKey;
231
+ }
232
+ }
233
+ return implode( self::CollateSeparator, $aToJoin );
234
+ }
235
+
236
+ /**
237
+ * @return array
238
+ */
239
+ protected function generateOptionsValues() {
240
+ if ( !isset( $this->m_aOptionsValues ) ) {
241
+ $this->loadStoredOptionsValues();
242
+ }
243
+ if ( empty( $this->m_aOptionsValues ) ) {
244
+ $this->buildOptions(); // set the defaults
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Loads the options and their stored values from the WordPress Options store.
250
+ */
251
+ protected function loadStoredOptionsValues() {
252
+ if ( empty( $this->m_aOptionsValues ) ) {
253
+ $this->m_aOptionsValues = $this->getOption( $this->m_aOptionsStoreName );
254
+ if ( empty( $this->m_aOptionsValues ) ) {
255
+ $this->m_aOptionsValues = array();
256
+ $this->m_fNeedSave = true;
257
+ }
258
+ }
259
+ }
260
+
261
+ protected function defineOptions() {
262
+
263
+ if ( !empty( $this->m_aOptions ) ) {
264
+ return true;
265
+ }
266
+
267
+ $aMisc = array(
268
+ 'section_title' => 'Miscellaneous Plugin Options',
269
+ 'section_options' => array(
270
+ array(
271
+ 'delete_on_deactivate',
272
+ '',
273
+ 'N',
274
+ 'checkbox',
275
+ 'Delete Plugin Settings',
276
+ 'Delete All Plugin Settings Upon Plugin Deactivation',
277
+ 'Careful: Removes all plugin options when you deactivite the plugin.'
278
+ ),
279
+ ),
280
+ );
281
+ $this->m_aOptions = array( $aMisc );
282
+ }
283
+
284
+ /**
285
+ * Will initiate the plugin options structure for use by the UI builder.
286
+ *
287
+ * It will also fill in $this->m_aOptionsValues with defaults where appropriate.
288
+ *
289
+ * It doesn't set any values, just populates the array created in buildOptions()
290
+ * with values stored.
291
+ *
292
+ * It has to handle the conversion of stored values to data to be displayed to the user.
293
+ *
294
+ * @param string $insUpdateKey - if only want to update a single key, supply it here.
295
+ */
296
+ protected function buildOptions() {
297
+
298
+ $this->defineOptions();
299
+ $this->loadStoredOptionsValues();
300
+
301
+ foreach ( $this->m_aOptions as &$aOptionsSection ) {
302
+
303
+ if ( empty( $aOptionsSection ) || !isset( $aOptionsSection['section_options'] ) ) {
304
+ continue;
305
+ }
306
+
307
+ foreach ( $aOptionsSection['section_options'] as &$aOptionParams ) {
308
+
309
+ list( $sOptionKey, $sOptionValue, $sOptionDefault, $sOptionType ) = $aOptionParams;
310
+
311
+ if ( $this->getOpt( $sOptionKey ) === false ) {
312
+ $this->setOpt( $sOptionKey, $sOptionDefault );
313
+ }
314
+ $mCurrentOptionVal = $this->getOpt( $sOptionKey );
315
+
316
+ if ( $sOptionType == 'password' && !empty( $mCurrentOptionVal ) ) {
317
+ $mCurrentOptionVal = '';
318
+ }
319
+ else if ( $sOptionType == 'ip_addresses' ) {
320
+
321
+ if ( empty( $mCurrentOptionVal ) ) {
322
+ $mCurrentOptionVal = '';
323
+ }
324
+ else {
325
+ $mCurrentOptionVal = implode( "\n", $this->convertIpListForDisplay( $mCurrentOptionVal ) );
326
+ }
327
+ }
328
+ else if ( $sOptionType == 'comma_separated_lists' ) {
329
+
330
+ if ( empty( $mCurrentOptionVal ) ) {
331
+ $mCurrentOptionVal = '';
332
+ }
333
+ else {
334
+ $aNewValues = array();
335
+ foreach( $mCurrentOptionVal as $sPage => $aParams ) {
336
+ $aNewValues[] = $sPage.', '. implode( ", ", $aParams );
337
+ }
338
+ $mCurrentOptionVal = implode( "\n", $aNewValues );
339
+ }
340
+ }
341
+ $aOptionParams[1] = $mCurrentOptionVal;
342
+ }
343
+ }
344
+
345
+ // Cater for Non-UI options that don't necessarily go through the UI
346
+ if ( isset($this->m_aNonUiOptions) && is_array($this->m_aNonUiOptions) ) {
347
+ foreach( $this->m_aNonUiOptions as $sOption ) {
348
+ if ( !$this->getOpt( $sOption ) ) {
349
+ $this->setOpt( $sOption, '' );
350
+ }
351
+ }
352
+ }
353
+ }
354
+
355
+ /**
356
+ * This is the point where you would want to do any options verification
357
+ */
358
+ protected function doPrePluginOptionsSave() { }
359
+
360
+ /**
361
+ * Will return the 'current_plugin_version' if it is set, 0.0 otherwise.
362
+ *
363
+ * @return string
364
+ */
365
+ public function getPluginOptionsVersion() {
366
+ $sVersion = $this->getOpt( 'current_plugin_version' );
367
+ return empty( $sVersion )? '0.0' :$sVersion;
368
+ }
369
+
370
+ /**
371
+ * Updates the 'current_plugin_version' to the offical plugin version.
372
+ */
373
+ protected function updateOptionsVersion() {
374
+ $this->setOpt( 'current_plugin_version', $this->m_sVersion );
375
+ }
376
+
377
+ /**
378
+ * Deletes all the options including direct save.
379
+ */
380
+ public function deletePluginOptions() {
381
+
382
+ $this->deleteOption( $this->m_aOptionsStoreName );
383
+
384
+ // Direct save options allow us to get fast access to certain values without loading the whole thing
385
+ if ( isset($this->m_aDirectSaveOptions) && is_array( $this->m_aDirectSaveOptions ) ) {
386
+ foreach( $this->m_aDirectSaveOptions as $sOptionKey ) {
387
+ $this->deleteOption( $sOptionKey );
388
+ }
389
+ }
390
+ // Independent options are those untouched by the User/UI that are saved elsewhere and directly to the WP Options table. They are "meta" options
391
+ if ( isset($this->m_aIndependentOptions) && is_array( $this->m_aIndependentOptions ) ) {
392
+ foreach( $this->m_aIndependentOptions as $sOptionKey ) {
393
+ $this->deleteOption( $sOptionKey );
394
+ }
395
+ }
396
+ }
397
+
398
+ protected function convertIpListForDisplay( $inaIpList = array() ) {
399
+
400
+ $aDisplay = array();
401
+ if ( empty( $inaIpList ) || empty( $inaIpList['ips'] ) ) {
402
+ return $aDisplay;
403
+ }
404
+ foreach( $inaIpList['ips'] as $sAddress ) {
405
+ // offset=1 in the case that it's a range and the first number is negative on 32-bit systems
406
+ $mPos = strpos( $sAddress, '-', 1 );
407
+
408
+ if ( $mPos === false ) { //plain IP address
409
+ $sDisplayText = long2ip( $sAddress );
410
+ }
411
+ else {
412
+ //we remove the first character in case this is '-'
413
+ $aParts = array( substr( $sAddress, 0, 1 ), substr( $sAddress, 1 ) );
414
+ list( $nStart, $nEnd ) = explode( '-', $aParts[1], 2 );
415
+ $sDisplayText = long2ip( $aParts[0].$nStart ) .'-'. long2ip( $nEnd );
416
+ }
417
+ $sLabel = $inaIpList['meta'][ md5($sAddress) ];
418
+ $sLabel = trim( $sLabel, '()' );
419
+ $aDisplay[] = $sDisplayText . ' ('.$sLabel.')';
420
+ }
421
+ return $aDisplay;
422
+ }
423
+
424
+ /**
425
+ * @param string $sAllOptionsInput - comma separated list of all the input keys to be processed from the $_POST
426
+ * @return void|boolean
427
+ */
428
+ public function updatePluginOptionsFromSubmit( $sAllOptionsInput ) {
429
+
430
+ if ( empty( $sAllOptionsInput ) ) {
431
+ return;
432
+ }
433
+
434
+ $this->loadStoredOptionsValues();
435
+
436
+ $aAllInputOptions = explode( self::CollateSeparator, $sAllOptionsInput );
437
+ foreach ( $aAllInputOptions as $sInputKey ) {
438
+ $aInput = explode( ':', $sInputKey );
439
+ list( $sOptionType, $sOptionKey ) = $aInput;
440
+
441
+ $sOptionValue = $this->getFromPost( $sOptionKey );
442
+ if ( is_null($sOptionValue) ) {
443
+
444
+ if ( $sOptionType == 'text' || $sOptionType == 'email' ) { //if it was a text box, and it's null, don't update anything
445
+ continue;
446
+ }
447
+ else if ( $sOptionType == 'checkbox' ) { //if it was a checkbox, and it's null, it means 'N'
448
+ $sOptionValue = 'N';
449
+ }
450
+ else if ( $sOptionType == 'integer' ) { //if it was a integer, and it's null, it means '0'
451
+ $sOptionValue = 0;
452
+ }
453
+ }
454
+ else { //handle any pre-processing we need to.
455
+
456
+ if ( $sOptionType == 'integer' ) {
457
+ $sOptionValue = intval( $sOptionValue );
458
+ }
459
+ else if ( $sOptionType == 'password' && $this->hasEncryptOption() ) { //md5 any password fields
460
+ $sTempValue = trim( $sOptionValue );
461
+ if ( empty( $sTempValue ) ) {
462
+ continue;
463
+ }
464
+ $sOptionValue = md5( $sTempValue );
465
+ }
466
+ else if ( $sOptionType == 'ip_addresses' ) { //ip addresses are textareas, where each is separated by newline
467
+
468
+ if ( !class_exists('ICWP_DataProcessor') ) {
469
+ require_once ( dirname(__FILE__).'/icwp-data-processor.php' );
470
+ }
471
+ $oProcessor = new ICWP_DataProcessor();
472
+ $sOptionValue = $oProcessor->ExtractIpAddresses( $sOptionValue );
473
+ }
474
+ else if ( $sOptionType == 'email' && function_exists( 'is_email' ) && !is_email( $sOptionValue ) ) {
475
+ $sOptionValue = '';
476
+ }
477
+ else if ( $sOptionType == 'comma_separated_lists' ) {
478
+ if ( !class_exists('ICWP_DataProcessor') ) {
479
+ require_once ( dirname(__FILE__).'/icwp-data-processor.php' );
480
+ }
481
+ $oProcessor = new ICWP_DataProcessor();
482
+ $sOptionValue = $oProcessor->ExtractCommaSeparatedList( $sOptionValue );
483
+ }
484
+ }
485
+ $this->setOpt( $sOptionKey, $sOptionValue );
486
+ }
487
+ return $this->savePluginOptions( true );
488
+ }
489
+
490
+ /**
491
+ * Should be over-ridden by each new class to handle upgrades.
492
+ *
493
+ * Called upon construction and after plugin options are initialized.
494
+ */
495
+ protected function updateHandler() { }
496
+
497
+ /**
498
+ * @param array $inaNewOptions
499
+ */
500
+ protected function mergeNonUiOptions( $inaNewOptions = array() ) {
501
+
502
+ if ( !empty( $this->m_aNonUiOptions ) ) {
503
+ $this->m_aNonUiOptions = array_merge( $this->m_aNonUiOptions, $inaNewOptions );
504
+ }
505
+ else {
506
+ $this->m_aNonUiOptions = $inaNewOptions;
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Copies WordPress Options to the options array and optionally deletes the original.
512
+ *
513
+ * @param array $inaOptions
514
+ * @param boolean $fDeleteOld
515
+ */
516
+ protected function migrateOptions( $inaOptions, $fDeleteOld = false ) {
517
+ foreach( $inaOptions as $sOptionKey ) {
518
+ $mCurrentValue = $this->getOption( $sOptionKey );
519
+ if ( $mCurrentValue === false ) {
520
+ continue;
521
+ }
522
+ $this->setOpt( $sOptionKey, $mCurrentValue );
523
+ if ( $fDeleteOld ) {
524
+ $this->deleteOption( $sOptionKey );
525
+ }
526
+ }
527
+ }
528
+
529
+ /**
530
+ * @return boolean
531
+ */
532
+ public function hasEncryptOption() {
533
+ return function_exists( 'md5' );
534
+ // return extension_loaded( 'mcrypt' );
535
+ }
536
+
537
+ protected function getVisitorIpAddress( $infAsLong = true ) {
538
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
539
+ return ICWP_BaseProcessor_WPSF::GetVisitorIpAddress( $infAsLong );
540
+ }
541
+
542
+ /**
543
+ * @param string $insKey - the POST key
544
+ * @param string $insPrefix
545
+ * @return Ambigous <null, string>
546
+ */
547
+ protected function getFromPost( $insKey, $insPrefix = null ) {
548
+ $sKey = ( is_null( $insPrefix )? $this->m_sOptionPrefix : $insPrefix ) . $insKey;
549
+ return ( isset( $_POST[ $sKey ] )? $_POST[ $sKey ]: null );
550
+ }
551
+ public function getOption( $insKey ) {
552
+ $sKey = $this->m_sOptionPrefix.$insKey;
553
+ return $this->m_fIsMultisite? get_site_option($sKey) : get_option($sKey);
554
+ }
555
+ public function addOption( $insKey, $insValue ) {
556
+ $sKey = $this->m_sOptionPrefix.$insKey;
557
+ return $this->m_fIsMultisite? add_site_option($sKey, $insValue) : add_option($sKey, $insValue);
558
+ }
559
+ public function updateOption( $insKey, $insValue ) {
560
+ $sKey = $this->m_sOptionPrefix.$insKey;
561
+ return $this->m_fIsMultisite? update_site_option($sKey, $insValue) : update_option($sKey, $insValue);
562
+ }
563
+ public function deleteOption( $insKey ) {
564
+ $sKey = $this->m_sOptionPrefix.$insKey;
565
+ return $this->m_fIsMultisite? delete_site_option($sKey) : delete_option($sKey);
566
+ }
567
+ }
568
+
569
+ endif;
src/icwp-optionshandler-commentsfilter.php ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_CommentsFilter') ):
21
+
22
+ class ICWP_OptionsHandler_CommentsFilter extends ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const StoreName = 'commentsfilter_options';
25
+
26
+ const DefaultCommentCooldown = 30; //seconds.
27
+ const DefaultCommentExpire = 600; //seconds.
28
+
29
+ public function __construct( $insPrefix, $insVersion ) {
30
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
31
+ }
32
+
33
+ public function defineOptions() {
34
+
35
+ $this->m_aDirectSaveOptions = array();
36
+
37
+ $aBase = array(
38
+ 'section_title' => 'Enable Comments Filter',
39
+ 'section_options' => array(
40
+ array(
41
+ 'enable_comments_filter',
42
+ '',
43
+ 'Y',
44
+ 'checkbox',
45
+ 'Enable Comments Filter',
46
+ 'Enable (or Disable) The Comments Filter Feature',
47
+ 'Regardless of any other settings, this option will turn Off the Comments Filter feature, or enable your chosen Comments Filter options.'
48
+ )
49
+ ),
50
+ );
51
+ $aGasp = array(
52
+ 'section_title' => 'G.A.S.P. Comment SPAM Protection',
53
+ 'section_options' => array(
54
+ array(
55
+ 'enable_comments_gasp_protection',
56
+ '',
57
+ 'Y',
58
+ 'checkbox',
59
+ 'GASP Protection',
60
+ 'Add Growmap Anti Spambot Protection to your comments',
61
+ 'Taking the lead from the original GASP plugin for WordPress, we have extended it to include further protection. '.sprintf( '[%smore info%s]', '<a href="http://icwp.io/2n" target="_blank">', '</a>' )
62
+ ),
63
+ array(
64
+ 'enable_comments_gasp_protection_for_logged_in',
65
+ '',
66
+ 'N',
67
+ 'checkbox',
68
+ 'Include Logged-In Users',
69
+ 'You may also enable GASP for logged in users',
70
+ 'Since logged-in users would be expected to be vetted, this is off by default.'
71
+ ),
72
+ array(
73
+ 'comments_cooldown_interval',
74
+ '',
75
+ '30',
76
+ 'integer',
77
+ 'Comments Cooldown',
78
+ 'Limit posting a comment to X seconds after the page has loaded',
79
+ "By forcing a comments cooldown period, you restrict a Spambot's ability to post mutliple times to your posts."
80
+ ),
81
+ array(
82
+ 'comments_token_expire_interval',
83
+ '',
84
+ '600',
85
+ 'integer',
86
+ 'Comment Token Expire',
87
+ 'A visitor has X seconds within which to post a comment',
88
+ "Default: 10 minutes (600 seconds). Each visitor is given a unique 'Token' so they can comment. This restricts spambots, but we need to force these tokens to expire and at the same time not bother the visitors."
89
+ ),
90
+ array(
91
+ 'custom_message_checkbox',
92
+ '',
93
+ "I'm not a spammer.",
94
+ 'text',
95
+ 'Custom Checkbox Message',
96
+ "If you want a custom checkbox message, please specify this here.",
97
+ "You can customise the message beside the checkbox. Default: I'm not a spammer"
98
+ ),
99
+ array(
100
+ 'custom_message_alert',
101
+ '',
102
+ "Please check the box to confirm you're not a spammer",
103
+ 'text',
104
+ 'Custom Alert Message',
105
+ "If you want a custom alert message, please specify this here.",
106
+ "Default: Please check the box to confirm you're not a spammer"
107
+ ),
108
+ array(
109
+ 'custom_message_comment_wait',
110
+ '',
111
+ "Please wait %s seconds before posting your comment",
112
+ 'text',
113
+ 'Custom Alert Message',
114
+ "If you want a custom submit-button message please specify this here.",
115
+ "Where you see the '%s' this will be the number of seconds. You must ensure you include 1, and only 1, of these.
116
+ <br />Default: Please wait %s seconds before posting your comment"
117
+ ),
118
+ array(
119
+ 'custom_message_comment_reload',
120
+ '',
121
+ "Please reload this page to post a comment",
122
+ 'text',
123
+ 'Custom Alert Message',
124
+ "This message is displayed on the submit-button when the comment token is expired.",
125
+ "Default: Please reload this page to post a comment"
126
+ )
127
+ )
128
+ );
129
+
130
+ $this->m_aOptions = array(
131
+ $aBase,
132
+ $aGasp
133
+ );
134
+ }
135
+
136
+ /**
137
+ * This is the point where you would want to do any options verification
138
+ */
139
+ protected function doPrePluginOptionsSave() {
140
+
141
+ $nCommentCooldown = $this->getOpt( 'comments_cooldown_interval' );
142
+ if ( $nCommentCooldown < 0 ) {
143
+ $nCommentCooldown = 0;
144
+ }
145
+
146
+ $nCommentTokenExpire = $this->getOpt( 'comments_token_expire_interval' );
147
+ if ( $nCommentTokenExpire < 0 ) {
148
+ $nCommentTokenExpire = 0;
149
+ }
150
+
151
+ if ( $nCommentTokenExpire != 0 && $nCommentCooldown > $nCommentTokenExpire ) {
152
+ $nCommentCooldown = self::DefaultCommentCooldown;
153
+ $nCommentTokenExpire = self::DefaultCommentExpire;
154
+ }
155
+ $this->setOpt( 'comments_cooldown_interval', $nCommentCooldown );
156
+ $this->setOpt( 'comments_token_expire_interval', $nCommentTokenExpire );
157
+ }
158
+
159
+ public function updateHandler() {
160
+ $sCurrentVersion = empty( $this->m_aOptionsValues[ 'current_plugin_version' ] )? '0.0' : $this->m_aOptionsValues[ 'current_plugin_version' ];
161
+ }
162
+ }
163
+
164
+ endif;
src/icwp-optionshandler-firewall.php ADDED
@@ -0,0 +1,255 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_Firewall') ):
21
+
22
+ class ICWP_OptionsHandler_Firewall extends ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const StoreName = 'firewall_options';
25
+
26
+ public function __construct( $insPrefix, $insVersion ) {
27
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
28
+ }
29
+
30
+ public function doPrePluginOptionsSave() {
31
+
32
+ $aIpWhitelist = $this->getOpt( 'ips_blacklist' );
33
+ if ( $aIpWhitelist === false ) {
34
+ $aIpWhitelist = '';
35
+ $this->setOpt( 'ips_whitelist', $aIpWhitelist );
36
+ }
37
+
38
+ $aIpBlacklist = $this->getOpt( 'ips_blacklist' );
39
+ if ( $aIpBlacklist === false ) {
40
+ $aIpBlacklist = '';
41
+ $this->setOpt( 'ips_blacklist', $aIpBlacklist );
42
+ }
43
+
44
+ $aPageWhitelist = $this->getOpt( 'page_params_whitelist' );
45
+ if ( $aPageWhitelist === false ) {
46
+ $aPageWhitelist = '';
47
+ $this->setOpt( 'page_params_whitelist', $aPageWhitelist );
48
+ }
49
+
50
+ $sBlockResponse = $this->getOpt( 'block_response' );
51
+ if ( empty( $sBlockResponse ) ) {
52
+ $sBlockResponse = 'redirect_die_message';
53
+ $aIpWhitelist = $this->setOpt( 'block_response', $sBlockResponse );
54
+ }
55
+ }
56
+
57
+ public function defineOptions() {
58
+
59
+ $this->m_aDirectSaveOptions = array( 'whitelist_admins' );
60
+
61
+ $this->m_aFirewallBase = array(
62
+ 'section_title' => 'Enable WordPress Firewall',
63
+ 'section_options' => array(
64
+ array(
65
+ 'enable_firewall',
66
+ '', 'N',
67
+ 'checkbox',
68
+ 'Enable Firewall',
69
+ 'Enable (or Disable) The WordPress Firewall Feature',
70
+ 'Regardless of any other settings, this option will turn Off the Firewall feature, or enable your selected Firewall options.'
71
+ )
72
+ )
73
+ );
74
+ $this->m_aBlockTypesSection = array(
75
+ 'section_title' => 'Firewall Blocking Options',
76
+ 'section_options' => array(
77
+ array(
78
+ 'include_cookie_checks',
79
+ '',
80
+ 'N',
81
+ 'checkbox',
82
+ 'Include Cookies',
83
+ 'Also Test Cookie Values In Firewall Tests',
84
+ 'The firewall will test GET and POST, but with this option checked, it will also COOKIE values for the same.'
85
+ ),
86
+ array(
87
+ 'block_dir_traversal',
88
+ '',
89
+ 'N',
90
+ 'checkbox',
91
+ 'Directory Traversals',
92
+ 'Block Directory Traversals',
93
+ 'This will block directory traversal paths in in application parameters (../, ../../etc/passwd, etc.)'
94
+ ),
95
+ array(
96
+ 'block_sql_queries',
97
+ '',
98
+ 'N',
99
+ 'checkbox',
100
+ 'SQL Queries',
101
+ 'Block SQL Queries',
102
+ 'This will block in application parameters (union select, concat(, /**/, etc.).'
103
+ ),
104
+ array(
105
+ 'block_wordpress_terms',
106
+ '',
107
+ 'N',
108
+ 'checkbox',
109
+ 'WordPress Terms',
110
+ 'Block WordPress Specific Terms',
111
+ 'This will block WordPress specific terms in application parameters (wp_, user_login, etc.).'
112
+ ),
113
+ array(
114
+ 'block_field_truncation',
115
+ '',
116
+ 'N',
117
+ 'checkbox',
118
+ 'Field Truncation',
119
+ 'Block Field Truncation Attacks',
120
+ 'This will block field truncation attacks in application parameters.'
121
+ ),
122
+ array(
123
+ 'block_exe_file_uploads',
124
+ '',
125
+ 'N',
126
+ 'checkbox',
127
+ 'Exe File Uploads',
128
+ 'Block Executable File Uploads',
129
+ 'This will block executable file uploads (.php, .exe, etc.).'
130
+ ),
131
+ array(
132
+ 'block_leading_schema',
133
+ '',
134
+ 'N',
135
+ 'checkbox',
136
+ 'Leading Schemas',
137
+ 'Block Leading Schemas (HTTPS / HTTP)',
138
+ 'This will block leading schemas http:// and https:// in application parameters (off by default; may cause problems with many plugins).'
139
+ )
140
+ ),
141
+ );
142
+ $aRedirectOptions = array( 'select',
143
+ array( 'redirect_die_message', 'Die With Message' ),
144
+ array( 'redirect_die', 'Die' ),
145
+ array( 'redirect_home', 'Redirect To Home Page' ),
146
+ array( 'redirect_404', 'Return 404' ),
147
+ );
148
+ $this->m_aBlockSection = array(
149
+ 'section_title' => 'Choose Firewall Block Response',
150
+ 'section_options' => array(
151
+ array( 'block_response', '', 'none', $aRedirectOptions, 'Block Response', 'Choose how the firewall responds when it blocks a request', '' ),
152
+ array( 'block_send_email', '', 'N', 'checkbox', 'Send Email Report', 'When a visitor is blocked it will send an email to the blog admin', 'Use with caution - if you get hit by automated bots you may send out too many emails and you could get blocked by your host.' )
153
+ )
154
+ );
155
+
156
+ $this->m_aWhitelistSection = array(
157
+ 'section_title' => 'Whitelist - IPs, Pages, Parameters, and Users that by-pass the Firewall',
158
+ 'section_options' => array(
159
+ array(
160
+ 'ips_whitelist',
161
+ '',
162
+ '',
163
+ 'ip_addresses',
164
+ 'Whitelist IP Addresses',
165
+ 'Choose IP Addresses that are never subjected to Firewall Rules',
166
+ sprintf( 'Take a new line per address. Your IP address is: %s', '<span class="code">'.$this->getVisitorIpAddress( false ).'</span>' )
167
+ ),
168
+ array(
169
+ 'page_params_whitelist',
170
+ '',
171
+ '',
172
+ 'comma_separated_lists',
173
+ 'Whitelist Parameters',
174
+ 'Detail pages and parameters that are whitelisted (ignored)',
175
+ 'This should be used with caution and you should only provide parameter names that you need to have excluded.'
176
+ .' [<a href="http://icwp.io/2a" target="_blank">Help</a>]'
177
+ ),
178
+ array(
179
+ 'whitelist_admins',
180
+ '',
181
+ 'N',
182
+ 'checkbox',
183
+ 'Ignore Administrators',
184
+ 'Ignore users logged in as Administrator',
185
+ 'Authenticated administrator users will not be processed by the firewall.'
186
+ )
187
+ )
188
+ );
189
+
190
+ $this->m_aBlacklistSection = array(
191
+ 'section_title' => 'Choose IP Addresses To Blacklist',
192
+ 'section_options' => array(
193
+ array(
194
+ 'ips_blacklist',
195
+ '',
196
+ '',
197
+ 'ip_addresses',
198
+ 'Blacklist IP Addresses',
199
+ 'Choose IP Addresses that are always blocked access to the site',
200
+ 'Take a new line per address. Each IP Address must be valid and will be checked.'
201
+ )
202
+ )
203
+ );
204
+ $this->m_aFirewallMiscSection = array(
205
+ 'section_title' => 'Miscellaneous Plugin Options',
206
+ 'section_options' => array(
207
+ array(
208
+ 'enable_firewall_log',
209
+ '', 'N',
210
+ 'checkbox',
211
+ 'Firewall Logging',
212
+ 'Turn on a detailed Firewall Log',
213
+ 'Will log every visit to the site and how the firewall processes it. Not recommended to leave on unless you want to debug something and check the firewall is working as you expect.'
214
+ )
215
+ )
216
+ );
217
+
218
+ $this->m_aOptions = array(
219
+ $this->m_aFirewallBase,
220
+ $this->m_aBlockSection,
221
+ $this->m_aWhitelistSection,
222
+ $this->m_aBlacklistSection,
223
+ $this->m_aBlockTypesSection,
224
+ $this->m_aFirewallMiscSection
225
+ );
226
+ }
227
+
228
+ public function updateHandler() {
229
+
230
+ $sCurrentVersion = empty( $this->m_aOptionsValues[ 'current_plugin_version' ] )? '0.0' : $this->m_aOptionsValues[ 'current_plugin_version' ];
231
+ if ( version_compare( $sCurrentVersion, '1.4.0', '<' ) ) {
232
+ $aSettingsKey = array(
233
+ 'current_plugin_version',
234
+ 'enable_firewall',
235
+ 'include_cookie_checks',
236
+ 'block_dir_traversal',
237
+ 'block_sql_queries',
238
+ 'block_wordpress_terms',
239
+ 'block_field_truncation',
240
+ 'block_exe_file_uploads',
241
+ 'block_leading_schema',
242
+ 'block_send_email',
243
+ 'ips_whitelist',
244
+ 'ips_blacklist',
245
+ 'page_params_whitelist',
246
+ 'block_response',
247
+ 'enable_firewall_log',
248
+ 'whitelist_admins'
249
+ );
250
+ $this->migrateOptions( $aSettingsKey );
251
+ }//v1.4.0
252
+ }
253
+ }
254
+
255
+ endif;
src/icwp-optionshandler-lockdown.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+ require_once( dirname(__FILE__).'/icwp-optionshandler-lockdown.php' );
20
+
21
+ if ( !class_exists('ICWP_OptionsHandler_Lockdown') ):
22
+
23
+ class ICWP_OptionsHandler_Lockdown extends ICWP_OptionsHandler_Base_WPSF {
24
+
25
+ const StoreName = 'lockdown_options';
26
+
27
+ public function __construct( $insPrefix, $insVersion ) {
28
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
29
+ }
30
+
31
+ public function doPrePluginOptionsSave() {
32
+
33
+ if ( $this->getOpt( 'action_reset_auth_salts' ) == 'Y' ) {
34
+ $this->setOpt( 'action_reset_auth_salts', 'P' );
35
+ }
36
+ else if ( $this->getOpt( 'action_reset_auth_salts' ) == 'P' ) {
37
+ $this->setOpt( 'action_reset_auth_salts', 'N' );
38
+ }
39
+ }
40
+
41
+ public function defineOptions() {
42
+
43
+ $aBase = array(
44
+ 'section_title' => 'Enable Lockdown Feature',
45
+ 'section_options' => array(
46
+ array(
47
+ 'enable_lockdown',
48
+ '',
49
+ 'N',
50
+ 'checkbox',
51
+ 'Enable Lockdown',
52
+ 'Enable (or Disable) The Lockdown Feature',
53
+ 'Regardless of any other settings, this option will turn Off the Lockdown feature, or enable your selected Lockdown options.'
54
+ )
55
+ )
56
+ );
57
+ $aAccess = array(
58
+ 'section_title' => 'Access Options',
59
+ 'section_options' => array(
60
+ array(
61
+ 'disable_file_editing',
62
+ '',
63
+ 'N',
64
+ 'checkbox',
65
+ 'Disable File Editing',
66
+ 'Disable Ability To Edit Files',
67
+ 'Removes the option to directly edit any files from within the WordPress admin area.
68
+ <br />Equivalent to setting DISALLOW_FILE_EDIT to TRUE.'
69
+ )
70
+ )
71
+ );
72
+
73
+ $this->m_aOptions = array(
74
+ $aBase,
75
+ $aAccess
76
+ );
77
+
78
+ if ( false && $this->getCanDoAuthSalts() ) {
79
+ $this->m_aOptions[] = array(
80
+ 'section_title' => 'Security Actions',
81
+ 'section_options' => array(
82
+ array(
83
+ 'action_reset_auth_salts',
84
+ '',
85
+ 'N',
86
+ 'checkbox',
87
+ 'Reset Auth Keys/Salts',
88
+ 'Reset WordPress Authentication Keys and Salts',
89
+ 'Selecting this and saving will reset the WordPress Authentication Keys and Salts in your wp-config.php file.
90
+ <br /><strong>Note: This will log you and all other users out of their current session.</strong>'
91
+ )
92
+ )
93
+ );
94
+ }
95
+ }
96
+
97
+ protected function getCanDoAuthSalts() {
98
+ require_once( dirname(__FILE__).'/icwp-wpfilesystem.php' );
99
+ $oWpFilesystem = new ICWP_WpFilesystem_WPSF();
100
+
101
+ if ( !$oWpFilesystem->getCanWpRemoteGet() ) {
102
+ return false;
103
+ }
104
+
105
+ if ( !$oWpFilesystem->getCanDiskWrite() ) {
106
+ return false;
107
+ }
108
+
109
+ $sWpConfigPath = is_file( ABSPATH.'wp-config.php' )? ABSPATH.'wp-config.php' : ABSPATH.'..'.ICWP_DS.'wp-config.php';
110
+
111
+ if ( !is_file( $sWpConfigPath ) ) {
112
+ var_dump('no wpconfig');
113
+ return false;
114
+ }
115
+ $mResult = $oWpFilesystem->getCanReadWriteFile( $sWpConfigPath );
116
+ return !empty( $mResult );
117
+ }
118
+
119
+ public function updateHandler() {
120
+ }
121
+ }
122
+
123
+ endif;
src/icwp-optionshandler-loginprotect.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_LoginProtect') ):
21
+
22
+ class ICWP_OptionsHandler_LoginProtect extends ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const StoreName = 'loginprotect_options';
25
+
26
+ public function __construct( $insPrefix, $insVersion ) {
27
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
28
+ }
29
+
30
+ public function defineOptions() {
31
+
32
+ $this->m_aDirectSaveOptions = array();
33
+
34
+ $this->m_aOptionsBase = array(
35
+ 'section_title' => 'Enable Login Protection',
36
+ 'section_options' => array(
37
+ array(
38
+ 'enable_login_protect',
39
+ '',
40
+ 'Y',
41
+ 'checkbox',
42
+ 'Enable Login Protect',
43
+ 'Enable (or Disable) The Login Protection Feature',
44
+ 'Regardless of any other settings, this option will turn Off the Login Protect feature, or enable your selected Login Protect options.'
45
+ )
46
+ ),
47
+ );
48
+ $this->m_aWhitelist = array(
49
+ 'section_title' => 'Whitelist IPs that by-pass Login Protect',
50
+ 'section_options' => array(
51
+ array(
52
+ 'ips_whitelist',
53
+ '',
54
+ '',
55
+ 'ip_addresses',
56
+ 'Whitelist IP Addresses',
57
+ 'Specify IP Addresses that by-pass all Login Protect rules',
58
+ sprintf( 'Take a new line per address. Your IP address is: %s', '<span class="code">'.$this->getVisitorIpAddress( false ).'</span>' )
59
+ )
60
+ )
61
+ );
62
+ $this->m_aTwoFactorAuth = array(
63
+ 'section_title' => 'Two-Factor Authentication Protection Options',
64
+ 'section_options' => array(
65
+ array(
66
+ 'enable_two_factor_auth_by_ip',
67
+ '',
68
+ 'N',
69
+ 'checkbox',
70
+ 'Two-Factor Authentication',
71
+ 'Two-Factor Login Authentication By IP Address',
72
+ 'All users will be required to authenticate their logins by email-based two-factor authentication when logging in from a new IP address.'
73
+ ),
74
+ array(
75
+ 'enable_two_factor_bypass_on_email_fail',
76
+ '',
77
+ 'N',
78
+ 'checkbox',
79
+ 'By-Pass On Failure',
80
+ 'If Sending Verification Email Sending Fails, Two-Factor Login Authentication Is Ignored',
81
+ 'If you enable two-factor authentication and sending the email with the verification link fails, turning this setting on will by-pass the verification step. Use with caution.'
82
+ )
83
+ )
84
+ );
85
+ $this->m_aLoginProtect = array(
86
+ 'section_title' => 'Login Protection Options',
87
+ 'section_options' => array(
88
+ array(
89
+ 'login_limit_interval',
90
+ '',
91
+ '5',
92
+ 'integer',
93
+ 'Login Cooldown Interval',
94
+ 'Limit login attempts to every X seconds',
95
+ 'WordPress will process only ONE login attempt for every number of seconds specified. Zero (0) turns this off. Suggested: 5'
96
+ ),
97
+ array(
98
+ 'enable_login_gasp_check',
99
+ '',
100
+ 'Y',
101
+ 'checkbox',
102
+ 'G.A.S.P Protection',
103
+ 'Prevent Login By Bots using G.A.S.P. Protection',
104
+ 'Adds a dynamically (Javascript) generated checkbox to the login form that prevents bots using automated login techniques. Recommended: ON'
105
+ )
106
+ )
107
+ );
108
+
109
+ $this->m_aLoggingSection = array(
110
+ 'section_title' => 'Logging Options',
111
+ 'section_options' => array(
112
+ array(
113
+ 'enable_login_protect_log',
114
+ '', 'N',
115
+ 'checkbox',
116
+ 'Login Protect Logging',
117
+ 'Turn on a detailed Login Protect Log',
118
+ 'Will log every event related to login protection and how it is processed. Not recommended to leave on unless you want to debug something and check the login protection is working as you expect.'
119
+ )
120
+ )
121
+ );
122
+
123
+ $this->m_aOptions = array(
124
+ $this->m_aOptionsBase,
125
+ $this->m_aWhitelist,
126
+ $this->m_aTwoFactorAuth,
127
+ $this->m_aLoginProtect,
128
+ $this->m_aLoggingSection
129
+ );
130
+ }
131
+
132
+ public function updateHandler() {
133
+
134
+ $sCurrentVersion = empty( $this->m_aOptionsValues[ 'current_plugin_version' ] )? '0.0' : $this->m_aOptionsValues[ 'current_plugin_version' ];
135
+ if ( version_compare( $sCurrentVersion, '1.4.0', '<' ) ) {
136
+ $aSettingsKey = array(
137
+ 'current_plugin_version',
138
+ 'enable_login_protect',
139
+ 'enable_two_factor_auth_by_ip',
140
+ 'enable_two_factor_bypass_on_email_fail',
141
+ 'login_limit_interval',
142
+ 'enable_login_gasp_check',
143
+ 'enable_login_protect_log',
144
+ );
145
+ $this->migrateOptions( $aSettingsKey );
146
+ }//'1.4.0', '<'
147
+ }
148
+ }
149
+
150
+ endif;
src/icwp-optionshandler-wpsf.php ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-optionshandler-base.php' );
19
+
20
+ if ( !class_exists('ICWP_OptionsHandler_Wpsf') ):
21
+
22
+ class ICWP_OptionsHandler_Wpsf extends ICWP_OptionsHandler_Base_WPSF {
23
+
24
+ const StoreName = 'plugin_options';
25
+ const Default_AccessKeyTimeout = 30;
26
+
27
+ public function __construct( $insPrefix, $insVersion ) {
28
+ parent::__construct( $insPrefix, self::StoreName, $insVersion );
29
+ }
30
+
31
+ public function defineOptions() {
32
+
33
+ $this->m_aIndependentOptions = array(
34
+ 'firewall_processor',
35
+ 'login_processor',
36
+ 'comments_processor',
37
+ 'lockdown_processor',
38
+ 'autoupdates_processor',
39
+ 'logging_processor',
40
+ 'email_processor'
41
+ );
42
+
43
+ $aNonUiOptions = array(
44
+ 'secret_key',
45
+ 'feedback_admin_notice',
46
+ 'update_success_tracker',
47
+ 'capability_can_disk_write',
48
+ 'capability_can_remote_get'
49
+ );
50
+ $this->mergeNonUiOptions( $aNonUiOptions );
51
+
52
+ if ( $this->hasEncryptOption() ) {
53
+
54
+ $aAccessKey = array(
55
+ 'section_title' => 'Admin Access Restriction',
56
+ 'section_options' => array(
57
+ array(
58
+ 'enable_admin_access_restriction',
59
+ '',
60
+ 'N',
61
+ 'checkbox',
62
+ 'Enable Access Key',
63
+ 'Enforce Admin Access Restriction',
64
+ sprintf(
65
+ 'Enable this with great care and consideration. When this Access Key option is enabled, you must specify a key below and use it to gain access to this plugin. [%smore info%s]',
66
+ '<a href="http://icwp.io/2r" target="_blank">',
67
+ '</a>'
68
+ )
69
+ ),
70
+ array(
71
+ 'admin_access_timeout',
72
+ '',
73
+ self::Default_AccessKeyTimeout,
74
+ 'integer',
75
+ 'Access Key Timeout',
76
+ 'Specify A Timeout For Plugin Admin Access',
77
+ 'This will automatically expire your WordPress Simple Firewall session. Does not apply until you enter the access key again. Default: 30 minutes.'
78
+ ),
79
+ array(
80
+ 'admin_access_key',
81
+ '',
82
+ '',
83
+ 'password',
84
+ 'Admin Access Key',
85
+ 'Specify Your Plugin Access Key',
86
+ 'If you forget this, you could potentially lock yourself out from using this plugin. <strong>Leave it blank to <u>not</u> update it</strong>.'
87
+ )
88
+ )
89
+ );
90
+ }
91
+
92
+ $aGeneral = array(
93
+ 'section_title' => 'General Plugin Options',
94
+ 'section_options' => array(
95
+ array(
96
+ 'enable_firewall',
97
+ '', 'N',
98
+ 'checkbox',
99
+ 'Enable Firewall',
100
+ 'Enable (or Disable) The WordPress Firewall Feature',
101
+ 'Regardless of any other settings, this option will turn Off the Firewall feature, or enable your selected Firewall options.'
102
+ ),
103
+ array(
104
+ 'enable_login_protect',
105
+ '',
106
+ 'Y',
107
+ 'checkbox',
108
+ 'Enable Login Protect',
109
+ 'Enable (or Disable) The Login Protection Feature',
110
+ 'Regardless of any other settings, this option will turn Off the Login Protect feature, or enable your selected Login Protect options.'
111
+ ),
112
+ array(
113
+ 'enable_comments_filter',
114
+ '',
115
+ 'Y',
116
+ 'checkbox',
117
+ 'Enable Comments Filter',
118
+ 'Enable (or Disable) The Comments Filter Feature',
119
+ 'Regardless of any other settings, this option will turn Off the Comments Filter feature, or enable your selected Comments Filter options.'
120
+ ),
121
+ array(
122
+ 'enable_lockdown',
123
+ '',
124
+ 'N',
125
+ 'checkbox',
126
+ 'Enable Lockdown',
127
+ 'Enable (or Disable) The Lockdown Feature',
128
+ 'Regardless of any other settings, this option will turn Off the Lockdown feature, or enable your selected Lockdown options.'
129
+ ),
130
+ array(
131
+ 'enable_autoupdates',
132
+ '',
133
+ 'N',
134
+ 'checkbox',
135
+ 'Enable Auto Updates',
136
+ 'Enable (or Disable) The WordPress Automatic Updates Feature',
137
+ 'Regardless of any other settings, this option will turn Off the Auto Updates feature, or enable your selected Auto Updates options.'
138
+ ),
139
+ /*
140
+ array(
141
+ 'enable_auto_plugin_upgrade',
142
+ '',
143
+ 'N',
144
+ 'checkbox',
145
+ 'Auto-Upgrade',
146
+ 'When an upgrade is detected, the plugin will automatically initiate the upgrade.',
147
+ 'If you prefer to manage plugin upgrades, deselect this option. Otherwise, this plugin will auto-upgrade once any available update is detected.'
148
+ ),
149
+ */
150
+ array(
151
+ 'enable_upgrade_admin_notice',
152
+ '',
153
+ 'Y',
154
+ 'checkbox',
155
+ 'Upgrade Notice',
156
+ 'Display A Notice When An Upgrade Is Available',
157
+ 'Will display a notice at the top of your WordPress admin section when a plugin upgrade is available.'
158
+ ),
159
+ array(
160
+ 'delete_on_deactivate',
161
+ '',
162
+ 'N',
163
+ 'checkbox',
164
+ 'Delete Plugin Settings',
165
+ 'Delete All Plugin Settings Upon Plugin Deactivation',
166
+ 'Careful: Removes all plugin options when you deactivite the plugin.'
167
+ )
168
+ )
169
+ );
170
+
171
+ $aEmail = array(
172
+ 'section_title' => 'Email Options',
173
+ 'section_options' => array(
174
+ array(
175
+ 'block_send_email_address',
176
+ '',
177
+ '',
178
+ 'email',
179
+ 'Report Email',
180
+ 'Where to send email reports',
181
+ 'If this is empty, it will default to the blog admin email address.'
182
+ ),
183
+ array(
184
+ 'send_email_throttle_limit',
185
+ '',
186
+ '10',
187
+ 'integer',
188
+ 'Email Throttle Limit',
189
+ 'Limit Emails Per Second',
190
+ 'You throttle emails sent by this plugin by limiting the number of emails sent every second. This is useful in case you get hit by a bot attack. Zero (0) turns this off. Suggested: 10'
191
+ )
192
+ )
193
+ );
194
+
195
+ $this->m_aOptions = array(
196
+ $aGeneral,
197
+ $aEmail
198
+ );
199
+ if ( isset( $aAccessKey ) ) {
200
+ array_unshift( $this->m_aOptions, $aAccessKey );
201
+ }
202
+ }
203
+
204
+ /**
205
+ * This is the point where you would want to do any options verification
206
+ */
207
+ protected function doPrePluginOptionsSave() {
208
+
209
+ $nTimeout = $this->getOpt( 'admin_access_key_timeout');
210
+ if ( $nTimeout <= 0 ) {
211
+ $nTimeout = self::Default_AccessKeyTimeout;
212
+ }
213
+ $this->setOpt( 'admin_access_key_timeout', $nTimeout );
214
+
215
+ $sAccessKey = $this->getOpt( 'admin_access_key');
216
+ if ( empty( $sAccessKey ) ) {
217
+ $this->setOpt( 'enable_admin_access_restriction', 'N' );
218
+ }
219
+
220
+ $sEmail = $this->getOpt( 'block_send_email_address');
221
+ if ( empty( $sEmail ) || !is_email( $sEmail ) ) {
222
+ $sEmail = get_option('admin_email');
223
+ }
224
+ if ( is_email( $sEmail ) ) {
225
+ $this->setOpt( 'block_send_email_address', $sEmail );
226
+ }
227
+
228
+ $sLimit = $this->getOpt( 'send_email_throttle_limit' );
229
+ if ( !is_numeric( $sLimit ) || $sLimit < 0 ) {
230
+ $sLimit = 0;
231
+ }
232
+ $this->setOpt( 'send_email_throttle_limit', $sLimit );
233
+ }
234
+
235
+ protected function updateHandler() {
236
+
237
+ // the 'current_plugin_version' value moved from a direct save option to be
238
+ // included in the plugin options object, so we have to account for it being
239
+ // empty.
240
+ $sCurrentVersion = empty( $this->m_aOptionsValues[ 'current_plugin_version' ] )? '0.0' : $this->m_aOptionsValues[ 'current_plugin_version' ];
241
+ if ( version_compare( $sCurrentVersion, '1.4.0', '<' ) ) {
242
+ $aSettingsKey = array(
243
+ 'current_plugin_version',
244
+ 'enable_firewall',
245
+ 'enable_login_protect',
246
+ 'feedback_admin_notice',
247
+ 'secret_key',
248
+ 'block_send_email_address',
249
+ 'send_email_throttle_limit',
250
+ 'delete_on_deactivate'
251
+ );
252
+ $this->migrateOptions( $aSettingsKey );
253
+ }// '1.4.0', '<'
254
+
255
+ if ( version_compare( $sCurrentVersion, '1.8.2', '<=' ) ) {
256
+
257
+ $fCanRemoteGet = $this->getOpt( 'capability_can_remote_get' );
258
+ $fCanDiskWrite = $this->getOpt( 'capability_can_disk_write' );
259
+
260
+ if ( $fCanDiskWrite === false || $fCanRemoteGet === false ) {
261
+ require_once( dirname(__FILE__).'/icwp-wpfunctions.php' );
262
+ $oWpFilesystem = new ICWP_WpFilesystem_WPSF();
263
+
264
+ $fCanRemoteGet = $oWpFilesystem->getCanWpRemoteGet();
265
+ $this->setOpt( 'capability_can_remote_get', $fCanRemoteGet? 'Y' : 'N' );
266
+
267
+ $fCanDiskWrite = $oWpFilesystem->getCanDiskWrite();
268
+ $this->setOpt( 'capability_can_disk_write', $fCanDiskWrite? 'Y' : 'N' );
269
+ }
270
+ }// '1.8.2', '<='
271
+ }
272
+
273
+ }
274
+
275
+ endif;
src/icwp-plugins-base.php CHANGED
@@ -1,7 +1,7 @@
1
  <?php
2
 
3
- if ( !defined('WORPIT_DS') ) {
4
- define( 'WORPIT_DS', DIRECTORY_SEPARATOR );
5
  }
6
 
7
  if ( !function_exists( '_hlt_e' ) ) {
@@ -15,14 +15,18 @@ if ( !function_exists( '_hlt__' ) ) {
15
  }
16
  }
17
 
 
 
18
  if ( !class_exists('ICWP_WPSF_Base_Plugin') ):
19
 
20
  class ICWP_WPSF_Base_Plugin {
21
 
22
  static public $VERSION;
23
 
 
24
  static public $PLUGIN_NAME;
25
  static public $PLUGIN_PATH;
 
26
  static public $PLUGIN_DIR;
27
  static public $PLUGIN_URL;
28
  static public $PLUGIN_BASENAME;
@@ -38,6 +42,15 @@ class ICWP_WPSF_Base_Plugin {
38
  const ViewExt = '.php';
39
  const ViewDir = 'views';
40
 
 
 
 
 
 
 
 
 
 
41
  protected $m_aPluginMenu;
42
 
43
  protected $m_aAllPluginOptions;
@@ -46,35 +59,64 @@ class ICWP_WPSF_Base_Plugin {
46
 
47
  protected $m_fShowMarketing = '';
48
 
49
- static protected $m_fUpdateSuccessTracker;
 
 
 
 
 
 
 
 
50
  static protected $m_aFailedUpdateOptions;
51
 
52
  public function __construct() {
53
 
54
- add_action( 'plugins_loaded', array( &$this, 'onWpPluginsLoaded' ) );
55
- add_action( 'init', array( &$this, 'onWpInit' ), 0 );
56
- if ( is_admin() ) {
57
- add_action( 'admin_init', array( &$this, 'onWpAdminInit' ) );
58
- add_action( 'admin_notices', array( &$this, 'onWpAdminNotices' ) );
59
- add_action( 'admin_menu', array( &$this, 'onWpAdminMenu' ) );
60
- add_action( 'plugin_action_links', array( &$this, 'onWpPluginActionLinks' ), 10, 4 );
61
- }
 
 
 
 
 
62
  /**
63
  * We make the assumption that all settings updates are successful until told otherwise
64
  * by an actual failing update_option call.
65
  */
66
- self::$m_fUpdateSuccessTracker = true;
67
  self::$m_aFailedUpdateOptions = array();
68
-
69
  $this->m_sParentMenuIdSuffix = 'base';
70
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  protected function getFullParentMenuId() {
73
  return self::ParentMenuId .'-'. $this->m_sParentMenuIdSuffix;
74
  }//getFullParentMenuId
75
 
76
  protected function display( $insView, $inaData = array() ) {
77
- $sFile = dirname(__FILE__).WORPIT_DS.'..'.WORPIT_DS.self::ViewDir.WORPIT_DS.$insView.self::ViewExt;
78
 
79
  if ( !is_file( $sFile ) ) {
80
  echo "View not found: ".$sFile;
@@ -107,8 +149,9 @@ class ICWP_WPSF_Base_Plugin {
107
  protected function getSubmenuPageTitle( $insTitle ) {
108
  return self::ParentTitle.' - '.$insTitle;
109
  }
110
- protected function getSubmenuId( $insId ) {
111
- return $this->getFullParentMenuId().'-'.$insId;
 
112
  }
113
 
114
  public function onWpPluginsLoaded() {
@@ -120,9 +163,11 @@ class ICWP_WPSF_Base_Plugin {
120
 
121
  if ( $this->isIcwpPluginAdminPage() ) {
122
  //Handle form submit
123
- $this->handlePluginFormSubmit();
 
 
124
  }
125
- }//onWpPluginsLoaded
126
 
127
  public function onWpInit() { }
128
 
@@ -130,10 +175,8 @@ class ICWP_WPSF_Base_Plugin {
130
 
131
  //Do Plugin-Specific Admin Work
132
  if ( $this->isIcwpPluginAdminPage() ) {
133
-
134
  //Links up CSS styles for the plugin itself (set the admin bootstrap CSS as a dependency also)
135
  $this->enqueuePluginAdminCss();
136
-
137
  }
138
 
139
  // Determine whether to show ads and marketing messages
@@ -141,25 +184,44 @@ class ICWP_WPSF_Base_Plugin {
141
  $this->isShowMarketing();
142
 
143
  }//onWpAdminInit
144
-
145
  public function onWpAdminMenu() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  $sFullParentMenuId = $this->getFullParentMenuId();
148
-
149
- add_menu_page( self::ParentTitle, self::ParentName, self::ParentPermissions, $sFullParentMenuId, array( $this, 'onDisplayMainMenu' ), $this->getImageUrl( 'icontrolwp_16x16.png' ) );
150
 
151
  //Create and Add the submenu items
152
  $this->createPluginSubMenuItems();
153
  if ( !empty($this->m_aPluginMenu) ) {
154
  foreach ( $this->m_aPluginMenu as $sMenuTitle => $aMenu ) {
155
  list( $sMenuItemText, $sMenuItemId, $sMenuCallBack ) = $aMenu;
156
- add_submenu_page( $sFullParentMenuId, $sMenuTitle, $sMenuItemText, self::ParentPermissions, $sMenuItemId, array( &$this, $sMenuCallBack ) );
157
  }
158
  }
159
 
160
  $this->fixSubmenu();
161
-
162
- }//onWpAdminMenu
 
 
 
 
 
 
163
 
164
  protected function createPluginSubMenuItems(){
165
  /* Override to create array of sub-menu items
@@ -231,14 +293,69 @@ class ICWP_WPSF_Base_Plugin {
231
  /**
232
  * Override this method to handle all the admin notices
233
  */
234
- public function onWpAdminNotices() { }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
 
236
  /**
237
  * This is called from within onWpAdminInit. Use this solely to manage upgrades of the plugin
238
  */
239
- protected function handlePluginUpgrade() { }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
 
241
  protected function handlePluginFormSubmit() { }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
 
243
  protected function enqueuePluginAdminCss() {
244
  $iRand = rand();
@@ -271,7 +388,7 @@ class ICWP_WPSF_Base_Plugin {
271
  } else {
272
  return $sFullNotice;
273
  }
274
- }//getAdminNotice
275
 
276
  protected function redirect( $insUrl, $innTimeout = 1 ) {
277
  echo '
@@ -287,17 +404,7 @@ class ICWP_WPSF_Base_Plugin {
287
  * A little helper function that populates all the plugin options arrays with DB values
288
  */
289
  protected function readyAllPluginOptions() {
290
- $this->initPluginOptions();
291
- $this->populateAllPluginOptions();
292
- }
293
-
294
- /**
295
- * Override to create the plugin options array.
296
- *
297
- * Returns false if nothing happens - i.e. not over-rided.
298
- */
299
- protected function initPluginOptions() {
300
- return false;
301
  }
302
 
303
  /**
@@ -309,7 +416,7 @@ class ICWP_WPSF_Base_Plugin {
309
  */
310
  protected function populateAllPluginOptions() {
311
 
312
- if ( empty($this->m_aAllPluginOptions) && !$this->initPluginOptions() ) {
313
  return false;
314
  }
315
  self::PopulatePluginOptions( $this->m_aAllPluginOptions );
@@ -334,18 +441,55 @@ class ICWP_WPSF_Base_Plugin {
334
  foreach ( $inaOptionsSection['section_options'] as &$aOptionParams ) {
335
 
336
  list( $sOptionKey, $sOptionCurrent, $sOptionDefault, $sOptionType ) = $aOptionParams;
337
- $sCurrentOptionVal = self::getOption( $sOptionKey );
338
  if ( $sOptionType == 'ip_addresses' ) {
339
- if ( !empty( $sCurrentOptionVal ) ) {
340
- $sCurrentOptionVal = implode( "\n", $sCurrentOptionVal );
 
 
 
 
 
 
 
 
 
 
 
 
341
  }
342
  else {
343
- $sCurrentOptionVal = '';
344
  }
345
  }
346
- $aOptionParams[1] = ($sCurrentOptionVal == '' )? $sOptionDefault : $sCurrentOptionVal;
347
  }
348
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
  /**
351
  * $sAllOptionsInput is a comma separated list of all the input keys to be processed from the $_POST
@@ -364,15 +508,20 @@ class ICWP_WPSF_Base_Plugin {
364
  $sOptionValue = $this->getAnswerFromPost( $sOptionKey );
365
  if ( is_null($sOptionValue) ) {
366
 
367
- if ( $sOptionType == 'text' ) { //if it was a text box, and it's null, don't update anything
368
  continue;
369
  } else if ( $sOptionType == 'checkbox' ) { //if it was a checkbox, and it's null, it means 'N'
370
  $sOptionValue = 'N';
 
 
371
  }
372
  }
373
  else { //handle any pre-processing we need to.
374
 
375
- if ( $sOptionType == 'ip_addresses' ) { //ip addresses are textareas, where each is separated by newline
 
 
 
376
 
377
  if ( !class_exists('ICWP_DataProcessor') ) {
378
  require_once ( dirname(__FILE__).'/icwp-data-processor.php' );
@@ -380,6 +529,16 @@ class ICWP_WPSF_Base_Plugin {
380
  $oProcessor = new ICWP_DataProcessor();
381
  $sOptionValue = $oProcessor->ExtractIpAddresses( $sOptionValue );
382
  }
 
 
 
 
 
 
 
 
 
 
383
  }
384
  $this->updateOption( $sOptionKey, $sOptionValue );
385
  }
@@ -429,7 +588,7 @@ class ICWP_WPSF_Base_Plugin {
429
  $iCount++;
430
  }
431
  return $sCollated;
432
- }//collateAllFormInputsForOptionsSection
433
 
434
  protected function isIcwpPluginAdminPage() {
435
 
@@ -447,7 +606,7 @@ class ICWP_WPSF_Base_Plugin {
447
  return;
448
  }
449
 
450
- if ( empty($this->m_aAllPluginOptions) && !$this->initPluginOptions() ) {
451
  return;
452
  }
453
 
@@ -463,34 +622,37 @@ class ICWP_WPSF_Base_Plugin {
463
 
464
  protected function getAnswerFromPost( $insKey, $insPrefix = null ) {
465
  if ( is_null( $insPrefix ) ) {
466
- $insKey = self::$OPTION_PREFIX.$insKey;
467
  }
468
  return ( isset( $_POST[$insKey] )? $_POST[$insKey]: null );
469
  }
470
 
471
  static public function getOption( $insKey, $insAddPrefix = '' ) {
472
- return get_option( self::$OPTION_PREFIX.$insKey );
473
  }
474
 
475
  static public function addOption( $insKey, $insValue ) {
476
- return add_option( self::$OPTION_PREFIX.$insKey, $insValue );
477
  }
478
 
479
  static public function updateOption( $insKey, $insValue ) {
480
- if ( self::getOption( $insKey ) == $insValue ) {
481
  return true;
482
  }
483
- $fResult = update_option( self::$OPTION_PREFIX.$insKey, $insValue );
484
  if ( !$fResult ) {
485
- self::$m_fUpdateSuccessTracker = false;
486
- self::$m_aFailedUpdateOptions[] = self::$OPTION_PREFIX.$insKey;
487
  }
488
  }
489
 
490
  static public function deleteOption( $insKey ) {
491
- return delete_option( self::$OPTION_PREFIX.$insKey );
492
  }
493
 
 
 
 
 
494
  public function onWpActivatePlugin() { }
495
  public function onWpDeactivatePlugin() { }
496
 
@@ -502,6 +664,12 @@ class ICWP_WPSF_Base_Plugin {
502
  }
503
  }
504
 
 
 
 
 
 
 
505
  /**
506
  * Takes an array, an array key, and a default value. If key isn't set, sets it to default.
507
  */
@@ -523,7 +691,13 @@ class ICWP_WPSF_Base_Plugin {
523
  $inaArgs[$insAttrKey] = ( empty($sAttrValue) ) ? '' : ' '.$insElement.'="'.$sAttrValue.'"';
524
  }
525
 
526
- }//CLASS ICWP_WPSF_Base_Plugin
 
 
 
 
 
 
527
 
528
  endif;
529
 
1
  <?php
2
 
3
+ if ( !defined('ICWP_DS') ) {
4
+ define( 'ICWP_DS', DIRECTORY_SEPARATOR );
5
  }
6
 
7
  if ( !function_exists( '_hlt_e' ) ) {
15
  }
16
  }
17
 
18
+ require_once( dirname(__FILE__).'/icwp-wpfunctions.php' );
19
+
20
  if ( !class_exists('ICWP_WPSF_Base_Plugin') ):
21
 
22
  class ICWP_WPSF_Base_Plugin {
23
 
24
  static public $VERSION;
25
 
26
+ static public $PLUGIN_HUMAN_NAME;
27
  static public $PLUGIN_NAME;
28
  static public $PLUGIN_PATH;
29
+ static public $PLUGIN_FILE;
30
  static public $PLUGIN_DIR;
31
  static public $PLUGIN_URL;
32
  static public $PLUGIN_BASENAME;
42
  const ViewExt = '.php';
43
  const ViewDir = 'views';
44
 
45
+ /**
46
+ * @var boolean
47
+ */
48
+ protected $m_fNetworkAdminOnly = false;
49
+ /**
50
+ * @var boolean
51
+ */
52
+ protected $m_fIsMultisite;
53
+
54
  protected $m_aPluginMenu;
55
 
56
  protected $m_aAllPluginOptions;
59
 
60
  protected $m_fShowMarketing = '';
61
 
62
+ protected $m_fDoAutoUpdateCheck = false;
63
+
64
+ protected $m_fAutoPluginUpgrade = false;
65
+
66
+ /**
67
+ * @var ICWP_WpFunctions_WPSF
68
+ */
69
+ protected $m_oWpFunctions;
70
+
71
  static protected $m_aFailedUpdateOptions;
72
 
73
  public function __construct() {
74
 
75
+ $this->m_fIsMultisite = function_exists( 'is_multisite' ) && is_multisite();
76
+
77
+ add_action( 'plugins_loaded', array( $this, 'onWpPluginsLoaded' ) );
78
+ add_action( 'init', array( $this, 'onWpInit' ), 0 );
79
+ if ( $this->isValidAdminArea() ) {
80
+ add_action( 'admin_init', array( $this, 'onWpAdminInit' ) );
81
+ add_action( 'admin_notices', array( $this, 'onWpAdminNotices' ) );
82
+ add_action( 'admin_menu', array( $this, 'onWpAdminMenu' ) );
83
+ add_action( 'network_admin_menu', array( $this, 'onWpNetworkAdminMenu' ) );
84
+ // add_action( 'plugin_action_links', array( $this, 'onWpPluginActionLinks' ), 10, 4 );
85
+ }
86
+ add_action( 'shutdown', array( $this, 'onWpShutdown' ) );
87
+
88
  /**
89
  * We make the assumption that all settings updates are successful until told otherwise
90
  * by an actual failing update_option call.
91
  */
 
92
  self::$m_aFailedUpdateOptions = array();
 
93
  $this->m_sParentMenuIdSuffix = 'base';
94
  }
95
+
96
+ protected function isValidAdminArea() {
97
+ if ( !$this->m_fIsMultisite && is_admin() ) {
98
+ return true;
99
+ }
100
+ else if ( $this->m_fNetworkAdminOnly && $this->m_fIsMultisite && is_network_admin() ) {
101
+ return true;
102
+ }
103
+ return false;
104
+ }
105
+
106
+ /**
107
+ * @return boolean - true if plugin update is available
108
+ */
109
+ public function doPluginUpdateCheck() {
110
+ $this->loadWpFunctions();
111
+ return $this->m_oWpFunctions->getIsPluginUpdateAvailable( self::$PLUGIN_FILE );
112
+ }
113
 
114
  protected function getFullParentMenuId() {
115
  return self::ParentMenuId .'-'. $this->m_sParentMenuIdSuffix;
116
  }//getFullParentMenuId
117
 
118
  protected function display( $insView, $inaData = array() ) {
119
+ $sFile = dirname(__FILE__).ICWP_DS.'..'.ICWP_DS.self::ViewDir.ICWP_DS.$insView.self::ViewExt;
120
 
121
  if ( !is_file( $sFile ) ) {
122
  echo "View not found: ".$sFile;
149
  protected function getSubmenuPageTitle( $insTitle ) {
150
  return self::ParentTitle.' - '.$insTitle;
151
  }
152
+ protected function getSubmenuId( $insId = '' ) {
153
+ $sExtension = empty($insId)? '' : '-'.$insId;
154
+ return $this->getFullParentMenuId().$sExtension;
155
  }
156
 
157
  public function onWpPluginsLoaded() {
163
 
164
  if ( $this->isIcwpPluginAdminPage() ) {
165
  //Handle form submit
166
+ if ( $this->isPluginFormSubmit() && $this->handlePluginFormSubmit() ) {
167
+ $this->m_oWpsfOptions->setOpt( 'feedback_admin_notice', 'Updating Settings <strong>Succeeded</strong>.' );
168
+ }
169
  }
170
+ }
171
 
172
  public function onWpInit() { }
173
 
175
 
176
  //Do Plugin-Specific Admin Work
177
  if ( $this->isIcwpPluginAdminPage() ) {
 
178
  //Links up CSS styles for the plugin itself (set the admin bootstrap CSS as a dependency also)
179
  $this->enqueuePluginAdminCss();
 
180
  }
181
 
182
  // Determine whether to show ads and marketing messages
184
  $this->isShowMarketing();
185
 
186
  }//onWpAdminInit
187
+
188
  public function onWpAdminMenu() {
189
+ if ( !$this->isValidAdminArea() ) {
190
+ return true;
191
+ }
192
+ $this->createMenu();
193
+ }
194
+
195
+ public function onWpNetworkAdminMenu() {
196
+ if ( !$this->isValidAdminArea() ) {
197
+ return true;
198
+ }
199
+ $this->createMenu();
200
+ }
201
+
202
+ protected function createMenu() {
203
 
204
  $sFullParentMenuId = $this->getFullParentMenuId();
205
+ add_menu_page( self::ParentTitle, self::ParentName, self::ParentPermissions, $sFullParentMenuId, array( $this, 'onDisplayAll' ), $this->getImageUrl( 'icontrolwp_16x16.png' ) );
 
206
 
207
  //Create and Add the submenu items
208
  $this->createPluginSubMenuItems();
209
  if ( !empty($this->m_aPluginMenu) ) {
210
  foreach ( $this->m_aPluginMenu as $sMenuTitle => $aMenu ) {
211
  list( $sMenuItemText, $sMenuItemId, $sMenuCallBack ) = $aMenu;
212
+ add_submenu_page( $sFullParentMenuId, $sMenuTitle, $sMenuItemText, self::ParentPermissions, $sMenuItemId, array( $this, $sMenuCallBack ) );
213
  }
214
  }
215
 
216
  $this->fixSubmenu();
217
+ }
218
+ /**
219
+ * Displaying all views now goes through this central function and we work out
220
+ * what to display based on the name of current hook/filter being processed.
221
+ */
222
+ public function onDisplayAll() {
223
+ $this->onDisplayMainMenu();
224
+ }
225
 
226
  protected function createPluginSubMenuItems(){
227
  /* Override to create array of sub-menu items
293
  /**
294
  * Override this method to handle all the admin notices
295
  */
296
+ public function onWpAdminNotices() {
297
+ // Do we have admin priviledges?
298
+ if ( !current_user_can( 'manage_options' ) ) {
299
+ return;
300
+ }
301
+
302
+ if ( $this->m_fDoAutoUpdateCheck ) {
303
+ $this->adminNoticePluginUpgradeAvailable();
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Hooked to 'shutdown'
309
+ */
310
+ public function onWpShutdown() { }
311
 
312
  /**
313
  * This is called from within onWpAdminInit. Use this solely to manage upgrades of the plugin
314
  */
315
+ protected function handlePluginUpgrade() {
316
+
317
+ if ( !is_admin() || !current_user_can( 'manage_options' ) ) {
318
+ return;
319
+ }
320
+
321
+ $this->flushCaches();
322
+
323
+ if ( $this->m_fAutoPluginUpgrade ) {
324
+ $this->loadWpFunctions();
325
+ $this->m_oWpFunctions->doPluginUpgrade( self::$PLUGIN_FILE );
326
+ }
327
+ }
328
+
329
+ protected function isPluginFormSubmit() { }
330
 
331
  protected function handlePluginFormSubmit() { }
332
+
333
+ protected function adminNoticePluginUpgradeAvailable() {
334
+
335
+ // Don't show on the update page.
336
+ if ( isset( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] == 'update.php' ) {
337
+ return;
338
+ }
339
+
340
+ if ( !isset( self::$PLUGIN_FILE ) ) {
341
+ self::$PLUGIN_FILE = plugin_basename(__FILE__);
342
+ }
343
+ if ( !$this->doPluginUpdateCheck() ) {
344
+ return;
345
+ }
346
+ $sNotice = $this->getAdminNoticePluginUpgradeAvailable();
347
+ $this->getAdminNotice( $sNotice, 'updated', true );
348
+ }
349
+
350
+ /**
351
+ * Override this to change the message for the particular plugin upgrade.
352
+ */
353
+ protected function getAdminNoticePluginUpgradeAvailable() {
354
+ $sUpgradeLink = $this->m_oWpFunctions->getPluginUpgradeLink( self::$PLUGIN_FILE );
355
+ $sNotice = '<p>There is an update available for the %s plugin. <a href="%s">Click to update immediately</a>.</p>';
356
+ $sNotice = sprintf( $sNotice, self::$PLUGIN_HUMAN_NAME, $sUpgradeLink );
357
+ return $sNotice;
358
+ }
359
 
360
  protected function enqueuePluginAdminCss() {
361
  $iRand = rand();
388
  } else {
389
  return $sFullNotice;
390
  }
391
+ }
392
 
393
  protected function redirect( $insUrl, $innTimeout = 1 ) {
394
  echo '
404
  * A little helper function that populates all the plugin options arrays with DB values
405
  */
406
  protected function readyAllPluginOptions() {
407
+ // $this->populateAllPluginOptions();
 
 
 
 
 
 
 
 
 
 
408
  }
409
 
410
  /**
416
  */
417
  protected function populateAllPluginOptions() {
418
 
419
+ if ( empty($this->m_aAllPluginOptions) ) {
420
  return false;
421
  }
422
  self::PopulatePluginOptions( $this->m_aAllPluginOptions );
441
  foreach ( $inaOptionsSection['section_options'] as &$aOptionParams ) {
442
 
443
  list( $sOptionKey, $sOptionCurrent, $sOptionDefault, $sOptionType ) = $aOptionParams;
444
+ $mCurrentOptionVal = self::getOption( $sOptionKey );
445
  if ( $sOptionType == 'ip_addresses' ) {
446
+ if ( !empty( $mCurrentOptionVal ) ) {
447
+ $mCurrentOptionVal = implode( "\n", self::ConvertIpListForDisplay( $mCurrentOptionVal ) );
448
+ }
449
+ else {
450
+ $mCurrentOptionVal = '';
451
+ }
452
+ }
453
+ else if ( $sOptionType == 'comma_separated_lists' ) {
454
+ if ( !empty( $mCurrentOptionVal ) ) {
455
+ $aNewValues = array();
456
+ foreach( $mCurrentOptionVal as $sPage => $aParams ) {
457
+ $aNewValues[] = $sPage.', '. implode( ", ", $aParams );
458
+ }
459
+ $mCurrentOptionVal = implode( "\n", $aNewValues );
460
  }
461
  else {
462
+ $mCurrentOptionVal = '';
463
  }
464
  }
465
+ $aOptionParams[1] = ($mCurrentOptionVal == '' )? $sOptionDefault : $mCurrentOptionVal;
466
  }
467
  }
468
+
469
+ public static function ConvertIpListForDisplay( $inaIpList = array() ) {
470
+
471
+ $aDisplay = array();
472
+ if ( empty( $inaIpList ) || empty( $inaIpList['ips'] ) ) {
473
+ return $aDisplay;
474
+ }
475
+
476
+ foreach( $inaIpList['ips'] as $sAddress ) {
477
+
478
+ $mPos = strpos( $sAddress, '-' );
479
+
480
+ if ( $mPos === false || $mPos === 0 ) { //plain IP address
481
+ $sDisplayText = long2ip( $sAddress );
482
+ }
483
+ else {
484
+ list($nStart, $nEnd) = explode( '-', $sAddress );
485
+ $sDisplayText = long2ip( $nStart ) .'-'. long2ip( $nEnd );
486
+ }
487
+ $sLabel = $inaIpList['meta'][ md5($sAddress) ];
488
+ $sLabel = trim( $sLabel, '()' );
489
+ $aDisplay[] = $sDisplayText . ' ('.$sLabel.')';
490
+ }
491
+ return $aDisplay;
492
+ }
493
 
494
  /**
495
  * $sAllOptionsInput is a comma separated list of all the input keys to be processed from the $_POST
508
  $sOptionValue = $this->getAnswerFromPost( $sOptionKey );
509
  if ( is_null($sOptionValue) ) {
510
 
511
+ if ( $sOptionType == 'text' || $sOptionType == 'email' ) { //if it was a text box, and it's null, don't update anything
512
  continue;
513
  } else if ( $sOptionType == 'checkbox' ) { //if it was a checkbox, and it's null, it means 'N'
514
  $sOptionValue = 'N';
515
+ } else if ( $sOptionType == 'integer' ) { //if it was a integer, and it's null, it means '0'
516
+ $sOptionValue = 0;
517
  }
518
  }
519
  else { //handle any pre-processing we need to.
520
 
521
+ if ( $sOptionType == 'integer' ) {
522
+ $sOptionValue = intval( $sOptionValue );
523
+ }
524
+ else if ( $sOptionType == 'ip_addresses' ) { //ip addresses are textareas, where each is separated by newline
525
 
526
  if ( !class_exists('ICWP_DataProcessor') ) {
527
  require_once ( dirname(__FILE__).'/icwp-data-processor.php' );
529
  $oProcessor = new ICWP_DataProcessor();
530
  $sOptionValue = $oProcessor->ExtractIpAddresses( $sOptionValue );
531
  }
532
+ else if ( $sOptionType == 'email' && function_exists( 'is_email' ) && !is_email( $sOptionValue ) ) {
533
+ $sOptionValue = '';
534
+ }
535
+ else if ( $sOptionType == 'comma_separated_lists' ) {
536
+ if ( !class_exists('ICWP_DataProcessor') ) {
537
+ require_once ( dirname(__FILE__).'/icwp-data-processor.php' );
538
+ }
539
+ $oProcessor = new ICWP_DataProcessor();
540
+ $sOptionValue = $oProcessor->ExtractCommaSeparatedList( $sOptionValue );
541
+ }
542
  }
543
  $this->updateOption( $sOptionKey, $sOptionValue );
544
  }
588
  $iCount++;
589
  }
590
  return $sCollated;
591
+ }
592
 
593
  protected function isIcwpPluginAdminPage() {
594
 
606
  return;
607
  }
608
 
609
+ if ( empty($this->m_aAllPluginOptions) ) {
610
  return;
611
  }
612
 
622
 
623
  protected function getAnswerFromPost( $insKey, $insPrefix = null ) {
624
  if ( is_null( $insPrefix ) ) {
625
+ $insKey = self::getKey($insKey);
626
  }
627
  return ( isset( $_POST[$insKey] )? $_POST[$insKey]: null );
628
  }
629
 
630
  static public function getOption( $insKey, $insAddPrefix = '' ) {
631
+ return get_option( self::getKey($insKey) );
632
  }
633
 
634
  static public function addOption( $insKey, $insValue ) {
635
+ return add_option( self::getKey($insKey), $insValue );
636
  }
637
 
638
  static public function updateOption( $insKey, $insValue ) {
639
+ if ( !is_object( $insValue ) && self::getOption( $insKey ) == $insValue ) {
640
  return true;
641
  }
642
+ $fResult = update_option( self::getKey($insKey), $insValue );
643
  if ( !$fResult ) {
644
+ self::$m_aFailedUpdateOptions[] = self::getKey($insKey);
 
645
  }
646
  }
647
 
648
  static public function deleteOption( $insKey ) {
649
+ return delete_option( self::getKey($insKey) );
650
  }
651
 
652
+ static public function getKey( $insKey ) {
653
+ return self::$OPTION_PREFIX.$insKey;
654
+ }
655
+
656
  public function onWpActivatePlugin() { }
657
  public function onWpDeactivatePlugin() { }
658
 
664
  }
665
  }
666
 
667
+ protected function loadWpFunctions() {
668
+ if ( !isset( $this->m_oWpFunctions ) ) {
669
+ $this->m_oWpFunctions = new ICWP_WpFunctions_WPSF();
670
+ }
671
+ }
672
+
673
  /**
674
  * Takes an array, an array key, and a default value. If key isn't set, sets it to default.
675
  */
691
  $inaArgs[$insAttrKey] = ( empty($sAttrValue) ) ? '' : ' '.$insElement.'="'.$sAttrValue.'"';
692
  }
693
 
694
+ protected function flushCaches() {
695
+ if (function_exists('w3tc_pgcache_flush')) {
696
+ w3tc_pgcache_flush();
697
+ }
698
+ }
699
+
700
+ }
701
 
702
  endif;
703
 
src/icwp-processor-autoupdates.php ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_AutoUpdatesProcessor') ):
21
+
22
+ class ICWP_AutoUpdatesProcessor extends ICWP_BaseProcessor_WPSF {
23
+
24
+ /**
25
+ * Resets the object values to be re-used anew
26
+ */
27
+ public function reset() {
28
+ parent::reset();
29
+ }
30
+
31
+ /**
32
+ */
33
+ public function run() {
34
+
35
+ if ( $this->m_aOptions['autoupdate_core'] == 'core_never' ) {
36
+ add_filter( 'allow_minor_auto_core_updates', '__return_false' );
37
+ add_filter( 'allow_major_auto_core_updates', '__return_false' );
38
+ }
39
+ else if ( $this->m_aOptions['autoupdate_core'] == 'core_minor' ) {
40
+ add_filter( 'allow_minor_auto_core_updates', '__return_true' );
41
+ add_filter( 'allow_major_auto_core_updates', '__return_false' );
42
+ }
43
+ else if ( $this->m_aOptions['autoupdate_core'] == 'core_major' ) {
44
+ add_filter( 'allow_minor_auto_core_updates', '__return_true' );
45
+ add_filter( 'allow_major_auto_core_updates', '__return_true' );
46
+ }
47
+
48
+ $sFunction = ( $this->m_aOptions['enable_autoupdate_translations'] == 'Y' )? '__return_true' : '__return_false';
49
+ add_filter( 'auto_update_translation', $sFunction );
50
+
51
+ add_filter( 'auto_update_plugin', array( $this, 'autoupdate_plugins' ), 1, 2 );
52
+
53
+ $sFunction = ( $this->m_aOptions['enable_autoupdate_themes'] == 'Y' )? '__return_true' : '__return_false';
54
+ add_filter( 'auto_update_themes', $sFunction );
55
+
56
+ if ( $this->m_aOptions['enable_autoupdate_ignore_vcs'] == 'Y' ) {
57
+ add_filter( 'automatic_updates_is_vcs_checkout', array( $this, 'disable_for_vcs'), 10, 2 );
58
+ }
59
+
60
+ if ( $this->m_aOptions['enable_autoupdate_disable_all'] == 'Y' ) {
61
+ add_filter( 'automatic_updater_disabled', '__return_true' );
62
+ }
63
+ }
64
+
65
+ public function autoupdate_plugins( $infUpdate, $insPluginSlug ) {
66
+
67
+ if ( strpos( $insPluginSlug, 'icwp-wpsf.php') !== false ) {
68
+ return $this->m_aOptions['autoupdate_plugin_wpsf'] == 'Y';
69
+ }
70
+ if ( $this->m_aOptions['enable_autoupdate_plugins'] == 'Y' ) {
71
+ return true;
72
+ }
73
+ return $infUpdate;
74
+ }
75
+
76
+ public function disable_for_vcs( $checkout, $context ) {
77
+ return false;
78
+ }
79
+ }
80
+
81
+ endif;
src/icwp-processor-commentsfilter.php ADDED
@@ -0,0 +1,431 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-basedb-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_CommentsFilterProcessor') ):
21
+
22
+ class ICWP_CommentsFilterProcessor extends ICWP_BaseDbProcessor_WPSF {
23
+
24
+ const TableName = 'comments_filter';
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ static protected $sModeFile_LoginThrottled;
30
+
31
+ /**
32
+ * The unique comment token assigned to this page
33
+ * @var integer
34
+ */
35
+ protected $m_sUniqueToken;
36
+
37
+ /**
38
+ * The unique comment token assigned to this page
39
+ * @var integer
40
+ */
41
+ protected $m_sUniqueFormId;
42
+
43
+ /**
44
+ * The length of time that must pass between a page being loaded and comment being posted.
45
+ * @var integer
46
+ */
47
+ protected $m_nCommentCooldown;
48
+
49
+ /**
50
+ * The maxium length of time that comment token may last and be used.
51
+ * @var integer
52
+ */
53
+ protected $m_nCommentTokenExpire;
54
+
55
+ protected $m_nLastLoginTime;
56
+ protected $m_sSecretKey;
57
+
58
+ protected $m_sGaspKey;
59
+
60
+ /**
61
+ * Flag as to whether Two Factor Authentication will be by-pass when sending the verification
62
+ * email fails.
63
+ *
64
+ * @var boolean
65
+ */
66
+ protected $m_fAllowTwoFactorByPass;
67
+
68
+ public function __construct() {
69
+ parent::__construct( self::TableName );
70
+ $this->createTable();
71
+ $this->reset();
72
+ }
73
+
74
+ /**
75
+ * Resets the object values to be re-used anew
76
+ */
77
+ public function reset() {
78
+ parent::reset();
79
+ $this->m_sUniqueToken = '';
80
+ $this->m_sCommentStatus = '';
81
+ }
82
+
83
+ /**
84
+ */
85
+ public function run() {
86
+ // Add GASP checking to the comment form.
87
+ if ( $this->m_aOptions[ 'enable_comments_gasp_protection' ] == 'Y' ) {
88
+ add_action( 'comment_form', array( $this, 'printGaspFormHook_Action' ), 1 );
89
+ add_action( 'comment_form', array( $this, 'printGaspFormParts_Action' ), 2 );
90
+ add_filter( 'preprocess_comment', array( $this, 'doGaspCommentCheck_Filter' ), 1, 1 );
91
+ add_filter( 'pre_comment_approved', array( $this, 'doGaspStatusSet_Filter' ), 1, 1 );
92
+ }
93
+ }
94
+
95
+ /**
96
+ * @return void
97
+ */
98
+ public function printGaspFormHook_Action() {
99
+
100
+ if ( !$this->getDoCommentsCheck() ) {
101
+ return;
102
+ }
103
+ global $post;
104
+ if ( !isset( $post ) || $post->comment_status != 'open' ) {
105
+ return;
106
+ }
107
+ $this->deleteOldPostCommentTokens( $post->ID );
108
+ $this->createUniquePostCommentToken( $post->ID, $this->m_sUniqueToken );
109
+
110
+ require_once( dirname(__FILE__).'/icwp-data-processor.php' );
111
+ $this->m_sUniqueFormId = ICWP_DataProcessor::GenerateRandomString( rand(7, 23), true );
112
+
113
+ echo $this->getGaspCommentsHookHtml();
114
+ }
115
+
116
+ /**
117
+ * Tells us whether, for this particular comment post, if we should do comments checking.
118
+ *
119
+ * @return boolean
120
+ */
121
+ protected function getDoCommentsCheck() {
122
+ if ( !is_user_logged_in() ) {
123
+ return true;
124
+ }
125
+ else if ( $this->m_aOptions[ 'enable_comments_gasp_protection_for_logged_in' ] == 'Y' ) {
126
+ return true;
127
+ }
128
+ return false;
129
+ }
130
+
131
+ /**
132
+ * @return void
133
+ */
134
+ public function printGaspFormParts_Action() {
135
+ if ( $this->getDoCommentsCheck() ) {
136
+ echo $this->getGaspCommentsHtml();
137
+ }
138
+ }
139
+
140
+ /**
141
+ * @return string
142
+ */
143
+ public function getGaspCommentsHookHtml() {
144
+ $sId = $this->m_sUniqueFormId;
145
+ $sReturn = '<p id="'.$sId.'"></p>'; // we use this unique <p> to hook onto using javascript
146
+ $sReturn .= '<input type="hidden" id="_sugar_sweet_email" name="sugar_sweet_email" value="" />';
147
+ $sReturn .= '<input type="hidden" id="_comment_token" name="comment_token" value="'.$this->m_sUniqueToken.'" />';
148
+ return $sReturn;
149
+ }
150
+
151
+ public function getGaspCommentsHtml() {
152
+
153
+ $sId = $this->m_sUniqueFormId;
154
+ $sConfirm = stripslashes( $this->m_aOptions[ 'custom_message_checkbox' ] );
155
+ $sAlert = stripslashes( $this->m_aOptions[ 'custom_message_alert' ] );
156
+ $sCommentWait = stripslashes( $this->m_aOptions[ 'custom_message_comment_wait' ] );
157
+ $nCooldown = $this->m_aOptions[ 'comments_cooldown_interval' ];
158
+ $nExpire = $this->m_aOptions[ 'comments_token_expire_interval' ];
159
+
160
+ if ( strpos( $sCommentWait, '%s' ) !== false ) {
161
+ $sCommentWait = sprintf( $sCommentWait, $nCooldown );
162
+ $sJsCommentWait = str_replace( '%s', '"+nRemaining+"', $this->m_aOptions[ 'custom_message_comment_wait' ] );
163
+ $sJsCommentWait = '"'.$sJsCommentWait.'"';
164
+ }
165
+ else {
166
+ $sJsCommentWait = '"'. $this->m_aOptions[ 'custom_message_comment_wait' ].'"';
167
+ }
168
+ $sCommentReload = $this->m_aOptions[ 'custom_message_comment_reload' ];
169
+
170
+ $sReturn = "
171
+ <script type='text/javascript'>
172
+
173
+ function cb_click$sId() {
174
+ cb_name$sId.value=cb$sId.name;
175
+ }
176
+ function check$sId() {
177
+ if( cb$sId.checked != true ) {
178
+ alert( \"$sAlert\" ); return false;
179
+ }
180
+ return true;
181
+ }
182
+ function reenableButton$sId() {
183
+ nTimerCounter{$sId}++;
184
+ nRemaining = $nCooldown - nTimerCounter$sId;
185
+ subbutton$sId.value = $sJsCommentWait;
186
+ if ( nTimerCounter$sId >= $nCooldown ) {
187
+ subbutton$sId.value = origButtonValue$sId;
188
+ subbutton$sId.disabled = false;
189
+ clearInterval( sCountdownTimer$sId );
190
+ }
191
+ }
192
+ function redisableButton$sId() {
193
+ subbutton$sId.value = \"$sCommentReload\";
194
+ subbutton$sId.disabled = true;
195
+ }
196
+
197
+ var $sId = document.getElementById('$sId');
198
+
199
+ var cb$sId = document.createElement('input');
200
+ cb$sId.type = 'checkbox';
201
+ cb$sId.id = 'checkbox$sId';
202
+ cb$sId.name = 'checkbox$sId';
203
+ cb$sId.style.width = '25px';
204
+ cb$sId.onclick = cb_click$sId;
205
+
206
+ var label$sId = document.createElement( 'label' );
207
+ label$sId.htmlFor = 'checkbox$sId';
208
+ label$sId.innerHTML = \"$sConfirm\";
209
+
210
+ var cb_name$sId = document.createElement('input');
211
+ cb_name$sId.type = 'hidden';
212
+ cb_name$sId.name = 'cb_nombre';
213
+
214
+ $sId.appendChild( cb$sId );
215
+ $sId.appendChild( label$sId );
216
+ $sId.appendChild( cb_name$sId );
217
+
218
+ var frm$sId = cb$sId.form;
219
+ frm$sId.onsubmit = check$sId;
220
+
221
+ ".(
222
+ ( $nCooldown > 0 || $nExpire > 0 ) ?
223
+ "
224
+ var subbuttonList$sId = frm$sId.querySelectorAll( 'input[type=\"submit\"]' );
225
+
226
+ if ( typeof( subbuttonList$sId ) != \"undefined\" ) {
227
+ subbutton$sId = subbuttonList{$sId}[0];
228
+ if ( typeof( subbutton$sId ) != \"undefined\" ) {
229
+
230
+ ".(
231
+ ( $nCooldown > 0 )?
232
+ "
233
+ subbutton$sId.disabled = true;
234
+ origButtonValue$sId = subbutton$sId.value;
235
+ subbutton$sId.value = \"$sCommentWait\";
236
+ nTimerCounter$sId = 0;
237
+ sCountdownTimer$sId = setInterval( reenableButton$sId, 1000 );
238
+ "
239
+ :''
240
+ ).(
241
+ ( $nExpire > 0 )? "sTimeoutTimer$sId = setTimeout( redisableButton$sId, ".(1000 * $nExpire - 1000)." );" : ''
242
+ )."
243
+ }
244
+ }
245
+ ":''
246
+ )."
247
+ </script>
248
+ ";
249
+ return $sReturn;
250
+ }
251
+
252
+ /**
253
+ * @param array $inaCommentData
254
+ * @return unknown|string
255
+ */
256
+ public function doGaspCommentCheck_Filter( $inaCommentData ) {
257
+
258
+ if ( !$this->getDoCommentsCheck() ) {
259
+ return $inaCommentData;
260
+ }
261
+ if( !isset( $_POST['cb_nombre'] ) ) {
262
+ $this->m_sCommentStatus = 'spam';
263
+ }
264
+ // we have the cb name, is it set?
265
+ else if ( !isset( $_POST[ $_POST['cb_nombre'] ] ) ) {
266
+ $this->m_sCommentStatus = 'spam';
267
+ }
268
+ // honeypot check
269
+ else if ( isset( $_POST['sugar_sweet_email'] ) && $_POST['sugar_sweet_email'] !== '' ) {
270
+ $this->m_sCommentStatus = 'spam';
271
+ }
272
+ // check the unique comment token is present
273
+ else if ( !isset( $_POST['comment_token'] ) || !$this->checkCommentToken( $_POST['comment_token'], $inaCommentData['comment_post_ID'] ) ) {
274
+ $this->m_sCommentStatus = 'spam';
275
+ }
276
+ if ( false && $this->m_sCommentStatus = 'spam' ) { //add option to die later
277
+ wp_die( "Ding! Dong! The witch is dead." );
278
+ }
279
+ return $inaCommentData;
280
+ }
281
+
282
+ protected function checkCommentToken( $insCommentToken, $insPostId ) {
283
+
284
+ $sToken = esc_sql( $insCommentToken ); //just incase someone try to get funky.
285
+
286
+ // Try to get the database entry that corresponds to this set of data. If we get nothing, fail.
287
+ $sQuery = "
288
+ SELECT *
289
+ FROM `%s`
290
+ WHERE
291
+ `unique_token` = '%s'
292
+ AND `post_id` = '%s'
293
+ AND `ip_long` = '%s'
294
+ AND `deleted_at` = '0'
295
+ ";
296
+ $sQuery = sprintf( $sQuery,
297
+ $this->m_sTableName,
298
+ $sToken,
299
+ $insPostId,
300
+ $this->m_nRequestIp
301
+ );
302
+ $mResult = $this->selectCustomFromTable( $sQuery );
303
+
304
+ if ( empty( $mResult ) || !is_array($mResult) || count($mResult) != 1 ) {
305
+ return false;
306
+ }
307
+ else {
308
+ // Only 1 chance is given per token, so we delete it
309
+ $this->deleteUniquePostCommentToken( $sToken, $insPostId );
310
+
311
+ // Did suficient time pass, or has it expired?
312
+ $nNow = time();
313
+ $aRecord = $mResult[0];
314
+ $nInterval = $nNow - $aRecord['created_at'];
315
+ if ( $nInterval < $this->m_aOptions[ 'comments_cooldown_interval' ]
316
+ || ( $this->m_aOptions[ 'comments_token_expire_interval' ] > 0 && $nInterval > $this->m_aOptions[ 'comments_token_expire_interval' ] )
317
+ ) {
318
+ return false;
319
+ }
320
+ return true;
321
+ }
322
+ }
323
+
324
+ public function doGaspStatusSet_Filter( $sApprovalStatus ) {
325
+ if( !empty( $this->m_sCommentStatus ) ){
326
+ $sApprovalStatus = $this->m_sCommentStatus;
327
+ }
328
+ return $sApprovalStatus;
329
+ }
330
+
331
+ public function createTable() {
332
+
333
+ // Set up comments ID table
334
+ $sSqlTables = "CREATE TABLE IF NOT EXISTS `%s` (
335
+ `id` int(11) NOT NULL AUTO_INCREMENT,
336
+ `post_id` int(11) NOT NULL DEFAULT '0',
337
+ `unique_token` varchar(32) NOT NULL DEFAULT '',
338
+ `ip_long` bigint(20) NOT NULL DEFAULT '0',
339
+ `created_at` int(15) NOT NULL DEFAULT '0',
340
+ `deleted_at` int(15) NOT NULL DEFAULT '0',
341
+ PRIMARY KEY (`id`)
342
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
343
+ $sSqlTables = sprintf( $sSqlTables, $this->m_sTableName );
344
+ $mResult = $this->doSql( $sSqlTables );
345
+ }
346
+
347
+ /**
348
+ *
349
+ * @param string $insUniqueToken
350
+ * @param string $insPostId
351
+ */
352
+ protected function deleteUniquePostCommentToken( $insUniqueToken, $insPostId, $infSoftDelete = false ) {
353
+
354
+ if ( $infSoftDelete ) {
355
+ $nNow = time();
356
+ $sQuery = "
357
+ UPDATE `%s`
358
+ SET `deleted_at` = '%s'
359
+ WHERE
360
+ `unique_token` = '%s'
361
+ AND `post_id` = '%s'
362
+ ";
363
+ $sQuery = sprintf( $sQuery,
364
+ $this->m_sTableName,
365
+ $nNow,
366
+ $sToken,
367
+ $insPostId
368
+ );
369
+ $this->doSql( $sQuery );
370
+ }
371
+ else {
372
+ $aWhere['unique_token'] = $insUniqueToken;
373
+ $aWhere['post_id'] = $insPostId;
374
+ $this->deleteRowsFromTable( $aWhere );
375
+ }
376
+ }
377
+
378
+ /**
379
+ *
380
+ * @param string $insUniqueToken
381
+ * @param string $insPostId
382
+ */
383
+ protected function deleteOldPostCommentTokens( $insPostId, $infSoftDelete = false ) {
384
+
385
+ if ( $infSoftDelete ) {
386
+ $nNow = time();
387
+ $sQuery = "
388
+ UPDATE `%s`
389
+ SET `deleted_at` = '%s'
390
+ WHERE
391
+ `ip_long` = '%s'
392
+ AND `post_id` = '%s'
393
+ ";
394
+ $sQuery = sprintf( $sQuery,
395
+ $this->m_sTableName,
396
+ $nNow,
397
+ $this->m_nRequestIp,
398
+ $insPostId
399
+ );
400
+ $this->doSql( $sQuery );
401
+ }
402
+ else {
403
+ $aWhere['ip_long'] = $this->m_nRequestIp;
404
+ $aWhere['post_id'] = $insPostId;
405
+ $this->deleteRowsFromTable( $aWhere );
406
+ }
407
+ }
408
+
409
+ protected function createUniquePostCommentToken( $insPostId, &$outsUniqueToken = '' ) {
410
+
411
+ // Now add new pending entry
412
+ $nNow = time();
413
+ $outsUniqueToken = $this->getUniqueToken( $insPostId );
414
+ $aData = array();
415
+ $aData[ 'post_id' ] = $insPostId;
416
+ $aData[ 'unique_token' ] = $outsUniqueToken;
417
+ $aData[ 'ip_long' ] = $this->m_nRequestIp;
418
+ $aData[ 'created_at' ] = $nNow;
419
+
420
+ $mResult = $this->insertIntoTable( $aData );
421
+ return $mResult;
422
+ }
423
+
424
+ protected function getUniqueToken( $insPostId ) {
425
+ $sToken = uniqid( $this->m_nRequestIp.$insPostId );
426
+ return md5( $sToken );
427
+ }
428
+
429
+ }
430
+
431
+ endif;
src/icwp-processor-email.php ADDED
@@ -0,0 +1,168 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_EmailProcessor') ):
21
+
22
+ class ICWP_EmailProcessor extends ICWP_BaseProcessor_WPSF {
23
+
24
+ protected $m_sRecipientAddress;
25
+ protected $m_sSiteName;
26
+
27
+ /**
28
+ * @var string
29
+ */
30
+ static protected $sModeFile_EmailThrottled;
31
+ /**
32
+ * @var int
33
+ */
34
+ static protected $nThrottleInterval = 20;
35
+ /**
36
+ * @var int
37
+ */
38
+ protected $m_nEmailThrottleLimit;
39
+ /**
40
+ * @var int
41
+ */
42
+ protected $m_nEmailThrottleTime;
43
+ /**
44
+ * @var int
45
+ */
46
+ protected $m_nEmailThrottleCount;
47
+ /**
48
+ * @var boolean
49
+ */
50
+ protected $m_fEmailIsThrottled;
51
+
52
+ public function reset() {
53
+ parent::reset();
54
+ self::$sModeFile_EmailThrottled = dirname( __FILE__ ).'/../mode.email_throttled';
55
+ }
56
+
57
+ /**
58
+ * @param string $insEmailAddress
59
+ * @param string $insEmailSubject
60
+ * @param array $inaMessage
61
+ * @uses wp_mail
62
+ */
63
+ public function sendEmailTo( $insEmailAddress, $insEmailSubject, $inaMessage ) {
64
+
65
+ $aHeaders = array(
66
+ 'MIME-Version: 1.0',
67
+ 'Content-type: text/plain;',
68
+ sprintf( 'From: %s, Simple Firewall Plugin <%s>', $this->m_sSiteName, $insEmailAddress ),
69
+ sprintf( "Subject: %s", $insEmailSubject ),
70
+ 'X-Mailer: PHP/'.phpversion()
71
+ );
72
+
73
+ $this->updateEmailThrottle();
74
+ // We appear to have "succeeded" if the throttle is applied.
75
+ if ( $this->m_fEmailIsThrottled ) {
76
+ return true;
77
+ }
78
+ return wp_mail( $insEmailAddress, $insEmailSubject, implode( "\r\n", $inaMessage ), implode( "\r\n", $aHeaders ) );
79
+ }
80
+
81
+ /**
82
+ * Will send email to the default recipient setup in the object.
83
+ *
84
+ * @param string $insEmailSubject
85
+ * @param array $inaMessage
86
+ */
87
+ public function sendEmail( $insEmailSubject, $inaMessage ) {
88
+ if ( !isset( $this->m_sRecipientAddress ) ) {
89
+ return false;
90
+ }
91
+ return $this->sendEmailTo( $this->m_sRecipientAddress, $insEmailSubject, $inaMessage );
92
+ }
93
+
94
+ /**
95
+ * Whether we're throttled is dependent on 2 signals. The time interval has changed, or the there's a file
96
+ * system object telling us we're throttled.
97
+ *
98
+ * The file system object takes precedence.
99
+ *
100
+ * @return boolean
101
+ */
102
+ protected function updateEmailThrottle() {
103
+
104
+ // Throttling Is Effectively Off
105
+ if ( $this->m_nEmailThrottleLimit <= 0 ) {
106
+ $this->setThrottledFile( false );
107
+ return $this->m_fEmailIsThrottled;
108
+ }
109
+
110
+ // Check that there is an email throttle file. If it exists and its modified time is greater than the
111
+ // current $this->m_nEmailThrottleTime it suggests another process has touched the file and updated it
112
+ // concurrently. So, we update our $this->m_nEmailThrottleTime accordingly.
113
+ if ( is_file( self::$sModeFile_EmailThrottled ) ) {
114
+ $nModifiedTime = filemtime( self::$sModeFile_EmailThrottled );
115
+ if ( $nModifiedTime > $this->m_nEmailThrottleTime ) {
116
+ $this->m_nEmailThrottleTime = $nModifiedTime;
117
+ }
118
+ }
119
+
120
+ $nNow = time();
121
+ if ( !isset($this->m_nEmailThrottleTime) || $this->m_nEmailThrottleTime > $nNow ) {
122
+ $this->m_nEmailThrottleTime = $nNow;
123
+ }
124
+ if ( !isset($this->m_nEmailThrottleCount) ) {
125
+ $this->m_nEmailThrottleCount = 0;
126
+ }
127
+
128
+ // If $nNow is greater than throttle interval (1s) we turn off the file throttle and reset the count
129
+ $nDiff = $nNow - $this->m_nEmailThrottleTime;
130
+ if ( $nDiff > self::$nThrottleInterval ) {
131
+ $this->m_nEmailThrottleTime = $nNow;
132
+ $this->m_nEmailThrottleCount = 1; //we set to 1 assuming that this was called because we're about to send, or have just sent, an email.
133
+ $this->setThrottledFile( false );
134
+ }
135
+ else if ( is_file( self::$sModeFile_EmailThrottled ) || ( $this->m_nEmailThrottleCount >= $this->m_nEmailThrottleLimit ) ) {
136
+ $this->setThrottledFile( true );
137
+ }
138
+ else {
139
+ $this->m_nEmailThrottleCount++;
140
+ }
141
+ }
142
+
143
+ public function setThrottledFile( $infOn = false ) {
144
+
145
+ $this->m_fEmailIsThrottled = $infOn;
146
+
147
+ if ( $infOn && !is_file( self::$sModeFile_EmailThrottled ) && function_exists('touch') ) {
148
+ @touch( self::$sModeFile_EmailThrottled );
149
+ }
150
+ else if ( is_file(self::$sModeFile_EmailThrottled) ) {
151
+ @unlink( self::$sModeFile_EmailThrottled );
152
+ }
153
+ }
154
+
155
+ public function setDefaultRecipientAddress( $insEmailAddress ) {
156
+ $this->m_sRecipientAddress = $insEmailAddress;
157
+ }
158
+
159
+ public function setSiteName( $insName ) {
160
+ $this->m_sSiteName = $insName;
161
+ }
162
+
163
+ public function setThrottleLimit( $innLimit ) {
164
+ $this->m_nEmailThrottleLimit = $innLimit;
165
+ }
166
+ }
167
+
168
+ endif;
src/icwp-processor-firewall.php ADDED
@@ -0,0 +1,620 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_FirewallProcessor') ):
21
+
22
+ class ICWP_FirewallProcessor extends ICWP_BaseProcessor_WPSF {
23
+
24
+ protected $m_nRequestTimestamp;
25
+
26
+ protected $m_aBlockSettings;
27
+
28
+ protected $m_aWhitelistPages;
29
+ protected $m_aWhitelistPagesPatterns;
30
+ protected $m_aCustomWhitelistPageParams;
31
+
32
+ protected $m_aRequestUriParts;
33
+
34
+ private $m_nLoopProtect;
35
+ private $m_sFirewallMessage;
36
+
37
+ /**
38
+ * @var string
39
+ */
40
+ protected $m_sListItemLabel;
41
+
42
+ /**
43
+ * A combination of all current request $_GET and $_POST (and optionally $_COOKIE)
44
+ * @var array
45
+ */
46
+ protected $m_aOrigPageParams;
47
+
48
+ /**
49
+ * This is $m_aOrigPageParams after any parameter whitelisting has taken place
50
+ * @var array
51
+ */
52
+ protected $m_aPageParams;
53
+
54
+ /**
55
+ * All the array values of $m_aPageParams
56
+ * @var array
57
+ */
58
+ protected $m_aPageParamValuesToCheck;
59
+
60
+ /**
61
+ * All the remaining values of the page parameters after they've been filtered
62
+ * @var array
63
+ */
64
+ protected $m_aPageParamValues;
65
+
66
+ public function __construct() {
67
+ parent::__construct();
68
+
69
+ $sMessage = "You were blocked by the %sWordPress Simple Firewall%s.";
70
+ $this->m_sFirewallMessage = sprintf( $sMessage, '<a href="http://wordpress.org/plugins/wp-simple-firewall/" target="_blank">', '</a>');
71
+ }
72
+
73
+ /**
74
+ * @see ICWP_BaseProcessor_WPSF::setOptions()
75
+ */
76
+ public function setOptions( &$inaOptions ) {
77
+ parent::setOptions( $inaOptions );
78
+
79
+ // collect up all the settings to pass to the processor
80
+ $aSettingSlugs = array(
81
+ 'include_cookie_checks',
82
+ 'block_dir_traversal',
83
+ 'block_sql_queries',
84
+ 'block_wordpress_terms',
85
+ 'block_field_truncation',
86
+ 'block_exe_file_uploads',
87
+ 'block_leading_schema'
88
+ );
89
+ $this->m_aBlockSettings = array();
90
+ foreach( $aSettingSlugs as $sSettingKey ) {
91
+ $this->m_aBlockSettings[ $sSettingKey ] = $this->m_aOptions[$sSettingKey] == 'Y';
92
+ }
93
+
94
+ $this->m_aCustomWhitelistPageParams = is_array( $this->m_aOptions[ 'page_params_whitelist' ] )? $this->m_aOptions[ 'page_params_whitelist' ] : array();
95
+ $this->setLogging( $this->m_aOptions[ 'enable_firewall_log' ] == 'Y' );
96
+ }
97
+
98
+ /**
99
+ * @return boolean
100
+ */
101
+ public function getNeedsEmailHandler() {
102
+ if ( $this->m_aOptions['block_send_email'] == 'Y' ) {
103
+ return true;
104
+ }
105
+ return false;
106
+ }
107
+
108
+ public function reset() {
109
+ parent::reset();
110
+ $this->m_nRequestTimestamp = time();
111
+ $this->m_nLoopProtect = 0;
112
+ }
113
+
114
+ /**
115
+ * Should return false when logging is disabled.
116
+ *
117
+ * @return false|array - false when logging is disabled, array with log data otherwise
118
+ * @see ICWP_BaseProcessor_WPSF::getLogData()
119
+ */
120
+ public function flushLogData() {
121
+
122
+ if ( !$this->m_fLoggingEnabled ) {
123
+ return false;
124
+ }
125
+
126
+ $this->m_aLog = array(
127
+ 'category' => self::LOG_CATEGORY_FIREWALL,
128
+ 'messages' => serialize( $this->m_aLogMessages ),
129
+ 'created_at' => $this->m_nRequestTimestamp,
130
+ 'ip' => long2ip( $this->m_nRequestIp ),
131
+ 'ip_long' => $this->m_nRequestIp,
132
+ );
133
+ $this->resetLog();
134
+ return $this->m_aLog;
135
+ }
136
+
137
+ /**
138
+ * @return boolean - true if visitor is permitted, false if it should be blocked.
139
+ */
140
+ public function doFirewallCheck() {
141
+
142
+ // if we couldn't process the REQUEST_URI parts, we can't firewall so we effectively whitelist without erroring.
143
+ $this->setRequestUriPageParts();
144
+ if ( empty( $this->m_aRequestUriParts ) ) {
145
+ $this->logInfo( 'Could not parse the URI so cannot effectively firewall.' );
146
+ return true;
147
+ }
148
+
149
+ // Set up the page parameters ($_GET and $_POST and optionally $_COOKIE). If there are none, quit since there's nothing for the firewall to check.
150
+ $this->setPageParams();
151
+ if ( empty( $this->m_aPageParams ) ) {
152
+ $this->logInfo( 'After any whitelist options were applied, there were no page parameters to check on this visit.' );
153
+ return true;
154
+ }
155
+ $this->m_aPageParamValuesToCheck = array_values( $this->m_aPageParams );
156
+
157
+ if ( $this->m_nRequestIp === false ) {
158
+ $this->logCritical(
159
+ "Visitor IP address could not be determined so we're by-passing the firewall. Label: %s"
160
+ );
161
+ return true;
162
+ }
163
+
164
+ // Check if the visitor is excluded from the firewall from the outset.
165
+ if ( $this->isVisitorOnWhitelist() ) {
166
+ $this->logInfo(
167
+ sprintf( 'Visitor is whitelisted by IP Address. Label: %s',
168
+ empty( $this->m_sListItemLabel )? 'No label.' : $this->m_sListItemLabel
169
+ )
170
+ );
171
+ return true;
172
+ }
173
+
174
+ //Check if the visitor is excluded from the firewall from the outset.
175
+ if ( $this->isVisitorOnBlacklist() ) {
176
+ $this->m_sFirewallMessage .= ' Your IP is Blacklisted.';
177
+ $this->logWarning(
178
+ sprintf( 'Visitor was blacklisted by IP Address. Label: %s',
179
+ empty( $this->m_sListItemLabel )? 'No label.' : $this->m_sListItemLabel
180
+ )
181
+ );
182
+ return false;
183
+ }
184
+
185
+ /* Removed as of version 1.5.0
186
+ // Checking this comes before all else but after the IP whitelist/blacklist check.
187
+ if ( $this->m_aBlockSettings[ 'block_wplogin_access' ] ) {
188
+ $fIsPermittedVisitor = $this->doPassCheckBlockWpLogin();
189
+ }
190
+ */
191
+
192
+ $this->logInfo( 'Visitor IP was neither whitelisted nor blacklisted. Firewall checking started.' );
193
+
194
+ $fIsPermittedVisitor = true;
195
+
196
+ // Check if the page and its parameters are whitelisted.
197
+ if ( $fIsPermittedVisitor && $this->isPageWhitelisted() ) {
198
+ return true;
199
+ }
200
+
201
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_dir_traversal' ] ) {
202
+ $fIsPermittedVisitor = $this->doPassCheckBlockDirTraversal();
203
+ }
204
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_sql_queries' ] ) {
205
+ $fIsPermittedVisitor = $this->doPassCheckBlockSqlQueries();
206
+ }
207
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_wordpress_terms' ] ) {
208
+ $fIsPermittedVisitor = $this->doPassCheckBlockWordpressTerms();
209
+ }
210
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_field_truncation' ] ) {
211
+ $fIsPermittedVisitor = $this->doPassCheckBlockFieldTruncation();
212
+ }
213
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_exe_file_uploads' ] ) {
214
+ $fIsPermittedVisitor = $this->doPassCheckBlockExeFileUploads();
215
+ }
216
+ if ( $fIsPermittedVisitor && $this->m_aBlockSettings[ 'block_leading_schema' ] ) {
217
+ $fIsPermittedVisitor = $this->doPassCheckBlockLeadingSchema();
218
+ }
219
+
220
+ return $fIsPermittedVisitor;
221
+ }
222
+
223
+ /**
224
+ * This function assumes that isVisitorOnWhitelist() check has been run previously. Meaning the
225
+ * current visitor is NOT on the whitelist and so if the page they're accessing is wp-login then
226
+ * they do not pass this check.
227
+ *
228
+ * If whitelisted IPs is empty, we never block access.
229
+ *
230
+ * @return boolean
231
+ */
232
+ protected function doPassCheckBlockWpLogin() {
233
+
234
+ list( $sRequestPage, $sRequestQuery ) = $this->m_aRequestUriParts;
235
+
236
+ if ( substr_count( $sRequestPage, '/wp-login.php' ) > 0 ) {
237
+
238
+ // We don't block wp-login.php if there are no whitelisted IPs.
239
+ if ( count( $this->m_aOptions[ 'ips_whitelist' ] ) < 1 ) {
240
+ $this->logInfo( 'Requested: Block access to wp-login.php, but this was skipped because whitelisted IPs list was empty.' );
241
+ return true;
242
+ }
243
+
244
+ $this->logWarning( 'Requested: Block access to wp-login.php. Visitor not on IP whitelist and blocked by firewall.' );
245
+ return false;
246
+ }
247
+ return true;
248
+ }
249
+
250
+ protected function doPassCheckBlockDirTraversal() {
251
+ $aTerms = array(
252
+ 'etc/passwd',
253
+ 'proc/self/environ',
254
+ '../'
255
+ );
256
+ $fPass = $this->doPassCheck( $this->m_aPageParamValuesToCheck, $aTerms );
257
+ if ( !$fPass ) {
258
+ $this->logWarning( 'Blocked Directory Traversal.' );
259
+ }
260
+ return $fPass;
261
+ }
262
+
263
+ protected function doPassCheckBlockSqlQueries() {
264
+ $aTerms = array(
265
+ '/concat\s*\(/i',
266
+ '/group_concat/i',
267
+ '/union.*select/i'
268
+ );
269
+ $fPass = $this->doPassCheck( $this->m_aPageParamValuesToCheck, $aTerms, true );
270
+ if ( !$fPass ) {
271
+ $this->logWarning( 'Requested: Block access to wp-login.php, but this was skipped because whitelisted IPs list was empty.' );
272
+ }
273
+ return $fPass;
274
+ }
275
+
276
+ protected function doPassCheckBlockWordpressTerms() {
277
+ $aTerms = array(
278
+ '/wp_/i',
279
+ '/user_login/i',
280
+ '/user_pass/i',
281
+ '/0x[0-9a-f][0-9a-f]/i',
282
+ '/\/\*\*\//'
283
+ );
284
+ $fPass = $this->doPassCheck( $this->m_aPageParamValuesToCheck, $aTerms, true );
285
+ if ( !$fPass ) {
286
+ $this->logWarning( 'Blocked WordPress Terms.' );
287
+ }
288
+ return $fPass;
289
+ }
290
+
291
+ protected function doPassCheckBlockFieldTruncation() {
292
+ $aTerms = array(
293
+ '/\s{49,}/i',
294
+ '/\x00/'
295
+ );
296
+ $fPass = $this->doPassCheck( $this->m_aPageParamValuesToCheck, $aTerms, true );
297
+ if ( !$fPass ) {
298
+ $this->logWarning( 'Blocked Field Truncation.' );
299
+ }
300
+ return $fPass;
301
+ }
302
+
303
+ protected function doPassCheckBlockExeFileUploads() {
304
+ $aTerms = array(
305
+ '/\.dll$/i', '/\.rb$/i', '/\.py$/i', '/\.exe$/i', '/\.php[3-6]?$/i', '/\.pl$/i',
306
+ '/\.perl$/i', '/\.ph[34]$/i', '/\.phl$/i', '/\.phtml$/i', '/\.phtm$/i'
307
+ );
308
+
309
+ if ( isset( $_FILES ) && !empty( $_FILES ) ) {
310
+ $aFileNames = array();
311
+ foreach( $_FILES as $aFile ) {
312
+ if ( !empty( $aFile['name'] ) ) {
313
+ $aFileNames[] = $aFile['name'];
314
+ }
315
+ }
316
+ $fPass = $this->doPassCheck( $aFileNames, $aTerms, true );
317
+ if ( !$fPass ) {
318
+ $this->logWarning( 'Blocked EXE File Uploads.' );
319
+ }
320
+ return $fPass;
321
+ }
322
+ return true;
323
+ }
324
+
325
+ protected function doPassCheckBlockLeadingSchema() {
326
+ $aTerms = array(
327
+ '/^http/i', '/\.shtml$/i'
328
+ );
329
+ $fPass = $this->doPassCheck( $this->m_aPageParamValuesToCheck, $aTerms, true );
330
+ if ( !$fPass ) {
331
+ $this->logWarning( 'Blocked Leading Schema.' );
332
+ }
333
+ return $fPass;
334
+ }
335
+
336
+ /**
337
+ * Returns false when check fails - that is to say, it should be blocked by the firewall.
338
+ *
339
+ * @param array $inaParamValues
340
+ * @param array $inaMatchTerms
341
+ * @param boolean $infRegex
342
+ * @return boolean
343
+ */
344
+ private function doPassCheck( $inaParamValues, $inaMatchTerms, $infRegex = false ) {
345
+
346
+ $fFAIL = false;
347
+ foreach ( $inaParamValues as $mValue ) {
348
+ if ( is_array( $mValue ) ) {
349
+
350
+ // Protection against an infinite loop and we limit depth to 3.
351
+ if ( $this->m_nLoopProtect > 2 ) {
352
+ return true;
353
+ }
354
+ else {
355
+ $this->m_nLoopProtect++;
356
+ }
357
+
358
+ if ( !$this->doPassCheck( $mValue, $inaMatchTerms, $infRegex ) ) {
359
+ return false;
360
+ }
361
+
362
+ $this->m_nLoopProtect--;
363
+ }
364
+ else {
365
+ $mValue = (string) $mValue;
366
+ foreach ( $inaMatchTerms as $sTerm ) {
367
+
368
+ if ( $infRegex && preg_match( $sTerm, $mValue ) ) { //dodgy term pattern found in a parameter value
369
+ $fFAIL = true;
370
+ }
371
+ else if ( strpos( $mValue, $sTerm ) !== false ) { //dodgy term found in a parameter value
372
+ $fFAIL = true;
373
+ }
374
+
375
+ if ( $fFAIL ) {
376
+ $this->m_sFirewallMessage .= " Something in the URL, Form or Cookie data wasn't appropriate.";
377
+ $this->logWarning(
378
+ sprintf( 'Page parameter failed firewall check. The value was %s and the term matched was %s', $mValue, $sTerm )
379
+ );
380
+ return false;
381
+ }
382
+
383
+ }//foreach
384
+ }
385
+ }//foreach
386
+
387
+ return true;
388
+ }
389
+
390
+ public function doPreFirewallBlock() {
391
+
392
+ switch( $this->m_aOptions['block_response'] ) {
393
+ case 'redirect_die':
394
+ $this->logWarning(
395
+ sprintf( 'Firewall Block: Visitor connection was killed with %s', 'die()' )
396
+ );
397
+ break;
398
+ case 'redirect_die_message':
399
+ $this->logWarning(
400
+ sprintf( 'Firewall Block: Visitor connection was killed with %s and message', 'wp_die()' )
401
+ );
402
+ break;
403
+ case 'redirect_home':
404
+ $this->logWarning(
405
+ sprintf( 'Firewall Block: Visitor was sent HOME: %s', home_url() )
406
+ );
407
+ break;
408
+ case 'redirect_404':
409
+ $this->logWarning(
410
+ sprintf( 'Firewall Block: Visitor was sent 404: %s', home_url().'/404' )
411
+ );
412
+ break;
413
+ }
414
+
415
+ if ( $this->m_aOptions['block_send_email'] == 'Y' ) {
416
+ $this->sendBlockEmail();
417
+ }
418
+ }
419
+
420
+ public function doFirewallBlock() {
421
+
422
+ switch( $this->m_aOptions['block_response'] ) {
423
+ case 'redirect_die':
424
+ die();
425
+ case 'redirect_die_message':
426
+ wp_die( $this->m_sFirewallMessage );
427
+ exit();
428
+ break;
429
+ case 'redirect_home':
430
+ header( "Location: ".home_url() );
431
+ exit();
432
+ break;
433
+ case 'redirect_404':
434
+ header( "Location: ".home_url().'/404' );
435
+ exit();
436
+ break;
437
+ }
438
+ }
439
+
440
+ /**
441
+ *
442
+ * @return boolean
443
+ */
444
+ public function isPageWhitelisted() {
445
+ return empty( $this->m_aPageParams );
446
+ }
447
+
448
+ public function filterWhitelistedPagesAndParams() {
449
+
450
+ if ( empty( $this->m_aWhitelistPages ) ) {
451
+ $this->setWhitelistPages();
452
+ if ( empty( $this->m_aWhitelistPages ) ) {
453
+ return false;
454
+ }
455
+ }
456
+ // Check normal whitelisting pages without patterns.
457
+ if ( $this->checkPagesForWhiteListing( $this->m_aWhitelistPages ) ) {
458
+ return true;
459
+ }
460
+ // Check pattern-based whitelisting pages.
461
+ if ( $this->checkPagesForWhiteListing( $this->m_aWhitelistPagesPatterns, true ) ) {
462
+ return true;
463
+ }
464
+ }
465
+
466
+ /**
467
+ * @param array $inaWhitelistPagesParams
468
+ * @param boolean $infUseRegex
469
+ * @return boolean
470
+ */
471
+ protected function checkPagesForWhiteListing( $inaWhitelistPagesParams = array(), $infUseRegex = false ) {
472
+
473
+ if ( !is_array( $this->m_aRequestUriParts ) || count( $this->m_aRequestUriParts ) < 1 ) {
474
+ return true;
475
+ }
476
+ $sRequestPage = $this->m_aRequestUriParts[0];
477
+
478
+ // Now we compare pages in the whitelist with the parts of the request uri. If we get a match, that page is whitelisted
479
+ $aWhitelistPages = array_keys( $inaWhitelistPagesParams );
480
+
481
+ // 1. Is the page in the list of white pages?
482
+ $fPageWhitelisted = false;
483
+ foreach ( $inaWhitelistPagesParams as $sPageName => $aWhitlistedParams ) {
484
+
485
+ if ( $infUseRegex ) {
486
+ if ( preg_match( $sPageName, $sRequestPage ) ) {
487
+ $fPageWhitelisted = true;
488
+ break;
489
+ }
490
+ }
491
+ else {
492
+ if ( preg_match( self::PcreDelimiter. preg_quote( $sPageName, self::PcreDelimiter ).'$'.self::PcreDelimiter, $sRequestPage ) ) {
493
+ $fPageWhitelisted = true;
494
+ break;
495
+ }
496
+ }
497
+ }
498
+
499
+ // There's a list of globally whitelisted parameters (i.e. parameter ignored for all pages)
500
+ if ( array_key_exists( '*', $inaWhitelistPagesParams ) ) {
501
+ foreach ( $inaWhitelistPagesParams['*'] as $sWhitelistParam ) {
502
+ if ( array_key_exists( $sWhitelistParam, $this->m_aPageParams ) ) {
503
+ unset( $this->m_aPageParams[ $sWhitelistParam ] );
504
+ }
505
+ }
506
+ }
507
+
508
+ // There's a list of globally whitelisted parameters (i.e. parameter ignored for all pages)
509
+ if ( $fPageWhitelisted ) {
510
+ // the current page is whitelisted - now check if it has request parameters.
511
+ if ( empty( $aWhitlistedParams ) ) {
512
+ return true; //because it's just plain whitelisted as represented by an empty or unset array
513
+ }
514
+ foreach ( $aWhitlistedParams as $sWhitelistParam ) {
515
+ if ( array_key_exists( $sWhitelistParam, $this->m_aPageParams ) ) {
516
+ unset( $this->m_aPageParams[ $sWhitelistParam ] );
517
+ }
518
+ }
519
+
520
+ // After removing all the whitelisted params, we now check if there are any params left that'll
521
+ // need matched later in the firewall checking. If there are no parameters left, we return true.
522
+ if ( empty( $this->m_aPageParams ) ) {
523
+ return true;
524
+ }
525
+ }
526
+ return false;
527
+ }
528
+
529
+ protected function setRequestUriPageParts() {
530
+
531
+ if ( !isset( $_SERVER['REQUEST_URI'] ) || empty( $_SERVER['REQUEST_URI'] ) ) {
532
+ $this->m_aRequestUriParts = false;
533
+ $this->logWarning( 'Could not parse the details of this page call as $_SERVER[REQUEST_URI] was empty or not defined.' );
534
+ return false;
535
+ }
536
+ $this->m_aRequestUriParts = explode( '?', $_SERVER['REQUEST_URI'] );
537
+ $this->logInfo( sprintf( 'Page Request URI: %s', $_SERVER['REQUEST_URI'] ) );
538
+ return true;
539
+ }
540
+
541
+ protected function setPageParams() {
542
+ $this->m_aPageParams = array_merge( $_GET, $_POST );
543
+ if ( $this->m_aBlockSettings[ 'include_cookie_checks' ] ) {
544
+ $this->m_aPageParams = array_merge( $this->m_aPageParams, $_COOKIE );
545
+ }
546
+
547
+ if ( !empty( $this->m_aPageParams ) ) {
548
+ $this->filterWhitelistedPagesAndParams();
549
+ }
550
+ return true;
551
+ }
552
+
553
+ private function setWhitelistPages() {
554
+
555
+ $aDefaultWlPages = array(
556
+ '/wp-admin/options-general.php' => array(),
557
+ '/wp-admin/post-new.php' => array(),
558
+ '/wp-admin/page-new.php' => array(),
559
+ '/wp-admin/link-add.php' => array(),
560
+ '/wp-admin/media-upload.php' => array(),
561
+ '/wp-admin/post.php' => array(),
562
+ '/wp-admin/page.php' => array(),
563
+ '/wp-admin/admin-ajax.php' => array(),
564
+ '/wp-comments-post.php' => array(
565
+ 'url',
566
+ 'comment'
567
+ ),
568
+ '/wp-login.php' => array(
569
+ 'redirect_to'
570
+ )
571
+ );
572
+
573
+ if ( !is_null($this->m_aCustomWhitelistPageParams) && is_array($this->m_aCustomWhitelistPageParams) ) {
574
+ $this->m_aWhitelistPages = array_merge( $aDefaultWlPages, $this->m_aCustomWhitelistPageParams );
575
+ }
576
+ else {
577
+ $this->m_aWhitelistPages = $aDefaultWlPages;
578
+ }
579
+
580
+ $this->m_aWhitelistPagesPatterns = array(
581
+ self::PcreDelimiter.'\/wp-admin\/\*'.self::PcreDelimiter => array(
582
+ '_wp_original_http_referer',
583
+ '_wp_http_referer'
584
+ ),
585
+ );
586
+ }
587
+
588
+ public function isVisitorOnWhitelist() {
589
+ return $this->isIpOnlist( $this->m_aOptions[ 'ips_whitelist' ], $this->m_nRequestIp, $this->m_sListItemLabel );
590
+ }
591
+
592
+ public function isVisitorOnBlacklist() {
593
+ return $this->isIpOnlist( $this->m_aOptions[ 'ips_blacklist' ], $this->m_nRequestIp, $this->m_sListItemLabel );
594
+ }
595
+
596
+ /**
597
+ * @return boolean
598
+ */
599
+ public function sendBlockEmail() {
600
+
601
+ $sIp = long2ip( $this->m_nRequestIp );
602
+ $aMessage = array(
603
+ 'WordPress Simple Firewall has blocked a visitor to your site.',
604
+ 'Log details for this visitor are below:',
605
+ '- IP Address: '.$sIp,
606
+ );
607
+ foreach( $this->m_aLogMessages as $aLogItem ) {
608
+ list( $sLogType, $sLogMessage ) = $aLogItem;
609
+ $aMessage[] = '- '.$sLogMessage;
610
+ }
611
+
612
+ $sEmailSubject = 'Firewall Block Alert: ' . home_url();
613
+ $aMessage[] = 'You could look up the offending IP Address here: http://ip-lookup.net/?ip='.$sIp;
614
+
615
+ $this->logInfo( 'Firewall block email.' );
616
+ $this->sendEmail( $sEmailSubject, $aMessage );
617
+ }
618
+ }
619
+
620
+ endif;
src/icwp-processor-lockdown.php ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-base-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_LockdownProcessor') ):
21
+
22
+ class ICWP_LockdownProcessor extends ICWP_BaseProcessor_WPSF {
23
+
24
+ /**
25
+ * Resets the object values to be re-used anew
26
+ */
27
+ public function reset() {
28
+ parent::reset();
29
+ }
30
+
31
+ /**
32
+ */
33
+ public function run() {
34
+
35
+ if ( $this->m_aOptions['disable_file_editing'] == 'Y' ) {
36
+ add_filter( 'user_has_cap', array( $this, 'disableFileEditing' ), 0, 3 );
37
+ }
38
+
39
+ if ( false && $this->m_aOptions['action_reset_auth_salts'] == 'Y' ) {
40
+ add_action( 'init', array( $this, 'resetAuthKeysSalts' ), 1 );
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @return array
46
+ */
47
+ public function disableFileEditing( $inaAllCaps, $cap, $inaArgs ) {
48
+
49
+ $aEditCapabilities = array( 'edit_themes', 'edit_plugins', 'edit_files' );
50
+ $sRequestedCapability = $inaArgs[0];
51
+
52
+ if ( !in_array( $sRequestedCapability, $aEditCapabilities ) ) {
53
+ return $inaAllCaps;
54
+ }
55
+ $inaAllCaps[ $sRequestedCapability ] = false;
56
+ return $inaAllCaps;
57
+ }
58
+
59
+ /**
60
+ *
61
+ */
62
+ public function resetAuthKeysSalts() {
63
+
64
+ require_once( dirname(__FILE__).'/icwp-wpfilesystem.php' );
65
+ $oWpFilesystem = new ICWP_WpFilesystem_WPSF();
66
+
67
+ // Get the new Salts
68
+ $sSaltsUrl = 'https://api.wordpress.org/secret-key/1.1/salt/';
69
+ $sSalts = $oWpFilesystem->getUrlContent( $sSaltsUrl );
70
+
71
+ $sWpConfigContent = $oWpFilesystem->getContent_WpConfig();
72
+ if ( is_null( $sWpConfigContent ) ) {
73
+ return;
74
+ }
75
+
76
+ $aKeys = array(
77
+ 'AUTH_KEY',
78
+ 'SECURE_AUTH_KEY',
79
+ 'LOGGED_IN_KEY',
80
+ 'NONCE_KEY',
81
+ 'AUTH_SALT',
82
+ 'SECURE_AUTH_SALT',
83
+ 'LOGGED_IN_SALT',
84
+ 'NONCE_SALT'
85
+ );
86
+
87
+ $aContent = explode( PHP_EOL, $sWpConfigContent );
88
+ $fKeyFound = false;
89
+ $nStartLine = 0;
90
+ foreach( $aContent as $nLineNumber => $sLine ) {
91
+ foreach( $aKeys as $nPosition => $sKey ) {
92
+ if ( strpos( $sLine, $sKey ) === false ) {
93
+ continue;
94
+ }
95
+ if ( $nStartLine == 0 ) {
96
+ $nStartLine = $nLineNumber;
97
+ }
98
+ else {
99
+ unset( $aContent[ $nLineNumber ] );
100
+ }
101
+ $fKeyFound = true;
102
+ }
103
+ }
104
+ $aContent[$nStartLine] = $sSalts;
105
+ $oWpFilesystem->putContent_WpConfig( implode( PHP_EOL, $aContent ) );
106
+ }
107
+ }
108
+
109
+ endif;
src/icwp-processor-logging.php ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-basedb-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_LoggingProcessor') ):
21
+
22
+ class ICWP_LoggingProcessor extends ICWP_BaseDbProcessor_WPSF {
23
+
24
+ const TableName = 'wpsf_log';
25
+ protected $m_sRequestId;
26
+
27
+ public function __construct() {
28
+ parent::__construct( self::TableName );
29
+ $this->createTable();
30
+ }
31
+
32
+ public function reset() {
33
+ parent::reset();
34
+ $this->m_sRequestId = uniqid();
35
+ $this->m_nRequestIp = self::GetVisitorIpAddress();
36
+ }
37
+
38
+ /**
39
+ * @return array - numerical array of all log data entries.
40
+ */
41
+ public function getLogs( $infReverseOrder = false ) {
42
+ $aLogData = $this->selectAllFromTable();
43
+ if ( $infReverseOrder && $aLogData && is_array( $aLogData ) ) {
44
+ $aLogData = array_reverse( $aLogData );
45
+ }
46
+ return $aLogData;
47
+ }
48
+
49
+ /**
50
+ * @return array - numerical array of all log data entries.
51
+ */
52
+ public function writeLog( $inaLogData ) {
53
+
54
+ if ( empty( $inaLogData ) || empty( $inaLogData['messages'] ) ) {
55
+ return;
56
+ }
57
+ $aData = $this->completeLogData( $inaLogData );
58
+ $fSuccess = $this->insertIntoTable( $aData );
59
+ return $fSuccess;
60
+ }
61
+
62
+ protected function completeLogData( $inaLogData ) {
63
+
64
+ if ( !isset( $inaLogData['category'] ) ) {
65
+ $inaLogData['category'] = self::LOG_CATEGORY_DEFAULT;
66
+ }
67
+ if ( !isset( $inaLogData['request_id'] ) ) {
68
+ $inaLogData['request_id'] = $this->m_sRequestId;
69
+ }
70
+ if ( !isset( $inaLogData['ip'] ) ) {
71
+ $inaLogData['ip'] = self::GetVisitorIpAddress( false );
72
+ }
73
+ if ( !isset( $inaLogData['ip_long'] ) ) {
74
+ $inaLogData['ip_long'] = ip2long( $inaLogData['ip'] );
75
+ }
76
+ if ( !isset( $inaLogData['created_at'] ) ) {
77
+ $inaLogData['created_at'] = time();
78
+ }
79
+ return $inaLogData;
80
+ }
81
+
82
+ public function createTable() {
83
+
84
+ // Set up log table
85
+ $sSqlTables = "CREATE TABLE IF NOT EXISTS `%s` (
86
+ `id` int(11) NOT NULL AUTO_INCREMENT,
87
+ `request_id` varchar(255) NOT NULL DEFAULT '',
88
+ `category` int(5) NOT NULL DEFAULT '0',
89
+ `messages` text NOT NULL,
90
+ `ip` varchar(20) NOT NULL DEFAULT '',
91
+ `ip_long` bigint(20) NOT NULL DEFAULT '0',
92
+ `created_at` int(15) NOT NULL DEFAULT '0',
93
+ `deleted_at` int(15) NOT NULL DEFAULT '0',
94
+ PRIMARY KEY (`id`)
95
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
96
+ $sSqlTables = sprintf( $sSqlTables, $this->m_sTableName );
97
+ return $this->doSql( $sSqlTables );
98
+ }
99
+
100
+ public function handleInstallUpgrade( $insCurrentVersion = '' ) {
101
+ if ( version_compare( $insCurrentVersion, '1.3.0', '<' ) ) {
102
+ // full delete of the log and recreate
103
+ $this->recreateTable();
104
+ }
105
+ }
106
+ }
107
+
108
+ endif;
src/icwp-processor-loginprotect.php ADDED
@@ -0,0 +1,686 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
7
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
8
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
9
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
10
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
11
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
12
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
13
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
15
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
+ */
17
+
18
+ require_once( dirname(__FILE__).'/icwp-basedb-processor.php' );
19
+
20
+ if ( !class_exists('ICWP_LoginProtectProcessor') ):
21
+
22
+ class ICWP_LoginProtectProcessor extends ICWP_BaseDbProcessor_WPSF {
23
+
24
+ const TableName = 'login_auth';
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ static protected $sModeFile_LoginThrottled;
30
+
31
+ /**
32
+ * The number of seconds between each authenticated login
33
+ * @var integer
34
+ */
35
+ protected $m_nRequiredLoginInterval;
36
+
37
+ protected $m_nLastLoginTime;
38
+ protected $m_sSecretKey;
39
+
40
+ protected $m_sGaspKey;
41
+
42
+ /**
43
+ * Flag as to whether Two Factor Authentication will be by-pass when sending the verification
44
+ * email fails.
45
+ *
46
+ * @var boolean
47
+ */
48
+ protected $m_fAllowTwoFactorByPass;
49
+
50
+ public function __construct() {
51
+ parent::__construct( self::TableName );
52
+
53
+ $this->m_sGaspKey = uniqid();
54
+ self::$sModeFile_LoginThrottled = dirname( __FILE__ ).'/../mode.login_throttled';
55
+ $this->updateLastLoginThrottleTime( time() );
56
+
57
+ $this->createTable();
58
+ $this->reset();
59
+ }
60
+
61
+ /**
62
+ * Resets the object values to be re-used anew
63
+ */
64
+ public function reset() {
65
+ parent::reset();
66
+ }
67
+
68
+ /**
69
+ * Set the secret key by which authentication is validated.
70
+ *
71
+ * @param string $insSecretKey
72
+ */
73
+ public function setSecretKey( $insSecretKey = '' ) {
74
+ if ( !empty( $insSecretKey ) ) {
75
+ $this->m_sSecretKey = $insSecretKey;
76
+ }
77
+ }
78
+
79
+ /**
80
+ *
81
+ * @param array $inoOptions
82
+ */
83
+ public function setOptions( &$inaOptions ) {
84
+ parent::setOptions( $inaOptions );
85
+ $this->setLogging();
86
+ $this->setLoginCooldownInterval();
87
+ $this->setTwoFactorByPassOnFail();
88
+ }
89
+
90
+ /**
91
+ * @return boolean
92
+ */
93
+ public function getNeedsEmailHandler() {
94
+ if ( isset( $this->m_aOptions['enable_two_factor_auth_by_ip'] ) && $this->m_aOptions['enable_two_factor_auth_by_ip'] == 'Y' ) {
95
+ return true;
96
+ }
97
+ return false;
98
+ }
99
+
100
+ public function setLogging() {
101
+ parent::setLogging( $this->m_aOptions[ 'enable_login_protect_log' ] == 'Y' );
102
+ }
103
+
104
+ /**
105
+ * @param ICWP_OptionsHandler_LoginProtect $inoOptions
106
+ */
107
+ public function run() {
108
+ $aWhitelist = $this->m_aOptions['ips_whitelist'];
109
+ if ( !empty( $aWhitelist ) && $this->isIpOnlist( $aWhitelist, self::GetVisitorIpAddress() ) ) {
110
+ return true;
111
+ }
112
+
113
+ // Add GASP checking to the login form.
114
+ if ( $this->m_aOptions['enable_login_gasp_check'] == 'Y' ) {
115
+ add_action( 'login_form', array( $this, 'printGaspLoginCheck_Action' ) );
116
+ add_filter( 'login_form_middle', array( $this, 'printGaspLoginCheck_Filter' ) );
117
+ add_filter( 'authenticate', array( $this, 'checkLoginForGasp_Filter' ), 9, 3);
118
+ }
119
+
120
+ if ( $this->m_aOptions['login_limit_interval'] > 0 ) {
121
+ // We give it a priority of 10 so that we can jump in before WordPress does its own validation.
122
+ add_filter( 'authenticate', array( $this, 'checkLoginInterval_Filter' ), 10, 3);
123
+ }
124
+
125
+ if ( $this->m_aOptions['enable_two_factor_auth_by_ip'] == 'Y' ) {
126
+ // User has clicked a link in their email to validate their IP address for login.
127
+ if ( isset( $_GET['wpsf-action'] ) && $_GET['wpsf-action'] == 'linkauth' ) {
128
+ $this->validateUserAuthLink();
129
+ }
130
+
131
+ // If their click was successful we give them a lovely message
132
+ if ( isset( $_GET['wpsfipverified']) ) {
133
+ add_filter( 'login_message', array( $this, 'displayVerifiedUserMessage_Filter' ) );
134
+ }
135
+
136
+ // Check the current logged-in user every page load.
137
+ add_action( 'init', array( $this, 'checkCurrentUserAuth_Action' ) );
138
+
139
+ // At this stage (30,3) WordPress has already authenticated the user. So if the login
140
+ // is valid, the filter will have a valid WP_User object passed to it.
141
+ add_filter( 'authenticate', array( $this, 'checkUserAuthLogin_Filter' ), 30, 3);
142
+ }
143
+ }
144
+
145
+ public function printGaspLoginCheck_Action() {
146
+ echo $this->getGaspLoginHtml();
147
+ }
148
+
149
+ public function printGaspLoginCheck_Filter() {
150
+ return $this->getGaspLoginHtml();
151
+ }
152
+
153
+ public function checkLoginForGasp_Filter( $inoUser, $insUsername, $insPassword ) {
154
+
155
+ if ( empty( $insUsername ) || is_wp_error( $inoUser ) ) {
156
+ return $inoUser;
157
+ }
158
+ if ( $this->doGaspChecks( $insUsername ) ) {
159
+ return $inoUser;
160
+ }
161
+ return null;
162
+ }
163
+
164
+ /**
165
+ * Checks whether the current user that is logged-in is authenticated by IP address.
166
+ *
167
+ * If the user is not found to be valid, they're logged out.
168
+ *
169
+ * Should be hooked to 'init' so we have is_user_logged_in()
170
+ */
171
+ public function checkCurrentUserAuth_Action() {
172
+ if ( is_user_logged_in() ) {
173
+ $this->verifyCurrentUser();
174
+ }
175
+ }
176
+
177
+ public function displayVerifiedUserMessage_Filter( $insMessage ) {
178
+ $sStyles .= 'background-color: #FAFFE8; border: 1px solid #DDDDDD; margin: 8px 0 10px 8px; padding: 16px;';
179
+ $insMessage .= '<h3 style="'.$sStyles.'">You successfully verified your IP address - you may now login.</h3>';
180
+ return $insMessage;
181
+ }
182
+
183
+ /**
184
+ * Should return false when logging is disabled.
185
+ *
186
+ * @return false|array - false when logging is disabled, array with log data otherwise
187
+ * @see ICWP_BaseProcessor_WPSF::getLogData()
188
+ */
189
+ public function flushLogData() {
190
+
191
+ if ( !$this->m_fLoggingEnabled || empty( $this->m_aLogMessages ) ) {
192
+ return false;
193
+ }
194
+
195
+ $this->m_aLog = array(
196
+ 'category' => self::LOG_CATEGORY_LOGINPROTECT,
197
+ 'messages' => serialize( $this->m_aLogMessages )
198
+ );
199
+ $this->resetLog();
200
+ return $this->m_aLog;
201
+ }
202
+
203
+ /**
204
+ * Checks the link details to ensure all is valid before authorizing the user.
205
+ *
206
+ * @return boolean
207
+ */
208
+ public function validateUserAuthLink() {
209
+ // wpsfkey=%s&wpsf-action=%s&username=%s&uniqueid
210
+
211
+ if ( !isset( $_GET['wpsfkey'] ) || $_GET['wpsfkey'] !== $this->m_sSecretKey ) {
212
+ return false;
213
+ }
214
+ if ( empty( $_GET['username'] ) || empty( $_GET['uniqueid'] ) ) {
215
+ return false;
216
+ }
217
+
218
+ $aWhere = array(
219
+ 'unique_id' => $_GET['uniqueid'],
220
+ 'wp_username' => $_GET['username']
221
+ );
222
+
223
+ if ( $this->loginAuthMakeActive( $aWhere ) ) {
224
+ $this->redirectToLogin( '?wpsfipverified=1' );
225
+ }
226
+ else {
227
+ header( "Location: ".home_url() );
228
+ }
229
+ }
230
+
231
+ public function redirectToLogin( $sParams = '' ) {
232
+ header( "Location: ".site_url().'/wp-login.php'.$sParams );
233
+ }
234
+
235
+ // WordPress Hooks and Filters:
236
+
237
+ /**
238
+ * Should be a filter added to WordPress's "authenticate" filter, but before WordPress performs
239
+ * it's own authentication (theirs is priority 30, so we could go in at around 20).
240
+ *
241
+ * @param null|WP_User|WP_Error $inoUser
242
+ * @param string $insUsername
243
+ * @param string $insPassword
244
+ * @return unknown|WP_Error
245
+ */
246
+ public function checkLoginInterval_Filter( $inoUser, $insUsername, $insPassword ) {
247
+
248
+ // No login attempt was made.
249
+ if ( empty( $insUsername ) ) {
250
+ return $inoUser;
251
+ }
252
+
253
+ // Is there an interval set?
254
+ $nRequiredLoginInterval = $this->m_nRequiredLoginInterval;
255
+ if ( $nRequiredLoginInterval === false || $nRequiredLoginInterval == 0 ) {
256
+ return $inoUser;
257
+ }
258
+
259
+ // Get the last login time (and update it also for the next time)
260
+ $sNow = time();
261
+ $this->m_nLastLoginTime = $this->getLastLoginTime();
262
+
263
+ if ( empty( $this->m_nLastLoginTime ) || $this->m_nLastLoginTime < 0 ) {
264
+ $this->updateLastLoginThrottleTime( $sNow );
265
+ }
266
+
267
+ // If we're outside the interval, let the login process proceed as per normal and
268
+ // update our last login time.
269
+ $nLoginInterval = $sNow - $this->m_nLastLoginTime;
270
+ if ( $nLoginInterval > $nRequiredLoginInterval ) {
271
+ $this->updateLastLoginThrottleTime( $sNow );
272
+ return $inoUser;
273
+ }
274
+
275
+ // At this point someone has attempted to login within the previous login wait interval
276
+ // So we remove WordPress's authentication filter and our own user check authentication
277
+ // And finally return a WP_Error which will be reflected back to the user.
278
+ remove_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 ); // wp-includes/user.php
279
+ remove_filter( 'authenticate', array( $this, 'checkUserAuthLogin_Filter' ), 30, 3);
280
+
281
+ $sErrorString = sprintf( "Sorry, you must wait %s seconds before attempting to login again.", ($nRequiredLoginInterval - $nLoginInterval ) );
282
+ $oError = new WP_Error( 'wpsf_logininterval', $sErrorString );
283
+ return $oError;
284
+ }
285
+
286
+ protected function getLastLoginTime() {
287
+
288
+ // Check that there is a login throttle file. If it exists and its modified time is greater than the
289
+ // current $this->m_nLastLoginTime it suggests another process has touched the file and updated it
290
+ // concurrently. So, we update our $this->m_nEmailThrottleTime accordingly.
291
+ if ( is_file( self::$sModeFile_LoginThrottled ) ) {
292
+ $nModifiedTime = filemtime( self::$sModeFile_LoginThrottled );
293
+ if ( $nModifiedTime > $this->m_nLastLoginTime ) {
294
+ $this->m_nLastLoginTime = $nModifiedTime;
295
+ }
296
+ }
297
+ return $this->m_nLastLoginTime;
298
+ }
299
+
300
+ public function updateLastLoginThrottleTime( $innLastLoginTime ) {
301
+ $this->m_nLastLoginTime = $innLastLoginTime;
302
+ if ( function_exists('touch') ) {
303
+ @touch( self::$sModeFile_LoginThrottled, $innLastLoginTime );
304
+ }
305
+ $this->setNeedSave();
306
+ }
307
+
308
+ /**
309
+ * If $inoUser is a valid WP_User object, then the user logged in correctly.
310
+ *
311
+ * The flow is as follows:
312
+ * 0. If username is empty, there was no login attempt.
313
+ * 1. First we determine whether the user's login credentials were valid according to WordPress ($fUserLoginSuccess)
314
+ * 2. Then we ask our 2-factor processor whether the current IP address + username combination is authenticated.
315
+ * a) if yes, we return the WP_User object and login proceeds as per usual.
316
+ * b) if no, we return null, which will send the message back to the user that the login details were invalid.
317
+ * 3. If however the user's IP address + username combination is not authenticated, we react differently. We do not want
318
+ * to give away whether a login was successful, or even the login username details exist. So:
319
+ * a) if the login was a success we add a pending record to the authentication DB for this username+IP address combination and send the appropriate verification email
320
+ * b) then, we give back a message saying that if the login was successful, they would have received a verification email. In this way we give nothing away.
321
+ * c) note at this stage, if the username was empty, we give back nothing (this happens when wp-login.php is loaded as normal.
322
+ *
323
+ * @param WP_User|string $inmUser - the docs say the first parameter a string, WP actually gives a WP_User object (or null)
324
+ * @param string $insUsername
325
+ * @param string $insPassword
326
+ * @return WP_Error|WP_User|null - WP_User when the login success AND the IP is authenticated. null when login not successful but IP is valid. WP_Error otherwise.
327
+ */
328
+ public function checkUserAuthLogin_Filter( $inoUser, $insUsername, $insPassword ) {
329
+
330
+ if ( empty( $insUsername ) ) {
331
+ return $inoUser;
332
+ }
333
+
334
+ $fUserLoginSuccess = is_object( $inoUser ) && ( $inoUser instanceof WP_User );
335
+
336
+ if ( is_wp_error( $inoUser ) ) {
337
+ $aCodes = $inoUser->get_error_codes();
338
+ if ( in_array( 'wpsf_logininterval', $aCodes ) ) {
339
+ return $inoUser;
340
+ }
341
+ }
342
+ else if ( $fUserLoginSuccess ) {
343
+
344
+ $aData = array( 'wp_username' => $insUsername );
345
+ if ( $this->isUserVerified( $aData ) ) {
346
+ return $inoUser;
347
+ }
348
+ else {
349
+ // Create a new 2-factor auth pending entry
350
+ $aNewAuthData = $this->loginAuthAddPending( array( 'wp_username' => $inoUser->user_login ) );
351
+
352
+ // Now send email with authentication link for user.
353
+ if ( is_array( $aNewAuthData ) ) {
354
+ $fEmailSuccess = $this->sendEmailTwoFactorVerify( $inoUser, $aNewAuthData['ip'], $aNewAuthData['unique_id'] );
355
+
356
+ // Failure to send email - log them in.
357
+ if ( !$fEmailSuccess && $this->getTwoFactorByPassOnFail() ) {
358
+ $this->loginAuthMakeActive( $aNewAuthData );
359
+ return $inoUser;
360
+ }
361
+ }
362
+ }
363
+ }
364
+
365
+ $sErrorString = "Login is protected by 2-factor authentication. If your login details were correct, you would have received an email to verify this IP address.";
366
+ return new WP_Error( 'wpsf_loginauth', $sErrorString );
367
+ }
368
+
369
+ public function getGaspLoginHtml() {
370
+
371
+ $sLabel = "I'm a human.";
372
+ $sAlert = "Please check the box to show us you're a human.";
373
+
374
+ $sUniqElem = 'icwp_wpsf_login_p'.uniqid();
375
+
376
+ $sStyles = '
377
+ <style>
378
+ #'.$sUniqElem.' {
379
+ clear:both;
380
+ border: 1px solid #888;
381
+ padding: 6px 8px 4px 10px;
382
+ margin: 0 0px 12px !important;
383
+ border-radius: 2px;
384
+ background-color: #f9f9f9;
385
+ }
386
+ #'.$sUniqElem.' input {
387
+ margin-right: 5px;
388
+ }
389
+ </style>
390
+ ';
391
+
392
+ $sHtml =
393
+ $sStyles.
394
+ '<p id="'.$sUniqElem.'"></p>
395
+ <script type="text/javascript">
396
+ var icwp_wpsf_login_p = document.getElementById("'.$sUniqElem.'");
397
+ var icwp_wpsf_login_cb = document.createElement("input");
398
+ var icwp_wpsf_login_text = document.createTextNode(" '.$sLabel.'");
399
+ icwp_wpsf_login_cb.type = "checkbox";
400
+ icwp_wpsf_login_cb.id = "'.$this->getGaspCheckboxName().'";
401
+ icwp_wpsf_login_cb.name = "'.$this->getGaspCheckboxName().'";
402
+ icwp_wpsf_login_p.appendChild( icwp_wpsf_login_cb );
403
+ icwp_wpsf_login_p.appendChild( icwp_wpsf_login_text );
404
+ var frm = icwp_wpsf_login_cb.form;
405
+ frm.onsubmit = icwp_wpsf_login_it;
406
+ function icwp_wpsf_login_it(){
407
+ if(icwp_wpsf_login_cb.checked != true){
408
+ alert("'.$sAlert.'");
409
+ return false;
410
+ }
411
+ return true;
412
+ }
413
+ </script>
414
+ <noscript>You MUST enable Javascript to be able to login</noscript>
415
+ <input type="hidden" id="icwp_wpsf_login_email" name="icwp_wpsf_login_email" value="" />
416
+ ';
417
+
418
+ return $sHtml;
419
+ }
420
+
421
+ public function getGaspCheckboxName() {
422
+ if ( empty( $this->m_sGaspKey ) ) {
423
+ $this->m_sGaspKey = uniqid();
424
+ }
425
+ return "icwp_wpsf_$this->m_sGaspKey";
426
+ }
427
+
428
+ public function doGaspChecks( $insUsername ) {
429
+ if ( !isset( $_POST[ $this->getGaspCheckboxName() ] ) ) {
430
+ $this->logWarning(
431
+ sprintf( 'User "%s" attempted to login but GASP checkbox was not present. Bot Perhaps? IP Address: "%s".', $insUsername, long2ip($this->m_nRequestIp) )
432
+ );
433
+ wp_die( "You must check that box to say you're not a bot." );
434
+ return false;
435
+ }
436
+ else if ( isset( $_POST['icwp_wpsf_login_email'] ) && $_POST['icwp_wpsf_login_email'] !== '' ){
437
+ $this->logWarning(
438
+ sprintf( 'User "%s" attempted to login but they were caught by the GASP honey pot. Bot Perhaps? IP Address: "%s".', $insUsername, long2ip($this->m_nRequestIp) )
439
+ );
440
+ wp_die( 'You smell like a bot.' );
441
+ return false;
442
+ }
443
+ return true;
444
+ }
445
+
446
+ public function setTwoFactorByPassOnFail() {
447
+ $this->m_fAllowTwoFactorByPass = $this->m_aOptions[ 'enable_two_factor_bypass_on_email_fail' ] == 'Y';
448
+ }
449
+
450
+ public function getTwoFactorByPassOnFail() {
451
+ if ( !isset( $this->m_fAllowTwoFactorByPass ) ) {
452
+ $this->m_fAllowTwoFactorByPass = false;
453
+ }
454
+ return $this->m_fAllowTwoFactorByPass;
455
+ }
456
+
457
+ public function setLoginCooldownInterval() {
458
+ $nInterval = intval( $this->m_aOptions[ 'login_limit_interval' ] );
459
+ $this->m_nRequiredLoginInterval = ( $nInterval < 0 )? 0 : $nInterval;
460
+ }
461
+
462
+ /**
463
+ * @param array $inaData
464
+ * @return boolean
465
+ */
466
+ public function loginAuthAddPending( $inaData ) {
467
+
468
+ $aChecks = array( 'wp_username' );
469
+ if ( !$this->validateParameters( $inaData, $aChecks) ) {
470
+ return false;
471
+ }
472
+
473
+ $sNow = time();
474
+
475
+ // First set any other pending entries for the given user to be deleted.
476
+ $aOldData = array(
477
+ 'deleted_at' => $sNow,
478
+ 'expired_at' => $sNow,
479
+ );
480
+ $aOldWhere = array(
481
+ 'pending' => 1,
482
+ 'deleted_at' => 0,
483
+ 'wp_username' => $inaData[ 'wp_username' ]
484
+ );
485
+ $this->updateRowsFromTable( $aOldData, $aOldWhere );
486
+
487
+ // Now add new pending entry
488
+ $inaData[ 'unique_id' ] = uniqid();
489
+ $inaData[ 'ip_long' ] = $this->m_nRequestIp;
490
+ $inaData[ 'ip' ] = long2ip( $this->m_nRequestIp );
491
+ $inaData[ 'pending' ] = 1;
492
+ $inaData[ 'created_at' ] = time();
493
+
494
+ $mResult = $this->insertIntoTable( $inaData );
495
+ if ( $mResult ) {
496
+ $this->logInfo(
497
+ sprintf( 'User "%s" created a pending Two-Factor Authentication for IP Address "%s".', $inaData[ 'wp_username' ], $inaData[ 'ip' ] )
498
+ );
499
+ $mResult = $inaData;
500
+ }
501
+ return $mResult;
502
+ }
503
+
504
+ /**
505
+ * Given a unique Id and a corresponding WordPress username, will update the authentication table so that it is active (pending=0).
506
+ *
507
+ * @param array $inaWhere - unique_id, wp_username
508
+ * @return boolean
509
+ */
510
+ public function loginAuthMakeActive( $inaWhere ) {
511
+
512
+ $aChecks = array( 'unique_id', 'wp_username' );
513
+ if ( !$this->validateParameters( $inaWhere, $aChecks ) ) {
514
+ return false;
515
+ }
516
+
517
+ $sNow = time();
518
+
519
+ // First set any active, non-pending entries for the given user to be deleted.
520
+ $sQuery = "
521
+ UPDATE `%s`
522
+ SET `deleted_at` = '%s',
523
+ `expired_at` = '%s'
524
+ WHERE
525
+ `wp_username` = '%s'
526
+ AND `deleted_at` = '0'
527
+ AND `pending` = '0'
528
+ ";
529
+ $sQuery = sprintf( $sQuery,
530
+ $this->m_sTableName,
531
+ $sNow,
532
+ $sNow,
533
+ $inaWhere['wp_username']
534
+ );
535
+ $this->doSql( $sQuery );
536
+
537
+ $inaWhere['pending'] = 1;
538
+ $inaWhere['deleted_at'] = 0;
539
+
540
+ // Now activate the new one.
541
+ $mResult = $this->updateRowsFromTable( array( 'pending' => 0 ), $inaWhere );
542
+ if ( $mResult ) {
543
+ $this->logInfo(
544
+ sprintf( 'User "%s" has verified their IP Address using Two-Factor Authentication.', $inaWhere[ 'wp_username' ] )
545
+ );
546
+ }
547
+ return $mResult;
548
+ }
549
+
550
+ /**
551
+ * Checks whether a given user is authenticated.
552
+ *
553
+ * @param array $inaWhere
554
+ * @return boolean
555
+ */
556
+ public function isUserVerified( $inaWhere ) {
557
+
558
+ $aChecks = array( 'wp_username' );
559
+ if ( !$this->validateParameters( $inaWhere, $aChecks ) ) {
560
+ return false;
561
+ }
562
+
563
+ $sQuery = "
564
+ SELECT *
565
+ FROM `%s`
566
+ WHERE
567
+ `wp_username` = '%s'
568
+ AND `ip_long` = '%s'
569
+ AND `pending` = '0'
570
+ AND `deleted_at` = '0'
571
+ AND `expired_at` = '0'
572
+ ";
573
+ $sQuery = sprintf( $sQuery,
574
+ $this->m_sTableName,
575
+ $inaWhere['wp_username'],
576
+ $this->m_nRequestIp
577
+ );
578
+
579
+ $mResult = $this->selectCustomFromTable( $sQuery );
580
+ if ( is_array( $mResult ) && count( $mResult ) == 1 ) {
581
+ return true;
582
+ }
583
+ else {
584
+ $this->logWarning(
585
+ sprintf( 'User "%s" was found to be un-verified at this given IP Address "%s".', $inaWhere[ 'wp_username' ], long2ip( $this->m_nRequestIp ) )
586
+ );
587
+ return false;
588
+ }
589
+ }
590
+
591
+ public function verifyCurrentUser() {
592
+ $oUser = wp_get_current_user();
593
+ if ( is_object( $oUser ) && $oUser instanceof WP_User ) {
594
+
595
+ $aData = array( 'wp_username' => $oUser->user_login );
596
+ if ( !$this->isUserVerified( $aData ) ) {
597
+ $this->logWarning(
598
+ sprintf( 'User "%s" was logged out.', $oUser->user_login )
599
+ );
600
+ wp_logout();
601
+ $this->redirectToLogin();
602
+ }
603
+ }
604
+ }
605
+
606
+ /**
607
+ * Given the necessary components, creates the 2-factor verification link for giving to the user.
608
+ *
609
+ * @param string $insKey
610
+ * @param string $insUser
611
+ * @param string $insUniqueId
612
+ * @return string
613
+ */
614
+ public function getTwoFactorVerifyLink( $insKey, $insUser, $insUniqueId ) {
615
+ $sSiteUrl = home_url() . '?wpsfkey=%s&wpsf-action=%s&username=%s&uniqueid=%s';
616
+ $sAction = 'linkauth';
617
+ return sprintf( $sSiteUrl, $insKey, $sAction, $insUser, $insUniqueId );
618
+ }
619
+
620
+ /**
621
+ * @param string $sEmail
622
+ * @param string $insIpAddress
623
+ * @param string $insAuthLink
624
+ */
625
+ public function sendEmailTwoFactorVerify( WP_User $inoUser, $insIpAddress, $insUniqueId ) {
626
+
627
+ $sEmail = $inoUser->user_email;
628
+ $sAuthLink = $this->getTwoFactorVerifyLink( $this->m_sSecretKey, $inoUser->user_login, $insUniqueId );
629
+
630
+ $aMessage = array(
631
+ 'You, or someone pretending to be you, just attempted to login into your WordPress site.',
632
+ 'The IP Address from which they tried to login is not currently valid.',
633
+ 'To validate this address, click the following link, and then login again.',
634
+ 'IP Address: '. $insIpAddress,
635
+ 'Authentication Link: '. $sAuthLink
636
+ );
637
+ $sEmailSubject = 'Two-Factor Login Verification: ' . home_url();
638
+
639
+ $fResult = $this->sendEmailTo( $sEmail, $sEmailSubject, $aMessage );
640
+ if ( $fResult ) {
641
+ $this->logInfo(
642
+ sprintf( 'User "%s" was sent an email to verify their Two-Factor Login for IP Address "%s".', $inoUser->user_login, $insIpAddress )
643
+ );
644
+ }
645
+ else {
646
+ $this->logCritical(
647
+ sprintf( 'Tried to send User "%s" email to verify their Two-Factor Login for IP Address "%s", but email sending failed.', $inoUser->user_login, $insIpAddress )
648
+ );
649
+ }
650
+ return $fResult;
651
+ }
652
+
653
+ public function createTable() {
654
+
655
+ // Set up login processor table
656
+ $sSqlTables = "CREATE TABLE IF NOT EXISTS `%s` (
657
+ `id` int(11) NOT NULL AUTO_INCREMENT,
658
+ `unique_id` varchar(20) NOT NULL DEFAULT '',
659
+ `wp_username` varchar(255) NOT NULL DEFAULT '',
660
+ `ip` varchar(20) NOT NULL DEFAULT '',
661
+ `ip_long` bigint(20) NOT NULL DEFAULT '0',
662
+ `pending` int(1) NOT NULL DEFAULT '0',
663
+ `created_at` int(15) NOT NULL DEFAULT '0',
664
+ `deleted_at` int(15) NOT NULL DEFAULT '0',
665
+ `expired_at` int(15) NOT NULL DEFAULT '0',
666
+ PRIMARY KEY (`id`)
667
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8;";
668
+ $sSqlTables = sprintf( $sSqlTables, $this->m_sTableName );
669
+ $mResult = $this->doSql( $sSqlTables );
670
+ }
671
+
672
+ /**
673
+ * Assumes that unique_id AND wp_username have been set correctly in the data array (no checking done).
674
+ *
675
+ * @param array $inaData
676
+ * @return array
677
+ */
678
+ protected function getLoginAuthData( $inaData ) {
679
+
680
+ $sQuery = "SELECT * FROM %s WHERE `unique_id` = `%s` AND `wp_username` = %s";
681
+ $sQuery = sprintf( $sQuery, $this->m_sTableName, $inaData['unique_id'], $inaData['wp_username'] );
682
+ return $this->selectRowFromTable( $sQuery );
683
+ }
684
+ }
685
+
686
+ endif;
src/icwp-wpfilesystem.php ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * Version: 2013-09-13_A
7
+ *
8
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
9
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
12
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
14
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
15
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
16
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
17
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
+ */
19
+
20
+ if ( !class_exists('ICWP_WpFilesystem_WPSF') ):
21
+
22
+ class ICWP_WpFilesystem_WPSF {
23
+
24
+ /**
25
+ * @var object
26
+ */
27
+ protected $m_oWpFilesystem = null;
28
+
29
+ /**
30
+ * @var string
31
+ */
32
+ protected $m_sWpConfigPath = null;
33
+
34
+ public function __construct() {
35
+ $this->initFileSystem();
36
+ $this->setWpConfigPath();
37
+ }
38
+
39
+ protected function setWpConfigPath() {
40
+ $this->m_sWpConfigPath = ABSPATH.'wp-config.php';
41
+ if ( !is_file($this->m_sWpConfigPath) ) {
42
+ $this->m_sWpConfigPath = ABSPATH.'..'.ICWP_DS.'wp-config.php';
43
+ if ( !is_file($this->m_sWpConfigPath) ) {
44
+ $this->m_sWpConfigPath = false;
45
+ }
46
+ }
47
+ }
48
+
49
+ protected function initFileSystem() {
50
+ if ( is_null( $this->m_oWpFilesystem ) ) {
51
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
52
+ WP_Filesystem();
53
+ global $wp_filesystem;
54
+ if ( isset( $wp_filesystem ) && is_object( $wp_filesystem ) ) {
55
+ $this->m_oWpFilesystem = &$wp_filesystem;
56
+ }
57
+ else {
58
+ $this->m_oWpFilesystem = false;
59
+ }
60
+ }
61
+ }
62
+
63
+ public function getContent_WpConfig() {
64
+ return $this->getFileContent( $this->m_sWpConfigPath );
65
+ }
66
+
67
+ public function putContent_WpConfig( $insContent ) {
68
+ return $this->putFileContent( $this->m_sWpConfigPath, $insContent );
69
+ }
70
+
71
+
72
+ /**
73
+ * @return string
74
+ */
75
+ public function getWpConfigPath() {
76
+ return $this->m_sWpConfigPath;
77
+ }
78
+
79
+ public function getUrl( $insUrl ) {
80
+ $mResult = wp_remote_get( $insUrl );
81
+ if ( is_wp_error( $mResult ) ) {
82
+ return false;
83
+ }
84
+ if ( !isset( $mResult['response']['code'] ) || $mResult['response']['code'] != 200 ) {
85
+ return false;
86
+ }
87
+ return $mResult;
88
+ }
89
+
90
+ public function getUrlContent( $insUrl ) {
91
+
92
+ $aResponse = $this->getUrl( $insUrl );
93
+ if ( !$aResponse ) {
94
+ return false;
95
+ }
96
+ return $aResponse['body'];
97
+ }
98
+
99
+ public function getCanWpRemoteGet() {
100
+ $aUrlsToTest = array(
101
+ 'https://www.microsoft.com',
102
+ 'https://www.google.com',
103
+ 'https://www.facebook.com'
104
+ );
105
+ foreach( $aUrlsToTest as $sUrl ) {
106
+ if ( $this->getUrl( $sUrl ) !== false ) {
107
+ return true;
108
+ }
109
+ }
110
+ return false;
111
+ }
112
+
113
+ public function getCanDiskWrite() {
114
+ $sFilePath = dirname( __FILE__ ).'/testfile.'.rand().'txt';
115
+ $sContents = "Testing icwp file read and write.";
116
+
117
+ // Write, read, verify, delete.
118
+ if ( $this->putFileContent( $sFilePath, $sContents ) ) {
119
+ $sFileContents = $this->getFileContent( $sFilePath );
120
+ if ( !is_null( $sFileContents ) && $sFileContents === $sContents ) {
121
+ return $this->deleteFile( $sFilePath );
122
+ }
123
+ }
124
+ return false;
125
+ }
126
+
127
+ /**
128
+ * @param string $insFilePath
129
+ * @return NULL|boolean
130
+ */
131
+ public function getCanReadWriteFile( $insFilePath ) {
132
+ if ( !file_exists( $insFilePath ) ) {
133
+ return null;
134
+ }
135
+
136
+ $nFileSize = filesize( $insFilePath );
137
+ if ( $nFileSize === 0 ) {
138
+ return null;
139
+ }
140
+
141
+ $sFileContent = $this->getFileContent( $insFilePath );
142
+ if ( empty( $sFileContent ) ) {
143
+ return false; //can't even read the file!
144
+ }
145
+ return $this->putFileContent( $insFilePath, $sFileContent );
146
+ }
147
+
148
+ /**
149
+ * @return string|null
150
+ */
151
+ public function getFileContent( $insFilePath ) {
152
+ if ( !file_exists( $insFilePath ) ) {
153
+ return null;
154
+ }
155
+ if ( $this->m_oWpFilesystem ) {
156
+ return $this->m_oWpFilesystem->get_contents( $insFilePath );
157
+ }
158
+ else if ( function_exists('file_get_contents') ) {
159
+ return file_get_contents( $insFilePath );
160
+ }
161
+ return null;
162
+ }
163
+
164
+ /**
165
+ * @return boolean
166
+ */
167
+ public function putFileContent( $insFilePath, $insContents ) {
168
+ if ( $this->m_oWpFilesystem ) {
169
+ return $this->m_oWpFilesystem->put_contents( $insFilePath, $insContents, FS_CHMOD_FILE );
170
+ }
171
+ else if ( file_put_contents( $insFilePath, $insContents ) === false ) {
172
+ return false;
173
+ }
174
+ return true;
175
+ }
176
+
177
+ /**
178
+ * @return boolean
179
+ */
180
+ public function deleteFile( $insFilePath ) {
181
+ if ( !file_exists( $insFilePath ) ) {
182
+ return null;
183
+ }
184
+ if ( $this->m_oWpFilesystem ) {
185
+ return $this->m_oWpFilesystem->delete( $insFilePath );
186
+ }
187
+ else {
188
+ return unlink( $insFilePath );
189
+ }
190
+ }
191
+ }
192
+
193
+ endif;
src/icwp-wpfunctions.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2013 iControlWP <support@icontrolwp.com>
4
+ * All rights reserved.
5
+ *
6
+ * Version: 2013-08-14_A
7
+ *
8
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
9
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
12
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
14
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
15
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
16
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
17
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
18
+ */
19
+
20
+ if ( !class_exists('ICWP_WpFunctions_WPSF') ):
21
+
22
+ class ICWP_WpFunctions_WPSF {
23
+
24
+ /**
25
+ * @var string
26
+ */
27
+ protected $m_sWpVersion;
28
+
29
+ public function __construct() {}
30
+
31
+ /**
32
+ * @param string $insPluginFile
33
+ * @return boolean|stdClass
34
+ */
35
+ public function getIsPluginUpdateAvailable( $insPluginFile ) {
36
+ $aUpdates = $this->getWordpressUpdates();
37
+ if ( empty( $aUpdates ) ) {
38
+ return false;
39
+ }
40
+ if ( isset( $aUpdates[ $insPluginFile ] ) ) {
41
+ return $aUpdates[ $insPluginFile ];
42
+ }
43
+ return false;
44
+ }
45
+
46
+ public function getPluginUpgradeLink( $insPluginFile ) {
47
+ $sUrl = self_admin_url( 'update.php' ) ;
48
+ $aQueryArgs = array(
49
+ 'action' => 'upgrade-plugin',
50
+ 'plugin' => urlencode( $insPluginFile ),
51
+ '_wpnonce' => wp_create_nonce( 'upgrade-plugin_' . $insPluginFile )
52
+ );
53
+ return add_query_arg( $aQueryArgs, $sUrl );
54
+ }
55
+
56
+ public function getWordpressUpdates() {
57
+ $oCurrent = $this->getTransient( 'update_plugins' );
58
+ return $oCurrent->response;
59
+ }
60
+
61
+ /**
62
+ * The full plugin file to be upgraded.
63
+ *
64
+ * @param string $insPluginFile
65
+ * @return boolean
66
+ */
67
+ public function doPluginUpgrade( $insPluginFile ) {
68
+
69
+ if ( !$this->getIsPluginUpdateAvailable($insPluginFile)
70
+ || ( isset( $GLOBALS['pagenow'] ) && $GLOBALS['pagenow'] == 'update.php' ) ) {
71
+ return true;
72
+ }
73
+ $sUrl = $this->getPluginUpgradeLink( $insPluginFile );
74
+ wp_redirect( $sUrl );
75
+ exit();
76
+ }
77
+ /**
78
+ * @param string $insKey
79
+ * @return object
80
+ */
81
+ protected function getTransient( $insKey ) {
82
+
83
+ // TODO: Handle multisite
84
+
85
+ if ( version_compare( $this->getWordPressVersion(), '2.7.9', '<=' ) ) {
86
+ return get_option( $insKey );
87
+ }
88
+
89
+ if ( function_exists( 'get_site_transient' ) ) {
90
+ return get_site_transient( $insKey );
91
+ }
92
+
93
+ if ( version_compare( $this->getWordPressVersion(), '2.9.9', '<=' ) ) {
94
+ return apply_filters( 'transient_'.$insKey, get_option( '_transient_'.$insKey ) );
95
+ }
96
+
97
+ return apply_filters( 'site_transient_'.$insKey, get_option( '_site_transient_'.$insKey ) );
98
+ }
99
+
100
+ /**
101
+ * @return string
102
+ */
103
+ public function getWordPressVersion() {
104
+ global $wp_version;
105
+
106
+ if ( empty( $this->m_sWpVersion ) ) {
107
+ $sVersionFile = ABSPATH.WPINC.'/version.php';
108
+ $sVersionContents = file_get_contents( $sVersionFile );
109
+
110
+ if ( preg_match( '/wp_version\s=\s\'([^(\'|")]+)\'/i', $sVersionContents, $aMatches ) ) {
111
+ $this->m_sWpVersion = $aMatches[1];
112
+ }
113
+ }
114
+ return $this->m_sWpVersion;
115
+ }
116
+ }
117
+
118
+ endif;
views/icwp_options_helper.php CHANGED
@@ -1,5 +1,19 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  function printAllPluginOptionsForm( $inaAllPluginOptions, $insVarPrefix = '', $iOptionsPerRow = 1 ) {
4
 
5
  if ( empty($inaAllPluginOptions) ) {
@@ -96,7 +110,8 @@ function getPluginOptionSpan( $inaOption, $iSpanSize, $insVarPrefix = '' ) {
96
 
97
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
98
 
99
- } else if ( $mOptionType === 'text' ) {
 
100
  $sTextInput = esc_attr( $sOptionSaved );
101
  $sHtml .= '
102
  <p>'.$sOptionTitle.'</p>
@@ -109,7 +124,35 @@ function getPluginOptionSpan( $inaOption, $iSpanSize, $insVarPrefix = '' ) {
109
 
110
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
111
 
112
- } else if ( is_array($mOptionType) ) { //it's a select, or radio
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  $sInputType = array_shift($mOptionType);
115
 
@@ -131,7 +174,23 @@ function getPluginOptionSpan( $inaOption, $iSpanSize, $insVarPrefix = '' ) {
131
 
132
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
133
 
134
- } else if ( $mOptionType === 'ip_addresses' ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  $sTextInput = esc_attr( $sOptionSaved );
136
  $nRows = substr_count( $sTextInput, "\n" ) + 1;
137
  $sHtml .= '
@@ -144,7 +203,21 @@ function getPluginOptionSpan( $inaOption, $iSpanSize, $insVarPrefix = '' ) {
144
  class="span5">'.$sTextInput.'</textarea>';
145
 
146
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
147
- } else {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  $sHtml .= 'we should never reach this point';
149
  }
150
 
1
  <?php
2
 
3
+ function printOptionsPageHeader( $insSection = '' ) {
4
+ $sLinkedIcwp = '<a href="http://icwp.io/3a" target="_blank">iControlWP</a>';
5
+ echo '<div class="page-header">';
6
+ echo '<a href="http://icwp.io/2k" target="_blank"><div class="icon32" id="icontrolwp-icon"><br /></div></a>';
7
+ echo '<h2>';
8
+ if ( !empty($insSection) ) {
9
+ echo sprintf( _hlt__( '%s :: WordPress Simple Firewall (from %s)'), $insSection, $sLinkedIcwp );
10
+ }
11
+ else {
12
+ echo sprintf( _hlt__( 'WordPress Simple Firewall (from %s)'), $sLinkedIcwp );
13
+ }
14
+ echo '</h2></div>';
15
+ }
16
+
17
  function printAllPluginOptionsForm( $inaAllPluginOptions, $insVarPrefix = '', $iOptionsPerRow = 1 ) {
18
 
19
  if ( empty($inaAllPluginOptions) ) {
110
 
111
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
112
 
113
+ }
114
+ else if ( $mOptionType === 'text' ) {
115
  $sTextInput = esc_attr( $sOptionSaved );
116
  $sHtml .= '
117
  <p>'.$sOptionTitle.'</p>
124
 
125
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
126
 
127
+ }
128
+ else if ( $mOptionType === 'password' ) {
129
+ $sTextInput = esc_attr( $sOptionSaved );
130
+ $sHtml .= '
131
+ <p>'.$sOptionTitle.'</p>
132
+ <input type="password"
133
+ name="'.$insVarPrefix.$sOptionKey.'"
134
+ value="'.$sTextInput.'"
135
+ placeholder="'.$sTextInput.'"
136
+ id="'.$insVarPrefix.$sOptionKey.'"
137
+ class="span5" />';
138
+
139
+ $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
140
+ }
141
+ else if ( $mOptionType === 'email' ) {
142
+ $sTextInput = esc_attr( $sOptionSaved );
143
+ $sHtml .= '
144
+ <p>'.$sOptionTitle.'</p>
145
+ <input type="text"
146
+ name="'.$insVarPrefix.$sOptionKey.'"
147
+ value="'.$sTextInput.'"
148
+ placeholder="'.$sTextInput.'"
149
+ id="'.$insVarPrefix.$sOptionKey.'"
150
+ class="span5" />';
151
+
152
+ $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
153
+
154
+ }
155
+ else if ( is_array($mOptionType) ) { //it's a select, or radio
156
 
157
  $sInputType = array_shift($mOptionType);
158
 
174
 
175
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
176
 
177
+ }
178
+ else if ( $mOptionType === 'ip_addresses' ) {
179
+ $sTextInput = esc_attr( $sOptionSaved );
180
+ $nRows = substr_count( $sTextInput, "\n" ) + 1;
181
+ $sHtml .= '
182
+ <p>'.$sOptionTitle.'</p>
183
+ <textarea type="text"
184
+ name="'.$insVarPrefix.$sOptionKey.'"
185
+ placeholder="'.$sTextInput.'"
186
+ id="'.$insVarPrefix.$sOptionKey.'"
187
+ rows="'.$nRows.'"
188
+ class="span5">'.$sTextInput.'</textarea>';
189
+
190
+ $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
191
+
192
+ }
193
+ else if ( $mOptionType === 'comma_separated_lists' ) {
194
  $sTextInput = esc_attr( $sOptionSaved );
195
  $nRows = substr_count( $sTextInput, "\n" ) + 1;
196
  $sHtml .= '
203
  class="span5">'.$sTextInput.'</textarea>';
204
 
205
  $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
206
+ }
207
+ else if ( $mOptionType === 'integer' ) {
208
+ $sTextInput = esc_attr( $sOptionSaved );
209
+ $sHtml .= '
210
+ <p>'.$sOptionTitle.'</p>
211
+ <input type="text"
212
+ name="'.$insVarPrefix.$sOptionKey.'"
213
+ value="'.$sTextInput.'"
214
+ placeholder="'.$sTextInput.'"
215
+ id="'.$insVarPrefix.$sOptionKey.'"
216
+ class="span5" />';
217
+
218
+ $sOptionHelpText = '<p class="help-block">'.$sOptionHelpText.'</p>';
219
+ }
220
+ else {
221
  $sHtml .= 'we should never reach this point';
222
  }
223
 
views/icwp_wpsf_access_key_request_index.php ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
+ $sPluginName = 'WordPress Simple Firewall';
5
+ $fFirewallOn = $icwp_aMainOptions['enable_firewall'] == 'Y';
6
+ $fLoginProtectOn = $icwp_aMainOptions['enable_login_protect'] == 'Y';
7
+ $fCommentsFilteringOn = $icwp_aMainOptions['enable_comments_filter'] == 'Y';
8
+ ?>
9
+
10
+ <div class="wrap">
11
+ <div class="bootstrap-wpadmin">
12
+ <?php echo printOptionsPageHeader( 'Admin Access Restriction' ); ?>
13
+ <div class="row">
14
+ <div class="span9">
15
+ <?php
16
+ if ( isset( $_COOKIE[ 'TODOcookie-name' ] ) ) { //the user hasn't created an encryption salt
17
+ ?>
18
+ <div class="alert alert-info">
19
+ <p>You are currently authorized to access your cPanel Manager functions with this plugin.</p>
20
+ <p>You will be returned here once your session times out.</p>
21
+ <form method="post" action="<?php echo $worpit_form_action; ?>" class="form-horizontal">
22
+ <?php wp_nonce_field( $worpit_nonce_field ); ?>
23
+ <input type="hidden" name="cpm_form_submit" value="1" />
24
+ <button type="submit" class="btn btn-primary" name="submit_remove_access">End cPanel Manager Session Now</button>
25
+ </form>
26
+ </div>
27
+ <?php
28
+ }
29
+ else {
30
+ ?>
31
+ <div class="well">
32
+ <h3>What should you enter here?</h3>
33
+ <p>At some point you supplied an <strong>Admin Access Key</strong> - to manage this plugin, you must supply it here first.</p>
34
+ </div>
35
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
36
+ <div class="control-group">
37
+ <label class="control-label" for="icwp_wpsf_admin_access_key_request">Enter Access Key<br></label>
38
+ <div class="controls">
39
+ <div class="option_section selected_item active" id="option_section_icwp_wpsf_admin_access_key">
40
+ <label>
41
+ <input type="text" name="icwp_wpsf_admin_access_key_request" value="" />
42
+ </label>
43
+ <p class="help-block">To manage this plugin you must enter the access key.</p>
44
+ </div>
45
+ </div><!-- controls -->
46
+ </div>
47
+ <div class="form-actions">
48
+ <?php wp_nonce_field( $icwp_nonce_field ); ?>
49
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
50
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Submit Key'); ?></button>
51
+ </div>
52
+ </form>
53
+ <?php
54
+ }
55
+ ?>
56
+ </div><!-- / span9 -->
57
+ <div class="span3" id="side_widgets">
58
+ <?php // echo getWidgetIframeHtml( 'cpm-side-widgets' ); ?>
59
+ </div>
60
+ </div>
61
+
62
+ </div><!-- / bootstrap-wpadmin -->
63
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
64
+ </div><!-- / wrap -->
views/{icwp_wpsf_firewall_config_index.php → icwp_wpsf_config_autoupdates_index.php} RENAMED
@@ -1,14 +1,10 @@
1
  <?php
2
- include_once( dirname(__FILE__).WORPIT_DS.'icwp_options_helper.php' );
3
- include_once( dirname(__FILE__).WORPIT_DS.'widgets'.WORPIT_DS.'icwp_widgets.php' );
4
  ?>
5
  <div class="wrap">
6
  <div class="bootstrap-wpadmin">
7
-
8
- <div class="page-header">
9
- <a href="http://wwwicontrolwp.com/"><div class="icon32" id="icontrolwp-icon"><br /></div></a>
10
- <h2><?php _hlt_e( 'Simple WordPress Firewall (from iControlWP)' ); ?></h2>
11
- </div>
12
 
13
  <div class="row">
14
  <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
1
  <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
  ?>
5
  <div class="wrap">
6
  <div class="bootstrap-wpadmin">
7
+ <?php echo printOptionsPageHeader( 'Auto WordPress Updates' ); ?>
 
 
 
 
8
 
9
  <div class="row">
10
  <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
views/icwp_wpsf_config_comments_filter_index.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
+ ?>
5
+ <div class="wrap">
6
+ <div class="bootstrap-wpadmin">
7
+ <?php echo printOptionsPageHeader( 'Comments (SPAM) Filter' ); ?>
8
+
9
+ <div class="row">
10
+ <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
11
+
12
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
13
+ <?php
14
+ wp_nonce_field( $icwp_nonce_field );
15
+ printAllPluginOptionsForm( $icwp_aAllOptions, $icwp_var_prefix, 1 );
16
+ ?>
17
+ <div class="form-actions">
18
+ <input type="hidden" name="<?php echo $icwp_var_prefix; ?>all_options_input" value="<?php echo $icwp_all_options_input; ?>" />
19
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
20
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Save All Settings'); ?></button>
21
+ </div>
22
+ </form>
23
+
24
+ </div><!-- / span9 -->
25
+
26
+ <?php if ( $icwp_fShowAds ) : ?>
27
+ <div class="span3" id="side_widgets">
28
+ <?php echo getWidgetIframeHtml('side-widgets-wtb'); ?>
29
+ </div>
30
+ <?php endif; ?>
31
+ </div><!-- / row -->
32
+
33
+ </div><!-- / bootstrap-wpadmin -->
34
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
35
+ </div>
views/icwp_wpsf_config_firewall_index.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
+ ?>
5
+ <div class="wrap">
6
+ <div class="bootstrap-wpadmin">
7
+ <?php echo printOptionsPageHeader( 'Firewall' ); ?>
8
+
9
+ <div class="row">
10
+ <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
11
+
12
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
13
+ <?php
14
+ wp_nonce_field( $icwp_nonce_field );
15
+ printAllPluginOptionsForm( $icwp_aAllOptions, $icwp_var_prefix, 1 );
16
+ ?>
17
+ <div class="form-actions">
18
+ <input type="hidden" name="<?php echo $icwp_var_prefix; ?>all_options_input" value="<?php echo $icwp_all_options_input; ?>" />
19
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
20
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Save All Settings'); ?></button>
21
+ <?php if ( get_option ('WP_firewall_redirect_page') ) :?>
22
+ <button type="submit" class="btn btn-warning" name="import-wpf2-submit"><?php _hlt_e( 'Import From WordPress Firewall 2'); ?></button>
23
+ <?php endif; ?>
24
+ </div>
25
+ </form>
26
+
27
+ </div><!-- / span9 -->
28
+
29
+ <?php if ( $icwp_fShowAds ) : ?>
30
+ <div class="span3" id="side_widgets">
31
+ <?php echo getWidgetIframeHtml('side-widgets-wtb'); ?>
32
+ </div>
33
+ <?php endif; ?>
34
+ </div><!-- / row -->
35
+
36
+ </div><!-- / bootstrap-wpadmin -->
37
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
38
+ </div>
views/icwp_wpsf_config_lockdown_index.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
+ ?>
5
+ <div class="wrap">
6
+ <div class="bootstrap-wpadmin">
7
+ <?php echo printOptionsPageHeader( 'Lockdown' ); ?>
8
+
9
+ <div class="row">
10
+ <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
11
+
12
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
13
+ <?php
14
+ wp_nonce_field( $icwp_nonce_field );
15
+ printAllPluginOptionsForm( $icwp_aAllOptions, $icwp_var_prefix, 1 );
16
+ ?>
17
+ <div class="form-actions">
18
+ <input type="hidden" name="<?php echo $icwp_var_prefix; ?>all_options_input" value="<?php echo $icwp_all_options_input; ?>" />
19
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
20
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Save All Settings'); ?></button>
21
+ </div>
22
+ </form>
23
+
24
+ </div><!-- / span9 -->
25
+
26
+ <?php if ( $icwp_fShowAds ) : ?>
27
+ <div class="span3" id="side_widgets">
28
+ <?php echo getWidgetIframeHtml('side-widgets-wtb'); ?>
29
+ </div>
30
+ <?php endif; ?>
31
+ </div><!-- / row -->
32
+
33
+ </div><!-- / bootstrap-wpadmin -->
34
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
35
+ </div>
views/icwp_wpsf_config_login_protect_index.php ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
+ ?>
5
+ <div class="wrap">
6
+ <div class="bootstrap-wpadmin">
7
+ <?php echo printOptionsPageHeader( 'Login Protection' ); ?>
8
+
9
+ <div class="row">
10
+ <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
11
+
12
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
13
+ <?php
14
+ wp_nonce_field( $icwp_nonce_field );
15
+ printAllPluginOptionsForm( $icwp_aAllOptions, $icwp_var_prefix, 1 );
16
+ ?>
17
+ <div class="form-actions">
18
+ <input type="hidden" name="<?php echo $icwp_var_prefix; ?>all_options_input" value="<?php echo $icwp_all_options_input; ?>" />
19
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
20
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Save All Settings'); ?></button>
21
+ </div>
22
+ </form>
23
+
24
+ </div><!-- / span9 -->
25
+
26
+ <?php if ( $icwp_fShowAds ) : ?>
27
+ <div class="span3" id="side_widgets">
28
+ <?php echo getWidgetIframeHtml('side-widgets-wtb'); ?>
29
+ </div>
30
+ <?php endif; ?>
31
+ </div><!-- / row -->
32
+
33
+ </div><!-- / bootstrap-wpadmin -->
34
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
35
+ </div>
views/icwp_wpsf_firewall_log_index.php CHANGED
@@ -1,5 +1,6 @@
1
- <?php
2
- include_once( dirname(__FILE__).'/widgets/icwp_widgets.php' );
 
3
  $sPluginName = 'WordPress Simple Firewall';
4
 
5
  $aLogTypes = array(
@@ -7,7 +8,6 @@ $aLogTypes = array(
7
  1 => 'Warning',
8
  2 => 'Critical'
9
  );
10
-
11
  ?>
12
  <style>
13
  tr.row-Info td {
@@ -18,55 +18,87 @@ $aLogTypes = array(
18
  tr.row-Critical td {
19
  background-color: #DBAFB0;
20
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  </style>
22
 
23
  <div class="wrap">
24
  <div class="bootstrap-wpadmin">
 
25
 
26
- <div class="page-header">
27
- <a href="http://icwp.io/t" target="_blank"><div class="icon32" id="icontrolwp-icon"><br /></div></a>
28
- <h2>Firewall Log :: <?php echo $sPluginName; ?> Plugin (from iControlWP)</h2>
29
- </div>
30
-
31
  <div class="row">
32
  <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
33
-
34
- <?php if ( !$icwp_firewall_log ) : ?>
35
- <?php echo 'There are currently no logs to display.'; ?>
36
- <?php else : ?>
37
-
38
  <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
39
  <?php
40
  wp_nonce_field( $icwp_nonce_field );
41
  ?>
42
  <div class="form-actions">
43
  <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
44
- <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Clear Log'); ?></button>
45
  </div>
46
  </form>
 
 
 
 
47
 
48
  <table class="table table-bordered table-hover table-condensed">
49
  <tr>
50
- <th>Time</th>
51
  <th>Message Type</th>
52
  <th>Message</th>
53
  </tr>
54
- <?php foreach( $icwp_firewall_log as $sId => $aLogData ) :
55
- list( $sRequestIp, $sRequestId ) = explode( '_', $sId );
56
- ?>
57
- <tr>
58
- <td colspan="3">IP: <?php echo $sRequestIp; ?> (Request ID: <?php echo $sRequestId; ?>)</td>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  </tr>
60
- <?php foreach( $aLogData as $aLogItem ) :
61
- list( $sTime, $sLogType, $sLogMessage ) = $aLogItem;
62
- ?>
63
- <tr class="row-<?php echo $aLogTypes[$sLogType]; ?>">
64
- <td><?php echo $sTime; ?></td>
65
- <td><?php echo $aLogTypes[$sLogType] ?></td>
66
- <td><?php echo $sLogMessage; ?></td>
67
- </tr>
68
- <?php endforeach; ?>
69
- <?php endforeach; ?>
 
 
 
 
70
  </table>
71
 
72
  <?php endif; ?>
1
+ <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
  $sPluginName = 'WordPress Simple Firewall';
5
 
6
  $aLogTypes = array(
8
  1 => 'Warning',
9
  2 => 'Critical'
10
  );
 
11
  ?>
12
  <style>
13
  tr.row-Info td {
18
  tr.row-Critical td {
19
  background-color: #DBAFB0;
20
  }
21
+ tr.row-log-header td {
22
+ border-top: 2px solid #999 !important;
23
+ }
24
+ td.cell-log-type {
25
+ text-align: right !important;
26
+ }
27
+ td .cell-section {
28
+ display: inline-block;
29
+ }
30
+ td .section-ip {
31
+ width: 68%;
32
+ }
33
+ td .section-timestamp {
34
+ text-align: right;
35
+ width: 28%;
36
+ }
37
  </style>
38
 
39
  <div class="wrap">
40
  <div class="bootstrap-wpadmin">
41
+ <?php echo printOptionsPageHeader( 'Firewall Log' ); ?>
42
 
 
 
 
 
 
43
  <div class="row">
44
  <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
 
 
 
 
 
45
  <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
46
  <?php
47
  wp_nonce_field( $icwp_nonce_field );
48
  ?>
49
  <div class="form-actions">
50
  <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
51
+ <button type="submit" class="btn btn-primary" name="clear_log_submit"><?php _hlt_e( 'Clear/Fix Log'); ?></button>
52
  </div>
53
  </form>
54
+
55
+ <?php if ( !$icwp_firewall_log ) : ?>
56
+ <?php echo 'There are currently no logs to display. If you expect there to be some, use the button above to Clean/Fix them.'; ?>
57
+ <?php else : ?>
58
 
59
  <table class="table table-bordered table-hover table-condensed">
60
  <tr>
 
61
  <th>Message Type</th>
62
  <th>Message</th>
63
  </tr>
64
+ <?php foreach( $icwp_firewall_log as $sId => $aLogData ) : ?>
65
+ <tr class="row-log-header">
66
+ <td>IP: <strong><?php echo $aLogData['ip']; ?></strong></td>
67
+ <td colspan="2">
68
+ <span class="cell-section section-ip">
69
+ [ <a href="http://whois.domaintools.com/<?php echo $aLogData['ip']; ?>" target="_blank">IPWHOIS Lookup</a> ]
70
+ [
71
+ <?php if ( in_array( $aLogData['ip_long'], $icwp_ip_blacklist ) ) : ?>
72
+ <a href="<?php echo $icwp_form_action; ?>&unblackip=<?php echo $aLogData['ip']; ?>&_wpnonce=<?php echo wp_create_nonce($icwp_nonce_field); ?>&icwp_link_action=1">Remove From Firewall Blacklist</a>
73
+ <?php else: ?>
74
+ <a href="<?php echo $icwp_form_action; ?>&blackip=<?php echo $aLogData['ip']; ?>&_wpnonce=<?php echo wp_create_nonce($icwp_nonce_field); ?>&icwp_link_action=1">Add To Firewall Blacklist</a>
75
+ <?php endif; ?>
76
+ ]
77
+ [
78
+ <?php if ( in_array( $aLogData['ip_long'], $icwp_ip_whitelist ) ) : ?>
79
+ <a href="<?php echo $icwp_form_action; ?>&unwhiteip=<?php echo $aLogData['ip']; ?>&_wpnonce=<?php echo wp_create_nonce($icwp_nonce_field); ?>&icwp_link_action=1">Remove From Firewall Whitelist</a>
80
+ <?php else: ?>
81
+ <a href="<?php echo $icwp_form_action; ?>&whiteip=<?php echo $aLogData['ip']; ?>&_wpnonce=<?php echo wp_create_nonce($icwp_nonce_field); ?>&icwp_link_action=1">Add To Firewall Whitelist</a>
82
+ <?php endif; ?>
83
+ ]
84
+ </span>
85
+ <span class="cell-section section-timestamp"><?php echo date( 'Y/m/d H:i:s', $aLogData['created_at'] ); ?></span>
86
+ </td>
87
  </tr>
88
+ <?php
89
+ $aMessages = unserialize( $aLogData['messages'] );
90
+ if ( is_array( $aMessages ) ) {
91
+ foreach( $aMessages as $aLogItem ) :
92
+ list( $sLogType, $sLogMessage ) = $aLogItem;
93
+ ?>
94
+ <tr class="row-<?php echo $aLogTypes[$sLogType]; ?>">
95
+ <td class="cell-log-type"><?php echo $aLogTypes[$sLogType] ?></td>
96
+ <td><?php echo esc_attr($sLogMessage); ?></td>
97
+ </tr>
98
+ <?php
99
+ endforeach;
100
+ }
101
+ endforeach; ?>
102
  </table>
103
 
104
  <?php endif; ?>
views/icwp_wpsf_index.php CHANGED
@@ -1,17 +1,42 @@
1
  <?php
2
- include_once( dirname(__FILE__).'/widgets/icwp_widgets.php' );
 
3
  $sPluginName = 'WordPress Simple Firewall';
 
 
 
 
4
  ?>
5
 
6
  <div class="wrap">
7
  <div class="bootstrap-wpadmin">
8
-
9
- <div class="page-header">
10
- <a href="http://icwp.io/t" target="_blank"><div class="icon32" id="icontrolwp-icon"><br /></div></a>
11
- <h2>Dashboard :: <?php echo $sPluginName; ?> Plugin (from iControlWP)</h2>
12
- </div>
13
 
14
  <?php include_once( dirname(__FILE__).'/widgets/icwp_common_widgets.php' ); ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  <?php if ( $icwp_fShowAds ) : ?>
17
  <div class="row" id="worpit_promo">
@@ -29,27 +54,28 @@ $sPluginName = 'WordPress Simple Firewall';
29
  <?php endif; ?>
30
 
31
  <div class="row" id="tbs_docs">
32
- <div class="span6" id="tbs_docs_shortcodes">
 
33
  <div class="well">
34
- <h3>Current Firewall Configuration</h3>
35
- <p>The following summarises your current firewall configuration:</p>
36
-
37
- <?php if ( !$icwp_fFirewallOn ) : ?>
38
- Firewall is currently OFF.
39
- <?php else: ?>
40
-
41
  <ul>
42
- <li>Firewall is: <?php echo $icwp_fFirewallOn? 'ON' : 'OFF'; ?></li>
43
- <li>Firewall logging is: <?php echo $icwp_fFirewallLogOn? 'ON' : 'OFF'; ?></li>
44
- <li>When the firewall blocks a visit, it <?php echo $icwp_fBlockSendEmail? 'will': 'will not'; ?> send an email and then :
45
  <?php
46
- if( $icwp_sBlockResponse == 'redirect_die' ) {
47
- echo 'die.';
48
  }
49
- else if ( $icwp_sBlockResponse == 'redirect_home' ) {
 
 
 
50
  echo 'Redirect to home.';
51
  }
52
- else if ( $icwp_sBlockResponse == 'redirect_404' ) {
53
  echo 'Redirect to 404 page.';
54
  }
55
  else {
@@ -57,28 +83,172 @@ $sPluginName = 'WordPress Simple Firewall';
57
  }
58
  ?>
59
  </li>
60
- <li>You have <?php echo count($icwp_aIpWhitelist);?> whitelisted IP addresses.
61
- <?php foreach( $icwp_aIpWhitelist as $sIp ) : ?>
62
- <p><?php echo $sIp; ?><p>
63
- <?php endforeach; ?>
64
- </li>
65
- <li>Firewall blocks WP Login Access: <?php echo $icwp_fBlockLogin? 'ON' : 'OFF'; ?>
66
- <?php if ( $icwp_fBlockLogin && count($icwp_aIpWhitelist) == 0 ) : ?>
67
- <strong>But, there are no whitelisted IPs so it is effectively off.</strong>
68
- <?php endif; ?>
69
- </li>
70
- <li>Firewall blocks Directory Traversals: <?php echo $icwp_fBlockDirTrav? 'ON' : 'OFF'; ?></li>
71
- <li>Firewall blocks SQL Queries: <?php echo $icwp_fBlockSql? 'ON' : 'OFF'; ?></li>
72
- <li>Firewall blocks WordPress Specific Terms: <?php echo $icwp_fBlockWpTerms? 'ON' : 'OFF'; ?></li>
73
- <li>Firewall blocks Field Truncation Attacks: <?php echo $icwp_fBlockFieldTrun? 'ON' : 'OFF'; ?></li>
74
- <li>Firewall blocks Executable File Uploads:<?php echo $icwp_fBlockExeFile? 'ON' : 'OFF'; ?> </li>
75
- <li>Firewall blocks Leading Schemas (HTTPS / HTTP): <?php echo $icwp_fBlockSchema? 'ON' : 'OFF'; ?></li>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  </ul>
77
  <?php endif; ?>
78
  </div>
79
  </div><!-- / span6 -->
80
  <div class="span6" id="tbs_docs_examples">
81
  <div class="well">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  </div>
83
  </div><!-- / span6 -->
84
  </div><!-- / row -->
@@ -92,5 +262,5 @@ $sPluginName = 'WordPress Simple Firewall';
92
  </div><!-- / row -->
93
 
94
  </div><!-- / bootstrap-wpadmin -->
95
-
96
  </div><!-- / wrap -->
1
  <?php
2
+ include_once( dirname(__FILE__).ICWP_DS.'icwp_options_helper.php' );
3
+ include_once( dirname(__FILE__).ICWP_DS.'widgets'.ICWP_DS.'icwp_widgets.php' );
4
  $sPluginName = 'WordPress Simple Firewall';
5
+ $fFirewallOn = $icwp_aMainOptions['enable_firewall'] == 'Y';
6
+ $fLoginProtectOn = $icwp_aMainOptions['enable_login_protect'] == 'Y';
7
+ $fCommentsFilteringOn = $icwp_aMainOptions['enable_comments_filter'] == 'Y';
8
+ $fLockdownOn = $icwp_aMainOptions['enable_lockdown'] == 'Y';
9
  ?>
10
 
11
  <div class="wrap">
12
  <div class="bootstrap-wpadmin">
13
+ <?php echo printOptionsPageHeader( 'Dashboard' ); ?>
 
 
 
 
14
 
15
  <?php include_once( dirname(__FILE__).'/widgets/icwp_common_widgets.php' ); ?>
16
+
17
+ <div class="row">
18
+ <div class="<?php echo $icwp_fShowAds? 'span9' : 'span12'; ?>">
19
+
20
+ <form action="<?php echo $icwp_form_action; ?>" method="post" class="form-horizontal">
21
+ <?php
22
+ wp_nonce_field( $icwp_nonce_field );
23
+ printAllPluginOptionsForm( $icwp_aAllOptions, $icwp_var_prefix, 1 );
24
+ ?>
25
+ <div class="form-actions">
26
+ <input type="hidden" name="<?php echo $icwp_var_prefix; ?>all_options_input" value="<?php echo $icwp_all_options_input; ?>" />
27
+ <input type="hidden" name="icwp_plugin_form_submit" value="Y" />
28
+ <button type="submit" class="btn btn-primary" name="submit"><?php _hlt_e( 'Save All Settings'); ?></button>
29
+ </div>
30
+ </form>
31
+
32
+ </div><!-- / span9 -->
33
+
34
+ <?php if ( $icwp_fShowAds ) : ?>
35
+ <div class="span3" id="side_widgets">
36
+ <?php echo getWidgetIframeHtml('side-widgets-wtb'); ?>
37
+ </div>
38
+ <?php endif; ?>
39
+ </div><!-- / row -->
40
 
41
  <?php if ( $icwp_fShowAds ) : ?>
42
  <div class="row" id="worpit_promo">
54
  <?php endif; ?>
55
 
56
  <div class="row" id="tbs_docs">
57
+ <h2>Plugin Configuration Summary</h2>
58
+ <div class="span6" id="tbs_docs_shortcodes">
59
  <div class="well">
60
+ <h3>Firewall Configuration</h3>
61
+
62
+ <h4 style="margin-top:20px;">Firewall is currently <?php echo $fFirewallOn ? 'ON' : 'OFF'; ?>.
63
+ [ <a href="admin.php?page=icwp-wpsf-firewall">Configure Now</a> ]</h4>
64
+ <?php if ( $fFirewallOn ) : ?>
 
 
65
  <ul>
66
+ <li>Firewall logging is: <?php echo $icwp_aFirewallOptions['enable_firewall'] == 'Y'? 'ON' : 'OFF'; ?></li>
67
+ <li>When the firewall blocks a visit, it will:
 
68
  <?php
69
+ if( $icwp_aFirewallOptions['block_response'] == 'redirect_die' ) {
70
+ echo 'Die.';
71
  }
72
+ else if ( $icwp_aFirewallOptions['block_response'] == 'redirect_die_message' ) {
73
+ echo 'Die with a message.';
74
+ }
75
+ else if ( $icwp_aFirewallOptions['block_response'] == 'redirect_home' ) {
76
  echo 'Redirect to home.';
77
  }
78
+ else if ( $icwp_aFirewallOptions['block_response'] == 'redirect_404' ) {
79
  echo 'Redirect to 404 page.';
80
  }
81
  else {
83
  }
84
  ?>
85
  </li>
86
+ <?php if ( isset($icwp_aFirewallOptions['ips_whitelist']['ips']) ) : ?>
87
+ <li>You have <?php echo count( $icwp_aFirewallOptions['ips_whitelist']['ips'] );?> whitelisted IP addresses:
88
+ <?php foreach( $icwp_aFirewallOptions['ips_whitelist']['ips'] as $sIp ) : ?>
89
+ <br /><?php echo long2ip($sIp); ?> labelled as <?php echo $icwp_aFirewallOptions['ips_whitelist']['meta'][md5( $sIp )]?>
90
+ <?php endforeach; ?>
91
+ </li>
92
+ <?php endif; ?>
93
+
94
+ <?php if ( isset($icwp_aFirewallOptions['ips_blacklist']['ips']) ) : ?>
95
+ <li>You have <?php echo count( $icwp_aFirewallOptions['ips_blacklist']['ips'] );?> blacklisted IP addresses:
96
+ <?php foreach( $icwp_aFirewallOptions['ips_blacklist']['ips'] as $sIp ) : ?>
97
+ <br /><?php echo long2ip($sIp); ?> labelled as <?php echo $icwp_aFirewallOptions['ips_whitelist']['meta'][md5( $sIp )]?>
98
+ <?php endforeach; ?>
99
+ </li>
100
+ <?php endif; ?>
101
+ <li>Firewall blocks Directory Traversals: <?php echo $icwp_aFirewallOptions['block_dir_traversal'] == 'Y'? 'ON' : 'OFF'; ?></li>
102
+ <li>Firewall blocks SQL Queries: <?php echo $icwp_aFirewallOptions['block_sql_queries'] == 'Y'? 'ON' : 'OFF'; ?></li>
103
+ <li>Firewall blocks WordPress Specific Terms: <?php echo $icwp_aFirewallOptions['block_wordpress_terms'] == 'Y'? 'ON' : 'OFF'; ?></li>
104
+ <li>Firewall blocks Field Truncation Attacks: <?php echo $icwp_aFirewallOptions['block_field_truncation'] == 'Y'? 'ON' : 'OFF'; ?></li>
105
+ <li>Firewall blocks Executable File Uploads:<?php echo $icwp_aFirewallOptions['block_exe_file_uploads'] == 'Y'? 'ON' : 'OFF'; ?> </li>
106
+ <li>Firewall blocks Leading Schemas (HTTPS / HTTP): <?php echo $icwp_aFirewallOptions['block_leading_schema'] == 'Y'? 'ON' : 'OFF'; ?></li>
107
+ <li>Firewall Logging: <?php echo ($icwp_aFirewallOptions['enable_firewall_log'] == 'Y')? 'ON' : 'OFF';?></li>
108
+ </ul>
109
+ <?php endif; ?>
110
+
111
+ <h4 style="margin-top:20px;">Login Protection is currently <?php echo $fLoginProtectOn? 'ON' : 'OFF'; ?>.
112
+ [ <a href="admin.php?page=icwp-wpsf-login_protect">Configure Now</a> ]</h4>
113
+ <?php if ( $fLoginProtectOn ) : ?>
114
+ <ul>
115
+ <?php if ( isset($icwp_aLoginProtectOptions['ips_whitelist']['ips']) ) : ?>
116
+ <li>You have <?php echo count( $icwp_aLoginProtectOptions['ips_whitelist']['ips'] );?> whitelisted IP addresses:
117
+ <?php foreach( $icwp_aLoginProtectOptions['ips_whitelist']['ips'] as $sIp ) : ?>
118
+ <br /><?php echo long2ip($sIp); ?> labelled as <?php echo $icwp_aLoginProtectOptions['ips_whitelist']['meta'][md5( $sIp )]?>
119
+ <?php endforeach; ?>
120
+ </li>
121
+ <?php endif; ?>
122
+ <li>Two Factor Login Authentication is: <?php echo $icwp_aLoginProtectOptions['enable_two_factor_auth_by_ip'] == 'Y'? 'ON' : 'OFF'; ?></li>
123
+ <li>Two Factor Login By Pass is: <?php echo $icwp_aLoginProtectOptions['enable_two_factor_bypass_on_email_fail'] == 'Y'? 'ON' : 'OFF'; ?></li>
124
+ <li>Login Cooldown Interval is: <?php echo ($icwp_aLoginProtectOptions['login_limit_interval'] == 0)? 'OFF' : $icwp_aLoginProtectOptions['login_limit_interval'].' seconds'; ?></li>
125
+ <li>Login Form GASP Protection: <?php echo ($icwp_aLoginProtectOptions['enable_login_gasp_check'] == 'Y')? 'ON' : 'OFF';?></li>
126
+ <li>Login Protect Logging: <?php echo ($icwp_aLoginProtectOptions['enable_login_protect_log'] == 'Y')? 'ON' : 'OFF';?></li>
127
+ </ul>
128
+ <?php endif; ?>
129
+
130
+ <h4 style="margin-top:20px;">Comments Filter is currently <?php echo $fCommentsFilteringOn? 'ON' : 'OFF'; ?>.
131
+ [ <a href="admin.php?page=icwp-wpsf-comments_filter">Configure Now</a> ]</h4>
132
+ <?php if ( $fCommentsFilteringOn ) : ?>
133
+ <ul>
134
+ <li>Enchanced GASP Protection is: <?php echo $icwp_aCommentsFilterOptions['enable_comments_gasp_protection'] == 'Y'? 'ON' : 'OFF'; ?></li>
135
+ <li>Comments Cooldown Interval is: <?php echo ($icwp_aCommentsFilterOptions['comments_cooldown_interval'] == 0)? 'OFF' : $icwp_aCommentsFilterOptions['comments_cooldown_interval'].' seconds'; ?></li>
136
+ <li>Comments Token Expire is: <?php echo ($icwp_aCommentsFilterOptions['comments_token_expire_interval'] == 0)? 'OFF' : $icwp_aCommentsFilterOptions['comments_token_expire_interval'].' seconds'; ?></li>
137
+ </ul>
138
+ <?php endif; ?>
139
+
140
+ <h4 style="margin-top:20px;">WordPress Lockdown is currently <?php echo $fLockdownOn? 'ON' : 'OFF'; ?>.
141
+ [ <a href="admin.php?page=icwp-wpsf-comments_filter">Configure Now</a> ]</h4>
142
+ <?php if ( $fLockdownOn ) : ?>
143
+ <ul>
144
+ <li>Disable File Editing is: <?php echo $icwp_aLockdownOptions['disable_file_editing'] == 'Y'? 'ON' : 'OFF'; ?></li>
145
  </ul>
146
  <?php endif; ?>
147
  </div>
148
  </div><!-- / span6 -->
149
  <div class="span6" id="tbs_docs_examples">
150
  <div class="well">
151
+ <h3>v1.9.x Release:</h3>
152
+ <p>The following summarises the main changes to the plugin in the 1.8.x release</p>
153
+ <p><span class="label ">new</span> means for the absolute latest release.</p>
154
+ <?php
155
+ $aNewLog = array(
156
+ 'ADDED: Block deactivation of plugin if admin access restriction is on.',
157
+ 'ADDED: New feature to manage WordPress Automatic Updates.',
158
+ 'FIXED: Several small bugs and streamlined codebase.',
159
+ );
160
+ ?>
161
+ <ul>
162
+ <?php foreach( $aNewLog as $sItem ) : ?>
163
+ <li><span class="label">new</span> <?php echo $sItem; ?></li>
164
+ <?php endforeach; ?>
165
+ </ul>
166
+ <?php
167
+ $aLog = array(
168
+ );
169
+ ?>
170
+ <ul>
171
+ <?php foreach( $aLog as $sItem ) : ?>
172
+ <li><?php echo $sItem; ?></li>
173
+ <?php endforeach; ?>
174
+ </ul>
175
+
176
+ <?php
177
+ $aLog = array(
178
+
179
+ '1.8.x' => array(
180
+ 'ADDED: Admin Access Key Restriction feature.',
181
+ 'ADDED: WordPress Lockdown feature.'
182
+ ),
183
+ '1.7.x' => array(
184
+ 'ADDED: Support for WPMU sites (only manageable as Super Admin).',
185
+ 'CHANGE: Serious performance optimizations and a few bug fixes.',
186
+ ),
187
+ '1.6.x' => array(
188
+ 'ADDED: GASP-based, and further enhanced, SPAM comments filtering functionality.',
189
+ ),
190
+ '1.5.x' => array(
191
+ 'IMPROVED: Whitelisting/Blacklisting operations and options',
192
+ 'NEW Option: Login Protect Dedicated IP Whitelist.',
193
+ 'REMOVED Option: Firewall wp-login.php blocking'
194
+ ),
195
+ '1.4.x' => array(
196
+ 'NEW Option: Plugin will automatically upgrade itself when an update is detected - ensures plugin always remains current.',
197
+ 'Now displays an admin notice when a plugin upgrade is available with a link to immediately update.',
198
+ 'Plugin collision protection: removes collision with All In One WordPress Security.',
199
+ 'Improved Login Cooldown Feature- works more like email throttling as it now uses an extra filesystem-based level of protection.',
200
+ "Fix - Login Cooldown Feature didn't take effect in certain circumstances.",
201
+ 'Brand new plugin options system making them more efficient, easier to manage/update, using fewer WordPress database options',
202
+ 'Huge improvements on database calls and efficiency in loading plugin options'
203
+ ),
204
+ '1.3.x' => array(
205
+ "New Feature - Email Throttle. It will prevent you getting bombarded by 1000s of emails in case you're hit by a bot.",
206
+ "Another Firewall die() option. New option will print a message and uses the wp_die() function instead.",
207
+ "Option to separately log Login Protect features.",
208
+ "Refactored and improved the logging system.",
209
+ "Option to by-pass 2-factor authentication in the case sending the verification email fails.",
210
+ "Login Protect checking now better logs out users immediately with a redirect.",
211
+ "We now escape the log data being printed - just in case there's any HTML/JS etc in there we don't want.",
212
+ "Optimized and cleaned a lot of the option caching code to improve reliability and performance (more to come).",
213
+ ),
214
+
215
+ '1.2.x' => array(
216
+ 'New Feature - Ability to import settings from WordPress Firewall 2 Plugin.',
217
+ 'New Feature - Login Form GASP-based Anti-Bot Protection.',
218
+ 'New Feature - Login Cooldown Interval.',
219
+ 'Performance optimizations.',
220
+ 'UI Cleanup and code improvements.',
221
+ 'Added new Login Protect feature where you can add 2-Factor Authentication to your WordPress user logins.',
222
+ 'Improved method for processing the IP address lists to be more cross-platform reliable.',
223
+ 'Improved .htaccess rules (thanks MickeyRoush).',
224
+ 'Mailing method now uses WP_MAIL.'
225
+ ),
226
+
227
+ '1.1.x' => array(
228
+ 'Option to check Cookies values in firewall testing.',
229
+ 'Ability to whitelist particular pages and their parameters.',
230
+ 'Quite a few improvements made to the reliability of the firewall processing.',
231
+ 'Option to completely ignore logged-in Administrators from the Firewall processing (they wont even trigger logging etc).',
232
+ 'Ability to (un)blacklist and (un)whitelist IP addresses directly from within the log.',
233
+ 'Helpful link to IP WHOIS from within the log.',
234
+ 'Firewall logging now has its own dedicated database table.',
235
+ 'Fix: Block email not showing the IPv4 friendly address.',
236
+ 'You can now specify IP ranges in whitelists and blacklists.',
237
+ 'You can now specify which email address to send the notification emails.',
238
+ "You can now add a comment to IP addresses in the whitelist/blacklist. To do this, write your IP address then type a SPACE and write whatever you want (don't take a new line').",
239
+ 'You can now set to delete ALL firewall settings when you deactivate the plugin.',
240
+ 'Improved formatting of the firewall log.'
241
+ )
242
+ );
243
+ ?>
244
+ <?php foreach( $aLog as $sVersion => $aItems ) : ?>
245
+ <h3>Change log for the v<?php echo $sVersion; ?> release:</h3>
246
+ <ul>
247
+ <?php foreach( $aItems as $sItem ) : ?>
248
+ <li><?php echo $sItem; ?></li>
249
+ <?php endforeach; ?>
250
+ </ul>
251
+ <?php endforeach; ?>
252
  </div>
253
  </div><!-- / span6 -->
254
  </div><!-- / row -->
262
  </div><!-- / row -->
263
 
264
  </div><!-- / bootstrap-wpadmin -->
265
+ <?php include_once( dirname(__FILE__).'/include_js.php' ); ?>
266
  </div><!-- / wrap -->
views/widgets/icwp_common_widgets.php CHANGED
@@ -10,20 +10,20 @@ $sPluginName = 'WordPress Simple Firewall';
10
  <p>Please help <u>spread the word</u> or check out what else we do ...</p>
11
  </div>
12
  <div class="span4">
13
- <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://icwp.io/r" data-text="I use the <?php echo $sPluginName; ?> plugin to protect my #WordPress sites!" data-via="iControlWP" data-size="large">Tweet</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
14
  </div>
15
  </div>
16
  <div class="row">
17
  <div class="span6">
18
  <ul>
19
- <li><a href="http://icwp.io/4" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
20
- <li><a href="http://icwp.io/y" target="_blank">Check out all our other WordPress Plugins</a></li>
21
  </ul>
22
  </div>
23
  <div class="span5">
24
  <ul>
25
- <li><a href="http://icwp.io/a" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
26
- <li><a href="http://wordpress.org/extend/plugins/wordpress-bootstrap-css/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
27
  </ul>
28
  </div>
29
  </div>
10
  <p>Please help <u>spread the word</u> or check out what else we do ...</p>
11
  </div>
12
  <div class="span4">
13
+ <a href="https://twitter.com/share" class="twitter-share-button" data-url="http://www.icontrolwp.com/our-wordpress-plugins/wordpress-simple-firewall-plugin/" data-text="I use the <?php echo $sPluginName; ?> plugin to protect my #WordPress sites!" data-via="iControlWP" data-size="large">Tweet</a><script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="//platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
14
  </div>
15
  </div>
16
  <div class="row">
17
  <div class="span6">
18
  <ul>
19
+ <li><a href="http://icwp.io/2b" target="_blank"><strong>Manage <u>All</u> Your WordPress Sites In 1 Place!</strong></a></li>
20
+ <li><a href="http://icwp.io/2c" target="_blank">Check out all our other WordPress Plugins</a></li>
21
  </ul>
22
  </div>
23
  <div class="span5">
24
  <ul>
25
+ <li><a href="http://icwp.io/2d" target="_blank"><strong>Visit the plugin Help & Support page</strong></a>.</li>
26
+ <li><a href="http://wordpress.org/extend/plugins/wp-simple-firewall/" target="_blank">Show some love, and give this a 5 star rating on WordPress.org.</a></li>
27
  </ul>
28
  </div>
29
  </div>
views/widgets/icwp_widgets.php CHANGED
@@ -1,4 +1,5 @@
1
- <?php
 
2
  function getWidgetIframeHtml( $insSnippet ) {
3
 
4
  $sSubPageNow = isset( $_GET['page'] )? 'page='.$_GET['page'].'&': '';
@@ -8,7 +9,7 @@ function getWidgetIframeHtml( $insSnippet ) {
8
  $sIframeName = 'iframe-hlt-bootstrapcss-'.$insSnippet;
9
 
10
  if ( strpos( $insSnippet, 'side-widgets') !== false ) {
11
- $sHeight = '1200px';
12
  }
13
  elseif ( strpos( $insSnippet, 'dashboard-widget-developerchannel') !== false ) {
14
  $sHeight = '312px';
1
+ <?php
2
+
3
  function getWidgetIframeHtml( $insSnippet ) {
4
 
5
  $sSubPageNow = isset( $_GET['page'] )? 'page='.$_GET['page'].'&': '';
9
  $sIframeName = 'iframe-hlt-bootstrapcss-'.$insSnippet;
10
 
11
  if ( strpos( $insSnippet, 'side-widgets') !== false ) {
12
+ $sHeight = '800px';
13
  }
14
  elseif ( strpos( $insSnippet, 'dashboard-widget-developerchannel') !== false ) {
15
  $sHeight = '312px';